Skip to content

Conversation

@wjdrjs00
Copy link
Member

@wjdrjs00 wjdrjs00 commented Jul 8, 2025

[ PR Content ]

소셜 로그인을 통해 발급받는 소셜 토큰값을 기반으로 자체(빛나길)서비스 로그인 구현

Related issue

Screenshot 📸

스크린샷 2025-07-08 17 16 17

Work Description

  • 소셜 토큰 값을 통한 자체 서비스 로그인 api 연동
  • 로그인 응답값으로 발급 받는 인증토큰(access, refresh) datastore에 업데이트
  • 누락된 직렬화 구현체에 대한 의존성 datastore Module에 정의
  • 로그인 api 요청 시 헤더에 자체 서비스 토큰 제외 로직 구현

To Reviewers 📢

  • 예외 처리 관련해서 커스텀Result<T>를 정의하여 사용하는 경우 기본 Result<T>의 확장함수들을 사용 못해서 예외 클래스를 정의하고 서비스 에러를 잡는 방향으로 구현했습니다.
  • 궁금한거, 이상한거, 의도가 궁금한거 등 편하게 리뷰남겨주시면 감사하겠슴다! 🙇🏻‍♂️

Summary by CodeRabbit

  • 신규 기능

    • 로그인 도메인 모델, 데이터 소스, 저장소, 서비스, 유즈케이스 등 인증 관련 구조 전반 추가
    • 로그인 요청/응답 데이터 모델 및 매핑 기능 추가
    • 네트워크 에러 응답, 커스텀 예외 타입(BitnagilError) 도입
    • 로그인 성공 시 홈 화면, 약관 화면 이동을 위한 사이드 이펙트 추가
    • 인증 관련 의존성 주입 모듈 추가 및 개선
    • 안전한 API 호출을 위한 공통 함수 도입
    • 인증 토큰 관리 및 인터셉터 동작 개선
    • 카카오 로그인 처리용 핸들러 인터페이스 및 구현체 추가
  • 버그 수정

    • 인증 토큰 관련 데이터 저장 및 업데이트 함수 시그니처 개선 및 반환값 단순화
  • 리팩터

    • 로그인 뷰모델에서 카카오 로그인 처리 및 앱 내 인증 플로우를 도메인 유즈케이스 기반으로 구조화
    • 로그인 상태 표현을 isLoading, isGuest로 세분화
    • 로그인 화면 내 카카오 로그인 호출 및 콜백 처리 방식 개선
  • 문서화/설정

    • 각 모듈의 의존성 및 DI(Hilt) 설정 추가 및 개선

wjdrjs00 added 11 commits July 8, 2025 02:31
- 레이어 모듈간 의존성 추가 data -> domain <- presentation
- 불필요한 .gitkeep 파일 제거
- AuthTokenSerializerImpl 의존성 주입 추가
- datastore 모듈 datastore-preferences 의존성 api로 변경
- AuthSession 모델 클래스 추가
- AuthRepository 인터페이스 추가
- LoginUseCase 클래스 구현
- AuthService, AuthRemoteDataSource, AuthRepository 구현
- 로그인 API 연동 및 관련 DTO, Mapper, SafeApiCall 구현
- Hilt를 사용하여 의존성 주입 설정 (DataSourceModule, RepositoryModule, ServiceModule)
- AuthLocalDataSource 및 구현체 AuthLocalDataSourceImpl 추가
- LoginUseCase에서 로그인 성공 시 토큰 저장 로직 추가
- UserRole enum class를 정의하여 사용자 역할 관리
- 로그인 결과에 따라 Home 또는 약관 동의 화면으로 이동하도록 SideEffect 추가
@wjdrjs00 wjdrjs00 requested a review from l5x5l July 8, 2025 15:51
@wjdrjs00 wjdrjs00 self-assigned this Jul 8, 2025
@wjdrjs00 wjdrjs00 added ✨ Feature 새로운 기능 구현 🧤 대현 labels Jul 8, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 8, 2025

## Walkthrough

로그인 API 연동을 위해 도메인, 데이터, 프레젠테이션, 네트워크, 데이터스토어 계층 전반에 걸쳐 인증 관련 모델, 데이터소스, 레포지토리, 유즈케이스, DI 모듈, 서비스, 에러 처리, 토큰 저장 및 인터셉터 로직이 추가 및 리팩토링되었습니다. 전반적으로 로그인 플로우와 토큰 관리의 전체 구조가 구현되었습니다.

## Changes

| 파일/그룹 | 변경 요약 |
|---|---|
| **도메인 계층**<br>domain/src/main/java/com/threegap/bitnagil/domain/auth/model/AuthSession.kt<br>domain/src/main/java/com/threegap/bitnagil/domain/auth/model/UserRole.kt<br>domain/src/main/java/com/threegap/bitnagil/domain/auth/repository/AuthRepository.kt<br>domain/src/main/java/com/threegap/bitnagil/domain/auth/usecase/LoginUseCase.kt<br>domain/src/main/java/com/threegap/bitnagil/domain/error/model/BitnagilError.kt | 인증 세션, 역할, 레포지토리, 유즈케이스, 에러 모델 등 로그인 도메인 모델 및 인터페이스/클래스 추가 |
| **데이터 계층**<br>data/src/main/java/com/threegap/bitnagil/data/auth/datasource/AuthLocalDataSource.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/datasource/AuthRemoteDataSource.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthLocalDataSourceImpl.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthRemoteDataSourceImpl.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/mapper/AuthMapper.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/model/request/LoginRequestDto.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/model/response/LoginResponseDto.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/repositoryimpl/AuthRepositoryImpl.kt<br>data/src/main/java/com/threegap/bitnagil/data/auth/service/AuthService.kt<br>data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt | 인증 관련 데이터소스, DTO, 매퍼, 레포지토리 구현 및 네트워크 응답/에러 처리 유틸 추가 |
| **DI 모듈**<br>app/src/main/java/com/threegap/bitnagil/di/core/DataStoreModule.kt<br>app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt<br>app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt<br>app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt<br>app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt | 데이터소스, 레포지토리, 서비스, 토큰 직렬화기, 토큰 프로바이더 등 DI 바인딩 및 프로바이더 추가/수정 |
| **데이터스토어/토큰**<br>core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/serializer/AuthTokenSerializerImpl.kt<br>core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStore.kt<br>core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStoreImpl.kt | 토큰 직렬화기 인터페이스 변경, 데이터스토어 인터페이스 및 구현의 시그니처 및 예외 처리 방식 변경 |
| **네트워크**<br>core/network/src/main/java/com/threegap/bitnagil/network/auth/AuthInterceptor.kt<br>core/network/src/main/java/com/threegap/bitnagil/network/model/ErrorResponse.kt<br>core/network/src/main/java/com/threegap/bitnagil/network/token/TokenProvider.kt | 인증 인터셉터 토큰 주입 로직 개선, 에러 응답 모델 추가, TokenProvider 시그니처 변경 |
| **프레젠테이션**<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginScreen.kt<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginSideEffect.kt<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginState.kt<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginIntent.kt<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandler.kt<br>presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandlerImpl.kt | 로그인 화면, 뷰모델, 상태, 사이드이펙트, 인텐트, 카카오 로그인 핸들러 인터페이스 및 구현 추가/리팩토링 |
| **빌드 설정**<br>data/build.gradle.kts<br>domain/build.gradle.kts<br>core/datastore/build.gradle.kts<br>presentation/build.gradle.kts | 각 모듈별 의존성 추가 및 플러그인 설정 변경 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant LoginScreen
    participant LoginViewModel
    participant LoginUseCase
    participant AuthRepository
    participant AuthRemoteDataSource
    participant AuthLocalDataSource
    participant AuthService
    participant AuthTokenDataStore

    User->>LoginScreen: 로그인 시도
    LoginScreen->>LoginViewModel: OnKakaoLoginResult(intent)
    LoginViewModel->>LoginUseCase: invoke(socialAccessToken, socialType)
    LoginUseCase->>AuthRepository: login(socialAccessToken, socialType)
    AuthRepository->>AuthRemoteDataSource: login(token, dto)
    AuthRemoteDataSource->>AuthService: postLogin(token, dto)
    AuthService-->>AuthRemoteDataSource: BaseResponse<LoginResponseDto>
    AuthRemoteDataSource-->>AuthRepository: Result<LoginResponseDto>
    AuthRepository-->>LoginUseCase: Result<AuthSession>
    LoginUseCase->>AuthRepository: updateAuthToken(accessToken, refreshToken)
    AuthRepository->>AuthLocalDataSource: updateAuthToken(accessToken, refreshToken)
    AuthLocalDataSource->>AuthTokenDataStore: updateAuthToken(accessToken, refreshToken)
    AuthTokenDataStore-->>AuthLocalDataSource: Unit
    AuthLocalDataSource-->>AuthRepository: Result<Unit>
    LoginUseCase-->>LoginViewModel: Result<AuthSession>
    LoginViewModel->>LoginScreen: 상태/사이드이펙트 업데이트

Assessment against linked issues

Objective Addressed Explanation
자체 서비스 로그인 API 연동 및 전체 인증 플로우 구현 (#18)
인증 토큰 저장 및 관리, 네트워크 인터셉터, 에러 처리 (#18)
로그인 화면, 뷰모델, 상태, 사이드이펙트에서 로그인 연동 (#18)

Possibly related PRs

Suggested reviewers

  • l5x5l

Poem

🐇
로그인 흐름이 완성됐어요,
토큰도 저장, 에러도 잡아요!
데이터 흐름이 촘촘해져
유저는 이제 쉽게 들어와요.
깡총깡총, 코드 속을 달려
오늘도 로그인은 토끼처럼 빠르게!
🥕


<!-- 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=YAPP-Github/Bitnagil-Android&utm_content=21):

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (9)
core/network/src/main/java/com/threegap/bitnagil/network/model/ErrorResponse.kt (1)

6-12: @SerialName 제거 검토

프로퍼티 이름과 JSON 필드가 동일하므로 @SerialName 애너테이션이 사실상 불필요합니다. 유지 보수성을 위해 불필요한 메타데이터를 줄이는 편이 가독성에 도움이 됩니다.

-    @SerialName("code")
-    val code: String,
-    @SerialName("message")
-    val message: String,
+    val code: String,
+    val message: String,
domain/build.gradle.kts (1)

5-8: javax.inject 의존성 범위 재검토

domain 모듈은 프레임워크 독립성을 유지하는 것이 일반적인데, javax.inject 는 러untime에도 포함됩니다.
필요한 것이 어노테이션만이라면 다음과 같이 compileOnly 로 변경해 바이너리 크기를 줄일 수 있습니다.

-    implementation(libs.javax.inject)
+    compileOnly(libs.javax.inject)
core/datastore/build.gradle.kts (1)

10-13: api 노출이 과도할 수 있음

androidx.datastore.preferencesapi 로 공개하면 상위 모듈이 Datastore 구현 세부사항에 직접 접근할 수 있습니다.
의도적으로 노출하는 것이 아니라면 기존 implementation 을 유지하는 편이 캡슐레이션에 유리합니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginScreen.kt (1)

44-50: TODO 주석 완료 추적 필요

새로운 네비게이션 side effect들이 추가되었지만 TODO 주석으로 남아있습니다. 이슈 트래킹을 통해 완료 일정을 관리해주세요.

네비게이션 로직 구현을 위한 새로운 이슈를 생성하시겠습니까?

domain/src/main/java/com/threegap/bitnagil/domain/error/model/BitnagilError.kt (1)

3-6: 도메인 에러 모델 구현 양호

커스텀 예외 클래스가 적절히 구현되었습니다. Exception을 상속하고 code와 message를 포함하는 구조가 좋습니다.

더 나은 타입 안전성을 위해 code 필드를 enum으로 변경하는 것을 고려해보세요:

+enum class ErrorCode {
+    NETWORK_ERROR,
+    AUTHENTICATION_ERROR,
+    VALIDATION_ERROR,
+    UNKNOWN_ERROR
+}
+
 data class BitnagilError(
-    val code: String,
+    val code: ErrorCode,
     override val message: String,
 ) : Exception()
data/src/main/java/com/threegap/bitnagil/data/auth/model/response/LoginResponseDto.kt (1)

6-14: 응답 DTO 구현 양호, 보안 고려사항 검토 필요

직렬화 어노테이션이 적절히 적용된 깔끔한 DTO 구현입니다.

보안 및 타입 안전성 개선을 위한 제안사항:

  1. role 필드를 enum으로 변경:
+enum class UserRole {
+    GUEST, MEMBER, ADMIN
+}
+
 @Serializable
 data class LoginResponseDto(
     @SerialName("accessToken")
     val accessToken: String,
     @SerialName("refreshToken")
     val refreshToken: String,
     @SerialName("role")
-    val role: String,
+    val role: UserRole,
 )
  1. 토큰 필드에 대한 추가 검증 로직 고려
data/src/main/java/com/threegap/bitnagil/data/auth/model/request/LoginRequestDto.kt (1)

6-10: 요청 DTO 구현 양호, 타입 안전성 개선 제안

직렬화가 적절히 구현된 간단한 요청 DTO입니다.

타입 안전성 향상을 위해 socialType을 enum으로 변경하는 것을 고려해보세요:

+enum class SocialType {
+    @SerialName("kakao")
+    KAKAO,
+    @SerialName("naver")
+    NAVER,
+    @SerialName("google")
+    GOOGLE
+}
+
 @Serializable
 data class LoginRequestDto(
     @SerialName("socialType")
-    val socialType: String,
+    val socialType: SocialType,
 )

이렇게 하면 컴파일 타임에 유효하지 않은 소셜 타입 값을 방지할 수 있습니다.

data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (2)

34-38: 예외 처리 타입 일관성을 개선해주세요.

현재 HttpExceptionBitnagilError로 반환하지만 IOException과 일반 ExceptionException으로 반환하여 일관성이 부족합니다. 모든 예외를 BitnagilError로 통일하는 것을 고려해보세요.

 } catch (e: IOException) {
-    Result.failure(Exception(e.message ?: "Network error"))
+    Result.failure(
+        BitnagilError(
+            code = "NETWORK_ERROR",
+            message = e.message ?: "Network error",
+        )
+    )
 } catch (e: Exception) {
-    Result.failure(Exception(e.message ?: "Unknown error"))
+    Result.failure(
+        BitnagilError(
+            code = "UNKNOWN_ERROR", 
+            message = e.message ?: "Unknown error",
+        )
+    )
 }

24-27: JSON 파싱 실패 시 예외 처리를 추가해주세요.

Json.decodeFromString 호출 시 파싱 실패 예외가 발생할 수 있습니다. 예외 처리를 추가하여 안정성을 높이는 것을 고려해보세요.

 val errorResponse = errorBody?.let {
-    Json.decodeFromString<ErrorResponse>(it)
+    try {
+        Json.decodeFromString<ErrorResponse>(it)
+    } catch (e: Exception) {
+        null
+    }
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a03e99d and c418049.

📒 Files selected for processing (34)
  • app/src/main/java/com/threegap/bitnagil/di/core/DataStoreModule.kt (2 hunks)
  • app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt (1 hunks)
  • app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt (1 hunks)
  • app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt (1 hunks)
  • app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt (1 hunks)
  • core/datastore/build.gradle.kts (1 hunks)
  • core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/serializer/AuthTokenSerializerImpl.kt (1 hunks)
  • core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStore.kt (1 hunks)
  • core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStoreImpl.kt (1 hunks)
  • core/network/src/main/java/com/threegap/bitnagil/network/auth/AuthInterceptor.kt (2 hunks)
  • core/network/src/main/java/com/threegap/bitnagil/network/model/ErrorResponse.kt (1 hunks)
  • core/network/src/main/java/com/threegap/bitnagil/network/token/TokenProvider.kt (1 hunks)
  • data/build.gradle.kts (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/datasource/AuthLocalDataSource.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/datasource/AuthRemoteDataSource.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthLocalDataSourceImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthRemoteDataSourceImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/mapper/AuthMapper.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/model/request/LoginRequestDto.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/model/response/LoginResponseDto.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/repositoryimpl/AuthRepositoryImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/auth/service/AuthService.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (1 hunks)
  • domain/build.gradle.kts (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/auth/model/AuthSession.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/auth/model/UserRole.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/auth/repository/AuthRepository.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/auth/usecase/LoginUseCase.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/error/model/BitnagilError.kt (1 hunks)
  • presentation/build.gradle.kts (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginScreen.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginSideEffect.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginState.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#16
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:12-46
Timestamp: 2025-07-03T09:05:30.067Z
Learning: 이 프로젝트에서는 네트워크 모듈을 점진적으로 개발하고 있으며, TokenAuthenticator 같은 인증 관련 기능은 실제 API 연동 작업 시점에 NetworkModule에 연결할 예정입니다.
app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt (1)
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#16
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:12-46
Timestamp: 2025-07-03T09:05:30.067Z
Learning: 이 프로젝트에서는 네트워크 모듈을 점진적으로 개발하고 있으며, TokenAuthenticator 같은 인증 관련 기능은 실제 API 연동 작업 시점에 NetworkModule에 연결할 예정입니다.
core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStore.kt (1)
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#19
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:16-20
Timestamp: 2025-07-06T04:26:03.870Z
Learning: TokenAuthenticator 클래스는 현재 임시 코드로 구현되어 있으며, 실제 토큰 재발급 API 연동 시점에 올바른 구현으로 수정될 예정입니다.
🧬 Code Graph Analysis (2)
data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthRemoteDataSourceImpl.kt (1)
data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (1)
  • safeApiCall (10-39)
presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/mviviewmodel/MviViewModel.kt (1)
  • sendSideEffect (23-23)
⏰ 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: build
🔇 Additional comments (30)
core/network/src/main/java/com/threegap/bitnagil/network/model/ErrorResponse.kt (1)

8-10: code 타입 확인

API 스펙상 code가 숫자라면 Int 타입으로 선언하는 편이 직관적이며, 문자열로 받아 숫자로 변환하는 비용을 줄일 수 있습니다. 스펙을 한 번 더 확인해 주세요.

presentation/build.gradle.kts (2)

4-5: Hilt 플러그인 추가 후 kapt 세팅 확인 필요

bitnagil.android.hilt 플러그인이 kotlin-kaptdagger-hilt-compiler 의존성을 자동으로 추가하지 않는 경우 컴파일 오류가 발생합니다.
필요 시 다음을 추가해 주세요.

plugins {
    kotlin("kapt")
}

dependencies {
    kapt(libs.hilt.compiler)
}

12-15: 의존성 순환 여부 확인

presentationdomain 참조가 추가되었는데, 다른 모듈에서 이미 presentation 을 의존하고 있지 않은지 확인해 순환 참조를 방지하세요.

data/build.gradle.kts (2)

13-17: 직렬화 라이브러리 중복 여부 확인

kotlinx.serialization.json 을 추가하셨는데, 상위 모듈(예: core/network)에서 이미 동일 라이브러리를 api 로 노출하고 있다면 중복 의존성이 생길 수 있습니다. 버전 충돌 가능성을 점검해 주세요.


13-17: LGTM!

도메인·데이터스토어 의존성 추가가 로그인 플로우 구현과 잘 맞아떨어집니다.

core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/serializer/AuthTokenSerializerImpl.kt (1)

14-14: 인터페이스 추상화 개선이 잘 되었습니다.

Serializer<AuthToken>에서 AuthTokenSerializer로 변경하여 더 나은 추상화와 의존성 주입을 가능하게 했습니다. 이는 테스트 가능성과 모듈 간 결합도를 낮추는 좋은 리팩토링입니다.

core/network/src/main/java/com/threegap/bitnagil/network/token/TokenProvider.kt (1)

4-4: 메서드 이름 개선이 적절합니다.

getToken()에서 getAccessToken()으로 변경하여 메서드의 역할을 더 명확하게 표현했습니다. 이는 코드의 가독성을 높이고 API를 더 자명하게 만듭니다.

domain/src/main/java/com/threegap/bitnagil/domain/auth/model/AuthSession.kt (1)

3-7: 잘 설계된 도메인 모델입니다.

인증 세션을 나타내는 데이터 클래스가 적절하게 구성되었습니다. 원시 타입 대신 도메인 특화 타입(UserRole)을 사용하고, 불변 객체로 설계하여 도메인 모델의 원칙을 잘 따르고 있습니다.

app/src/main/java/com/threegap/bitnagil/di/core/DataStoreModule.kt (1)

30-33: 의존성 주입 설정이 올바릅니다.

AuthTokenSerializer의 프로바이더 메서드가 적절하게 구성되었습니다. 싱글톤 스코프와 의존성 주입이 올바르게 설정되어 있으며, Dagger/Hilt 모범 사례를 잘 따르고 있습니다.

data/src/main/java/com/threegap/bitnagil/data/auth/mapper/AuthMapper.kt (1)

7-11: 매핑 함수가 적절하게 구현되었습니다.

DTO에서 도메인 모델로의 변환 로직이 깔끔하게 구현되었습니다. 확장 함수 방식을 사용하여 가독성을 높였고, UserRole.from()을 통한 역할 변환이 적절히 처리되어 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginScreen.kt (1)

24-26: 함수 파라미터 다중 줄 포맷팅 개선

함수 파라미터를 다중 줄로 포맷팅하여 가독성이 향상되었습니다.

data/src/main/java/com/threegap/bitnagil/data/auth/datasource/AuthLocalDataSource.kt (1)

3-5: 인터페이스 설계 우수

로컬 데이터 소스 인터페이스가 깔끔하게 설계되었습니다. Result 반환 타입으로 에러 처리가 적절히 구현되었고, suspend 함수로 비동기 처리가 고려되었습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginSideEffect.kt (1)

10-12: 새로운 네비게이션 사이드 이펙트 추가가 적절합니다.

NavigateToHomeNavigateToTermsOfService 사이드 이펙트가 기존 패턴을 잘 따르고 있으며, MVI 아키텍처에서 네비게이션을 처리하는 적절한 방법입니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginState.kt (1)

8-9: 상태 관리 개선이 우수합니다.

isLoggedIn을 제거하고 isLoadingisGuest를 추가한 것은 더 세밀한 상태 제어를 가능하게 합니다. 로딩 상태와 게스트 상태를 명확히 구분하여 UI 피드백을 향상시킬 수 있습니다.

data/src/main/java/com/threegap/bitnagil/data/auth/datasource/AuthRemoteDataSource.kt (1)

6-8: 깔끔한 인터페이스 설계입니다.

AuthRemoteDataSource 인터페이스가 클린 아키텍처 원칙을 잘 따르고 있습니다. Result 래퍼를 사용한 에러 처리와 명확한 메서드 시그니처가 우수합니다.

app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt (1)

18-19: @Auth 한정자 정의 확인 완료 및 DI 설정 승인

Qualifier.kt에서 @Auth 한정자가 올바르게 정의(annotation class Auth)되어 있으며, NetworkModule과 ServiceModule 양쪽에서 일관되게 사용되고 있습니다. 추가 변경 없이 현재 DI 설정을 그대로 승인합니다.

app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt (1)

15-17: 리포지토리 바인딩이 올바르게 구현되었습니다.

@Binds 어노테이션을 사용한 인터페이스 바인딩이 효율적이며, 싱글톤 스코프 설정도 리포지토리 레이어에 적합합니다.

domain/src/main/java/com/threegap/bitnagil/domain/auth/model/UserRole.kt (1)

3-18: 잘 구현된 UserRole 열거형입니다.

코드가 깔끔하고 명확하며, 다음과 같은 장점이 있습니다:

  • isGuest() 메서드로 편리한 역할 확인 제공
  • from() 팩토리 메서드로 문자열 변환 지원
  • 적절한 예외 처리로 안전성 보장
data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthLocalDataSourceImpl.kt (1)

7-18: 로컬 데이터 소스 구현이 올바르게 되어 있습니다.

runCatching을 사용한 에러 처리와 의존성 주입이 적절히 구현되어 있습니다. AuthTokenDataStore에 대한 위임 패턴이 깔끔하게 적용되었습니다.

domain/src/main/java/com/threegap/bitnagil/domain/auth/repository/AuthRepository.kt (1)

5-9: 인증 레포지토리 인터페이스가 잘 설계되었습니다.

명확한 메서드 시그니처와 일관된 Result<T> 반환 타입을 사용하여 에러 처리가 일관성 있게 구현되어 있습니다.

data/src/main/java/com/threegap/bitnagil/data/auth/service/AuthService.kt (1)

11-18: 인증 서비스 인터페이스가 올바르게 구현되었습니다.

Retrofit 어노테이션이 적절히 사용되었고, No-Service-Token 헤더를 통한 토큰 바이패스 로직이 잘 구현되어 있습니다. API 엔드포인트와 파라미터 구조가 명확합니다.

data/src/main/java/com/threegap/bitnagil/data/auth/datasourceimpl/AuthRemoteDataSourceImpl.kt (1)

10-17: 깔끔한 데이터 소스 구현입니다.

Clean Architecture 원칙을 잘 따르고 있으며, safeApiCall을 활용한 일관된 에러 처리가 잘 적용되어 있습니다. 의존성 주입과 인터페이스 구현이 적절합니다.

core/network/src/main/java/com/threegap/bitnagil/network/auth/AuthInterceptor.kt (2)

14-18: 로그인 시 토큰 제외 로직이 잘 구현되었습니다.

No-Service-Token 헤더를 확인하여 서비스 토큰을 제외하는 로직이 명확하고 적절합니다. 로그인 API 호출 시 순환 참조를 방지하는 좋은 접근 방법입니다.


32-36: 헬퍼 메서드가 명확하고 재사용 가능합니다.

removeNoTokenHeader 메서드가 명확하게 구현되어 있고, 단일 책임 원칙을 잘 따르고 있습니다.

app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt (1)

13-24: 의존성 주입 모듈이 표준적으로 잘 구현되었습니다.

Dagger Hilt를 사용한 데이터 소스 바인딩이 적절하며, @Singleton 스코프가 올바르게 적용되어 있습니다. 모듈 구조가 명확하고 확장 가능합니다.

data/src/main/java/com/threegap/bitnagil/data/auth/repositoryimpl/AuthRepositoryImpl.kt (1)

11-21: 레포지토리 패턴이 잘 구현되었습니다.

원격 및 로컬 데이터 소스를 적절히 조합하고 있으며, 도메인 모델로의 매핑이 올바르게 처리되어 있습니다. 코드가 간결하고 명확합니다.

data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (1)

10-22: 전반적으로 잘 구현된 API 에러 처리 유틸리티입니다.

기본적인 에러 처리 로직이 잘 구현되어 있고, 널 데이터 케이스도 적절히 처리되어 있습니다. 제안된 개선사항을 적용하면 더욱 견고한 구현이 될 것입니다.

app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt (1)

67-68: TokenAuthenticator 통합 필요

provideAuthOkHttpClient에서 토큰 재발급을 처리할 TokenAuthenticator가 빠져 있습니다. OkHttpClient에 .authenticator(...)로 설정하고, 이를 주입하는 함수도 추가해주세요.

  • 위치:
    app/src/main/java/com/threegap/bitnagil/di/core/NetworkModule.kt
    provideAuthOkHttpClient 메서드 내 OkHttpClient.Builder 체인

  • 예시 수정

@Provides
@Singleton
fun provideTokenAuthenticator(tokenProvider: TokenProvider): Authenticator =
    TokenAuthenticator(tokenProvider)

@Provides
@Singleton
@Auth
fun provideAuthOkHttpClient(
    httpLoggingInterceptor: HttpLoggingInterceptor,
    @Auth authInterceptor: Interceptor,
    tokenAuthenticator: Authenticator   // ← 추가
): OkHttpClient = OkHttpClient.Builder()
    .addInterceptor(authInterceptor)
    .addInterceptor(httpLoggingInterceptor)
    .authenticator(tokenAuthenticator)    // ← 추가
    .connectTimeout(10L, TimeUnit.SECONDS)
    .writeTimeout(30L, TimeUnit.SECONDS)
    .readTimeout(30L, TimeUnit.SECONDS)
    .build()

위와 같이 TokenAuthenticator를 등록해 토큰 만료 시 자동 갱신 로직이 동작하도록 해주세요.

⛔ Skipped due to learnings
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#16
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:12-46
Timestamp: 2025-07-03T09:05:30.067Z
Learning: 이 프로젝트에서는 네트워크 모듈을 점진적으로 개발하고 있으며, TokenAuthenticator 같은 인증 관련 기능은 실제 API 연동 작업 시점에 NetworkModule에 연결할 예정입니다.
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#19
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:16-20
Timestamp: 2025-07-06T04:26:03.870Z
Learning: TokenAuthenticator 클래스는 현재 임시 코드로 구현되어 있으며, 실제 토큰 재발급 API 연동 시점에 올바른 구현으로 수정될 예정입니다.
core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStoreImpl.kt (1)

13-57: 토큰 관리 메서드 구현이 잘 되었습니다.

try-catch 블록을 사용한 명시적 에러 처리와 적절한 로깅이 구현되었습니다. 에러를 다시 throw하여 호출자가 처리할 수 있도록 한 점도 좋습니다.

core/datastore/src/main/java/com/threegap/bitnagil/datastore/auth/storage/AuthTokenDataStore.kt (1)

9-15: 인터페이스 개선이 잘 되었습니다.

토큰을 개별적으로 업데이트할 수 있도록 메서드 시그니처를 변경한 것이 좋습니다. 이는 액세스 토큰만 갱신하는 경우에 유용합니다.

wjdrjs00 added 3 commits July 9, 2025 01:35
- mapCatching을 사용하여 예외 발생 시 전파하도록 수정
- firstOrNull()로 변경하여, 토큰이 존재하지 않을 경우 NullPointerException이 발생하는 문제를 해결
wjdrjs00 added 3 commits July 10, 2025 02:35
LoginIntent를 세분화하여 각 상황에 맞는 Intent를 사용하도록 수정
- KakaoLoginHandlerImpl 추가하여 카카오 로그인 관련 로직 위임
- LoginScreen 카카오톡 설치 여부에 따른 분기 처리 제거
- OnKakaoLoginClick Intent 제거
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandlerImpl.kt (1)

7-8: 테스트 가능성을 위한 구조 개선을 고려해보세요.

object로 구현하면 테스트 시 모킹이 어려울 수 있습니다. 향후 단위 테스트를 고려한다면 다음과 같은 구조를 고려해보세요:

-object KakaoLoginHandlerImpl : KakaoLoginHandler {
-    private val client = UserApiClient.instance
+@Singleton
+class KakaoLoginHandlerImpl @Inject constructor(
+    private val client: UserApiClient = UserApiClient.instance
+) : KakaoLoginHandler {
presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (2)

84-85: 소셜 타입 하드코딩 개선을 고려해보세요.

socialType = "KAKAO"가 하드코딩되어 있습니다. 향후 다른 소셜 로그인 지원 시 유연성을 위해 상수나 enum으로 관리하는 것을 고려해보세요.

// 예시: 상수 또는 enum 사용
object SocialType {
    const val KAKAO = "KAKAO"
    const val GOOGLE = "GOOGLE"
}

// 사용
socialType = SocialType.KAKAO

93-96: 에러 로깅 중복 개선이 필요합니다.

과거 리뷰에서 지적되었던 에러 로깅 중복 문제가 여전히 존재합니다. 다음과 같이 개선할 수 있습니다:

-                if (e is BitnagilError) {
-                    Log.e("Login", "${e.code} ${e.message}")
-                }
-                Log.e("Login", "${e.message}")
+                when (e) {
+                    is BitnagilError -> {
+                        Log.e("Login", "BitnagilError: ${e.code} - ${e.message}")
+                    }
+                    else -> {
+                        Log.e("Login", "Login error: ${e.message}")
+                    }
+                }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fe99bbb and 5869a40.

📒 Files selected for processing (6)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginScreen.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandler.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandlerImpl.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginIntent.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginSideEffect.kt (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandler.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginScreen.kt
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#16
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:12-46
Timestamp: 2025-07-03T09:05:30.067Z
Learning: 이 프로젝트에서는 네트워크 모듈을 점진적으로 개발하고 있으며, TokenAuthenticator 같은 인증 관련 기능은 실제 API 연동 작업 시점에 NetworkModule에 연결할 예정입니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (1)
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#16
File: core/network/src/main/java/com/threegap/bitnagil/network/auth/TokenAuthenticator.kt:12-46
Timestamp: 2025-07-03T09:05:30.067Z
Learning: 이 프로젝트에서는 네트워크 모듈을 점진적으로 개발하고 있으며, TokenAuthenticator 같은 인증 관련 기능은 실제 API 연동 작업 시점에 NetworkModule에 연결할 예정입니다.
🧬 Code Graph Analysis (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/mviviewmodel/MviViewModel.kt (2)
  • sendSideEffect (23-23)
  • sendIntent (30-37)
⏰ 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: build
🔇 Additional comments (6)
presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginSideEffect.kt (1)

7-8: 로그인 플로우 개선이 잘 반영되었습니다.

카카오 로그인 처리를 별도 핸들러로 분리하고, 사이드 이펙트를 로그인 성공 후의 네비게이션에 집중하도록 개선한 것이 좋습니다. 게스트 사용자와 정회원 사용자의 플로우를 명확히 구분할 수 있게 되었네요.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/kakao/KakaoLoginHandlerImpl.kt (1)

14-18: 카카오톡 로그인 가용성 체크 로직이 명확합니다.

카카오톡 앱 설치 여부에 따른 로그인 방식 분기 처리가 잘 구현되어 있습니다. 사용자 경험을 고려한 좋은 접근법이네요.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/LoginViewModel.kt (3)

23-23: 도메인 유즈케이스 도입으로 아키텍처가 개선되었습니다.

LoginUseCase를 의존성 주입으로 받아 도메인 로직을 분리한 것이 좋습니다. 클린 아키텍처 원칙에 잘 맞는 구조네요.


28-59: MVI 패턴 준수가 잘 개선되었습니다.

이전 리뷰 피드백을 잘 반영하여 sendIntent를 통한 상태 변경으로 구조를 개선했네요. 각 인텐트별로 명확한 상태 변경과 사이드 이펙트 처리가 구현되어 있습니다.


61-80: 코루틴 기반 로그인 처리 로직이 잘 구현되었습니다.

OAuth 토큰과 에러를 매개변수로 받아 적절히 분기 처리하는 구조가 명확합니다. 특히 카카오 로그인 취소에 대한 별도 처리가 사용자 경험을 고려한 좋은 접근법입니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/login/model/LoginIntent.kt (1)

6-9: 인텐트 구조가 크게 개선되었습니다.

기존의 카카오 특화 인텐트들을 제거하고 일반적인 로그인 플로우 인텐트로 리팩토링한 것이 좋습니다. 특히 다음 개선점들이 돋보입니다:

  • SetLoading: 로딩 상태 관리를 위한 명확한 인텐트
  • LoginSuccess: 게스트 여부를 포함한 성공 상태 처리
  • KakaoTalkLoginCancel: 카카오톡 로그인 취소 시 계정 로그인으로 fallback 처리
  • LoginFailure: 명확한 실패 상태 처리

향후 다른 소셜 로그인 추가 시에도 재사용 가능한 구조네요.

@wjdrjs00 wjdrjs00 requested a review from l5x5l July 10, 2025 12:52
Copy link
Contributor

@l5x5l l5x5l left a comment

Choose a reason for hiding this comment

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

오늘 스크럼에서 나온 context이용한 부분을 인터페이스로 분리해 viewModel에서 참조하도록 하는 것 관련해서는 서로 이해하는 방식이 달랐던 것 같습니다!
일단 현재 큰 문제가 될만한 부분은 아니니 일단 머지 진행하시고, 주말에 다시 한번 얘기 나눠보죠!

@wjdrjs00 wjdrjs00 merged commit b63d33e into develop Jul 10, 2025
2 checks passed
@wjdrjs00 wjdrjs00 deleted the feature/#18-login-api branch July 10, 2025 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 구현 🧤 대현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] 로그인 api 연동

3 participants