현대적인 Swift Concurrency를 위한 간단하고 강력한 의존성 주입 프레임워크
참고: 읽기(그래프/통계/최적화 여부)는 UnifiedDI의 동기 헬퍼를 사용하세요. 내부 AutoDIOptimizer의 읽기용 API는 스냅샷 기반 내부용이며 외부 직접 호출은 비권장(Deprecated)입니다.
📖 문서: 한국어 | English | 공식 문서 | 로드맵
- 🚀 TCA 스타일 극단적 단순화:
@Injected var service: Service- 키패스 없이 타입만으로! (v4.0.0) - 🎨 SwiftUI 스타일 선언적 등록:
@DependencyConfigurationResult Builder로 의존성 선언 (v4.0.0) - 🌍 환경별 자동 설정:
DependencyEnvironment.production/development/testing자동 분기 (v4.0.0) - 📦 극단적 경량화: 9개 모듈 → 3개 모듈로 70% 축소, 컴파일 시간 50% 단축 (v4.0.0)
- ⚡ Swift 6 완벽 지원: Strict Concurrency, Modern Macros, Structured Concurrency 네이티브
- 🔒 타입 안전성: 컴파일 타임 타입 검증 및 자동 의존성 주입
- 🤖 자동 최적화: 의존성 그래프, Actor hop 감지, 타입 안전성 검증 자동화
- 🧪 테스트 친화적: 의존성 모킹과 격리 지원, SwiftUI Preview 최적화
| 구분 | Before (v3.x) | After (v4.0.0) | 개선율 |
|---|---|---|---|
| 모듈 수 | 9개 복잡한 모듈 | 3개 핵심 모듈 | 70% 감소 |
| 등록 코드 | 50+ 줄 boilerplate | 5줄 선언적 등록 | 90% 감소 |
| 사용법 | @Injected(\.keyPath) |
@Injected var service: Service |
키패스 불필요 |
| 컴파일 시간 | 기준 | 50% 단축 | 2배 빨라짐 |
| 학습 비용 | 높음 (복잡한 API) | 낮음 (TCA 스타일) | 극단적 단순화 |
- 🚀 @Injected 혁신: 키패스 없이 타입만으로 의존성 주입!
- 🎨 SwiftUI 스타일:
@DependencyConfigurationResult Builder로 선언적 등록 - 🌍 환경별 자동 분기: development/production/testing/preview 자동 선택
- 📦 극단적 경량화: 9개 모듈 → 3개 모듈, 의존성 체인 단순화
- ✅ 100% 호환성: 기존 코드 수정 없이 새로운 기능 사용 가능
dependencies: [
.package(url: "https://github.com/Roy-wonji/WeaveDI.git", from: "4.0.0")
]import WeaveDI
// 1. 📦 SwiftUI 스타일 선언적 등록 (90% 코드 감소!)
@DependencyConfiguration
var appDependencies {
UserServiceImpl() // UserService로 자동 등록
RepositoryImpl() // Repository로 자동 등록
// 환경별 자동 분기
#if DEBUG
ConsoleLogger() as Logger // 개발용 로거
#else
ProductionLogger() as Logger // 프로덕션 로거
#endif
}
// 2. 앱 시작 시 한 줄 설정
appDependencies.configure()
// 3. 🎯 타입만으로 간단한 사용! (키패스 없음)
class ViewModel: ObservableObject {
@Injected var userService: UserService // ✅ 타입만으로!
@Injected var repository: Repository // ✅ 키패스 불필요!
@Injected var logger: Logger // ✅ 환경별 자동 선택!
func loadData() async {
let data = await userService.fetchData()
logger.log("Data loaded!")
}
}
// 4. 🌍 환경별 설정 (자동 분기)
#if DEBUG
DependencyEnvironment.development {
MockUserService() as UserService // 개발용 Mock
ConsoleLogger() as Logger // 디버그 로거
}.configure()
#else
DependencyEnvironment.production {
UserServiceImpl() as UserService // 실제 구현체
ProductionLogger() as Logger // 프로덕션 로거
}.configure()
#endif// 기존 키패스 방식도 그대로 동작
@Injected(\.userService) var userService: UserService // ✅ 여전히 동작
// 기존 UnifiedDI API도 그대로 동작
UnifiedDI.register(UserService.self) { UserServiceImpl() } // ✅ 여전히 동작import WeaveDI
import WeaveDIAppDI
await UnifiedDI.bootstrap { _ in
await UnifiedDI.registerDi { register in
[
register.authRepositoryImplModule(),
register.authUseCaseImplModule()
]
}
}WeaveDI는 컴파일 타임 최적화와 Needle 스타일 아키텍처를 위한 강력한 Swift 매크로를 제공합니다.
import WeaveDI
@Component
public struct UserComponent {
@Provide var userService: UserService = UserService()
@Provide var userRepository: UserRepository = UserRepository()
@Provide var authService: AuthService = AuthService()
}
// 컴파일 타임에 자동 생성됨:
// UnifiedDI.register(UserService.self) { UserService() }
// UnifiedDI.register(UserRepository.self) { UserRepository() }
// UnifiedDI.register(AuthService.self) { AuthService() }@AutoRegister(lifetime: .singleton)
class DatabaseService: DatabaseServiceProtocol {
// 자동으로 UnifiedDI에 등록됨
}
@AutoRegister(lifetime: .transient)
class RequestHandler: RequestHandlerProtocol {
// 매번 새 인스턴스 생성
}@DIActor
public final class AutoMonitor {
public static let shared = AutoMonitor()
// 모든 메서드가 자동으로 스레드 안전해짐
public func onModuleRegistered<T>(_ type: T.Type) {
// Actor 격리된 안전한 작업
}
}@DependencyGraph([
UserService.self: [UserRepository.self, Logger.self],
UserRepository.self: [DatabaseService.self],
DatabaseService.self: [],
Logger.self: []
])
class ApplicationDependencyGraph {
// ✅ 컴파일 타임에 순환 의존성 검증
}| 프레임워크 | 등록 | 해결 | 메모리 | 동시성 |
|---|---|---|---|---|
| Swinject | ~1.2ms | ~0.8ms | 높음 | 수동 락 |
| Needle | ~0.8ms | ~0.6ms | 보통 | 제한적 |
| WeaveDI | ~0.2ms | ~0.1ms | 낮음 | 네이티브 async/await |
더 자세한 매크로 사용법은 WeaveDI 매크로 가이드를 참고하세요.
import WeaveDI
// 동기 부트스트랩
UnifiedDI.bootstrap { di in
di.register { ConsoleLogger() }
di.register { DefaultNetworking() }
}
// 비동기 부트스트랩
await UnifiedDI.bootstrap { di in
let flags = await FeatureFlags.fetch()
di.register { flags }
}읽기(그래프/통계/최적화 여부)는 UnifiedDI의 동기 헬퍼 사용을 권장합니다. 내부 AutoDIOptimizer 리더는 스냅샷 기반 내부용이며, 외부 직접 호출은 비권장(Deprecated)입니다.
// Core 권장: bootstrap 안에서 등록
UnifiedDI.bootstrap { di in
di.register(ServiceProtocol.self) { ServiceImpl() }
di.register(UserRepositoryProtocol.self) { UserRepositoryImpl() }
}| Property Wrapper | 용도 | 예시 | 상태 |
|---|---|---|---|
@Injected |
TCA 스타일 주입 (권장) | @Injected(\.service) var service |
✅ v3.2.0 |
@Dependency |
TCA 스타일 주입 (동일 저장소) | @Dependency(\.service) var service |
✅ v3.2.0 |
@Factory |
팩토리 패턴 (새 인스턴스) | @Factory var generator: Generator |
✅ 유지 |
@Inject |
기본 주입 (레거시) | @Inject var service: Service? |
|
@SafeInject |
안전한 주입 (레거시) | @SafeInject var api: API? |
📖 마이그레이션 가이드: @Injected 문서 | AppDI 간소화
// 일반 해결
let service = UnifiedDI.resolve(ServiceProtocol.self)
// 필수 해결 (없으면 크래시)
let logger = UnifiedDI.requireResolve(Logger.self)
// 기본값 포함 해결
let cache = UnifiedDI.resolve(Cache.self, default: MemoryCache())별도 설정 없이 자동으로 실행됩니다:
// 등록/해결만 하면 자동으로 그래프 생성 및 최적화
let service = UnifiedDI.register(UserService.self) { UserServiceImpl() }
let resolved = UnifiedDI.resolve(UserService.self)
// 자동 수집된 정보는 LogMacro로 자동 출력됩니다
// 📊 Auto tracking registration: UserService
// ⚡ Auto optimized: UserService (10 uses)// 해결하기만 하면 자동으로 Actor hop 감지
await withTaskGroup(of: Void.self) { group in
for _ in 1...10 {
group.addTask {
_ = UnifiedDI.resolve(UserService.self) // Actor hop 자동 감지
}
}
}
// 자동 로그 (5회 이상 hop 발생 시):
// Actor optimization suggestion for UserService: MainActor로 이동 권장// 해결 시 자동으로 타입 안전성 검증
let service = UnifiedDI.resolve(UserService.self)
// 자동 로그 (문제 감지 시):
// Type safety issue: UserService is not Sendable
// Auto safety check: UserService resolved to nil// 여러 번 사용하면 자동으로 최적화됨
for _ in 1...15 {
let service = UnifiedDI.resolve(UserService.self)
}
// 최적화된 타입들은 자동으로 로깅됩니다
// ⚡ Auto optimized: UserService (15 uses)// 사용 통계는 30초마다 자동으로 로깅됩니다
// 📊 [AutoDI] Current stats: ["UserService": 15, "DataRepository": 8]고성능이 요구되는 앱을 위한 미세 최적화 기능입니다.
import WeaveDI
// 최적화 모드 활성화 (기존 API는 그대로 작동)
UnifiedRegistry.shared.enableOptimization()
// 기존 코드는 변경 없이 성능 향상
let service = await UnifiedDI.resolve(UserService.self)- TypeID + 인덱스 접근: 딕셔너리 → 배열 슬롯으로 O(1) 접근
- 락-프리 읽기: 스냅샷 방식으로 읽기 경합 제거
- 인라인 최적화: 함수 호출 오버헤드 축소
- 팩토리 체이닝 제거: 직접 호출 경로로 중간 단계 제거
- 스코프별 저장소: 싱글톤/세션/요청 스코프 분리 최적화
| 시나리오 | 개선율 | 설명 |
|---|---|---|
| 단일 스레드 resolve | 50-80% | TypeID + 직접 접근 |
| 멀티스레드 읽기 | 2-3배 | 락-프리 스냅샷 |
| 복잡한 의존성 | 20-40% | 체인 플래튼화 |
swift run -c release Benchmarks --count 100k --quick자세한 내용은 PERFORMANCE-OPTIMIZATION.md를 참고하세요.
UnifiedDI.setLogLevel(.registration) // 등록만 로깅
UnifiedDI.setLogLevel(.optimization) // 최적화만 로깅
UnifiedDI.setLogLevel(.errors) // 에러/경고만 로깅
UnifiedDI.setLogLevel(.off) // 로깅 끄기// 테스트용 초기화
@MainActor
override func setUp() {
UnifiedDI.releaseAll()
// 테스트용 의존성 등록
_ = UnifiedDI.register(UserService.self) {
MockUserService()
}
}// 🔄 자동 생성된 의존성 그래프
UnifiedDI.autoGraph
// ⚡ 자동 최적화된 타입들
UnifiedDI.optimizedTypes
// 📊 자동 수집된 사용 통계
UnifiedDI.stats
// 🎯 Actor 최적화 제안 목록
UnifiedDI.actorOptimizations
// 🔒 타입 안전성 이슈 목록
UnifiedDI.typeSafetyIssues
//
// ⚡ Actor hop 통계
UnifiedDI.actorHopStats
// 📊 비동기 성능 통계 (밀리초)
UnifiedDI.asyncPerformanceStats아래 AutoDIOptimizer의 읽기용 API는 내부 스냅샷 기반으로 재구성되었으며, 외부 사용은 비권장(Deprecated)입니다. UnifiedDI의 동기 헬퍼를 사용하세요.
| Deprecated (AutoDIOptimizer) | Replacement |
|---|---|
getCurrentStats() |
UnifiedDI.stats() |
visualizeGraph() |
UnifiedDI.autoGraph() |
getFrequentlyUsedTypes() |
UnifiedDI.optimizedTypes() |
getDetectedCircularDependencies() |
UnifiedDI.circularDependencies() |
isOptimized(_:) |
UnifiedDI.isOptimized(_:) |
getActorOptimizationSuggestions() |
UnifiedDI.actorOptimizations |
getDetectedTypeSafetyIssues() |
UnifiedDI.typeSafetyIssues |
getDetectedAutoFixedTypes() |
UnifiedDI.autoFixedTypes |
getActorHopStats() |
UnifiedDI.actorHopStats |
getAsyncPerformanceStats() |
UnifiedDI.asyncPerformanceStats |
getRecentGraphChanges(...) |
UnifiedDI.getGraphChanges(...) |
getCurrentLogLevel() |
UnifiedDI.logLevel / UnifiedDI.getLogLevel() |
내부 용도로는
AutoDIOptimizer.readSnapshot()를 통해 스냅샷을 읽어 필요한 정보를 계산하세요.
실행:
swift run -c release Benchmarks -- --count 100000 --debounce 100
# 여러 조합 테스트(10k/100k/1M × 50/100/200ms)
swift run -c release Benchmarks출력 예시:
📊 Bench: counts=[10000, 100000, 1000000], debounces=[50, 100, 200] (ms)
debounce= 50ms, n= 10000 | total= 12.34ms | p50= 0.010 p95= 0.020 p99= 0.030
...
CSV 저장 및 차트 생성(선택)
# CSV에 누적 저장
swift run -c release Benchmarks -- --count 100000 --debounce 100 --csv bench.csv
# 빠른 확인(첫 조합만)
swift run -c release Benchmarks -- --quick --csv bench.csv
# 텍스트 요약 + PNG 차트(선택, matplotlib 필요)
python3 Scripts/plot_bench.py --csv bench.csv --out bench_plotmatplotlib이 없으면 텍스트 요약만 출력합니다. 설치:
pip install matplotlib
- API 문서
- 로드맵 (v3.2.0) - 현재 버전 및 향후 계획
- @Injected 가이드 - TCA 스타일 의존성 주입
- TCA 통합 가이드 - Composable Architecture에서 WeaveDI 사용
- AppDI 간소화 - 자동 의존성 등록
- 자동 최적화 가이드
- Property Wrapper 가이드
- 마이그레이션 3.0.0
- 의미: 반복·프레임 루프 등 핫패스에서 런타임 해석을 없애 정적 생성/캐시로 대체해 비용을 0에 수렴하게 합니다.
- 사용 위치: 코드에
#if USE_STATIC_FACTORY분기(이미 템플릿 포함) → 빌드 플래그로 on/off - 활성화 방법
- Xcode: Target → Build Settings → Other Swift Flags(Release 또는 전용 스킴)에
-DUSE_STATIC_FACTORY추가 - SPM CLI:
swift build -c release -Xswiftc -DUSE_STATIC_FACTORY- 테스트:
swift test -c release -Xswiftc -DUSE_STATIC_FACTORY
- 테스트:
- Xcode: Target → Build Settings → Other Swift Flags(Release 또는 전용 스킴)에
- 반드시 Release + WMO(Whole‑Module Optimization)에서 측정하세요.
- Xcode: Release 스킴으로 실행(Release는 기본적으로 WMO 적용)
- SPM:
swift build -c release,swift test -c release
- 노이즈 최소화 팁
- 로그 레벨 낮추기:
UnifiedDI.setLogLevel(.errors)또는.off - 자동 최적화 ON:
UnifiedDI.configureOptimization(...),UnifiedDI.setAutoOptimization(true) - 반복 루프는 resolve 캐시(루프 밖 1회 확보 → 안에서는 재사용)
- 로그 레벨 낮추기:
| 특징 | Needle | WeaveDI | 결과 |
|---|---|---|---|
| 컴파일타임 안전성 | ✅ 코드 생성 | ✅ 매크로 기반 | 동등 |
| 런타임 성능 | ✅ 제로 코스트 | ✅ 제로 코스트 + Actor 최적화 | WeaveDI 우수 |
| Swift 6 지원 | ✅ 완벽 네이티브 | WeaveDI 우수 | |
| 코드 생성 필요 | ❌ 필수 | ✅ 선택적 | WeaveDI 우수 |
| 학습 곡선 | ❌ 가파름 | ✅ 점진적 | WeaveDI 우수 |
| 마이그레이션 | ❌ All-or-nothing | ✅ 점진적 | WeaveDI 우수 |
// Needle 수준 성능 + 더 쉬운 사용법
UnifiedDI.enableStaticOptimization() // Needle과 동일한 제로 코스트
@DependencyGraph([ // 컴파일타임 검증
UserService.self: [NetworkService.self, Logger.self]
])
extension WeaveDI {}
print(UnifiedDI.migrateFromNeedle()) // Needle → WeaveDI 마이그레이션 가이드- 별도 설정 없이 Actor hop 감지, 타입 안전성 검증, 성능 최적화가 자동 실행
- 실시간 분석으로 30초마다 최적화 수행 (Needle에 없는 기능)
- 개발자 친화적 제안으로 성능 개선점 자동 안내
- Actor 안전성 자동 검증 및 최적화 제안
- async/await 완벽 지원 (Needle은 제한적)
- Sendable 프로토콜 준수 검증
- 2개 Property Wrapper만으로 모든 주입 패턴 커버 (
@Injected,@Factory)- 참고:
@Inject와@SafeInject는 v3.2.0부터 Deprecated. @Injected 사용 권장
- 참고:
- 타입 안전한 KeyPath 기반 등록
- 직관적인 조건부 등록
MIT License. 자세한 내용은 LICENSE 파일을 참고하세요.
서원지 (Roy, Wonji Suh)
WeaveDI를 더 좋게 만들어주세요!
- 이슈 제기: GitHub Issues에서 버그 리포트나 기능 요청
- Pull Request: 개선사항이나 새로운 기능을 직접 구현해서 기여
- 문서 개선: README나 문서의 오타, 개선사항 제안
git clone https://github.com/Roy-wonji/WeaveDI.git
cd WeaveDI
swift build
swift testWeaveDI와 함께 더 나은 Swift 개발 경험을 만들어가세요! 🚀
⭐ 이 프로젝트가 도움이 되었다면 Star를 눌러주세요! ⭐
