Skip to content

Commit 4203ba2

Browse files
authored
Feat: Dependency Assembler 구현 (#T3-63)
* Refactor: DIContainer 로직 수정 (#T3-63) - 기존 Any 타입으로 인스턴스를 저장하던 의존성을 factory closure 형태로 변경 - resolve 시 클로저 실행을 통해 인스턴스를 생성하도록 개선 * Feat: DependencyAssemblerProtocol 구현 (#T3-63) * Feat: 각 모듈별 DependencyAssembler 구현 - Network, Persistence, DataSource, Domain, Presentation 모듈의 DependencyAssembler 구현 - previous 의존성을 명시하여 DI 등록 순서를 보장하도록 함 * Feat: App 모듈에 DependencyInjection 함수 구현 (#T3-63) * Del: DataSoure의 불필요한 Resources 파일 제거 * Refactor: NetworkService 테스트 가능한 구조를 위해 NetworkProviderProtocol 구현 및 채택
1 parent f735937 commit 4203ba2

14 files changed

Lines changed: 209 additions & 49 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// DependencyInjection.swift
3+
// App
4+
//
5+
// Created by 최정인 on 6/26/25.
6+
//
7+
8+
import Foundation
9+
import DataSource
10+
import Domain
11+
import Presentation
12+
import NetworkService
13+
import Persistence
14+
import Shared
15+
16+
extension DIContainer {
17+
func dependencyInjection() {
18+
let networkAssembler = NetworkDependencyAssembler()
19+
let persistenceAssembler = PersistenceDependencyAssembler()
20+
let dataSourceAssembler = DataSourceDependencyAssembler(preAssemblers: [networkAssembler, persistenceAssembler])
21+
let domainAssembler = DomainDependencyAssembler(preAssembler: dataSourceAssembler)
22+
let presentationAssembler = PresentationDependencyAssembler(preAssembler: domainAssembler)
23+
presentationAssembler.assemble()
24+
}
25+
}

Projects/App/Sources/SceneDelegate.swift

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,21 @@
66
//
77

88
import UIKit
9-
import DataSource
10-
import Domain
119
import Presentation
12-
import NetworkService
1310
import Shared
1411

1512
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
1613
var window: UIWindow?
1714

18-
// TODO: DI 수정 필요
19-
private func dependencyInjection() {
20-
let networkService = NetworkService()
21-
DIContainer.shared.register(type: NetworkServiceProtocol.self, instance: networkService)
22-
23-
let testRepository: TestRepositoryProtocol = TestRepository(networkService: networkService)
24-
DIContainer.shared.register(type: TestRepositoryProtocol.self, instance: testRepository)
25-
26-
let testUseCase: TestUseCase = TestUseCase(testRepository: testRepository)
27-
DIContainer.shared.register(type: TestUseCaseProtocol.self, instance: testUseCase)
28-
}
29-
3015
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
3116
guard let windowScene = (scene as? UIWindowScene) else { return }
3217
let window = UIWindow(windowScene: windowScene)
33-
dependencyInjection()
3418

35-
let testUseCase = DIContainer.shared.resolve(type: TestUseCaseProtocol.self)!
36-
let homeViewModel = HomeViewModel(testUseCase: testUseCase)
19+
DIContainer.shared.dependencyInjection()
20+
guard let homeViewModel = DIContainer.shared.resolve(type: HomeViewModel.self) else {
21+
fatalError("homeViewModel 의존성이 등록되지 않았습니다.")
22+
}
23+
3724
let navigationController = UINavigationController(rootViewController: HomeViewController(viewModel: homeViewModel))
3825
navigationController.isNavigationBarHidden = true
3926
window.rootViewController = navigationController

Projects/DataSource/Resources/Info.plist

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// DataSourceDependencyAssembler.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 6/26/25.
6+
//
7+
8+
import Foundation
9+
import Domain
10+
import Shared
11+
12+
public struct DataSourceDependencyAssembler: DependencyAssemblerProtocol {
13+
private let preAssemblers: [DependencyAssemblerProtocol]
14+
15+
public init(preAssemblers: [DependencyAssemblerProtocol]) {
16+
self.preAssemblers = preAssemblers
17+
}
18+
19+
public func assemble() {
20+
preAssemblers.forEach { assembler in
21+
assembler.assemble()
22+
}
23+
24+
guard
25+
let networkService = DIContainer.shared.resolve(type: NetworkServiceProtocol.self),
26+
let keychainStorage = DIContainer.shared.resolve(type: KeychainStorageProtocol.self),
27+
let userDefaultsStorage = DIContainer.shared.resolve(type: UserDefaultsStorageProtocol.self)
28+
else { return }
29+
30+
DIContainer.shared.register(type: TestRepositoryProtocol.self) { _ in
31+
return TestRepository(networkService: networkService)
32+
}
33+
}
34+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// DomainDependencyAssembler.swift
3+
// Domain
4+
//
5+
// Created by 최정인 on 6/26/25.
6+
//
7+
8+
import Foundation
9+
import Shared
10+
11+
public struct DomainDependencyAssembler: DependencyAssemblerProtocol {
12+
private let preAssembler: DependencyAssemblerProtocol
13+
14+
public init(preAssembler: DependencyAssemblerProtocol) {
15+
self.preAssembler = preAssembler
16+
}
17+
18+
public func assemble() {
19+
preAssembler.assemble()
20+
21+
DIContainer.shared.register(type: TestUseCaseProtocol.self) { container in
22+
guard let testRepository = container.resolve(type: TestRepositoryProtocol.self) else {
23+
return
24+
}
25+
return TestUseCase(testRepository: testRepository)
26+
}
27+
}
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// URLSession+.swift
3+
// NetworkService
4+
//
5+
// Created by 최정인 on 6/27/25.
6+
//
7+
8+
import Foundation
9+
10+
extension URLSession: NetworkProviderProtocol { }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// NetworkDependencyAssembler.swift
3+
// NetworkService
4+
//
5+
// Created by 최정인 on 6/26/25.
6+
//
7+
8+
import Foundation
9+
import DataSource
10+
import Shared
11+
12+
public struct NetworkDependencyAssembler: DependencyAssemblerProtocol {
13+
14+
public init() { }
15+
16+
public func assemble() {
17+
DIContainer.shared.register(type: NetworkServiceProtocol.self) { _ in
18+
return NetworkService()
19+
}
20+
}
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// NetworkProviderProtocol.swift
3+
// NetworkService
4+
//
5+
// Created by 최정인 on 6/27/25.
6+
//
7+
8+
import Foundation
9+
10+
public protocol NetworkProviderProtocol {
11+
func data(for urlRequest: URLRequest) async throws -> (Data, URLResponse)
12+
}

Projects/NetworkService/Sources/NetworkService.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import Foundation
99
import DataSource
1010

1111
public final class NetworkService: NetworkServiceProtocol {
12-
private let session = URLSession.shared
12+
private let networkProvider: NetworkProviderProtocol
1313
private let decoder = JSONDecoder()
1414

15-
public init() { }
15+
public init(networkProvider: NetworkProviderProtocol = URLSession.shared) {
16+
self.networkProvider = networkProvider
17+
}
1618

1719
public func request<T: Decodable>(endpoint: Endpoint, type: T.Type) async throws -> T {
1820
let request = try endpoint.makeURLRequest()
19-
let (data, response) = try await session.data(for: request)
21+
let (data, response) = try await networkProvider.data(for: request)
2022

2123
guard let httpResponse = response as? HTTPURLResponse else {
2224
throw NetworkError.invalidResponse
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// PersistenceDependencyAssembler.swift
3+
// Persistence
4+
//
5+
// Created by 최정인 on 6/26/25.
6+
//
7+
8+
import Foundation
9+
import DataSource
10+
import Shared
11+
12+
public struct PersistenceDependencyAssembler: DependencyAssemblerProtocol {
13+
14+
public init() { }
15+
16+
public func assemble() {
17+
DIContainer.shared.register(type: KeychainStorageProtocol.self) { _ in
18+
return KeychainStorage()
19+
}
20+
21+
DIContainer.shared.register(type: UserDefaultsStorageProtocol.self) { _ in
22+
return UserDefaultsStorage()
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)