Skip to content

아키텍처

Clean Architecture + MVVM

truloop iOS는 Clean Architecture 원칙에 기반한 레이어 분리와 MVVM 패턴의 프레젠테이션 구조를 결합하여 사용합니다.

레이어 다이어그램

레이어별 역할

1. App (Presentation Entry)

앱의 진입점으로, CompositionRoot를 통해 전체 의존성을 구성하고 화면 라우팅을 관리합니다.

구성 요소역할
TRULOOP.swiftSwiftUI App 진입점
CompositionRootSwinject Container 구성, 모든 Assembly 등록
MainTabBar/메인 탭 네비게이션 (MainTabBarController, LegacyMainTabBarController)
AppScheme/URL Scheme / Deep Link 처리 (AppSchemeManager, AppSchemeHandler)
Branch/Branch SDK 딥링크 핸들링 (BranchHandler)
Managers/이미지 업로드, Live Activity, 네트워크 에러 라우팅 등
LiveActivity/Live Activity Attributes 정의
Assembly/AppAssembly - App 레이어 DI 등록

2. Features (Presentation)

Feature 모듈의 내부 구조는 모듈마다 다릅니다.

Home Feature (대규모 모듈):

디렉토리역할
Assembly/Swinject Assembly - 해당 Feature의 의존성 등록
PresentationSources/ViewModel 및 Presentation 모델
UISources/SwiftUI View 및 Builder
Protocols/Feature 내부 프로토콜 정의

OnBoard Feature (화면별 분리 구조):

디렉토리역할
Assembly/Swinject Assembly
Views/{화면명}/화면별로 Builder, View, ViewModel을 함께 배치

정보

Feature 모듈은 Domain, Repository에 의존하며, 다른 Feature 모듈에 직접 의존하지 않습니다. Feature 간 통신이 필요한 경우 OnBoardInterface와 같은 Interface 모듈을 통해 프로토콜 기반으로 통신합니다.

3. Domain

비즈니스 로직의 핵심 레이어입니다. 외부 프레임워크에 의존하지 않습니다.

구성 요소역할
Entities/비즈니스 모델 (User, Loop, Channel, Mission 등)
Repository/Repository 프로토콜 정의 (AuthRepository, HomeRepository 등)
UseCase/비즈니스 로직 단위 (AccountUseCase, HomeUseCase 등)
Assembly/Domain 레이어 DI 등록

4. Repository (Data)

Domain에서 정의한 Repository 프로토콜의 구현체를 제공합니다.

구성 요소역할
DataSource/Repository 프로토콜 구현체 (예: AuthDataSourceImpl)
Requests/서버 요청 DTO
Responses/서버 응답 DTO
Assembly/Repository 레이어 DI 등록

5. Networking (Infrastructure)

Moya 기반 네트워크 인프라를 제공합니다.

구성 요소역할
API/Moya TargetType 구현 (AuthAPI, HomeAPI, DeviceAPI, FriendAPI, LiveActivityAPI 등)
Networking/Networking 프로토콜, NetworkingImpl, NetworkingError, TruloopError
Plugins/AuthInterceptor, ValidatingSession
Assembly/Networking 레이어 DI 등록

데이터 흐름

ViewModel은 비즈니스 로직이 필요한 경우 UseCase를 통해, 단순 데이터 조회의 경우 Repository를 직접 호출합니다.

정보

실제 코드에서 HomeViewModelaccountUseCase를 통해 계정 정보를 조회하면서도, homeRepository.fetchLoops()를 직접 호출하여 룹 목록을 가져옵니다. UseCase를 반드시 거치는 것은 아닙니다.

요청 흐름 예시

  1. View: 사용자가 룹 목록 화면에서 Pull-to-refresh 실행
  2. ViewModel: homeRepository.loops(...) 호출 (UseCase 없이 Repository 직접 호출)
  3. Repository (HomeDataSourceImpl): HomeAPI.loops(...) 타겟으로 Networking 호출
  4. Networking (NetworkingImpl): MoyaProvider<MultiTarget>을 통해 실제 HTTP 요청 수행
  5. 응답 처리: LoopsResponse DTO를 Loops Entity로 변환하여 반환
  6. ViewModel: @Published 프로퍼티 업데이트로 View에 반영

Builder 패턴

Feature 모듈의 각 화면은 Builder 패턴을 사용하여 생성됩니다:

swift
// Protocol 정의
protocol HomeBuildable {
    func build() -> HomeView
}

// Builder 구현
struct HomeBuilder: HomeBuildable {
    let dependency: HomeDependency

    func build() -> HomeView {
        let viewModel = HomeViewModel(
            homeRepository: dependency.homeRepository,
            // ...
        )
        return HomeView(viewModel: viewModel)
    }
}

이 패턴을 통해:

  • 화면 생성 시 필요한 의존성을 명확히 선언
  • Swinject Assembly에서 Builder를 등록하여 DI 체인 구성
  • 테스트 시 Mock Builder를 주입 가능

변경 이력

날짜변경 내용
2026-03-10소스 코드 기반 검증: 데이터 흐름 다이어그램에 ViewModel→Repository 직접 호출 경로 반영, Feature 모듈별 내부 구조 차이 명시, App/Networking 레이어 구성 요소 보완