Skip to content

[Feat] 로그인 UI 구현-대표 화면(#3)#20

Merged
dada4679 merged 13 commits into
developfrom
feature/3-login-ui
Jul 19, 2026
Merged

[Feat] 로그인 UI 구현-대표 화면(#3)#20
dada4679 merged 13 commits into
developfrom
feature/3-login-ui

Conversation

@dada4679

@dada4679 dada4679 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

📄 작업 내용 요약

-랜딩 화면 구현
-로그인 화면 구현
-약관 동의 화면 구현
-약관 보기 화면 구현


📎 Issue 번호


✅ 작업 목록

  • 기능 구현
  • 코드 리뷰 반영
  • 테스트 코드 작성
  • 문서 업데이트

📝 기타 참고사항

Summary by CodeRabbit

  • 새로운 기능
    • 로그인 랜딩 화면과 카카오 로그인 UI를 추가했습니다.
    • 약관 동의 화면에 필수/선택 항목 체크, 전체 동의, 약관 상세 보기, 다음 버튼을 제공합니다.
    • 약관 상세 화면에서 글머리표/번호/일반 텍스트 형식이 보기 좋게 정리되어 표시됩니다.
    • 체크박스, 뒤로가기 상단 바, 다음 버튼 등의 공용 UI 컴포넌트를 추가했습니다.
    • 디자인 시스템에 새로운 색상·그라디언트·타이포 스타일을 확장했습니다.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: abfd3a70-cda8-4d89-acae-721b7b73188f

📥 Commits

Reviewing files that changed from the base of the PR and between 2c40058 and afadf56.

📒 Files selected for processing (6)
  • core/designsystem/src/main/java/kr/co/call/designsystem/theme/Color.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/entry/LoginEntryBuilder.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt

📝 Walkthrough

Walkthrough

랜딩·로그인 화면을 실제 Compose UI로 구성하고 카카오 로그인 콜백을 연결했습니다. 약관 동의 상태, 체크박스, 약관 목록, 다음 버튼, 상세 약관 화면과 관련 디자인 토큰을 추가했습니다.

Changes

로그인 및 약관 UI

Layer / File(s) Summary
랜딩 및 로그인 화면
core/designsystem/..., feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt, feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt, feature/login/impl/src/main/java/kr/co/call/impl/entry/LoginEntryBuilder.kt
랜딩 화면과 카카오 로그인 UI를 추가하고 로그인 클릭 콜백 및 관련 디자인 토큰을 구성했습니다.
약관 동의 컨트롤 및 상태 관리
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt, feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/*, feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementItem.kt, feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt, feature/login/impl/src/main/java/kr/co/call/impl/component/NextButton.kt, feature/login/impl/src/main/res/drawable/ic_check.xml
약관 유형과 선택 상태를 정의하고, 개별·전체 동의 토글과 필수 약관 기준의 다음 버튼 활성화를 구현했습니다.
약관 상세 표시
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt, feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementSection.kt, feature/login/impl/src/main/java/kr/co/call/impl/component/BackTopBar.kt
뒤로가기 상단 바와 스크롤 가능한 약관 상세 화면을 추가하고, 일반 문장·글머리표·번호 목록을 구분해 렌더링합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgreementScreen
  participant AgreementItem
  participant CheckBox
  participant AgreementViewModel
  AgreementScreen->>AgreementItem: 약관 상태와 토글 콜백 전달
  AgreementItem->>CheckBox: checked 상태와 변경 콜백 전달
  CheckBox->>AgreementScreen: 체크 변경 이벤트 전달
  AgreementScreen->>AgreementViewModel: 약관 토글 요청
  AgreementViewModel->>AgreementScreen: 갱신된 AgreementUiState 제공
Loading

Possibly related PRs

Suggested reviewers: codebidoof

🚥 Pre-merge checks | ✅ 3 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning 제목이 Conventional Commits 형식이 아니고, 요구된 feat: 접두사도 없습니다. 제목을 feat: 로그인 UI 구현 (#3)처럼 type: description 형식으로 수정하세요.
Out of Scope Changes check ⚠️ Warning 랜딩/약관/디자인 시스템 변경이 #3의 로그인 화면 UI 범위를 넘어 포함되어 있습니다. 로그인 UI와 무관한 변경은 별도 PR로 분리하거나 해당 이슈를 추가로 연결하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Ui 변경 시 스크린샷 첨부 확인 ⚠️ Warning Compose 화면 파일이 변경됐지만 PR 설명에 스크린샷/화면 녹화 첨부 언급이 없습니다. PR 설명에 변경 화면의 스크린샷 또는 화면 녹화 링크/첨부를 추가하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed 로그인 화면 UI가 구현되어 직접 링크된 #3의 핵심 요구사항을 충족합니다.
모듈 의존성 방향 검증 ✅ Passed 변경된 feature/login/impl은 타 feature impl/core.data를 import하지 않았고, core/domain/src/main에 android/androidx import도 없었습니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/3-login-ui

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

@dada4679 dada4679 changed the title Feature/3 login UI [Feat] 로그인 UI 구현-대표 화면(#3) Jul 17, 2026
@github-actions

Copy link
Copy Markdown

Gemini AI 코드리뷰

안녕하세요, 시니어 Android 개발자입니다. 이번 PR의 코드 변경 사항들을 꼼꼼하게 검토했습니다. 전반적으로 Jetpack Compose 컴포넌트들이 잘 분리되어 있고, Preview 기능도 잘 활용하여 개발하신 점이 인상 깊습니다. 몇 가지 개선할 점과 질문 사항이 있어 피드백을 드립니다.


1. Kotlin & Jetpack Compose (공통)

  • 하드코딩된 문자열/매직 넘버:

    • 많은 Text 컴포넌트의 text 파라미터에 직접 문자열 리터럴이 사용되었습니다. (예: AgreementItem, BackTopBar, AgreementScreen, LandingScreen, LoginScreen)
    • 이는 다국어 지원 및 문자열 관리를 어렵게 합니다. 모든 사용자에게 노출되는 문자열은 strings.xml 리소스 파일로 이동하여 관리하는 것을 권장합니다.
    • 마찬가지로, Modifier.width(7.dp), Spacer(modifier = Modifier.height(12.dp)) 등 다수의 dp 값들이 하드코딩되어 있습니다. 디자인 시스템에 정의된 spacing 토큰을 사용하거나, 공통적으로 사용되는 값들은 상수로 분리하여 관리하는 것이 좋습니다.
  • null 안전성 (!! 사용 지양):

    • !! 연산자는 발견되지 않았습니다. 이는 좋은 코드 습관입니다.
  • Design System Theme 활용:

    • CallTheme.typography를 사용하고 있지만, 일부 Text 컴포넌트에서는 fontSizefontWeight를 직접 지정하고 있습니다. (예: AgreementDetailScreen, LandingScreen, LoginScreen)
    • 디자인 시스템의 typography에 미리 정의된 스타일을 최대한 활용하여 일관성을 유지하고 불필요한 중복 정의를 피하는 것이 좋습니다. 만약 현재 typography에 필요한 스타일이 없다면, 디자인 시스템에 추가하는 것을 고려해 보세요.
    • LoginScreenKakaoLoginButton에서 containerColor = Color(0xFFFDE500)와 같이 특정 색상이 하드코딩되어 있습니다. 이 또한 디자인 시스템의 Color 팔레트에 추가하여 관리하는 것이 좋습니다.
  • "No newline at end of file":

    • 여러 파일(AgreementItem.kt, AgreementSection.kt, CheckBox.kt, AgreementDetailScreen.kt, AgreementScreen.kt, LandingScreen.kt, LoginScreen.kt, ProgressBar.kt)에서 파일의 마지막에 개행 문자가 없습니다. 이는 일부 IDE나 Git 설정에서 경고를 유발할 수 있으므로, 모든 파일의 끝에 개행 문자를 추가하는 것이 좋습니다.

2. Jetpack Compose (상세)

feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementItem.kt

  • Recomposition: AgreementItem은 전달받은 파라미터에 따라 UI를 렌더링하므로 불필요한 Recomposition 유발 요인은 없어 보입니다. onCheckedChangeonViewClick 람다를 remember로 감쌀 필요는 없습니다. (Compose compiler plugin이 알아서 처리)
  • Custom CheckBox: android.widget.CheckBox를 import 해놓고 실제로는 custom CheckBox를 사용하고 있습니다. android.widget.CheckBox는 삭제해주세요. 현재 사용하고 있는 custom CheckBox에 대한 디자인적 요구사항이 명확하다면 괜찮지만, Jetpack Compose Material 3의 Checkbox 컴포넌트를 사용하는 것을 먼저 고려해볼 수 있습니다. 만약 디자인이 다르다면, 이처럼 커스텀 구현하는 것이 맞습니다.

feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementSection.kt

  • Content Parsing: 약관 내용을 라인별로 파싱하여 텍스트 스타일을 적용하는 방식이 흥미롭습니다. Regex를 사용하여 글머리 기호와 번호 목록을 처리하는 로직은 유연성을 제공합니다.
    • matchResult?.groupValues?.get(1).orEmpty()와 같이 orEmpty()를 사용하여 null 안전성을 확보한 점이 좋습니다.
  • Recomposition: content 문자열이 변경될 때만 전체 Column이 recomposition될 것이므로, 이 컴포넌트의 사용 목적(정적인 약관 내용 표시)을 고려했을 때 성능 문제는 없을 것으로 예상됩니다.
  • Hardcoded Dp: startPadding = 10.dp, markerWidth = 15.dp 등 직접 dp 값을 사용하는 대신, 디자인 시스템에 spacing 토큰으로 정의하는 것을 고려해 보세요.

feature/login/impl/src/main/java/kr/co/call/impl/component/BackTopBar.kt

  • Icon Size: modifier = Modifier.size(width = 8.dp, height = 17.dp)로 아이콘 크기를 직접 지정했습니다. 아이콘 리소스 자체가 이 크기라면 괜찮지만, 일반적으로 IconButton 내의 IconModifier.wrapContentSize() 또는 Modifier.size(24.dp)와 같이 표준 아이콘 크기를 따르는 경우가 많습니다. 디자인 가이드에 맞는지 확인해 주세요.

feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt

  • Clickable Area: Modifier.clip(RoundedCornerShape(3.dp)).background(...).clickable { ... } 순서가 좋습니다. 클릭 가능한 영역이 시각적인 배경과 잘 일치합니다.
  • Content Description: contentDescription="check"는 좋은 시작이지만, 스크린 리더 사용자를 위해 체크박스의 현재 상태(체크됨/체크되지 않음)를 포함하는 것이 더 좋습니다. 예: contentDescription = if (checked) "체크됨" else "체크되지 않음".

feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt

  • State Hoisting: title, content, onBackClick이 잘 호이스팅되어 있습니다.
  • Typography: Text(text=title, fontSize=15.sp, fontFamily=Pretendard, fontWeight=FontWeight.Medium,) 부분은 디자인 시스템의 typography에 해당하는 스타일이 있다면 사용하는 것이 좋습니다.

feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt

  • UI 상태를 data class로 표현: enum class AgreementType을 사용하여 약관 유형을 명확하게 정의한 점이 좋습니다. checkedAgreementsSet<AgreementType>으로 관리하는 것도 효율적입니다.
  • Derived State: isAllCheckedisRequiredCheckedagreementTypescheckedAgreements로부터 파생된 상태로 선언한 것이 적절합니다.
  • 버튼 활성화/비활성화 처리: 현재 isRequiredChecked 여부에 따라 SecondaryButtonBox를 조건부로 렌더링하고 있습니다.
    if (isRequiredChecked){
        SecondaryButton(...)
    }else{
        Box(...) // Disabled visual state
    }
    이 방식은 SecondaryButtonBox 간에 UI 로직(색상, 텍스트 스타일 등)이 중복될 가능성이 높습니다. SecondaryButton 컴포넌트 자체에 enabled: Boolean = true 파라미터를 추가하고, 이 enabled 값에 따라 내부적으로 배경색, 텍스트 색상, 클릭 가능 여부를 변경하도록 구현하는 것이 더 깔끔합니다. 이렇게 하면 UI 로직의 중복을 피하고 재사용성을 높일 수 있습니다.
    (예시)
    SecondaryButton(
        text = "동의하고 프로필 설정하기",
        onClick = onNextClick,
        modifier = Modifier.fillMaxWidth(),
        enabled = isRequiredChecked, // enabled 파라미터 추가
        containerColor = if (isRequiredChecked) MainVariant2 else Gray200, // 색상 변경 로직
        contentColor = if (isRequiredChecked) Black else Gray500,
    )

feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt

  • Typography: Text(text="전화왔어", style=TextStyle(brush = ..., fontSize = 34.sp, fontWeight= FontWeight.Bold,)) 및 하단 텍스트 역시 디자인 시스템의 typography를 활용하는 것이 좋습니다. TextStyle에 직접 Brush를 적용하는 것은 특별한 경우이므로 예외적으로 허용될 수 있지만, fontSize, fontWeight, lineHeight 등은 typography에서 관리하는 것이 일반적입니다.

feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt

  • buildAnnotatedString: "나만의 연애 시뮬레이션" 부분에 FontWeight.Bold를 적용하기 위해 buildAnnotatedString를 사용한 것은 적절합니다.
  • KakaoLoginButton Composable 분리: 로그인 버튼을 별도 Composable로 분리한 것은 재사용성과 가독성 측면에서 좋은 접근입니다.
  • Hardcoded Color: containerColor = Color(0xFFFDE500)는 디자인 시스템 Color 팔레트에 추가하여 CallTheme.colors.KakaoYellow와 같이 사용하는 것을 권장합니다.

3. Repository/DataSource & ViewModel & FCM/SSE (해당 사항 없음)

  • 이번 PR은 UI 컴포넌트 및 화면 개발에 집중되어 있어, Repository/DataSource, ViewModel, FCM/SSE 관련 규칙을 적용할 만한 코드는 포함되어 있지 않습니다. 이 부분에 대한 리뷰는 다음 PR에서 기회가 된다면 진행하겠습니다.

4. feature/onboarding/impl/src/main/java/kr/co/call/impl/component/ProgressBar.kt

  • 현재 이 파일은 빈 클래스로 존재합니다. 만약 의도적으로 빈 상태로 추가한 것이 아니라면, 불필요한 파일이므로 삭제하는 것을 권장합니다. 이후 구현이 필요할 때 다시 추가하거나 이 PR에서 바로 구현을 완료하는 것이 좋습니다.

결론

전반적으로 Compose 코드의 구조와 State Hoisting 원칙은 잘 지켜져 있습니다. Preview 기능 활용도 매우 좋습니다. 핵심 피드백은 디자인 시스템의 strings.xmlcolors.xml, typography를 좀 더 적극적으로 활용하여 하드코딩된 값들을 줄이는 것AgreementScreen의 버튼 활성화/비활성화 로직을 개선하는 것입니다.

위 피드백들을 검토하시고 반영해 주시면 더 견고하고 유지보수하기 쉬운 코드가 될 것입니다. 수고하셨습니다!

@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: 6

🧹 Nitpick comments (2)
feature/onboarding/impl/src/main/java/kr/co/call/impl/component/ProgressBar.kt (1)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

빈 클래스 블록을 제거하거나 실제 구현을 추가해 주세요.

현재 ProgressBar는 아무 동작도 제공하지 않아 detekt의 EmptyClassBlock 경고를 발생시킵니다. 자리표시자라면 중괄호를 제거하고, 실제 온보딩 UI 컴포넌트라면 필요한 상태와 렌더링 API를 구현한 뒤 추가하는 편이 좋습니다.

권장 수정
-class ProgressBar {
-
-}
+class ProgressBar
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@feature/onboarding/impl/src/main/java/kr/co/call/impl/component/ProgressBar.kt`
around lines 3 - 5, ProgressBar의 빈 클래스 블록을 제거하세요. 아직 구현이 필요하지 않은 자리표시자라면 중괄호 없는
클래스 선언으로 변경하고, 실제 UI 동작이 요구되는 경우에만 상태와 렌더링 API를 추가하세요.

Source: Linters/SAST tools

feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt (1)

38-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

화면 Composable의 Modifier API를 경로 규칙에 맞춰 통일해 주세요.

  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt#L38-L41: modifier를 콜백보다 앞에 배치하고 기본값을 제거해 주세요.
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt#L31-L33: modifier 기본값을 제거해 주세요.
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt#L73-L78: modifier를 콜백보다 앞에 배치하고 기본값을 제거해 주세요.
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt#L23-L29: modifier를 콜백보다 앞에 배치하고 기본값을 제거해 주세요.

Modifier 파라미터 위치(파라미터 앞쪽) 및 기본값 없이 전달 여부 경로 지침에 따른 의견입니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt`
around lines 38 - 41, 화면 Composable들의 Modifier API를 경로 규칙에 맞게 통일하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt 38-41에서는
LoginScreen의 modifier를 콜백보다 앞에 배치하고 기본값을 제거하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt
31-33에서는 LandingScreen의 modifier 기본값을 제거하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt
73-78와
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt
23-29에서는 각각 modifier를 콜백보다 앞에 배치하고 기본값을 제거하세요.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt`:
- Around line 27-46: Update the checkbox composable’s Box interaction to expose
Role.Checkbox and the checked state to accessibility services, while preserving
the existing visual appearance and onCheckedChange behavior. Ensure the
interactive area meets the minimum 48dp touch target, using Material Checkbox or
an equivalent toggleable implementation with the required semantics.

In
`@feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt`:
- Around line 30-36: Apply the status-bar inset to the top-level Column or
BackTopBar in AgreementDetailScreen so the back button is offset below the
status bar in edge-to-edge mode. Use the existing Compose inset handling
conventions, ensuring the inset is applied only once and the remaining layout
behavior is preserved.

In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt`:
- Around line 74-94: AgreementScreen의 remember 기반 checkedAgreements 상태와
isAllChecked, isRequiredChecked 판정 로직을 ViewModel 또는 Orbit Container로 이동하세요. 화면은
선택된 약관 상태와 전체/필수 동의 결과를 인자로 받고, onAgreementViewClick 및 onNextClick 같은 이벤트 콜백만
호출하도록 AgreementScreen을 stateless에 가깝게 수정하세요.
- Around line 95-104: AgreementScreen의 고정 높이 레이아웃을 수정해 작은 화면·가로 모드·큰 글꼴에서도 전체
콘텐츠에 접근할 수 있게 하세요. 상단 Spacer와 약관 목록 등 고정 간격이 있는 본문을 스크롤 가능한 영역으로 분리하고, 다음 버튼
CTA는 화면 하단에 고정된 별도 영역으로 배치하세요. 관련 Spacer 및 CTA 배치(122-130, 202-230)의 기존 동작은 유지하되
콘텐츠가 CTA 아래로 밀리지 않도록 조정하세요.
- Around line 168-172: Update the text in the AgreementScreen composable to
correct the Korean spacing in the all-agreements consent message, changing
“이용을위한” to the properly spaced form while preserving the rest of the wording and
styling.

In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt`:
- Around line 38-40: Remove the empty default callback from LoginScreen’s
onKakaoLoginClick parameter so callers must provide login behavior, then update
LoginEntryBuilder’s LoginScreen invocation to pass the actual Kakao login
action.

---

Nitpick comments:
In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt`:
- Around line 38-41: 화면 Composable들의 Modifier API를 경로 규칙에 맞게 통일하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt 38-41에서는
LoginScreen의 modifier를 콜백보다 앞에 배치하고 기본값을 제거하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt
31-33에서는 LandingScreen의 modifier 기본값을 제거하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt
73-78와
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt
23-29에서는 각각 modifier를 콜백보다 앞에 배치하고 기본값을 제거하세요.

In
`@feature/onboarding/impl/src/main/java/kr/co/call/impl/component/ProgressBar.kt`:
- Around line 3-5: ProgressBar의 빈 클래스 블록을 제거하세요. 아직 구현이 필요하지 않은 자리표시자라면 중괄호 없는
클래스 선언으로 변경하고, 실제 UI 동작이 요구되는 경우에만 상태와 렌더링 API를 추가하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8025c11c-ad8a-4a88-ba7d-7454e8ed8fad

📥 Commits

Reviewing files that changed from the base of the PR and between 148b3eb and b6267fe.

⛔ Files ignored due to path filters (4)
  • feature/login/impl/src/main/res/drawable/ic_arrow_back.png is excluded by !**/*.png
  • feature/login/impl/src/main/res/drawable/kakao.png is excluded by !**/*.png
  • feature/login/impl/src/main/res/drawable/landing.png is excluded by !**/*.png
  • feature/login/impl/src/main/res/drawable/logo.png is excluded by !**/*.png
📒 Files selected for processing (10)
  • feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementItem.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementSection.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/BackTopBar.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt
  • feature/login/impl/src/main/res/drawable/ic_check.xml
  • feature/onboarding/impl/src/main/java/kr/co/call/impl/component/ProgressBar.kt

Comment thread feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt Outdated
Comment thread feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt Outdated
Comment thread feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt Outdated
Comment on lines +38 to +40
fun LoginScreen(
modifier: Modifier = Modifier,
onKakaoLoginClick:()->Unit={},
modifier: Modifier=Modifier,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

빈 기본 콜백 때문에 실제 로그인 버튼이 동작하지 않습니다.

LoginEntryBuilder.kt에서 LoginScreen()을 인자 없이 호출하므로 Line 39의 기본값이 사용되고, 카카오 버튼 클릭이 그대로 소실됩니다. 기본값을 제거하고 엔트리에서 실제 로그인 동작을 연결해 주세요.

수정 예시
 fun LoginScreen(
-    onKakaoLoginClick: () -> Unit = {},
+    onKakaoLoginClick: () -> Unit,
     modifier: Modifier = Modifier,
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt`
around lines 38 - 40, Remove the empty default callback from LoginScreen’s
onKakaoLoginClick parameter so callers must provide login behavior, then update
LoginEntryBuilder’s LoginScreen invocation to pass the actual Kakao login
action.

onAgreementViewClick:(AgreementType)->Unit,
onNextClick:()->Unit,
modifier: Modifier=Modifier,
) {

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.

약관동의 API 호출하는 부분이랑 연관되어있어서 컴포저블에 있는것보다는 ViewModel로 빼는게 더 좋을 것 같아요

예시 약간 이런식..?!

   private fun toggleAgreement(type: AgreementType) = intent {
        reduce {
            state.copy(
                checkedAgreements = if (type in state.checkedAgreements) {
                    state.checkedAgreements - type
                } else {
                    state.checkedAgreements + type
                },
            )
        }
    }

    private fun toggleAll(checked: Boolean) = intent {
        reduce {
            state.copy(
                checkedAgreements = if (checked) AgreementType.entries.toSet() else emptySet(),
            )
        }
    }

Comment on lines +66 to +80
text = buildAnnotatedString {
append("AI가 먼저 전화하고 관계를 이어가는,\n")
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold,
),
) {
append("나만의 연애 시뮬레이션")
}
append("\uD83D\uDC9E")
},
fontSize = 22.sp,
color = Gray900,
fontFamily = Pretendard,
fontWeight = FontWeight.Normal,

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.

요거 디자인 시스템에 정의한 타이포그래피 스타일로 바꾸는 게 좋을 것 같아요! 이미 다 정의해놨습니당 😊

Text(
    text = buildAnnotatedString {
        withStyle(
            style = typography.titleSmall.toSpanStyle(),
        ) {
            append("AI가 먼저 전화하고 관계를 이어가는,\n")
        }

        withStyle(
            style = typography.titleSmallBold.toSpanStyle(),
        ) {
            append("나만의 연애 시뮬레이션")
        }

        append("💞")
    },
    textAlign = TextAlign.Center,
)

Comment on lines +205 to +211
SecondaryButton(
text="동의하고 프로필 설정하기",
onClick=onNextClick,
modifier=Modifier.fillMaxWidth(),
containerColor=MainVariant2,
contentColor=Black,
)

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.

이거는 버튼 컴포넌트를 따로 만드는 걸 추천드립니다!

Comment on lines +212 to +229
}else{
Box(
modifier= Modifier
.fillMaxWidth()
.height(60.dp)
.background(
color= MainVariant2,
shape=RoundedCornerShape(percent=10),
),
contentAlignment=Alignment.Center,
){
Text(
text="동의하고 프로필 설정하기",
color=Black,
style=typography.bodyLargeBold,
)
}
}

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.

버튼 컴포넌트를 따로 만든담에 isEnabled 같은 파라미터를 만들어서 거기에 isRequiredChecked를 주입시킨 다음에 컴포넌트 함수 본문에서 분기처리를 하는 게 좋을 것 같아.

@Composable
fun AgreementButton(
    isEnabled: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    if (isEnabled) {

    } else {

    }
}

설계를 이런 식으로?

painter=painterResource(id=R.drawable.logo),
contentDescription = "로고",
modifier=Modifier.size(97.dp),
contentScale=ContentScale.Fit

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.

여기 Fit으로 준 이유가 있나용

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

사진 안 잘리게 하려고 넣었어요
혹시 이렇게 하면 안 되나요...?

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.

엑 아녀아녀 궁금해성 저원래 궁금하면 코멘트루 질문해어

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

넹 그러면 일단 이대로 둘게요
혹시 더 좋은 방법이 있다면 알려주세요!

Comment on lines +63 to +74
Text(
text="전화왔어",
style= TextStyle(
brush = Brush.verticalGradient(
colors = listOf(
MainVariant1,
Main
)
),
fontSize = 34.sp,
fontWeight= FontWeight.Bold,
)

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.

브러시도 디자인 시스템에 정의해 둬서, 아래처럼 style.copy()로 적용하면 더 짧고 깔끔하게 작성할 수 있을 것 같아요!

Text(
    text = "전화왔어",
    style = typography.titleLargeBold.copy(
        brush = CallTheme.colors.chatGradient,
    ),
)

@github-actions

Copy link
Copy Markdown

Gemini AI 코드리뷰

안녕하세요! 새로운 로그인 플로우 및 약관 동의 화면 구현하시느라 고생 많으셨습니다. 전반적으로 Jetpack Compose의 모범 사례들을 잘 적용하셨고, ViewModel에서 UI 상태를 관리하는 방식도 깔끔하게 구현해주셨습니다. 몇 가지 개선점과 제안을 드릴게요.


🔍 PR 코드 리뷰

1. Design System 모듈 (core/designsystem)

  • Color.kt - ChatGradient 정의:
    • Brush.linearGradient에서 end=Offset.Infinite를 사용하셨는데, 이는 일반적으로 의도하지 않은 렌더링을 유발하거나 성능에 영향을 줄 수 있습니다. Offset.Infinite는 무한한 크기를 의미하며, 특정 Composable의 크기에 맞춰 그라디언트를 적용하려면 해당 Composable의 크기를 고려한 Offset 값(예: Offset(size.width, size.height))을 사용하거나, 단순히 Brush.horizontalGradient 또는 Brush.verticalGradient를 사용하는 것이 더 적절합니다. 만약 가로 전체에 걸쳐 균일한 그라디언트를 원하신다면 Brush.horizontalGradient가 더 직관적이고 정확합니다.
    • 개선 제안: Brush.horizontalGradient를 다시 사용하거나, 만약 특정 시작점/끝점의 제어가 필요하다면 Composable 내부에서 size를 측정하여 동적으로 Offset 값을 계산하여 전달하는 것이 좋습니다.
      // 만약 가로 전체에 걸친 그라디언트가 목적이라면
      val ChatGradient = Brush.horizontalGradient(
          listOf(
              Color(0xFFFF789F),
              Color(0xFFFFA3BD)
          )
      )
  • Type.kt - 새로운 Typography 추가:
    • titleExtraLargeBold, titleExtraLarge, titleExtraSmall 등 새로운 텍스트 스타일을 추가하신 점 좋습니다. 일관된 타이포그래피 시스템을 확장하는 좋은 방법입니다.

2. Jetpack Compose 코드

  • State Hoisting 원칙 준수:
    • AgreementItem, CheckBox, NextButton, AgreementScreen, AgreementDetailScreen, LoginScreen 등 대부분의 Composable 함수에서 상태(State)와 이벤트(Event)를 분리하여 상위 Composable로 끌어올리는 State Hoisting 원칙을 잘 준수하셨습니다. onCheckedChange, onClick 등의 콜백을 통해 이벤트 처리를 위임하고, uiState와 같은 형태로 상태를 전달하는 방식이 좋습니다.
  • Recomposition 최적화 (remember, key 등):
    • NextButton에서 remember { MutableInteractionSource() }를 사용하여 interactionSource를 기억하고, collectIsPressedAsState()를 통해 isPressed 상태를 추적하는 방식은 불필요한 리컴포지션을 줄이는 좋은 패턴입니다.
    • AgreementDetailScreenAgreementScreen에서 rememberScrollState()를 사용하여 스크롤 상태를 기억하는 방식도 적절합니다.
  • UI 상태를 data class로 표현:
    • AgreementUiStatedata class로 정의하여 UI 상태를 명확하게 표현하신 점 아주 좋습니다. 특히 isAllChecked, isRequiredChecked와 같이 파생된 상태(derived state)를 get() 프로퍼티로 깔끔하게 관리하는 방식이 인상 깊습니다.
  • Preview Composable 활용:
    • 대부분의 Composable에 @Preview를 사용하여 다양한 상태와 레이아웃을 미리 볼 수 있도록 작성하신 점은 개발 효율성과 UI 검증에 매우 도움이 됩니다.
  • Magic Number 및 Hardcoded String:
    • Magic Number: height(58.dp), padding(horizontal=18.dp), size(70.dp), RoundedCornerShape(30.dp), width = 1.dp 등 많은 곳에서 하드코딩된 크기, 패딩, 도형 반경 값을 사용하고 있습니다. 이러한 값들은 추후 디자인 변경 시 많은 수정이 필요하고 일관성을 유지하기 어렵게 만듭니다.
      • 개선 제안: designsystem 모듈에 CallDimensCallShapes와 같은 객체를 만들어 디자인 토큰으로 관리하는 것을 강력히 권장합니다. 예를 들어, CallTheme.dimens.spacingMedium, CallTheme.shapes.cornerExtraLarge 등으로 사용하면 유지보수성이 크게 향상됩니다.
    • Hardcoded String: "필수", "선택", "보기", "뒤로가기", "전화왔어", "AI가 먼저 전화하고...", 카카오 로그인 버튼 텍스트, 이모지(\uD83D\uDC9E) 등 UI에 표시되는 문자열이 직접 코드에 삽입되어 있습니다. 이는 다국어 지원에 어려움을 주며, 텍스트 변경 시 코드 수정이 필요해집니다.
      • 개선 제안: 모든 UI 텍스트는 strings.xml 리소스 파일에 정의하고 stringResource(R.string.my_string) 형태로 사용하는 것이 표준적인 방법입니다.
  • AgreementType enum class:
    • 약관의 종류를 enum class로 정의하고 title, isRequired 속성을 부여하여 관리하는 방식은 가독성과 유지보수성 측면에서 매우 훌륭합니다.
  • AgreementSection의 텍스트 파싱:
    • 정규식을 사용하여 글머리표()나 번호 목록(1., 2.)을 파싱하여 들여쓰기를 적용하는 아이디어가 좋습니다. 현재는 간단한 패턴만 처리하지만, 추후 약관 내용이 복잡해질 경우 이 파싱 로직을 더 견고하게 만들 필요가 있을 수 있습니다. (예: 중첩 목록, 더 다양한 글머리표)
  • LoginScreenKakaoLoginButton contentDescription:
    • 카카오 로고 ImagecontentDescription"카카오 로고"로 잘 명시되어 있습니다. 시각 장애가 있는 사용자에게 유용한 정보를 제공합니다.

3. ViewModel 레이어 (feature/login/impl/viewmodel)

  • AgreementViewModel:
    • Hilt DI: @HiltViewModel @Inject constructor()를 사용하여 의존성 주입을 올바르게 설정하셨습니다.
    • UI 상태 관리: MutableStateFlowasStateFlow()를 사용하여 UI 상태를 외부에 노출하고, update 함수를 통해 상태를 안전하고 불변적으로 업데이트하는 방식은 모범 사례에 해당합니다.
    • 비즈니스 로직 분리: toggleAgreement, toggleAllAgreements 함수가 UI 로직이 아닌, 약관 동의 상태를 변경하는 비즈니스 로직을 담당하고 있어 ViewModel의 역할에 적합합니다.
    • Orbit ContainerHost 패턴 준수 여부: 이 ViewModel은 비교적 간단한 상태 관리만 필요하므로, Orbit ContainerHost 패턴을 사용하지 않은 것이 오남용으로 보이지 않습니다. 현재 요구사항에 맞춰 StateFlow만으로 충분히 효율적으로 구현되었습니다.
    • Blocking 작업/SideEffect 남용: 현재 ViewModel에는 네트워크 호출이나 데이터베이스 접근과 같은 blocking 작업이 없으며, postSideEffect를 사용하는 부분도 없습니다. 이는 현재 로직에 적합합니다.

전반적인 총평 및 제안

  • 긍정적인 부분: Jetpack Compose의 핵심 원칙(State Hoisting, UI 상태 data class화, remember 활용)을 잘 이해하고 적용하셨습니다. ViewModel도 깔끔하게 상태를 관리하고 비즈니스 로직을 분리하여 구현되었습니다. Preview 함수를 적극적으로 활용하여 UI 개발 편의성을 높인 점도 칭찬할 만합니다.
  • 개선이 필요한 부분: 가장 시급하게 개선해야 할 부분은 Magic Number와 Hardcoded String의 제거입니다. Design System 모듈을 활용하여 UI 요소의 일관성을 확보하고 유지보수성을 높이는 것이 중요합니다. designsystem 모듈에 Dimens, Shapes, Strings 등을 추가하여 재활용 가능한 디자인 토큰으로 만들면 좋을 것 같습니다.
  • ChatGradientOffset.Infinite 사용에 대한 명확한 의도 확인 및 수정이 필요합니다.

이 부분들을 개선하신다면, 훨씬 더 견고하고 유지보수하기 쉬운 코드가 될 것입니다. 수고 많으셨습니다!

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt (1)

45-77: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

작은 화면에서도 로그인 버튼에 접근할 수 있게 해 주세요.

고정된 223dp 상단 여백과 여러 고정 간격을 비스크롤 Column에 누적해, 가로 모드나 큰 글꼴에서 카카오 로그인 버튼이 화면 밖으로 밀릴 수 있습니다. 가변 여백 또는 스크롤 가능한 레이아웃으로 조정해 주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt`
around lines 45 - 77, Update the LoginScreen layout around the non-scrollable
Column and KakaoLoginButton so the login button remains reachable on small,
landscape, and large-font screens. Replace the fixed vertical spacing,
especially the 223.dp top Spacer and accumulated gaps, with responsive or
weighted spacing, or make the content vertically scrollable while preserving the
existing visual order.
🧹 Nitpick comments (1)
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt (1)

42-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

약관 모델이 화면 계층을 ViewModel로 역참조하게 만들고 있습니다.

AgreementTypeisRequired 규칙을 viewmodel 또는 별도 UI 모델 패키지로 이동해 의존 방향을 바로잡아 주세요.

  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt#L42-L64: AgreementType을 화면 파일 밖의 모델 계층으로 이동합니다.
  • feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementUiState.kt#L3-L6: 이동한 모델을 참조하도록 import를 변경합니다.
  • feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt#L9-L15: ViewModel에서 screen 패키지 의존성을 제거합니다.

경로 지침의 “ViewModel/Container에 있어야 할 비즈니스 로직이 Composable에 섞여있지 않은지 확인” 규칙에 따른 의견입니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt`
around lines 42 - 64, AgreementType과 isRequired 규칙을 화면 계층에서 분리해 ViewModel 또는 별도
UI 모델 패키지로 이동하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt
42-64에서는 선언을 제거하고,
feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementUiState.kt
3-6에서는 이동한 AgreementType을 import하도록 변경하세요.
feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt
9-15에서는 screen 패키지 의존성을 제거하고 새 모델을 참조하세요.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt`:
- Around line 47-52: In the checked branch of the CheckBox composable, update
the check Icon’s contentDescription from the redundant "check" label to null,
while leaving the toggleable semantics and other icon properties unchanged.

In
`@feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt`:
- Around line 12-41: AgreementViewModel의 MutableStateFlow 직접 갱신을 제거하고 Orbit MVI의
ContainerHost를 구현하도록 변경하세요. AgreementUiState를 초기 상태로 사용하는 컨테이너를 구성한 뒤,
toggleAgreement와 toggleAllAgreements의 상태 변경을 각각 intent 블록 내부의 reduce {
state.copy(...) }로 수행하고 외부의 mutableUiState 접근은 제거하세요.

---

Outside diff comments:
In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt`:
- Around line 45-77: Update the LoginScreen layout around the non-scrollable
Column and KakaoLoginButton so the login button remains reachable on small,
landscape, and large-font screens. Replace the fixed vertical spacing,
especially the 223.dp top Spacer and accumulated gaps, with responsive or
weighted spacing, or make the content vertically scrollable while preserving the
existing visual order.

---

Nitpick comments:
In `@feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt`:
- Around line 42-64: AgreementType과 isRequired 규칙을 화면 계층에서 분리해 ViewModel 또는 별도
UI 모델 패키지로 이동하세요.
feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt
42-64에서는 선언을 제거하고,
feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementUiState.kt
3-6에서는 이동한 AgreementType을 import하도록 변경하세요.
feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt
9-15에서는 screen 패키지 의존성을 제거하고 새 모델을 참조하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f9de1ab-8588-4133-8585-9106d809034e

📥 Commits

Reviewing files that changed from the base of the PR and between b6267fe and 2c40058.

📒 Files selected for processing (13)
  • core/designsystem/src/main/java/kr/co/call/designsystem/theme/Color.kt
  • core/designsystem/src/main/java/kr/co/call/designsystem/theme/Type.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementItem.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementSection.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/NextButton.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/entry/LoginEntryBuilder.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementUiState.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt
💤 Files with no reviewable changes (1)
  • feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementSection.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt
  • feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementItem.kt

@codebidoof codebidoof 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.

고생했서요~~ Color.kt 쪽만 수정해 주세용

Comment on lines +61 to +67
style=typography.titleSmall.toSpanStyle(),
) {
append("AI가 먼저 전화하고 관계를 이어가는,\n")
}
withStyle(
style = typography.titleSmallBold.toSpanStyle(),
){

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.

굿굿

Comment on lines 51 to 58
val ChatGradient = Brush.linearGradient(
listOf(
Color(0xFFFF789F),
Color(0xFFFFA3BD)
)
),
start=Offset.Zero,
end=Offset.Infinite
)

@codebidoof codebidoof Jul 19, 2026

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.

이거는 공통 코드라, 랜딩스크린에 적용할 브러시 그래디언트는 새로 만드는 게 좋을 것 같아요. 얘는 피그마 그대로 뽑은 거라... ChatGradient 의 변경사항은 롤백하고 브러시 변수를 새로 만들어서 랜딩스크린에 적용하는 게 좋을 것 같습니다!!!!

fun EntryProviderScope<NavKey>.loginEntry() {
entry<LoginNavKey> {
LoginScreen()
LoginScreen(

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.

Modifier는 어차피 디폴트로 화면에서 가지고 있어서 지워도 될 것 같아요 다음 화면으로 넘어갈때 필요한 값(id나 콜백)만 넘기면 좋을 것 같아용

@github-actions

Copy link
Copy Markdown

Gemini AI 코드리뷰

PR 리뷰 요청드립니다.

안녕하세요! 새로운 로그인 및 약관 동의 플로우를 구현하신 PR 잘 봤습니다. 전반적으로 Jetpack Compose의 모범 사례와 Orbit MVI 패턴을 잘 적용하셨고, UI 컴포넌트들이 잘 분리되어 있어 깔끔합니다. 몇 가지 개선사항과 제안을 드립니다.


🔍 코드 리뷰 요약

칭찬하는 점:

  • Jetpack Compose 모범 사례: AgreementItem, CheckBox, NextButton, LoginScreen 등 대부분의 컴포넌트에서 상태 호이스팅(State Hoisting) 원칙을 잘 준수하여 stateevent를 분리하고 있습니다. rememberMutableInteractionSource 사용도 적절합니다.
  • Orbit MVI 패턴: AgreementViewModel에서 Orbit ContainerHost 패턴을 잘 따르고 있으며, intent 내에서 reduce를 통해 불변(immutable) 상태를 변경하는 방식이 올바르게 적용되었습니다. 비동기/블로킹 작업 없이 순수하게 상태 변경만 수행하는 점도 좋습니다.
  • UI 상태 모델링: AgreementUiStatedata class로 명확하게 정의하고, isAllChecked, isRequiredChecked와 같은 파생 상태를 get() 속성으로 처리한 점이 좋습니다. Set<AgreementType>을 사용하여 중복 없는 동의 목록을 관리하는 것도 효율적입니다.
  • 디자인 시스템 활용: CallTheme.typography, CallTheme.colors를 사용하여 디자인 시스템의 폰트와 색상을 활용하는 점이 좋습니다. 특히 LandingScreen에서 brush를 이용해 그라데이션 텍스트를 표현한 부분이 인상 깊습니다.
  • 널 안전성: ?. 연산자와 .orEmpty()를 사용하여 널 안전성을 확보한 점이 훌륭합니다. (예: AgreementSection.kt)

개선이 필요한 점:

  1. 하드코딩된 값 (Hardcoded Values): dp 단위의 크기, 패딩, 너비, 높이, 코너 반경, 보더 너비 등 숫자 값들이 곳곳에 하드코딩되어 있습니다. 또한 Color.White, Black 등 특정 색상도 직접 사용되는 경우가 있습니다.
    • 개선 방안: designsystem 모듈 내에 Dimens.kt, Shapes.kt 파일 등을 생성하여 상수로 정의하거나, CallTheme 내에 토큰으로 추가하여 관리하면 일관성을 유지하고 유지보수성을 크게 향상시킬 수 있습니다.
  2. 문자열 리소스 (String Resources): "필수", "선택", "보기", "뒤로가기", "로고", "전화왔어", "카카오 로고", "카카오로 3초 만에 시작하기" 등 사용자에게 노출되는 모든 텍스트는 strings.xml 파일로 분리하여 관리해야 합니다. 이는 다국어 지원 및 유지보수에 필수적입니다.
  3. AgreementSection의 텍스트 파싱: AgreementSection에서 content: String을 받아 lines()로 분리 후 정규식을 통해 파싱하는 로직이 Composable 함수 내에 있습니다. 약관 내용이 자주 변경되지 않는 정적인 텍스트이므로 현재는 큰 문제가 아닐 수 있지만, 만약 content가 동적으로 변경되거나 매우 긴 경우 불필요한 리컴포지션을 유발할 가능성이 있습니다.
    • 개선 방안 (고려사항): 만약 성능 문제가 발생할 여지가 있다면, ViewModel에서 미리 파싱된 데이터를 (예: List<AgreementSectionItem>) Composable로 전달하는 것을 고려해볼 수 있습니다. 하지만 현재로서는 과도한 최적화일 수 있으므로, 일단은 지켜봐도 좋습니다.
  4. AgreementSection Preview 누락 (Minor): AgreementSection 컴포넌트에 @Preview 어노테이션이 누락되어 있습니다. 컴포넌트 단위로 시각적인 테스트가 가능하도록 프리뷰를 추가하는 것이 좋습니다.
  5. LandingGradientOffset.Infinite (Minor): Brush.linearGradientend 파라미터로 Offset.Infinite를 사용하셨는데, 이는 일반적으로 "그려질 수 있는 최대 범위까지"를 의미합니다. 대부분의 경우 의도대로 동작하지만, 특정 환경이나 커스텀 Canvas 드로잉 시 미묘한 동작 차이가 있을 수 있습니다.
    • 개선 방안 (고려사항): 만약 특정 너비/높이에 맞춰 그라데이션이 끝나야 한다면, Offset(width, height)와 같이 명시적인 값을 사용하는 것이 의도를 더욱 명확히 할 수 있습니다. 현재의 Offset.Infinite는 문제가 없을 가능성이 높지만, 알고 있으면 좋습니다.

파일별 상세 리뷰

core/designsystem/src/main/java/kr/co/call/designsystem/theme/Color.kt

  • SubYellow 추가 및 LandingGradient 정의, CallColors 업데이트 모두 적절합니다. @Immutable 사용도 좋습니다.

core/designsystem/src/main/java/kr/co/call/designsystem/theme/Type.kt

  • 새로운 TextStyle 정의와 CallTypography 업데이트 모두 좋습니다. @Immutable 사용도 좋습니다.

feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementItem.kt

  • 개선 사항:
    • contentPadding= PaddingValues(horizontal=3.dp): 3.dp는 하드코딩된 값입니다. 디자인 시스템 내 상수로 정의하는 것을 고려해주세요.
    • 텍스트 "필수", "선택", "보기"는 strings.xml로 분리해야 합니다.

feature/login/impl/src/main/java/kr/co/call/impl/component/AgreementSection.kt

  • numberedLineRegex, bulletLineRegexprivate val로 선언하여 스코프 제한 및 불변성을 지킨 점 좋습니다.
  • matchResult?.groupValues?.get(index).orEmpty()를 사용하여 널 안전성을 잘 확보했습니다.
  • 개선 사항:
    • Spacer(modifier=Modifier.height(12.dp)): 12.dp는 하드코딩된 값입니다. 상수로 정의하는 것을 고려해주세요.
    • @Preview 함수가 누락되어 있습니다.

feature/login/impl/src/main/java/kr/co/call/impl/component/BackTopBar.kt

  • onBackClick 이벤트 처리가 잘 분리되어 있습니다.
  • 개선 사항:
    • height(58.dp), padding(horizontal=18.dp), size(width=8.dp, height=17.dp): 모두 하드코딩된 값입니다. 디자인 시스템 내 상수로 정의하는 것을 고려해주세요.
    • contentDescription="뒤로가기": "뒤로가기"는 strings.xml로 분리해야 합니다.

feature/login/impl/src/main/java/kr/co/call/impl/component/CheckBox.kt

  • checked, onCheckedChange로 상태 호이스팅을 잘 적용했습니다. toggleableRole.Checkbox를 사용하여 접근성을 고려한 점 좋습니다.
  • 개선 사항:
    • size(48.dp), size(23.dp), RoundedCornerShape(3.dp): 모두 하드코딩된 값입니다. 상수로 정의하는 것을 고려해주세요.

feature/login/impl/src/main/java/kr/co/call/impl/component/NextButton.kt

  • onClick, enabled, containerColor, contentColor, pressedContainerColor 등 다양한 파라미터를 통해 컴포넌트의 유연성을 높였습니다. MutableInteractionSourceripple() 사용도 적절합니다.
  • 개선 사항:
    • height(60.dp), RoundedCornerShape(20.dp): 하드코딩된 값입니다. 상수로 정의하는 것을 고려해주세요.
    • text 파라미터는 strings.xml 참조로 변경하는 것이 좋습니다.

feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementDetailScreen.kt

  • title, content, onBackClick으로 상태 호이스팅을 잘 적용했습니다. rememberScrollStateverticalScroll 사용도 적절합니다.
  • statusBarsPadding() 적용도 좋습니다.
  • 개선 사항:
    • padding(horizontal =34.dp, vertical=22.dp), Spacer(modifier=Modifier.height(22.dp)), Spacer(modifier=Modifier.height(40.dp)): 모두 하드코딩된 값입니다. 상수로 정의하는 것을 고려해주세요.
    • title 파라미터는 strings.xml 참조로 변경하는 것이 좋습니다.

feature/login/impl/src/main/java/kr/co/call/impl/screen/AgreementScreen.kt

  • AgreementType enum class를 통해 약관 종류를 명확히 정의한 점이 좋습니다.
  • AgreementUiState를 사용하여 UI 상태를 관리하고, 이벤트 콜백을 통해 상위로 전달하는 방식이 아주 잘 구현되었습니다. statusBarsPadding()navigationBarsPadding() 적용도 좋습니다.
  • 개선 사항:
    • height(156.dp), size(70.dp), width(14.dp), height(20.dp), height(80.dp), RoundedCornerShape(30.dp), padding(horizontal = 20.dp, vertical = 30.dp), height(10.dp), thickness = 1.dp, height(25.dp)대부분의 dp 값들이 하드코딩되어 있습니다. NextButton 부분의 패딩(23.dp, 25.dp)도 마찬가지입니다. 이 부분은 반드시 디자인 시스템의 상수로 빼주세요.
    • ImagecontentDescription = "로고", Text의 "전화왔어", "전화왔어를 이용하려면\n약관에 동의가 필요해요", "모두 동의", "서비스 이용을 위한 아래 약관에 모두 동의합니다", "동의하고 프로필 설정하기" 등 모든 사용자 노출 문자열은 strings.xml로 분리해야 합니다.
    • BorderStroke(width = 1.dp, color = Gray100)에서 1.dp도 상수로 관리하면 좋습니다.

feature/login/impl/src/main/java/kr/co/call/impl/screen/LandingScreen.kt

  • ImagecontentScale= ContentScale.CropcontentDescription = null 사용이 적절합니다. 그라데이션 텍스트 구현도 좋습니다.
  • 개선 사항:
    • height(266.dp), size(97.dp), width(20.dp), height(22.dp): 모두 하드코딩된 값입니다. 상수로 정의하는 것을 고려해주세요.
    • "전화왔어", "오늘, 기다리던 사람에게\n전화가 옵니다." 등 모든 사용자 노출 문자열은 strings.xml로 분리해야 합니다.

feature/login/impl/src/main/java/kr/co/call/impl/screen/LoginScreen.kt

  • onKakaoLoginClick 이벤트 처리가 잘 분리되어 있습니다. buildAnnotatedStringwithStyle을 사용하여 풍부한 텍스트 스타일링을 적용한 점은 매우 훌륭합니다. navigationBarsPadding() 적용도 좋습니다.
  • 개선 사항:
    • height(223.dp), size(81.dp), height(16.dp), height(37.dp), height(51.dp), padding(horizontal = 20.dp), padding(start=20.dp), size(width=21.dp, height=20.1.dp): 모두 하드코딩된 값입니다. 상수로 정의하는 것을 고려해주세요.
    • "전화왔어", "AI가 먼저 전화하고 관계를 이어가는,\n나만의 연애 시뮬레이션", "카카오로 3초 만에 시작하기", "카카오 로고" 등 모든 사용자 노출 문자열은 strings.xml로 분리해야 합니다.

feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementUiState.kt

  • data class로 UI 상태를 잘 표현하고 있습니다. val만을 사용하여 불변성을 유지하고, isAllChecked, isRequiredChecked를 계산된 속성으로 제공하는 패턴이 아주 좋습니다.

feature/login/impl/src/main/java/kr/co/call/impl/viewmodel/AgreementViewModel.kt

  • @HiltViewModel@Inject constructor()를 사용하여 Hilt 의존성 주입 원칙을 잘 따르고 있습니다.
  • ContainerHost 패턴을 잘 준수하고, intent { reduce { ... } } 구조로 상태 변경을 안전하게 처리하고 있습니다.
  • 블로킹 작업이나 Side Effect 없이 순수하게 UI 상태 로직만 처리하는 점이 훌륭합니다.

최종 의견

전반적으로 매우 높은 수준의 코드 퀄리티를 보여주고 있습니다. 특히 Jetpack Compose의 핵심 원칙인 상태 호이스팅과 UI 상태 모델링, 그리고 ViewModel에서 Orbit MVI 패턴을 적용한 방식이 훌륭합니다.

가장 중요한 개선사항은 하드코딩된 값들(매직 넘버)을 디자인 시스템의 상수로 분리하고, 사용자에게 노출되는 모든 문자열을 strings.xml로 이동하는 것입니다. 이 두 가지를 해결하면 코드의 유지보수성, 확장성, 그리고 다국어 지원에 대한 준비가 크게 향상될 것입니다.

이외의 제안사항들은 선택적으로 적용하셔도 좋습니다.

수정 후 다시 리뷰 요청해주시면 감사하겠습니다. 수고하셨습니다!

@codebidoof codebidoof 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.

고생했서요~~ 화이팅!!!

@dada4679
dada4679 merged commit 8f30c10 into develop Jul 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 로그인 화면 UI 구현

3 participants