Skip to content

[Feat] 온보딩 프로필 - 닉네임 중복체크 API연동#150

Closed
ckals413 wants to merge 6 commits into
developfrom
feat/#141-onboarding-shared-viewmodel
Closed

[Feat] 온보딩 프로필 - 닉네임 중복체크 API연동#150
ckals413 wants to merge 6 commits into
developfrom
feat/#141-onboarding-shared-viewmodel

Conversation

@ckals413

@ckals413 ckals413 commented Jan 21, 2026

Copy link
Copy Markdown
Contributor

📮 관련 이슈

📌 작업 내용

  • 온보딩 프로필 - 닉네임 중복체크

📸 스크린샷

스크린샷
Screen_recording_20260121_122119.mp4

😅 미구현

  • 닉네임 유효성검사(특수문자,이모지)
  • 토스트 (닉네임 중복검사 완료/실패/유효성)

@ckals413 ckals413 requested a review from a team as a code owner January 21, 2026 03:22
@ckals413 ckals413 linked an issue Jan 21, 2026 that may be closed by this pull request
1 task
@ckals413 ckals413 changed the title [ [Feat] 온보딩 프로필 - 닉네임 중복체크 api 연결 Jan 21, 2026
@coderabbitai

coderabbitai Bot commented Jan 21, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

닉네임 중복 검사 API/도메인 흐름과 온보딩 UI·ViewModel 상태 관리가 추가되었고, 온보딩 네비게이션이 중첩 그래프로 재구성되며 NavBackStackEntry용 sharedViewModel 확장 함수가 도입되었습니다.

Changes

Cohort / File(s) 요약
ViewModel 공유 확장
app/src/main/java/com/flint/core/common/extension/sharedViewModel.kt
NavBackStackEntry용 제네릭 @Composable 확장 sharedViewModel(navController: NavHostController) 추가. 부모 backStackEntry가 존재하면 부모 스코프의 Hilt ViewModel을 반환하고, 없으면 현재 scope에 바인딩.
네비게이션 라우트 및 Route
app/src/main/java/com/flint/core/navigation/Route.kt, app/src/main/java/com/flint/presentation/onboarding/navigation/OnboardingNavigation.kt
Route.OnboardingGraph 직렬화 가능 데이터 클래스 추가. 온보딩을 중첩 네비게이션 그래프로 재구성하고 NavHostController 사용, 각 온보딩 목적지에 sharedViewModel으로 얻은 OnboardingViewModel 전달.
API 계층
app/src/main/java/com/flint/data/api/UserApi.kt
닉네임 중복 검사 엔드포인트 GET /api/v1/users/nickname/checkcheckNickname(nickname: String) 메서드 추가.
DTO / 매퍼 / 모델 / 리포지토리
app/src/main/java/com/flint/data/dto/user/response/NicknameCheckResponseDto.kt, app/src/main/java/com/flint/domain/mapper/user/UserMapper.kt, app/src/main/java/com/flint/domain/model/user/NicknameCheckModel.kt, app/src/main/java/com/flint/domain/repository/UserRepository.kt
응답 DTO(NicknameCheckResponseDto)와 도메인 모델(NicknameCheckModel) 추가, DTO→모델 매퍼 toModel() 추가, 리포지토리에 checkNickname(nickname) 비동기 래퍼 추가(결과를 Result로 반환).
온보딩 UI / 상태 / ViewModel
app/src/main/java/com/flint/presentation/onboarding/OnboardingUiState.kt, app/src/main/java/com/flint/presentation/onboarding/OnboardingViewModel.kt, app/src/main/java/com/flint/presentation/onboarding/OnboardingProfileScreen.kt, app/src/main/java/com/flint/presentation/onboarding/OnboardingContentScreen.kt, app/src/main/java/com/flint/presentation/onboarding/OnboardingOttScreen.kt, app/src/main/java/com/flint/presentation/onboarding/OnboardingDoneScreen.kt
OnboardingProfileUiState 도입(닉네임/유효성/가용성, MAX/MIN 길이, canProceed 계산), OnboardingViewModel에 userRepository 주입 및 상태 플로우·닉네임 업데이트/중복 검사 로직 추가. 각 온보딩 Route에 viewModel: OnboardingViewModel = hiltViewModel() 파라미터 추가 및 일부 레이아웃에서 statusBarsPadding 제거. OnboardingProfileScreen은 상태를 props로 수신하도록 리팩토링 및 프리뷰 추가.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Profile as OnboardingProfileScreen
    participant VM as OnboardingViewModel
    participant Repo as UserRepository
    participant API as UserApi

    User->>Profile: 닉네임 입력
    Profile->>VM: updateNickname(nickname)
    VM->>VM: 유효성 검사 및 uiState 갱신

    User->>Profile: 확인 버튼 클릭
    Profile->>VM: checkNicknameDuplication()
    VM->>Repo: checkNickname(nickname)
    Repo->>API: GET /api/v1/users/nickname/check?nickname=...
    API-->>Repo: NicknameCheckResponseDto
    Repo-->>VM: Result<NicknameCheckModel>
    VM->>Profile: uiState(isNicknameAvailable) 변경
    Profile->>User: 가용성 표시/다음 행동 활성화
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • nahy-512

Poem

🐰 깡충깡충 닉네임을 살피네,
뷰모델은 앞뒤로 나누어 공유하고,
그래프는 층층이 길을 만들며,
API가 휙—응답을 보내오니,
토끼는 당근처럼 기뻐 춤추네 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR 설명이 대부분 완성되었으나 '작업 내용' 섹션이 너무 간략하고, '📸 스크린샷'과 '😅 미구현' 섹션의 내용이 불완전합니다. '작업 내용' 섹션에 API 연동, 닉네임 검증 로직, 네비게이션 그래프 변경 등 주요 변경사항을 상세히 기술하고, 스크린샷 URL이 유효한지 확인하며, '미구현' 섹션의 체크리스트를 명확히 작성해주시기 바랍니다.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 온보딩 프로필의 닉네임 중복체크 API 연동이라는 주요 변경사항을 명확하게 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

@ckals413 ckals413 self-assigned this Jan 21, 2026
@ckals413 ckals413 added 🔖 API feat - API 연동 Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 labels Jan 21, 2026
@ckals413 ckals413 changed the title [Feat] 온보딩 프로필 - 닉네임 중복체크 api 연결 [Feat] 온보딩 프로필 - 닉네임 중복체크 API연동 Jan 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/flint/presentation/onboarding/OnboardingProfileScreen.kt`:
- Around line 123-144: The buttons only change visual state via the
FlintButtonState but remain clickable because FlintBasicButton uses an enabled
parameter to block clicks; update the two FlintBasicButton calls (the "확인"
button that calls onCheckNickname and the "시작하기" button that calls onNextClick)
to pass enabled = isValid (or enabled = isValid && isNicknameAvailable == true
as appropriate) for the 확인 button and enabled = canProceed for the 시작하기 button
so that the visual Disabled state matches actual clickability and prevent
onCheckNickname/onNextClick from firing when inputs are invalid.

In `@app/src/main/java/com/flint/presentation/onboarding/OnboardingViewModel.kt`:
- Around line 36-47: The checkNicknameDuplication flow captures currentNickname
but may apply an out-of-date response; modify checkNicknameDuplication (and its
viewModelScope.launch block that calls userRepository.checkNickname and handles
onSuccess) to compare the nickname from the request with the latest state before
updating: store currentNickname, call checkNickname(currentNickname), and inside
onSuccess verify that _uiState.value.nickname == currentNickname before calling
_uiState.update to set isNicknameAvailable; if they differ, ignore the response.
🧹 Nitpick comments (1)
app/src/main/java/com/flint/presentation/onboarding/OnboardingDoneScreen.kt (1)

19-32: 공유 ViewModel 연결 여부 확인

OnboardingDoneRoute에 ViewModel 파라미터가 추가됐지만 현재 화면 내부에서 사용/전달이 없습니다. 이 화면에서도 공유 상태/이벤트를 쓰려는 의도라면 전달·사용을 추가하고, 아니라면 불필요한 인스턴스 생성을 줄이는 방향을 검토해 주세요.

Comment on lines +36 to +47
fun checkNicknameDuplication() {
val currentNickname = _uiState.value.nickname

viewModelScope.launch {
userRepository.checkNickname(currentNickname).onSuccess { result ->
_uiState.update { currentState ->
currentState.copy(
isNicknameAvailable = result.isAvailable,
)
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "OnboardingViewModel.kt" -type f

Repository: imflint/Flint-Android

Length of output: 141


🏁 Script executed:

git ls-files | grep -i onboarding | head -20

Repository: imflint/Flint-Android

Length of output: 1034


🏁 Script executed:

cat -n app/src/main/java/com/flint/presentation/onboarding/OnboardingViewModel.kt

Repository: imflint/Flint-Android

Length of output: 2242


🏁 Script executed:

grep -n "checkNickname\|userRepository\|onSuccess" app/src/main/java/com/flint/presentation/onboarding/OnboardingViewModel.kt

Repository: imflint/Flint-Android

Length of output: 238


🏁 Script executed:

find . -name "UserRepository.kt" -type f | head -5

Repository: imflint/Flint-Android

Length of output: 130


🏁 Script executed:

grep -r "interface UserRepository\|class.*UserRepository" --include="*.kt" | head -10

Repository: imflint/Flint-Android

Length of output: 149


🏁 Script executed:

cat -n app/src/main/java/com/flint/domain/repository/UserRepository.kt

Repository: imflint/Flint-Android

Length of output: 1092


🏁 Script executed:

grep -r "fun checkNickname" --include="*.kt" -A 10

Repository: imflint/Flint-Android

Length of output: 3248


요청 중 닉네임이 바뀌면 이전 응답의 결과가 현재 상태에 잘못 적용될 수 있음

사용자가 닉네임을 입력하고 중복검사를 요청한 후, 응답이 도착하기 전에 닉네임을 다시 변경하면 이전 요청의 응답이 새 닉네임에 대해 잘못된 isNicknameAvailable 값을 설정합니다.

응답을 적용하기 전에 요청 시점의 닉네임과 현재 상태의 닉네임이 일치하는지 확인하세요:

🔧 제안 수정안
 fun checkNicknameDuplication() {
-    val currentNickname = _uiState.value.nickname
+    val requestedNickname = _uiState.value.nickname
 
     viewModelScope.launch {
-        userRepository.checkNickname(currentNickname).onSuccess { result ->
-                _uiState.update { currentState ->
-                    currentState.copy(
-                        isNicknameAvailable = result.isAvailable,
-                    )
-                }
-            }
+        userRepository.checkNickname(requestedNickname).onSuccess { result ->
+            _uiState.update { currentState ->
+                if (currentState.nickname != requestedNickname) return@update currentState
+                currentState.copy(
+                    isNicknameAvailable = result.isAvailable,
+                )
+            }
+        }
     }
 }
🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/onboarding/OnboardingViewModel.kt`
around lines 36 - 47, The checkNicknameDuplication flow captures currentNickname
but may apply an out-of-date response; modify checkNicknameDuplication (and its
viewModelScope.launch block that calls userRepository.checkNickname and handles
onSuccess) to compare the nickname from the request with the latest state before
updating: store currentNickname, call checkNickname(currentNickname), and inside
onSuccess verify that _uiState.value.nickname == currentNickname before calling
_uiState.update to set isNicknameAvailable; if they differ, ignore the response.

@giovannijunseokim giovannijunseokim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM 👍

Comment on lines +14 to +15
@GET("/api/v1/users/nickname/check")
suspend fun checkNickname(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 현재의 함수명에 중복이라는 의미는 포함되지 않은 것 같아요. 추후 중복 외에 다른 요소도 확인하는 상황을 예상해서 구현된 부분일까요?

modifier
.fillMaxSize()
.background(color = FlintTheme.colors.background)
.statusBarsPadding(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: statusBarsPadding()을 제거해주신 이유가 무엇인가요? 👀

Comment on lines +111 to +116
maxLines = 1,
maxLength = OnboardingProfileUiState.MAX_LENGTH,
onValueChange = onNicknameChange,
trailingContent = {
Text(
text = "${nickname.length}/$maxLength",
text = "${nickname.length}/${OnboardingProfileUiState.MAX_LENGTH}",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 무엇에 대한 MAX_LENGTH일까요? 이것이 드러나는 변수명이면 더욱 좋을 것 같아요. 👍

Comment on lines +8 to +15
companion object {
const val MAX_LENGTH = 8
const val MIN_LENGTH = 2
}

//다믐단게 활성화
val canProceed: Boolean
get() = isValid && isNicknameAvailable == true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: companion object가 가장 아래에 있는 것이 Kotlin 공식 코딩 컨벤션이니 참고해주세요!

p3: canProceed가 계산된 프로퍼티(getter만 존재하는 상황)로 구현된 이유가 있을까요? 👀

Comment on lines +3 to +7
data class OnboardingProfileUiState(
val nickname: String = "",
val isValid: Boolean = false,
val isNicknameAvailable: Boolean? = null,
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 파라미터 기본값을 사용하는 이유는 무엇인가요? 문제는 없을까요?

Comment on lines +24 to +29
fun updateNickname(nickname: String) {
if (nickname.length <= OnboardingProfileUiState.MAX_LENGTH) {
_uiState.update { currentState ->
currentState.copy(
nickname = nickname,
isValid = nickname.length >= OnboardingProfileUiState.MIN_LENGTH,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: nickname의 길이를 두 번 검사하고 있어요. 한 번에 검사할 수 없을까요?

nickname.length in MIN_LENGTH .. MAX_LENGTH

@nahy-512 nahy-512 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

닉네임 유효성 검사 로직 고치신다고 하셨으니까 우선은 머지하시죠~!
작품 검색 API부터 세팅해주시고, 남은 회원가입 단계 작업해주시면 될 것 같습니다

sharedViewModel 어려웠을텐데 고생하셨습니다

Comment on lines +9 to +10
const val MAX_LENGTH = 8
const val MIN_LENGTH = 2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3
유효성 검사 시에 Range나 정규식으로 관리할 수도 있을 것 같아요

Comment on lines +5 to +6
val isValid: Boolean = false,
val isNicknameAvailable: Boolean? = null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p2
isValidisNicknameAvailable이 정확히 어떤 목적으로 두개가 존재하는 걸까요?
닉네임 형식 오류도 결과적으로 “사용할 수 없는 닉네임입니다” 토스트를 노출하는 것 같던데, 구분하신 이유가 있는지 궁금합니다.

형식 체크와 중복 확인을 위한 구분값이라면, 네이밍이 변경되어야할 것 같아요. 그게 아니더라도 두개로 관리할 필요가 있을까? 하는 생각이 드네용

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

isValid가 유효성 검사를 위한 값이라면 결국 nickname과 연관된 상태 아닌가요? canProceed처럼 백킹 프로퍼티로 구현하신 게 아니라, 프로퍼티로 하신 이유가 있으신지 궁금합니다

const val MIN_LENGTH = 2
}

//다믐단게 활성화

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

다믐단게 👀

ckals413 added a commit that referenced this pull request Jan 21, 2026
…ng-contnet-api

# Conflicts:
#	app/src/main/java/com/flint/data/api/UserApi.kt
#	app/src/main/java/com/flint/domain/repository/UserRepository.kt
@nahy-512 nahy-512 deleted the feat/#141-onboarding-shared-viewmodel branch January 21, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔖 API feat - API 연동 Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] shared view model, 온보딩 프로필 API 연동

3 participants