-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REFACTOR] 데이터 레이어 다른 계층 참조하지 않도록 코드 분리 #340
Merged
Merged
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
a2d3c2b
[FEAT] #339 Interceptor에서 액세스, 리프레시 토큰 관리 작업 구현
sxunea b609e6c
[FEAT] #339 액세스 토큰 재발급 성공시, preference에 해당 값 저장
sxunea 8b882b7
[FEAT] #339 리프레시 토큰 또한 만료 / 통신 실패 시 빈 값 preference에 저장
sxunea 123e7e7
[CHORE] #339 주석 추가 및 코드 정리
sxunea 60adff3
[DEL] #339 Authenticator 제거
sxunea 3e8d971
[MOD] #339 레트로핏 모듈에 Interceptor 반영
sxunea 1aebc18
[FEAT] #339 accessToken 빈 문자열이면 토스트메시지와 함께 재로그인 요청
sxunea 2ac7be5
[REFACTOR] #339 non null 단언 연산자 제거
sxunea 0c210d0
[REFACTOR] #339 named argument 적용
sxunea ead0369
[REFACTOR] #339 interceptor return if로 response처리
sxunea 60bbd81
[REFACTOR] #339 리뷰 간단사항 반영
sxunea a5eab3d
[ADD] #339 LoginStatus Enum Class 만들어 적용해 토큰 값 따른 로그인 상태 관리
sxunea b3267c8
[REFACTOR] #339 토큰preferenceManager AuthUtil로 빼서 관리
sxunea 625d913
[MOD] #339 applicationContext 주입
sxunea e9c245b
[MOD] #339 runBlocking제거
sxunea 0af3516
[REFACTOR] #339 applicationContext -> context로 변경하고, null 안전하게 체크
sxunea a390166
Merge remote-tracking branch 'origin/develop' into feature/refactor-data
sxunea 7315ecc
[MERGE] #339 충돌 해결
sxunea File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 0 additions & 24 deletions
24
app/src/main/java/com/runnect/runnect/data/service/AppInterceptor.kt
This file was deleted.
Oops, something went wrong.
130 changes: 130 additions & 0 deletions
130
app/src/main/java/com/runnect/runnect/data/service/AuthInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package com.runnect.runnect.data.service | ||
|
||
import android.content.Context | ||
import com.runnect.runnect.application.ApplicationClass | ||
import com.runnect.runnect.data.dto.response.ResponseGetRefreshToken | ||
import com.runnect.runnect.data.dto.response.base.BaseResponse | ||
import com.runnect.runnect.util.preference.AuthUtil.getAccessToken | ||
import com.runnect.runnect.util.preference.AuthUtil.getNewToken | ||
import com.runnect.runnect.util.preference.AuthUtil.saveToken | ||
import com.runnect.runnect.util.preference.StatusType.LoginStatus | ||
import dagger.hilt.android.qualifiers.ApplicationContext | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.runBlocking | ||
import kotlinx.serialization.decodeFromString | ||
import kotlinx.serialization.json.Json | ||
import okhttp3.Interceptor | ||
import okhttp3.Request | ||
import okhttp3.RequestBody.Companion.toRequestBody | ||
import okhttp3.Response | ||
import timber.log.Timber | ||
import javax.inject.Inject | ||
|
||
class AuthInterceptor @Inject constructor( | ||
@ApplicationContext private val context: Context, | ||
private val json: Json | ||
) : Interceptor { | ||
// access Header 에 보내고 이때 401(토큰 만료) 뜨면 액세스 재발급 요청 | ||
// 재발급 성공 : 저장 | ||
// 재발급 실패 : 재 로그인 토스트 메시지 띄우고 preference 빈 값 넣고 로그인 화면 이동 | ||
override fun intercept(chain: Interceptor.Chain): Response { | ||
val originalRequest = chain.request() | ||
|
||
val headerRequest = originalRequest.newAuthTokenBuilder() | ||
.build() | ||
|
||
val response = headerRequest.let { chain.proceed(it) } | ||
|
||
return if (response.code == CODE_TOKEN_EXPIRED) { | ||
try { | ||
Timber.e("Access Token Expired: getNewAccessToken") | ||
response.close() | ||
handleTokenExpired(chain, originalRequest, headerRequest) | ||
} catch (t: Throwable) { | ||
Timber.e("Exception: ${t.message}") | ||
context.saveToken( | ||
accessToken = LoginStatus.EXPIRED.value, | ||
refreshToken = LoginStatus.EXPIRED.value | ||
) | ||
response | ||
} | ||
} else { | ||
response | ||
} | ||
} | ||
|
||
private fun Request.newAuthTokenBuilder() = | ||
runBlocking(Dispatchers.IO) { | ||
val accessToken = context.getAccessToken() | ||
val refreshToken = context.getNewToken() | ||
newBuilder().apply { | ||
addHeader(ACCESS_TOKEN, accessToken) | ||
addHeader(REFRESH_TOKEN, refreshToken) | ||
} | ||
} | ||
|
||
|
||
private fun handleTokenExpired( | ||
chain: Interceptor.Chain, | ||
originalRequest: Request, | ||
headerRequest: Request | ||
): Response { | ||
val refreshTokenResponse = getNewToken(originalRequest, chain) | ||
return if (refreshTokenResponse.isSuccessful) { | ||
handleGetRefreshTokenSuccess(refreshTokenResponse, originalRequest, chain) | ||
} else { | ||
handleGetNewTokenFailure(refreshTokenResponse, headerRequest, chain) | ||
} | ||
} | ||
|
||
private fun getNewToken(originalRequest: Request, chain: Interceptor.Chain): Response { | ||
val baseUrl = ApplicationClass.getBaseUrl() | ||
val refreshToken = context.getNewToken() | ||
val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody()) | ||
.url("$baseUrl/api/auth/getNewToken") | ||
.addHeader(REFRESH_TOKEN, refreshToken) | ||
.build() | ||
|
||
return chain.proceed(refreshTokenRequest) | ||
} | ||
|
||
private fun handleGetRefreshTokenSuccess( | ||
refreshTokenResponse: Response, | ||
originalRequest: Request, | ||
chain: Interceptor.Chain | ||
): Response { | ||
refreshTokenResponse.use { response -> | ||
val responseToken = json.decodeFromString<BaseResponse<ResponseGetRefreshToken>>( | ||
response.body?.string().orEmpty() | ||
) | ||
responseToken.data?.data?.let { | ||
Timber.e("New Refresh Token Success: ${it.refreshToken}") | ||
context.saveToken(it.accessToken, it.refreshToken) | ||
} | ||
} | ||
|
||
val newRequest = originalRequest.newAuthTokenBuilder().build() | ||
return chain.proceed(newRequest) | ||
} | ||
|
||
private fun handleGetNewTokenFailure( | ||
refreshTokenResponse: Response, | ||
headerRequest: Request, | ||
chain: Interceptor.Chain | ||
): Response { | ||
Timber.e("New Refresh Token Failure: ${refreshTokenResponse.code}") | ||
context.saveToken( | ||
accessToken = LoginStatus.EXPIRED.value, | ||
refreshToken = LoginStatus.EXPIRED.value | ||
) | ||
return chain.proceed(headerRequest) | ||
} | ||
|
||
|
||
companion object { | ||
private const val ACCESS_TOKEN = "accessToken" | ||
private const val CODE_TOKEN_EXPIRED = 401 | ||
private const val REFRESH_TOKEN = "refreshToken" | ||
} | ||
|
||
} |
126 changes: 0 additions & 126 deletions
126
app/src/main/java/com/runnect/runnect/data/service/TokenAuthenticator.kt
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분 runBlocking 쓰신 이유가 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저 때는 해당 작업이 완료될때까지 스레드를 막으려고 runBlocking을 사용하긴 했습니다 ! 다만 runBlocking이 아예 차단이라는 점에서 스레드를 점유하니까 좋진 않을거라고 작성할때도 생각하긴했습니다 .. .ㅎ ㅎ 처음에는 suspend로 작성했었는데 그러면 newAuthTokenBuilder를 사용하는 okhttp3 intercept까지 suspend func이 되어야 하는데, 이를 지원하지 않더라구요 😭 그래서 차선책으로 사용했습니다!!
지금 생각해보니 저 동작이 꼭 비동기여야할 필요는 없다고 생각이 드는데 어떻게 생각하시나요 ? 아니면 혹시 runBlocking을 쓰지 않고 다르게 처리할 방법이 있는지 궁금합니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 runBlocking을 작성하지 않았을 때 제대로 동작하지 않았나요??
interceptor가 비동기적으로 동작하더라도 같은 메소드 내에서는 실행 순서가 보장될테니 별도 처리가 필요 없을 것 같다는 생각이 들었습니다~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음에 구현할땐 저도 필요없다고 생각했어서 작성하지 않았는데 동작이 안됐어서 runBlocking을 사용했는데, 지금 제거하고 보니 또 잘 가져오네요 🥹 다른 부분이 잘못됐었는데 불필요하게 같이 고쳐준 것 같아요 저도 runBlocking이 여전히 불필요하다고 생각합니다 ㅎㅎㅎ 없애서 수정할게요 !