석지한
한지석일지
석지한
전체 방문자
오늘
어제
  • 분류 전체보기 (33)
    • iOS & Swift (15)
    • 프로젝트 (13)
    • 알고리즘 (0)
    • 잡담 (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • BFS
  • 시간표
  • Swift
  • 인터뷰
  • TableView
  • Python
  • 개발일지
  • dp
  • dfs
  • pickerView
  • Realm
  • Greedy
  • 그리디
  • 프로젝트
  • 공식문서
  • ios
  • AppDelegate
  • 동적프로그래밍
  • 이분탐색
  • 알고리즘

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
석지한

한지석일지

iOS & Swift

[iOS] swift Alamofire 토큰 재발급 구현 방법(adapt, retry, RequestIntercepter)

2022. 4. 25. 18:39
반응형

 본인은 개발을 시작한지 얼마 되지 않은 대학생이지만, 최근에 토큰 재발급하는 로직 관련해서 어려움을 크게 겪어 누군가 도움이 되었으면 하는 마음에 글을 포스팅해본다 😁

 

 Alamofire에서는 자체적으로 RequestIntercepter라는 프로토콜을 제공하고, 그 안에는 adapt, retry 메소드가 정의 되어있다. 따라서 우리는 이 두개의 메소드를 잘 활용한 후, api 호출할 때 인터셉트로 넣어주면 토큰을 쉽게 재발급할 수 있다.

 

 작동 순서

request() -> adapt() -> 토큰 만료 -> retry() 에서 RefreshToken을 넣은 후 다시 reqeust() -> adapt() -> 서버에서 확인 후 토큰 갱신 -> retry() // 로컬에 토큰 업데이트 -> request() -> adapt() -> 성공순서이다.

 

나는 어떻게 구현했는지 한번 살펴보겠다.(본인이 개발을 시작한 지 얼마 안된 대학생이므로, 참고용으로만 보는것도 좋겠다!)

 

adapt

 func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
            guard urlRequest.url?.absoluteString.hasPrefix("api 요청 사이트의 공통 주소") == true,
                  let accessToken = keychain.get("AccessToken") else {
                      completion(.success(urlRequest))
                      return
                  }

            var urlRequest = urlRequest
            urlRequest.setValue(accessToken, forHTTPHeaderField: "Authorization")
            completion(.success(urlRequest))
           
        }

본인이 요청하는 api 명세서에는 bearer을 붙힐 필요가 없어 빠져있으나, 본인의 프로젝트 명세서를 잘 확인한 후 앞에 bearer이 붙는지 안붙는지를 확인해 볼 필요가 있다.

(bearer 개념 참고 : https://velog.io/@cada/%ED%86%A0%EA%B7%BC-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D%EC%97%90%EC%84%9C-bearer%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C)

 

 completion은 어댑터가 호출이 완료되었음을 알리는 비동기 완료 핸들러이다. 본인은 accessToken이 필요한 호출 마다 intercepter를 넣어줬기 때문에, guard 문을 통과하지 못한다면, .success가 아닌 .failure를 넘겨준다. 정상적으로 진행이 될 경우 accessToken이 포함된 urlRequest 객체를 넘겨주어 그 다음 호출이 진행되도록 한다.

 

retry

   func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {

        guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else {
            completion(.doNotRetryWithError(error))
            return
        }
        
        let url = "호출할 api 주소"
        let headers: HTTPHeaders = [
            "Authorization" : "저장 되어있는 RefreshToken"
        ]
        
        AF.request(url, method: .post, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
            let data = response.data
            let json = JSON(data!)
            
            switch response.result{
            case .success(_):
                self.keychain.clear()
                self.keychain.set("리프레시_토큰", forKey: "RefreshToken")
                self.keychain.set("액세스_토큰", forKey: "AccessToken")
                completion(.retry)
            case .failure(let error):
                completion(.doNotRetryWithError(error))
            }
            
            
        }

 처음 guard문에서 토큰 만료시 내려오는 에러 401이 아닌 다른 에러가 떨어질 경우 doNotRetryWithError(error)로 retry 하지 않고 넘어간다. retry 함수는 액세스 토큰이 만료 되었을 때 실행이 되는데, 나는 retry 함수가 실행될 때 리프레시 토큰으로 토큰 재발급 api를 호출한 후, 리프레시 토큰이 유효할 경우 새로 엑세스 토큰과 리프레시 토큰을 setting 해주고, 유효하지 않을 경우는 retry 하게 만들었다.

 

 이 후에는 request에 intercepter 객체를 넣어주면 된다. 본인은 뒤에 request 괄호 닫힌 부분에 .validate()를 사용하지 않았어서 한참 헤멨으나, 이 글을 본 이들은 그런 실수를 하지 않았으면 좋겠다. 

(validate()는 유효성 검사를 해줌 : https://velog.io/@chagmn/Swift-Alamofire-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B02)

 

 본인의 글은 최대한 쉽게 써보려 노력했으며, 초보자의 관점에서 작성이 되었으므로 더 좋은 블로그 포스팅 주소도 남겨놓겠다 !

[참고자료]

https://bugle.tistory.com/53

https://ios-development.tistory.com/730?category=899471 

https://velog.io/@jisng/Alamofire-Interceptor

Alamofire 공식 문서 : https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor

반응형

'iOS & Swift' 카테고리의 다른 글

[iOS] iOS 인터뷰 질문 - 1  (0) 2022.05.11
[Swift] 타입 캐스팅이란 ? (is, as)  (0) 2022.05.11
[Swift] Swift 공식문서 정리 - 5(옵셔널 체이닝)  (0) 2022.04.06
[Swift] Swift 공식문서 정리 - 4(열거형)  (0) 2022.04.06
[Swift] Swift 공식문서 정리 - 3(클래스 & 구조체)  (0) 2022.04.04
    'iOS & Swift' 카테고리의 다른 글
    • [iOS] iOS 인터뷰 질문 - 1
    • [Swift] 타입 캐스팅이란 ? (is, as)
    • [Swift] Swift 공식문서 정리 - 5(옵셔널 체이닝)
    • [Swift] Swift 공식문서 정리 - 4(열거형)
    석지한
    석지한
    iOS

    티스토리툴바