Feature/#30 feat timetable name limit - #45
Conversation
- `Image`에 `cchClickable` Modifier를 적용하여 클릭 영역을 명확히 함
- 시간표 리스트가 비어있을 때 표시되는 문구를 "등록된 시간표가 없어요!"로 변경 - 문구의 상단 패딩 값을 150.dp에서 324.dp로 변경
- 시간표 이름이 20자를 초과하는 경우 에러 상태로 표시 - 텍스트 필드에 `isError` 파라미터 추가하여 에러 상태 시 테두리 색상 변경 - 에러 메시지를 표시하여 사용자에게 입력 제한을 알림
- 시간표 이름이 20자를 초과하는 경우 에러 상태로 표시 - 텍스트 필드에 `isError` 파라미터 추가하여 에러 상태 시 테두리 색상 변경 - 에러 메시지를 표시하여 사용자에게 입력 제한을 알림
Walkthrough시간표 이름 입력/편집에 최대 20자 검증을 추가하고 오류 상태를 텍스트필드(isError)와 에러 메시지로 표시하도록 변경. CchRegularTextField 시그니처에 isError 추가. 앱바 뒤로가기 클릭을 Image로 이동. 시간표 리스트 빈 상태의 패딩과 문구 조정. Changes
Sequence Diagram(s)sequenceDiagram
actor User as 사용자
participant Screen as TimetableName Screen
participant TF as CchRegularTextField
participant Ext as TimetableNameLimit (utils)
participant VM as Contract/State
User->>TF: 이름 입력
TF->>VM: onValueChanged(name)
VM->>Ext: checkTimetableNameRule(name)
Ext-->>VM: valid:Boolean
VM-->>Screen: uiState(name, buttonEnabled)
Screen->>TF: isError = checkOverTimetableNameLimit(name)
alt name.length > 20
Screen-->>User: 에러 텍스트 표시 (Red300)
Screen-->>User: 저장 버튼 비활성
else
Screen-->>User: 에러 숨김, 버튼 상태 반영
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.kt (1)
65-71: 추가 버튼도 동일한 접근성/일관성 적용 필요Add 아이콘도 클릭 가능 요소이므로 동일하게 48dp 터치 영역과 의미 있는 contentDescription을 제공하는 것이 바람직합니다. 디자인 시스템 관점에서도 좌/우 액션의 인터랙션 영역을 일치시키는 것이 좋습니다.
적용 제안(diff):
Icon( modifier = Modifier - .clip(CircleShape) + .size(48.dp) + .clip(CircleShape) .align(Alignment.CenterEnd) - .cchClickable(onClick = onClickAdd), + .cchClickable(onClick = onClickAdd) + .padding(12.dp), painter = painterResource(Res.drawable.ic_timetable_add), - contentDescription = "", + contentDescription = stringResource(Res.string.appbar_add_content_description), tint = Black100, )추가:
- 문자열 리소스 예시:
appbar_add_content_description = "시간표 추가"composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt (1)
104-113: 고정 패딩(324.dp) 제거 및 빈 상태 화면 중앙 정렬 적용
현재TimetableListScreen.kt107행의Modifier.padding(top = 324.dp)는 디바이스 해상도, 글꼴 크기, 시스템 인셋 변화에 취약한 매직 넘버입니다. 빈 상태 UI는 Box를 활용해 가로·세로 중앙에 배치하는 방식이 반응형·접근성 측면에서 더 안전합니다.검토 위치
- 파일: composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt
- 대략 104~113행
제안된 리팩터링(diff):
- if (uiState.timetableList.isEmpty()) { - Text( - modifier = Modifier - .padding(top = 324.dp), - textAlign = TextAlign.Center, - text = "등록된 시간표가 없어요!", - style = CchTheme.typography.bodyMd, - color = Gray600, - ) - } + if (uiState.timetableList.isEmpty()) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 20.dp) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = stringResource(Res.string.timetable_list_screen_empty_timetable), + textAlign = TextAlign.Center, + style = CchTheme.typography.bodyMd, + color = Gray600, + ) + } + } else { LazyColumn( modifier = Modifier.padding(vertical = 8.dp, horizontal = 20.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { /* ... */ } }추가 import:
import androidx.compose.foundation.layout.Box import androidx.compose.ui.Alignment이 변경은 이번 PR의 핵심(시간표 이름 20자 제한)과 직접 관련이 없으므로, 별도 커밋/PR로 분리해 적용하는 것을 권장합니다.
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt (3)
83-86: 컴파일 오류: 널이 아닌semester에 안전 호출(?.) 사용
TimetableNameInputState.semester는 널이 아닌 타입입니다. 안전 호출은 컴파일되지 않습니다. 아래처럼 안전 호출을 제거하세요.- title = "${uiState.semester?.year}년 ${uiState.semester?.semester}학기", + title = "${uiState.semester.year}년 ${uiState.semester.semester}학기",
103-103: 196.dp 상단 패딩은 매직 넘버 — 디자인 토큰 또는 레이아웃로 대체 권장고정 196.dp는 기기/폰트 스케일에 따라 레이아웃 왜곡을 유발합니다. 디자인 시스템의 spacing 토큰, dimension 리소스, 또는
Spacer(Modifier.weight(...))/Arrangement.spacedBy등으로 대체를 권장합니다.예시:
- modifier = Modifier.padding(top = 196.dp, start = 4.dp, end = 4.dp), + modifier = Modifier.padding(top = 16.dp, start = 4.dp, end = 4.dp),
102-111: 문자열·상수 하드코딩 제거 및 입력 제한 강제화 필요아래 위치들에서 “시간표 이름은 최대 20자까지 설정 가능합니다.” 문자열과 숫자
20이 하드코딩되어 있습니다. 리소스화 및 상수화하여 한 곳에서 관리하고, 입력 단계에서 20자 초과 입력을 강제 컷팅하도록 수정하세요.• 문자열 하드코딩
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt:117
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt:118
• 길이 비교식 하드코딩
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt:12 (
name.length <= 20)- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt:107, 112 (
uiState.name.length > 20)- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt:108, 113 (
uiState.name.length > 20)- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.kt:16 (
name.length <= 20)제안된 수정 예시
- isError = uiState.name.length > 20, + isError = uiState.name.length > TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH,- text = "시간표 이름은 최대 20자까지 설정 가능합니다.", + text = stringResource(Res.string.timetable_name_error_max_20),ViewModel(예:
TimetableNameInputViewModel.updateName)에서 입력을 강제 컷팅하도록 변경:fun updateName(input: String) { val trimmed = input.trim() val clipped = trimmed.take(TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH) mviStore.update { it.copy(name = clipped) } }
Res.string.timetable_name_error_max_20등의 리소스를 추가하고,TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH상수를 정의하여 중복을 제거하세요.- 입력 뷰단에서는
isError표시만 남기고, 실제 상태 업데이트는 ViewModel에서 강제 컷팅된 값을 사용하도록 일원화해 UX 일관성을 확보해야 합니다.composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt (1)
73-75: CchRegularTextField의 클리어 버튼 주석 처리 복구 또는 파라미터 제거 필요
현재onClickClearButton콜백을 전달하는 화면에서도 버튼 UI가 주석 처리되어 노출되지 않습니다. 사용자 혼란을 방지하려면 다음 중 하나를 적용해주세요.• 수정 위치
- 파일:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.ktdecorationBox내부, 현재 주석 처리된if (isActive) { TextFieldClearButton(...) }블록• 개선안
- 버튼 복구
- // if (isActive) {
- // TextFieldClearButton(onClick = onClickClearButton)
- // }
if (isActive) {TextFieldClearButton(onClick = onClickClearButton)}- 활성화 상태(`isActive == true`)일 때만 클리어 버튼을 표시 - 기존 `onClickClearButton` 콜백 활용 2. 파라미터 제거 - `isActive`와 `onClickClearButton` 파라미터 삭제 - 호출부(예: `TimetableNameInputScreen`, `TimetableEditorScreen`)에서 관련 인자 제거위 변경 후, 클리어 버튼 동작 및 레이아웃을 한 번 더 확인해주세요.
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt (1)
103-111: isActive 기준이 ‘값 변경 여부’로 되어 있어 다른 화면과 불일치 — 입력 유무 기준으로 통일 권장NameInputScreen은
isActive = name.isNotEmpty()인데, 본 화면은preName과의 비교로 활성 스타일이 달라집니다. 활성 스타일은 “값 존재 여부” 기준으로 일관되게 가져가는 것을 권장합니다.적용 예시(diff):
- isActive = uiState.name.isNotEmpty() && uiState.name != uiState.preName, + isActive = uiState.name.isNotEmpty(),
♻️ Duplicate comments (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt (1)
113-123: 에러 문구 하드코딩/중복 — 리소스화 및 상수 사용으로 통일(Cf. NameInputScreen 동일 코멘트)본 코멘트는 NameInputScreen 동일 사안의 중복입니다. 문자열 리소스로 이동하고, 비교식은 공유 상수를 사용하세요.
적용 예시(diff):
- text = "시간표 이름은 최대 20자까지 설정 가능합니다.", + text = stringResource(Res.string.timetable_name_error_max_20),- isError = uiState.name.length > 20, + isError = uiState.name.length > TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.kt(1 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt(2 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.kt(1 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt(3 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt(1 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt(1 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt(3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.kt: Use the Kotlin official code style
Use immutable state with data classes
Files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/**/*.kt: When creating new UI components, useCCHaksaThemefor new features
Prefer typography styles over hardcoded text styles
Files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
Organize UI components and themes in the designsystem directory
Files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt
🧠 Learnings (6)
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/preview/designsystem/**/*.kt : Preview components in the `preview/designsystem/` package
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/**/*.kt : Organize UI components and themes in the designsystem directory
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/**/*.kt : Prefer typography styles over hardcoded text styles
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/**/*.kt : When creating new UI components, use `CCHaksaTheme` for new features
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/preview/designsystem/**/*.kt : Organize previews for UI components in the preview/designsystem directory
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/theme/**/*.kt : Follow existing color naming conventions (100-600 scale) when creating new colors
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt
⏰ 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: build
🔇 Additional comments (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt (1)
31-39: isError 파라미터 추가는 API 확장으로 타 호출처에 안전 — 기본값 제공 OK기본값이 있어 기존 호출처 호환성 문제가 없고, 에러 연동도 명확합니다. 👍
| modifier = Modifier | ||
| .wrapContentWidth() | ||
| .height(24.dp) | ||
| .clickable { onClickBackButton() }, | ||
| .height(24.dp), | ||
| verticalAlignment = Alignment.CenterVertically, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
뒤로가기 영역 높이 24dp → 최소 48dp로 상향 필요
Row 높이가 24dp로 고정되어 있어 탭 타겟이 권장 최소(48dp)에 미달합니다. 클릭 처리를 Image로 이동한 만큼, 컨테이너(Row)도 최소 48dp 이상을 보장해야 접근성/사용성 저하가 없습니다.
적용 제안(diff):
- .height(24.dp),
+ .heightIn(min = 48.dp),추가: heightIn 사용을 위해 import androidx.compose.foundation.layout.heightIn가 필요합니다.
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.kt
around lines 44–47, the Row for the back area is fixed to height(24.dp) which
yields a tap target below the recommended minimum; replace the fixed height with
heightIn(min = 48.dp) to guarantee at least 48dp touch target and update the
modifier accordingly, and add the import statement import
androidx.compose.foundation.layout.heightIn.
| modifier = Modifier | ||
| .clip(CircleShape) | ||
| .cchClickable(onClick = onClickBackButton), |
There was a problem hiding this comment.
🛠️ Refactor suggestion
클릭 영역이 아이콘 크기에 한정됨 — 48dp 터치 영역을 명시적으로 부여하세요
클릭이 Image에만 걸리면서 실제 터치 영역이 아이콘(추정 24dp) 크기로 축소되었습니다. 최소 48dp의 터치 영역을 보장하도록 size(48.dp)와 내부 padding(12.dp)을 적용해 주세요. 이렇게 하면 시각적 아이콘은 24dp로 유지되면서 터치 영역은 48dp가 됩니다.
적용 제안(diff):
- modifier = Modifier
- .clip(CircleShape)
- .cchClickable(onClick = onClickBackButton),
+ modifier = Modifier
+ .size(48.dp)
+ .clip(CircleShape)
+ .cchClickable(onClick = onClickBackButton)
+ .padding(12.dp),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| modifier = Modifier | |
| .clip(CircleShape) | |
| .cchClickable(onClick = onClickBackButton), | |
| modifier = Modifier | |
| .size(48.dp) | |
| .clip(CircleShape) | |
| .cchClickable(onClick = onClickBackButton) | |
| .padding(12.dp), |
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.kt
around lines 51-53, the clickable modifier is only applied to the Image
(reducing touch target to ~24dp); update the modifier chain to guarantee a 48.dp
touch target by adding size(48.dp) and padding(12.dp) so the visible icon stays
24.dp while the touch area is 48.dp, and ensure cchClickable is applied to that
48.dp area (e.g., apply size(48.dp) then cchClickable(onClick =
onClickBackButton) then padding(12.dp)/clip as needed).
| .clip(CircleShape) | ||
| .cchClickable(onClick = onClickBackButton), | ||
| painter = painterResource(resource = Res.drawable.ic_appbar_arrow_left_chukchuk), | ||
| contentDescription = "", |
There was a problem hiding this comment.
🛠️ Refactor suggestion
접근성: 비어있는 contentDescription을 의미 있는 문자열로 교체하세요
조작 가능한 뒤로가기 버튼인데 contentDescription = ""로 되어 있습니다. 스크린리더 사용자를 위해 의미 있는 문자열을 제공해야 합니다.
적용 제안(diff):
- contentDescription = "",
+ contentDescription = stringResource(Res.string.appbar_navigate_up_content_description),추가:
import org.jetbrains.compose.resources.stringResource- 문자열 리소스 예시:
appbar_navigate_up_content_description = "뒤로가기"(플랫폼별 현지화 가능)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| contentDescription = "", | |
| contentDescription = stringResource(Res.string.appbar_navigate_up_content_description), |
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/appbar/CchAppBarWithTitle.kt
around line 55, the IconButton's contentDescription is an empty string which
breaks accessibility; replace it with a localized stringResource (e.g.
stringResource(R.string.appbar_navigate_up_content_description)) and add the
import org.jetbrains.compose.resources.stringResource, and add a string resource
key (appbar_navigate_up_content_description = "뒤로가기") to the shared resources so
screen readers get a meaningful label.
| val (borderColor, textColor, textStyle) = if(!isActive) { | ||
| Triple(Gray200, Gray400, CchTheme.typography.bodyLg) | ||
| } else { | ||
| Triple(Purple600, Black100, CchTheme.typography.bodyLgStrong) | ||
| Triple(if (isError) Red300 else Purple600, Black100, CchTheme.typography.bodyLgStrong) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 상태가 비활성(isActive=false)일 때 빨간 보더가 표시되지 않음 — 에러 우선 로직으로 정정
현재는 isActive가 false면 에러여도 회색 보더가 보여 사용자 피드백이 약합니다. 에러일 때는 활성/비활성 무관하게 Red300이 보이도록 조건식을 정리하세요.
적용 예시(diff):
- val (borderColor, textColor, textStyle) = if(!isActive) {
- Triple(Gray200, Gray400, CchTheme.typography.bodyLg)
- } else {
- Triple(if (isError) Red300 else Purple600, Black100, CchTheme.typography.bodyLgStrong)
- }
+ val borderColor = when {
+ isError -> Red300
+ isActive -> Purple600
+ else -> Gray200
+ }
+ val textColor = if (isActive) Black100 else Gray400
+ val textStyle = if (isActive) CchTheme.typography.bodyLgStrong else CchTheme.typography.bodyLg📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| val (borderColor, textColor, textStyle) = if(!isActive) { | |
| Triple(Gray200, Gray400, CchTheme.typography.bodyLg) | |
| } else { | |
| Triple(Purple600, Black100, CchTheme.typography.bodyLgStrong) | |
| Triple(if (isError) Red300 else Purple600, Black100, CchTheme.typography.bodyLgStrong) | |
| } | |
| // Before: destructuring all three values based on isActive/error | |
| - val (borderColor, textColor, textStyle) = if(!isActive) { | |
| - Triple(Gray200, Gray400, CchTheme.typography.bodyLg) | |
| - } else { | |
| - Triple(if (isError) Red300 else Purple600, Black100, CchTheme.typography.bodyLgStrong) | |
| - } | |
| // After: error-first borderColor, then active/inactive; separate textColor and textStyle | |
| val borderColor = when { | |
| isError -> Red300 | |
| isActive -> Purple600 | |
| else -> Gray200 | |
| } | |
| val textColor = if (isActive) Black100 else Gray400 | |
| val textStyle = if (isActive) CchTheme.typography.bodyLgStrong else CchTheme.typography.bodyLg |
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/textfield/CchRegularTextField.kt
around lines 40 to 44, the conditional currently hides the error border when
isActive is false; change the logic to make error take precedence: compute
borderColor as if (isError) Red300 else if (!isActive) Gray200 else Purple600,
and keep textColor/textStyle determined by isActive (Gray400/bodyLg when
inactive, Black100/bodyLgStrong when active). Update the Triple construction
accordingly so error always shows Red300 regardless of isActive.
| text = "등록된 시간표가 없어요!", | ||
| style = CchTheme.typography.bodyMd, | ||
| color = Gray600, | ||
| ) |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
문자열 하드코딩으로 i18n 깨짐 — stringResource 사용으로 복구 필요 (R)
- Line 109에서 하드코딩된 문자열은 다국어/로컬라이제이션을 깨뜨립니다. 기존처럼 stringResource를 사용해 주세요. 기획 의도가 문구 변경이라면 리소스 값 자체를 갱신하고 번역 키를 유지/추가하세요.
원하시면 리소스 키/번역 추가 PR 스캐폴딩까지 준비해 드립니다.
- text = "등록된 시간표가 없어요!",
+ text = stringResource(Res.string.timetable_list_screen_empty_timetable),추가 검증을 위해 아래 스크립트를 실행해 주세요.
해당 리소스 키의 존재 여부와 stringResource 사용 패턴을 확인합니다.
🏁 Script executed:
#!/bin/bash
# 1. 리소스 파일 검색
find . -type f -name strings.xml
# 2. 키 존재 여부 확인
rg -n 'timetable_list_screen_empty_timetable' .
# 3. TimetableListScreen.kt의 import 및 stringResource 사용 여부 확인
sed -n '1,200p' composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt
# 4. 프로젝트 전반의 stringResource 사용 패턴 검색
rg -n 'stringResource' -C2 composeAppLength of output: 45853
하드코딩된 문자열 제거 및 stringResource 적용 필요
109행의 Text 컴포저블에 하드코딩된 "등록된 시간표가 없어요!" 문자열이 남아 있어 다국어/로컬라이제이션(i18n)이 깨집니다. 이미 timetable_list_screen_empty_timetable 리소스(commonMain/composeResources/values/strings.xml 150행)에 기본값 "시간표가 없어요"로 정의되어 있으므로, 화면에 표시할 문구가 변경된 게 목적이라면 리소스 값을 "등록된 시간표가 없어요!"로 갱신하고 stringResource로 참조해야 합니다.
• 대상 위치
- 파일: composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt
- 라인: 109–112 (Text 컴포저블의 text 속성)
• 확인 사항
commonMain/composeResources/values/strings.xml에timetable_list_screen_empty_timetable정의 여부 및 값(현재"시간표가 없어요")- 리소스 값을
"등록된 시간표가 없어요!"로 업데이트할지 결정 - 수정 후 Text 컴포저블에
stringResource호출 적용
@@ composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt:109
- Text(
- modifier = Modifier
- .padding(top = 324.dp),
- textAlign = TextAlign.Center,
- text = "등록된 시간표가 없어요!",
- style = CchTheme.typography.bodyMd,
- color = Gray600,
- )
+ Text(
+ modifier = Modifier
+ .padding(top = 324.dp),
+ textAlign = TextAlign.Center,
+ text = stringResource(Res.string.timetable_list_screen_empty_timetable),
+ style = CchTheme.typography.bodyMd,
+ color = Gray600,
+ )— 리소스 갱신 및 참조 적용에 어려움이 있으면 알려주세요. PR 스캐폴딩 지원 가능합니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| text = "등록된 시간표가 없어요!", | |
| style = CchTheme.typography.bodyMd, | |
| color = Gray600, | |
| ) | |
| Text( | |
| modifier = Modifier | |
| .padding(top = 324.dp), | |
| textAlign = TextAlign.Center, | |
| text = stringResource(Res.string.timetable_list_screen_empty_timetable), | |
| style = CchTheme.typography.bodyMd, | |
| color = Gray600, | |
| ) |
🤖 Prompt for AI Agents
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablelist/TimetableListScreen.kt
lines 109–112: Text composable currently uses a hardcoded string "등록된 시간표가 없어요!"
which breaks localization; replace the hardcoded text with a stringResource
lookup (R or resource key) referencing timetable_list_screen_empty_timetable,
and if you intend the displayed wording to be "등록된 시간표가 없어요!" update
commonMain/composeResources/values/strings.xml entry
timetable_list_screen_empty_timetable to that value; finally rebuild to ensure
the resource key is resolved and the Text uses stringResource(...) instead of a
raw string.
| val semester: Semester = Semester("", "") | ||
| ) { | ||
| val buttonEnabled = name.isNotEmpty() | ||
| val buttonEnabled = name.isNotEmpty() && name.length <= 20 |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
버튼 활성화 조건에서 공백-only 허용 및 하드코딩 상수(20) 중복 위험 — 상수화 + trim/isNotBlank 권장
현재는 공백만 입력한 경우에도 isNotEmpty()로 활성화될 수 있고, 길이 비교가 name.length로 직접 들어가 있어 다른 화면과 중복/불일치가 생길 수 있습니다. 공통 상수로 중앙화하고, 공백 제거 후 길이 체크를 권장합니다. 또한 코드포인트/이모지 고려가 필요하면 추가 확장 가능하도록 해두면 좋습니다.
적용 예시(diff):
- val buttonEnabled = name.isNotEmpty() && name.length <= 20
+ val buttonEnabled =
+ name.isNotBlank() &&
+ name.trim().length <= TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH공유 상수 정의 예시(별도 파일, 예: presentation/timetable/TimetableConstraints.kt):
package com.chukchukhaksa.mobile.presentation.timetable
object TimetableConstraints {
const val MAX_TIMETABLE_NAME_LENGTH = 20
}리포지터리 내 하드코딩된 20자 비교식 일괄 확인 스크립트:
🏁 Script executed:
#!/bin/bash
# 20자 제한 하드코딩 사용처 점검
rg -nP --type=kt '(length\s*[<>]=?\s*20|\s*>\s*20|\s*<\s*20)'Length of output: 90
🏁 Script executed:
#!/bin/bash
# 20자 제한 하드코딩 사용처 점검 (`.kt` 파일만 검색)
rg -nP -g '*.kt' 'length\s*(?:<=|>=|<|>)\s*20'Length of output: 1242
공통 상수화 및 공백 처리(isNotBlank, trim) 적용 필요
하드코딩된 20자 제한이 여러 파일에 중복되어 있어, 추후 변경 시 일관성 유지가 어렵습니다. 또한 isNotEmpty()만으로는 공백만 입력한 경우를 걸러낼 수 없으므로, isNotBlank()과 trim()을 활용해 정확히 비어 있지 않은 문자열만 처리하도록 개선해야 합니다.
수정 대상 파일 및 위치:
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt (12행)
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt (107·112행)
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt (108·113행)
- composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.kt (16행)
공통 상수 정의 예시 (presentation/timetable/TimetableConstraints.kt):
package com.chukchukhaksa.mobile.presentation.timetable
object TimetableConstraints {
const val MAX_TIMETABLE_NAME_LENGTH = 20
}주요 변경 예시 (Contract.kt 기준):
- val buttonEnabled = name.isNotEmpty() && name.length <= 20
+ val buttonEnabled =
+ name.isNotBlank() &&
+ name.trim().length <= TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH주요 변경 예시 (Screen.kt 기준):
- isError = uiState.name.length > 20,
+ isError = uiState.name.trim().length > TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH,
- if (uiState.name.length > 20) {
+ if (uiState.name.trim().length > TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH) {- 모든 비교 로직에서
length <= 20,length > 20등을TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH로 교체 isNotEmpty()→isNotBlank()- 비교 전
trim()적용으로 공백 전후 제거
위 리팩토링을 통해 상수 중복을 제거하고, 공백만 입력된 경우를 차단하며, 추후 제약 변경 시 한곳만 수정하도록 개선할 수 있습니다.
🤖 Prompt for AI Agents
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt
(line 12), plus related locations composeApp/.../TimetableNameInputScreen.kt
(lines 107,112), TimetableEditorScreen.kt (lines 108,113) and
TimetableEditorContract.kt (line 16): replace the hardcoded 20 with a shared
constant by adding presentation/timetable/TimetableConstraints.kt containing
MAX_TIMETABLE_NAME_LENGTH = 20, change all name emptiness checks from
isNotEmpty() to isNotBlank() and apply trim() before length checks (e.g., val
trimmed = name.trim(); buttonEnabled = trimmed.isNotBlank() && trimmed.length <=
TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH), and update any >20 / <=20
comparisons to use TimetableConstraints.MAX_TIMETABLE_NAME_LENGTH so all files
reference the common constraint.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
코멘트 내용 반영했습니다~ @jinukeu
- 시간표 이름 글자 수 제한 상수 `TIMETABLE_NAME_LIMIT` 정의 - `checkOverTimetableNameLimit`: 시간표 이름이 글자 수 제한을 초과하는지 확인 - `checkTimetableNameRule`: 시간표 이름이 유효한지(공백이 아니고 글자 수 제한 이내) 확인
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt (1)
103-111: isActive 조건도 공백-only 입력을 비활성화하도록 정합성 맞추기버튼 활성화는
isNotBlank()기준인데, 텍스트필드의 active 표시는isNotEmpty()로 공백-only에서도 active로 보일 수 있습니다. 동일 규칙으로 맞추면 UX가 일관됩니다.- isActive = uiState.name.isNotEmpty(), + isActive = uiState.name.isNotBlank(),composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt (1)
104-112: isActive 조건 공백-only 방지 일치화에디터에서도 텍스트필드 활성 조건을 공백-only에서 비활성화하도록 맞추면 일관됩니다.
- isActive = uiState.name.isNotEmpty() && uiState.name != uiState.preName, + isActive = uiState.name.isNotBlank() && uiState.name != uiState.preName,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt(1 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.kt(2 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt(3 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt(1 hunks)composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt(3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.kt: Use the Kotlin official code style
Use immutable state with data classes
Files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
🧠 Learnings (7)
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/util/**/*.kt : Organize utility functions in the util directory
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/**/*.kt : When creating new UI components, use `CCHaksaTheme` for new features
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/component/**/*.kt : Prefer typography styles over hardcoded text styles
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/**/*.kt : Organize UI components and themes in the designsystem directory
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/preview/designsystem/**/*.kt : Preview components in the `preview/designsystem/` package
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.ktcomposeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/designsystem/theme/**/*.kt : Follow existing color naming conventions (100-600 scale) when creating new colors
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt
📚 Learning: 2025-08-03T02:53:00.607Z
Learnt from: CR
PR: cchaksa/cchaksa-kmp#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T02:53:00.607Z
Learning: Applies to composeApp/src/androidMain/kotlin/com/chukchukhaksa/mobile/preview/designsystem/**/*.kt : Organize previews for UI components in the preview/designsystem directory
Applied to files:
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
🧬 Code graph analysis (4)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt (1)
checkTimetableNameRule(9-11)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.kt (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt (1)
checkTimetableNameRule(9-11)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt (1)
checkOverTimetableNameLimit(5-7)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt (1)
checkOverTimetableNameLimit(5-7)
⏰ 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: build
🔇 Additional comments (1)
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputContract.kt (1)
3-3: 버튼 활성화 규칙 공통화 적용 👍
checkTimetableNameRule(name)로 버튼 활성화 조건을 통일한 점 좋습니다. 입력이 공백-only인 경우 비활성화되고, 길이 제한도 공유 규칙으로 일치합니다. 이 변경은 타 화면(에디터)과 일관성 있게 유지됩니다.Also applies to: 12-12
| const val TIMETABLE_NAME_LIMIT = 20 | ||
|
|
||
| fun checkOverTimetableNameLimit(name: String): Boolean { | ||
| return name.trim().length > TIMETABLE_NAME_LIMIT | ||
| } | ||
|
|
||
| fun checkTimetableNameRule(name: String): Boolean { | ||
| return name.isNotBlank() && name.trim().length <= TIMETABLE_NAME_LIMIT | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
문자 길이 기준(UTF-16 length vs. 사용자 체감 글자수) 확인 필요
현재 length 기준은 UTF‑16 코드 유닛 수입니다. 이모지/조합형 문자(예: 피부톤 이모지, 합성 플래그)에서는 사용자 체감 글자수와 어긋날 수 있습니다. 요구사항이 “시각적으로 20자”를 의미한다면, KMP 환경에서의 그라페메 클러스터 카운팅 전략(플랫폼별 ICU/Regex) 검토가 필요합니다. 현 단계에서는 정책 확정 전까지는 현 구현 유지 + 명확한 스펙 주석 추가를 권장합니다.
🧹 Nitpick (assertive)
불리언 규칙 함수 네이밍과 위치 일관성 개선 제안
- 네이밍:
checkOverTimetableNameLimit/checkTimetableNameRule는 결과가 true/false인 검증기이므로 술어형(is*)이 가독성이 더 좋습니다.
예:isTimetableNameOverLimit,isTimetableNameValid. - 패키지/위치: 본 파일은 확장 함수가 아닌 공통 유틸입니다. 기존 가이드(learned) 상 util 디렉터리로의 이동을 권장합니다:
common/util/...로 이동하면 검색성과 응집도가 좋아집니다.
아래와 같이 네이밍을 정리하면 호출부 가독성이 개선됩니다.
-const val TIMETABLE_NAME_LIMIT = 20
-
-fun checkOverTimetableNameLimit(name: String): Boolean {
- return name.trim().length > TIMETABLE_NAME_LIMIT
-}
-
-fun checkTimetableNameRule(name: String): Boolean {
- return name.isNotBlank() && name.trim().length <= TIMETABLE_NAME_LIMIT
-}
+const val TIMETABLE_NAME_LIMIT = 20
+
+fun isTimetableNameOverLimit(name: String): Boolean =
+ name.trim().length > TIMETABLE_NAME_LIMIT
+
+fun isTimetableNameValid(name: String): Boolean =
+ name.isNotBlank() && name.trim().length <= TIMETABLE_NAME_LIMIT호출부 변경은 본 PR의 두 Screen/두 Contract에서만 필요해 영향 범위가 작습니다. 원하시면 관련 호출부 diff도 함께 제안드리겠습니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const val TIMETABLE_NAME_LIMIT = 20 | |
| fun checkOverTimetableNameLimit(name: String): Boolean { | |
| return name.trim().length > TIMETABLE_NAME_LIMIT | |
| } | |
| fun checkTimetableNameRule(name: String): Boolean { | |
| return name.isNotBlank() && name.trim().length <= TIMETABLE_NAME_LIMIT | |
| } | |
| const val TIMETABLE_NAME_LIMIT = 20 | |
| fun isTimetableNameOverLimit(name: String): Boolean = | |
| name.trim().length > TIMETABLE_NAME_LIMIT | |
| fun isTimetableNameValid(name: String): Boolean = | |
| name.isNotBlank() && name.trim().length <= TIMETABLE_NAME_LIMIT |
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/extension/TimetableNameLimit.kt
around lines 3-11, the boolean validator functions use non-predicate names and
live in an extensions package though they are general utilities; rename
checkOverTimetableNameLimit -> isTimetableNameOverLimit and
checkTimetableNameRule -> isTimetableNameValid, keep the same logic, move the
file into common/util (e.g.,
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/common/util/TimetableNameLimit.kt)
and update the two Screens/Contracts that call these functions to use the new
names and import path.
| import com.chukchukhaksa.mobile.presentation.timetable.navigation.argument.TimetableEditorArgument | ||
| import com.chukchukhaksa.mobile.presentation.timetable.semesterselect.semesterList | ||
| import com.chukchukhaksa.mobile.presentation.timetable.semesterselect.Semester | ||
| import com.chukchukhaksa.mobile.common.extension.checkTimetableNameRule |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
에디터 버튼 활성화 규칙 공통화 적용 👍
checkTimetableNameRule(name)(= 공백 방지 + 트림 후 길이 제한)와 변경 유무 체크를 함께 적용한 구성은 명확합니다. 괄호는 중복이므로 간결화 여지는 있지만 기능상 문제는 없습니다.
- val buttonEnabled = ((checkTimetableNameRule(name)) && (preName != name || preSelectedSemesterPosition != selectedSemesterPosition))
+ val buttonEnabled = checkTimetableNameRule(name) &&
+ (preName != name || preSelectedSemesterPosition != selectedSemesterPosition)Also applies to: 17-17
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorContract.kt
around lines 6 and 17, there are redundant parentheses around the condition that
uses checkTimetableNameRule(name) together with the change-check; remove the
extra parentheses to simplify the boolean expression (e.g., change from
((checkTimetableNameRule(name)) && changed) or (checkTimetableNameRule(name) &&
changed) with extra surrounding parens to simply checkTimetableNameRule(name) &&
changed) while keeping the same logic and spacing.
| isError = checkOverTimetableNameLimit(uiState.name), | ||
| onValueChanged = onValueChangeTimetableName, | ||
| onClickClearButton = onClickTextFieldClearButton, | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 표시 조건 불일치와 하드코딩(20) 중복 — 입력 화면과 동일 이슈
입력 화면과 동일하게 isError(trim 기준)와 에러 텍스트 표시(원문 length > 20)의 기준이 달라집니다. 공통 함수/상수를 사용해 정합성을 맞춰주세요.
- isError = checkOverTimetableNameLimit(uiState.name),
+ isError = checkOverTimetableNameLimit(uiState.name),
...
- if (uiState.name.length > 20) {
+ if (checkOverTimetableNameLimit(uiState.name)) {
Text(
modifier = Modifier
.padding(top = 12.dp, start = 4.dp, end = 4.dp)
.fillMaxWidth(),
- text = "시간표 이름은 최대 20자까지 설정 가능합니다.",
+ text = "시간표 이름은 최대 ${TIMETABLE_NAME_LIMIT}자까지 설정 가능합니다.",
textAlign = TextAlign.Start,
style = CchTheme.typography.bodyMd,
color = Red300,
)
}필요한 import:
import com.chukchukhaksa.mobile.common.extension.checkOverTimetableNameLimit
+import com.chukchukhaksa.mobile.common.extension.TIMETABLE_NAME_LIMITAlso applies to: 114-124
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
around lines 109-112 (and also apply to 114-124), the isError check uses a
different criterion than the error-text visibility and the hardcoded limit 20 is
duplicated; refactor to a shared constant (e.g., TIMETABLE_NAME_MAX_LENGTH) and
a shared trim-aware validator function (e.g., isOverTimetableNameLimit(name:
String): Boolean) imported from the common validation/constants file, then
replace the inline isError and the length > 20 checks to use that single
function/constant so both the error flag and displayed message use the same
trim-based threshold; update imports to include the constant/function and adjust
any error message formatting to reference the constant instead of the hardcoded
20.
| if (uiState.name.length > 20) { | ||
| Text( | ||
| modifier = Modifier | ||
| .padding(top = 12.dp, start = 4.dp, end = 4.dp) | ||
| .fillMaxWidth(), | ||
| text = "시간표 이름은 최대 20자까지 설정 가능합니다.", | ||
| textAlign = TextAlign.Start, | ||
| style = CchTheme.typography.bodyMd, | ||
| color = Red300, | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
문구 하드코딩 → 리소스화 고려 (동일 제안)
입력 화면과 동일하게 문자열 리소스 사용을 권장합니다. 자리표시자에 TIMETABLE_NAME_LIMIT 주입 방식으로 국제화 대비가 용이합니다.
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetableeditor/TimetableEditorScreen.kt
around lines 114–124, the displayed Korean message is hardcoded; extract it into
a string resource (e.g., TIMETABLE_NAME_LIMIT) and replace the literal with a
localized lookup (use stringResource or your multiplatform resource helper and
inject the limit placeholder value). Add the new resource entry in your strings
resources (with a placeholder for the max length) and update the UI code to call
the resource with the max length (20) instead of the hardcoded text.
| isError = checkOverTimetableNameLimit(uiState.name), | ||
| onValueChanged = onValueChangeTimetableName, | ||
| onClickClearButton = onClickTextFieldClearButton, | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 표시 조건 불일치와 하드코딩(20) 중복 — 단일 소스 사용으로 정합성 보장 필요
- 텍스트필드
isError는checkOverTimetableNameLimit(uiState.name)(trim 기준)인데, 아래 에러 문구 표시는uiState.name.length > 20(trim 미적용)로 달라 UX 혼란이 생깁니다. - 메시지의 숫자 20이 하드코딩되어 유지보수성이 떨어집니다.
동일한 검증 함수와 공통 상수를 사용하도록 아래와 같이 정리해주세요.
- isError = checkOverTimetableNameLimit(uiState.name),
+ isError = checkOverTimetableNameLimit(uiState.name),
...
- if (uiState.name.length > 20) {
+ if (checkOverTimetableNameLimit(uiState.name)) {
Text(
modifier = Modifier
.padding(top = 12.dp, start = 4.dp, end = 4.dp)
.fillMaxWidth(),
- text = "시간표 이름은 최대 20자까지 설정 가능합니다.",
+ text = "시간표 이름은 최대 ${TIMETABLE_NAME_LIMIT}자까지 설정 가능합니다.",
textAlign = TextAlign.Start,
style = CchTheme.typography.bodyMd,
color = Red300,
)
}필요한 import 추가:
import com.chukchukhaksa.mobile.common.extension.checkOverTimetableNameLimit
+import com.chukchukhaksa.mobile.common.extension.TIMETABLE_NAME_LIMITAlso applies to: 113-123
| if (uiState.name.length > 20) { | ||
| Text( | ||
| modifier = Modifier | ||
| .padding(top = 12.dp, start = 4.dp, end = 4.dp) | ||
| .fillMaxWidth(), | ||
| text = "시간표 이름은 최대 20자까지 설정 가능합니다.", | ||
| textAlign = TextAlign.Start, | ||
| style = CchTheme.typography.bodyMd, | ||
| color = Red300, | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
문구 하드코딩 → 리소스화 고려
국제화/복수 플랫폼 대응을 위해 에러 메시지를 String 리소스로 이동하고 자리표시자에 상수를 주입하는 방식을 권장합니다.
예)
- strings:
timetable_name_over_limit = "시간표 이름은 최대 %1$d자까지 설정 가능합니다." - 코드:
text = stringResource(Res.string.timetable_name_over_limit, TIMETABLE_NAME_LIMIT)
현재 PR 범위를 최소화하려면 위의 상수 보간 버전으로 반영하고, 후속 PR에서 리소스화를 진행해도 됩니다.
🤖 Prompt for AI Agents
In
composeApp/src/commonMain/kotlin/com/chukchukhaksa/mobile/presentation/timetable/timetablenameinput/TimetableNameInputScreen.kt
around lines 113–123, replace the hardcoded Korean error text with a
stringResource that injects the max-length constant; change text = "시간표 이름은 최대
20자까지 설정 가능합니다." to text = stringResource(Res.string.timetable_name_over_limit,
TIMETABLE_NAME_LIMIT) (or similar project resource identifier), ensure
TIMETABLE_NAME_LIMIT constant is referenced/defined and import
stringResource/Res.string as needed so the message uses the constant placeholder
rather than hardcoded "20".
📌 PR 요약
🌱 작업한 내용
🌱 PR 포인트
📸 스크린샷
📮 관련 이슈
RCA 룰을 사용하여 코드 리뷰를 해주세요
R (Request Changes): 적극적으로 반영을 고려해주세요C (Comment): 웬만하면 반영해주세요A (Approve): 반영해도 좋고, 넘어가도 좋습니다. 사소한 의견입니다.Summary by CodeRabbit
신기능
버그 수정