Skip to content

Coordinator 추가 & 화면 전환 책임 분리#88

Open
kanghun1121 wants to merge 32 commits intodevfrom
refactor/coordinator
Open

Coordinator 추가 & 화면 전환 책임 분리#88
kanghun1121 wants to merge 32 commits intodevfrom
refactor/coordinator

Conversation

@kanghun1121
Copy link
Copy Markdown
Member

@kanghun1121 kanghun1121 commented Apr 12, 2026

✏️ 변경 내용

Coordinator 패턴 도입

  • Coordinator 프로토콜 및 화면별 Coordinator 추가
  • Coordinator가 자식 Coordinator의 생명주기를 관리하는 트리 구조 형성
  • ViewTransitionHandling 프로토콜로 화면 전환 애니메이션 추상화 (기존 AppFlowController의 transition(_:))

ViewModelFactory 추가 — DI Container에서 ViewModel 등록 제거

  • 기존: SceneDelegate에서 DI Container에 ViewModel을 직접 등록 → 팩토리 클로저가 연쇄적으로 중첩되는 구조
  • 변경: 각 Feature별 SceneFactoryUseCase만 주입받아 ViewModel/VC를 직접 생성
  • DI Container는 Domain 레이어(UseCase/Repository)까지만 관리하고, Presentation 레이어 객체 생성은 Factory가 담당

연쇄 Factory 클로저 제거

  • 기존: AppFlowController 내부에서 VC 생성 시 중첩 클로저로 하위 화면까지 연쇄 생성하는 구조
  • 변경: 각 Coordinator가 자신의 Factory를 통해 독립적으로 화면을 생성 → 클로저 체인 완전 제거

AppFlowController 책임 분배

  • 기존 AppFlowController가 담당하던 역할을 분리

기타 개선

  • LoginSession 도입으로 로그인/로그아웃/탈퇴 이벤트를 Combine Publisher로 통합
  • CoachmarkStorage 추가

Coordinator 구조

graph TD
    classDef root fill:#FF6B6B,stroke:#333,color:#fff,font-weight:bold
    classDef auth fill:#FFA07A,stroke:#333,color:#fff
    classDef tab fill:#4ECDC4,stroke:#333,color:#fff,font-weight:bold
    classDef tabChild fill:#45B7AA,stroke:#333,color:#fff
    classDef detail fill:#5B8DEE,stroke:#333,color:#fff
    classDef leaf fill:#A78BFA,stroke:#333,color:#fff
    classDef modal fill:#F59E0B,stroke:#333,color:#fff

    App["🏠 AppCoordinator"]:::root
    App -->|"replace scene"| Login["🔐 LoginCoordinator"]:::auth
    App -->|"replace scene"| Main["📱 MainCoordinator"]:::tab
    App -.->|"deep link"| Detail

    Main -->|"Tab 1"| Calendar["📅 CalendarCoordinator"]:::tabChild
    Main -->|"Tab 2"| Insight["📊 InsightCoordinator"]:::tabChild
    Main -->|"push"| MyPage["👤 MyPageCoordinator"]:::tabChild

    Calendar -->|"push"| Detail["🍽️ DetailCoordinator"]:::detail
    Calendar -->|"push"| IP1["🖼️ ImagePickerCoordinator"]:::leaf

    Detail -->|"push"| Edit["✏️ EditCoordinator"]:::detail
    Detail -->|"push"| IP2["🖼️ ImagePickerCoordinator"]:::leaf

    Edit -->|"modal"| Address["📍 AddressSearchCoordinator"]:::modal
Loading

- EditCoordinator, EditSceneFactory, EditSceneInput, EditFlow 추가
- AddressSearchCoordinator, AddressSearchSceneFactory, AddressSearchSceneInput 추가
- CalendarFlow, DetailFlow enum 및 FlowEmitting 프로토콜 추가
- CalendarViewControllerDelegate, ImagePickerDelegate 제거
- WeeklyVC, MonthlyVC에 flowSubject/flowPublisher 추가 및 CalendarFlowEmitting 채택
- CalendarViewController에 generic init 도입, 자식 VC publisher 병합
- CalendarSceneFactory가 CalendarViewController 직접 반환
- CalendarCoordinator가 flowPublisher 구독하여 화면 전환 처리
- ImagePickerDelegate, EditDelegate, AddressSearchDelegate 제거
- DetailViewController에 flowSubject/flowPublisher 추가 및 DetailFlowEmitting 채택
- EditFoodRecordViewController에 flowSubject/flowPublisher 추가 및 EditFlowEmitting 채택
- DetailSceneFactory, EditSceneFactory가 UIViewController 직접 반환
- DetailCoordinator, EditCoordinator가 flowPublisher 구독하여 화면 전환 처리
…직접 주입

- DetailSceneFactory: DIContainer 제거, FetchFoodRecordsUseCase / SaveFoodRecordUseCase / DeleteFoodRecordUseCase / PushNotificationObserver 직접 주입, makeDetailScene(input:)에서 ViewModel 자체 생성
- EditSceneFactory: DIContainer 제거, UpdateFoodRecordUseCase / DeleteFoodRecordUseCase 직접 주입
- AddressSearchSceneFactory: DIContainer 제거, SearchAddressUseCase 직접 주입
- SceneDelegate: registerPresentation()에서 DetailVM / EditVM / AddressSearchVM 등록 블록 삭제, makeAppFlowController()에서 각 Factory에 UseCase 직접 전달
- CalendarSceneFactory: WeeklyVM / MonthlyVM 저장 방식 제거, UseCase 8개(requestPhotoAuthorizationUseCase, loadWeeklyCalendarDataUseCase, saveFoodRecordUseCase, pushNotificationObserver, getNicknameUseCase, coachmarkStorage, fetchMonthlyCalendarDaysUseCase, fetchFoodRecordsUseCase) 직접 주입, makeScene()에서 ViewModel 자체 생성
- SceneDelegate: registerPresentation()에서 WeeklyVM / MonthlyVM 등록 블록 삭제, makeAppFlowController()에서 Calendar UseCase를 직접 resolve하여 Factory에 전달, typealias RecordRepo / AssetFetcher 함수 상단 통합
- FlowEmitting 프로토콜(DetailFlowEmitting, EditFlowEmitting, CalendarFlowEmitting) 제거
  → Coordinator가 구체 VC 타입의 flowPublisher에 직접 접근
- SceneProducing 프로토콜 반환 타입을 UIViewController에서 구체 VC 타입으로 변경
  → 런타임 다운캐스팅(as? any FlowEmitting) 제거
- Factories 구조체 프로퍼티를 any SceneProducing에서 구체 Factory 타입으로 변경
  → 컴파일 타임에 타입 정보 보존
@kanghun1121 kanghun1121 requested a review from enebin April 12, 2026 10:44
@kanghun1121 kanghun1121 self-assigned this Apr 12, 2026
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

로그인 상태를 책임지는 객체가 하나 있으면 좋을 거 같아서 추가했습니다!


LoginSession

개요

LoginSession은 로그인/로그아웃/탈퇴 이벤트를 Combine Publisher로 전달하는 이벤트 버스 입니다.
기존 NotificationCenter 기반의 인증 이벤트 전달을 대체한다.

구조

graph LR
    classDef usecase fill:#5B8DEE,stroke:#333,color:#fff
    classDef session fill:#FF6B6B,stroke:#333,color:#fff,font-weight:bold
    classDef consumer fill:#4ECDC4,stroke:#333,color:#fff

    A["FinalizeAppleLoginUseCase"]:::usecase -->|"notifyLoginSuccess(result)"| S["LoginSession"]:::session
    B["LogoutUseCase"]:::usecase -->|"notifyLogout()"| S
    C["WithdrawUserUseCase"]:::usecase -->|"notifyLogout()"| S

    S -->|"loginResultPublisher"| D["AppFlowController"]:::consumer
    S -->|"logoutPublisher"| D
Loading

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

머메이드 색깔 칠해지는건 첨 알았네 ㄷㄷ


import UIKit

public struct Factories {
Copy link
Copy Markdown
Member Author

@kanghun1121 kanghun1121 Apr 12, 2026

Choose a reason for hiding this comment

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

지금 바뀐게 많은데 여기에 추상화까지 더해버리면 이해하기 힘들거 같아서 최대한 추상화는 걷어냈음!

// MARK: - Screen Routing

private extension DetailCoordinator {
func flowBind(vc: DetailViewController<FoodRecordRepositoryImpl<HTTPClient, AuthTokenStorage<KeychainService>>, PushNotificationObserver>) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

이것도 추상화를 걷어내서 구체 타입을 알게 되는데, 이 부분은 추후에 테스트 로직 쓸 때 보완할게!!

Comment on lines +6 to +8
import Combine
import UIKit
import Data
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Data 모듈 알게 되는건 이번에 얘기한 제네릭 Erase 적용하면 해결될 듯!

import Domain
import Foundation

public struct DetailSceneInput {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

DetailVC를 만드는데 필요한 파라미터를 정의한 모델

@kanghun1121 kanghun1121 marked this pull request as ready for review April 12, 2026 10:55
Copy link
Copy Markdown
Member

@enebin enebin left a comment

Choose a reason for hiding this comment

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

아니 해놓고 submit을 안했네.. 일단 이거 하나 있었음

let loginCoordinator = LoginCoordinator(factories: factories)
loginCoordinator.sceneTransitioner = sceneTransitioner
loginCoordinator.parentCoordinator = self
addChild(loginCoordinator)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

pushLoginVC() / pushMainVC() 호출 할 때 기존 childCoordinators를 정리하는 코드가 없어서 로그아웃 → 재로그인하면 MainCoordinator가 중복으로 쌓일 것 같음! remove 해주는 코드가 있으면 좋아 보입니당ㅎㅎ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants