Skip to content

[Feature/#179] authenticator 구현#180

Closed
chanubc wants to merge 9 commits into
developfrom
feature/#179-authenticator
Closed

[Feature/#179] authenticator 구현#180
chanubc wants to merge 9 commits into
developfrom
feature/#179-authenticator

Conversation

@chanubc

@chanubc chanubc commented Jul 17, 2025

Copy link
Copy Markdown
Member

✅ 𝗖𝗵𝗲𝗰𝗸-𝗟𝗶𝘀𝘁

  • merge할 브랜치의 위치를 확인해 주세요(main❌/develop⭕)
  • 리뷰가 필요한 경우 리뷰어를 지정해 주세요
  • P1 단계의 리뷰는 필수로 반영합니다.
  • Approve된 PR은 assigner가 머지하고, 수정 요청이 온 경우 수정 후 다시 push를 합니다.

📌 𝗜𝘀𝘀𝘂𝗲𝘀

📎𝗪𝗼𝗿𝗸 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻

  • authenticator 구현
  • intercepotor 불필요한 로직 제거

📷 𝗦𝗰𝗿𝗲𝗲𝗻𝘀𝗵𝗼𝘁

💬 𝗧𝗼 𝗥𝗲𝘃𝗶𝗲𝘄𝗲𝗿𝘀

아직 access token만료 테스트는 못해봐서 해보고 말씀드릴게요!
token 값 다른 것 넣는거 말고 테스트 하는법 어떤거 있었죠?

Summary by CodeRabbit

  • 신규 기능

    • HTTP 401 인증 오류 시 토큰을 자동으로 갱신하고, 실패 시 재로그인을 안내하며 앱을 재시작하는 기능이 추가되었습니다.
    • 앱 재시작 및 토스트 메시지 표시를 담당하는 공통 인터페이스와 구현체가 도입되었습니다.
  • 리팩터링

    • 토큰 인터셉터에서 토큰 갱신 및 재로그인 처리 로직이 제거되어, 인증 헤더 추가만 수행하도록 단순화되었습니다.
    • 인증 관련 의존성 주입 모듈이 개선되고, 불필요한 어노테이션이 제거되었습니다.
    • 앱 재시작 기능이 각 기능 모듈에서 공통 컴포넌트로 통합되어 코드 중복이 줄어들었습니다.
  • 기타

    • 내부 유틸리티 함수의 패키지 경로가 정리되었습니다.

@chanubc chanubc requested a review from sohyun127 July 17, 2025 12:51
@chanubc chanubc self-assigned this Jul 17, 2025
@coderabbitai

coderabbitai Bot commented Jul 17, 2025

Copy link
Copy Markdown
## Walkthrough

기존의 토큰 만료 및 갱신 처리가 TokenInterceptor에서 TokenAuthenticator 클래스로 이전되었습니다. TokenAuthenticator는 OkHttp Authenticator 인터페이스를 구현하여 401 응답 시 토큰을 갱신하고, 성공 시 재요청, 실패 시 토큰 삭제 및 앱 재시작을 처리합니다. 관련 의존성 및 모듈 설정도 이에 맞게 변경되었습니다.

## Changes

| 파일(들) | 변경 요약 |
|---|---|
| core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt<br>core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt<br>core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt<br>core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt | `runHandledCatching` import 경로를 `com.teamwable.network.util`로 변경 |
| core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt | TokenAuthenticator 클래스 신규 추가 (OkHttp Authenticator 구현, 토큰 갱신/재요청/실패 처리) |
| core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt | 토큰 만료/갱신/재시도 로직 삭제, 단순 Authorization 헤더 추가만 수행하도록 단순화 |
| core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt | OkHttpClient에 TokenAuthenticator 추가, @AccessToken 어노테이션 제거 및 관련 시그니처 수정 |
| core/network/src/main/java/com/teamwable/network/di/Qualifier.kt | AccessToken Qualifier 어노테이션 삭제 |
| core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt | 패키지명을 `com.teamwable.network.util`로 변경, 불필요 import 제거 |
| core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt<br>core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt<br>core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt | 앱 재시작 및 토스트 표시를 위한 AppReStarter 인터페이스 및 구현체, 모듈 추가 |
| feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt<br>feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt | 앱 재시작 로직을 Intent 직접 호출에서 AppReStarter 주입 및 호출로 변경 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant App
    participant OkHttpClient
    participant TokenInterceptor
    participant TokenAuthenticator
    participant AuthService
    participant DataStore

    App->>OkHttpClient: API 요청
    OkHttpClient->>TokenInterceptor: 요청 인터셉트
    TokenInterceptor->>DataStore: AccessToken 조회
    TokenInterceptor->>OkHttpClient: Authorization 헤더 추가 후 요청 진행
    OkHttpClient->>서버: API 요청
    서버-->>OkHttpClient: 401 Unauthorized 응답
    OkHttpClient->>TokenAuthenticator: authenticate 호출
    TokenAuthenticator->>DataStore: RefreshToken 조회
    TokenAuthenticator->>AuthService: 토큰 갱신 시도
    alt 토큰 갱신 성공
        AuthService-->>TokenAuthenticator: 새로운 토큰 반환
        TokenAuthenticator->>DataStore: 토큰 저장
        TokenAuthenticator->>OkHttpClient: 새로운 토큰으로 재요청 반환
    else 토큰 갱신 실패
        TokenAuthenticator->>DataStore: 토큰 삭제
        TokenAuthenticator->>App: Toast 표시 및 앱 재시작
        TokenAuthenticator->>OkHttpClient: null 반환 (요청 실패)
    end

Assessment against linked issues

Objective Addressed Explanation
Authenticator 도입 (#179)
refresh로직 수정 (#179) TokenAuthenticator에 refresh 로직이 구현되어 있으나, "수정"의 구체적 요구사항 충족 여부는 명확하지 않음. 기존 로직을 TokenAuthenticator로 옮긴 것으로 보이나, 추가적인 요구사항이 있다면 확인 필요.

Poem

🐇
새로운 Authenticator, 토큰을 지켜
401이 와도 걱정 마, 다시 살아나
앱은 부드럽게 다시 시작
토큰의 춤, 네트워크의 박자
깡총깡총, 토끼도 기뻐
코드 속 봄바람, 오늘도 분다!
🌱✨


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Team-Wable/WABLE-ANDROID&utm_content=180):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@chanubc chanubc added ⭐ [FEAT] 새로운 기능 구현 🐻 찬우 차은우 labels Jul 17, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt (1)

27-44: 에러 핸들링 패턴의 일관성을 개선하는 것을 고려해보세요.

일부 메서드는 runHandledCatching을 사용하고 있지만, 다른 메서드들은 runCatching과 수동 에러 핸들링을 사용하고 있습니다. 학습된 내용에 따르면 WABLE 프로젝트에서는 Repository 레이어에서 runHandledCatching을 사용하여 중앙집중식 에러 핸들링을 권장합니다.

모든 메서드에서 일관되게 runHandledCatching을 사용하는 것을 고려해보세요:

-    override suspend fun getProfileInfo(userId: Long): Result<Profile> = runCatching {
-        apiService.getProfileInfo(userId).data.toProfile()
-    }.onFailure {
-        return it.handleThrowable()
-    }
+    override suspend fun getProfileInfo(userId: Long): Result<Profile> = runHandledCatching {
+        apiService.getProfileInfo(userId).data.toProfile()
+    }
core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt (1)

14-21: Interceptor에서 runBlocking 사용 시 성능 영향

모든 요청마다 runBlocking으로 토큰을 가져오는 것은 성능에 영향을 줄 수 있습니다. 토큰 캐싱을 고려해보세요.

다음과 같은 개선을 제안합니다:

 class TokenInterceptor @Inject constructor(
     private val dataStore: WablePreferencesDataSource,
 ) : Interceptor {
+    private var cachedToken: String? = null
+    
     override fun intercept(chain: Interceptor.Chain): Response {
-        val accessToken = runBlocking { dataStore.accessToken.first() }
+        val accessToken = cachedToken ?: runBlocking { 
+            dataStore.accessToken.first().also { cachedToken = it }
+        }
         val authRequest: Request = chain.request().newBuilder()
             .addHeader("Authorization", accessToken)
             .build()
 
         return chain.proceed(authRequest)
     }
+    
+    // TokenAuthenticator에서 토큰 갱신 시 캐시 무효화 필요
+    fun invalidateCache() {
+        cachedToken = null
+    }
 }

또는 토큰을 Flow로 관찰하여 변경 시에만 업데이트하는 방식도 고려해볼 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24102ab and 968e3dd.

📒 Files selected for processing (9)
  • core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt (1 hunks)
  • core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt (1 hunks)
  • core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt (1 hunks)
  • core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt (1 hunks)
  • core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (1 hunks)
  • core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt (1 hunks)
  • core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt (2 hunks)
  • core/network/src/main/java/com/teamwable/network/di/Qualifier.kt (0 hunks)
  • core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt (1 hunks)
💤 Files with no reviewable changes (1)
  • core/network/src/main/java/com/teamwable/network/di/Qualifier.kt
🧰 Additional context used
🧠 Learnings (4)
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt (1)
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#168
File: core/data/src/main/java/com/teamwable/data/gallery/GallerySaver.kt:22-34
Timestamp: 2025-06-08T14:50:22.089Z
Learning: WABLE Android 프로젝트에서는 에러 핸들링을 Repository 레이어에서 `runHandledCatching`을 사용하여 중앙집중식으로 처리합니다. 따라서 하위 레이어의 유틸리티 클래스(예: GallerySaver)에서는 별도의 try-catch 블록을 추가할 필요가 없습니다.
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt (1)
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#168
File: core/data/src/main/java/com/teamwable/data/gallery/GallerySaver.kt:22-34
Timestamp: 2025-06-08T14:50:22.089Z
Learning: WABLE Android 프로젝트에서는 에러 핸들링을 Repository 레이어에서 `runHandledCatching`을 사용하여 중앙집중식으로 처리합니다. 따라서 하위 레이어의 유틸리티 클래스(예: GallerySaver)에서는 별도의 try-catch 블록을 추가할 필요가 없습니다.
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt (1)
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#168
File: core/data/src/main/java/com/teamwable/data/gallery/GallerySaver.kt:22-34
Timestamp: 2025-06-08T14:50:22.089Z
Learning: WABLE Android 프로젝트에서는 에러 핸들링을 Repository 레이어에서 `runHandledCatching`을 사용하여 중앙집중식으로 처리합니다. 따라서 하위 레이어의 유틸리티 클래스(예: GallerySaver)에서는 별도의 try-catch 블록을 추가할 필요가 없습니다.
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt (1)
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#168
File: core/data/src/main/java/com/teamwable/data/gallery/GallerySaver.kt:22-34
Timestamp: 2025-06-08T14:50:22.089Z
Learning: WABLE Android 프로젝트에서는 에러 핸들링을 Repository 레이어에서 `runHandledCatching`을 사용하여 중앙집중식으로 처리합니다. 따라서 하위 레이어의 유틸리티 클래스(예: GallerySaver)에서는 별도의 try-catch 블록을 추가할 필요가 없습니다.
🧬 Code Graph Analysis (1)
core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (1)
core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt (1)
  • runSuspendCatching (6-16)
🔇 Additional comments (6)
core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt (1)

1-1: 패키지 이동이 적절하게 수행되었습니다.

네트워크 관련 유틸리티를 network.util 패키지로 이동시킨 것은 모듈 구조를 개선하고 관련 기능을 한 곳에 집중시키는 좋은 접근입니다.

core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt (1)

16-16: import 문 업데이트가 올바르게 수행되었습니다.

유틸리티 함수가 network.util 패키지로 이동한 것에 맞춰 import 문이 적절히 업데이트되었습니다.

core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt (1)

7-7: import 문 업데이트가 올바르게 수행되었습니다.

유틸리티 함수가 network.util 패키지로 이동한 것에 맞춰 import 문이 적절히 업데이트되었습니다.

core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt (1)

8-8: import 문 업데이트가 올바르게 수행되었습니다.

유틸리티 함수가 network.util 패키지로 이동한 것에 맞춰 import 문이 적절히 업데이트되었습니다.

core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt (1)

8-8: import 문 업데이트가 올바르게 수행되었습니다.

유틸리티 함수가 network.util 패키지로 이동한 것에 맞춰 import 문이 적절히 업데이트되었습니다. Flow 기반 메서드와 suspend 함수에서 각각 적절한 에러 핸들링 패턴을 사용하고 있습니다.

core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt (1)

38-48: 인증 처리 구조가 적절하게 분리되었습니다.

TokenInterceptor와 TokenAuthenticator의 역할을 명확하게 분리한 것이 좋습니다. OkHttp의 권장 패턴을 잘 따르고 있습니다.

Comment on lines +77 to +84
private fun restartApp() {
val restartIntent = context.packageManager
.getLaunchIntentForPackage(context.packageName)
?.component
?.let(Intent::makeRestartActivityTask)

context.startActivity(restartIntent)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

앱 재시작 로직 검증 필요

makeRestartActivityTask를 사용한 앱 재시작 로직이 모든 상황에서 올바르게 동작하는지 확인이 필요합니다.

다음 사항들을 확인해주세요:

  • 백그라운드에서 실행될 때의 동작
  • 다양한 Android 버전에서의 호환성
  • 멀티 태스크 환경에서의 동작

🏁 Script executed:

#!/bin/bash
# Description: 앱 재시작 관련 코드 패턴 확인

# makeRestartActivityTask 사용 사례 검색
rg -A 5 "makeRestartActivityTask" --type kotlin

# Intent.FLAG_ACTIVITY_NEW_TASK 사용 확인
rg -A 5 "FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TASK" --type kotlin

Length of output: 4071


앱 재시작 로직 검증 결과 및 개선 제안

  • Intent.makeRestartActivityTask는 내부적으로 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK를 설정하므로 별도 플래그 추가는 불필요합니다.
  • TokenAuthenticator.kt 외에도 ProfileHamburgerBottomSheet.kt, ProfileDeleteConfirmFragment.kt 에서 동일 패턴으로 구현되어 있습니다.
  • 다만, getLaunchIntentForPackage()null을 반환할 경우 startActivity(null)로 인해 예외가 발생할 수 있으므로, 호출 전/후에 반드시 null 체크 및 적절한 예외 처리(로그 출력 등)를 추가해주세요.

예시 수정안:

-    val restartIntent = context.packageManager
-        .getLaunchIntentForPackage(context.packageName)
-        ?.component
-        ?.let(Intent::makeRestartActivityTask)
-    context.startActivity(restartIntent)
+    val restartIntent = context.packageManager
+        .getLaunchIntentForPackage(context.packageName)
+        ?.component
+        ?.let(Intent::makeRestartActivityTask)
+    restartIntent?.let {
+        context.startActivity(it)
+    } ?: run {
+        Timber.e("앱 재시작용 인텐트를 생성하지 못했습니다.")
+    }
🤖 Prompt for AI Agents
In core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt around
lines 77 to 84, the restartApp function calls startActivity with a potentially
null intent from getLaunchIntentForPackage, which can cause an exception. To fix
this, add a null check after obtaining the restartIntent and before calling
startActivity; if restartIntent is null, log an appropriate error or handle the
case gracefully without calling startActivity with null.

@chanubc chanubc requested a review from Copilot July 17, 2025 12:56

This comment was marked as resolved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (3)

61-64: 네트워크 레이어에서 UI 작업 수행 문제

네트워크 레이어에서 Toast 표시와 앱 재시작을 직접 처리하는 것은 관심사 분리 원칙에 위배됩니다.

이전 리뷰에서 제안한 대로 인증 실패 이벤트를 발행하는 콜백 메커니즘이나 이벤트 버스를 도입하여 UI 관련 로직을 프레젠테이션 레이어로 분리하는 것을 고려해보세요.


49-50: 토큰 저장 작업의 순서 보장 필요

updateAccessTokenupdateRefreshToken 호출이 순차적으로 완료되도록 보장해야 합니다.

다음과 같이 수정하세요:

 }.onSuccess {
     val newAccess = "Bearer ${it.data.accessToken}"
-    dataStore.updateAccessToken(newAccess)
-    dataStore.updateRefreshToken(it.data.refreshToken)
+    dataStore.updateAccessToken(newAccess)
+    dataStore.updateRefreshToken(it.data.refreshToken)
     return newAccess
 }.onFailure {

두 작업이 모두 완료되기를 기다려야 합니다.


54-54: 데이터 클리어 작업 완료 보장 필요

앱 재시작 전에 dataStore.clear() 작업이 완료되도록 보장해야 합니다.

다음과 같이 수정하세요:

 }.onFailure {
     Timber.e(it)
-    dataStore.clear()
+    runBlocking { dataStore.clear() }
     notifyReLoginRequired()
 }
🧹 Nitpick comments (1)
core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt (1)

16-16: 코루틴 스코프 관리 개선 고려

현재 CoroutineScope(Dispatchers.Main)을 직접 생성하고 있습니다. 애플리케이션 생명주기와 연결된 스코프 사용을 고려해보세요.

다음과 같은 대안을 고려할 수 있습니다:

private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

또는 ApplicationScope 주입을 고려해보세요.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 968e3dd and 13442b9.

📒 Files selected for processing (7)
  • core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt (1 hunks)
  • core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt (1 hunks)
  • core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt (1 hunks)
  • core/network/build.gradle.kts (1 hunks)
  • core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (1 hunks)
  • feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt (4 hunks)
  • feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt (3 hunks)
✅ Files skipped from review due to trivial changes (2)
  • core/network/build.gradle.kts
  • core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp Authenticator에서 runBlocking 사용 시 메인 스레드 블로킹은 문제가 되지 않지만, 같은 OkHttp 인스턴스로 토큰 갱신 요청을 보내면 스레드 풀 고갈로 인한 데드락이 발생할 수 있다.
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp의 Authenticator는 워커 스레드에서 실행되므로 runBlocking을 사용해도 메인 스레드를 블로킹하지 않는다.
core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (2)
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp Authenticator에서 runBlocking 사용 시 메인 스레드 블로킹은 문제가 되지 않지만, 같은 OkHttp 인스턴스로 토큰 갱신 요청을 보내면 스레드 풀 고갈로 인한 데드락이 발생할 수 있다.
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp의 Authenticator는 워커 스레드에서 실행되므로 runBlocking을 사용해도 메인 스레드를 블로킹하지 않는다.
🧬 Code Graph Analysis (1)
core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (1)
core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt (1)
  • runSuspendCatching (6-16)
🔇 Additional comments (7)
feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt (1)

7-7: 의존성 주입을 통한 앱 재시작 로직 중앙화

AppReStarter 인터페이스를 통해 앱 재시작 로직을 중앙화한 것은 좋은 개선입니다. 직접적인 Intent 사용을 제거하고 의존성 주입을 통해 관심사 분리를 잘 구현했습니다.

Also applies to: 26-26, 35-36, 69-69

core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt (1)

9-17: 표준적인 Dagger Hilt 모듈 구현

AppReStarter 인터페이스의 싱글톤 바인딩이 적절하게 구현되었습니다. 애플리케이션 전체에서 사용되는 앱 재시작 기능에 대해 싱글톤 스코프가 적합합니다.

feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt (1)

5-5: 일관된 앱 재시작 로직 적용

ProfileDeleteConfirmFragment와 동일한 패턴으로 AppReStarter를 사용하여 앱 재시작 로직을 중앙화했습니다. 코드베이스 전반에 걸쳐 일관된 접근 방식이 적용되었습니다.

Also applies to: 17-17, 23-24, 75-75

core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt (1)

29-35: 토스트 관리 로직 적절히 구현됨

기존 토스트를 취소하고 새 토스트를 표시하는 로직이 잘 구현되어 있습니다. 메모리 누수를 방지하고 사용자 경험을 향상시킵니다.

core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt (3)

25-38: 무한 재시도 방지 로직 적절히 구현됨

401 응답과 이미 재시도된 요청을 확인하는 로직이 잘 구현되어 있습니다. Authorization-Retry 헤더를 사용한 재시도 방지 메커니즘이 적절합니다.


40-59: 토큰 갱신 로직의 동시성 처리 적절함

Mutex를 사용하여 동시 토큰 갱신 요청을 적절히 처리하고 있습니다. 하나의 갱신 작업만 실행되도록 보장하는 것이 좋습니다.


30-37: 별도 OkHttpClient 사용으로 데드락 위험 없음
AuthService@WithoutTokenInterceptor가 붙은 별도의 Retrofit/OkHttpClient 인스턴스를 사용해 토큰 재발급 요청을 수행합니다. 따라서 TokenAuthenticatorrunBlocking 블록이 같은 OkHttp 클라이언트를 재귀 호출하지 않으므로 스레드 풀 고갈에 따른 데드락은 발생하지 않습니다.

Comment on lines +18 to +27
override fun restartApp() {
scope.launch {
val restartIntent = context.packageManager
.getLaunchIntentForPackage(context.packageName)
?.component
?.let(Intent::makeRestartActivityTask)

context.startActivity(restartIntent)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

앱 재시작 시 null 안전성 개선 필요

getLaunchIntentForPackage()가 null을 반환할 경우 startActivity(null)로 인한 예외가 발생할 수 있습니다.

다음과 같이 null 체크를 추가하세요:

 override fun restartApp() {
     scope.launch {
         val restartIntent = context.packageManager
             .getLaunchIntentForPackage(context.packageName)
             ?.component
             ?.let(Intent::makeRestartActivityTask)
-
-        context.startActivity(restartIntent)
+        
+        restartIntent?.let {
+            context.startActivity(it)
+        } ?: run {
+            // 로그 출력 또는 다른 처리
+        }
     }
 }
🤖 Prompt for AI Agents
In
core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt
around lines 18 to 27, the restartApp method calls startActivity with a
potentially null intent if getLaunchIntentForPackage returns null, which can
cause an exception. To fix this, add a null check before calling startActivity
to ensure the intent is not null, and only call startActivity if the intent is
valid.

@Team-Wable Team-Wable deleted a comment from Copilot AI Jul 17, 2025
@Team-Wable Team-Wable deleted a comment from Copilot AI Jul 17, 2025
@Team-Wable Team-Wable deleted a comment from Copilot AI Jul 17, 2025
@Team-Wable Team-Wable deleted a comment from Copilot AI Jul 17, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 683fb27 and 32861c3.

📒 Files selected for processing (1)
  • core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:0-0
Timestamp: 2025-07-17T16:55:14.963Z
Learning: WABLE-ANDROID 프로젝트에서 AppReStarter 인터페이스 패턴을 사용하여 네트워크 레이어에서 UI 작업(토스트, 앱 재시작)을 분리한다. TokenAuthenticator에서 인증 실패 시 AppReStarter를 통해 UI 작업을 위임한다.
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp Authenticator에서 runBlocking 사용 시 메인 스레드 블로킹은 문제가 되지 않지만, 같은 OkHttp 인스턴스로 토큰 갱신 요청을 보내면 스레드 풀 고갈로 인한 데드락이 발생할 수 있다.
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp의 Authenticator는 워커 스레드에서 실행되므로 runBlocking을 사용해도 메인 스레드를 블로킹하지 않는다.
core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt (3)
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:0-0
Timestamp: 2025-07-17T16:55:14.963Z
Learning: WABLE-ANDROID 프로젝트에서 AppReStarter 인터페이스 패턴을 사용하여 네트워크 레이어에서 UI 작업(토스트, 앱 재시작)을 분리한다. TokenAuthenticator에서 인증 실패 시 AppReStarter를 통해 UI 작업을 위임한다.
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp의 Authenticator는 워커 스레드에서 실행되므로 runBlocking을 사용해도 메인 스레드를 블로킹하지 않는다.
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:32-42
Timestamp: 2025-07-17T15:00:12.687Z
Learning: OkHttp Authenticator에서 runBlocking 사용 시 메인 스레드 블로킹은 문제가 되지 않지만, 같은 OkHttp 인스턴스로 토큰 갱신 요청을 보내면 스레드 풀 고갈로 인한 데드락이 발생할 수 있다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR Checker
🔇 Additional comments (4)
core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt (4)

5-5: 새로운 TokenAuthenticator 도입을 확인했습니다.

TokenAuthenticator 클래스가 추가되어 인증 처리 로직이 분리되었습니다. 이는 관심사 분리 원칙에 따른 좋은 아키텍처 개선입니다.


38-38: TokenInterceptor 의존성 단순화가 적절합니다.

@AccessToken 어노테이션이 제거되고 구체적인 TokenInterceptor 타입으로 변경되었습니다. 이는 의존성 주입을 단순화하고 코드의 가독성을 향상시킵니다.


40-40: TokenAuthenticator 의존성 추가가 올바릅니다.

새로운 TokenAuthenticator 파라미터가 추가되어 401 응답 처리를 위한 인증자가 주입됩니다. 이전 학습 내용에 따르면 이 클래스는 AppReStarter 인터페이스를 통해 UI 작업을 적절히 분리하고 있습니다.


47-47: OkHttpClient에 authenticator 설정이 적절합니다.

.authenticator(tokenAuthenticator)가 추가되어 401 Unauthorized 응답 시 자동으로 토큰 갱신을 처리할 수 있습니다. 이전 학습 내용에 따르면 TokenAuthenticator는 워커 스레드에서 실행되므로 성능상 문제가 없습니다.

@Team-Wable Team-Wable deleted a comment from coderabbitai Bot Jul 17, 2025
@coderabbitai

This comment was marked as resolved.

@chanubc

chanubc commented Jul 17, 2025

Copy link
Copy Markdown
Member Author

@coderabbitai Changes, Sequence diagaram등에서 코드블록이 제대로 랜더링 되지가 않아

@coderabbitai

coderabbitai Bot commented Jul 17, 2025

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@chanubc 죄송합니다! 제가 생성한 마크다운에서 코드블록이 제대로 렌더링되지 않았네요.

문제가 된 부분들을 올바른 형식으로 다시 제공해드리겠습니다:

Changes

파일(들) 변경 요약
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultAuthRepository.kt
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultCommunityRepository.kt
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultFeedImageRepository.kt
core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt
runHandledCatching import 경로를 com.teamwable.network.util로 변경
core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt TokenAuthenticator 클래스 신규 추가 (OkHttp Authenticator 구현, 토큰 갱신/재요청/실패 처리)
core/network/src/main/java/com/teamwable/network/TokenInterceptor.kt 토큰 만료/갱신/재시도 로직 삭제, 단순 Authorization 헤더 추가만 수행하도록 단순화
core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt OkHttpClient에 TokenAuthenticator 추가, @accesstoken 어노테이션 제거 및 관련 시그니처 수정
core/network/src/main/java/com/teamwable/network/di/Qualifier.kt AccessToken Qualifier 어노테이션 삭제
core/network/src/main/java/com/teamwable/network/util/runSuspendCatching.kt 패키지명을 com.teamwable.network.util로 변경, 불필요 import 제거
core/common/src/main/java/com/teamwable/common/restarter/AppReStarter.kt
core/common/src/main/java/com/teamwable/common/restarter/AppReStarterModule.kt
core/common/src/main/java/com/teamwable/common/restarter/DefaultAppReStarter.kt
앱 재시작 및 토스트 표시를 위한 AppReStarter 인터페이스 및 구현체, 모듈 추가
feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileDeleteConfirmFragment.kt
feature/profile/src/main/java/com/teamwable/profile/hamburger/ProfileHamburgerBottomSheet.kt
앱 재시작 로직을 Intent 직접 호출에서 AppReStarter 주입 및 호출로 변경

Sequence Diagram

sequenceDiagram
    participant App
    participant OkHttpClient
    participant TokenInterceptor
    participant TokenAuthenticator
    participant AuthService
    participant DataStore

    App->>OkHttpClient: API 요청
    OkHttpClient->>TokenInterceptor: 요청 인터셉트
    TokenInterceptor->>DataStore: AccessToken 조회
    TokenInterceptor->>OkHttpClient: Authorization 헤더 추가 후 요청 진행
    OkHttpClient->>서버: API 요청
    서버-->>OkHttpClient: 401 Unauthorized 응답
    OkHttpClient->>TokenAuthenticator: authenticate 호출
    TokenAuthenticator->>DataStore: RefreshToken 조회
    TokenAuthenticator->>AuthService: 토큰 갱신 시도
    alt 토큰 갱신 성공
        AuthService-->>TokenAuthenticator: 새로운 토큰 반환
        TokenAuthenticator->>DataStore: 토큰 저장
        TokenAuthenticator->>OkHttpClient: 새로운 토큰으로 재요청 반환
    else 토큰 갱신 실패
        TokenAuthenticator->>DataStore: 토큰 삭제
        TokenAuthenticator->>App: Toast 표시 및 앱 재시작
        TokenAuthenticator->>OkHttpClient: null 반환 (요청 실패)
    end
Loading

이제 제대로 렌더링될 것입니다! 향후 마크다운 형식에 더 주의를 기울이겠습니다.


🧠 Learnings used
Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/di/NetworkModule.kt:37-48
Timestamp: 2025-07-17T17:14:37.766Z
Learning: WABLE-ANDROID 프로젝트에서 TokenAuthenticator는 AuthService를 통해 토큰 갱신을 수행하며, AuthService는 ServiceModule에서 @WithoutTokenInterceptor 어노테이션이 붙은 Retrofit을 사용하여 생성된다. 이를 통해 토큰 갱신 시 TokenInterceptor가 적용되지 않은 별도의 OkHttpClient를 사용하여 데드락을 방지한다.

Learnt from: chanubc
PR: Team-Wable/WABLE-ANDROID#180
File: core/network/src/main/java/com/teamwable/network/TokenAuthenticator.kt:0-0
Timestamp: 2025-07-17T16:55:14.963Z
Learning: WABLE-ANDROID 프로젝트에서 AppReStarter 인터페이스 패턴을 사용하여 네트워크 레이어에서 UI 작업(토스트, 앱 재시작)을 분리한다. TokenAuthenticator에서 인증 실패 시 AppReStarter를 통해 UI 작업을 위임한다.

@sohyun127 sohyun127 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

늦게 확인했네요😢 고생하셨습니다!!

private val mutex = Mutex()

override fun authenticate(route: Route?, response: Response): Request? {
if (response.code != 401) return null

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토큰 만료만 401로 오나요?! 따로 토큰 만료인지 확인하는 분기가 없어도 되는지 궁금합니다!

}

private suspend fun refreshToken(): String? {
return mutex.withLock {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍 👍 👍 👍 👍

@sohyun127

Copy link
Copy Markdown
Collaborator

token만료 테스트 해보시고 알려주시와요~!
서버에 토큰 만료 시간 짧게 설정해달라고 해도 되고 mock 서버? 만드는 방법도 있다고 하네요!

@chanubc

chanubc commented Sep 11, 2025

Copy link
Copy Markdown
Member Author

@sohyun127
authienticator와 intercepotr간 의존하는 datastore의 토큰이 실시간으로 상호 갱신이 안되어(runBlocking+first)의 문제로 인해 close하겠습니다 ㅠㅠ
-> 그렇다고 해서 코루틴스코프 내부에서 collect하긴 위험하다 생각해서요! 좋은 방법 생각 나시면 알려주세요!

@chanubc chanubc closed this Sep 11, 2025
@chanubc chanubc deleted the feature/#179-authenticator branch October 19, 2025 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⭐ [FEAT] 새로운 기능 구현 🐻 찬우 차은우

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] : Authenticator 도입

3 participants