Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 2.0.0 준비작업 #184

Merged
merged 13 commits into from
Jan 24, 2025
Merged

feat: 2.0.0 준비작업 #184

merged 13 commits into from
Jan 24, 2025

Conversation

ShapeKim98
Copy link
Contributor

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호
#171

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

  • 스플래시 + 인트로 기능을 모듈화 하였습니다.
  • 성능 개선을 위해 도입한 내부 shared 함수 액션 도입 및 탬플릿을 수정하였습니다.
  • 조건에 따른 overlay, background 모디파이어를 작성하였습니다.
  • 런치스크린의 contraint를 수정하였습니다.

스크린샷 (선택)

Simulator Screen Recording - iPhone 16 Pro - 2025-01-18 at 00 59 14 Simulator Screen Recording - iPhone 16 Pro - 2025-01-18 at 00 57 28

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

스플래시 + 인트로 기능을 모듈화 하였습니다.

  • 저희는 기능별 데모앱으로 좀 더 효율적인 개발과 디버깅을 위해 기능들을 tuist로 열심히 모듈화 했었습니다.
    하지만, API 통신 이후 기능별 데모앱은 로그인 기능이 없어 API를 받아오지 못하는 앱이 되었고, 저희도 디버깅시 자연스럽게 데모앱을 사용하지 않았습니다.
    그렇기 때문에 스플래시 + 인트로 기능을 모듈화 하여, 데모 뷰를 만들고, 데모앱에서도 로그인을 할 수 있도록 하였습니다. (사실 좀 늦은 감이..ㅎㅎ)
    사용법은 다음과 같습니다.
DemoView(store: .init(
    initialState: .init(),
    reducer: { DemoFeature() }
)) {
// 데모 기능 뷰
    NavigationStack {
        RemindView(
            store: .init(
                initialState: .init(),
                reducer: { RemindFeature() }
            )
        )
    }
}

성능 개선을 위해 도입한 내부 shared 함수 액션 도입 및 탬플릿을 수정하였습니다.

  • 모든 것은 알려주신 TCA의 문서로부터 시작합니다. Performace
    저희는 Reducer내부에서도 액선들을 필요시 재사용 하기위해 InnerAction, AsyncAction, ScopeAction, DelegateAction`으로 액션을을 관리하였습니다.
    그러면서 자연스럽게 액션 안에 액션을 호출하는 일이 빈번하였고, 이는 공식문서에서 권장하지 않는 방법으로 소개되고 있습니다.
case .메타데이터_조회:
    return .send(.async(.메타데이터_조회_수행))
...
case .메타데이터_조회_수행:
    guard let url = URL(string: state.content.data) else {
        return .none
    }
    return .run { send in
        let imageURL = try await swiftSoupClient.parseOGImageURL(url)
        guard let imageURL else { return }
        await send(.inner(.메타데이터_조회_수행_반영(imageURL)))
    }
  • 문서에서는 액션을 보내는 것이 클래스의 메서드를 호출하는 것처럼 가벼운 작업이 아니라고 설명하고 있고, 최대한 액션을 보내는 것을 줄이는 것을 권장하고 있습니다. 그렇기 때문에, 성능 차이가 있는지 확인해보기 위한 테스트를 진행하였습니다.
    XCTest 다루는 것은 처음이라 이렇게 하는게 맞는지 확신이 안서는데..(잘 못 된거 있으면 바로 알려주세요..)
    만들어주신 테스트 타겟에서 테스트를 진행해 보았습니다.
func test_primeTest() async {
   let count = 10
   var sharedAverage: CFAbsoluteTime = 0.0
   for _ in 0..<count {
       await test_shared_메서드_적용(&sharedAverage)
   }
   
   var noSharedAverage: CFAbsoluteTime = 0.0
   for _ in 0..<count {
       await test_shared_메서드_미적용(&noSharedAverage)
   }
   
   print(
       "shared_메서드_적용 \(count)번 테스트 평균 소요시간",
       sharedAverage / CFAbsoluteTime(count)
   )
   print(
       "shared_메서드_미적용 \(count)번 테스트 평균 소요시간",
       noSharedAverage / CFAbsoluteTime(count)
   )
}
  • 저희는 인스타그램과 같이 썸네일 파싱 오류를 해결하기 위해, 셀단위의 컴포넌트에 ContentFeautureStore를 의존시키고 있습니다. 이는 셀 단위이기 때문에 수많은 Store를 생성하게 되고, 수많은 Store내부에서 여러 액션들을 수행하게 됩니다.
    그렇기 때문에 ContentFeature를 테스트 대상으로 삼았고,
    테스팅 로직은 메타데이터 조회 -> 메타데이터 반영 -> 썸네일 수정 API 요청 입니다.
    아래는 테스트 결과들 입니다.
  • 10회 반복
스크린샷 2025-01-18 00 37 20
  • 100회 반복
스크린샷 2025-01-18 00 38 55
  • 1000회 반복
스크린샷 2025-01-18 00 39 27
  • 10000회 반복
스크린샷 2025-01-18 00 42 41
  • 특이점으로는 액션 호출 수가 많아 질 수 록 더 나은 성능을 보여준다는 것입니다.
  • 사실 요즘 하드웨어들이 좋아져서 아주 미미한 차이이긴 합니다.. 하지만, 그렇다고 해서 문서에서 권장하지 않는 방법을 내버려두는 것도 아니라고 생각합니다... 그렇기 때문에 아래와 같이 Store내부에 Shared 메서드를 작성하였습니다.
func shared(_ action: Action, state: inout State) -> Effect<Action> {
    switch action {
    case .view(let viewAction):
        return handleViewAction(viewAction, state: &state)
    case .inner(let innerAction):
        return handleInnerAction(innerAction, state: &state)
    case .async(let asyncAction):
        return handleAsyncAction(asyncAction, state: &state)
    case .scope(let scopeAction):
        return handleScopeAction(scopeAction, state: &state)
    case .delegate(let delegateAction):
        return handleDelegateAction(delegateAction, state: &state)
    }
}
  • 리팩토링도 고려한 구조이기도 하여, 사용법은 너무너무 간단합니다.
case .메타데이터_조회:
    return .send(.async(.메타데이터_조회_수행))

이런 액션을

case .메타데이터_조회:
    return shared(.async(.메타데이터_조회_수행), state: &state)

이렇게 바꿔 주기만 하면 됩니다. 저렇게 재사용 가능한 메서드만 추가해주면, 기존에 쓰던 InnerAction, AsyncAction, DelegateAction 열거형들도 문제없이 사용가능하며, run 액션 내부에서 어쩔 수 없이 send 호출해야 하는 상황에도 대응이 됩니다.(기존 액션 정의 코드들이 남아있기 때문)

  • 당장 적용하기 위해 모든 코드를 리펙토링 하기보단 신규 피처에서 적용하는 것을 시작으로 점진적으로 적용해나갔으면 좋겠습니다!! 일단 2.0 다 끝내고 리팩토링을 해보져..!!

조건에 따른 overlay, background 모디파이어를 작성하였습니다.

  • if - else 문, 옵셔널 바인딩에 대응하였습니다.
...
.overlay(ifLet: title) { title in
    Text(title)
        .pokitFont(.title3)
        .foregroundStyle(.pokit(.text(.primary)))
}
...
.overlay(if: store.linkPopup != nil, alignment: .bottom) {
    PokitLinkPopup(type: $store.linkPopup)
}
....
.background(if: isSelected) {
    Color.pokit(.bg(.primary))
        .matchedGeometryEffect(id: "SELECT", in: heroEffect)
} else: {
    isDisabled
    ? Color.pokit(.bg(.disable))
    : Color.pokit(.bg(.base))
}
...
.background(ifLet: URL(string: content.thumbNail)) { url in
    recommendedContentCellImage(url: url, contentId: content.id)
} else: {
    imagePlaceholder
}

close #171

@ShapeKim98 ShapeKim98 added Feat 기능구현 Fix 기능 수정 labels Jan 17, 2025
@ShapeKim98 ShapeKim98 requested a review from stealmh January 17, 2025 16:47
@ShapeKim98 ShapeKim98 self-assigned this Jan 17, 2025
Copy link
Member

@stealmh stealmh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생많으셨습니다!
shared 함수들 보면서 든 생각인데 기존의 FeatureAction을 shared로 아예 대체해도 좋을 것 같아요.
말로 설명하기 좀 복잡한 면이 많아 시간잡아서 같이 얘기해보시져!

그외 피쳐들 모듈화 분리나 background, overlay modifier는 너무 잘 작업해주셔서 좋습니다!👍👍

  • 저도 컨디션에따른 overlay를 적용해볼까 하다가 커밋은 안했었는데 이유가 뷰가 재생성되버려서 특정영역만 바뀌는게아니라 전체를 다시 그리더라구요 이런 이슈는 없었는지 궁금합니다

  • 런치스크린관련 수정은 어떤건지 알려주세요!

@ShapeKim98
Copy link
Contributor Author

고생많으셨습니다! shared 함수들 보면서 든 생각인데 기존의 FeatureAction을 shared로 아예 대체해도 좋을 것 같아요. 말로 설명하기 좀 복잡한 면이 많아 시간잡아서 같이 얘기해보시져!

그외 피쳐들 모듈화 분리나 background, overlay modifier는 너무 잘 작업해주셔서 좋습니다!👍👍

  • 저도 컨디션에따른 overlay를 적용해볼까 하다가 커밋은 안했었는데 이유가 뷰가 재생성되버려서 특정영역만 바뀌는게아니라 전체를 다시 그리더라구요 이런 이슈는 없었는지 궁금합니다
  • 런치스크린관련 수정은 어떤건지 알려주세요!
  • 혹시 어떤 상황이었는지 확인할 수 있을까요? 저는 아직까지는 발견하지 못했습니다..
  • 아 런치스크린 짤막하게 적는다 하고 안적었는데, 그냥 잘못된 Constraint 수정입니다 ㅎㅎ 최근에 스토리보드를 열심히 배워서 수정해봤어요

@stealmh
Copy link
Member

stealmh commented Jan 21, 2025

고생많으셨습니다! shared 함수들 보면서 든 생각인데 기존의 FeatureAction을 shared로 아예 대체해도 좋을 것 같아요. 말로 설명하기 좀 복잡한 면이 많아 시간잡아서 같이 얘기해보시져!
그외 피쳐들 모듈화 분리나 background, overlay modifier는 너무 잘 작업해주셔서 좋습니다!👍👍

  • 저도 컨디션에따른 overlay를 적용해볼까 하다가 커밋은 안했었는데 이유가 뷰가 재생성되버려서 특정영역만 바뀌는게아니라 전체를 다시 그리더라구요 이런 이슈는 없었는지 궁금합니다
  • 런치스크린관련 수정은 어떤건지 알려주세요!
  • 혹시 어떤 상황이었는지 확인할 수 있을까요? 저는 아직까지는 발견하지 못했습니다..
  • 아 런치스크린 짤막하게 적는다 하고 안적었는데, 그냥 잘못된 Constraint 수정입니다 ㅎㅎ 최근에 스토리보드를 열심히 배워서 수정해봤어요
 .overlay(alignment: .bottom) {
    if store.linkPopup != nil {
        PokitLinkPopup(...)
    }
}

이런상황에서 쓰려고 만들었는데 자연스럽게 사라지지않고 뷰가 재생성되서 깜빡거리는 현상을 발견했었던 기억이 나네요 😂

@ShapeKim98 ShapeKim98 merged commit 0f501e5 into develop Jan 24, 2025
1 check passed
@stealmh stealmh linked an issue Feb 2, 2025 that may be closed by this pull request
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feat 기능구현 Fix 기능 수정
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[2.0.0] 준비작업
2 participants