Skip to content

Commit a7c3a51

Browse files
committed
Feat: 토큰 재발급 함수 reissueToken 구현
- AuthEndpoint에 reissue 케이스 추가 - AuthRepositoryProtocol 및 AuthRepository에 reissueToken 정의 및 구현 - LoginViewModel, LoginViewController에 reissue 테스트 관련 버튼 및 함수 추가
1 parent e5b5cd5 commit a7c3a51

7 files changed

Lines changed: 76 additions & 4 deletions

File tree

Projects/DataSource/Sources/Endpoints/AuthEndpoint.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ enum AuthEndpoint {
1111
case login(socialLoginType: SocialLoginType, nickname: String?, token: String)
1212
case logout(accessToken: String)
1313
case withdraw(accessToken: String)
14+
case reissue(refreshToken: String)
1415
}
1516

1617
extension AuthEndpoint: Endpoint {
@@ -23,6 +24,7 @@ extension AuthEndpoint: Endpoint {
2324
case .login: baseURL + "/login"
2425
case .logout: baseURL + "/logout"
2526
case .withdraw: baseURL + "/withdrawal"
27+
case .reissue: baseURL + "/token/reissue"
2628
}
2729
}
2830

@@ -43,6 +45,8 @@ extension AuthEndpoint: Endpoint {
4345
headers["Authorization"] = "Bearer \(accessToken)"
4446
case .withdraw(let accessToken):
4547
headers["Authorization"] = "Bearer \(accessToken)"
48+
case .reissue(let refreshToken):
49+
headers["Refresh-Token"] = refreshToken
4650
}
4751

4852
return headers

Projects/DataSource/Sources/Repositories/AuthRepository.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ public final class AuthRepository: AuthRepositoryProtocol {
5959
BitnagilLogger.log(logType: .debug, message: "\(response.message)")
6060
}
6161

62+
public func reissueToken() async throws {
63+
let refreshToken = try loadToken(tokenType: .refreshToken)
64+
let endpoint = AuthEndpoint.reissue(refreshToken: refreshToken)
65+
let tokenResponse = try await networkService.request(endpoint: endpoint, type: TokenResponseDTO.self)
66+
guard
67+
let tokenEntity = tokenResponse.data?.toTokenEntity(),
68+
saveToken(tokenType: .accessToken, token: tokenEntity.accessToken),
69+
saveToken(tokenType: .refreshToken, token: tokenEntity.refreshToken)
70+
else { throw AuthError.tokenSaveFailed }
71+
72+
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(tokenEntity.accessToken)")
73+
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(tokenEntity.refreshToken)")
74+
}
75+
6276
private func fetchKakaoToken() async throws -> String {
6377
try await withCheckedThrowingContinuation { continuation in
6478
let resultHandler: (OAuthToken?, Error?) -> Void = { oauthToken, error in

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@ public protocol AuthRepositoryProtocol {
2020

2121
/// 계정 탈퇴를 진행합니다.
2222
func withdraw() async throws
23+
24+
/// 토큰 재발급을 진행합니다.
25+
func reissueToken() async throws
2326
}

Projects/Domain/Sources/Protocols/UseCase/LogoutUseCaseProtocol.swift

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

8-
import Foundation
9-
108
public protocol LogoutUseCaseProtocol {
119
/// 로그아웃을 진행합니다.
1210
func logout() async throws
11+
12+
// TODO: 추후 reissue 어디로 가야 하나 ..
13+
/// 토큰 재발급을 진행합니다.
14+
func reissueToken() async throws
1315
}

Projects/Domain/Sources/UseCases/Auth/LogoutUseCase.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ public final class LogoutUseCase: LogoutUseCaseProtocol {
1717
public func logout() async throws {
1818
try await authRepository.logout()
1919
}
20+
21+
public func reissueToken() async throws {
22+
try await authRepository.reissueToken()
23+
}
2024
}

Projects/Presentation/Sources/Login/LoginViewController.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public final class LoginViewController: BaseViewController<LoginViewModel> {
2525
private let appleLoginButton = UIButton()
2626
private let logoutButton = UIButton()
2727
private let withdrawButton = UIButton()
28+
private let reissueButton = UIButton()
2829
private var cancellables: Set<AnyCancellable>
2930

3031
public override init(viewModel: LoginViewModel) {
@@ -72,6 +73,14 @@ public final class LoginViewController: BaseViewController<LoginViewModel> {
7273
self?.viewModel.action(input: .withdraw)
7374
}, for: .touchUpInside)
7475
}
76+
77+
reissueButton.do {
78+
$0.setTitle("토큰 재발급", for: .normal)
79+
$0.setTitleColor(.blue, for: .normal)
80+
$0.addAction(UIAction { [weak self] _ in
81+
self?.viewModel.action(input: .reissue)
82+
}, for: .touchUpInside)
83+
}
7584
}
7685

7786
override func configureLayout() {
@@ -82,6 +91,7 @@ public final class LoginViewController: BaseViewController<LoginViewModel> {
8291
view.addSubview(appleLoginButton)
8392
view.addSubview(logoutButton)
8493
view.addSubview(withdrawButton)
94+
view.addSubview(reissueButton)
8595

8696
kakaoLoginButton.snp.makeConstraints { make in
8797
make.leading.equalTo(safeArea).offset(Layout.horizontalMargin)
@@ -110,6 +120,13 @@ public final class LoginViewController: BaseViewController<LoginViewModel> {
110120
make.bottom.equalTo(logoutButton.snp.top).offset(-Layout.loginButtonSpacing)
111121
make.height.equalTo(Layout.loginButtonHeight)
112122
}
123+
124+
reissueButton.snp.makeConstraints { make in
125+
make.leading.equalTo(safeArea).offset(Layout.horizontalMargin)
126+
make.trailing.equalTo(safeArea).inset(Layout.horizontalMargin)
127+
make.bottom.equalTo(withdrawButton.snp.top).offset(-Layout.loginButtonSpacing)
128+
make.height.equalTo(Layout.loginButtonHeight)
129+
}
113130
}
114131

115132
override func bind() {
@@ -148,6 +165,17 @@ public final class LoginViewController: BaseViewController<LoginViewModel> {
148165
}
149166
}
150167
.store(in: &cancellables)
168+
169+
viewModel.output.reissueResultPublisher
170+
.receive(on: DispatchQueue.main)
171+
.sink { reissueResult in
172+
if reissueResult {
173+
BitnagilLogger.log(logType: .debug, message: "토큰 재발급 성공")
174+
} else {
175+
BitnagilLogger.log(logType: .debug, message: "토큰 재발급 실패")
176+
}
177+
}
178+
.store(in: &cancellables)
151179
}
152180

153181
private func appleLogin() {
@@ -171,7 +199,9 @@ extension LoginViewController: ASAuthorizationControllerDelegate {
171199
guard
172200
let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
173201
let authCodeData = credential.authorizationCode,
174-
let authToken = String(data: authCodeData, encoding: .utf8)
202+
let identityTokenData = credential.identityToken,
203+
let authToken = String(data: authCodeData, encoding: .utf8),
204+
let identityToken = String(data: identityTokenData, encoding: .utf8)
175205
else {
176206
BitnagilLogger.log(logType: .error, message: "Apple AuthorizationCode 파싱 실패")
177207
return

Projects/Presentation/Sources/Login/ViewModel/LoginViewModel.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@ public final class LoginViewModel: ViewModel {
1515
case appleLogin(nickname: String?, authToken: String)
1616
case logout
1717
case withdraw
18+
case reissue
1819
}
1920

2021
public struct Output {
2122
let loginResultPublisher: AnyPublisher<Bool, Never>
2223
let logoutResultPublisher: AnyPublisher<Bool, Never>
2324
let withdrawResultPublisher: AnyPublisher<Bool, Never>
25+
let reissueResultPublisher: AnyPublisher<Bool, Never>
2426
}
2527

2628
private(set) var output: Output
2729
private let loginResultSubject = PassthroughSubject<Bool, Never>()
2830
// TODO: 추후 설정 페이지로 옮겨야 합니다.
2931
private let logoutResultSubject = PassthroughSubject<Bool, Never>()
3032
private let withdrawResultSubject = PassthroughSubject<Bool, Never>()
33+
private let reissueResultSubject = PassthroughSubject<Bool, Never>()
3134

3235
private let loginUseCase: LoginUseCaseProtocol
3336
// TODO: 추후 설정 페이지로 옮겨야 합니다.
@@ -45,7 +48,8 @@ public final class LoginViewModel: ViewModel {
4548
self.output = Output(
4649
loginResultPublisher: loginResultSubject.eraseToAnyPublisher(),
4750
logoutResultPublisher: logoutResultSubject.eraseToAnyPublisher(),
48-
withdrawResultPublisher: withdrawResultSubject.eraseToAnyPublisher()
51+
withdrawResultPublisher: withdrawResultSubject.eraseToAnyPublisher(),
52+
reissueResultPublisher: reissueResultSubject.eraseToAnyPublisher()
4953
)
5054
}
5155

@@ -94,6 +98,17 @@ public final class LoginViewModel: ViewModel {
9498
withdrawResultSubject.send(false)
9599
}
96100
}
101+
102+
case .reissue:
103+
Task {
104+
do {
105+
try await logoutUseCase.reissueToken()
106+
reissueResultSubject.send(true)
107+
} catch {
108+
BitnagilLogger.log(logType: .error, message: "\(error.localizedDescription)")
109+
reissueResultSubject.send(false)
110+
}
111+
}
97112
}
98113
}
99114
}

0 commit comments

Comments
 (0)