-
Notifications
You must be signed in to change notification settings - Fork 0
[feature/#37,41,42] 채팅 인트로 구현, base url https로 변경, 스플래시 및 앱 아이콘 추가 #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- 아이콘 네모 현상 수정 필요
Walkthrough안드로이드 스플래시 화면·아이콘 리소스와 채팅방 생성 인트로 UI를 추가하고, 기본 프로토콜을 HTTP→HTTPS로 변경했으며 CI 및 플랫폼별 평문(HTTP) 예외(network_security_config.xml, Info.plist) 생성을 제거했습니다. 일부 네트워크 API에서 토큰 파라미터가 제거되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as 사용자
participant Android as Android OS
participant App as MainActivity
participant UI as Compose UI
Android->>App: 앱 시작
App->>App: installSplashScreen()
App->>Android: 스플래시 테마 표시
App->>UI: setContent()
UI-->>Android: UI 렌더링 (CreateRoom 또는 ChattingScreen)
Android-->>User: 스플래시 → 메인 화면 전환
sequenceDiagram
autonumber
participant VM as ChattingViewModel
participant UI as ChattingScreen
participant DS as DesignSystem
VM->>UI: state(messages, firstMessage, fairies, showFairyPager)
alt 메시지 수 ≤ 1
UI->>UI: CreateRoom 렌더(입력, 첫메시지 표시)
UI->>DS: ChatBubble(skipTypewriterEffect=true)
else 메시지 수 > 1
UI->>UI: 채팅 리스트 렌더, 페어리 페이저 동작
UI->>DS: ChatBubble(초기 항목에 skipTypewriterEffect=true)
UI->>VM: SelectFairy(currentPage)
end
UI->>VM: onInputTextChange / onSendClick
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (2)
3-3: 컴파일 오류 — 잘못된 import 구문
import EmotiaChatTextField는 패키지 없이 심볼만 가져오는 형태로 Kotlin에서 허용되지 않습니다. 정규 패키지를 명시하세요.-import EmotiaChatTextField +import com.nexters.emotia.core.designsystem.component.EmotiaChatTextField
135-141: 잠재적 NumberFormatException — roomId toInt 변환
uiState.roomId?.toInt() ?: 0는roomId가 숫자 문자열이 아닐 때 NFE로 크래시가 납니다. 안전 변환으로 교체하세요.- uiState.roomId?.toInt() ?: 0 + uiState.roomId?.toIntOrNull() ?: 0
🧹 Nitpick comments (18)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingViewModel.kt (3)
61-69: 빈 감정 칩 표시 가능성 — showEmotionChips=true인데 emotionOptions가 비어 있음채팅방 생성 직후
showEmotionChips = true로 켜지만, 동시에emotionOptions = persistentListOf()로 비워 두고 있어, UI에 “비어 있는 칩 영역”만 노출될 수 있습니다. 옵션 로딩 전까지는 칩을 숨기거나, 로딩이 끝난 뒤에만 켜는 편이 자연스럽습니다.다음처럼 기본값을 false로 두고, 실제 옵션을 주입할 때 토글하는 것을 권장합니다.
- showEmotionChips = true, - emotionOptions = persistentListOf(), + showEmotionChips = false, + emotionOptions = persistentListOf(),또는 reduce 이전에 임시 리스트를 만들어
showEmotionChips = options.isNotEmpty()로 일관되게 설정하세요.
55-60: 중복 상태 보관(firstMessage) — messages[0]와 firstMessage 동시 유지최초 메시지를
messages의 첫 요소로 넣는 동시에state.firstMessage로도 별도 보관합니다. 동일 데이터의 이중 소스는 추후 동기화 비용/버그 포인트가 됩니다. UI에서 “첫 메시지 존재 여부/내용”이 필요하다면, 장기적으로는messages.firstOrNull()?.text로 유도하고firstMessage는 제거하는 편이 단순합니다. 지금 PR 목적 상 유지가 필요하다면 TODO로 정리해두면 좋겠습니다.Also applies to: 61-69
30-31: 하드코딩 문자열을 리소스로 이전하세요다음 문자열은 다국어/일관성/테스트 용이성을 위해 Compose Resources(Res.string.*)로 이전하는 것이 좋습니다.
MAX_CHAT_REACHED_MESSAGE(Line 30)"채팅룸 생성에 실패했습니다: ..."(Line 73)"메시지 전송에 실패했습니다: ..."(Line 139)리소스 키(예:
chat_max_reached_message,error_create_room_failed,error_send_message_failed) 정의 후 교체해 주세요.Also applies to: 73-74, 139-140
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/contract/ChattingState.kt (1)
20-21: selectedFairyIndex 기본값 1은 놀랄 수 있음 — 0이 더 안전기본 리스트가 빈 상태이므로, 기본 인덱스는 일반적으로 0이 안전합니다. ViewModel/Screen에서
coerceIn으로 방어하고 있지만, 기본값이 1인 것은 오해 유발 여지가 있습니다. 초기 페이지를 1로 열고 싶다면 화면 레벨에서만 처리하고, 상태 기본값은 0을 권장합니다.- val selectedFairyIndex: Int = 1, + val selectedFairyIndex: Int = 0,core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTextField.kt (1)
123-127: 이중 보더(OutlinedTextField + Modifier.border)로 과도한 강조/오버드로우 가능외곽선(Modifier.border)과 내부 OutlinedTextField 보더가 동시에 그려져 시각적으로 두껍거나, 테마 변경 시 관리 포인트가 2곳으로 늘어납니다. 내부 보더를 투명 처리해 외곽선만 유지하는 편이 단순합니다.
적용 예:
- colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = state.getBorderColor(), - unfocusedBorderColor = state.getBorderColor(), - disabledBorderColor = state.getBorderColor(), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = Color.Transparent, + unfocusedBorderColor = Color.Transparent, + disabledBorderColor = Color.Transparent,또는 반대로 Modifier.border를 제거하고 내부 보더만 사용하도록 일원화하세요.
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (3)
156-166: 스크롤 인덱스 계산 재검토
showFairyPager가 true일 때lastIndex = uiState.messages.size로 스크롤합니다. 현재 구현은 messages 블록 뒤에 항상item { ... }를 추가하므로 유효 인덱스가 맞지만, 향후 아이템 구조 변경 시 범위를 벗어날 수 있습니다. 가장 안전한 패턴은 “실제 아이템 수 - 1”로 스크롤하거나, Lazy DSL의 고정 푸터 아이템 개수를 명시적으로 더해 계산하는 것입니다.예)
val extra = if (uiState.isLoading && uiState.messages.isNotEmpty()) 1 else 0// 타이핑 인디케이터val footer = 1// 요정 카드 컨테이너 itemanimateScrollToItem(uiState.messages.size + extra + footer - 1)
238-248: N^2 성능 — 메시지마다 indexOf 호출
uiState.messages.indexOf(message)를 각 아이템에서 호출하면 O(n^2) 입니다.itemsIndexed로 대체하여 인덱스를 바로 받으세요.- items( - items = uiState.messages, - key = { message -> message.timestamp } - ) { message -> - val messageIndex = uiState.messages.indexOf(message) + itemsIndexed( + items = uiState.messages, + key = { _, message -> message.timestamp } + ) { messageIndex, message -> ChatBubble( text = message.text, type = message.type, messageId = message.timestamp.toString(), skipTypewriterEffect = messageIndex <= 1 ) }
371-373: 하드코딩 문자열을 리소스로 이전다음 텍스트는 Compose Resources로 추출해 주세요. 다국어 및 테스트 용이성에 유리합니다.
- 버튼 라벨:
"내 감정은 ${uiState.fairies[selectedIndex].emotion}이야"- 링크 텍스트:
"다시 대화하기"- 플레이스홀더:
"요정에게 지금 기분을 설명해보자"(2곳)기존 테마/타이포그래피 그대로 유지되도록
stringResource+ 서브스트링 포맷으로 교체를 권장합니다.Also applies to: 380-391, 407-408, 538-539
core/network/src/commonMain/kotlin/com/nexters/emotia/network/EmotiaNetwork.kt (3)
40-43: Ktor Logging 레벨 ALL은 토큰/민감정보 노출 우려 — 릴리스 빌드에서 비활성화 권장현재 전역으로
LogLevel.ALL을 사용하면 요청/응답 바디/헤더가 로그에 남을 수 있습니다(Authorization 헤더 포함 가능). 빌드 타입/플랫폼별로 로그를 게이팅하세요.예시(개념 코드): KMP에서
expect/actual또는BuildKonfig로 디버그 플래그를 주입해 디버그에서만 설치.if (Platform.isDebug) { install(Logging) { logger = Logger.DEFAULT level = LogLevel.HEADERS // 최소화 또는 BODY 금지 } }
86-90:get(..., token: String?)매개변수는 더 이상 사용되지 않음 — 혼동 방지를 위해 제거 또는 Deprecate토큰은
Auth { bearer { ... } }로 처리되고 있어token매개변수가 무의미합니다. API 혼동을 줄이기 위해 Deprecate 후 제거를 제안합니다.- suspend inline fun <reified T : Any> get( - path: String, - token: String? = null, - ): T = httpClient.get(path) { - }.body() + @Deprecated("토큰은 Auth 플러그인이 처리합니다. token 인자는 곧 제거 예정입니다.", ReplaceWith("get<T>(path)")) + suspend inline fun <reified T : Any> get( + path: String, + token: String? = null, + ): T = httpClient.get(path) {}.body() + + // 또는 즉시 제거 + // suspend inline fun <reified T : Any> get(path: String): T = + // httpClient.get(path).body()
78-82: 기능 개선:baseUrl파싱을URLBuilder.takeFrom으로 일원화하고 파라미터명을 명확히 변경다음과 같이 리팩토링을 권장드립니다. 현재 호스트명만 지정하는
hostName대신 전체 URL을 입력받아 Ktor의takeFrom(...)으로 한 번에 안전하게 파싱하도록 일원화하고, 파라미터명도hostName→baseUrl로 바꿔 가독성과 의도를 명확히 합니다.– 변경 위치
- core/network/src/commonMain/kotlin/com/nexters/emotia/network/EmotiaNetwork.kt
– 주요 변경사항
- 함수 시그니처
- 기존
private fun createHttpClient(hostName: String): HttpClient = HttpClient {- 변경
private fun createHttpClient(baseUrl: String): HttpClient = HttpClient {defaultRequest내 URL 설정
- 기존
defaultRequest { contentType(ContentType.Application.Json) url { protocol = URLProtocol.HTTPS host = hostName } }- 변경
defaultRequest { contentType(ContentType.Application.Json) url { takeFrom(baseUrl) } }– 호출부 수정
EmotiaNetwork.kt29행- val httpClient = createHttpClient(NetworkConfig.baseUrl) + val httpClient = createHttpClient(NetworkConfig.baseUrl)(시그니처만 변경됐기 때문에 호출부는 그대로 유지됩니다.)
– 추가 검토
Android 쪽BuildConfig.BASE_URL(BuildConfig.android.kt)와 iOS 쪽NetworkConfigLocal.BASE_URL(NetworkConfig.ios.kt) 모두 스킴(https://) 포함 여부가 환경별로 다를 수 있으니, CI/CD 시크릿이나 Gradle 설정에서BASE_URL값에 스킴이 포함되어 있는지 한 번만 확인해주세요.--- a/core/network/src/commonMain/kotlin/com/nexters/emotia/network/EmotiaNetwork.kt +++ b/core/network/src/commonMain/kotlin/com/nexters/emotia/network/EmotiaNetwork.kt @@ -29,7 +29,7 @@ class EmotiaNetwork( val httpClient = createHttpClient(NetworkConfig.baseUrl) - private fun createHttpClient(hostName: String): HttpClient = HttpClient { + private fun createHttpClient(baseUrl: String): HttpClient = HttpClient { ... defaultRequest { contentType(ContentType.Application.Json) @@ -81,8 +81,5 @@ class EmotiaNetwork( url { - protocol = URLProtocol.HTTPS - host = hostName + takeFrom(baseUrl) } } }composeApp/build.gradle.kts (1)
65-71:debug빌드타입 블록이 중복 선언되어 있음 — 하나로 합치세요
debug { ... }와getByName("debug") { ... }가 나뉘어 있어 유지보수성이 떨어집니다. 한 블록으로 합치는 것이 명확합니다.- buildTypes { - debug { - isDebuggable = true - } - getByName("debug") { - isMinifyEnabled = false - } - } + buildTypes { + debug { + isDebuggable = true + isMinifyEnabled = false + } + }composeApp/src/androidMain/kotlin/com/nexters/emotia/MainActivity.kt (2)
14-16: installSplashScreen() 호출 위치는 좋으나, super.onCreate() 호출 순서 점검 권장권장 순서는
installSplashScreen()→super.onCreate()→enableEdgeToEdge()→setContent(...)입니다. 현재는enableEdgeToEdge()가super.onCreate()보다 앞에 와 있어, 일부 기기/테마 조합에서 윈도우 플래그 적용 타이밍 차이가 생길 수 있습니다.override fun onCreate(savedInstanceState: Bundle?) { - installSplashScreen() - - enableEdgeToEdge() - super.onCreate(savedInstanceState) + installSplashScreen() + super.onCreate(savedInstanceState) + enableEdgeToEdge()
14-15: 초기 로딩(온보딩/채팅 인트로 준비) 동안 스플래시 유지가 필요하면setKeepOnScreenCondition을 활용하세요채팅 인트로의 첫 메시지 프리패치/상태 초기화가 필요하다면 스플래시 유지 조건을 걸어 UX를 매끈하게 만들 수 있습니다.
예시:
val splash = installSplashScreen() super.onCreate(savedInstanceState) // viewModel의 준비 상태 플래그 사용 예시 splash.setKeepOnScreenCondition { !viewModel.isInitialized }composeApp/src/androidMain/AndroidManifest.xml (1)
9-12: icon/roundIcon이 밀도 고정 리소스를 가리킴 — 일반 alias 또는 어댑티브 아이콘으로 교체 권장현재
@mipmap/ic_launcher_xxxhdpi는 특정 밀도 전용입니다. 런처/스플래시에 동일 비트맵을 재사용하더라도, 매니페스트에는@mipmap/ic_launcher(및@mipmap/ic_launcher_round) 같은 일반 alias를 사용하세요. 어댑티브 아이콘(anydpi-v26) 복원 시 동적 마스크/머티리얼 유연성이 좋아집니다.- android:icon="@mipmap/ic_launcher_xxxhdpi" + android:icon="@mipmap/ic_launcher" ... - android:roundIcon="@mipmap/ic_launcher_xxxhdpi" + android:roundIcon="@mipmap/ic_launcher_round"작성자 코멘트대로 라운드 아이콘은 추후 조정 예정이라고 하셨으니, 최소한 위 변경으로 안전한 기본값만 먼저 적용하는 것을 제안드립니다.
gradle/libs.versions.toml (1)
33-33: SplashScreen 의존성 안정 버전 사용 검토 요청gradle/libs.versions.toml 파일의 아래 라인을 확인해 주세요:
androidx-splashscreen = "1.2.0-alpha02"– 최신 RC 릴리즈인 1.2.0-rc01(2025-07-02) 또는 완전 안정(release) 릴리즈인 1.0.1(2023-04) 사용을 권장합니다.
– 특별히 알파 기능이 꼭 필요하다면, 해당 이유를 PR 설명에 명시해 주세요.Possible replacements:
• androidx-splashscreen = "1.2.0-rc01"
• androidx-splashscreen = "1.0.1"composeApp/src/androidMain/res/values/themes.xml (2)
4-8: Splash 테마 구성은 적절 — 다크 모드 대비(white flash)용 values-night 오버레이 추가 제안현재 Splash 배경이 고정 흰색이므로 다크 모드에서 “화이트 플래시”가 발생할 수 있습니다. values-night에 동일 스타일을 정의해 어두운 배경을 지정하는 것을 권장합니다.
예시(새 파일:
composeApp/src/androidMain/res/values-night/themes.xml):<?xml version="1.0" encoding="utf-8"?> <resources> <style name="Theme.Emotia.SplashScreen" parent="Theme.SplashScreen"> <item name="windowSplashScreenBackground">@android:color/black</item> <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash_screen</item> <item name="postSplashScreenTheme">@style/Theme.Emotia.Main</item> </style> </resources>추가로 필요 시 애니메이션 지속시간/아이콘 배경 컬러도 조절 가능합니다:
- item name="windowSplashScreenAnimationDuration"
- item name="windowSplashScreenIconBackgroundColor"
10-16: Edge-to-Edge는 WindowInsets 기반 접근 권장 — legacy translucent 플래그 제거 검토
android:windowTranslucentStatus/Navigation=true는 레거시 방식입니다. Compose 환경에서는WindowCompat.setDecorFitsSystemWindows(window, false)+ 시스템 바 inset 처리로 일관되게 Edge-to-Edge를 구현하는 편이 안정적입니다. 해당 플래그 제거 및 코드 측면 처리로의 전환을 검토해 주세요.전환 시 다음을 점검해 주세요:
- 루트 컨테이너에서
WindowInsets소비 여부- 라이트/다크 모드에 따른 status bar 아이콘 대비(
android:windowLightStatusBar)- 내비게이션 바 색상/아이콘 대비
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
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 ignored due to path filters (16)
composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_hdpi.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_mdpi.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_xhdpi.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_xxhdpi.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.pngis excluded by!**/*.pngcomposeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_xxxhdpi.pngis excluded by!**/*.pngcore/designsystem/src/commonMain/composeResources/drawable/img_chatting_fairy.pngis excluded by!**/*.png
📒 Files selected for processing (22)
.github/workflows/ace_build.yml(0 hunks).github/workflows/ace_firebase_distribution.yml(0 hunks)composeApp/build.gradle.kts(1 hunks)composeApp/src/androidMain/AndroidManifest.xml(1 hunks)composeApp/src/androidMain/kotlin/com/nexters/emotia/MainActivity.kt(1 hunks)composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml(1 hunks)composeApp/src/androidMain/res/drawable/ic_launcher_background.xml(1 hunks)composeApp/src/androidMain/res/drawable/ic_splash_screen.xml(1 hunks)composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml(0 hunks)composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml(0 hunks)composeApp/src/androidMain/res/values/themes.xml(1 hunks)composeApp/src/main/res/xml/network_security_config.xml(0 hunks)core/data/chatting/src/commonMain/kotlin/com/nexters/emotia/core/data/chatting/datasource/ChattingRemoteDataSourceImpl.kt(0 hunks)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatBubble.kt(3 hunks)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTextField.kt(2 hunks)core/network/src/commonMain/kotlin/com/nexters/emotia/network/EmotiaNetwork.kt(1 hunks)core/network/src/commonMain/kotlin/com/nexters/emotia/network/service/ChatApiService.kt(0 hunks)feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt(6 hunks)feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingViewModel.kt(1 hunks)feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/contract/ChattingState.kt(1 hunks)gradle/libs.versions.toml(2 hunks)iosApp/iosApp/Info.plist(0 hunks)
💤 Files with no reviewable changes (8)
- .github/workflows/ace_firebase_distribution.yml
- iosApp/iosApp/Info.plist
- core/network/src/commonMain/kotlin/com/nexters/emotia/network/service/ChatApiService.kt
- composeApp/src/main/res/xml/network_security_config.xml
- core/data/chatting/src/commonMain/kotlin/com/nexters/emotia/core/data/chatting/datasource/ChattingRemoteDataSourceImpl.kt
- composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml
- composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml
- .github/workflows/ace_build.yml
🧰 Additional context used
📓 Path-based instructions (4)
composeApp/build.gradle*
📄 CodeRabbit inference engine (CLAUDE.md)
composeApp/build.gradle*: Common dependencies in commonMain.dependencies
Platform-specific dependencies in respective androidMain/iosMain blocks
Static iOS frameworks for better performance
Resource exclusions configured for Android packaging
Files:
composeApp/build.gradle.kts
**/build.gradle*
📄 CodeRabbit inference engine (CLAUDE.md)
Use libs.* references for dependency declarations
Files:
composeApp/build.gradle.kts
gradle/libs.versions.toml
📄 CodeRabbit inference engine (CLAUDE.md)
Version catalog managed in gradle/libs.versions.toml
Files:
gradle/libs.versions.toml
composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt: Follow expect/actual pattern for platform-specific code
Use @composable functions with @Preview annotations
Leverage Compose resources via Res.drawable.* and Res.string.*
Use expect/actual for platform-specific APIs (camera, notifications, etc.)
Use Compose Resources for strings, images, and other assets
Files:
composeApp/src/androidMain/kotlin/com/nexters/emotia/MainActivity.kt
🧠 Learnings (13)
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/build.gradle* : Platform-specific dependencies in respective androidMain/iosMain blocks
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/build.gradle* : Common dependencies in commonMain.dependencies
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Use Compose Resources for strings, images, and other assets
Applied to files:
composeApp/build.gradle.ktscomposeApp/src/androidMain/res/drawable/ic_launcher_background.xmlcomposeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xmlfeature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.ktcomposeApp/src/androidMain/res/drawable/ic_splash_screen.xml
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/build.gradle* : Resource exclusions configured for Android packaging
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/build.gradle* : Static iOS frameworks for better performance
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Use Composable functions with Preview annotations
Applied to files:
composeApp/build.gradle.ktscomposeApp/src/androidMain/kotlin/com/nexters/emotia/MainActivity.kt
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Leverage Compose resources via Res.drawable.* and Res.string.*
Applied to files:
composeApp/build.gradle.ktscomposeApp/src/androidMain/res/drawable/ic_launcher_background.xmlcomposeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xmlfeature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.ktcomposeApp/src/androidMain/res/drawable/ic_splash_screen.xml
📚 Learning: 2025-08-10T08:22:45.334Z
Learnt from: sxunea
PR: Nexters/team-ace-client#22
File: feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt:26-31
Timestamp: 2025-08-10T08:22:45.334Z
Learning: In Kotlin Multiplatform (KMP) projects, `org.koin.compose.viewmodel.koinViewModel()` from Koin is now compatible with commonMain source sets and can be used across all platforms, not just Android.
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-08-10T08:22:45.334Z
Learnt from: sxunea
PR: Nexters/team-ace-client#22
File: feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt:26-31
Timestamp: 2025-08-10T08:22:45.334Z
Learning: In Kotlin Multiplatform (KMP) projects, `androidx.lifecycle.compose.collectAsStateWithLifecycle` is now compatible with commonMain source sets and can be used across all platforms, not just Android.
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Use expect/actual for platform-specific APIs (camera, notifications, etc.)
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Follow expect/actual pattern for platform-specific code
Applied to files:
composeApp/build.gradle.kts
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to gradle/libs.versions.toml : Version catalog managed in gradle/libs.versions.toml
Applied to files:
gradle/libs.versions.toml
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/commonMain/kotlin/**/*.kt : Use common state management patterns (ViewModel, StateFlow)
Applied to files:
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt
🧬 Code graph analysis (2)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (5)
core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatBubble.kt (1)
ChatBubble(116-161)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTypingIndicator.kt (1)
TypingIndicator(28-44)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/FairyCard.kt (1)
FairyCard(38-138)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/EmotiaButton.kt (1)
EmotiaButton(16-42)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTextField.kt (1)
EmotiaChatTextField(93-170)
core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatBubble.kt (1)
core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/Typewriter.kt (1)
TypewriterText(29-72)
⏰ 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). (2)
- GitHub Check: build
- GitHub Check: Firebase App Distribution
🔇 Additional comments (10)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/contract/ChattingState.kt (1)
21-22: LGTM — firstMessage 필드 추가
copy/equals/serialization에도 자연스럽게 포함되어 사용성이 좋습니다. 기본값이 있어 역호환에도 문제 없습니다.core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTextField.kt (1)
131-135: 플레이스홀더 타이포그래피 조정 LGTM
emotia14M→emotia14R로 placeholder 가독성/위계가 개선됩니다. 문제 없습니다.core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatBubble.kt (2)
65-85: 타자기 효과 스킵 토글 추가 매우 적절
skipTypewriterEffect로 초기 노출 메시지의 애니메이션을 제어할 수 있어 요구사항에 부합합니다. 기본값을 false로 둔 것도 ABI/기본 동작 호환에 안전합니다.
156-158: 파라미터 전달 일관성 OK
ChatBubble에서ChatBubbleText로skipTypewriterEffect를 그대로 위임하고 있어 호출부 제어가 명확합니다.feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (2)
185-200: 초기 분기(UI 전환) 로직 의도 적합 — LGTM
messages.size <= 1인 경우 인트로(CreateRoom)를 노출하고, 첫 사용자 전송 이후 채팅 화면으로 전환되는 플로우가 요구사항과 일치합니다. 입력 활성화 조건도 인트로에서firstMessage수신 이후로 제한되어 UX 측면에서 적합합니다.
534-541: 인트로 입력 활성화 조건 적절
enabled = !isLoading && firstMessage.isNotEmpty()로 서버 최초 메시지 수신 전 입력을 막는 것은 자연스러운 UX입니다. 이후 본 채팅 화면에서는showFairyPager여부로 입력 영역을 토글하여 흐름이 깨지지 않습니다.composeApp/build.gradle.kts (1)
26-27: SplashScreen/Core KTX 의존성 추가는 적절합니다안드로이드 스플래시 API 사용 및 KTX 유틸 사용을 위해 필요한 의존성이며, 버전 카탈로그(libs.*) 규칙도 준수하고 있습니다.
composeApp/src/androidMain/AndroidManifest.xml (2)
13-13: Splash 테마 적용은 적절합니다
Theme.Emotia.SplashScreen적용이installSplashScreen()과 맞물려 정상 동작할 구성입니다. 후속 전환 테마(postSplashScreenTheme)만 실제 메인 테마와 일치하는지 재확인해 주세요.
9-13: 하드코딩된 HTTP 호출 없음 확인 — networkSecurityConfig 제거 유지 가능검사 결과, 코드베이스 내에 실제 네트워크 호출로서의
http://하드코딩은 존재하지 않습니다. 발견된 항목들은 모두 XML 네임스페이스 선언이나 주석 내 문서 참조이며, 런타임 시 평문 HTTP 요청을 발생시키지 않습니다.
- iosApp/iosApp/Info.plist (라인 2):
<!DOCTYPE … "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
• XML DTD 참조- AndroidManifest 및 리소스 XML들:
•xmlns:android="http://schemas.android.com/apk/res/android"등 네임스페이스 선언- .gitignore (라인 70, 185):
• 주석 내http://www.java.com/en/download/help/error_hotspot.xml(문서 링크)따라서
networkSecurityConfig제거가 의도한 대로 적용된 상태이며, 레거시 HTTP 호출은 더 이상 없습니다.gradle/libs.versions.toml (1)
58-58: 의존성 alias 추가 적절 — 설치 지점과 일관성 확인group/name/version.ref 모두 올바르며, composeApp/build.gradle.kts에서
libs.androidx.core.splashscreen을 실제로 참조하는 것으로 보입니다. 별도 이슈 없어요.혹시 멀티모듈이라면 사용 모듈 전반에서 버전 카탈로그 alias를 일관되게 사용 중인지 점검 부탁드립니다.
| <?xml version="1.0" encoding="utf-8"?> | ||
| <bitmap xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:src="@mipmap/ic_launcher_xxxhdpi" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foreground를 단일 …xxxhdpi 비트맵에 고정하지 마세요 — density-agnostic 참조로 교체
ic_launcher_foreground 또한 특정 density 비트맵을 고정 참조하고 있습니다. 아래처럼 @mipmap/ic_launcher로 바꾸어 기기별 최적 밀도를 사용하도록 해 주세요.
또한 가능하면 Adaptive Icon 전경을 벡터/레이어드로 구성하는 것이 마스크/적응형 처리에 유리합니다.
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@mipmap/ic_launcher_xxxhdpi" />
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@mipmap/ic_launcher"
+ android:gravity="center" />참고: minSdk 24로 drawable-v24만 두어도 동작은 하지만, 아이콘/스플래시는 안드로이드 26+에서 adaptive 구성이 권장됩니다.
📝 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.
| <?xml version="1.0" encoding="utf-8"?> | |
| <bitmap xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:src="@mipmap/ic_launcher_xxxhdpi" /> | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <bitmap xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:src="@mipmap/ic_launcher" | |
| android:gravity="center" /> |
🤖 Prompt for AI Agents
In composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml lines
1-3, the bitmap currently references a specific density mipmap
(@mipmap/ic_launcher_xxxhdpi); replace that density-specific reference with a
density-agnostic one (e.g., @mipmap/ic_launcher) so the system picks the correct
density at runtime, and if possible convert the foreground to a vector or
layered drawable for adaptive icon support (keeping this file under drawable-v24
for API24+ behavior).
| <bitmap xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:src="@mipmap/ic_launcher_xxxhdpi" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
밀도 고정된 mipmap(…xxxhdpi) 직접 참조는 스케일링/블러 유발 — density-agnostic 참조 또는 Adaptive Icon으로 전환 권장
현재 @mipmap/ic_launcher_xxxhdpi를 직접 지정하여 모든 기기에서 xxxhdpi 비트맵이 스케일링됩니다. 다양한 density에서 블러링/aliasing이 발생할 수 있습니다. 최소한 density-agnostic 참조(@mipmap/ic_launcher)로 변경해 시스템이 적절한 리소스를 선택하도록 하거나, 가능한 경우 Android 권장 방식의 Adaptive Icon으로 회귀하는 것을 권장합니다.
다음과 같이 수정해 주세요(간편 개선안):
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@mipmap/ic_launcher_xxxhdpi" />
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@mipmap/ic_launcher"
+ android:gravity="center" />가능하면 Adaptive Icon(배경/전경 분리) 복구도 고려해 주세요:
res/mipmap-anydpi-v26/ic_launcher.xml(adaptive-icon) 재도입- Manifest의
android:icon및android:roundIcon을@mipmap/ic_launcher계열로 지정
📝 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.
| <bitmap xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:src="@mipmap/ic_launcher_xxxhdpi" /> | |
| <bitmap xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:src="@mipmap/ic_launcher" | |
| android:gravity="center" /> |
| <item android:gravity="center"> | ||
| <bitmap | ||
| android:src="@mipmap/ic_launcher_xxxhdpi" | ||
| android:gravity="center" /> | ||
| </item> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
밀도 고정 리소스(@mipmap/ic_launcher_xxxhdpi) 직접 참조는 비권장 — 일반 alias로 변경 권장
특정 밀도 전용 리소스를 직접 참조하면 다른 밀도 기기에서 스케일링 저하/블러가 날 수 있습니다. @mipmap/ic_launcher 같은 밀도-독립 alias로 바꿔주세요.
- android:src="@mipmap/ic_launcher_xxxhdpi"
+ android:src="@mipmap/ic_launcher"추가로, 장기적으로는 어댑티브 아이콘(adaptive-icon, anydpi-v26)을 복원해 라운드/스쿼클 등 시스템 마스크에 매끄럽게 대응하는 것을 권장합니다.
📝 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.
| <item android:gravity="center"> | |
| <bitmap | |
| android:src="@mipmap/ic_launcher_xxxhdpi" | |
| android:gravity="center" /> | |
| </item> | |
| <item android:gravity="center"> | |
| <bitmap | |
| android:src="@mipmap/ic_launcher" | |
| android:gravity="center" /> | |
| </item> |
🤖 Prompt for AI Agents
In composeApp/src/androidMain/res/drawable/ic_splash_screen.xml around lines
5-9, the splash XML directly references a density-specific resource
(@mipmap/ic_launcher_xxxhdpi) which can cause scaling/blur on other devices;
replace that reference with the density-independent alias (@mipmap/ic_launcher)
so the system picks the correct density version, and verify the
@mipmap/ic_launcher alias exists in mipmap resources; additionally, plan to
restore an adaptive icon in anydpi-v26 (adaptive-icon) later so system masks
(round/squircle) are handled properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (1)
3-3: 컴파일 오류: EmotiaChatTextField import 경로가 잘못되었습니다Kotlin import에는 FQCN이 필요합니다. 현재
import EmotiaChatTextField는 패키지 없이 선언되어 컴파일이 실패합니다. 디자인시스템 컴포넌트의 정식 패키지 경로로 교체해 주세요.다음 diff를 적용해 주세요:
-import EmotiaChatTextField +import com.nexters.emotia.core.designsystem.component.EmotiaChatTextField
🧹 Nitpick comments (8)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (8)
203-215: 내부 레이아웃에 외부 modifier 재사용 — 중복 적용 및 부작용 가능성루트 Box에서 이미
modifier.fillMaxSize()를 적용했고, 내부 Column에서도 동일한modifier를 다시 사용하고 있습니다. 상위에서 전달된 semantics나 테스트 태그 등이 이중 적용될 수 있으므로 내부에는Modifier를 사용하세요.- Column( - modifier = modifier + Column( + modifier = Modifier .fillMaxSize() .background( brush = Brush.verticalGradient( colors = listOf( colors.backgroundBlue, Color.Black ) ) ) .padding(16.dp) .safeDrawingPadding() // 화면 상단의 노치 등 안전 영역 패딩 ) {
238-248: 메시지 인덱스 계산에 O(n²) 비용 — itemsIndexed로 단순화
uiState.messages.indexOf(message)가 각 아이템마다 O(n)으로 호출되므로 전체 O(n²)입니다.itemsIndexed를 사용하면 인덱스를 바로 받아 성능과 가독성이 개선됩니다.- items( - items = uiState.messages, - key = { message -> message.timestamp } - ) { message -> - val messageIndex = uiState.messages.indexOf(message) - ChatBubble( - text = message.text, - type = message.type, - messageId = message.timestamp.toString(), - skipTypewriterEffect = messageIndex <= 1 - ) - } + itemsIndexed( + items = uiState.messages, + key = { index, message -> message.timestamp } + ) { index, message -> + ChatBubble( + text = message.text, + type = message.type, + messageId = message.timestamp.toString(), + skipTypewriterEffect = index <= 1 + ) + }추가로 필요한 import:
import androidx.compose.foundation.lazy.itemsIndexed
239-241: 리스트 key 안정성 점검 권장
key = { message -> message.timestamp }는 타임스탬프 충돌 시(동시 전송 등) 재활용 이슈를 유발할 수 있습니다. 고유id가 있다면id로 교체하고, 없다면"$timestamp-$index"형태로 보강하는 것을 권장합니다.
407-409: 문자열 하드코딩 제거 — Compose Resources(Res.string)로 리소스화placeholder 문자열이 하드코딩되어 있습니다. 다국어/일관성(learned guideline: Compose Resources 활용)을 위해 string 리소스로 옮겨 주세요.
- placeholder = "요정에게 지금 기분을 설명해보자", + placeholder = stringResource(Res.string.chat_input_placeholder),- placeholder = "요정에게 지금 기분을 설명해보자", + placeholder = stringResource(Res.string.chat_input_placeholder),필요 import:
import org.jetbrains.compose.resources.stringResourcestrings 추가(예시):
- 키:
chat_input_placeholder- 값(ko):
요정에게 지금 기분을 설명해보자원하시면 리소스 파일 추가 PR 패치를 함께 드리겠습니다.
Also applies to: 541-543
486-491: 접근성(a11y): 요정 이미지 contentDescription 보강 권장요정 이미지는 핵심 UI 요소로 보입니다. 현재
contentDescription = null이라 스크린리더에서 무시됩니다. 의미 있는 설명을 제공하거나 의도적으로 장식 이미지라면semantics { invisibleToUser() }등으로 명확히 처리해 주세요.예시:
- contentDescription = null, + contentDescription = stringResource(Res.string.cd_fairy_character),strings 예시:
- 키:
cd_fairy_character- 값(ko):
요정 캐릭터
506-509: 디자인 토큰 미사용(하드코딩 색상) — 테마 컬러 사용 권장
Color(0xE62D2B38)등 하드코딩 색상은 유지보수와 다크모드 대응에 불리합니다. 디자인시스템의LocalEmotiaColors혹은 지정된 토큰으로 대체해 주세요.
224-236: IME 처리 방식 일관화 제안채팅 화면은
contentPadding에 IME 높이를 수동 반영(라인 224–236), 인트로 화면은.imePadding()으로 처리(라인 461–465)하고 있습니다. 두 화면 모두.imePadding()을 사용하거나 공통 유틸로 정리하면 단순하고 안전합니다.Also applies to: 461-465
185-201: 인트로 분기 로직은 요구사항과 부합합니다
uiState.messages.size <= 1일 때 인트로 UI를 노출하는 분기, 그리고 본 채팅 진입 전 첫 두 메시지에 대한 타자기 효과 skip은 PR 목적과 일치합니다. 이후 확장성을 위해val isIntro = ...와 같은 명명된 플래그로 추출해 두면 가독성이 더 좋아집니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
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 (1)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt(6 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Leverage Compose resources via Res.drawable.* and Res.string.*
Applied to files:
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/{commonMain,androidMain,iosMain}/kotlin/**/*.kt : Use Compose Resources for strings, images, and other assets
Applied to files:
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt
📚 Learning: 2025-07-27T10:25:59.389Z
Learnt from: CR
PR: Nexters/team-ace-client#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-27T10:25:59.389Z
Learning: Applies to composeApp/src/commonMain/kotlin/**/*.kt : Use common state management patterns (ViewModel, StateFlow)
Applied to files:
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt
🧬 Code graph analysis (1)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (5)
core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatBubble.kt (1)
ChatBubble(116-161)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTypingIndicator.kt (1)
TypingIndicator(28-44)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/FairyCard.kt (1)
FairyCard(38-138)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/EmotiaButton.kt (1)
EmotiaButton(16-42)core/designsystem/src/commonMain/kotlin/com/nexters/emotia/core/designsystem/component/ChatTextField.kt (1)
EmotiaChatTextField(93-170)
⏰ 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: Firebase App Distribution
🔇 Additional comments (2)
feature/chatting/src/commonMain/kotlin/com/nexters/emotia/feature/chatting/ChattingScreen.kt (2)
414-447: 스포트라이트 전환 연출과 터치 차단 레이어 구성 좋습니다
ClipOp.Difference로 원형 마스크를 적용하고, 터치 차단을 위한 투명 clickable 레이어를 둔 구성은 의도대로 동작할 것으로 보입니다.isSpotlightAnimating전환과 내비게이션 시퀀스도 자연스럽습니다.
70-73: 검증 필요: Generated Resources 패키지 경로 일치 여부 확인현재 ChattingScreen.kt를含む여러 모듈에서
import emotia.core.designsystem.generated.resources.Res import emotia.core.designsystem.generated.resources.img_chatting_fairy import emotia.core.designsystem.generated.resources.img_letter_background와 같이
emotia.core.designsystem.generated.resources패키지를 사용하고 있습니다.
그러나 프로젝트 네이밍 컨벤션에 따라com.nexters.emotia.core.designsystem.generated.resources일 가능성이 제기되었으므로, 실제로 생성된 리소스 파일의package선언과 import 경로가 일치하는지 반드시 확인해야 합니다.• 아래 스크립트로 빌드된/generated 디렉터리에서 패키지 선언을 검색해 보세요.
#!/usr/bin/env bash # build/generated 또는 src/main/generated 하위에서 package 선언 확인 rg -nP 'package\s+[\w\.]+\.generated\.resources' -n build/generated -n src/main/generated• 검증 결과 실제 패키지 경로가
com.nexters.emotia.core.designsystem.generated.resources라면,
ChattingScreen.kt 포함 모든 사용 파일의 import를 다음과 같이 변경해 주세요.-import emotia.core.designsystem.generated.resources.Res -import emotia.core.designsystem.generated.resources.img_chatting_fairy -import emotia.core.designsystem.generated.resources.img_letter_background +import com.nexters.emotia.core.designsystem.generated.resources.Res +import com.nexters.emotia.core.designsystem.generated.resources.img_chatting_fairy +import com.nexters.emotia.core.designsystem.generated.resources.img_letter_background• 반대로 실제 패키지 선언이
emotia.core.designsystem.generated.resources인 경우라면,
제안된 변경은 불필요하니 그대로 유지하시면 됩니다.위 검증 과정을 통해 import 경로가 실제 패키지 구조와 일치하는지 확인해 주시기 바랍니다.
haeti-dev
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
깔끔하네요 고생하셨습니다~
작업 내용
이건 꼭 봐주세요
Summary by CodeRabbit
New Features
Style
Chores