From 16c0a43f48ffb8f2998d31f2224b48f919186b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 17:09:33 +0900 Subject: [PATCH 01/10] =?UTF-8?q?refactor.=20expense,=20settlement=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SettlementRepositoryProtocol.swift | 17 +++++++++++ .../Expense/CreateExpenseUseCase.swift | 29 ++++++++----------- .../Expense/DeleteExpenseUseCase.swift | 8 ++--- .../Expense/FetchTravelExpenseUseCase.swift | 8 ++--- .../Expense/UpdateExpenseUseCase.swift | 8 ++--- .../Settlement/FetchSettlementUseCase.swift | 15 +++++----- .../FetchSettlementUseCaseProtocol.swift | 12 -------- .../MockFetchSettlementUseCase.swift | 0 .../Application/LiveDependencies.swift | 23 ++++++--------- 9 files changed, 52 insertions(+), 68 deletions(-) delete mode 100644 Domain/Sources/UseCase/Settlement/FetchSettlementUseCaseProtocol.swift rename Domain/Sources/UseCase/Settlement/{ => Mock}/MockFetchSettlementUseCase.swift (100%) diff --git a/Domain/Sources/Repository/SettlementRepositoryProtocol.swift b/Domain/Sources/Repository/SettlementRepositoryProtocol.swift index b769112d..53d14343 100644 --- a/Domain/Sources/Repository/SettlementRepositoryProtocol.swift +++ b/Domain/Sources/Repository/SettlementRepositoryProtocol.swift @@ -6,7 +6,24 @@ // import Foundation +import Dependencies public protocol SettlementRepositoryProtocol { func fetchSettlement(travelId: String) async throws -> TravelSettlement } + +// MARK: - Dependency +private enum SettlementRepositoryDepensencyKey: DependencyKey { + static let liveValue: SettlementRepositoryProtocol = { + fatalError("SettlementRepository liveValue not implemented") + }() + + static let testValue: SettlementRepositoryProtocol = MockSettlementRepository() +} + +extension DependencyValues { + public var settlementRepository: SettlementRepositoryProtocol { + get { self[SettlementRepositoryDepensencyKey.self] } + set { self[SettlementRepositoryDepensencyKey.self] = newValue } + } +} diff --git a/Domain/Sources/UseCase/Expense/CreateExpenseUseCase.swift b/Domain/Sources/UseCase/Expense/CreateExpenseUseCase.swift index b5a3fffd..b2d8a6e6 100644 --- a/Domain/Sources/UseCase/Expense/CreateExpenseUseCase.swift +++ b/Domain/Sources/UseCase/Expense/CreateExpenseUseCase.swift @@ -8,33 +8,28 @@ import Foundation import Dependencies -public struct CreateExpenseUseCase { - private let repository: ExpenseRepositoryProtocol - - public init(repository: ExpenseRepositoryProtocol) { - self.repository = repository - } +public protocol CreateExpenseUseCaseProtocol { + func execute(travelId: String, input: ExpenseInput) async throws +} +public struct CreateExpenseUseCase: CreateExpenseUseCaseProtocol { + @Dependency(\.expenseRepository) private var repository: ExpenseRepositoryProtocol + public func execute(travelId: String, input: ExpenseInput) async throws { try input.validate() try await repository.save(travelId: travelId, input: input) } } -extension CreateExpenseUseCase: DependencyKey { - public static var liveValue: CreateExpenseUseCase { - @Dependency(\.expenseRepository) var repository - return CreateExpenseUseCase(repository: repository) - } +public enum CreateExpenseUseCaseDependencyKey: DependencyKey { + public static var liveValue: CreateExpenseUseCaseProtocol = CreateExpenseUseCase() - public static var testValue: CreateExpenseUseCase { - CreateExpenseUseCase(repository: MockExpenseRepository()) - } + public static var testValue: CreateExpenseUseCaseProtocol = CreateExpenseUseCase() } extension DependencyValues { - public var createExpenseUseCase: CreateExpenseUseCase { - get { self[CreateExpenseUseCase.self] } - set { self[CreateExpenseUseCase.self] = newValue } + public var createExpenseUseCase: CreateExpenseUseCaseProtocol { + get { self[CreateExpenseUseCaseDependencyKey.self] } + set { self[CreateExpenseUseCaseDependencyKey.self] = newValue } } } diff --git a/Domain/Sources/UseCase/Expense/DeleteExpenseUseCase.swift b/Domain/Sources/UseCase/Expense/DeleteExpenseUseCase.swift index ed343e1c..5d505262 100644 --- a/Domain/Sources/UseCase/Expense/DeleteExpenseUseCase.swift +++ b/Domain/Sources/UseCase/Expense/DeleteExpenseUseCase.swift @@ -13,11 +13,7 @@ public protocol DeleteExpenseUseCaseProtocol { } public struct DeleteExpenseUseCase: DeleteExpenseUseCaseProtocol { - private let repository: ExpenseRepositoryProtocol - - public init(repository: ExpenseRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.expenseRepository) private var repository: ExpenseRepositoryProtocol public func execute(travelId: String, expenseId: String) async throws { try await repository.delete(travelId: travelId, expenseId: expenseId) @@ -26,7 +22,7 @@ public struct DeleteExpenseUseCase: DeleteExpenseUseCaseProtocol { // MARK: - DependencyKey public enum DeleteExpenseUseCaseDependencyKey: DependencyKey { - public static var liveValue: any DeleteExpenseUseCaseProtocol = MockDeleteExpenseUseCase() + public static var liveValue: any DeleteExpenseUseCaseProtocol = DeleteExpenseUseCase() public static var testValue: any DeleteExpenseUseCaseProtocol = MockDeleteExpenseUseCase() diff --git a/Domain/Sources/UseCase/Expense/FetchTravelExpenseUseCase.swift b/Domain/Sources/UseCase/Expense/FetchTravelExpenseUseCase.swift index 47c9aa56..8b476e45 100644 --- a/Domain/Sources/UseCase/Expense/FetchTravelExpenseUseCase.swift +++ b/Domain/Sources/UseCase/Expense/FetchTravelExpenseUseCase.swift @@ -13,11 +13,7 @@ public protocol FetchTravelExpenseUseCaseProtocol { } public struct FetchTravelExpenseUseCase: FetchTravelExpenseUseCaseProtocol { - private let repository: ExpenseRepositoryProtocol - - public init(repository: ExpenseRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.expenseRepository) private var repository: ExpenseRepositoryProtocol public func execute( travelId: String, @@ -54,7 +50,7 @@ public struct FetchTravelExpenseUseCase: FetchTravelExpenseUseCaseProtocol { // MARK: - DependencyKey public enum FetchTravelExpenseUseCaseDependencyKey: DependencyKey { - public static var liveValue: any FetchTravelExpenseUseCaseProtocol = MockFetchTravelExpenseUseCase() + public static var liveValue: any FetchTravelExpenseUseCaseProtocol = FetchTravelExpenseUseCase() public static var testValue: any FetchTravelExpenseUseCaseProtocol = MockFetchTravelExpenseUseCase() diff --git a/Domain/Sources/UseCase/Expense/UpdateExpenseUseCase.swift b/Domain/Sources/UseCase/Expense/UpdateExpenseUseCase.swift index cb49e9e4..2d243220 100644 --- a/Domain/Sources/UseCase/Expense/UpdateExpenseUseCase.swift +++ b/Domain/Sources/UseCase/Expense/UpdateExpenseUseCase.swift @@ -13,11 +13,7 @@ public protocol UpdateExpenseUseCaseProtocol { } public struct UpdateExpenseUseCase: UpdateExpenseUseCaseProtocol { - private let repository: ExpenseRepositoryProtocol - - public init(repository: ExpenseRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.expenseRepository) private var repository: ExpenseRepositoryProtocol public func execute(travelId: String, expenseId: String, input: ExpenseInput) async throws { try input.validate() @@ -27,7 +23,7 @@ public struct UpdateExpenseUseCase: UpdateExpenseUseCaseProtocol { // MARK: - DependencyKey public enum UpdateExpenseUseCaseDependencyKey: DependencyKey { - public static var liveValue: any UpdateExpenseUseCaseProtocol = MockUpdateExpenseUseCase() + public static var liveValue: any UpdateExpenseUseCaseProtocol = UpdateExpenseUseCase() public static var testValue: any UpdateExpenseUseCaseProtocol = MockUpdateExpenseUseCase() diff --git a/Domain/Sources/UseCase/Settlement/FetchSettlementUseCase.swift b/Domain/Sources/UseCase/Settlement/FetchSettlementUseCase.swift index 5195e38e..dd044f26 100644 --- a/Domain/Sources/UseCase/Settlement/FetchSettlementUseCase.swift +++ b/Domain/Sources/UseCase/Settlement/FetchSettlementUseCase.swift @@ -6,14 +6,15 @@ // import Foundation -import ComposableArchitecture +import Dependencies -public struct FetchSettlementUseCase: FetchSettlementUseCaseProtocol { - private let repository: SettlementRepositoryProtocol +public protocol FetchSettlementUseCaseProtocol { + func execute(travelId: String) async throws -> TravelSettlement +} - public init(repository: SettlementRepositoryProtocol) { - self.repository = repository - } + +public struct FetchSettlementUseCase: FetchSettlementUseCaseProtocol { + @Dependency(\.settlementRepository) private var repository: SettlementRepositoryProtocol public func execute(travelId: String) async throws -> TravelSettlement { return try await repository.fetchSettlement(travelId: travelId) @@ -22,7 +23,7 @@ public struct FetchSettlementUseCase: FetchSettlementUseCaseProtocol { // MARK: - DependencyKey public enum FetchSettlementUseCaseDependencyKey: DependencyKey { - public static var liveValue: any FetchSettlementUseCaseProtocol = MockFetchSettlementUseCase() + public static var liveValue: any FetchSettlementUseCaseProtocol = FetchSettlementUseCase() public static var testValue: any FetchSettlementUseCaseProtocol = MockFetchSettlementUseCase() diff --git a/Domain/Sources/UseCase/Settlement/FetchSettlementUseCaseProtocol.swift b/Domain/Sources/UseCase/Settlement/FetchSettlementUseCaseProtocol.swift deleted file mode 100644 index ffc911ba..00000000 --- a/Domain/Sources/UseCase/Settlement/FetchSettlementUseCaseProtocol.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// FetchSettlementUseCaseProtocol.swift -// Domain -// -// Created by 홍석현 on 12/4/25. -// - -import Foundation - -public protocol FetchSettlementUseCaseProtocol { - func execute(travelId: String) async throws -> TravelSettlement -} diff --git a/Domain/Sources/UseCase/Settlement/MockFetchSettlementUseCase.swift b/Domain/Sources/UseCase/Settlement/Mock/MockFetchSettlementUseCase.swift similarity index 100% rename from Domain/Sources/UseCase/Settlement/MockFetchSettlementUseCase.swift rename to Domain/Sources/UseCase/Settlement/Mock/MockFetchSettlementUseCase.swift diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index 870639c2..587c241c 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -11,7 +11,7 @@ import Data import Domain public enum LiveDependencies { - @MainActor public static func register(_ dependencies: inout DependencyValues) { + @MainActor public static func register(_ dependencies: inout DependencyValues) { // Repository 인스턴스 생성 (재사용) let travelRepository = TravelRepository( remote: TravelRemoteDataSource(), @@ -29,7 +29,7 @@ public enum LiveDependencies { let settlementRepository = SettlementRepository(remote: SettlementRemoteDataSource()) let profileRepository = ProfileRepository() let versionRepository = VersionRepository() - + // Auth & Session let oAuthUseCase = makeOAuthUseCase(repository: oAuthRepository) dependencies.oAuthUseCase = oAuthUseCase @@ -42,10 +42,10 @@ public enum LiveDependencies { dependencies.authUseCase = AuthUseCase(repository: authRepository) dependencies.profileUseCase = ProfileUseCase(repository: profileRepository) dependencies.versionUseCase = VersionUseCase(repository: versionRepository) - + // Analytics dependencies.analyticsUseCase = AnalyticsUseCase(repository: FirebaseAnalyticsRepository()) - + // Travel dependencies.fetchTravelsUseCase = FetchTravelsUseCase(repository: travelRepository) dependencies.loadTravelCacheUseCase = LoadTravelCacheUseCase(repository: travelRepository) @@ -53,28 +53,23 @@ public enum LiveDependencies { dependencies.fetchTravelDetailUseCase = FetchTravelDetailUseCase(repository: travelRepository) dependencies.updateTravelUseCase = UpdateTravelUseCase(repository: travelRepository) dependencies.deleteTravelUseCase = DeleteTravelUseCase(repository: travelRepository) - + // Expense dependencies.expenseRepository = expenseRepository - dependencies.fetchTravelExpenseUseCase = FetchTravelExpenseUseCase(repository: expenseRepository) - dependencies.createExpenseUseCase = CreateExpenseUseCase(repository: expenseRepository) - dependencies.updateExpenseUseCase = UpdateExpenseUseCase(repository: expenseRepository) - dependencies.deleteExpenseUseCase = DeleteExpenseUseCase(repository: expenseRepository) - + // TravelMember dependencies.joinTravelUseCase = JoinTravelUseCase(repository: travelMemberRepository) dependencies.delegateOwnerUseCase = DelegateOwnerUseCase(repository: travelMemberRepository) dependencies.deleteTravelMemberUseCase = DeleteTravelMemberUseCase(repository: travelMemberRepository) dependencies.leaveTravelUseCase = LeaveTravelUseCase(repository: travelMemberRepository) dependencies.fetchMemberUseCase = FetchMemberUseCase(repository: travelMemberRepository) - + // Country & Exchange dependencies.fetchCountriesUseCase = FetchCountriesUseCase(repository: countryRepository) dependencies.fetchExchangeRateUseCase = FetchExchangeRateUseCase(repository: exchangeRateRepository) - + // Settlement - dependencies.fetchSettlementUseCase = FetchSettlementUseCase(repository: settlementRepository) - dependencies.calculateSettlementUseCase = CalculateSettlementUseCase() + dependencies.settlementRepository = settlementRepository } // MARK: - Factory Methods From 732a7e649fe9c1831b27553c281f8f7867ef1acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 17:18:13 +0900 Subject: [PATCH 02/10] =?UTF-8?q?refactor.=20travelMemberRepository?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TravelMemberRepositoryProtocol.swift | 17 +++++++++++++++++ .../Travel/Member/DelegateOwnerUseCase.swift | 18 ++++-------------- .../Member/DeleteTravelMemberUseCase.swift | 18 ++++-------------- .../Travel/Member/FetchMemberUseCase.swift | 18 +++++------------- .../Travel/Member/JoinTravelUseCase.swift | 18 +++++------------- .../Travel/Member/LeaveTravelUseCase.swift | 18 +++++------------- .../Sources/Application/LiveDependencies.swift | 8 ++------ 7 files changed, 42 insertions(+), 73 deletions(-) diff --git a/Domain/Sources/Repository/Travel/TravelMemberRepositoryProtocol.swift b/Domain/Sources/Repository/Travel/TravelMemberRepositoryProtocol.swift index 6b12e151..09959345 100644 --- a/Domain/Sources/Repository/Travel/TravelMemberRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Travel/TravelMemberRepositoryProtocol.swift @@ -6,6 +6,7 @@ // import Foundation +import Dependencies public protocol TravelMemberRepositoryProtocol { func deleteMember(travelId: String, memberId: String) async throws @@ -14,3 +15,19 @@ public protocol TravelMemberRepositoryProtocol { func leaveTravel(travelId: String) async throws func fetchMember(travelId: String) async throws -> MyTravelMember } + +// MARK: - Dependency +private enum TravelMemberRepositoryDepensencyKey: DependencyKey { + static let liveValue: TravelMemberRepositoryProtocol = { + fatalError("TravelMemberRepository liveValue not implemented") + }() + + static let testValue: TravelMemberRepositoryProtocol = MockTravelMemberRepository() +} + +extension DependencyValues { + public var travelMemberRepository: TravelMemberRepositoryProtocol { + get { self[TravelMemberRepositoryDepensencyKey.self] } + set { self[TravelMemberRepositoryDepensencyKey.self] = newValue } + } +} diff --git a/Domain/Sources/UseCase/Travel/Member/DelegateOwnerUseCase.swift b/Domain/Sources/UseCase/Travel/Member/DelegateOwnerUseCase.swift index 9563dbe8..74db16b8 100644 --- a/Domain/Sources/UseCase/Travel/Member/DelegateOwnerUseCase.swift +++ b/Domain/Sources/UseCase/Travel/Member/DelegateOwnerUseCase.swift @@ -13,11 +13,7 @@ public protocol DelegateOwnerUseCaseProtocol { } public struct DelegateOwnerUseCase: DelegateOwnerUseCaseProtocol { - private let repository: TravelMemberRepositoryProtocol - - public init(repository: TravelMemberRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.travelMemberRepository) private var repository: TravelMemberRepositoryProtocol public func execute(travelId: String, newOwnerId: String) async throws -> Travel { try await repository.delegateOwner(travelId: travelId, newOwnerId: newOwnerId) @@ -25,17 +21,11 @@ public struct DelegateOwnerUseCase: DelegateOwnerUseCaseProtocol { } extension DelegateOwnerUseCase: DependencyKey { - public static var liveValue: DelegateOwnerUseCaseProtocol = { - DelegateOwnerUseCase(repository: MockTravelMemberRepository()) - }() + public static var liveValue: DelegateOwnerUseCaseProtocol = DelegateOwnerUseCase() - public static var previewValue: DelegateOwnerUseCaseProtocol = { - DelegateOwnerUseCase(repository: MockTravelMemberRepository()) - }() + public static var previewValue: DelegateOwnerUseCaseProtocol = DelegateOwnerUseCase() - public static var testValue: DelegateOwnerUseCaseProtocol = { - DelegateOwnerUseCase(repository: MockTravelMemberRepository()) - }() + public static var testValue: DelegateOwnerUseCaseProtocol = DelegateOwnerUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/Member/DeleteTravelMemberUseCase.swift b/Domain/Sources/UseCase/Travel/Member/DeleteTravelMemberUseCase.swift index 74a8b64e..cc8c29b8 100644 --- a/Domain/Sources/UseCase/Travel/Member/DeleteTravelMemberUseCase.swift +++ b/Domain/Sources/UseCase/Travel/Member/DeleteTravelMemberUseCase.swift @@ -13,11 +13,7 @@ public protocol DeleteTravelMemberUseCaseProtocol { } public struct DeleteTravelMemberUseCase: DeleteTravelMemberUseCaseProtocol { - private let repository: TravelMemberRepositoryProtocol - - public init(repository: TravelMemberRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.travelMemberRepository) private var repository: TravelMemberRepositoryProtocol public func execute(travelId: String, memberId: String) async throws { try await repository.deleteMember(travelId: travelId, memberId: memberId) @@ -25,17 +21,11 @@ public struct DeleteTravelMemberUseCase: DeleteTravelMemberUseCaseProtocol { } extension DeleteTravelMemberUseCase: DependencyKey { - public static var liveValue: DeleteTravelMemberUseCaseProtocol = { - DeleteTravelMemberUseCase(repository: MockTravelMemberRepository()) - }() + public static var liveValue: DeleteTravelMemberUseCaseProtocol = DeleteTravelMemberUseCase() - public static var previewValue: DeleteTravelMemberUseCaseProtocol = { - DeleteTravelMemberUseCase(repository: MockTravelMemberRepository()) - }() + public static var previewValue: DeleteTravelMemberUseCaseProtocol = DeleteTravelMemberUseCase() - public static var testValue: DeleteTravelMemberUseCaseProtocol = { - DeleteTravelMemberUseCase(repository: MockTravelMemberRepository()) - }() + public static var testValue: DeleteTravelMemberUseCaseProtocol = DeleteTravelMemberUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/Member/FetchMemberUseCase.swift b/Domain/Sources/UseCase/Travel/Member/FetchMemberUseCase.swift index 4ca11dd3..420f964d 100644 --- a/Domain/Sources/UseCase/Travel/Member/FetchMemberUseCase.swift +++ b/Domain/Sources/UseCase/Travel/Member/FetchMemberUseCase.swift @@ -13,11 +13,9 @@ public protocol FetchMemberUseCaseProtocol { } public struct FetchMemberUseCase: FetchMemberUseCaseProtocol { - private let repository: TravelMemberRepositoryProtocol + @Dependency(\.travelMemberRepository) private var repository: TravelMemberRepositoryProtocol - public init(repository: TravelMemberRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(travelId: String) async throws -> MyTravelMember { try await repository.fetchMember(travelId: travelId) @@ -25,17 +23,11 @@ public struct FetchMemberUseCase: FetchMemberUseCaseProtocol { } extension FetchMemberUseCase: DependencyKey { - public static var liveValue: FetchMemberUseCaseProtocol = { - FetchMemberUseCase(repository: MockTravelMemberRepository()) - }() + public static var liveValue: FetchMemberUseCaseProtocol = FetchMemberUseCase() - public static var previewValue: FetchMemberUseCaseProtocol = { - FetchMemberUseCase(repository: MockTravelMemberRepository()) - }() + public static var previewValue: FetchMemberUseCaseProtocol = FetchMemberUseCase() - public static var testValue: FetchMemberUseCaseProtocol = { - FetchMemberUseCase(repository: MockTravelMemberRepository()) - }() + public static var testValue: FetchMemberUseCaseProtocol = FetchMemberUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/Member/JoinTravelUseCase.swift b/Domain/Sources/UseCase/Travel/Member/JoinTravelUseCase.swift index 1b180ce8..487226b1 100644 --- a/Domain/Sources/UseCase/Travel/Member/JoinTravelUseCase.swift +++ b/Domain/Sources/UseCase/Travel/Member/JoinTravelUseCase.swift @@ -13,11 +13,9 @@ public protocol JoinTravelUseCaseProtocol { } public struct JoinTravelUseCase: JoinTravelUseCaseProtocol { - private let repository: TravelMemberRepositoryProtocol + @Dependency(\.travelMemberRepository) private var repository: TravelMemberRepositoryProtocol - public init(repository: TravelMemberRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(inviteCode: String) async throws -> Travel { try await repository.joinTravel(inviteCode: inviteCode) @@ -25,17 +23,11 @@ public struct JoinTravelUseCase: JoinTravelUseCaseProtocol { } extension JoinTravelUseCase: DependencyKey { - public static var liveValue: JoinTravelUseCaseProtocol = { - JoinTravelUseCase(repository: MockTravelMemberRepository()) - }() + public static var liveValue: JoinTravelUseCaseProtocol = JoinTravelUseCase() - public static var previewValue: JoinTravelUseCaseProtocol = { - JoinTravelUseCase(repository: MockTravelMemberRepository()) - }() + public static var previewValue: JoinTravelUseCaseProtocol = JoinTravelUseCase() - public static var testValue: JoinTravelUseCaseProtocol = { - JoinTravelUseCase(repository: MockTravelMemberRepository()) - }() + public static var testValue: JoinTravelUseCaseProtocol = JoinTravelUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/Member/LeaveTravelUseCase.swift b/Domain/Sources/UseCase/Travel/Member/LeaveTravelUseCase.swift index 7e1a0f60..32b49ac4 100644 --- a/Domain/Sources/UseCase/Travel/Member/LeaveTravelUseCase.swift +++ b/Domain/Sources/UseCase/Travel/Member/LeaveTravelUseCase.swift @@ -13,11 +13,9 @@ public protocol LeaveTravelUseCaseProtocol { } public struct LeaveTravelUseCase: LeaveTravelUseCaseProtocol { - private let repository: TravelMemberRepositoryProtocol + @Dependency(\.travelMemberRepository) private var repository: TravelMemberRepositoryProtocol - public init(repository: TravelMemberRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(travelId: String) async throws { try await repository.leaveTravel(travelId: travelId) @@ -25,17 +23,11 @@ public struct LeaveTravelUseCase: LeaveTravelUseCaseProtocol { } extension LeaveTravelUseCase: DependencyKey { - public static var liveValue: LeaveTravelUseCaseProtocol = { - LeaveTravelUseCase(repository: MockTravelMemberRepository()) - }() + public static var liveValue: LeaveTravelUseCaseProtocol = LeaveTravelUseCase() - public static var previewValue: LeaveTravelUseCaseProtocol = { - LeaveTravelUseCase(repository: MockTravelMemberRepository()) - }() + public static var previewValue: LeaveTravelUseCaseProtocol = LeaveTravelUseCase() - public static var testValue: LeaveTravelUseCaseProtocol = { - LeaveTravelUseCase(repository: MockTravelMemberRepository()) - }() + public static var testValue: LeaveTravelUseCaseProtocol = LeaveTravelUseCase() } public extension DependencyValues { diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index 587c241c..3d8d37a7 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -58,12 +58,8 @@ public enum LiveDependencies { dependencies.expenseRepository = expenseRepository // TravelMember - dependencies.joinTravelUseCase = JoinTravelUseCase(repository: travelMemberRepository) - dependencies.delegateOwnerUseCase = DelegateOwnerUseCase(repository: travelMemberRepository) - dependencies.deleteTravelMemberUseCase = DeleteTravelMemberUseCase(repository: travelMemberRepository) - dependencies.leaveTravelUseCase = LeaveTravelUseCase(repository: travelMemberRepository) - dependencies.fetchMemberUseCase = FetchMemberUseCase(repository: travelMemberRepository) - + dependencies.travelMemberRepository = travelMemberRepository + // Country & Exchange dependencies.fetchCountriesUseCase = FetchCountriesUseCase(repository: countryRepository) dependencies.fetchExchangeRateUseCase = FetchExchangeRateUseCase(repository: exchangeRateRepository) From 7bf75bb1089b3b42c39e972045a16170c0a90f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 17:25:13 +0900 Subject: [PATCH 03/10] =?UTF-8?q?refactor.=20travel=20repository=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Travel/TravelRepositoryProtocol.swift | 17 ++++++++++++++ .../UseCase/Travel/CreateTravelUseCase.swift | 20 +++++------------ .../UseCase/Travel/DeleteTravelUseCase.swift | 20 +++++------------ .../Travel/FetchTravelDetailUseCase.swift | 12 +++++----- .../UseCase/Travel/FetchTravelsUseCase.swift | 20 +++++------------ .../Travel/LoadTravelCacheUseCase.swift | 22 +++++-------------- .../UseCase/Travel/UpdateTravelUseCase.swift | 22 +++++-------------- .../Application/LiveDependencies.swift | 7 +----- 8 files changed, 50 insertions(+), 90 deletions(-) diff --git a/Domain/Sources/Repository/Travel/TravelRepositoryProtocol.swift b/Domain/Sources/Repository/Travel/TravelRepositoryProtocol.swift index 615c07bb..ad97417c 100644 --- a/Domain/Sources/Repository/Travel/TravelRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Travel/TravelRepositoryProtocol.swift @@ -6,6 +6,7 @@ // import Foundation +import Dependencies public protocol TravelRepositoryProtocol { func fetchTravels(input: FetchTravelsInput) async throws -> [Travel] @@ -15,3 +16,19 @@ public protocol TravelRepositoryProtocol { func fetchTravelDetail(id: String) async throws -> Travel func loadCachedTravels(status: TravelStatus) async throws -> [Travel]? } + +// MARK: - Dependency +private enum TravelRepositoryDepensencyKey: DependencyKey { + static let liveValue: TravelRepositoryProtocol = { + fatalError("TravelRepository liveValue not implemented") + }() + + static let testValue: TravelRepositoryProtocol = MockTravelRepository() +} + +extension DependencyValues { + public var travelRepository: TravelRepositoryProtocol { + get { self[TravelRepositoryDepensencyKey.self] } + set { self[TravelRepositoryDepensencyKey.self] = newValue } + } +} diff --git a/Domain/Sources/UseCase/Travel/CreateTravelUseCase.swift b/Domain/Sources/UseCase/Travel/CreateTravelUseCase.swift index 5ddb9f34..2de58e6b 100644 --- a/Domain/Sources/UseCase/Travel/CreateTravelUseCase.swift +++ b/Domain/Sources/UseCase/Travel/CreateTravelUseCase.swift @@ -13,11 +13,9 @@ public protocol CreateTravelUseCaseProtocol { } public struct CreateTravelUseCase: CreateTravelUseCaseProtocol { - private let repository: TravelRepositoryProtocol + @Dependency(\.travelRepository) private var repository: TravelRepositoryProtocol - public init(repository: TravelRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(input: CreateTravelInput) async throws -> Travel { try await repository.createTravel(input: input) @@ -25,17 +23,9 @@ public struct CreateTravelUseCase: CreateTravelUseCaseProtocol { } extension CreateTravelUseCase: DependencyKey { - public static var liveValue: CreateTravelUseCaseProtocol = { - CreateTravelUseCase(repository: MockTravelRepository()) - }() - - public static var previewValue: CreateTravelUseCaseProtocol = { - CreateTravelUseCase(repository: MockTravelRepository()) - }() - - public static var testValue: CreateTravelUseCaseProtocol = { - CreateTravelUseCase(repository: MockTravelRepository()) - }() + public static var liveValue: CreateTravelUseCaseProtocol = CreateTravelUseCase() + public static var previewValue: CreateTravelUseCaseProtocol = CreateTravelUseCase() + public static var testValue: CreateTravelUseCaseProtocol = CreateTravelUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/DeleteTravelUseCase.swift b/Domain/Sources/UseCase/Travel/DeleteTravelUseCase.swift index 582d79c6..236973d0 100644 --- a/Domain/Sources/UseCase/Travel/DeleteTravelUseCase.swift +++ b/Domain/Sources/UseCase/Travel/DeleteTravelUseCase.swift @@ -13,11 +13,9 @@ public protocol DeleteTravelUseCaseProtocol { } public struct DeleteTravelUseCase: DeleteTravelUseCaseProtocol { - private let repository: TravelRepositoryProtocol + @Dependency(\.travelRepository) private var repository: TravelRepositoryProtocol - public init(repository: TravelRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(id: String) async throws { try await repository.deleteTravel(id: id) @@ -25,17 +23,9 @@ public struct DeleteTravelUseCase: DeleteTravelUseCaseProtocol { } extension DeleteTravelUseCase: DependencyKey { - public static var liveValue: DeleteTravelUseCaseProtocol = { - DeleteTravelUseCase(repository: MockTravelRepository()) - }() - - public static var previewValue: DeleteTravelUseCaseProtocol = { - DeleteTravelUseCase(repository: MockTravelRepository()) - }() - - public static var testValue: DeleteTravelUseCaseProtocol = { - DeleteTravelUseCase(repository: MockTravelRepository()) - }() + public static var liveValue: DeleteTravelUseCaseProtocol = DeleteTravelUseCase() + public static var previewValue: DeleteTravelUseCaseProtocol = DeleteTravelUseCase() + public static var testValue: DeleteTravelUseCaseProtocol = DeleteTravelUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/FetchTravelDetailUseCase.swift b/Domain/Sources/UseCase/Travel/FetchTravelDetailUseCase.swift index c7fddbd1..b61814aa 100644 --- a/Domain/Sources/UseCase/Travel/FetchTravelDetailUseCase.swift +++ b/Domain/Sources/UseCase/Travel/FetchTravelDetailUseCase.swift @@ -13,11 +13,9 @@ public protocol FetchTravelDetailUseCaseProtocol { } public struct FetchTravelDetailUseCase: FetchTravelDetailUseCaseProtocol { - private let repository: TravelRepositoryProtocol + @Dependency(\.travelRepository) private var repository: TravelRepositoryProtocol - public init(repository: TravelRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(id: String) async throws -> Travel { try await repository.fetchTravelDetail(id: id) @@ -27,11 +25,11 @@ public struct FetchTravelDetailUseCase: FetchTravelDetailUseCaseProtocol { // MARK: - DependencyKey public enum FetchTravelDetailUseCaseDependencyKey: DependencyKey { - public static var liveValue: any FetchTravelDetailUseCaseProtocol = MockFetchTravelDetailUseCase() + public static var liveValue: any FetchTravelDetailUseCaseProtocol = FetchTravelDetailUseCase() - public static var testValue: any FetchTravelDetailUseCaseProtocol = MockFetchTravelDetailUseCase() + public static var testValue: any FetchTravelDetailUseCaseProtocol = FetchTravelDetailUseCase() - public static var previewValue: any FetchTravelDetailUseCaseProtocol = MockFetchTravelDetailUseCase() + public static var previewValue: any FetchTravelDetailUseCaseProtocol = FetchTravelDetailUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/FetchTravelsUseCase.swift b/Domain/Sources/UseCase/Travel/FetchTravelsUseCase.swift index 49999368..1da84600 100644 --- a/Domain/Sources/UseCase/Travel/FetchTravelsUseCase.swift +++ b/Domain/Sources/UseCase/Travel/FetchTravelsUseCase.swift @@ -13,11 +13,9 @@ public protocol FetchTravelsUseCaseProtocol { } public struct FetchTravelsUseCase: FetchTravelsUseCaseProtocol { - private let repository: TravelRepositoryProtocol + @Dependency(\.travelRepository) private var repository: TravelRepositoryProtocol - public init(repository: TravelRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(input: FetchTravelsInput) async throws -> [Travel] { try await repository.fetchTravels(input: input) @@ -25,17 +23,9 @@ public struct FetchTravelsUseCase: FetchTravelsUseCaseProtocol { } extension FetchTravelsUseCase: DependencyKey { - public static var liveValue: FetchTravelsUseCaseProtocol = { - FetchTravelsUseCase(repository: MockTravelRepository()) - }() - - public static var previewValue: FetchTravelsUseCaseProtocol = { - FetchTravelsUseCase(repository: MockTravelRepository()) - }() - - public static var testValue: FetchTravelsUseCaseProtocol = { - FetchTravelsUseCase(repository: MockTravelRepository()) - }() + public static var liveValue: FetchTravelsUseCaseProtocol = FetchTravelsUseCase() + public static var previewValue: FetchTravelsUseCaseProtocol = FetchTravelsUseCase() + public static var testValue: FetchTravelsUseCaseProtocol = FetchTravelsUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/LoadTravelCacheUseCase.swift b/Domain/Sources/UseCase/Travel/LoadTravelCacheUseCase.swift index 2a67c136..76897714 100644 --- a/Domain/Sources/UseCase/Travel/LoadTravelCacheUseCase.swift +++ b/Domain/Sources/UseCase/Travel/LoadTravelCacheUseCase.swift @@ -13,11 +13,9 @@ public protocol LoadTravelCacheUseCaseProtocol { } public struct LoadTravelCacheUseCase: LoadTravelCacheUseCaseProtocol { - private let repository: TravelRepositoryProtocol - - public init(repository: TravelRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.travelRepository) private var repository: TravelRepositoryProtocol + + public init() {} public func execute(status: TravelStatus) async throws -> [Travel]? { try await repository.loadCachedTravels(status: status) @@ -25,17 +23,9 @@ public struct LoadTravelCacheUseCase: LoadTravelCacheUseCaseProtocol { } extension LoadTravelCacheUseCase: DependencyKey { - public static var liveValue: LoadTravelCacheUseCaseProtocol = { - LoadTravelCacheUseCase(repository: MockTravelRepository()) - }() - - public static var previewValue: LoadTravelCacheUseCaseProtocol = { - LoadTravelCacheUseCase(repository: MockTravelRepository()) - }() - - public static var testValue: LoadTravelCacheUseCaseProtocol = { - LoadTravelCacheUseCase(repository: MockTravelRepository()) - }() + public static var liveValue: LoadTravelCacheUseCaseProtocol = LoadTravelCacheUseCase() + public static var previewValue: LoadTravelCacheUseCaseProtocol = LoadTravelCacheUseCase() + public static var testValue: LoadTravelCacheUseCaseProtocol = LoadTravelCacheUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Travel/UpdateTravelUseCase.swift b/Domain/Sources/UseCase/Travel/UpdateTravelUseCase.swift index de063ff4..68a8d4a5 100644 --- a/Domain/Sources/UseCase/Travel/UpdateTravelUseCase.swift +++ b/Domain/Sources/UseCase/Travel/UpdateTravelUseCase.swift @@ -13,11 +13,9 @@ public protocol UpdateTravelUseCaseProtocol { } public struct UpdateTravelUseCase: UpdateTravelUseCaseProtocol { - private let repository: TravelRepositoryProtocol - - public init(repository: TravelRepositoryProtocol) { - self.repository = repository - } + @Dependency(\.travelRepository) private var repository: TravelRepositoryProtocol + + public init() {} public func execute(id: String, input: UpdateTravelInput) async throws -> Travel { try await repository.updateTravel(id: id, input: input) @@ -25,17 +23,9 @@ public struct UpdateTravelUseCase: UpdateTravelUseCaseProtocol { } extension UpdateTravelUseCase: DependencyKey { - public static var liveValue: UpdateTravelUseCaseProtocol = { - UpdateTravelUseCase(repository: MockTravelRepository()) - }() - - public static var previewValue: UpdateTravelUseCaseProtocol = { - UpdateTravelUseCase(repository: MockTravelRepository()) - }() - - public static var testValue: UpdateTravelUseCaseProtocol = { - UpdateTravelUseCase(repository: MockTravelRepository()) - }() + public static var liveValue: UpdateTravelUseCaseProtocol = UpdateTravelUseCase() + public static var previewValue: UpdateTravelUseCaseProtocol = UpdateTravelUseCase() + public static var testValue: UpdateTravelUseCaseProtocol = UpdateTravelUseCase() } public extension DependencyValues { diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index 3d8d37a7..99e019d6 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -47,12 +47,7 @@ public enum LiveDependencies { dependencies.analyticsUseCase = AnalyticsUseCase(repository: FirebaseAnalyticsRepository()) // Travel - dependencies.fetchTravelsUseCase = FetchTravelsUseCase(repository: travelRepository) - dependencies.loadTravelCacheUseCase = LoadTravelCacheUseCase(repository: travelRepository) - dependencies.createTravelUseCase = CreateTravelUseCase(repository: travelRepository) - dependencies.fetchTravelDetailUseCase = FetchTravelDetailUseCase(repository: travelRepository) - dependencies.updateTravelUseCase = UpdateTravelUseCase(repository: travelRepository) - dependencies.deleteTravelUseCase = DeleteTravelUseCase(repository: travelRepository) + dependencies.travelRepository = travelRepository // Expense dependencies.expenseRepository = expenseRepository From 1a3edd47d7e87c3bc0c6ce4087508f01b12bafef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 19:26:14 +0900 Subject: [PATCH 04/10] =?UTF-8?q?refactor:=20auth,=20analytics=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Kakao/KakaoFinalizeRepository.swift | 29 ------------ .../AnalyticsRepositoryProtocol.swift | 18 ++++++- .../Auth/AuthRepositoryProtocol.swift | 37 +++++++++++---- .../ExpenseRepositoryProtocol.swift | 0 .../Mock/MockExpenseRepository.swift | 0 .../Login/LoginRepositoryProtocol.swift | 20 +++++++- .../Apple/AppleOAuthRepositoryProtocol.swift | 20 ++++---- .../GoogleOAuthRepositoryProtocol.swift | 17 ++++++- .../KakaoFinalizeRepositoryProtocol.swift | 12 ----- .../Kakao/KakaoOAuthRepositoryProtocol.swift | 18 ++++++- .../Kakao/MockKakaoFinalizeRepository.swift | 27 ----------- .../OAuth/OAuth/OAuthRepositoryProtocol.swift | 35 ++++++++++---- .../Session/SessionRepositoryProtocol.swift | 21 +++++---- .../Mock/MockSettlementRepository.swift | 0 .../SettlementRepositoryProtocol.swift | 0 .../SignUp/SignUpRepositoryProtocol.swift | 16 +++++++ .../UseCase/Analytics/AnalyticsUseCase.swift | 11 ++--- Domain/Sources/UseCase/Auth/AuthUseCase.swift | 47 ++++++++----------- .../Application/LiveDependencies.swift | 4 +- 19 files changed, 186 insertions(+), 146 deletions(-) delete mode 100644 Data/Sources/Repository/Kakao/KakaoFinalizeRepository.swift rename Domain/Sources/Repository/{ => Expense}/ExpenseRepositoryProtocol.swift (100%) rename Domain/Sources/Repository/{ => Expense}/Mock/MockExpenseRepository.swift (100%) delete mode 100644 Domain/Sources/Repository/OAuth/Kakao/KakaoFinalizeRepositoryProtocol.swift delete mode 100644 Domain/Sources/Repository/OAuth/Kakao/MockKakaoFinalizeRepository.swift rename Domain/Sources/Repository/{ => Settlement}/Mock/MockSettlementRepository.swift (100%) rename Domain/Sources/Repository/{ => Settlement}/SettlementRepositoryProtocol.swift (100%) diff --git a/Data/Sources/Repository/Kakao/KakaoFinalizeRepository.swift b/Data/Sources/Repository/Kakao/KakaoFinalizeRepository.swift deleted file mode 100644 index b02be179..00000000 --- a/Data/Sources/Repository/Kakao/KakaoFinalizeRepository.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// KakaoFinalizeRepository.swift -// Data -// -// Created by Assistant on 12/5/25. -// - -import Foundation -import Domain -import Moya -import NetworkService - -public final class KakaoFinalizeRepository: KakaoFinalizeRepositoryProtocol { - private let provider: MoyaProvider - - public init(provider: MoyaProvider = .default) { - self.provider = provider - } - - public func finalize(ticket: String) async throws -> AuthResult { - let body = KakaoFinalizeRequestDTO(ticket: ticket) - let response: BaseResponse = try await provider.request(.kakaoFinalize(body: body)) - guard let data = response.data else { - throw NetworkError.noData - } - return data.toDomain() - } - -} diff --git a/Domain/Sources/Repository/Analytics/AnalyticsRepositoryProtocol.swift b/Domain/Sources/Repository/Analytics/AnalyticsRepositoryProtocol.swift index 880db1b4..652564ff 100644 --- a/Domain/Sources/Repository/Analytics/AnalyticsRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Analytics/AnalyticsRepositoryProtocol.swift @@ -1,6 +1,22 @@ import Foundation - +import Dependencies /// Analytics 이벤트 전송을 위한 Repository 프로토콜 public protocol AnalyticsRepositoryProtocol: Sendable { func sendEvent(_ event: AnalyticsEvent) async } + +// MARK: - Dependencies +public struct AnalyticsRepositoryDependency: DependencyKey { + public static var liveValue: AnalyticsRepositoryProtocol { + fatalError("AnalyticsRepositoryDependency liveValue not implemented") + } + public static var previewValue: AnalyticsRepositoryProtocol = MockAnalyticsRepository() + public static var testValue: AnalyticsRepositoryProtocol = MockAnalyticsRepository() +} + +public extension DependencyValues { + var analyticsRepository: AnalyticsRepositoryProtocol { + get { self[AnalyticsRepositoryDependency.self] } + set { self[AnalyticsRepositoryDependency.self] = newValue } + } +} diff --git a/Domain/Sources/Repository/Auth/AuthRepositoryProtocol.swift b/Domain/Sources/Repository/Auth/AuthRepositoryProtocol.swift index 6f5ed4b3..b1ed4e68 100644 --- a/Domain/Sources/Repository/Auth/AuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Auth/AuthRepositoryProtocol.swift @@ -6,17 +6,34 @@ // import Foundation +import Dependencies public protocol AuthRepositoryProtocol { - // 세션/토큰 - func refresh(token: String) async throws -> TokenResult - func logout(sessionId: String) async throws -> LogoutStatus - func delete() async throws -> AuthDeleteStatus - func registerDeviceToken(token: String) async throws -> DeviceToken + // 세션/토큰 + func refresh(token: String) async throws -> TokenResult + func logout(sessionId: String) async throws -> LogoutStatus + func delete() async throws -> AuthDeleteStatus + func registerDeviceToken(token: String) async throws -> DeviceToken + + // 인증/가입 + func checkUser(input: OAuthUserInput) async throws -> OAuthCheckUser + func login(input: OAuthUserInput) async throws -> AuthResult + func signUp(input: OAuthUserInput) async throws -> AuthResult + func finalizeKakao(ticket: String) async throws -> AuthResult +} + +// MARK: - Dependencies +public struct AuthRepositoryDependency: DependencyKey { + public static var liveValue: AuthRepositoryProtocol { + fatalError("AuthRepositoryDependency liveValue not implemented") + } + public static var previewValue: AuthRepositoryProtocol = MockAuthRepository() + public static var testValue: AuthRepositoryProtocol = MockAuthRepository() +} - // 인증/가입 - func checkUser(input: OAuthUserInput) async throws -> OAuthCheckUser - func login(input: OAuthUserInput) async throws -> AuthResult - func signUp(input: OAuthUserInput) async throws -> AuthResult - func finalizeKakao(ticket: String) async throws -> AuthResult +public extension DependencyValues { + var authRepository: AuthRepositoryProtocol { + get { self[AuthRepositoryDependency.self] } + set { self[AuthRepositoryDependency.self] = newValue } + } } diff --git a/Domain/Sources/Repository/ExpenseRepositoryProtocol.swift b/Domain/Sources/Repository/Expense/ExpenseRepositoryProtocol.swift similarity index 100% rename from Domain/Sources/Repository/ExpenseRepositoryProtocol.swift rename to Domain/Sources/Repository/Expense/ExpenseRepositoryProtocol.swift diff --git a/Domain/Sources/Repository/Mock/MockExpenseRepository.swift b/Domain/Sources/Repository/Expense/Mock/MockExpenseRepository.swift similarity index 100% rename from Domain/Sources/Repository/Mock/MockExpenseRepository.swift rename to Domain/Sources/Repository/Expense/Mock/MockExpenseRepository.swift diff --git a/Domain/Sources/Repository/Login/LoginRepositoryProtocol.swift b/Domain/Sources/Repository/Login/LoginRepositoryProtocol.swift index 7e3b13cf..12367e7c 100644 --- a/Domain/Sources/Repository/Login/LoginRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Login/LoginRepositoryProtocol.swift @@ -4,7 +4,25 @@ // // Created by Wonji Suh on 11/26/25. // +import Foundation +import Dependencies public protocol LoginRepositoryProtocol { - func login(input: OAuthUserInput) async throws -> AuthResult + func login(input: OAuthUserInput) async throws -> AuthResult +} + +// MARK: - Dependencies +public struct LoginRepositoryDependency: DependencyKey { + public static var liveValue: LoginRepositoryProtocol { + fatalError("LoginRepositoryDependency liveValue not implemented") + } + public static var previewValue: LoginRepositoryProtocol = MockLoginRepository() + public static var testValue: LoginRepositoryProtocol = MockLoginRepository() +} + +public extension DependencyValues { + var loginRepository: LoginRepositoryProtocol { + get { self[LoginRepositoryDependency.self] } + set { self[LoginRepositoryDependency.self] = newValue } + } } diff --git a/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift index 19f12861..62a0c663 100644 --- a/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift @@ -9,19 +9,21 @@ import Foundation import Dependencies public protocol AppleOAuthRepositoryProtocol { - func signIn() async throws -> AppleOAuthPayload + func signIn() async throws -> AppleOAuthPayload } // MARK: - Dependencies -public struct AppleOAuthServiceDependency: DependencyKey { - public static var liveValue: AppleOAuthRepositoryProtocol = MockAppleOAuthRepository() - public static var previewValue: AppleOAuthRepositoryProtocol = MockAppleOAuthRepository() - public static var testValue: AppleOAuthRepositoryProtocol = MockAppleOAuthRepository() +public struct AppleOAuthRepositoryDependency: DependencyKey { + public static var liveValue: AppleOAuthRepositoryProtocol { + fatalError("AppleOAuthServiceDependency liveValue not implemented") + } + public static var previewValue: AppleOAuthRepositoryProtocol = MockAppleOAuthRepository() + public static var testValue: AppleOAuthRepositoryProtocol = MockAppleOAuthRepository() } public extension DependencyValues { - var appleOAuthService: AppleOAuthRepositoryProtocol { - get { self[AppleOAuthServiceDependency.self] } - set { self[AppleOAuthServiceDependency.self] = newValue } - } + var appleOAuthRepository: AppleOAuthRepositoryProtocol { + get { self[AppleOAuthRepositoryDependency.self] } + set { self[AppleOAuthRepositoryDependency.self] = newValue } + } } diff --git a/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift index 8c9457a0..3555f434 100644 --- a/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift @@ -9,6 +9,21 @@ import Foundation import Dependencies public protocol GoogleOAuthRepositoryProtocol { - func signIn() async throws -> GoogleOAuthPayload + func signIn() async throws -> GoogleOAuthPayload } +// MARK: - Dependencies +public struct GoogleOAuthRespositoryDependency: DependencyKey { + public static var liveValue: GoogleOAuthRepositoryProtocol { + fatalError("GoogleOAuthServiceDependency liveValue not implemented") + } + public static var previewValue: GoogleOAuthRepositoryProtocol = MockGoogleOAuthRepository() + public static var testValue: GoogleOAuthRepositoryProtocol = MockGoogleOAuthRepository() +} + +public extension DependencyValues { + var googleOAuthService: GoogleOAuthRepositoryProtocol { + get { self[GoogleOAuthRespositoryDependency.self] } + set { self[GoogleOAuthRespositoryDependency.self] = newValue } + } +} diff --git a/Domain/Sources/Repository/OAuth/Kakao/KakaoFinalizeRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Kakao/KakaoFinalizeRepositoryProtocol.swift deleted file mode 100644 index da0d7d22..00000000 --- a/Domain/Sources/Repository/OAuth/Kakao/KakaoFinalizeRepositoryProtocol.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// KakaoFinalizeRepositoryProtocol.swift -// Domain -// -// Created by Assistant on 12/5/25. -// - -import Foundation - -public protocol KakaoFinalizeRepositoryProtocol { - func finalize(ticket: String) async throws -> AuthResult -} diff --git a/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift index af9ca25b..7a63d0d6 100644 --- a/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift @@ -9,5 +9,21 @@ import Foundation import Dependencies public protocol KakaoOAuthRepositoryProtocol { - func signIn() async throws -> KakaoOAuthPayload + func signIn() async throws -> KakaoOAuthPayload } + +// MARK: - Dependencies +public struct KakaoOAuthRepositoryDependency: DependencyKey { + public static var liveValue: KakaoOAuthRepositoryProtocol { + fatalError("KakaoOAuthRepositoryDependency liveValue not implemented") + } + public static var previewValue: KakaoOAuthRepositoryProtocol = MockKakaoOAuthRepository() + public static var testValue: KakaoOAuthRepositoryProtocol = MockKakaoOAuthRepository() +} + +public extension DependencyValues { + var kakaoOAuthRepository: KakaoOAuthRepositoryProtocol { + get { self[KakaoOAuthRepositoryDependency.self] } + set { self[KakaoOAuthRepositoryDependency.self] = newValue } + } +} \ No newline at end of file diff --git a/Domain/Sources/Repository/OAuth/Kakao/MockKakaoFinalizeRepository.swift b/Domain/Sources/Repository/OAuth/Kakao/MockKakaoFinalizeRepository.swift deleted file mode 100644 index 2949e691..00000000 --- a/Domain/Sources/Repository/OAuth/Kakao/MockKakaoFinalizeRepository.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// MockKakaoFinalizeRepository.swift -// Domain -// -// Created by Assistant on 12/5/25. -// - -import Foundation - -public final class MockKakaoFinalizeRepository: KakaoFinalizeRepositoryProtocol { - public init() {} - - public func finalize(ticket: String) async throws -> AuthResult { - let tokens = AuthTokens( - authToken: "mock-kakao-auth-\(ticket.prefix(6))", - accessToken: "mock-kakao-access-\(ticket.prefix(6))", - refreshToken: "mock-kakao-refresh", - sessionID: "mock-kakao-session" - ) - return AuthResult( - userId: "mock-kakao-user", - name: "Mock Kakao", - provider: .kakao, - token: tokens - ) - } -} diff --git a/Domain/Sources/Repository/OAuth/OAuth/OAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/OAuth/OAuthRepositoryProtocol.swift index 6fb41e8a..91ada265 100644 --- a/Domain/Sources/Repository/OAuth/OAuth/OAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/OAuth/OAuthRepositoryProtocol.swift @@ -6,15 +6,32 @@ // import Foundation -import ComposableArchitecture +import Dependencies public protocol OAuthRepositoryProtocol { - func signIn( - provider: SocialType, - idToken: String, - nonce: String?, - displayName: String?, - authorizationCode: String? - ) async throws -> UserProfile - func updateUserDisplayName(_ name: String) async throws + func signIn( + provider: SocialType, + idToken: String, + nonce: String?, + displayName: String?, + authorizationCode: String? + ) async throws -> UserProfile + + func updateUserDisplayName(_ name: String) async throws +} + +// MARK: - Dependencies +public struct OAuthRepositoryDependency: DependencyKey { + public static var liveValue: OAuthRepositoryProtocol { + fatalError("OAuthRepositoryDependency liveValue not implemented") + } + public static var previewValue: OAuthRepositoryProtocol = MockOAuthRepository() + public static var testValue: OAuthRepositoryProtocol = MockOAuthRepository() +} + +public extension DependencyValues { + var oAuthRepository: OAuthRepositoryProtocol { + get { self[OAuthRepositoryDependency.self] } + set { self[OAuthRepositoryDependency.self] = newValue } + } } diff --git a/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift b/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift index 80dd480e..d27e7fe8 100644 --- a/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift @@ -6,22 +6,23 @@ // import Foundation -import Dependencies +import Dependencies public protocol SessionRepositoryProtocol { - /// Validate a stored session id and return its status from backend. - func checkSession(sessionId: String) async throws -> SessionStatus + /// Validate a stored session id and return its status from backend. + func checkSession(sessionId: String) async throws -> SessionStatus } +// MARK: - Dependencies public struct SessionRepositoryDependency: DependencyKey { - public static var liveValue: SessionRepositoryProtocol = MockSessionRepository() - public static var previewValue: SessionRepositoryProtocol = MockSessionRepository() - public static var testValue: SessionRepositoryProtocol = MockSessionRepository() + public static var liveValue: SessionRepositoryProtocol = MockSessionRepository() + public static var previewValue: SessionRepositoryProtocol = MockSessionRepository() + public static var testValue: SessionRepositoryProtocol = MockSessionRepository() } public extension DependencyValues { - var sessionRepository: SessionRepositoryProtocol { - get { self[SessionRepositoryDependency.self] } - set { self[SessionRepositoryDependency.self] = newValue } - } + var sessionRepository: SessionRepositoryProtocol { + get { self[SessionRepositoryDependency.self] } + set { self[SessionRepositoryDependency.self] = newValue } + } } diff --git a/Domain/Sources/Repository/Mock/MockSettlementRepository.swift b/Domain/Sources/Repository/Settlement/Mock/MockSettlementRepository.swift similarity index 100% rename from Domain/Sources/Repository/Mock/MockSettlementRepository.swift rename to Domain/Sources/Repository/Settlement/Mock/MockSettlementRepository.swift diff --git a/Domain/Sources/Repository/SettlementRepositoryProtocol.swift b/Domain/Sources/Repository/Settlement/SettlementRepositoryProtocol.swift similarity index 100% rename from Domain/Sources/Repository/SettlementRepositoryProtocol.swift rename to Domain/Sources/Repository/Settlement/SettlementRepositoryProtocol.swift diff --git a/Domain/Sources/Repository/SignUp/SignUpRepositoryProtocol.swift b/Domain/Sources/Repository/SignUp/SignUpRepositoryProtocol.swift index 09310790..fcaad8ca 100644 --- a/Domain/Sources/Repository/SignUp/SignUpRepositoryProtocol.swift +++ b/Domain/Sources/Repository/SignUp/SignUpRepositoryProtocol.swift @@ -6,6 +6,7 @@ // import Foundation +import Dependencies public protocol SignUpRepositoryProtocol { func checkSignUp(input: OAuthUserInput) async throws -> OAuthCheckUser @@ -13,3 +14,18 @@ public protocol SignUpRepositoryProtocol { } +// MARK: - Dependencies +public struct SignUpRepositoryDependency: DependencyKey { + public static var liveValue: SignUpRepositoryProtocol { + fatalError("SignUpRepositoryDependency liveValue not implemented") + } + public static var previewValue: SignUpRepositoryProtocol = MockSignUpRepository() + public static var testValue: SignUpRepositoryProtocol = MockSignUpRepository() +} + +public extension DependencyValues { + var signUpRepository: SignUpRepositoryProtocol { + get { self[SignUpRepositoryDependency.self] } + set { self[SignUpRepositoryDependency.self] = newValue } + } +} diff --git a/Domain/Sources/UseCase/Analytics/AnalyticsUseCase.swift b/Domain/Sources/UseCase/Analytics/AnalyticsUseCase.swift index abf9bee6..23d71619 100644 --- a/Domain/Sources/UseCase/Analytics/AnalyticsUseCase.swift +++ b/Domain/Sources/UseCase/Analytics/AnalyticsUseCase.swift @@ -1,13 +1,12 @@ import Foundation import ComposableArchitecture +import Dependencies /// Analytics UseCase 구현체 public struct AnalyticsUseCase: AnalyticsUseCaseProtocol, Sendable { - private let repository: any AnalyticsRepositoryProtocol + @Dependency(\.analyticsRepository) private var repository: any AnalyticsRepositoryProtocol - public init(repository: any AnalyticsRepositoryProtocol) { - self.repository = repository - } + public init() {} public func track(_ event: AnalyticsEvent) { Task { @@ -19,8 +18,8 @@ public struct AnalyticsUseCase: AnalyticsUseCaseProtocol, Sendable { // MARK: - Dependency Key extension AnalyticsUseCase: DependencyKey { - public static let liveValue: any AnalyticsUseCaseProtocol = AnalyticsUseCase(repository: MockAnalyticsRepository()) - public static let testValue: any AnalyticsUseCaseProtocol = AnalyticsUseCase(repository: MockAnalyticsRepository()) + public static let liveValue: any AnalyticsUseCaseProtocol = AnalyticsUseCase() + public static let testValue: any AnalyticsUseCaseProtocol = AnalyticsUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Auth/AuthUseCase.swift b/Domain/Sources/UseCase/Auth/AuthUseCase.swift index 8929ff95..410cb236 100644 --- a/Domain/Sources/UseCase/Auth/AuthUseCase.swift +++ b/Domain/Sources/UseCase/Auth/AuthUseCase.swift @@ -7,41 +7,32 @@ import ComposableArchitecture +import Dependencies public struct AuthUseCase: AuthUseCaseProtocol { - @Shared(.appStorage("sessionId")) var sessionId: String? = "" - - private let repository: AuthRepositoryProtocol - public init( - repository: AuthRepositoryProtocol - ) { - self.repository = repository + @Shared(.appStorage("sessionId")) var sessionId: String? = "" + + @Dependency(\.authRepository) private var repository: AuthRepositoryProtocol + public init() {} + + // MARK: - 로그아웃 + public func logout() async throws -> LogoutStatus { + let sessionId = self.sessionId ?? "" + return try await repository.logout(sessionId: sessionId) + } + + public func deleteUser() async throws -> AuthDeleteStatus { + let result = try await repository.delete() + KeychainManager.shared.clearAll() + return result } - - // MARK: - 로그아웃 - public func logout() async throws -> LogoutStatus { - let sessionId = self.sessionId ?? "" - return try await repository.logout(sessionId: sessionId) - } - - public func deleteUser() async throws -> AuthDeleteStatus { - let result = try await repository.delete() - KeychainManager.shared.clearAll() - return result - } } extension AuthUseCase: DependencyKey { - public static var liveValue: AuthUseCaseProtocol { - return AuthUseCase(repository: MockAuthRepository()) - } - - public static var previewValue: any AuthUseCaseProtocol { liveValue } - - public static let testValue: AuthUseCaseProtocol = AuthUseCase( - repository: MockAuthRepository() - ) + public static let liveValue: AuthUseCaseProtocol = AuthUseCase() + public static let previewValue: any AuthUseCaseProtocol = AuthUseCase() + public static let testValue: AuthUseCaseProtocol = AuthUseCase() } diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index 99e019d6..baa89b9d 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -31,6 +31,7 @@ public enum LiveDependencies { let versionRepository = VersionRepository() // Auth & Session + dependencies.authRepository = authRepository let oAuthUseCase = makeOAuthUseCase(repository: oAuthRepository) dependencies.oAuthUseCase = oAuthUseCase dependencies.unifiedOAuthUseCase = UnifiedOAuthUseCase( @@ -39,12 +40,11 @@ public enum LiveDependencies { sessionStoreRepository: SessionStoreRepository() ) dependencies.sessionUseCase = SessionUseCase(repository: SessionRepository()) - dependencies.authUseCase = AuthUseCase(repository: authRepository) dependencies.profileUseCase = ProfileUseCase(repository: profileRepository) dependencies.versionUseCase = VersionUseCase(repository: versionRepository) // Analytics - dependencies.analyticsUseCase = AnalyticsUseCase(repository: FirebaseAnalyticsRepository()) + dependencies.analyticsRepository = FirebaseAnalyticsRepository() // Travel dependencies.travelRepository = travelRepository From a3bb5a887764ce4a2bee012184ad738e26d9f343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 19:48:04 +0900 Subject: [PATCH 05/10] refactor: profile --- .../Profile/ProfileRepositoryProtocol.swift | 21 ++++++- .../UseCase/Profile/ProfileEditPayload.swift | 26 ++++----- .../UseCase/Profile/ProfileUseCase.swift | 58 ++++++++----------- .../Profile/ProfileUseCaseProtocol.swift | 4 +- 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/Domain/Sources/Repository/Profile/ProfileRepositoryProtocol.swift b/Domain/Sources/Repository/Profile/ProfileRepositoryProtocol.swift index e50d9c3c..6c423b5f 100644 --- a/Domain/Sources/Repository/Profile/ProfileRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Profile/ProfileRepositoryProtocol.swift @@ -6,8 +6,25 @@ // import Foundation +import Dependencies public protocol ProfileRepositoryProtocol { - func getProfile() async throws -> Profile - func editProfile(name: String?, avatarData: Data?, fileName: String?) async throws -> Profile + func getProfile() async throws -> Profile + func editProfile(name: String?, avatarData: Data?, fileName: String?) async throws -> Profile +} + +// MARK: - Dependencies +private struct ProfileRepositoryDependencyKey: DependencyKey { + public static var liveValue: ProfileRepositoryProtocol { + fatalError("ProfileRepositoryDependency liveValue not implemented") + } + public static var previewValue: ProfileRepositoryProtocol = MockProfileRepository() + public static var testValue: ProfileRepositoryProtocol = MockProfileRepository() +} + +public extension DependencyValues { + var profileRepository: ProfileRepositoryProtocol { + get { self[ProfileRepositoryDependencyKey.self] } + set { self[ProfileRepositoryDependencyKey.self] = newValue } + } } diff --git a/Domain/Sources/UseCase/Profile/ProfileEditPayload.swift b/Domain/Sources/UseCase/Profile/ProfileEditPayload.swift index 7c78b4ba..18c6f174 100644 --- a/Domain/Sources/UseCase/Profile/ProfileEditPayload.swift +++ b/Domain/Sources/UseCase/Profile/ProfileEditPayload.swift @@ -8,17 +8,17 @@ import Foundation public struct ProfileEditPayload: Equatable { - public let name: String? - public let avatarData: Data? - public let fileName: String? - - public init( - name: String? = nil, - avatarData: Data? = nil, - fileName: String? = nil - ) { - self.name = name - self.avatarData = avatarData - self.fileName = fileName - } + public let name: String? + public let avatarData: Data? + public let fileName: String? + + public init( + name: String? = nil, + avatarData: Data? = nil, + fileName: String? = nil + ) { + self.name = name + self.avatarData = avatarData + self.fileName = fileName + } } diff --git a/Domain/Sources/UseCase/Profile/ProfileUseCase.swift b/Domain/Sources/UseCase/Profile/ProfileUseCase.swift index aadc1841..bf8be5c8 100644 --- a/Domain/Sources/UseCase/Profile/ProfileUseCase.swift +++ b/Domain/Sources/UseCase/Profile/ProfileUseCase.swift @@ -6,45 +6,37 @@ // import Foundation -import ComposableArchitecture +import Dependencies public struct ProfileUseCase: ProfileUseCaseProtocol { - private let repository: ProfileRepositoryProtocol - - public init(repository: ProfileRepositoryProtocol) { - self.repository = repository - } - - public func getProfile() async throws -> Profile { - return try await repository.getProfile() - } - - public func editProfile(_ payload: ProfileEditPayload) async throws -> Profile { - let resolvedFileName: String? = { - // 파일이 있을 때만 기본 파일명을 지정하고, 없으면 그대로 nil 전달 - guard payload.avatarData != nil else { return nil } - return payload.fileName ?? "avatar.jpg" - }() - - return try await repository.editProfile( - name: payload.name, - avatarData: payload.avatarData, - fileName: resolvedFileName - ) - } + @Dependency(\.profileRepository) private var repository: ProfileRepositoryProtocol + + public init() {} + + public func getProfile() async throws -> Profile { + return try await repository.getProfile() + } + + public func editProfile(_ payload: ProfileEditPayload) async throws -> Profile { + let resolvedFileName: String? = { + // 파일이 있을 때만 기본 파일명을 지정하고, 없으면 그대로 nil 전달 + guard payload.avatarData != nil else { return nil } + return payload.fileName ?? "avatar.jpg" + }() + + return try await repository.editProfile( + name: payload.name, + avatarData: payload.avatarData, + fileName: resolvedFileName + ) + } } extension ProfileUseCase: DependencyKey { - public static var liveValue: ProfileUseCaseProtocol { - return ProfileUseCase(repository: MockProfileRepository()) - } - - public static var previewValue: any ProfileUseCaseProtocol { liveValue } - - public static let testValue: ProfileUseCaseProtocol = ProfileUseCase( - repository: MockProfileRepository() - ) + public static let liveValue: ProfileUseCaseProtocol = ProfileUseCase() + public static let previewValue: any ProfileUseCaseProtocol = ProfileUseCase() + public static let testValue: ProfileUseCaseProtocol = ProfileUseCase() } diff --git a/Domain/Sources/UseCase/Profile/ProfileUseCaseProtocol.swift b/Domain/Sources/UseCase/Profile/ProfileUseCaseProtocol.swift index 561ab4da..aebd5d77 100644 --- a/Domain/Sources/UseCase/Profile/ProfileUseCaseProtocol.swift +++ b/Domain/Sources/UseCase/Profile/ProfileUseCaseProtocol.swift @@ -8,6 +8,6 @@ import Foundation public protocol ProfileUseCaseProtocol { - func getProfile() async throws -> Profile - func editProfile(_ payload: ProfileEditPayload) async throws -> Profile + func getProfile() async throws -> Profile + func editProfile(_ payload: ProfileEditPayload) async throws -> Profile } From 1cdfcaf40fa7bce0d09b967a505262350db09905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 19:48:20 +0900 Subject: [PATCH 06/10] refactor: session --- .../Session/SessionRepositoryProtocol.swift | 14 ++-- .../UseCase/OAuth/UnifiedOAuthUseCase.swift | 80 +++++++++---------- .../UseCase/Session/SessionUseCase.swift | 24 ++---- 3 files changed, 52 insertions(+), 66 deletions(-) diff --git a/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift b/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift index d27e7fe8..e2560b9f 100644 --- a/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Session/SessionRepositoryProtocol.swift @@ -14,15 +14,17 @@ public protocol SessionRepositoryProtocol { } // MARK: - Dependencies -public struct SessionRepositoryDependency: DependencyKey { - public static var liveValue: SessionRepositoryProtocol = MockSessionRepository() - public static var previewValue: SessionRepositoryProtocol = MockSessionRepository() - public static var testValue: SessionRepositoryProtocol = MockSessionRepository() +public struct SessionRepositoryDependencyKey: DependencyKey { + public static var liveValue: SessionRepositoryProtocol { + fatalError("SessionRepositoryDependency liveValue not implemented") + } + public static let previewValue: SessionRepositoryProtocol = MockSessionRepository() + public static let testValue: SessionRepositoryProtocol = MockSessionRepository() } public extension DependencyValues { var sessionRepository: SessionRepositoryProtocol { - get { self[SessionRepositoryDependency.self] } - set { self[SessionRepositoryDependency.self] = newValue } + get { self[SessionRepositoryDependencyKey.self] } + set { self[SessionRepositoryDependencyKey.self] = newValue } } } diff --git a/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift b/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift index 70966470..dcfe65b5 100644 --- a/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift +++ b/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift @@ -9,21 +9,18 @@ import Foundation import Dependencies import LogMacro import AuthenticationServices -import ComposableArchitecture /// 통합 OAuth UseCase - 로그인/회원가입 플로우를 하나로 통합 (AuthFacade 역할) public struct UnifiedOAuthUseCase { private let oAuthUseCase: any OAuthUseCaseProtocol - private let authRepository: any AuthRepositoryProtocol + @Dependency(\.authRepository) private var authRepository: AuthRepositoryProtocol private let sessionStoreRepository: any SessionStoreRepositoryProtocol - + public init( oAuthUseCase: any OAuthUseCaseProtocol = OAuthUseCase.liveValue, - authRepository: any AuthRepositoryProtocol = MockAuthRepository(), sessionStoreRepository: any SessionStoreRepositoryProtocol = SessionStoreRepository() ) { self.oAuthUseCase = oAuthUseCase - self.authRepository = authRepository self.sessionStoreRepository = sessionStoreRepository } } @@ -31,7 +28,7 @@ public struct UnifiedOAuthUseCase { // MARK: - Public Interface public extension UnifiedOAuthUseCase { - + /// OAuth Provider에서 토큰 획득 (Google/Apple SDK 호출) func socialLogin( with socialType: SocialType, @@ -44,34 +41,34 @@ public extension UnifiedOAuthUseCase { nonce: nonce ) } - + /// 회원가입 상태 확인 func checkSignUpUser( with oAuthData: AuthData ) async -> Result { return await checkUserRegistrationStatus(with: oAuthData) } - + /// 로그인 처리 func loginUser( with oAuthData: AuthData ) async -> Result { let loginResult = await attemptLogin(with: oAuthData) - + if case .success(let authEntity) = loginResult { saveTokensAndComplete(authEntity: authEntity) } - + return loginResult } - + /// 회원가입 처리 func signUpUser( with oAuthData: AuthData ) async -> Result { return await attemptSignUp(with: oAuthData) } - + /// 약관 동의 후 회원가입 처리 func signUpWithTermsAgreement( with oAuthData: AuthData @@ -79,7 +76,7 @@ public extension UnifiedOAuthUseCase { Log.info("✅ Terms agreement completed, proceeding with signup") return await attemptSignUp(with: oAuthData) } - + /// OAuth 플로우 처리 (AuthFlowOutcome 반환) func processOAuthFlow( with socialType: SocialType, @@ -87,7 +84,7 @@ public extension UnifiedOAuthUseCase { nonce: String? = nil ) async -> AuthFlowOutcome { Log.info("🔐 Starting OAuth flow for: \(socialType.rawValue)") - + // 1단계: OAuth Provider 인증 let oAuthData = await getOAuthCredentials( socialType: socialType, @@ -101,7 +98,7 @@ public extension UnifiedOAuthUseCase { return .failure(.unknownError("OAuth 인증 실패")) } } - + // 2단계: 사용자 등록 상태 확인 let registrationStatus = await checkUserRegistrationStatus(with: authData) guard case .success(let checkUser) = registrationStatus else { @@ -111,7 +108,7 @@ public extension UnifiedOAuthUseCase { return .failure(.unknownError("등록 상태 확인 실패")) } } - + // 3단계: 등록 여부에 따른 분기 처리 if checkUser.registered { // 이미 등록된 사용자 -> 로그인 진행 @@ -137,14 +134,14 @@ public extension UnifiedOAuthUseCase { } } } - + func loginOrSignUp( with socialType: SocialType, appleCredential: ASAuthorizationAppleIDCredential? = nil, nonce: String? = nil ) async -> Result { Log.info("🔐 Starting unified OAuth flow for: \(socialType.rawValue)") - + let oAuthData = await getOAuthCredentials( socialType: socialType, appleCredential: appleCredential, @@ -157,7 +154,7 @@ public extension UnifiedOAuthUseCase { return .failure(.unknownError("OAuth 인증 실패")) } } - + let registrationStatus = await checkUserRegistrationStatus(with: authData) guard case .success(let checkUser) = registrationStatus else { if case .failure(let error) = registrationStatus { @@ -166,9 +163,9 @@ public extension UnifiedOAuthUseCase { return .failure(.unknownError("등록 상태 확인 실패")) } } - + let authResult: Result - + if checkUser.registered { authResult = await attemptLogin(with: authData) } else { @@ -179,11 +176,11 @@ public extension UnifiedOAuthUseCase { authResult = await attemptSignUp(with: authData) } } - + if case .success(let authEntity) = authResult, checkUser.registered { saveTokensAndComplete(authEntity: authEntity) } - + return authResult } } @@ -191,7 +188,7 @@ public extension UnifiedOAuthUseCase { // MARK: - Private Methods private extension UnifiedOAuthUseCase { - + /// OAuth Provider에서 인증 정보 획득 func getOAuthCredentials( socialType: SocialType, @@ -208,12 +205,12 @@ private extension UnifiedOAuthUseCase { else { return .failure(.invalidCredential("Apple 자격정보가 없습니다")) } - + let profile = try await oAuthUseCase.signInWithApple( credential: credential, nonce: nonce ) - + let oAuthData = AuthData( socialType: profile.provider, accessToken: profile.tokens.accessToken, @@ -226,10 +223,10 @@ private extension UnifiedOAuthUseCase { sessionID: profile.tokens.sessionID, userId: profile.id ) - - + + return .success(oAuthData) - + case .google: let profile = try await oAuthUseCase.signUp(with: socialType) let oAuthData = AuthData( @@ -266,7 +263,7 @@ private extension UnifiedOAuthUseCase { userId: finalized.userId ) return .success(oAuthData) - + case .none: return .failure(.invalidCredential("잘못된 소셜 타입")) } @@ -275,7 +272,7 @@ private extension UnifiedOAuthUseCase { return .failure(authError) } } - + /// 로그인 시도 func attemptLogin( with oAuthData: AuthData @@ -297,7 +294,7 @@ private extension UnifiedOAuthUseCase { Log.info("✅ Kakao finalize-only login completed") return .success(authEntity) } - + let input = OAuthUserInput( accessToken: oAuthData.authToken , socialType: oAuthData.socialType, @@ -305,18 +302,18 @@ private extension UnifiedOAuthUseCase { codeVerifier: oAuthData.codeVerifier, redirectUri: oAuthData.redirectUri ) - + var authEntity = try await authRepository.login(input: input) authEntity.token.authToken = oAuthData.authToken Log.info("✅ Login successful for \(oAuthData.socialType.rawValue)") return .success(authEntity) - + } catch { Log.info("⚠️ Login failed: \(error.localizedDescription)") return .failure(.networkError(error.localizedDescription)) } } - + /// 회원가입 상태 확인 func checkUserRegistrationStatus( with oAuthData: AuthData @@ -325,7 +322,7 @@ private extension UnifiedOAuthUseCase { if oAuthData.socialType == .kakao { return .success(OAuthCheckUser(registered: true, needsTerms: false)) } - + let checkInput = OAuthUserInput( accessToken: oAuthData.authToken, socialType: oAuthData.socialType, @@ -340,7 +337,7 @@ private extension UnifiedOAuthUseCase { return .failure(authError) } } - + /// 회원가입 시도 func attemptSignUp( with oAuthData: AuthData @@ -354,7 +351,7 @@ private extension UnifiedOAuthUseCase { sessionID: oAuthData.sessionID ?? "" ) let authEntity = AuthResult( - userId: oAuthData.userId ?? "kakao-user", + userId: oAuthData.userId ?? "kakao-user", name: oAuthData.displayName ?? "", provider: .kakao, token: tokens @@ -362,7 +359,7 @@ private extension UnifiedOAuthUseCase { saveTokensAndComplete(authEntity: authEntity) return .success(authEntity) } - + let checkInput = OAuthUserInput( accessToken: oAuthData.authToken, socialType: oAuthData.socialType, @@ -379,7 +376,7 @@ private extension UnifiedOAuthUseCase { return .failure(authError) } } - + /// 토큰 저장 및 로깅 func saveTokensAndComplete( authEntity: AuthResult @@ -401,10 +398,9 @@ private extension UnifiedOAuthUseCase { extension UnifiedOAuthUseCase: DependencyKey { public static let liveValue = UnifiedOAuthUseCase() - + public static let testValue = UnifiedOAuthUseCase( oAuthUseCase: OAuthUseCase.testValue, - authRepository: MockAuthRepository(), sessionStoreRepository: SessionStoreRepository() ) } diff --git a/Domain/Sources/UseCase/Session/SessionUseCase.swift b/Domain/Sources/UseCase/Session/SessionUseCase.swift index fb632f2a..0c5aa98e 100644 --- a/Domain/Sources/UseCase/Session/SessionUseCase.swift +++ b/Domain/Sources/UseCase/Session/SessionUseCase.swift @@ -6,17 +6,12 @@ // import Foundation - -import ComposableArchitecture +import Dependencies public struct SessionUseCase: SessionUseCaseProtocol { - private let repository: SessionRepositoryProtocol + @Dependency(\.sessionRepository) private var repository: SessionRepositoryProtocol - public init( - repository: SessionRepositoryProtocol - ) { - self.repository = repository - } + public init() {} public func checkSession( sessionId: String @@ -25,17 +20,10 @@ public struct SessionUseCase: SessionUseCaseProtocol { } } - extension SessionUseCase: DependencyKey { - public static var liveValue: SessionUseCaseProtocol { - return SessionUseCase(repository: MockSessionRepository()) - } - - public static var previewValue: SessionUseCaseProtocol { liveValue } - - public static var testValue: SessionUseCaseProtocol { - return SessionUseCase(repository: MockSessionRepository()) - } + public static let liveValue: SessionUseCaseProtocol = SessionUseCase() + public static let previewValue: SessionUseCaseProtocol = SessionUseCase() + public static let testValue: SessionUseCaseProtocol = SessionUseCase() } public extension DependencyValues { From 024900177d75a65104f9bcb4e8f6a648ce2246b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 19:48:38 +0900 Subject: [PATCH 07/10] refactor: version --- .../Version/VersionRepositoryProtocol.swift | 17 ++++++++++++ .../UseCase/Version/VersionUseCase.swift | 27 +++++++------------ .../Sources/Reducer/SplashFeature.swift | 2 +- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/Domain/Sources/Repository/Version/VersionRepositoryProtocol.swift b/Domain/Sources/Repository/Version/VersionRepositoryProtocol.swift index e0cc2054..53f2d334 100644 --- a/Domain/Sources/Repository/Version/VersionRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Version/VersionRepositoryProtocol.swift @@ -6,7 +6,24 @@ // import Foundation +import Dependencies public protocol VersionRepositoryProtocol { func getVersion(bundleId: String, version: String) async throws -> Version } + +// MARK: - Dependencies +private struct VersionRepositoryDependencyKey: DependencyKey { + public static var liveValue: VersionRepositoryProtocol { + fatalError("VersionRepositoryDependency liveValue not implemented") + } + public static let previewValue: VersionRepositoryProtocol = MockVersionRepository() + public static let testValue: VersionRepositoryProtocol = MockVersionRepository() +} + +public extension DependencyValues { + var versionRepository: VersionRepositoryProtocol { + get { self[VersionRepositoryDependencyKey.self] } + set { self[VersionRepositoryDependencyKey.self] = newValue } + } +} diff --git a/Domain/Sources/UseCase/Version/VersionUseCase.swift b/Domain/Sources/UseCase/Version/VersionUseCase.swift index ca91a3d4..7b6b25b7 100644 --- a/Domain/Sources/UseCase/Version/VersionUseCase.swift +++ b/Domain/Sources/UseCase/Version/VersionUseCase.swift @@ -6,37 +6,28 @@ // import Foundation -import ComposableArchitecture +import Dependencies public struct VersionUseCase: VersionUseCaseProtocol { - private let repository: VersionRepositoryProtocol + @Dependency(\.versionRepository) private var repository: VersionRepositoryProtocol - public init(repository: VersionRepositoryProtocol) { - self.repository = repository - } + public init() {} public func getVersion(bundleId: String, version: String) async throws -> Version { return try await repository.getVersion(bundleId: bundleId, version: version) } } -extension VersionUseCase: DependencyKey { - public static var liveValue: VersionUseCaseProtocol { - return VersionUseCase(repository: MockVersionRepository()) - } - - public static var previewValue: VersionUseCaseProtocol { liveValue } - - public static let testValue: VersionUseCaseProtocol = VersionUseCase( - repository: MockVersionRepository() - ) +private struct VersionUseCaseDependencyKey: DependencyKey { + public static let liveValue: VersionUseCaseProtocol = VersionUseCase() + public static let previewValue: VersionUseCaseProtocol = VersionUseCase() + public static let testValue: VersionUseCaseProtocol = VersionUseCase() } - public extension DependencyValues { var versionUseCase : VersionUseCaseProtocol { - get { self[VersionUseCase.self] } - set { self[VersionUseCase.self] = newValue } + get { self[VersionUseCaseDependencyKey.self] } + set { self[VersionUseCaseDependencyKey.self] = newValue } } } diff --git a/Features/Splash/Sources/Reducer/SplashFeature.swift b/Features/Splash/Sources/Reducer/SplashFeature.swift index 63555b42..daf2af0d 100644 --- a/Features/Splash/Sources/Reducer/SplashFeature.swift +++ b/Features/Splash/Sources/Reducer/SplashFeature.swift @@ -83,7 +83,7 @@ public struct SplashFeature { @Dependency(SessionUseCase.self) var sessionUseCase @Dependency(\.continuousClock) var clock - @Dependency(VersionUseCase.self) var versionUseCase + @Dependency(\.versionUseCase) var versionUseCase public var body: some Reducer { BindingReducer() From 108f7f9e908f2edd90f0d41d54b3fbb07bdd2493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 19:48:47 +0900 Subject: [PATCH 08/10] refactor: live depenedencies --- .../Application/LiveDependencies.swift | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index baa89b9d..e1bfb922 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -13,54 +13,46 @@ import Domain public enum LiveDependencies { @MainActor public static func register(_ dependencies: inout DependencyValues) { // Repository 인스턴스 생성 (재사용) - let travelRepository = TravelRepository( - remote: TravelRemoteDataSource(), - local: TravelLocalDataSource() - ) - let expenseRepository = ExpenseRepository( - remote: ExpenseRemoteDataSource(), - local: ExpenseLocalDataSource() - ) - let travelMemberRepository = TravelMemberRepository(remote: TravelMemberRemoteDataSource()) - let authRepository = AuthRepository() let oAuthRepository = OAuthRepository() let countryRepository = CountryRepository(remote: CountryRemoteDataSource()) let exchangeRateRepository = ExchangeRateRepository(remote: ExchangeRateRemoteDataSource()) - let settlementRepository = SettlementRepository(remote: SettlementRemoteDataSource()) - let profileRepository = ProfileRepository() - let versionRepository = VersionRepository() // Auth & Session - dependencies.authRepository = authRepository + dependencies.authRepository = AuthRepository() + dependencies.sessionRepository = SessionRepository() + dependencies.profileRepository = ProfileRepository() + dependencies.versionRepository = VersionRepository() + let oAuthUseCase = makeOAuthUseCase(repository: oAuthRepository) dependencies.oAuthUseCase = oAuthUseCase dependencies.unifiedOAuthUseCase = UnifiedOAuthUseCase( oAuthUseCase: oAuthUseCase, - authRepository: authRepository, sessionStoreRepository: SessionStoreRepository() ) - dependencies.sessionUseCase = SessionUseCase(repository: SessionRepository()) - dependencies.profileUseCase = ProfileUseCase(repository: profileRepository) - dependencies.versionUseCase = VersionUseCase(repository: versionRepository) - // Analytics dependencies.analyticsRepository = FirebaseAnalyticsRepository() // Travel - dependencies.travelRepository = travelRepository + dependencies.travelRepository = TravelRepository( + remote: TravelRemoteDataSource(), + local: TravelLocalDataSource() + ) // Expense - dependencies.expenseRepository = expenseRepository + dependencies.expenseRepository = ExpenseRepository( + remote: ExpenseRemoteDataSource(), + local: ExpenseLocalDataSource() + ) // TravelMember - dependencies.travelMemberRepository = travelMemberRepository + dependencies.travelMemberRepository = TravelMemberRepository(remote: TravelMemberRemoteDataSource()) // Country & Exchange dependencies.fetchCountriesUseCase = FetchCountriesUseCase(repository: countryRepository) dependencies.fetchExchangeRateUseCase = FetchExchangeRateUseCase(repository: exchangeRateRepository) // Settlement - dependencies.settlementRepository = settlementRepository + dependencies.settlementRepository = SettlementRepository(remote: SettlementRemoteDataSource()) } // MARK: - Factory Methods From bfb4021db062ee79128c60bf4dfdc563da5e543f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 19:53:45 +0900 Subject: [PATCH 09/10] refactor: Count, ExchangeRate --- .../Country/CountryRepositoryProtocol.swift | 17 ++++++++++++++++ .../ExchangeRateRepositoryProtocol.swift | 16 +++++++++++++++ .../FetchCountriesUseCaseProtocol.swift | 20 +++++-------------- .../FetchExchangeRateUseCaseProtocol.swift | 20 +++++-------------- .../Application/LiveDependencies.swift | 8 +++----- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift b/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift index 38261433..b40e9b18 100644 --- a/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift @@ -6,7 +6,24 @@ // import Foundation +import Dependencies public protocol CountryRepositoryProtocol { func fetchCountries() async throws -> [Country] } + +// MARK: - Dependencies +public struct CountryRepositoryDependencyKey: DependencyKey { + public static var liveValue: CountryRepositoryProtocol { + fatalError("CountryRepositoryDependency liveValue not implemented") + } + public static var previewValue: CountryRepositoryProtocol = MockCountryRepository() + public static var testValue: CountryRepositoryProtocol = MockCountryRepository() +} + +public extension DependencyValues { + var countryRepository: CountryRepositoryProtocol { + get { self[CountryRepositoryDependencyKey.self] } + set { self[CountryRepositoryDependencyKey.self] = newValue } + } +} diff --git a/Domain/Sources/Repository/Country/ExchangeRateRepositoryProtocol.swift b/Domain/Sources/Repository/Country/ExchangeRateRepositoryProtocol.swift index c62d7ae4..b63531f1 100644 --- a/Domain/Sources/Repository/Country/ExchangeRateRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Country/ExchangeRateRepositoryProtocol.swift @@ -6,7 +6,23 @@ // import Foundation +import Dependencies public protocol ExchangeRateRepositoryProtocol { func fetchExchangeRate(base: String) async throws -> ExchangeRate } + +public struct ExchangeRateRepositoryDependencyKey: DependencyKey { + public static var liveValue: ExchangeRateRepositoryProtocol { + fatalError("ExchangeRateRepositoryDependencyKey liveValue not implemented") + } + public static var previewValue: ExchangeRateRepositoryProtocol = MockExchangeRateRepository() + public static var testValue: ExchangeRateRepositoryProtocol = MockExchangeRateRepository() +} + +public extension DependencyValues { + var exchangeRateRepository: ExchangeRateRepositoryProtocol { + get { self[ExchangeRateRepositoryDependencyKey.self] } + set { self[ExchangeRateRepositoryDependencyKey.self] = newValue } + } +} diff --git a/Domain/Sources/UseCase/Country/FetchCountriesUseCaseProtocol.swift b/Domain/Sources/UseCase/Country/FetchCountriesUseCaseProtocol.swift index 00b9076c..8b8e4f43 100644 --- a/Domain/Sources/UseCase/Country/FetchCountriesUseCaseProtocol.swift +++ b/Domain/Sources/UseCase/Country/FetchCountriesUseCaseProtocol.swift @@ -13,28 +13,18 @@ public protocol FetchCountriesUseCaseProtocol { } public final class FetchCountriesUseCase: FetchCountriesUseCaseProtocol { - private let repository: CountryRepositoryProtocol + @Dependency(\.countryRepository) private var repository: CountryRepositoryProtocol - public init(repository: CountryRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute() async throws -> [Country] { try await repository.fetchCountries() } } extension FetchCountriesUseCase: DependencyKey { - public static var liveValue: FetchCountriesUseCaseProtocol = { - FetchCountriesUseCase(repository: MockCountriesRepository()) - }() - - public static var previewValue: FetchCountriesUseCaseProtocol = { - FetchCountriesUseCase(repository: MockCountriesRepository()) - }() - - public static var testValue: FetchCountriesUseCaseProtocol = { - FetchCountriesUseCase(repository: MockCountriesRepository()) - }() + public static var liveValue: FetchCountriesUseCaseProtocol = FetchCountriesUseCase() + public static var previewValue: FetchCountriesUseCaseProtocol = FetchCountriesUseCase() + public static var testValue: FetchCountriesUseCaseProtocol = FetchCountriesUseCase() } public extension DependencyValues { diff --git a/Domain/Sources/UseCase/Country/FetchExchangeRateUseCaseProtocol.swift b/Domain/Sources/UseCase/Country/FetchExchangeRateUseCaseProtocol.swift index fefd442c..97e73a7e 100644 --- a/Domain/Sources/UseCase/Country/FetchExchangeRateUseCaseProtocol.swift +++ b/Domain/Sources/UseCase/Country/FetchExchangeRateUseCaseProtocol.swift @@ -13,28 +13,18 @@ public protocol FetchExchangeRateUseCaseProtocol { } public final class FetchExchangeRateUseCase: FetchExchangeRateUseCaseProtocol { - private let repository: ExchangeRateRepositoryProtocol + @Dependency(\.exchangeRateRepository) private var repository: ExchangeRateRepositoryProtocol - public init(repository: ExchangeRateRepositoryProtocol) { - self.repository = repository - } + public init() {} public func execute(base: String) async throws -> ExchangeRate { try await repository.fetchExchangeRate(base: base) } } extension FetchExchangeRateUseCase: DependencyKey { - public static var liveValue: FetchExchangeRateUseCaseProtocol = { - FetchExchangeRateUseCase(repository: MockExchangeRateRepository()) - }() - - public static var previewValue: FetchExchangeRateUseCaseProtocol = { - FetchExchangeRateUseCase(repository: MockExchangeRateRepository()) - }() - - public static var testValue: FetchExchangeRateUseCaseProtocol = { - FetchExchangeRateUseCase(repository: MockExchangeRateRepository()) - }() + public static var liveValue: FetchExchangeRateUseCaseProtocol = FetchExchangeRateUseCase() + public static var previewValue: FetchExchangeRateUseCaseProtocol = FetchExchangeRateUseCase() + public static var testValue: FetchExchangeRateUseCaseProtocol = FetchExchangeRateUseCase() } public extension DependencyValues { diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index e1bfb922..e345d287 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -14,9 +14,7 @@ public enum LiveDependencies { @MainActor public static func register(_ dependencies: inout DependencyValues) { // Repository 인스턴스 생성 (재사용) let oAuthRepository = OAuthRepository() - let countryRepository = CountryRepository(remote: CountryRemoteDataSource()) - let exchangeRateRepository = ExchangeRateRepository(remote: ExchangeRateRemoteDataSource()) - + // Auth & Session dependencies.authRepository = AuthRepository() dependencies.sessionRepository = SessionRepository() @@ -48,8 +46,8 @@ public enum LiveDependencies { dependencies.travelMemberRepository = TravelMemberRepository(remote: TravelMemberRemoteDataSource()) // Country & Exchange - dependencies.fetchCountriesUseCase = FetchCountriesUseCase(repository: countryRepository) - dependencies.fetchExchangeRateUseCase = FetchExchangeRateUseCase(repository: exchangeRateRepository) + dependencies.countryRepository = CountryRepository(remote: CountryRemoteDataSource()) + dependencies.exchangeRateRepository = ExchangeRateRepository(remote: ExchangeRateRemoteDataSource()) // Settlement dependencies.settlementRepository = SettlementRepository(remote: SettlementRemoteDataSource()) From bda5678979bbc86f2ca2efb10c223992bceecbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 16 Dec 2025 20:11:44 +0900 Subject: [PATCH 10/10] refactor: oAuth --- .../Country/CountryRepositoryProtocol.swift | 4 +- .../Apple/AppleOAuthRepositoryProtocol.swift | 6 +- .../GoogleOAuthRepositoryProtocol.swift | 10 +- .../Kakao/KakaoOAuthRepositoryProtocol.swift | 8 +- .../Sources/UseCase/OAuth/OAuthUseCase.swift | 244 ++++++++---------- .../UseCase/OAuth/OAuthUseCaseProtocol.swift | 14 +- .../UseCase/OAuth/UnifiedOAuthUseCase.swift | 5 +- .../Application/LiveDependencies.swift | 41 ++- 8 files changed, 149 insertions(+), 183 deletions(-) diff --git a/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift b/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift index b40e9b18..0b3aa91a 100644 --- a/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift +++ b/Domain/Sources/Repository/Country/CountryRepositoryProtocol.swift @@ -17,8 +17,8 @@ public struct CountryRepositoryDependencyKey: DependencyKey { public static var liveValue: CountryRepositoryProtocol { fatalError("CountryRepositoryDependency liveValue not implemented") } - public static var previewValue: CountryRepositoryProtocol = MockCountryRepository() - public static var testValue: CountryRepositoryProtocol = MockCountryRepository() + public static var previewValue: CountryRepositoryProtocol = MockCountriesRepository() + public static var testValue: CountryRepositoryProtocol = MockCountriesRepository() } public extension DependencyValues { diff --git a/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift index 62a0c663..dbacc147 100644 --- a/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/Apple/AppleOAuthRepositoryProtocol.swift @@ -13,7 +13,7 @@ public protocol AppleOAuthRepositoryProtocol { } // MARK: - Dependencies -public struct AppleOAuthRepositoryDependency: DependencyKey { +public struct AppleOAuthRepositoryDependencyKey: DependencyKey { public static var liveValue: AppleOAuthRepositoryProtocol { fatalError("AppleOAuthServiceDependency liveValue not implemented") } @@ -23,7 +23,7 @@ public struct AppleOAuthRepositoryDependency: DependencyKey { public extension DependencyValues { var appleOAuthRepository: AppleOAuthRepositoryProtocol { - get { self[AppleOAuthRepositoryDependency.self] } - set { self[AppleOAuthRepositoryDependency.self] = newValue } + get { self[AppleOAuthRepositoryDependencyKey.self] } + set { self[AppleOAuthRepositoryDependencyKey.self] = newValue } } } diff --git a/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift index 3555f434..e885772e 100644 --- a/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/Google/GoogleOAuthRepositoryProtocol.swift @@ -1,5 +1,5 @@ // -// GoogleOAuthServicing.swift +// GoogleOAuthRepositoryProtocol.swift // Domain // // Created by Wonji Suh on 11/17/25. @@ -13,7 +13,7 @@ public protocol GoogleOAuthRepositoryProtocol { } // MARK: - Dependencies -public struct GoogleOAuthRespositoryDependency: DependencyKey { +public struct GoogleOAuthRepositoryDependencyKey: DependencyKey { public static var liveValue: GoogleOAuthRepositoryProtocol { fatalError("GoogleOAuthServiceDependency liveValue not implemented") } @@ -22,8 +22,8 @@ public struct GoogleOAuthRespositoryDependency: DependencyKey { } public extension DependencyValues { - var googleOAuthService: GoogleOAuthRepositoryProtocol { - get { self[GoogleOAuthRespositoryDependency.self] } - set { self[GoogleOAuthRespositoryDependency.self] = newValue } + var googleOAuthRepository: GoogleOAuthRepositoryProtocol { + get { self[GoogleOAuthRepositoryDependencyKey.self] } + set { self[GoogleOAuthRepositoryDependencyKey.self] = newValue } } } diff --git a/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift b/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift index 7a63d0d6..defb3794 100644 --- a/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift +++ b/Domain/Sources/Repository/OAuth/Kakao/KakaoOAuthRepositoryProtocol.swift @@ -13,17 +13,17 @@ public protocol KakaoOAuthRepositoryProtocol { } // MARK: - Dependencies -public struct KakaoOAuthRepositoryDependency: DependencyKey { +public struct KakaoOAuthRepositoryDependencyKey: DependencyKey { public static var liveValue: KakaoOAuthRepositoryProtocol { fatalError("KakaoOAuthRepositoryDependency liveValue not implemented") - } + } public static var previewValue: KakaoOAuthRepositoryProtocol = MockKakaoOAuthRepository() public static var testValue: KakaoOAuthRepositoryProtocol = MockKakaoOAuthRepository() } public extension DependencyValues { var kakaoOAuthRepository: KakaoOAuthRepositoryProtocol { - get { self[KakaoOAuthRepositoryDependency.self] } - set { self[KakaoOAuthRepositoryDependency.self] = newValue } + get { self[KakaoOAuthRepositoryDependencyKey.self] } + set { self[KakaoOAuthRepositoryDependencyKey.self] = newValue } } } \ No newline at end of file diff --git a/Domain/Sources/UseCase/OAuth/OAuthUseCase.swift b/Domain/Sources/UseCase/OAuth/OAuthUseCase.swift index b6087e7c..67235258 100644 --- a/Domain/Sources/UseCase/OAuth/OAuthUseCase.swift +++ b/Domain/Sources/UseCase/OAuth/OAuthUseCase.swift @@ -6,162 +6,138 @@ // import Foundation -import ComposableArchitecture +import Dependencies import LogMacro import AuthenticationServices public struct OAuthUseCase: OAuthUseCaseProtocol { - private let repository: OAuthRepositoryProtocol - private let googleRepository: GoogleOAuthRepositoryProtocol - private let appleRepository: AppleOAuthRepositoryProtocol - private let kakaoRepository: KakaoOAuthRepositoryProtocol - - public init( - repository: OAuthRepositoryProtocol, - googleRepository: GoogleOAuthRepositoryProtocol, - appleRepository: AppleOAuthRepositoryProtocol, - kakaoRepository: KakaoOAuthRepositoryProtocol - ) { - self.repository = repository - self.googleRepository = googleRepository - self.appleRepository = appleRepository - self.kakaoRepository = kakaoRepository - } - - public func signInWithApple( - credential: ASAuthorizationAppleIDCredential, - nonce: String - ) async throws -> UserProfile { - guard let identityTokenData = credential.identityToken, - let identityToken = String(data: identityTokenData, encoding: .utf8), - let authCode = String(data: credential.authorizationCode ?? Data(), encoding: .utf8) - else { - throw AuthError.missingIDToken + @Dependency(\.oAuthRepository) private var repository: OAuthRepositoryProtocol + @Dependency(\.googleOAuthRepository) private var googleRepository: GoogleOAuthRepositoryProtocol + @Dependency(\.appleOAuthRepository) private var appleRepository: AppleOAuthRepositoryProtocol + @Dependency(\.kakaoOAuthRepository) private var kakaoRepository: KakaoOAuthRepositoryProtocol + + public init() {} + + public func signInWithApple( + credential: ASAuthorizationAppleIDCredential, + nonce: String + ) async throws -> UserProfile { + guard let identityTokenData = credential.identityToken, + let identityToken = String(data: identityTokenData, encoding: .utf8), + let authCode = String(data: credential.authorizationCode ?? Data(), encoding: .utf8) + else { + throw AuthError.missingIDToken + } + + let displayName = formatDisplayName(credential.fullName) + Log.info("Apple sign-in credential received for \(displayName ?? "unknown user")") + + let profile = try await repository.signIn( + provider: .apple, + idToken: identityToken, + nonce: nonce, + displayName: displayName, + authorizationCode: authCode + ) + Log.info("Supabase sign-in with Apple succeeded") + return profile } - - let displayName = formatDisplayName(credential.fullName) - Log.info("Apple sign-in credential received for \(displayName ?? "unknown user")") - - let profile = try await repository.signIn( - provider: .apple, - idToken: identityToken, - nonce: nonce, - displayName: displayName, - authorizationCode: authCode - ) - Log.info("Supabase sign-in with Apple succeeded") - return profile - } - - // MARK: - Helper Methods - private func formatDisplayName( - _ components: PersonNameComponents? - ) -> String? { - guard let components else { return nil } - let formatter = PersonNameComponentsFormatter() - let name = formatter.string(from: components).trimmingCharacters(in: .whitespacesAndNewlines) - return name.isEmpty ? nil : name - } - + + // MARK: - Helper Methods + private func formatDisplayName( + _ components: PersonNameComponents? + ) -> String? { + guard let components else { return nil } + let formatter = PersonNameComponentsFormatter() + let name = formatter.string(from: components).trimmingCharacters(in: .whitespacesAndNewlines) + return name.isEmpty ? nil : name + } + public func signUp( - with provider: SocialType + with provider: SocialType ) async throws -> UserProfile { // Kakao는 Supabase OAuth를 거치지 않고 Kakao SDK 토큰을 그대로 사용 if provider == .kakao { let kakaoPayload = try await kakaoRepository.signIn() let tokens = AuthTokens( - authToken: kakaoPayload.authorizationCode ?? "", - accessToken: kakaoPayload.accessToken, - refreshToken: kakaoPayload.refreshToken ?? "", - sessionID: "" + authToken: kakaoPayload.authorizationCode ?? "", + accessToken: kakaoPayload.accessToken, + refreshToken: kakaoPayload.refreshToken ?? "", + sessionID: "" ) return UserProfile( - id: kakaoPayload.authorizationCode ?? UUID().uuidString, - email: nil, - displayName: kakaoPayload.displayName, - provider: .kakao, - tokens: tokens, - authCode: kakaoPayload.authorizationCode, - codeVerifier: kakaoPayload.codeVerifier + id: kakaoPayload.authorizationCode ?? UUID().uuidString, + email: nil, + displayName: kakaoPayload.displayName, + provider: .kakao, + tokens: tokens, + authCode: kakaoPayload.authorizationCode, + codeVerifier: kakaoPayload.codeVerifier ) } - - let payload = try await fetchPayload(for: provider) - Log.info("\(provider.rawValue) sign-in succeeded for \(payload.displayName ?? "unknown user")") - - let profile = try await repository.signIn( - provider: payload.provider, - idToken: payload.idToken, - nonce: payload.nonce, - displayName: payload.displayName, - authorizationCode: payload.authorizationCode - ) - Log.info("Supabase sign-in with \(provider.rawValue) succeeded") - - return profile - } - - private func fetchPayload( - for provider: SocialType - ) async throws -> OAuthSignInPayload { - switch provider { - case .apple: - let payload = try await appleRepository.signIn() - return OAuthSignInPayload( - provider: .apple, - idToken: payload.idToken, - nonce: payload.nonce, - displayName: payload.displayName, - authorizationCode: payload.authorizationCode + + let payload = try await fetchPayload(for: provider) + Log.info("\(provider.rawValue) sign-in succeeded for \(payload.displayName ?? "unknown user")") + + let profile = try await repository.signIn( + provider: payload.provider, + idToken: payload.idToken, + nonce: payload.nonce, + displayName: payload.displayName, + authorizationCode: payload.authorizationCode ) - case .google: - let payload = try await googleRepository.signIn() - return OAuthSignInPayload( - provider: .google, - idToken: payload.idToken, - nonce: nil, - displayName: payload.displayName, - authorizationCode: payload.authorizationCode - ) - case .kakao: - // Kakao는 SDK 토큰을 바로 사용하므로 여기서는 빈 페이로드 반환 - return OAuthSignInPayload( - provider: .kakao, - idToken: "", - nonce: nil, - displayName: nil, - authorizationCode: nil - ) - case .none: - throw AuthError.configurationMissing + Log.info("Supabase sign-in with \(provider.rawValue) succeeded") + + return profile + } + + private func fetchPayload( + for provider: SocialType + ) async throws -> OAuthSignInPayload { + switch provider { + case .apple: + let payload = try await appleRepository.signIn() + return OAuthSignInPayload( + provider: .apple, + idToken: payload.idToken, + nonce: payload.nonce, + displayName: payload.displayName, + authorizationCode: payload.authorizationCode + ) + case .google: + let payload = try await googleRepository.signIn() + return OAuthSignInPayload( + provider: .google, + idToken: payload.idToken, + nonce: nil, + displayName: payload.displayName, + authorizationCode: payload.authorizationCode + ) + case .kakao: + // Kakao는 SDK 토큰을 바로 사용하므로 여기서는 빈 페이로드 반환 + return OAuthSignInPayload( + provider: .kakao, + idToken: "", + nonce: nil, + displayName: nil, + authorizationCode: nil + ) + case .none: + throw AuthError.configurationMissing + } } - } } // MARK: - Dependencies extension OAuthUseCase: DependencyKey { - public static var liveValue: OAuthUseCaseProtocol = { - return OAuthUseCase( - repository: MockOAuthRepository(), - googleRepository: MockGoogleOAuthRepository(), - appleRepository: MockAppleOAuthRepository(), - kakaoRepository: MockKakaoOAuthRepository() - ) - }() - public static var previewValue: OAuthUseCaseProtocol = liveValue - public static var testValue: OAuthUseCaseProtocol = { - return OAuthUseCase( - repository: MockOAuthRepository(), - googleRepository: MockGoogleOAuthRepository(), - appleRepository: MockAppleOAuthRepository(), - kakaoRepository: MockKakaoOAuthRepository() - ) - }() + public static var liveValue: OAuthUseCaseProtocol = OAuthUseCase() + public static var previewValue: OAuthUseCaseProtocol = OAuthUseCase() + public static var testValue: OAuthUseCaseProtocol = OAuthUseCase() } public extension DependencyValues { - var oAuthUseCase: OAuthUseCaseProtocol { - get { self[OAuthUseCase.self] } - set { self[OAuthUseCase.self] = newValue } - } + var oAuthUseCase: OAuthUseCaseProtocol { + get { self[OAuthUseCase.self] } + set { self[OAuthUseCase.self] = newValue } + } } diff --git a/Domain/Sources/UseCase/OAuth/OAuthUseCaseProtocol.swift b/Domain/Sources/UseCase/OAuth/OAuthUseCaseProtocol.swift index d9609c93..3414780f 100644 --- a/Domain/Sources/UseCase/OAuth/OAuthUseCaseProtocol.swift +++ b/Domain/Sources/UseCase/OAuth/OAuthUseCaseProtocol.swift @@ -8,11 +8,13 @@ import Foundation import AuthenticationServices - public protocol OAuthUseCaseProtocol { - func signInWithApple( - credential: ASAuthorizationAppleIDCredential, - nonce: String - ) async throws -> UserProfile - func signUp(with provider: SocialType) async throws -> UserProfile + func signInWithApple( + credential: ASAuthorizationAppleIDCredential, + nonce: String + ) async throws -> UserProfile + + func signUp( + with provider: SocialType + ) async throws -> UserProfile } diff --git a/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift b/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift index dcfe65b5..b664b86c 100644 --- a/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift +++ b/Domain/Sources/UseCase/OAuth/UnifiedOAuthUseCase.swift @@ -12,15 +12,13 @@ import AuthenticationServices /// 통합 OAuth UseCase - 로그인/회원가입 플로우를 하나로 통합 (AuthFacade 역할) public struct UnifiedOAuthUseCase { - private let oAuthUseCase: any OAuthUseCaseProtocol + @Dependency(\.oAuthUseCase) private var oAuthUseCase: any OAuthUseCaseProtocol @Dependency(\.authRepository) private var authRepository: AuthRepositoryProtocol private let sessionStoreRepository: any SessionStoreRepositoryProtocol public init( - oAuthUseCase: any OAuthUseCaseProtocol = OAuthUseCase.liveValue, sessionStoreRepository: any SessionStoreRepositoryProtocol = SessionStoreRepository() ) { - self.oAuthUseCase = oAuthUseCase self.sessionStoreRepository = sessionStoreRepository } } @@ -400,7 +398,6 @@ extension UnifiedOAuthUseCase: DependencyKey { public static let liveValue = UnifiedOAuthUseCase() public static let testValue = UnifiedOAuthUseCase( - oAuthUseCase: OAuthUseCase.testValue, sessionStoreRepository: SessionStoreRepository() ) } diff --git a/SseuDamApp/Sources/Application/LiveDependencies.swift b/SseuDamApp/Sources/Application/LiveDependencies.swift index e345d287..0c974eb5 100644 --- a/SseuDamApp/Sources/Application/LiveDependencies.swift +++ b/SseuDamApp/Sources/Application/LiveDependencies.swift @@ -11,22 +11,19 @@ import Data import Domain public enum LiveDependencies { - @MainActor public static func register(_ dependencies: inout DependencyValues) { - // Repository 인스턴스 생성 (재사용) - let oAuthRepository = OAuthRepository() - + @MainActor + public static func register(_ dependencies: inout DependencyValues) { // Auth & Session dependencies.authRepository = AuthRepository() dependencies.sessionRepository = SessionRepository() - dependencies.profileRepository = ProfileRepository() - dependencies.versionRepository = VersionRepository() - - let oAuthUseCase = makeOAuthUseCase(repository: oAuthRepository) - dependencies.oAuthUseCase = oAuthUseCase - dependencies.unifiedOAuthUseCase = UnifiedOAuthUseCase( - oAuthUseCase: oAuthUseCase, - sessionStoreRepository: SessionStoreRepository() + + dependencies.oAuthRepository = OAuthRepository() + dependencies.googleOAuthRepository = GoogleOAuthRepository() + dependencies.appleOAuthRepository = AppleOAuthRepository() + dependencies.kakaoOAuthRepository = KakaoOAuthRepository( + presentationContextProvider: AppPresentationContextProvider() ) + // Analytics dependencies.analyticsRepository = FirebaseAnalyticsRepository() @@ -51,18 +48,12 @@ public enum LiveDependencies { // Settlement dependencies.settlementRepository = SettlementRepository(remote: SettlementRemoteDataSource()) - } - - // MARK: - Factory Methods - @MainActor - private static func makeOAuthUseCase(repository: OAuthRepository) -> OAuthUseCaseProtocol { - OAuthUseCase( - repository: repository, - googleRepository: GoogleOAuthRepository(), - appleRepository: AppleOAuthRepository(), - kakaoRepository: KakaoOAuthRepository( - presentationContextProvider: AppPresentationContextProvider() - ) - ) + + // Profile + dependencies.profileRepository = ProfileRepository() + + // AppVersion + dependencies.versionRepository = VersionRepository() + } }