okHttpInterceptor란?
OkHttp에서 제공하는 Interceptor는 요청 및 응답을 가로채고 조작할 수 있는 기능을 제공합니다.
주로 두 가지 종류의 인터셉터가 있습니다.
- Application Interceptor
- Network Interceptor.
Application Interceptor
애플리케이션 수준의 요청과 응답을 처리하는 인터셉터입니다. 네트워크 요청이 실제로 전송되기 전에 가로채고 수정할 수 있습니다.
- 애플리케이션 수준에서 요청/응답을 가로챔.
- 오직 한 번만 실행됨 (네트워크 요청이 실패해도 다시 실행되지 않음).
1. 헤더 수정 및 추가: 요청 헤더를 추가하거나 수정할 수 있습니다
class CommonHeaderInterceptor() :Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
// 요청 헤더 추가
val modifiedRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer my-secret-token")
.addHeader("User-Agent", "MyApp Android")
.build()
println("🚀 Application Interceptor - Request: ${modifiedRequest.url}")
return chain.proceed(modifiedRequest)
}
}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(CommonHeaderInterceptor()) // Application Interceptor 추가
.build()
2. accessToken 갱신: 요청을 보내기 전에 유효한 token을 확인하고, 응답에서 인증 오류를 처리하여 token을 갱신한 후 요청을 재시도할 수 있습니다.
class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
// 현재 Access Token 가져오기
val accessToken = tokenManager.getAccessToken()
// 요청에 Access Token 추가
request = request.newBuilder()
.addHeader("Authorization", "Bearer $accessToken")
.build()
val response = chain.proceed(request)
// 401 Unauthorized 발생하면 Access Token 갱신 후 재시도
if (response.code == 401) {
synchronized(this) {
val newAccessToken = tokenManager.refreshAccessToken()
if (newAccessToken != null) {
tokenManager.saveAccessToken(newAccessToken)
// 새로운 Access Token을 포함하여 요청 재시도
val newRequest = request.newBuilder()
.addHeader("Authorization", "Bearer $newAccessToken")
.build()
return chain.proceed(newRequest)
}
}
}
return response
}
}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor(tokenManager)) // Application Interceptor 추가
.build()
3. 로컬 캐시 적용: 네트워크가 이용 불가할 때, 네트워크 요청 없이 캐시된 데이터를 반환할 수 있습니다.
class OfflineCacheInterceptor(private val context: Context) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if (!isNetworkAvailable(context)) { // 네트워크 연결 확인
val maxStale = 60 * 60 * 24 * 7 // 7일 (604800초)
request = request.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
.build()
}
return chain.proceed(request)
}
// 네트워크 연결 여부 확인 함수
private fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetworkInfo
return activeNetwork != null && activeNetwork.isConnected
}
}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(OfflineCacheInterceptor(tokenManager)) // Application Interceptor 추가
.build()
- 네트워크 연결 확인
- isNetworkAvailable(context) 함수를 사용하여 네트워크 연결 여부를 확인
- 네트워크가 없다면(Offline 모드), 기존 캐시된 응답을 사용하도록 요청을 변경
- 오프라인 상태에서 캐시된 응답 사용
- only-if-cached: 서버로 요청을 보내지 않고 로컬 캐시에서만 데이터를 찾음.
- max-stale=604800 (7일): 캐시된 데이터가 7일까지 오래되어도 허용.
4. 요청 재시도: 요청 실패 시 지정된 횟수만큼 재시도할 수 있습니다.
class RetryInterceptor(private val maxRetry: Int = 3) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
var response: Response? = null
var retryCount = 0
while (response == null && retryCount < maxRetry) {
try {
response = chain.proceed(request)
} catch (e: IOException) {
retryCount++
if (retryCount == maxRetry) {
throw e
}
}
}
return response!!
}
}
Network Interceptor
네트워크 수준에서 요청과 응답을 처리하는 인터셉터입니다. 요청이 서버에 도달하고 응답이 클라이언트에 도달하기 전후에 가로챌 수 있습니다.
- 네트워크 요청을 보내기 직전에 실행됨.
- 실제 네트워크 요청이 수행될 때마다 실행됨 (리트라이 요청이 있으면 다시 실행됨).
- 원격 서버에서 반환된 응답을 가로채고 헤더를 추가할 수 있음.
1. 응답 캐시: 실제 네트워크 요청이 서버에 전송된 후 응답을 받기 때문에, 서버에서 제공하는 응답의 캐시 정책(Cache-Control)을 가로채고 수정하여 클라이언트가 응답을 얼마나 오랫동안 캐시해야 하는지를 조정할 수 있습니다. 이를 통해 애플리케이션의 성능을 최적화할 수 있습니다.
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
// 응답에 캐시 헤더 추가
return response.newBuilder()
.header("Cache-Control", "public, max-age=3600") // 1시간 동안 캐시 유지
.build()
}
}
val cacheSize = (10 * 1024 * 1024).toLong() // 10MB 캐시 저장
val cache = Cache(File(context.cacheDir, "http_cache"), cacheSize)
val okHttpClient = OkHttpClient.Builder()
.cache(cache) // 캐시 적용
.addNetworkInterceptor(CacheInterceptor()) // 응답 캐시 적용
.build()
2. 로깅: 네트워크 요청 및 응답에 대한 로깅을 수행하여 디버깅을 용이하게 합니다. 예를 들어, 성공적인 응답이나 오류에 대한 정보를 기록할 수 있습니다.
class LoggingInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
println("Sending request: ${request.url} on ${chain.connection()} with headers ${request.headers}")
val response = chain.proceed(request)
println("Received response for ${response.request.url} with headers ${response.headers}")
return response
}
}
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(LoggingInterceptor()) // Application Interceptor 추가
.build()
3. 네트워크 성능 분석: 요청을 보내고 응답을 받을 때 까지 걸린 시간 측정
class NetworkSpeedInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.nanoTime()
val response = chain.proceed(request)
val endTime = System.nanoTime()
val durationMs = (endTime - startTime) / 1e6 // 밀리초 변환
println("🌐 Network Request: ${request.url} took ${durationMs}ms")
return response
}
}
val okHttpClient = OkHttpClient.Builder()
.addNetworkInterceptor(NetworkSpeedInterceptor()) // Application Interceptor 추가
.build()
끝.
참고자료
https://velog.io/@heetaeheo/OkHttp-Interceptors
OkHttp - Interceptors
오늘은 Android에서 OkHttp - Interceptors에 대해 알아보는 글을 작성하려고 합니다. Interceptors는 네트워크 요청과 응답을 관리하고 조작하는 역할을 합니다. OkHttp Interceptors 란? Interceptors는 OkHttp의 핵심
velog.io
[Android] 프로젝트로 배우는 OkHttp Interceptor 활용
대부분 프로젝트를 진행하면 서버에서 사용자 인증을 JWT 사용하여 처리하는 경우가 많을 것 같습니다. 서버에서 전달해준 Token을 받게 되면 로컬 저장소에 저장한 후 API 요청 시에 HTTP Header에 추
velog.io
'Android > Retrofit' 카테고리의 다른 글
[Android] 직렬화/역직렬화 라이브러리 (Gson, Moshi, Kotlin-serialization) (0) | 2025.02.20 |
---|---|
[Android] Retrofit CallAdapter를 통해 효과적으로 예외 처리하기 (1) | 2025.02.20 |
[Android] Retrofit 내부적으로 어떻게 동작할까? (0) | 2025.02.20 |
[Android] OkHttp vs Retrofit (0) | 2023.09.13 |
[Android] Retrofit2 - REST API 통신 라이브러리 (0) | 2023.09.07 |