Retrofit에 Authenticator라는 녀석이 access token을 갱신할 수 있도록 지원하는데, 님의 use case에 맞는지 모르겠네요.
아래 블로그를 참조해 보세요. 저는 참고로 써보지는 않았습니다.
https://www.simplifiedcoding.net/retrofit-authenticator-refresh-token/
참고로, 인터셉터에서 access token 갱신하려면 여러가지 어려움이 있습니다. 상황에 따라서는 현재 사용자의 로그인상태를 알아야 할 수도 있고, refresh token이 존재해야 보안상 안전합니다. 그리고 interceptor자체가 async가 아니기 때문에 access token 을 비동기로 가져온 다음, 어떻게 interceptor 안에서 리턴할 수 있을지... 잘 안떠오르네요. 401 에러가 올 때 외부로 에러를 던져서 캡쳐하는 쪽에서 access token 갱신을 요청할 수는 있겠지만요.
저같으면 그냥 API 클래스 핸들링하는 클래스를 하나 만들어서 응답을 체크해서 401이면 access token을 가져오는 클래스를 이용해 access token을 갱신하게 한 다음, 성공하면 요청을 계속할 수 있도록 할 것 같습니다만. RxJava나 Coroutine의 경우는 이 부분이 좀 더 수월해 보입니다만...
제 아이디어는 이렇습니다. Interceptor에서 401일 때 TokenExpiredException(사용자 정의 Exception)을 던집니다. 이렇게 하면 ApiService에서 apiCall 을 할 때 401일 경우 TokenExpiredException를 캡쳐할 수 있을 겁니다. 아래와 코드의 흐름처럼 (머릿속으로만 생각해본 코드임) TokenExpiredException이 리턴된 경우는 access token을 먼저 갱신해서 토큰을 singleton 오브젝트(tokenStore)를 이용해 저장한 다음 apiCall을 합니다.
Tokenstore에서 access token읽어와 Http에 헤더에 설정해주는Retrofit 인터셉터도 필요하겠죠.
class ApiService(
private val tokenApi: TokenApi,
private val tokenStore: TokenStore
) {
suspend fun <T> sendRequest(apiCall: suspend () -> T): T {
return try {
apiCall()
} catch (e: TokenExpiredException) {
renewAccessToken()
apiCall()
}
}
private suspend fun renewAccessToken() {
val accessToken = tokenApi.renewAccessToken(tokenStore.refreshToken)
tokenStore.accessToken = accessToken
}
}