Skip to content

Commit d61d940

Browse files
committed
Feat: 이용 약관 동의 로직 구현 (#T3-86)
- 기존 TokenResponseDTO에서 LoginResponseDTO로 수정 (서버 응답에 User의 role이 추가되었음) - AuthEndpoint에 agreements 케이스 추가 - AuthRepositoryProtocol 및 AuthRepository에 submitAgreements 정의 및 구현 - LoginUseCaseProtocol 및 LoginUseCase에 submitAgreements 정의 및 구현 - LoginViewModel에 submitAgreement 로직 구현
1 parent b7776d1 commit d61d940

17 files changed

Lines changed: 149 additions & 58 deletions

File tree

Projects/DataSource/Sources/DTO/BaseResponseDTO.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ struct BaseResponseDTO<T: Decodable>: Decodable {
1010
let data: T?
1111
let message: String
1212
}
13+
14+
struct EmptyResponse: Decodable {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// TokenResponseDTO.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 6/30/25.
6+
//
7+
8+
import Domain
9+
10+
typealias LoginResponseDTO = BaseResponseDTO<LoginResponse>
11+
12+
struct LoginResponse: Decodable {
13+
let accessToken: String
14+
let refreshToken: String
15+
let userState: String
16+
17+
enum CodingKeys: String, CodingKey {
18+
case accessToken
19+
case refreshToken
20+
case userState = "role"
21+
}
22+
}
23+
24+
extension LoginResponse {
25+
func toUserEntity() -> UserEntity {
26+
return UserEntity(
27+
accessToken: accessToken,
28+
refreshToken: refreshToken,
29+
userState: UserState(rawValue: userState) ?? .guest)
30+
}
31+
}

Projects/DataSource/Sources/DTO/TokenResponseDTO.swift

Lines changed: 0 additions & 21 deletions
This file was deleted.

Projects/DataSource/Sources/Endpoints/AuthEndpoint.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
//
77

88
import Foundation
9+
import Domain
910

1011
enum AuthEndpoint {
1112
case login(socialLoginType: SocialLoginType, nickname: String?, token: String)
1213
case logout(accessToken: String)
1314
case withdraw(accessToken: String)
1415
case reissue(refreshToken: String)
16+
case agreements(accessToken: String, agreements: [TermsType: Bool])
1517
}
1618

1719
extension AuthEndpoint: Endpoint {
@@ -25,6 +27,7 @@ extension AuthEndpoint: Endpoint {
2527
case .logout: baseURL + "/logout"
2628
case .withdraw: baseURL + "/withdrawal"
2729
case .reissue: baseURL + "/token/reissue"
30+
case .agreements: baseURL + "/agreements"
2831
}
2932
}
3033

@@ -47,6 +50,8 @@ extension AuthEndpoint: Endpoint {
4750
headers["Authorization"] = "Bearer \(accessToken)"
4851
case .reissue(let refreshToken):
4952
headers["Refresh-Token"] = refreshToken
53+
case .agreements(let accessToken, _):
54+
headers["Authorization"] = "Bearer \(accessToken)"
5055
}
5156

5257
return headers
@@ -64,8 +69,24 @@ extension AuthEndpoint: Endpoint {
6469
parameters["nickname"] = nickname
6570
}
6671
return parameters
72+
case .agreements(_, let agreements):
73+
var parameters: [String: Any] = [:]
74+
for agreement in agreements {
75+
parameters[agreement.key.termKey] = agreement.value
76+
}
77+
return parameters
6778
default:
6879
return [:]
6980
}
7081
}
7182
}
83+
84+
extension TermsType {
85+
var termKey: String {
86+
switch self {
87+
case .service: "agreedToTermsOfService"
88+
case .privacy: "agreedToPrivacyPolicy"
89+
case .age: "isOverFourteen"
90+
}
91+
}
92+
}

Projects/DataSource/Sources/Repositories/AuthRepository.swift

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ public final class AuthRepository: AuthRepositoryProtocol {
4242
try await requestServerLogin(socialType: .apple, nickname: savedNickname, token: authToken)
4343
}
4444

45+
public func submitAgreement(agreements: [TermsType : Bool]) async throws {
46+
let accessToken = try loadToken(tokenType: .accessToken)
47+
let endpoint = AuthEndpoint.agreements(accessToken: accessToken, agreements: agreements)
48+
let response = try await networkService.request(endpoint: endpoint, type: BaseResponseDTO<EmptyResponse>.self)
49+
BitnagilLogger.log(logType: .debug, message: "\(response)")
50+
}
51+
4552
public func logout() async throws {
4653
let accessToken = try loadToken(tokenType: .accessToken)
4754
let endpoint = AuthEndpoint.logout(accessToken: accessToken)
@@ -62,15 +69,16 @@ public final class AuthRepository: AuthRepositoryProtocol {
6269
public func reissueToken() async throws {
6370
let refreshToken = try loadToken(tokenType: .refreshToken)
6471
let endpoint = AuthEndpoint.reissue(refreshToken: refreshToken)
65-
let tokenResponse = try await networkService.request(endpoint: endpoint, type: TokenResponseDTO.self)
72+
let userResponse = try await networkService.request(endpoint: endpoint, type: LoginResponseDTO.self)
6673
guard
67-
let tokenEntity = tokenResponse.data?.toTokenEntity(),
68-
saveToken(tokenType: .accessToken, token: tokenEntity.accessToken),
69-
saveToken(tokenType: .refreshToken, token: tokenEntity.refreshToken)
74+
let userEntity = userResponse.data?.toUserEntity(),
75+
saveToken(tokenType: .accessToken, token: userEntity.accessToken),
76+
saveToken(tokenType: .refreshToken, token: userEntity.refreshToken)
7077
else { throw AuthError.tokenSaveFailed }
7178

72-
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(tokenEntity.accessToken)")
73-
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(tokenEntity.refreshToken)")
79+
BitnagilLogger.log(logType: .debug, message: "User Logined: \(userEntity.userState)")
80+
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(userEntity.accessToken)")
81+
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(userEntity.refreshToken)")
7482
}
7583

7684
private func fetchKakaoToken() async throws -> String {
@@ -105,15 +113,16 @@ public final class AuthRepository: AuthRepositoryProtocol {
105113
nickname: nickname,
106114
token: token)
107115

108-
let tokenResponse = try await networkService.request(endpoint: endpoint, type: TokenResponseDTO.self)
116+
let userResponse = try await networkService.request(endpoint: endpoint, type: LoginResponseDTO.self)
109117
guard
110-
let tokenEntity = tokenResponse.data?.toTokenEntity(),
111-
saveToken(tokenType: .accessToken, token: tokenEntity.accessToken),
112-
saveToken(tokenType: .refreshToken, token: tokenEntity.refreshToken)
118+
let userEntity = userResponse.data?.toUserEntity(),
119+
saveToken(tokenType: .accessToken, token: userEntity.accessToken),
120+
saveToken(tokenType: .refreshToken, token: userEntity.refreshToken)
113121
else { throw AuthError.tokenSaveFailed }
114122

115-
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(tokenEntity.accessToken)")
116-
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(tokenEntity.refreshToken)")
123+
BitnagilLogger.log(logType: .debug, message: "User Logined: \(userEntity.userState)")
124+
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(userEntity.accessToken)")
125+
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(userEntity.refreshToken)")
117126
}
118127

119128
private func saveToken(tokenType: TokenType, token: String) -> Bool {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// TermsType.swift
3+
// Domain
4+
//
5+
// Created by 최정인 on 7/8/25.
6+
//
7+
8+
public enum TermsType: CaseIterable {
9+
case service
10+
case privacy
11+
case age
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// UserState.swift
3+
// Domain
4+
//
5+
// Created by 최정인 on 7/8/25.
6+
//
7+
8+
public enum UserState: String {
9+
case guest = "GUEST"
10+
case user = "USER"
11+
}

Projects/Domain/Sources/Entities/TokenEntity.swift

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// UserEntity.swift
3+
// Domain
4+
//
5+
// Created by 최정인 on 6/30/25.
6+
//
7+
8+
public struct UserEntity {
9+
public let accessToken: String
10+
public let refreshToken: String
11+
public let userState: UserState
12+
13+
public init(
14+
accessToken: String,
15+
refreshToken: String,
16+
userState: UserState
17+
) {
18+
self.accessToken = accessToken
19+
self.refreshToken = refreshToken
20+
self.userState = userState
21+
}
22+
}

Projects/Domain/Sources/Protocols/Repository/AuthRepositoryProtocol.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ public protocol AuthRepositoryProtocol {
1414
/// - nickname: Apple 계정의 이름입니다. 첫 로그인 시에만 전달되며 이후에는 UserDefaults에 저장된 값을 사용합니다.
1515
/// - authToken: Apple로부터 발급받은 authorization token 값입니다.
1616
func appleLogin(nickname: String?, authToken: String) async throws
17-
17+
18+
/// 동의한 약관들에 대한 정보를 보냅니다.
19+
func submitAgreement(agreements: [TermsType: Bool]) async throws
20+
1821
/// 로그아웃을 진행합니다.
1922
func logout() async throws
2023

0 commit comments

Comments
 (0)