Skip to content

초대 코드 직접 입력 기능 구현#51

Merged
chanho0908 merged 24 commits intofeat/#35-profile-settingfrom
feat/#35-couple-connect
Feb 6, 2026
Merged

초대 코드 직접 입력 기능 구현#51
chanho0908 merged 24 commits intofeat/#35-profile-settingfrom
feat/#35-couple-connect

Conversation

@chanho0908
Copy link
Member

@chanho0908 chanho0908 commented Feb 1, 2026

이슈 번호

#35

리뷰/머지 희망 기한 (선택)

아직 리뷰 미요청 !

작업내용

  • 커플 연결 메인 화면 구현
  • 초대 코드 입력 기능 구현
  • 내 초대 코드 복사 기능 구현

결과물

2132.mp4

리뷰어에게 추가로 요구하는 사항 (선택)

@chanho0908 chanho0908 self-assigned this Feb 1, 2026
@chanho0908 chanho0908 added the Feature Extra attention is needed label Feb 1, 2026
@chanho0908
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
data/src/main/java/com/twix/data/repository/DefaultOnboardingRepository.kt (1)

14-17: ⚠️ Potential issue | 🟠 Major

InviteCode 생성 시 팩토리 메서드를 사용하여 도메인 유효성 검증을 거쳐야 합니다

InviteCode는 create(value: String): Result<InviteCode> 팩토리 메서드로만 안전하게 생성되어야 합니다. 현재 14-17줄의 직접 생성자 호출은 이 검증 로직을 완전히 우회하고 있습니다.

문제점:

  • InviteCode는 정확히 8자 길이 검증이 필수이나, 직접 생성자는 이 검증을 수행하지 않습니다
  • 서버 응답이 항상 유효하다고 가정하지만, 도메인 계약을 보호하지 않습니다
  • 에러 발생 시 도메인 레벨의 InviteCodeException으로 변환되지 않습니다

개선 방법:

override suspend fun fetchInviteCode(): InviteCode {
    val response = service.fetchInviteCode()
    return InviteCode.create(response.inviteCode)
        .getOrElse { throw it }  // 또는 도메인 에러로 매핑
}

또는 Result를 그대로 처리하는 구조를 원한다면, 서버 응답 검증 이유를 명확한 주석으로 남겨주세요.

🤖 Fix all issues with AI agents
In `@domain/src/main/java/com/twix/domain/model/invitecode/InviteCode.kt`:
- Around line 8-12: InviteCode.create 현재 require(...)가 실패할 때
IllegalArgumentException을 던져 실제 의도한 InviteCodeException.InvalidLengthException가
발생하지 않으므로, 길이 검증 실패 시 명시적으로 InviteCodeException.InvalidLengthException을 throw하도록
수정하세요; 즉 InviteCode.create의 검증 로직(InviteCode.create,
InviteCodeException.InvalidLengthException)을 require 대신 if 문 또는 직접 throw로 바꿔
runCatching이 의도한 InviteCodeException을 캡처하도록 만드세요.

In
`@feature/onboarding/src/main/java/com/twix/onboarding/couple/component/ConnectButton.kt`:
- Around line 46-51: The border is drawn before the background in
ConnectButton.kt which allows the background to visually obscure the border;
swap the order so the background modifier is applied first and the border
modifier is applied last (i.e., apply background before border on the same
composable) so the border is rendered on top; update the modifier chain that
currently uses .border(...).background(...).noRippleClickable { onClickConnect()
} to apply .background(...) then .border(...) and keep .noRippleClickable {
onClickConnect() } unchanged.

In
`@feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt`:
- Around line 30-39: CoupleConnectRoute currently injects viewModel
(koinViewModel()) but never uses it; either remove the unused viewModel
parameter from the CoupleConnectRoute signature and call site if it’s not
needed, or if onClickSend is intended to be implemented later, keep the
viewModel and replace the empty lambda onClickSend = { } with a TODO comment
(e.g., // TODO: implement send using viewModel) and then reference viewModel
inside the future implementation; update the CoupleConnectScreen invocation
accordingly and ensure no unused-import or lint warnings remain.
- Around line 71-75: 현재 두 버튼이 동일한 핸들러를 사용하여 의도와 다르게 동작합니다: InvitationButton과
ConnectButton이 둘 다 onClickConnect를 호출하고 있어 초대 전송 로직(onClickSend)이 사용되지 않습니다.
CoupleConnectRoute에서 InvitationButton의 onClick 인자를 onClickSend로 변경하여 초대 전송 핸들러를
연결하고, ConnectButton은 그대로 onClickConnect를 유지하도록 수정하세요; 관련 식별자는 InvitationButton,
ConnectButton, onClickSend, onClickConnect입니다.

In
`@feature/onboarding/src/main/java/com/twix/onboarding/invite/InviteCodeScreen.kt`:
- Around line 161-168: The click handler currently only uses
clipboardManager.setText(AnnotatedString(uiModel.myInviteCode)) and must also
dispatch the copy intent to the ViewModel so the ShowCopyInviteCodeSuccessToast
side effect runs; update the Modifier.noRippleClickable lambda (the Image
composable click) to dispatch the appropriate intent (e.g., an intent named
ShowCopyInviteCodeSuccessToast or CopyInviteCode) to the screen's ViewModel
after setting the clipboard, or instead move the clipboard copy logic into the
ViewModel and have the click handler only dispatch that CopyInviteCode intent;
ensure you reference the existing clipboardManager, uiModel.myInviteCode, and
the ViewModel intent name (ShowCopyInviteCodeSuccessToast / CopyInviteCode) when
making the change.
- Around line 69-82: Replace the current LaunchedEffect(viewModel.sideEffect)
block with a collector that collects viewModel.sideEffect Flow (e.g.,
LaunchedEffect(Unit) { viewModel.sideEffect.collect { effect -> when(effect) {
is OnBoardingSideEffect.InviteCode.ShowCopyInviteCodeSuccessToast ->
toastManager.tryShow(ToastData(message =
context.getString(R.string.onboarding_invite_code_copy), type =
ToastType.SUCCESS)) else -> Unit } } }) so the composable responds to emitted
side effects, and update the copy button handler (the Modifier.noRippleClickable
handler that currently calls clipboardManager.setText) to also dispatch the
intent to the ViewModel by calling
viewModel.dispatch(OnBoardingIntent.CopyInviteCode) before/after
clipboardManager.setText so the ViewModel can emit the
ShowCopyInviteCodeSuccessToast side effect.

In
`@feature/onboarding/src/main/java/com/twix/onboarding/navigation/OnboardingNavGraph.kt`:
- Around line 30-44: The current popUpTo usage in the onNext handlers for
InviteCodeRoute and the prior step uses popUpTo(graphRoute.route) { inclusive =
true }, which fully removes previous steps and prevents back navigation; update
both occurrences (the onNext in the composable that navigates to
NavRoutes.InviteRoute.route and the onNext that navigates to
NavRoutes.ProfileRoute.route) to target the specific earlier step you want to
keep in the back stack—for example use
popUpTo(NavRoutes.CoupleConnectionRoute.route) { inclusive = false }—so previous
screens (Couple/Invite) remain reachable, or explicitly confirm and leave as-is
if the intent is to block back navigation entirely.

In
`@feature/onboarding/src/main/java/com/twix/onboarding/vm/OnBoardingViewModel.kt`:
- Around line 15-18: The init block in OnBoardingViewModel currently has
fetchInviteCode() commented out which leaves myInviteCode empty and breaks the
CopyInviteCode intent; un-comment or re-enable fetchInviteCode() so it runs when
the onboarding screen is entered (or invoke fetchInviteCode() immediately after
successful login), and improve the error logging inside fetchInviteCode() by
replacing Log.d("FetchInviteCodeException", "$it") with a log that includes the
throwable/stacktrace (e.g., Log.e or pass the Exception to the logger or call
it.printStackTrace()) so failures are recorded with full diagnostics.
🧹 Nitpick comments (9)
app/src/main/java/com/yapp/twix/main/MainActivity.kt (1)

28-28: 주석 처리된 코드 정리 및 Safe Area 처리 확인 필요

.safeContentPadding()이 제거가 아닌 주석 처리되어 있습니다. 이는 두 가지 관점에서 검토가 필요합니다:

  1. 코드 품질 관점: 주석 처리된 코드는 제거하는 것이 좋습니다. 필요시 Git 히스토리를 통해 복구할 수 있으므로, 코드베이스에 남겨둘 필요가 없습니다.

  2. UI 관점: safeContentPadding() 없이는 노치, 라운드 코너, 제스처 네비게이션 바 등이 있는 기기에서 콘텐츠가 시스템 UI와 겹칠 수 있습니다.

개별 화면에서 Safe Area를 직접 처리하도록 변경한 것인지, 아니면 디버깅 중 임시로 비활성화한 것인지 확인 부탁드립니다. 의도적인 변경이라면 해당 라인을 완전히 삭제하는 것을 권장합니다.

♻️ 주석 처리된 코드 제거 제안
                     modifier =
                         Modifier
-                            // .safeContentPadding()
                             .fillMaxSize(),
feature/onboarding/src/main/res/drawable/ic_copy.xml (1)

8-14: 아이콘이 잘 추가되었습니다. 색상 관리에 대해 한 가지 제안드립니다.

현재 하드코딩된 색상값(#F4F4F4, #858585)을 사용하고 있습니다. 벡터 드로어블에서는 흔한 패턴이지만, 디자인 시스템의 색상 리소스를 참조하면 테마 변경 시 일관성을 유지하기 더 쉽습니다.

다크 모드 지원이나 테마 변경이 예정되어 있다면, @color/ 리소스 참조를 고려해 보시는 것은 어떨까요?

feature/onboarding/src/main/java/com/twix/onboarding/vm/OnBoardingViewModel.kt (1)

20-25: 실패 로그에 스택트레이스를 남겨 원인 파악을 쉽게 해주세요

Log.d로 문자열만 남기면 장애 분석이 어렵습니다. Log.e(tag, msg, throwable) 형태로 스택트레이스를 남기거나, 필요 시 UI에 실패를 전달하는 쪽을 고려해 주세요.

♻️ 제안 변경
-                .onFailure { Log.d("FetchInviteCodeException", "$it") }
+                .onFailure { Log.e("FetchInviteCodeException", "fetchInviteCode 실패", it) }
feature/onboarding/src/main/java/com/twix/onboarding/invite/component/InviteCodeTextField.kt (2)

62-68: 포커스된 박스만 깜빡임 애니메이션을 돌리면 리소스를 줄일 수 있어요

현재 모든 CodeBox에서 무한 애니메이션이 동작합니다. 포커스된 박스에만 애니메이션을 적용하면 불필요한 recomposition/애니메이션 비용을 줄일 수 있습니다.

♻️ 제안 변경
-    val infiniteTransition = rememberInfiniteTransition()
-    val alpha by infiniteTransition.animateFloat(
-        0f,
-        1f,
-        infiniteRepeatable(tween(500), RepeatMode.Reverse),
-    )
+    val alpha =
+        if (isFocused) {
+            val infiniteTransition = rememberInfiniteTransition()
+            val animatedAlpha by infiniteTransition.animateFloat(
+                0f,
+                1f,
+                infiniteRepeatable(tween(500), RepeatMode.Reverse),
+            )
+            animatedAlpha
+        } else {
+            0f
+        }

37-54: 초대 코드는 단일 라인/ASCII 키보드로 제한하는 게 UX에 유리합니다

다중 라인 입력은 불필요하고 IME가 문자형으로 뜰 수 있습니다. singleLinekeyboardOptions로 입력 경험을 고정해주세요.

💡 제안 변경
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
+import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
@@
     BasicTextField(
         modifier = modifier,
         value = TextFieldValue(inviteCode, selection = TextRange(inviteCode.length)),
+        singleLine = true,
+        keyboardOptions = KeyboardOptions(
+            keyboardType = KeyboardType.Ascii,
+            imeAction = ImeAction.Done,
+        ),
         onValueChange = {
             if (it.text.length <= InviteCode.INVITE_CODE_LENGTH) {
                 onValueChange(it.text)
             }
         },
domain/src/test/java/com/twix/domain/model/InviteCodeTest.kt (1)

24-30: 실패 원인 타입까지 검증하면 회귀를 더 빨리 잡을 수 있습니다

현재는 isFailure만 확인해 실패 원인이 바뀌어도 테스트가 통과합니다. InvalidLengthException 여부까지 확인하면 의도가 더 명확해집니다.

✅ 제안 변경
 import com.twix.domain.model.invitecode.InviteCode
+import com.twix.domain.model.invitecode.InviteCodeException
 import org.assertj.core.api.Assertions.assertThat
@@
         // when
         val result = InviteCode.create(invalidLengthCode)
+        val throwable = result.exceptionOrNull()
 
         // then
         assertThat(result.isFailure).isTrue()
+        assertThat(throwable)
+            .isInstanceOf(InviteCodeException.InvalidLengthException::class.java)
domain/src/main/java/com/twix/domain/model/invitecode/InviteCode.kt (1)

3-6: 생성자가 공개면 검증을 우회할 수 있는데 의도된 건가요?

현재는 InviteCode(value)로 유효성 체크 없이 생성 가능합니다. 도메인 불변식을 강제하려면 생성자를 private로 두고 create만 공개하는 방향을 검토해주세요. 의도적으로 열어둔 것인지 확인 부탁드립니다.

🔒 제안 변경
 `@JvmInline`
-value class InviteCode(
+value class InviteCode private constructor(
     val value: String,
 )
feature/onboarding/src/main/java/com/twix/onboarding/model/OnBoardingUiState.kt (1)

4-4: 네이밍 컨벤션 확인: InViteCodeUiModelInviteCodeUiModel

InViteCodeUiModel에서 중간에 대문자 V가 사용되어 있습니다. 일반적인 PascalCase 컨벤션에 따르면 InviteCodeUiModel이 더 적합합니다. 이 네이밍이 의도된 것인지 확인이 필요합니다.

Also applies to: 11-11

feature/onboarding/src/main/java/com/twix/onboarding/couple/CoupleConnectRoute.kt (1)

79-88: Preview 함수 접근 제한자 권장

Preview 함수는 외부에서 사용되지 않으므로 private으로 선언하는 것이 좋습니다. InviteCodeScreen.kt의 Preview와 일관성을 맞추는 것도 고려해주세요.

♻️ 수정 제안
 `@Preview`(showBackground = true)
 `@Composable`
-fun CoupleConnectScreenPreview() {
+private fun CoupleConnectScreenPreview() {
     TwixTheme {

@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 3, 2026
Copy link
Member

@dogmania dogmania left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!

onComplete: () -> Unit,
) {
val focusRequester = remember { FocusRequester() }
val clipboardManager = LocalClipboardManager.current
Copy link
Member

Choose a reason for hiding this comment

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

LocalClipboard 이걸 사용하라고 경고가 뜨고 있어요

Comment on lines +69 to +76
Text(
text = stringResource(R.string.onboarding_couple_connect_direct),
fontFamily = NanumSquareNeoFamily,
fontWeight = ExtraBold,
color = GrayColor.C500,
fontSize = 16.sp,
lineHeight = 22.2.sp,
)
Copy link
Member

Choose a reason for hiding this comment

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

이게 아마 제가 올려놓은 공통 바텀시트 PR에는 디자인시스템 새로 추가해놨을 거예요. 그거 머지되면 시스템에 맞게 수정해주세요!

},
decorationBox = {
Row(
horizontalArrangement = Arrangement.spacedBy(4.29.dp),
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 소수점으로 되어있는 것들은 나중에 디자이너 친구들이랑 확인해서 정수로 다 바꿔주세요!

@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 4, 2026
@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 4, 2026
@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 5, 2026
@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 6, 2026
@YAPP-Github YAPP-Github deleted a comment from coderabbitai bot Feb 6, 2026
@chanho0908 chanho0908 merged commit dc31a63 into feat/#35-profile-setting Feb 6, 2026
1 check passed
@chanho0908 chanho0908 deleted the feat/#35-couple-connect branch February 8, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants