현대적인 Swift Concurrency를 위한 간단하고 강력한 의존성 주입 프레임워크
참고: 읽기(그래프/통계/최적화 여부)는 UnifiedDI/DIContainer의 동기 헬퍼를 사용하세요. 내부 AutoDIOptimizer의 읽기용 API는 스냅샷 기반 내부용이며 외부 직접 호출은 비권장(Deprecated)입니다.
📖 문서: 한국어 | English | 공식 문서 | 로드맵
- ⚡ Swift Concurrency 네이티브: async/await와 Actor 완벽 지원
- 🔒 타입 안전성: 컴파일 타임 타입 검증
- 📝 TCA 스타일 의존성 주입:
@Injected로 KeyPath와 타입 기반 주입 지원 (v3.2.0) - 🏗️ AppDI 간소화:
AppDIManager로 자동 의존성 등록 (v3.2.0) - 🎯 Needle-style Components:
@Component매크로로 10x 빠른 Needle 호환성 (v3.2.1) - ⚡ UnifiedRegistry: TypeSafeRegistry 통합으로 성능과 동시성 안전성 향상 (v3.2.1)
- 🤖 자동 최적화: 의존성 그래프, Actor hop 감지, 타입 안전성 검증 자동화
- 🚀 런타임 핫패스 최적화: TypeID + 락-프리 읽기로 50-80% 성능 향상
- 🧪 테스트 친화적: 의존성 모킹과 격리 지원
dependencies: [
.package(url: "https://github.com/Roy-wonji/WeaveDI.git", from: "3.2.1")
]import WeaveDI
// 1. 앱 초기화 - 자동 의존성 등록
@main
struct MyApp: App {
init() {
WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
await AppDIManager.shared.registerDefaultDependencies()
}
}
}
// 2. TCA 스타일 @Injected 사용 (권장)
class ViewModel {
@Injected(\.userService) var userService
@Injected(ExchangeUseCaseImpl.self) var exchangeUseCase
func loadData() async {
let data = await userService.fetchData()
}
}
// 3. InjectedKey로 의존성 정의
extension InjectedValues {
var userService: UserServiceProtocol {
get { self[UserServiceKey.self] }
set { self[UserServiceKey.self] = newValue }
}
}
struct UserServiceKey: InjectedKey {
static var currentValue: UserServiceProtocol = UserService()
}
// ⚠️ 레거시 Property Wrapper (v3.2.0부터 Deprecated)
class LegacyViewController {
@Inject var userService: UserServiceProtocol? // (v3.2.0부터 Deprecated)
@Factory var generator: PDFGenerator // 유지됨
@SafeInject var apiService: APIServiceProtocol? // (v3.2.0부터 Deprecated)
}
// 마이그레이션 가이드: @Injected를 사용하는 것을 권장합니다.
// 더 나은 타입 안전성과 TCA 스타일 KeyPath 접근을 제공합니다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
// 동기 부트스트랩
await DIContainer.bootstrap { di in
di.register(Logger.self) { ConsoleLogger() }
di.register(Networking.self) { DefaultNetworking() }
}
// 비동기 부트스트랩
let ok = await DIContainer.bootstrapAsync { di in
let flags = try await FeatureFlags.fetch()
di.register(FeatureFlags.self) { flags }
}
// 혼합 부트스트랩
@MainActor
await DIContainer.bootstrapMixed(
sync: { di in di.register(Logger.self) { ConsoleLogger() } },
async: { di in
let analytics = await AnalyticsClient.make()
di.register(AnalyticsClient.self) { analytics }
}
)
// 조건부 부트스트랩
_ = await DIContainer.bootstrapIfNeeded { di in
di.register(Config.self) { LocalConfig() }
}
// 보장/테스트
DIContainer.ensureBootstrapped()
@MainActor
DIContainer.resetForTesting() // DEBUG 전용읽기(그래프/통계/최적화 여부)는 UnifiedDI/DIContainer의 동기 헬퍼 사용을 권장합니다. 내부 AutoDIOptimizer 리더는 스냅샷 기반 내부용이며, 외부 직접 호출은 비권장(Deprecated)입니다.
// 기본 등록 (권장)
let service = UnifiedDI.register(ServiceProtocol.self) {
ServiceImpl()
}
// KeyPath 등록
let repository = UnifiedDI.register(\.userRepository) {
UserRepositoryImpl()
}
// 조건부 등록
let service = UnifiedDI.Conditional.registerIf(
ServiceProtocol.self,
condition: isProduction,
factory: { ProductionService() },
fallback: { MockService() }
)
// 스코프 기반 등록
let sessionService = UnifiedDI.registerScoped(
SessionService.self,
scope: .session
) {
SessionServiceImpl()
}| Property Wrapper | 용도 | 예시 | 상태 |
|---|---|---|---|
@Injected |
TCA 스타일 주입 (권장) | @Injected(\.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/DIContainer의 동기 헬퍼를 사용하세요.
| Deprecated (AutoDIOptimizer) | Replacement |
|---|---|
getCurrentStats() |
UnifiedDI.stats() / DIContainer.getUsageStatistics() |
visualizeGraph() |
UnifiedDI.autoGraph() / DIContainer.getAutoGeneratedGraph() |
getFrequentlyUsedTypes() |
UnifiedDI.optimizedTypes() / DIContainer.getOptimizedTypes() |
getDetectedCircularDependencies() |
UnifiedDI.circularDependencies() / DIContainer.getDetectedCircularDependencies() |
isOptimized(_:) |
UnifiedDI.isOptimized(_:) / DIContainer.isAutoOptimized(_:) |
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를 눌러주세요! ⭐