Skip to content

Commit 79b682d

Browse files
authored
Feat: 모듈 구조 변경 및 토큰 로직
* Refactor: NetworkService, Persistence 모듈 DataSource 모듈 합치기 * Refactor: Then 외부 라이브러리 제거 및 사용 코드 제거 * Feat: TokenManager 구현 * Refactor: Endpoint에 isAuthorized 값 추가 - Endpoint에 isAuthorized 값 추가 (인증이 필요한지 여부) - 그렇다면 Endpoint 만들 때 header에 TokenManager에서 accessToken 빼와서 저장 * Refactor: UserDataRepository에 token 재발급 로직 추가 * Feat: 로그인 시 소셜 로그인 타입, 프로필 이미지 저장 - 카카오 로그인 시 nickname, profileImageUrl을 KakaoSDK에서 받아와 저장하도록 로직 추가 - 카카오, 애플 로그인 시 소셜 로그인 타입 저장하도록 로직 추가 * Fix: 온보딩 서버 Request Enum 값 변경 - 온보딩 서버 Request Enum 값 변경 - 온보딩 UI 수정 (SubTitle 여부) * Feat: UserDataRepository에서 토큰 재발급 가능 여부에 따라 앱 진입점 분기처리 * Fix: 토큰 재발급 로직 Response 타입 변경 - TokenResponseDTO 구현 - reissue response 타입 교체 * Style: 불필요한 주석 제거 * Style: UserDataRepository 오타 수정 * Refactor: 코드 리뷰 반영 - NetworkService에서 endpoint.isAuthorized 값 확인하여 헤더에 토큰 값 세팅해주는 로직으로 수정 - 카카오 유저 정보 (닉네임, 프로필 이미지 URL) 메인스레드에서 동작하는 것 제거 - SceneDelegate에서 SplashView를 먼저 표시하고, reissueToken 결과에 따라 rootViewController 교체하도록 구조 개선 * Refactor: import SnapKit 추가
1 parent b28498b commit 79b682d

66 files changed

Lines changed: 577 additions & 711 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Projects/App/Project.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ let project = Project(
2525
.project(target: "Presentation", path: "../Presentation"),
2626
.project(target: "Domain", path: "../Domain"),
2727
.project(target: "DataSource", path: "../DataSource"),
28-
.project(target: "NetworkService", path: "../NetworkService"),
29-
.project(target: "Persistence", path: "../Persistence"),
3028
.project(target: "Shared", path: "../Shared")
3129
]
3230
)

Projects/App/Sources/DependencyInjection.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,12 @@
88
import DataSource
99
import Domain
1010
import Foundation
11-
import NetworkService
12-
import Persistence
1311
import Presentation
1412
import Shared
1513

1614
extension DIContainer {
1715
func dependencyInjection() {
18-
let networkAssembler = NetworkDependencyAssembler()
19-
let persistenceAssembler = PersistenceDependencyAssembler()
20-
let dataSourceAssembler = DataSourceDependencyAssembler(preAssemblers: [networkAssembler, persistenceAssembler])
16+
let dataSourceAssembler = DataSourceDependencyAssembler()
2117
let domainAssembler = DomainDependencyAssembler(preAssembler: dataSourceAssembler)
2218
let presentationAssembler = PresentationDependencyAssembler(preAssembler: domainAssembler)
2319
presentationAssembler.assemble()

Projects/App/Sources/SceneDelegate.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created by 최정인 on 6/15/25.
66
//
77

8+
import Domain
89
import KakaoSDKAuth
910
import Presentation
1011
import Shared
@@ -19,13 +20,23 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
1920

2021
DIContainer.shared.dependencyInjection()
2122

22-
let introView = IntroView()
23-
let navigationController = UINavigationController(rootViewController: introView)
23+
guard let userDataRepository = DIContainer.shared.resolve(type: UserDataRepositoryProtocol.self)
24+
else { fatalError("userDataRepository 의존성이 등록되지 않았습니다.") }
2425

25-
window.rootViewController = navigationController
26+
window.rootViewController = SplashView()
2627
window.makeKeyAndVisible()
27-
2828
self.window = window
29+
30+
Task { @MainActor in
31+
let isLogined = await userDataRepository.reissueToken()
32+
if isLogined {
33+
window.rootViewController = TabBarView()
34+
} else {
35+
let introView = IntroView()
36+
let navigationController = UINavigationController(rootViewController: introView)
37+
window.rootViewController = navigationController
38+
}
39+
}
2940
}
3041

3142
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {

Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,24 @@
55
// Created by 최정인 on 6/26/25.
66
//
77

8-
import Foundation
98
import Domain
9+
import Foundation
1010
import Shared
1111

1212
public struct DataSourceDependencyAssembler: DependencyAssemblerProtocol {
13-
private let preAssemblers: [DependencyAssemblerProtocol]
14-
15-
public init(preAssemblers: [DependencyAssemblerProtocol]) {
16-
self.preAssemblers = preAssemblers
17-
}
13+
public init() { }
1814

1915
public func assemble() {
20-
preAssemblers.forEach { assembler in
21-
assembler.assemble()
22-
}
23-
24-
guard
25-
let networkService = DIContainer.shared.resolve(type: NetworkServiceProtocol.self),
26-
let keychainStorage = DIContainer.shared.resolve(type: KeychainStorageProtocol.self),
27-
let userDefaultsStorage = DIContainer.shared.resolve(type: UserDefaultsStorageProtocol.self)
28-
else { fatalError("networkService, keychainStorage, userDefaultsStorage 의존성이 등록되지 않았습니다.") }
29-
3016
DIContainer.shared.register(type: AuthRepositoryProtocol.self) { _ in
31-
return AuthRepository(
32-
networkService: networkService,
33-
keychainStorage: keychainStorage,
34-
userDefaultsStorage: userDefaultsStorage)
17+
return AuthRepository()
3518
}
3619

3720
DIContainer.shared.register(type: OnboardingRepositoryProtocol.self) { _ in
38-
return OnboardingRepository(networkService: networkService, keychainStorage: keychainStorage)
21+
return OnboardingRepository()
3922
}
4023

4124
DIContainer.shared.register(type: UserDataRepositoryProtocol.self) { _ in
42-
return UserDataRepository(keychainStorage: keychainStorage, userDefaultsStorage: userDefaultsStorage)
25+
return UserDataRepository()
4326
}
4427
}
4528
}

Projects/DataSource/Sources/Common/Enum/Endpoint.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ public protocol Endpoint {
1212
var headers: [String: String] { get }
1313
var queryParameters: [String: String] { get }
1414
var bodyParameters: [String: Any] { get }
15+
var isAuthorized: Bool { get }
1516
}

Projects/DataSource/Sources/Common/Enum/UserDefaultsKey.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77

88
enum UserDefaultsKey: String {
99
case nickname
10+
case socialLoginType
11+
case profileImageUrl
1012
}

Projects/DataSource/Sources/Common/Error/AuthError.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
enum AuthError: Error, CustomStringConvertible {
99
case kakaoTokenFetchFailed
10+
case kakaoUserInformationFetchFailed
1011
case tokenSaveFailed
1112
case tokenLoadFailed
1213
case tokenRemoveFailed
@@ -20,6 +21,8 @@ enum AuthError: Error, CustomStringConvertible {
2021
switch self {
2122
case .kakaoTokenFetchFailed:
2223
return "카카오 토큰을 가져오는데 실패했습니다."
24+
case .kakaoUserInformationFetchFailed:
25+
return "카카오 유저 정보를 가져오는데 실패했습니다."
2326
case .tokenSaveFailed:
2427
return "토큰 저장에 실패했습니다."
2528
case .tokenLoadFailed:
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// TokenError.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 7/26/25.
6+
//
7+
8+
enum TokenError: Error, CustomStringConvertible {
9+
case tokenSaveFailed
10+
case tokenLoadFailed
11+
case tokenRemoveFailed
12+
13+
public var description: String {
14+
switch self {
15+
case .tokenSaveFailed:
16+
return "토큰 저장에 실패했습니다."
17+
case .tokenLoadFailed:
18+
return "토큰 불러오기에 실패했습니다."
19+
case .tokenRemoveFailed:
20+
return "토큰 삭제에 실패했습니다."
21+
}
22+
}
23+
}

Projects/DataSource/Sources/Common/Error/UserError.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,37 @@
66
//
77

88
enum UserError: Error, CustomStringConvertible {
9-
case accessTokenLoadFailed
9+
case nicknameSaveFailed
1010
case nicknameLoadFailed
11+
case nicknameRemoveFailed
12+
case socialLoginTypeSaveFailed
13+
case socialLoginTypeLoadFailed
14+
case socialLoginTypeRemoveFailed
15+
case profileImageUrlSaveFailed
16+
case profileImageUrlLoadFailed
17+
case profileImageUrlRemoveFailed
1118
case unknown(error: Error)
1219

13-
1420
var description: String {
1521
switch self {
16-
case .accessTokenLoadFailed:
17-
return "토큰 불러오기에 실패했습니다."
22+
case .nicknameSaveFailed:
23+
return "닉네임 저장 실패했습니다."
1824
case .nicknameLoadFailed:
1925
return "닉네임 불러오기에 실패했습니다."
26+
case .nicknameRemoveFailed:
27+
return "닉네임 삭제 실패했습니다."
28+
case .socialLoginTypeSaveFailed:
29+
return "소셜 로그인 타입 저장 실패했습니다."
30+
case .socialLoginTypeLoadFailed:
31+
return "소셜 로그인 타입 불러오기에 실패했습니다."
32+
case .socialLoginTypeRemoveFailed:
33+
return "소셜 로그인 타입 삭제 실패했습니다."
34+
case .profileImageUrlSaveFailed:
35+
return "유저 프로필 저장 실패했습니다."
36+
case .profileImageUrlLoadFailed:
37+
return "유저 프로필 불러오기에 실패했습니다."
38+
case .profileImageUrlRemoveFailed:
39+
return "유저 프로필 삭제 실패했습니다."
2040
case .unknown(let error):
2141
return "알 수 없는 에러가 발생했습니다. \(error.localizedDescription)"
2242
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// TokenManager.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 7/26/25.
6+
//
7+
8+
final class TokenManager {
9+
static let shared = TokenManager()
10+
private let keychainStorage = KeychainStorage.shared
11+
12+
private init() { }
13+
14+
func loadToken(tokenType: TokenType) throws -> String {
15+
guard let token: String = keychainStorage.load(forKey: tokenType.rawValue)
16+
else { throw TokenError.tokenLoadFailed }
17+
return token
18+
}
19+
20+
func saveToken(token: String, tokenType: TokenType) throws {
21+
guard keychainStorage.save(token, forKey: tokenType.rawValue)
22+
else { throw TokenError.tokenSaveFailed }
23+
}
24+
25+
func removeToken() throws {
26+
guard
27+
keychainStorage.remove(forKey: TokenType.accessToken.rawValue),
28+
keychainStorage.remove(forKey: TokenType.refreshToken.rawValue)
29+
else { throw TokenError.tokenRemoveFailed }
30+
}
31+
}

0 commit comments

Comments
 (0)