[Network] Moya RenewalToken
Moya RenewalToken
Moya를 활용해 네트워크로직을 구성했을때 401 에러에 대한 처리 로직을를 매번 API를 호출할때 마다 작성 해 줄 수는 없기 때문에 RxSwift를 활용해 오퍼레이터를 만들어주려고한다
1. AuthAPI 작성
enum AuthAPI {
case renewalToken(accessToken: String, refreshToken: String)
}
extension AuthAPI: TargetType {
var baseURL: URL {
return APIEnvironment.baseUrl
}
var path: String {
switch self {
case .renewalToken:
return "/auth/refresh"
}
}
var method: Moya.Method {
switch self {
case .renewalToken:
return .post
}
}
var sampleData: Data {
return Data()
}
var task: Task {
switch self {
case .renewalToken(let accessToken, let refreshToken):
return .requestParameters(parameters: ["accessToken" : accessToken, "refreshToken" : refreshToken], encoding: JSONEncoding.default)
}
}
var headers: [String : String]? {
let accessToken = Userdefaults.standard.string(forKey: "accessToken") ?? ""
return ["Authorization": "Bearer \(accessToken)"]
}
}
2. TokenReseponse 작성
struct TokenResponse: Decodable {
var accessToken: String
var refreshToken: String?
}
3. PrimitiveSequence extension 을 활용해 토큰 만료처리 오퍼레이터를 만들어줌
extension PrimitiveSequence where Trait == SingleTrait, Element == Moya.Response {
private func filter401StatusCode() -> Single<Moya.Response> {
return flatMap {
if $0.statusCode == 401 {
throw MoyaError.statusCode($0)
} else {
return .just($0)
}
}
}
func retryWithAuthIfNeeded() -> Single<Moya.Response> {
let access = UserDefaults.standard.string(forKey: "accessToken") ?? ""
let refresh = UserDefaults.standard.string(forKey: "refreshToken") ?? ""
return filter401StatusCode().catchError { tokenExpiredError in
APIService.shared.authAPI.rx.request(.renewalToken(accessToken: access, refreshToken: refresh))
.filterSuccessfulStatusCodes()
.catchError { refreshTokenError in
if (refreshTokenError as! MoyaError).response?.statusCode == 401 {
//RefreshToken 도 만료되었으므로 로그아웃 처리
}
return Single.error(refreshTokenError)
}
.flatMap { response -> Single<Moya.Response> in
if let token = try? response.map(TokenResponse.self) {
UserDefaults.standard.set(token.accessToken, forKey: "accessToken")
// 새로 발급받은 AccessToken 로컬에 저장
}
return Single.error(tokenExpiredError)
}
}.retry(2)
}
}
4. MoyaProvider를 활용해 API호출시 토큰만료 오퍼레이터를 붙여준다
let provider = MoyaProvider<SomeAPI>()
func someRequest() -> Single<SomeResponse> {
return provider.rx.request(.getSomeData)
.retryWithAuthIfNeeded() // 401에러가 뜨게되면 토큰만료 처리로직이 실행됨
.filterSuccessfulStatusCodes()
.map(SomeResponse.self)
}