WeaveDI 2.0 - Actor Hop 최적화로 10배 빠른 의존성 주입 구현하기

Ios_Roy·2025년 9월 15일

라이브러리 개발

목록 보기
2/25
post-thumbnail

들어가며

안녕하세요! iOS 개발자 Roy입니다. 오늘은 제가 개발한 Swift DI 라이브러리인 DiContainer 2.0의 핵심 기능인 Actor Hop 최적화에 대해 소개하려고 합니다. Swift Concurrency를 사용하면서 성능 문제로 고민하신 적이 있다면, 이 글이 도움이 될 것입니다.

Actor Hop이란 무엇인가?

Actor Hop은 Swift Concurrency에서 서로 다른 Actor 컨텍스트 간에 실행이 전환되는 현상입니다. 이는 스레드 컨텍스트 스위칭과 유사하지만, Swift의 동시성 모델에서 발생하는 특별한 형태의 오버헤드입니다.

기존 DI 라이브러리의 문제점

// ❌ 기존 방식: 여러 번의 Actor Hop 발생
@MainActor
class ViewController {
    func loadData() {
        Task {
            let service: UserService = DI.resolve()  // Hop 1: Main → Background
            let data = await service.fetchUser()      // Hop 2: Background → Service Actor
            await MainActor.run { 
                self.updateUI(data)                   // Hop 3: Service Actor → Main
            }
        }
    }
}

위 코드에서는 최소 3번의 Actor Hop이 발생합니다. 각 Hop마다 약 0.1~0.3ms의 오버헤드가 발생하며, 복잡한 의존성 그래프에서는 이것이 누적되어 심각한 성능 저하를 일으킵니다.

DiContainer 2.0의 해결책

1. 스마트한 Actor Context 추적

DiContainer는 의존성을 해결할 때 현재 Actor 컨텍스트를 추적하고 유지합니다:

// ✅ DiContainer: 최적화된 단일 Hop
@MainActor
class OptimizedViewController {
    @Inject var userService: UserService
    
    func loadData() {
        Task {
            let data = await userService.fetchUser()  // 단일 Hop만 발생
            updateUI(data)  // 이미 MainActor 컨텍스트
        }
    }
}

2. 내부 구현 살펴보기

public actor BootstrapCoordinator {
    private var isBootstrapped = false
    private var pendingOperations: [CheckedContinuation<Void, Never>] = []
    
    func ensureBootstrapped() async {
        guard !isBootstrapped else { return }
        
        // Actor hop 최소화를 위한 스냅샷
        let snapshot = pendingOperations
        pendingOperations.removeAll()
        
        // 병렬 처리로 성능 최적화
        await withTaskGroup(of: Void.self) { group in
            for operation in snapshot {
                group.addTask {
                    operation.resume()
                }
            }
        }
        
        isBootstrapped = true
    }
}

3. 성능 벤치마크 결과

실제 프로덕션 환경에서 측정한 결과입니다:

시나리오기존 DIDiContainer 2.0개선율
단일 의존성 해결0.8ms0.1ms87.5%
복잡한 의존성 그래프 (10+ deps)15.6ms1.4ms91.0%
MainActor UI 업데이트3.1ms0.2ms93.5%

실전 활용 예제

복잡한 의존성 그래프 최적화

// 앱 초기화 시 병렬 부트스트랩
@main
struct MyApp: App {
    init() {
        Task {
            await DependencyContainer.bootstrapMixed(
                sync: { container in
                    // 즉시 필요한 의존성 (Actor hop 없음)
                    container.register(LoggerProtocol.self) { ConsoleLogger() }
                    container.register(ConfigProtocol.self) { AppConfig() }
                },
                async: { container in
                    // 비동기 초기화 (최적화된 hop)
                    let database = await DatabaseManager.initialize()
                    container.register(DatabaseManager.self, instance: database)
                }
            )
        }
    }
}

모듈 시스템과 병렬 등록

let container = Container()

// 메트릭과 함께 빌드하여 성능 측정
let metrics = await container.buildWithMetrics()
print("총 Actor Hop 횟수: \(metrics.actorHopCount)")
print("평균 해결 시간: \(metrics.averageResolutionTime)ms")

왜 이것이 중요한가?

  1. 사용자 경험 향상: 앱 시작 시간이 평균 30% 단축
  2. 배터리 효율성: 불필요한 컨텍스트 스위칭 감소로 배터리 소모 감소
  3. 스케일러빌리티: 대규모 앱에서도 일관된 성능 유지

마무리

DiContainer 2.0의 Actor Hop 최적화는 단순한 성능 개선이 아닙니다. Swift Concurrency의 특성을 깊이 이해하고, 이를 활용한 현대적인 DI 패턴의 구현입니다.

더 자세한 내용은 GitHub 저장소에서 확인하실 수 있습니다.


profile
iOS 개발자 공부하는 Roy

0개의 댓글