안녕하세요! iOS 개발자 Roy입니다. 오늘은 제가 개발한 Swift DI 라이브러리인 DiContainer 2.0의 핵심 기능인 Actor Hop 최적화에 대해 소개하려고 합니다. Swift Concurrency를 사용하면서 성능 문제로 고민하신 적이 있다면, 이 글이 도움이 될 것입니다.
Actor Hop은 Swift Concurrency에서 서로 다른 Actor 컨텍스트 간에 실행이 전환되는 현상입니다. 이는 스레드 컨텍스트 스위칭과 유사하지만, Swift의 동시성 모델에서 발생하는 특별한 형태의 오버헤드입니다.
// ❌ 기존 방식: 여러 번의 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는 의존성을 해결할 때 현재 Actor 컨텍스트를 추적하고 유지합니다:
// ✅ DiContainer: 최적화된 단일 Hop
@MainActor
class OptimizedViewController {
@Inject var userService: UserService
func loadData() {
Task {
let data = await userService.fetchUser() // 단일 Hop만 발생
updateUI(data) // 이미 MainActor 컨텍스트
}
}
}
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
}
}
실제 프로덕션 환경에서 측정한 결과입니다:
| 시나리오 | 기존 DI | DiContainer 2.0 | 개선율 |
|---|---|---|---|
| 단일 의존성 해결 | 0.8ms | 0.1ms | 87.5% |
| 복잡한 의존성 그래프 (10+ deps) | 15.6ms | 1.4ms | 91.0% |
| MainActor UI 업데이트 | 3.1ms | 0.2ms | 93.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")
DiContainer 2.0의 Actor Hop 최적화는 단순한 성능 개선이 아닙니다. Swift Concurrency의 특성을 깊이 이해하고, 이를 활용한 현대적인 DI 패턴의 구현입니다.
더 자세한 내용은 GitHub 저장소에서 확인하실 수 있습니다.