Skip to content

Commit b04683d

Browse files
authored
Merge branch 'develop' into TNT-213-TrainerTraineeList
2 parents eaa3848 + f75cf83 commit b04683d

File tree

79 files changed

+2023
-508
lines changed

Some content is hidden

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

79 files changed

+2023
-508
lines changed

TnT/Projects/DIContainer/Sources/DIContainer.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,17 @@ private enum TrainerUseCaseRepoKey: DependencyKey {
3434
static let liveValue: TrainerRepository = DefaultTrainerUseCase(trainerRepository: TrainerRepositoryImpl())
3535
}
3636

37+
private enum KeyChainManagerKey: DependencyKey {
38+
static let liveValue: KeyChainManager = keyChainManager
39+
}
40+
3741
// MARK: - DependencyValues
3842
public extension DependencyValues {
43+
var keyChainManager: KeyChainManager {
44+
get { self[KeyChainManagerKey.self] }
45+
set { self[KeyChainManagerKey.self] = newValue }
46+
}
47+
3948
var userUseCase: UserUseCase {
4049
get { self[UserUseCaseKey.self] }
4150
set { self[UserUseCaseKey.self] = newValue }

TnT/Projects/Data/Sources/LocalStorage/KeyChainManager.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import Foundation
1010

11+
public let keyChainManager = KeyChainManager()
12+
1113
/// KeyChainManager
1214
/// - 키체인을 통해 데이터를 CRUD 하기 위한 유틸리티입니다.
1315
public struct KeyChainManager {
@@ -18,7 +20,7 @@ public struct KeyChainManager {
1820
/// - value: 저장할 데이터 (Generic 타입)
1921
/// - key: Key 열거형으로 정의된 키
2022
/// - Throws: 타입 불일치, 데이터 변환 실패, 키체인 저장 실패 에러
21-
public static func save<T>(_ value: T, for key: Key) throws {
23+
public func save<T>(_ value: T, for key: Key) throws {
2224

2325
guard type(of: value) == key.converter.type else {
2426
throw KeyChainError.typeMismatch(
@@ -50,7 +52,7 @@ public struct KeyChainManager {
5052
/// - Parameter key: Key 열거형으로 정의된 키
5153
/// - Returns: Generic 타입으로 변환된 값 (데이터가 없으면 nil)
5254
/// - Throws: 데이터 변환 실패, 읽기 실패, 타입 불일치 에러
53-
public static func read<T>(for key: Key) throws -> T? {
55+
public func read<T>(for key: Key) throws -> T? {
5456
let keyString: String = key.keyString
5557

5658
let query: [String: Any] = [
@@ -89,7 +91,7 @@ public struct KeyChainManager {
8991
/// 키체인에서 데이터를 삭제합니다.
9092
/// - Parameter key: Key 열거형으로 정의된 키
9193
/// - Throws: 삭제 실패 에러
92-
public static func delete(_ key: Key) throws {
94+
public func delete(_ key: Key) throws {
9395
let keyString: String = key.keyString
9496

9597
let query: [String: Any] = [
@@ -107,21 +109,21 @@ public struct KeyChainManager {
107109
public extension KeyChainManager {
108110
/// Key 열거형: 키체인에 저장할 데이터를 정의
109111
enum Key {
110-
case token
112+
case sessionId
111113
case userId
112114

113115
/// 키 고유 문자열
114116
var keyString: String {
115117
switch self {
116-
case .token: return "com.TnT.token"
118+
case .sessionId: return "com.TnT.sessionId"
117119
case .userId: return "com.TnT.userId"
118120
}
119121
}
120122

121123
/// 각 데이터 별 타입 & 변환 로직 정의
122124
var converter: KeyConverter {
123125
switch self {
124-
case .token: return KeyConverter(type: String.self, convert: { $0 })
126+
case .sessionId: return KeyConverter(type: String.self, convert: { $0 })
125127
case .userId: return KeyConverter(type: Int.self, convert: { Int($0) })
126128
}
127129
}

TnT/Projects/Data/Sources/Network/Interceptor/Implements/AuthToken.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@
77
//
88

99
import Foundation
10+
import Dependencies
1011

1112
/// 네트워크 요청에 인증 정보를 추가하는 인터셉터
1213
struct AuthTokenInterceptor: Interceptor {
1314
let priority: InterceptorPriority = .highest
1415

1516
func adapt(request: URLRequest) async throws -> URLRequest {
1617
var request: URLRequest = request
17-
guard let token: String = try KeyChainManager.read(for: .token) else {
18+
guard let sessionId: String = try keyChainManager.read(for: .sessionId) else {
1819
return request
1920
}
20-
request.setValue(token, forHTTPHeaderField: "Authorization")
21+
request.setValue("SESSION-ID \(sessionId)", forHTTPHeaderField: "Authorization")
2122
return request
2223
}
2324
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// ProgressIndicator.swift
3+
// Data
4+
//
5+
// Created by 박민서 on 2/9/25.
6+
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
import Domain
12+
13+
struct ProgressIndicatorInterceptor: Interceptor {
14+
let priority: InterceptorPriority = .normal
15+
16+
func adapt(request: URLRequest) async throws -> URLRequest {
17+
NotificationCenter.default.postProgress(visible: true)
18+
return request
19+
}
20+
21+
func validate(response: URLResponse, data: Data) async throws {
22+
NotificationCenter.default.postProgress(visible: false)
23+
}
24+
}

TnT/Projects/Data/Sources/Network/NetworkService.swift

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import Foundation
1010

11+
import Domain
12+
1113
/// 네트워크 요청을 처리하는 서비스 클래스
1214
public final class NetworkService {
1315

@@ -30,15 +32,27 @@ public final class NetworkService {
3032
decodingType: T.Type
3133
) async throws -> T {
3234
let pipeline: InterceptorPipeline = InterceptorPipeline(interceptors: target.interceptors)
33-
// URL Request 생성
34-
var request: URLRequest = try buildRequest(from: target)
35-
request = try await pipeline.adapt(request)
36-
37-
// Request 수행
38-
let data: Data = try await executeRequest(request, pipeline: pipeline)
39-
40-
// Data 디코딩
41-
return try decodeData(data, as: decodingType)
35+
do {
36+
// URL Request 생성
37+
var request: URLRequest = try buildRequest(from: target)
38+
request = try await pipeline.adapt(request)
39+
40+
// Request 수행
41+
let data: Data = try await executeRequest(request, pipeline: pipeline)
42+
43+
// Data 디코딩
44+
return try decodeData(data, as: decodingType)
45+
} catch let error as NetworkError {
46+
// TODO: 추후 인터셉터 리팩토링 시 error middleWare로 분리
47+
NotificationCenter.default.postProgress(visible: false)
48+
switch error {
49+
case .unauthorized:
50+
NotificationCenter.default.postSessionExpired()
51+
default:
52+
NotificationCenter.default.post(toast: .init(presentType: .text(""), message: "서버 요청에 실패했어요"))
53+
}
54+
throw error
55+
}
4256
}
4357
}
4458

@@ -85,6 +99,9 @@ private extension NetworkService {
8599

86100
/// JSON 데이터 디코딩
87101
func decodeData<T: Decodable>(_ data: Data, as type: T.Type) throws -> T {
102+
// 빈 데이터인 경우 EmptyResponse 타입으로 처리
103+
if data.isEmpty, let emptyValue = EmptyResponse() as? T { return emptyValue }
104+
88105
do {
89106
return try JSONDecoder(setting: .defaultSetting).decode(T.self, from: data)
90107
} catch let decodingError as DecodingError {

TnT/Projects/Data/Sources/Network/Service/SocialLogin/SNSLoginManager.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ extension SNSLoginManager: ASAuthorizationControllerDelegate, ASWebAuthenticatio
8282

8383
guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else {
8484
self.appleLoginCompletion?(nil)
85+
self.appleLoginCompletion = nil
8586
return
8687
}
8788

@@ -92,6 +93,7 @@ extension SNSLoginManager: ASAuthorizationControllerDelegate, ASWebAuthenticatio
9293
let authorizationCodeString = String(data: authorizationCode, encoding: .utf8)
9394
else {
9495
self.appleLoginCompletion?(nil)
96+
self.appleLoginCompletion = nil
9597
return
9698
}
9799

@@ -101,12 +103,14 @@ extension SNSLoginManager: ASAuthorizationControllerDelegate, ASWebAuthenticatio
101103
authorizationCode: authorizationCodeString
102104
)
103105
)
106+
self.appleLoginCompletion = nil
104107
}
105108

106109
// 애플 로그인 실패 시 nil 리턴
107110
public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
108111
print("Apple login failed: \(error.localizedDescription)")
109112
self.appleLoginCompletion?(nil)
113+
self.appleLoginCompletion = nil
110114
}
111115

112116
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {

TnT/Projects/Data/Sources/Network/Service/Trainee/TraineeTargetType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ extension TraineeTargetType: TargetType {
5454
return [
5555
LoggingInterceptor(),
5656
AuthTokenInterceptor(),
57+
ProgressIndicatorInterceptor(),
5758
ResponseValidatorInterceptor(),
5859
RetryInterceptor(maxRetryCount: 0)
5960
]
6061
}
6162
}
62-

TnT/Projects/Data/Sources/Network/Service/Trainer/TrainerTargetType.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ extension TrainerTargetType: TargetType {
9494
return [
9595
LoggingInterceptor(),
9696
AuthTokenInterceptor(),
97+
ProgressIndicatorInterceptor(),
9798
ResponseValidatorInterceptor(),
9899
RetryInterceptor(maxRetryCount: 0)
99100
]

TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ public struct UserRepositoryImpl: UserRepository {
1717

1818
public init() {}
1919

20+
/// 로그인 세션 유효 체크 요청을 수행
21+
public func getSessionCheck() async throws -> GetSessionCheckResDTO {
22+
return try await networkService.request(UserTargetType.getSessionCheck, decodingType: GetSessionCheckResDTO.self)
23+
}
24+
2025
/// 소셜 로그인 요청을 수행
2126
public func postSocialLogin(_ reqDTO: PostSocialLoginReqDTO) async throws -> PostSocialLoginResDTO {
2227
return try await networkService.request(
@@ -35,4 +40,14 @@ public struct UserRepositoryImpl: UserRepository {
3540
decodingType: PostSignUpResDTO.self
3641
)
3742
}
43+
44+
/// 로그아웃 요청을 수행
45+
public func postLogout() async throws -> PostLogoutResDTO {
46+
return try await networkService.request(UserTargetType.postLogout, decodingType: PostLogoutResDTO.self)
47+
}
48+
49+
/// 회원탈퇴 요청을 수행
50+
public func postWithdrawal() async throws -> PostWithdrawalResDTO {
51+
return try await networkService.request(UserTargetType.postWithdrawal, decodingType: PostWithdrawalResDTO.self)
52+
}
3853
}

TnT/Projects/Data/Sources/Network/Service/User/UserTargetType.swift

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ import Domain
1212

1313
/// 사용자 관련 API 요청 타입 정의
1414
public enum UserTargetType {
15+
/// 로그인 세션 유효 확인
16+
case getSessionCheck
1517
/// 소셜 로그인 요청
1618
case postSocialLogin(reqDTO: PostSocialLoginReqDTO)
1719
/// 회원가입 요청
1820
case postSignUp(reqDTO: PostSignUpReqDTO, imgData: Data?)
21+
/// 로그아웃 요청
22+
case postLogout
23+
/// 회원 탈퇴 요청
24+
case postWithdrawal
1925
}
2026

2127
extension UserTargetType: TargetType {
@@ -25,23 +31,38 @@ extension UserTargetType: TargetType {
2531

2632
var path: String {
2733
switch self {
34+
case .getSessionCheck:
35+
return "/check-session"
36+
2837
case .postSocialLogin:
2938
return "/login"
3039

3140
case .postSignUp:
3241
return "/members/sign-up"
42+
43+
case .postLogout:
44+
return "/logout"
45+
46+
case .postWithdrawal:
47+
return "/members/withdraw"
3348
}
3449
}
3550

3651
var method: HTTPMethod {
3752
switch self {
38-
case .postSocialLogin, .postSignUp:
53+
case .getSessionCheck:
54+
return .get
55+
56+
case .postSocialLogin, .postSignUp, .postLogout, .postWithdrawal:
3957
return .post
4058
}
4159
}
4260

4361
var task: RequestTask {
4462
switch self {
63+
case .getSessionCheck, .postLogout, .postWithdrawal:
64+
return .requestPlain
65+
4566
case .postSocialLogin(let reqDto):
4667
return .requestJSONEncodable(encodable: reqDto)
4768

@@ -59,6 +80,9 @@ extension UserTargetType: TargetType {
5980

6081
var headers: [String: String]? {
6182
switch self {
83+
case .getSessionCheck, .postLogout, .postWithdrawal:
84+
return nil
85+
6286
case .postSocialLogin:
6387
return ["Content-Type": "application/json"]
6488

@@ -71,10 +95,22 @@ extension UserTargetType: TargetType {
7195
}
7296

7397
var interceptors: [any Interceptor] {
74-
return [
75-
LoggingInterceptor(),
76-
ResponseValidatorInterceptor(),
77-
RetryInterceptor(maxRetryCount: 2)
78-
]
98+
switch self {
99+
case .getSessionCheck, .postLogout, .postWithdrawal:
100+
return [
101+
LoggingInterceptor(),
102+
AuthTokenInterceptor(),
103+
ProgressIndicatorInterceptor(),
104+
ResponseValidatorInterceptor(),
105+
RetryInterceptor(maxRetryCount: 2)
106+
]
107+
default:
108+
return [
109+
LoggingInterceptor(),
110+
ResponseValidatorInterceptor(),
111+
ProgressIndicatorInterceptor(),
112+
RetryInterceptor(maxRetryCount: 2)
113+
]
114+
}
79115
}
80116
}

0 commit comments

Comments
 (0)