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] #392 - 솝마디 결과 뷰, 부적 화면 전환 및 데이터 바인딩 #394

Merged
merged 13 commits into from
Sep 29, 2024

Conversation

dlwogus0128
Copy link
Contributor

@dlwogus0128 dlwogus0128 commented Sep 28, 2024

🌴 PR 요약

🌱 작업한 브랜치

🌱 PR Point

  • 솝마디 결과 뷰 -> 부적 화면 -> 홈 화면 전환을 구현했습니다.
  • 홈 화면 전환은 딥링크로 구현했습니다.
  • 부적 화면 데이터 바인딩을 진행했습니다.

📌 참고 사항

  • 기존에는 PokeFeatureBottomSheetDailySoptuneFeature 모듈에서 사용하면서, DailySoptuneBuilderPokeFeature를 import해 직접 생성하고 있었습니다. 따라서 모듈 간의 강한 결합이 이루어졌던 부분을 제거하고, ApplicationCoordinator에서 PokeBuilder()를 주입하여 인터페이스를 통해 PokeBuilder에서 BottomSheet를 생성하도록 수정했습니다. (이 부분에 대해 잘못된 부분이 있다면 리뷰 부탁드립니다..!)
@discardableResult
    internal func runDailySoptuneResultFlow() -> DailySoptuneResultCoordinator {
        let coordinator = DailySoptuneResultCoordinator(
            router: router,
            factory: DailySoptuneBuilder(),
            pokeFactory: PokeBuilder()
        )
        
        coordinator.finishFlow = { [weak self, weak coordinator] in
            coordinator?.childCoordinators = []
            self?.removeDependency(coordinator)
        }
        
        coordinator.requestCoordinating = { [weak self] in
            self?.notificationHandler.receive(deepLink: "home")
            guard let deepLinkComponent = self?.notificationHandler.deepLink.value else { return }
            self?.handleDeepLink(deepLink: deepLinkComponent)
        }
        
        addDependency(coordinator)
        coordinator.start()
        
        return coordinator
    }
  • 아직 ProfileImageView 눌렀을 때 플그 프로필로 넘어가는 부분이 안 되어있습니다..~ 다음 이슈에서 할게요 ! @yungu0010

📸 스크린샷

기능 스크린샷
솝마디 결과 -> 부적화면

📮 관련 이슈

@dlwogus0128 dlwogus0128 added Feat 새로운 기능 구현 재현✦ labels Sep 28, 2024
@dlwogus0128 dlwogus0128 self-assigned this Sep 28, 2024
Copy link

height bot commented Sep 28, 2024

Link Height tasks by mentioning a task ID in the pull request title or commit messages, or description and comments with the keyword link (e.g. "Link T-123").

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

Copy link
Contributor

@yungu0010 yungu0010 left a comment

Choose a reason for hiding this comment

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

덕분에 많이 배워갑니다 !! 감사해요

@@ -24,4 +24,18 @@ public extension UIColor {
blue: rgb & 0xFF
)
}

convenience init(hex: String) {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍👍

Comment on lines 43 to 44
self?.router.dismissModule(animated: true)
self?.finishFlow?()
Copy link
Contributor

Choose a reason for hiding this comment

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

guard let으로 self 한 번 확인해주세요 !

public final class DailySoptuneCoordinator: DefaultCoordinator {
public final class DailySoptuneResultCoordinator: DefaultCoordinator {
Copy link
Contributor

Choose a reason for hiding this comment

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

ㅌㅋㅋㅋㅋ죄송합니다 .. 🫶

Comment on lines 73 to 74
rootController = dailySoptuneResult.vc.asNavigationController
router.present(rootController, animated: false, modalPresentationSytle: .overFullScreen)
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분 저는 viewcontrollable까지만 전달했는데 asNavigationController로 지정해주신 이유가 있을까요?
개인적인 생각으로는 main에서는 새 내비게이션이 시작하니까 showDailySoptuneMain에서는 asNavigationController가 맞는 것 같고 result랑 card에서는 viewControllable만 넘겨주는 것이 맞는 것 같습니다 !

근데 vc를 넘겨주는 것과 vc.viewControllable의 차이를 잘 모르겠어요. . . @lsj8706 혹시 설명해주실 수 있나요 ? 선배님 ..

Copy link
Member

Choose a reason for hiding this comment

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

dailySoptuneResult.vc를 present를 띄우고 있는데, 만약 이 vc위에 다른 vc를 push 해야 한다는 니즈가 있다면, 지금처럼 asNavigationController 사용해서 네비게이션 컨트롤러로 만들어서 올려야해요.
이런 니즈가 없다면 그냥 viewControllable 상태로 올려도 무방해요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아하!!! 감사합니다

.withUnretained(self)
.sink { _, cardModel in
self.onReceiveTodaysFortuneCardButtonTapped?(cardModel)
output.todaysFortuneCard.send(cardModel)
Copy link
Contributor

Choose a reason for hiding this comment

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

ResultViewController에서 output을 사용하지 않기 때문에 필요없는 코드라고 생각합니다 .. !

let coordinator = DailySoptuneResultCoordinator(
router: router,
factory: DailySoptuneBuilder(),
pokeFactory: PokeBuilder()
Copy link
Contributor

Choose a reason for hiding this comment

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

배워갑니다👍

Copy link
Member

@lsj8706 lsj8706 left a comment

Choose a reason for hiding this comment

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

고생하셨어요~! 윤서가 질문 남긴거 답변하려고 들어왔다가 눈에 보이는거 몇가지만 코멘트 남겨둬요.

추가로~ 제 의견 또는 팀원들의 코멘트를 무조건 수용하는 것 보다는 스스로 고민해보고 팀원들과 논의도 많이 해보는 시간을 가지셨으면 좋겠네요! (물론 데드라인 때문에 바쁘면 불가능)

private let factory: DailySoptuneFeatureBuildable
private let router: Router

private let cardModel: DailySoptuneCardModel
Copy link
Member

Choose a reason for hiding this comment

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

질문!
현재 요구사항과 뷰 구조를 제가 잘 몰라서 생긴 질문인데요~!
cardModel을 Coordinator가 속성으로 들고 있지 않고 start()의 파라미터로 받게 하는건 어려울까요!?
지금도 크게 문제될 것은 없지만 코디네이터가 굳이 이런 도메인 상태를 가져야 하는가 하는 짧은 생각이 들어서요~ ㅎㅎ

    public override func start(cardModel: DailySoptuneCardModel) {
        showDailySoptuneCard(cardModel: DailySoptuneCardModel)
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

헉 ... 이 부분 논의해보고 수정하겠습니다 ...ㅇ.ㅇ

dailySoptuneCardCoordinator.start()
}

private func showMessageBottomSheet(userModel: PokeUserModel, on view: UIViewController?) -> AnyPublisher<(PokeUserModel, PokeMessageModel, isAnonymous: Bool), Never> {
Copy link
Member

@lsj8706 lsj8706 Sep 28, 2024

Choose a reason for hiding this comment

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

이건 그냥 제가 예전에 쌓아둔 개인적인 아쉬움인데 공유차 남겨요.

현재는 바텀 시트를 보여주고 이 바텀시트에서 사용자가 선택한 데이터들을 Publisher 형태로 제공하고 있는데 이걸 바텀 시트를 띄우는 메서드인 showMessageBottomSheet()의 리턴값으로 바로 반환하니까 이 함수를 처음 보는 사람 입장에서는 showMessageBottomSheet()는 왜 (PokeUserModel, PokeMessageModel, isAnonymous: Bool) 이런 값들을 반환하는거지? 이 값들은 뭘까 하는 의문이 생길 수 있다고 봐요. 함수명은 그저 바텀 시트를 show 한다고만 알려주는데 뭔가를 리턴하고 이 리턴 값의 정체가 불분명 하니까요.

그래서 이런 바텀 시트류의 UI에서는 콜백 인터페이스가 Publisher나 클로저가 아닌 Delegate 패턴이어야 더 명확하다고 생각해요. (예전에는 이런 개념이 없었어서 그냥 혼용했었음 😅)
예를 들어 이렇게 바뀌는 거에요.

private func showMessageBottomSheet(userModel: PokeUserModel, on view: UIViewController?) {
  guard let bottomSheet = self.pokeFactory
            .makePokeMessageTemplateBottomSheet(messageType: messageType)
  // 생략
  bottomSheet.delegate = self
  // 생략
}

extension DailySoptuneResultCoordinator: MessageBottomSheetDelegate {
   func messageBottomSheet(pickedMessage: PokeMessageModel) {} // 필요한 정보들 전달(축약 버전)
}

즉, 이벤트 전달을 위한 바인딩을 할 때 언제 클로저(또는 Publisher)를 쓸지 또는 Delegate를 쓰면 좋을지 상황에 맞게 잘 고민해보면 좋을 것 같아요. 각각 장단점이 있어요.

사실 여기서는 이미 publisher로 인터페이스가 맞춰져 있어서 그냥 이대로 가는게 최선이에요 ㅋㅋㅋ 그냥 공부할 때 도움되라고 남긴 코멘트입니당


public var viewModel: DailySoptuneCardViewModel
private let cardModel: DailySoptuneCardModel
private var cancelBag = CancelBag()
Copy link
Member

Choose a reason for hiding this comment

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

요거 private let으로 바꿔도 될 것 같긴하네요

private extension DailySoptuneCardVC {
func setData() {
self.subCardLabel.text = cardModel.description
self.cardLabel.text = "\(cardModel.name)이 왔솝"
Copy link
Member

Choose a reason for hiding this comment

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

아 ㅋㅋㅋ 왔솝이 의도된 워딩인거죠???ㅋㅋㅋㅋ
힙하네요 😆

Comment on lines 52 to 56
input.goToHomeButtonTap
.withUnretained(self)
.sink { _ in
self.onGoToHomeButtonTapped?()
}.store(in: cancelBag)
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 쓰면 withUnretained는 의미가 없을거에요.
withUnretained의 구현부를 보시면 self를 weak 참조 한다음에 리턴해주고 있어요. 이 리턴된 값을 사용해야 약한 참조가 유지돼요.
(그래서 개인 취향이지만 전 귀찮아서 [weak self] 쓰고 그냥 guard let이나 self?로 처리해요)

Suggested change
input.goToHomeButtonTap
.withUnretained(self)
.sink { _ in
self.onGoToHomeButtonTapped?()
}.store(in: cancelBag)
input.goToHomeButtonTap
.withUnretained(self)
.sink { owner in
owner.onGoToHomeButtonTapped?() // 여기서 owner가 self를 약한 참조한 변수
}.store(in: cancelBag)

@dlwogus0128 dlwogus0128 merged commit ea95385 into develop Sep 29, 2024
@dlwogus0128 dlwogus0128 deleted the feat/#392-soptune-result-navigation-binding branch September 29, 2024 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feat 새로운 기능 구현 size/L 재현✦
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feat] 솝마디 결과 뷰, 부적 화면 전환 및 데이터 바인딩
3 participants