Skip to content

kuring-251 동아리 온보딩 화면 개발#662

Open
mwy3055 wants to merge 11 commits intodevelopfrom
feature/kuring-251_create-club-onboarding
Open

kuring-251 동아리 온보딩 화면 개발#662
mwy3055 wants to merge 11 commits intodevelopfrom
feature/kuring-251_create-club-onboarding

Conversation

@mwy3055
Copy link
Member

@mwy3055 mwy3055 commented Feb 7, 2026

요약

동아리 온보딩 화면을 구현했습니다. 동아리를 4개의 카테고리로 나누고, 동아리 필터를 바꾸기 전까지는 여기에서 선택한 카테고리가 필터로 설정됩니다.

(아래 preview에서는 똑같은 카테고리가 4번 반복되고 있지만, 실제로는 서로 다른 값이 보입니다)

image

개발 시 참고사항

@boiledeggg

  • 최초 선택한 카테고리는 PreferenceUtil.clubInitialCategory로 정의하였습니다. 이 값이 비어있지 않을 때 필터를 설정하면 됩니다.
    • 사용자가 필터를 바꾸기 전까지는 이 값이 필터로 설정됩니다.
  • 앱 설치 후 동아리 탭에 최초로 진입할 때 이 activity를 보여주면 됩니다.

Summary by CodeRabbit

  • 새로운 기능

    • 클럽 온보딩 플로우 추가 — 학술/문화예술/사회가치/활동 중 선호 카테고리 선택 및 저장(초기 카테고리 저장 지원)
    • 앱 내에서 클럽 온보딩 화면으로 이동하는 네비게이션 경로 추가
    • 클럽 관련 UI 컴포넌트 및 한국어 텍스트(문구) 추가
  • 잡업(설정)

    • 클럽 기능 모듈이 프로젝트에 포함됨 (모듈 등록 및 빌드 설정)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

Walkthrough

앱에 새로운 feature/club 모듈을 추가하고, 클럽 온보딩용 Activity·Compose 화면·ViewModel·컴포넌트·리소스와 네비게이터 확장, Preference 항목 및 디자인시스템 편의 오버로드를 도입했습니다. (요약 50단어 이내)

Changes

Cohort / File(s) Summary
앱 통합
app/build.gradle.kts, app/src/main/AndroidManifest.xml
앱에 feature:club 의존성 추가 및 ClubOnboardingActivity 매니페스트 등록
네비게이션 인프라
domain/navigation/src/main/java/.../KuringNavigator.kt, app/src/main/java/.../KuringNavigatorImpl.kt
인터페이스에 navigateToClubOnboarding(context: Context) 추가 및 구현에서 ClubOnboardingActivity.start(context) 호출 추가
클럽 피처 모듈 구성
settings.gradle.kts, feature/club/build.gradle.kts, feature/club/.gitignore, feature/club/src/main/AndroidManifest.xml
새로운 feature 모듈 등록, Gradle 설정·플러그인 및 /build 무시 규칙 추가
클럽 온보딩 Activity
feature/club/src/main/java/.../ClubOnboardingActivity.kt
Hilt 주입(@AndroidEntryPoint) ComponentActivity 추가, Compose로 ClubOnboardingScreen 설정 및 static start(context) 제공
클럽 온보딩 화면 및 ViewModel
feature/club/src/main/java/.../ClubOnboardingScreen.kt, feature/club/src/main/java/.../ClubOnboardingViewModel.kt
카테고리 선택 UI와 상태 관리(ViewModel), 선택 저장(saveInitialCategory) 및 저장 흐름 추가
클럽 UI 컴포넌트 및 프리뷰
feature/club/src/main/java/.../components/ClubCategoryItem.kt, .../ClubCategoryItemPreviewParameterProvider.kt, .../ClubCategoryItemState.kt
선택 가능한 카테고리 아이템 컴포저블, 상태 데이터 클래스, PreviewParameterProvider 및 프리뷰 추가
리소스 및 문자열
feature/club/src/main/res/values/strings.xml
클럽 온보딩 관련 한국어 문자열 리소스 추가
디자인시스템·설정
core/designsystem/.../CenterTitleTopBar.kt, core/preferences/.../PreferenceUtil.kt
제목 전용 TopBar 오버로드 추가 및 PreferenceUtil에 clubInitialCategory 프로퍼티와 상수(CLUB_INITIAL_CATEGORY) 추가

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Navigator as KuringNavigatorImpl
    participant Activity as ClubOnboardingActivity
    participant Screen as ClubOnboardingScreen
    participant VM as ClubOnboardingViewModel
    participant Pref as PreferenceUtil

    User->>Navigator: navigateToClubOnboarding(context)
    Navigator->>Activity: start(context) (Intent)
    Activity->>Screen: setContent(Compose)
    Screen->>VM: hiltViewModel() (주입)
    User->>Screen: 카테고리 선택
    Screen->>VM: setSelectedItem(index)
    User->>Screen: CTA 클릭
    Screen->>VM: saveInitialCategory()
    VM->>Pref: clubInitialCategory = categoryId
    Pref->>Pref: SharedPreferences 저장
    VM-->>Screen: 저장 결과(Boolean)
    Screen->>Activity: onClose() -> finish()
Loading

Possibly related PRs

시 🐰

새로운 문을 열었네, 깡총깡총,
카테고리 고르고 콕, 선택 한 방,
뷰모델이 살며시 저장하고,
설정 속에 조용히 담겼네,
토끼가 축하 춤을 추네 🐇🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 'kuring-251 동아리 온보딩 화면 개발'로, 변경사항의 주요 목적인 동아리 온보딩 화면 개발을 명확하게 설명하고 있습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/kuring-251_create-club-onboarding

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
Contributor

@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

🤖 Fix all issues with AI agents
In `@feature/club/build.gradle.kts`:
- Around line 10-13: Remove the duplicate namespace assignment inside the
android block: keep the LibraryExtension call setNameSpace("club") and delete
the direct property assignment namespace = "com.ku_stacks.ku_ring.club" (remove
the line that sets namespace explicitly) so only setNameSpace(...) is used to
configure the module namespace.

In
`@feature/club/src/main/java/com/ku_stacks/ku_ring/club/onboarding/ClubOnboardingActivity.kt`:
- Around line 11-20: The Activity ClubOnboardingActivity is missing the Hilt
entry point annotation causing runtime IllegalStateException when
ClubOnboardingScreen calls hiltViewModel(); add `@AndroidEntryPoint` to the
ClubOnboardingActivity class declaration so Hilt can provide ViewModel
dependencies to the Compose host (ensure the import for
dagger.hilt.android.AndroidEntryPoint is added and kept).

In
`@feature/club/src/main/java/com/ku_stacks/ku_ring/club/onboarding/ClubOnboardingViewModel.kt`:
- Around line 47-74: initOnboardingItems() currently sets
R.drawable.ic_academic_v2 for every ClubCategoryItemState (academic,
culture_art, social_value, activity); update the iconId for each
ClubCategoryItemState to the correct drawable resource (e.g., ic_culture_art,
ic_social_value, ic_activity) or, if the identical icon is intentional, add a
TODO comment documenting it next to the ClubCategoryItemState entries so
reviewers know it’s deliberate; ensure you edit the iconId values in
initOnboardingItems() where ClubCategoryItemState is constructed.
🧹 Nitpick comments (6)
app/src/main/AndroidManifest.xml (1)

97-99: 기존 OnboardingActivity와 달리 screenOrientation="portrait" 누락

기존 온보딩 액티비티(Line 74-76)에는 android:screenOrientation="portrait"가 설정되어 있습니다. 동아리 온보딩도 세로 고정이 의도된 것이라면 추가하는 것이 좋겠습니다.

제안
         <activity
             android:name=".club.onboarding.ClubOnboardingActivity"
-            android:exported="false" />
+            android:exported="false"
+            android:screenOrientation="portrait" />
feature/club/src/main/java/com/ku_stacks/ku_ring/club/onboarding/ClubOnboardingScreen.kt (2)

137-143: "건너뛰기" 텍스트의 터치 영역이 접근성 최소 기준(48dp)에 미달할 수 있음

clickable modifier만 적용된 Text는 텍스트 크기에 따라 터치 영역이 너무 작을 수 있습니다. 최소 터치 영역 48dp를 보장하기 위해 minimumInteractiveComponentSize()를 추가하거나, 충분한 padding을 적용하는 것이 좋습니다.

수정 제안
         Text(
             text = stringResource(R.string.club_onboarding_dismiss),
             style = KuringTheme.typography.body2SB,
             color = KuringTheme.colors.textCaption1,
             textAlign = TextAlign.Center,
-            modifier = Modifier.clickable(onClick = onDismiss),
+            modifier = Modifier
+                .clickable(onClick = onDismiss)
+                .padding(vertical = 12.dp, horizontal = 16.dp),
         )

128-135: 선택 항목 없이 확인 버튼 클릭 시 사용자 피드백 부재

isSelectedItemIndexValidfalse일 때 CTA 클릭이 아무 동작도 하지 않습니다. 버튼의 enabled 상태를 활용하거나, 시각적 피드백(예: 스낵바)을 제공하면 사용자 경험이 개선됩니다.

예시: CTA 비활성화

KuringCallToActionenabled 파라미터를 지원한다면:

         KuringCallToAction(
             text = stringResource(R.string.club_onboarding_cta),
-            onClick = {
-                if (isSelectedItemIndexValid(selectedItemIndex)) {
-                    saveSelectedCategory()
-                }
-            },
+            onClick = saveSelectedCategory,
+            enabled = isSelectedItemIndexValid(selectedItemIndex),
             modifier = Modifier.fillMaxWidth(),
         )
feature/club/src/main/java/com/ku_stacks/ku_ring/club/onboarding/components/ClubCategoryItem.kt (2)

45-59: Modifier 순서: clipborderbackground 순서 확인 필요

현재 modifier 체인에서 .background().border() 앞에 위치하고 있어, 배경색이 border 위에 그려질 수 있습니다. 일반적으로 .border().background() 앞에 배치하거나, 같은 shape를 사용하므로 시각적으로 큰 문제는 없을 수 있지만, 의도를 명확히 하기 위해 순서를 확인해 주세요.

또한 .clip(shape)이 이미 적용되어 있으므로, .background().border()shape을 다시 전달할 필요가 없습니다.

♻️ 제안
     Row(
         modifier = modifier
             .clip(shape)
             .clickable(onClick = onClick)
-            .background(
-                color = background,
-                shape = shape,
-            )
             .border(
                 width = 1.dp,
                 color = borderColor,
                 shape = shape,
             )
+            .background(
+                color = background,
+                shape = shape,
+            )
             .padding(16.dp)
             .fillMaxWidth(),

84-89: item이 public top-level 변수로 노출되어 있습니다.

item은 preview용 샘플 데이터인데, public top-level val로 선언되어 있고 이름도 너무 일반적입니다. 다른 파일에서 의도치 않게 import될 수 있고, 이름 충돌 가능성도 있습니다. private으로 변경하는 것을 권장합니다.

♻️ 제안
-val item = ClubCategoryItemState(
+private val sampleClubCategoryItem = ClubCategoryItemState(
     categoryName = R.string.category_academic,
     description = R.string.description_academic,
     categoryId = "",
     iconId = R.drawable.ic_academic_v2,
 )
feature/club/src/main/java/com/ku_stacks/ku_ring/club/onboarding/ClubOnboardingViewModel.kt (1)

43-45: isSelectedItemIndexValid 메서드의 사용 목적을 명확히 해주세요.

이 메서드는 UI 레이어에서 저장 버튼의 onClick 핸들러에서 isSelectedItemIndexValid(selectedItemIndex)로 호출되어 현재 선택된 인덱스의 유효성을 검증합니다. setSelectedItem과 달리 이 메서드는 상태 업데이트가 아닌 UI 검증 목적입니다. 다만, 실제 호출에서는 항상 selectedItemIndex가 전달되므로, 파라미터를 제거하고 내부 상태를 직접 검증하는 방식으로 리팩토링하면 의도가 더 명확해질 것입니다.

Copy link
Member

@boiledeggg boiledeggg left a comment

Choose a reason for hiding this comment

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

👍 👍 👍 👍 👍 👍 👍

Comment on lines 84 to 89
val item = ClubCategoryItemState(
categoryName = R.string.category_academic,
description = R.string.description_academic,
categoryId = "",
iconId = R.drawable.ic_academic_v2,
)
Copy link
Member

Choose a reason for hiding this comment

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

621ae7b

이번에 PreviewParameterProvider로 프리뷰에 파라미터를 넣는 방식을 바꿔봤습니다.
메인 코드가 깔끔해지기도 하고, 재사용하기도 좋아서 참고해보시면 좋을 것 같습니다.

혹시라도 더 좋은 방법이 있다면 의견 남겨주세요!

Copy link
Member Author

Choose a reason for hiding this comment

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

의견 없습니다! 저도 적용해봤는데 이거 참 좋네요 01ca9c2

@LightAndDarkPreview
@Composable
private fun ClubCategoryItemPreview() {

Copy link
Member

Choose a reason for hiding this comment

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

빈줄은 일부러 남기신건가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

그럴리가.. dc64017 에서 제거헀습니다.

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.

2 participants