Skip to content

Latest commit

 

History

History
374 lines (297 loc) · 8.73 KB

File metadata and controls

374 lines (297 loc) · 8.73 KB

AppDI 간소화 가이드

개요

WeaveDI 3.2.0은 자동 의존성 등록을 도입하여, 수동으로 registerRepositories()registerUseCases()를 호출할 필요가 없어졌습니다. 프레임워크가 개선된 registerAllDependencies() 시스템을 통해 이러한 메서드를 자동으로 호출합니다.

변경 사항

이전 (수동 등록)

// ❌ 이전 방식 - 수동 호출 필요
@main
struct MyApp: App {
    init() {
        Task {
            await WeaveDI.Container.bootstrap { container in
                // 수동 등록
                await WeaveDI.Container.registerRepositories()
                await WeaveDI.Container.registerUseCases()
            }
        }
    }
}

이후 (자동 등록)

// ✅ 새로운 방식 - 자동 등록
@main
struct MyApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }
}

작동 방식

AppDIManager.shared.registerDefaultDependencies() 메서드가 registerRepositories()registerUseCases()를 자동으로 호출합니다:

// AppDIManager가 자동으로 모든 의존성을 등록
public actor AppDIManager {
    public static let shared = AppDIManager()

    public func registerDefaultDependencies() async {
        // 이 메서드들을 자동으로 호출
        await WeaveDI.Container.registerRepositories()
        await WeaveDI.Container.registerUseCases()

        #if DEBUG
        print("✅ AppDIManager.registerDefaultDependencies() 완료")
        #endif
    }
}

모듈 기반 등록 패턴

모듈 정의

extension WeaveDI.Container {
    private static let helper = RegisterModule()

    /// 📦 Repository 등록
    static func registerRepositories() async {
        let repositories = [
            helper.exchangeRepositoryModule(),
            helper.userRepositoryModule(),
            // 추가 repository 모듈...
        ]

        await repositories.asyncForEach { module in
            await module.register()
        }
    }

    /// 🔧 UseCase 등록
    static func registerUseCases() async {
        let useCases = [
            helper.exchangeUseCaseModule(),
            helper.userUseCaseModule(),
            // 추가 useCase 모듈...
        ]

        await useCases.asyncForEach { module in
            await module.register()
        }
    }
}

모듈 Extension 생성

extension RegisterModule {
    var exchangeUseCaseModule: @Sendable () -> Module {
        makeUseCaseWithRepository(
            ExchangeRateInterface.self,
            repositoryProtocol: ExchangeRateInterface.self,
            repositoryFallback: MockExchangeRepositoryImpl(),
            factory: { repo in
                ExchangeUseCaseImpl(repository: repo)
            }
        )
    }

    var exchangeRepositoryModule: @Sendable () -> Module {
        makeDependency(ExchangeRateInterface.self) {
            ExchangeRepositoryImpl()
        }
    }
}

이점

1. 보일러플레이트 감소

  • 이전: 모든 앱에서 수동 등록 호출 필요
  • 이후: 프레임워크가 자동으로 등록 처리

2. 더 깔끔한 앱 초기화

// 깔끔하고 간단한 앱 초기화
@main
struct CurrencyConverterApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

3. 더 나은 구조화

모듈 extension을 사용하여 기능별로 의존성 구성:

// 인증 모듈
extension RegisterModule {
    var authRepositoryModule: @Sendable () -> Module { ... }
    var authUseCaseModule: @Sendable () -> Module { ... }
}

// 사용자 모듈
extension RegisterModule {
    var userRepositoryModule: @Sendable () -> Module { ... }
    var userUseCaseModule: @Sendable () -> Module { ... }
}

// 환율 모듈
extension RegisterModule {
    var exchangeRepositoryModule: @Sendable () -> Module { ... }
    var exchangeUseCaseModule: @Sendable () -> Module { ... }
}

마이그레이션 가이드

1단계: 수동 호출 제거

앱 초기화에서 명시적인 registerRepositories()registerUseCases() 호출 제거:

// ❌ 이 라인들을 제거하세요
await WeaveDI.Container.registerRepositories()
await WeaveDI.Container.registerUseCases()

2단계: Extension 존재 확인

WeaveDI.Container extension이 기본 구현을 오버라이드하는지 확인:

extension WeaveDI.Container {
    static func registerRepositories() async {
        // Repository 등록 로직
    }

    static func registerUseCases() async {
        // UseCase 등록 로직
    }
}

3단계: 앱 테스트

bootstrapInTaskAppDIManager를 사용하여 의존성 등록:

@main
struct MyApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }
}

고급: asyncForEach

병렬 모듈 등록을 위해 asyncForEach 사용:

static func registerRepositories() async {
    let repositories = [
        helper.exchangeRepositoryModule(),
        helper.userRepositoryModule(),
        helper.authRepositoryModule(),
    ]

    // 모든 모듈을 병렬로 등록
    await repositories.asyncForEach { module in
        await module.register()
    }
}

실전 예제

// AutoDIRegistry.swift
import WeaveDI

extension WeaveDI.Container {
    private static let helper = RegisterModule()

    static func registerRepositories() async {
        let repositories = [
            helper.exchangeRepositoryModule(),
        ]

        await repositories.asyncForEach { module in
            await module.register()
        }
    }

    static func registerUseCases() async {
        let useCases = [
            helper.exchangeUseCaseModule(),
        ]

        await useCases.asyncForEach { module in
            await module.register()
        }
    }
}

extension RegisterModule {
    var exchangeUseCaseModule: @Sendable () -> Module {
        makeUseCaseWithRepository(
            ExchangeRateInterface.self,
            repositoryProtocol: ExchangeRateInterface.self,
            repositoryFallback: MockExchangeRepositoryImpl(),
            factory: { repo in
                ExchangeUseCaseImpl(repository: repo)
            }
        )
    }

    var exchangeRepositoryModule: @Sendable () -> Module {
        makeDependency(ExchangeRateInterface.self) {
            ExchangeRepositoryImpl()
        }
    }
}

// App.swift
@main
struct CurrencyConverterApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

모범 사례

1. 기능별 구조화

관련 의존성을 기능 기반 모듈로 그룹화:

// 기능: 인증
extension RegisterModule {
    var authModule: @Sendable () -> [Module] {
        [
            authRepositoryModule(),
            authUseCaseModule(),
        ]
    }
}

2. 명확한 이름 사용

// ✅ 좋음 - 명확하고 설명적
var exchangeRateRepositoryModule: @Sendable () -> Module { ... }
var userAuthenticationUseCaseModule: @Sendable () -> Module { ... }

// ❌ 피함 - 불명확한 이름
var repo1Module: @Sendable () -> Module { ... }
var module2: @Sendable () -> Module { ... }

3. 의존성 문서화

extension RegisterModule {
    /// 환율 repository 모듈
    /// 통화 환율 데이터 접근 제공
    var exchangeRepositoryModule: @Sendable () -> Module {
        makeDependency(ExchangeRateInterface.self) {
            ExchangeRepositoryImpl()
        }
    }
}

문제 해결

의존성이 등록되지 않음

의존성이 자동으로 등록되지 않는 경우:

  1. registerRepositories()registerUseCases() extension이 있는지 확인
  2. bootstrap이 호출되고 있는지 확인
  3. Extension이 앱과 같은 타겟에 있는지 확인

디버그 로깅

등록 진행 상황을 보기 위해 디버그 로깅 활성화:

#if DEBUG
extension WeaveDI.Container {
    static func registerRepositories() async {
        print("📦 Repository 등록 중...")
        // ... 등록 로직
        print("✅ Repository 등록 완료")
    }
}
#endif

참고