Skip to content

Conversation

@casper-jr
Copy link
Member

@casper-jr casper-jr commented Oct 3, 2025

🚀 이슈번호

✏️ 변경사항

  • 동아리 추가 화면에서 사용할 AddClubViewModel 작성, Di 모듈에 추가
  • 동아리 코드를 통한 동아리 이름 확인 api 연결
  • 동아리 추가 api 연결
  • datasource에서 요청 오류에 대한 처리에 사용할 ErrorResponseDto 추가
  • ui에 동아리 조회, 동아리 추가에 대한 오류 메세지 반영
  • 동아리 조회 성공시 키보드 숨김 로직 추가
  • 기존에 ClubCodeInputScreen에서 사용하던 상태 관련 변수들을 uiState로 리팩토링

📷 스크린샷

안드로이드

Screen_Recording_20251004_000039_WhosInClient.mp4

iOS

2025-10-03.23.47.23.mov

✍️ 사용법

  • api 요청에 실패했을 때, 오류 메세지를 사용해야 되는 경우에는 dataSource에서 추가된 ErrorResponseDto를 사용해서 파싱하면 됩니다.

🎸 기타

Summary by CodeRabbit

  • New Features
    • 클럽 코드 확인 및 클럽 추가 기능을 지원합니다.
    • 확인 성공 시 클럽 이름 표시 및 자동 추가/완료 처리, 홈으로 자동 이동을 제공합니다.
  • Refactor
    • 클럽 코드 입력 화면이 ViewModel 기반 상태 관리로 전환되어 더 일관된 로딩/성공/오류 흐름을 제공합니다.
    • 오류 메시지가 구조화되어 표시되어 실패 원인을 명확히 안내합니다.
    • 입력 확인/성공 시 키보드 자동 숨김으로 사용성이 개선되었습니다.

@casper-jr casper-jr self-assigned this Oct 3, 2025
@casper-jr casper-jr added FEAT 기능 개발 REFACTOR 기능을 건드리지 않는 리팩토링 OK Merge 완료된 PR labels Oct 3, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 3, 2025

Walkthrough

동아리 코드 확인 및 추가 기능을 위한 end-to-end 흐름을 추가했다. 새 DTO 3종을 도입하고, RemoteClubDataSource/Repository에 확인·추가 API를 구현했다. AddClubViewModel을 신설하고 DI에 등록했으며, ClubCodeInputScreen을 ViewModel 기반 상태 구동 방식으로 리팩터링했다.

Changes

Cohort / File(s) Summary
DTO 추가
composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/AddClubResponseDto.kt, .../ClubCodeConfirmResponseDto.kt, .../ErrorResponseDto.kt
신규 응답 DTO 3종 추가: AddClubResponseDto, ClubCodeConfirmResponseDto(+ClubCodeConfirmData), ErrorResponseDto. kotlinx.serialization 및 @SerialName 매핑 포함.
원격 데이터소스
composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteClubDataSource.kt
confirmClubCode(GET, query: clubNumber)와 addClub(POST, path: clubs/{clubId}) 구현. 성공 시 DTO 반환, 실패 시 ErrorResponseDto 파싱 시도 후 메시지 폴백. 예외는 ApiResult.Error 처리.
리포지토리
composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/ClubRepository.kt
confirmClubCode, addClub 위임 메서드 2개 추가.
DI 모듈
composeApp/src/commonMain/kotlin/org/whosin/client/di/DIModules.kt
AddClubViewModel를 viewModelModule에 등록.
ViewModel 추가
composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/AddClubViewModel.kt
AddClubUiState 및 AddClubViewModel 신설. confirmClubCode, addClub, resetErrorState 제공. StateFlow로 UI 상태 관리.
UI 리팩터링
composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/ClubCodeInputScreen.kt
콜백 기반에서 ViewModel 기반으로 변경. uiState 수집, 확인 성공 시 addClub 호출 및 성공 시 내비게이션 트리거. 파라미터 단순화 및 미리보기 갱신.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as ClubCodeInputScreen
  participant VM as AddClubViewModel
  participant Repo as ClubRepository
  participant DS as RemoteClubDataSource
  participant API as Server (clubs API)

  User->>UI: 동아리 코드 입력/확인
  UI->>VM: confirmClubCode(code)
  VM->>Repo: confirmClubCode(code)
  Repo->>DS: GET /clubs?clubNumber=code
  DS->>API: HTTP GET
  API-->>DS: 200 OK (ClubCodeConfirmResponseDto) / 4xx (ErrorResponseDto/텍스트)
  DS-->>Repo: ApiResult.Success / ApiResult.Error
  Repo-->>VM: 결과 전달
  VM-->>UI: uiState 업데이트(성공: clubId/clubName, 실패: errorMessage)

  rect rgba(200, 245, 200, 0.25)
    Note over UI,VM: 확인 성공 시
    UI->>VM: addClub(clubId)
    VM->>Repo: addClub(clubId)
    Repo->>DS: POST /clubs/{clubId}
    DS->>API: HTTP POST
    API-->>DS: 200 OK (AddClubResponseDto) / 4xx (ErrorResponseDto/텍스트)
    DS-->>Repo: ApiResult.Success / Error
    Repo-->>VM: 결과 전달
    VM-->>UI: uiState.isAddClubSuccess=true or errorMessage
    UI-->>User: 성공 시 홈으로 이동
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • [UI] 로그인 화면 ui 구현 #16: ClubCodeInputScreen 초기 UI 구현과 직접적으로 연관된 후속 리팩터링으로, 이번 PR에서 ViewModel 기반으로 대체됨.
  • [CHORE] Ktor, Koin 세팅 #10: Ktor/DI 기반 마련 이후, 이번 PR이 동아리 관련 실제 엔드포인트 구현과 DI 연결을 완료함.

Suggested reviewers

  • Nico1eKim
  • ikseong00

Poem

코드 한 줄에 문이 열리고,
숫자 여섯에 이름이 빛나네.
확인하고, 추가하고, 집으로 가자—
플로우는 부드럽게, 상태는 고요하게.
오늘도 팀은 CLUB에 IN! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 제목 “[FEAT] 동아리 추가 기능 구현”은 변경사항의 핵심인 동아리 추가 기능 구현을 정확히 요약하고 있으며 불필요한 용어 없이 명확합니다.
Linked Issues Check ✅ Passed PR은 이슈 #28의 동아리 확인 API 연결, 동아리 추가 API 연결, 화면 인터랙션 마무리 요구사항을 모두 구현하여 링크된 이슈의 TODO 항목을 충족합니다.
Out of Scope Changes Check ✅ Passed 변경된 모든 파일과 코드는 동아리 추가 기능 구현 범위 내에 있으며, 이외의 불필요하거나 관련 없는 수정은 포함되어 있지 않습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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 (6)
composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteClubDataSource.kt (2)

102-133: 동아리 코드 확인 로직이 잘 구현되었습니다.

에러 응답 파싱 패턴이 적절합니다. ErrorResponseDto 파싱 실패 시 기본 메시지로 폴백하는 구조가 안정적입니다.

다만, 디버깅을 위해 파싱 실패 시 예외를 로깅하는 것을 고려해보세요:

                 } catch (e: Exception) {
+                    println("ErrorResponseDto 파싱 실패: ${e.message}")
                     // 파싱 실패 시 기본 에러 메시지
                     ApiResult.Error(

135-164: 동아리 추가 로직이 잘 구현되었습니다.

confirmClubCode와 동일한 에러 핸들링 패턴을 사용하여 일관성이 유지됩니다.

동일하게, 디버깅을 위해 파싱 실패 시 예외를 로깅하는 것을 고려해보세요:

                 } catch (e: Exception) {
+                    println("ErrorResponseDto 파싱 실패: ${e.message}")
                     // 파싱 실패 시 기본 에러 메시지
                     ApiResult.Error(
composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/ClubCodeInputScreen.kt (2)

221-224: 중복된 키보드 숨김 로직을 제거하세요.

LaunchedEffect(currentState) 블록(Lines 87-90)에서 이미 SUCCESS 상태일 때 키보드를 숨기고 있습니다. 여기서의 추가 호출은 불필요합니다.

                     if (isComplete) {
                         viewModel.confirmClubCode(clubCode = fullCode)
-                        if (currentState == ClubCodeState.SUCCESS){
-                            keyboardController?.hide()
-                        }
                     }

284-289: println 디버그 코드를 제거하거나 로깅으로 개선하세요.

프로덕션 코드에 println을 남기는 것보다는 제거하거나, 필요하다면 적절한 로깅 프레임워크를 사용하는 것이 좋습니다.

                 } else {
-                    println("ClubCodeInputScreen : 확인 버튼 오류")
                 }
composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/AddClubViewModel.kt (2)

28-57: 동아리 코드 확인 로직이 잘 구현되었습니다.

API 결과에 따른 상태 업데이트가 적절하며, 에러 메시지 폴백 처리도 좋습니다. 중첩된 result.data.data 접근은 DTO 구조(ClubCodeConfirmResponseDto.data: ClubCodeConfirmData)를 고려하면 올바릅니다.

다만, Lines 43과 53의 println 디버그 코드는 제거하거나 적절한 로깅으로 교체하는 것이 좋습니다:

-                    println("AddClubViewModel : 조회 성공")
-                    println("AddClubViewModel : 조회 실패")

71-95: 동아리 추가 로직이 잘 구현되었습니다.

에러 처리와 로딩 상태 관리가 적절합니다.

다만, isAddClubSuccess가 한번 true로 설정되면 리셋되지 않습니다. 사용자가 다시 시도할 경우를 대비해 resetErrorState()나 별도 메서드에서 이를 초기화하는 것을 고려해보세요:

     fun resetErrorState() {
         _uiState.update {
             it.copy(
                 verificationState = ClubCodeState.INPUT,
                 errorMessage = null,
                 clubName = null,
-                clubId = null
+                clubId = null,
+                isAddClubSuccess = false
             )
         }
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b83617 and 16e9264.

📒 Files selected for processing (8)
  • composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/AddClubResponseDto.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/ClubCodeConfirmResponseDto.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/ErrorResponseDto.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteClubDataSource.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/ClubRepository.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/di/DIModules.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/AddClubViewModel.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/ClubCodeInputScreen.kt (9 hunks)
🧰 Additional context used
🪛 detekt (1.23.8)
composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteClubDataSource.kt

[warning] 122-122: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)


[warning] 153-153: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ 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: ios-build
🔇 Additional comments (11)
composeApp/src/commonMain/kotlin/org/whosin/client/di/DIModules.kt (1)

13-13: LGTM!

AddClubViewModel이 DI 모듈에 올바르게 등록되었습니다. 다른 ViewModel들과 동일한 패턴을 따르고 있어 일관성이 유지됩니다.

Also applies to: 53-53

composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/AddClubResponseDto.kt (1)

6-16: LGTM!

표준 API 응답 구조를 잘 따르고 있습니다. @Serializable@SerialName 어노테이션이 올바르게 사용되었고, data 필드의 nullable 처리도 적절합니다.

composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/ClubRepository.kt (1)

25-29: LGTM!

Repository 레이어가 DataSource에 적절히 위임하고 있습니다. 단일 책임 원칙을 잘 따르고 있으며, 반환 타입도 올바릅니다.

composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/ClubCodeConfirmResponseDto.kt (1)

6-24: LGTM!

중첩된 DTO 구조가 API 응답 형태에 적합하게 설계되었습니다. 모든 필드의 직렬화 어노테이션이 올바르게 적용되어 있습니다.

composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/ErrorResponseDto.kt (1)

6-16: LGTM!

구조화된 에러 응답 처리를 위한 DTO가 잘 설계되었습니다. 이를 통해 일관된 에러 핸들링이 가능해집니다.

composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/ClubCodeInputScreen.kt (3)

52-58: ViewModel 기반 상태 관리로의 리팩토링이 잘 되었습니다.

콜백 기반에서 uiState 기반으로 전환하여 단방향 데이터 흐름을 구현했습니다. collectAsStateWithLifecycle을 사용하여 생명주기를 고려한 상태 수집도 적절합니다.


79-97: 상태 변경에 따른 사이드 이펙트 처리가 적절합니다.

에러 상태 리셋 및 키보드 숨김 로직이 LaunchedEffect를 통해 올바르게 처리되고 있습니다.


264-275: 동아리 추가 실패 시 에러 메시지가 적절히 표시됩니다.

에러 상태에 따라 조건부로 메시지를 렌더링하는 로직이 명확합니다.

composeApp/src/commonMain/kotlin/org/whosin/client/presentation/auth/clubcode/AddClubViewModel.kt (3)

13-20: LGTM!

AddClubUiState가 필요한 모든 상태를 잘 정의하고 있습니다. 불변 데이터 클래스로 설계되어 안전한 상태 관리가 가능합니다.


22-27: LGTM!

StateFlow 패턴이 올바르게 구현되었습니다. private MutableStateFlow와 public StateFlow의 분리가 캡슐화를 잘 보장합니다.


59-69: LGTM!

에러 상태를 깔끔하게 리셋하고 있습니다. 모든 관련 필드를 초기화하여 일관된 상태를 유지합니다.

@casper-jr casper-jr merged commit e6fb514 into WhosInRoom:develop Oct 3, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FEAT 기능 개발 OK Merge 완료된 PR REFACTOR 기능을 건드리지 않는 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API] 동아리 추가 기능 구현

1 participant