Skip to content

Conversation

@easyhooon
Copy link
Contributor

@easyhooon easyhooon commented Jan 3, 2026

🔗 관련 이슈

📙 작업 설명

  • 추가된 색상 변수 추가
  • 독서 기록 상세 화면 UI 반영 사항 반영
  • 다크 모드 프리뷰 제거(다크 모드 현재 지원 X, 프리뷰 볼때 걸리적 거림)
  • 독서 기록 상세 화면 상황별 프리뷰 추가
  • record 모듈 stability check stable 로 개선(map -> persistentMap)
  • detail 모듈 stability check stable 로 개선(ReadingRecordModel @Stable ->@Immutable)

🧪 테스트 내역 (선택)

  • 주요 기능 정상 동작 확인
  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
  • 기존 기능 영향 없음

📸 스크린샷 또는 시연 영상 (선택)

image

💬 추가 설명 or 리뷰 포인트 (선택)

  • 최다 감정 선출에 있어 기획 결정에 따라 서버 API 변경이 필요할 것으로 보여 API 변경 사항 반영시 추가 PR 올려서 대응하도록 하겠습니다
  • 삭제된 코드가 많은데 보니까 stability-compose-analyzer가 버전 업데이트됨에 따라 preview composable의 stability가 dump되는 것을 제외시켰다는군요 참고바랍니다.
    https://github.com/skydoves/compose-stability-analyzer/releases/tag/0.5.2

Summary by CodeRabbit

  • New Features
    • 기타(ETC) 감정 지원 추가(색상·이미지·설명·비율 색상 매핑)
    • 책 상세: 통계 확장/축소 토글, 통계 카드·비율 차트, 레코드 항목 클릭 동작 추가
  • Design System
    • 오렌지·바이올렛 팔레트 및 ETC 색상 토큰 추가
    • 버튼·테마 네이밍/스타일 체계 정비(Reed…)
    • 다크 프리뷰 제거로 라이트 프리뷰만 유지
  • Bug Fixes
    • 감정 이미지 널 안전성 개선, 모델 불변성 강화(@immutable)
  • API / State
    • 독서 기록 API v2 적용 및 읽기 레코드 감정 구조 변경(주감정·세부감정)
    • 레코드 등록 상태 Map → PersistentMap 전환

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

Walkthrough

ETC 감정(기타) 추가, 디자인 시스템 색상 토큰 확장, 버튼/칩 API 리네이밍, BookDetail에 isStatsExpanded 토글 도입 및 CollectedSeeds/RecordItem UI 변경, 레코드 등록 상태 Map→PersistentMap 전환, 다수 Preview 제거 및 안정성 어노테이션 업데이트.

Changes

응집군 / 파일(s) 변경 요약
색상 토큰 추가
core/designsystem/src/main/kotlin/.../theme/Color.kt
Orange50–Orange900, Violet50–Violet900, EtcTextColor, EtcBgColor 신규 선언
Emotion 리소스·매핑 확장
core/model/src/main/kotlin/.../SeedModel.kt,
core/designsystem/src/main/kotlin/.../Emotion.kt,
core/designsystem/src/main/res/values/strings.xml
Emotion.ETC("기타") 추가; ETC에 대한 bg/text color, 그래픽(및 nullable graphicResV2), ratioBarColor, emotion_etc_description 문자열 추가
디자인시스템 API 리네이밍/재정의
core/designsystem/stability/designsystem.stability
ReedButton/ReedTextButton 및 스타일 접근자/테마 접근자명 변경과 안정성 어노테이션 보강(공용 함수 시그니처/명칭 재정의)
프리뷰 블록/Preview 제거
core/designsystem/src/main/kotlin/.../ComponentPreview.kt,
core/designsystem/src/main/kotlin/.../DevicePreview.kt,
**/*/stability/*.stability
다크 프리뷰 블록 및 다수의 private/public @Preview 제거(안정성 덤프 항목 축소)
BookDetail 상태·이벤트 확장
feature/detail/.../BookDetailPresenter.kt,
feature/detail/.../BookDetailUiState.kt
isStatsExpanded: Boolean 필드 추가 및 OnStatsToggleClick 이벤트 도입, Presenter에서 상태 보존·반영
CollectedSeeds UI 확장
feature/detail/.../CollectedSeeds.kt
접힘/펼침(AnimatedVisibility) 도입, 헤더·EmotionRatioBar·EmotionStatCard 추가, isStatsExpanded·onToggleClick 파라미터 수용
RecordItem · BookDetail UI 변경
feature/detail/.../RecordItem.kt,
feature/detail/.../BookDetailUi.kt
onRecordClick 콜백 공개(클릭 처리 위임), 감정 이미지 대신 텍스트/페이지/날짜 표시로 변경, ETC 이미지 매핑 추가
SeedItem 감정 매핑 추가
feature/detail/.../SeedItem.kt
Emotion.ETCR.drawable.img_etc 매핑 추가
레코드 등록 상태 컬렉션 변경
feature/record/.../RecordRegisterPresenter.kt,
feature/record/.../RecordRegisterUiState.kt
Map<Emotion,ImmutableList<String>>PersistentMap<Emotion,ImmutableList<String>>, 초기화 persistentMapOf()로 변경 및 사용 방식 수정
Emotion 이미지 안전성 강화
feature/record/.../EmotionItem.kt
graphicResV2 null 체크 추가, 조건부 Image 렌더링으로 NPE 방지
모델·네트워크 구조 변경
core/network/.../ReadingRecordsResponse.kt,
core/data/impl/.../ResponseToModel.kt,
core/data/.../ReadingRecordsModel.kt
네트워크 ReadingRecord의 emotionTagsprimaryEmotion/detailEmotions 구조로 변경, 모델 매핑 함수 및 내부 모델(PrimaryEmotionModel, DetailEmotionModel) 추가, @Stable@Immutable 일부 적용
엔드포인트 버전 업그레이드
core/network/src/main/kotlin/.../ReedService.kt
reading-records 경로의 API 버전 v1 → v2(경로 어노테이션 변경)
빌드·도구·CI 업데이트
gradle/libs.versions.toml,
.github/workflows/android-ci.yml,
gradle/wrapper/gradle-wrapper.properties
여러 라이브러리 및 Kotlin/KSP/Compose 버전 업데이트, stabilityDump CI 단계 추가, Gradle wrapper 버전 갱신

Sequence Diagram(s)

(생성 기준 미충족 — 다이어그램 생략)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45분

Possibly related PRs

Suggested reviewers

  • seoyoon513

Poem

"🐰 팔레트에 새 빛 하나, '기타'가 깃들었네.
버튼 이름은 옷을 갈아입고 칩들은 줄맞추고,
통계는 접혔다 펼쳐졌다 헤더에 숨고,
맵은 견고해져 퍼시스턴트가 되었지 —
당근 한 입에 고침 완료, 냠냠! 🥕"

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.86% 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 제목이 변경 사항의 주요 목표와 일치합니다. '독서 기록 상세 화면 UI 반영 사항 반영'은 독서 기록 상세 화면의 UI 업데이트를 명확하게 설명하고 있습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@github-actions github-actions bot requested a review from seoyoon513 January 3, 2026 14:51
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt (1)

67-75: img_etc drawable 리소스 존재 확인 및 승인

Emotion.ETC 케이스가 적절하게 추가되어 when 표현식이 완전해졌으며, img_etc.webp drawable 리소스도 존재합니다. 변경 사항은 정상입니다.

다만, 유사한 패턴을 사용하는 RecordItem.ktgetEmotionImageResourceByDisplayName() 함수도 함께 업데이트하여 일관성을 맞춰주세요:

  • 현재: "기타" -> R.drawable.img_warm // TODO: ETC 이미지 리소스 추가 필요
  • 변경 필요: "기타" -> R.drawable.img_etc
🧹 Nitpick comments (5)
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt (1)

238-248: 일관성을 위한 선택적 개선 제안

Line 240에서 selectedEmotionDetails[selectedEmotion]을 사용하고 있는데, emotionKey를 사용하면 일관성이 높아집니다.

🔎 제안된 변경사항
 is RecordRegisterUiEvent.OnEmotionDetailToggled -> {
     val emotionKey = selectedEmotion ?: return
-    val currentDetails = selectedEmotionDetails[selectedEmotion].orEmpty()
+    val currentDetails = selectedEmotionDetails[emotionKey].orEmpty()
     val updatedDetails = if (event.detail in currentDetails) {
         currentDetails - event.detail
     } else {
         currentDetails + event.detail
     }

     selectedEmotionDetails = selectedEmotionDetails.put(emotionKey, updatedDetails.toPersistentList())
 }
feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step_v2/EmotionItem.kt (1)

76-85: null 체크 후 !! 사용 대신 let 또는 스마트 캐스트 사용을 권장합니다.

null 체크 후 !!를 사용하는 것보다 let을 사용하면 더 안전하고 관용적인 Kotlin 코드가 됩니다. 또한 이미지가 없을 때 Spacer가 항상 렌더링되어 불필요한 여백이 생길 수 있습니다.

🔎 제안된 수정
-            if (emotion.graphicResV2 != null) {
-                Image(
-                    painter = painterResource(emotion.graphicResV2!!),
-                    contentDescription = "Emotion Image",
-                    modifier = Modifier
-                        .size(60.dp)
-                        .clip(CircleShape),
-                )
-            }
-            Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing4))
+            emotion.graphicResV2?.let { graphicRes ->
+                Image(
+                    painter = painterResource(graphicRes),
+                    contentDescription = "Emotion Image",
+                    modifier = Modifier
+                        .size(60.dp)
+                        .clip(CircleShape),
+                )
+                Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing4))
+            }
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt (1)

101-102: 기존 Neutral 색상을 재사용하는 것을 고려해보세요.

EtcTextColorEtcBgColor는 각각 Neutral500, Neutral100과 동일한 색상 값을 하드코딩하고 있습니다. 기존 색상 상수를 참조하면 유지보수성이 향상됩니다.

🔎 제안하는 리팩토링
 // Emotion Color
 val WarmthTextColor = Color(0xFFE3931B)
 val WarmthBgColor = Color(0xFFFFF5D3)
 val JoyTextColor = Color(0xFFEE6B33)
 val JoyBgColor = Color(0xFFFFEBE3)
 val InsightTextColor = Color(0xFF9A55E4)
 val InsightBgColor = Color(0xFFF3E8FF)
 val SadnessTextColor = Color(0xFF2872E9)
 val SadnessBgColor = Color(0xFFE1ECFF)
-val EtcTextColor = Color(0xFF737373)
-val EtcBgColor = Color(0xFFF5F5F5)
+val EtcTextColor = Neutral500
+val EtcBgColor = Neutral100
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt (1)

109-109: TODO 주석을 추적하여 완료하세요.

"기타" 감정에 대한 적절한 이미지 리소스가 아직 추가되지 않았습니다. 이 작업이 추적되고 있는지 확인하세요.

이 TODO 항목을 추적하기 위한 별도 이슈를 생성하시겠습니까?

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt (1)

139-148: topEmotion이 null일 때 UX 개선 필요

topEmotion이 null인 경우 "''" (빈 따옴표)가 표시됩니다. 감정 통계가 없는 경우 더 나은 대체 메시지를 표시하거나 해당 섹션을 숨기는 것을 고려하세요.

🔎 개선 제안
-            Row {
-                Text(
-                    text = "'${topEmotion?.name?.displayName ?: ""}'",
-                    color = Yellow700,
-                    style = ReedTheme.typography.label1SemiBold,
-                )
-                Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing1))
-                Text(
-                    text = "감정을 많이 느꼈어요",
-                    color = ReedTheme.colors.contentSecondary,
-                    style = ReedTheme.typography.label1Medium,
-                )
-            }
+            if (topEmotion != null) {
+                Row {
+                    Text(
+                        text = "'${topEmotion.name.displayName}'",
+                        color = Yellow700,
+                        style = ReedTheme.typography.label1SemiBold,
+                    )
+                    Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing1))
+                    Text(
+                        text = "감정을 많이 느꼈어요",
+                        color = ReedTheme.colors.contentSecondary,
+                        style = ReedTheme.typography.label1Medium,
+                    )
+                }
+            } else {
+                Text(
+                    text = "아직 수집된 감정이 없어요",
+                    color = ReedTheme.colors.contentSecondary,
+                    style = ReedTheme.typography.label1Medium,
+                )
+            }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21c99a4 and 7a650da.

📒 Files selected for processing (39)
  • app/stability/app.stability
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/ComponentPreview.kt
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/DevicePreview.kt
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/Emotion.kt
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt
  • core/designsystem/src/main/res/values/strings.xml
  • core/designsystem/stability/designsystem.stability
  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt
  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt
  • core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt
  • core/ui/stability/ui.stability
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/ReadingRecordsHeader.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt
  • feature/detail/src/main/res/drawable/img_etc.webp
  • feature/detail/src/main/res/drawable/img_insight.webp
  • feature/detail/src/main/res/drawable/img_joy.webp
  • feature/detail/src/main/res/drawable/img_sad.webp
  • feature/detail/src/main/res/drawable/img_warm.webp
  • feature/detail/stability/detail.stability
  • feature/edit/stability/edit.stability
  • feature/home/stability/home.stability
  • feature/library/stability/library.stability
  • feature/login/stability/login.stability
  • feature/onboarding/stability/onboarding.stability
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUiState.kt
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step_v2/EmotionItem.kt
  • feature/record/stability/record.stability
  • feature/screens/stability/screens.stability
  • feature/search/stability/search.stability
  • feature/settings/stability/settings.stability
  • feature/splash/stability/splash.stability
  • feature/webview/stability/webview.stability
  • gradle/libs.versions.toml
💤 Files with no reviewable changes (13)
  • feature/screens/stability/screens.stability
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/DevicePreview.kt
  • feature/splash/stability/splash.stability
  • core/ui/stability/ui.stability
  • feature/webview/stability/webview.stability
  • feature/login/stability/login.stability
  • core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/ComponentPreview.kt
  • feature/settings/stability/settings.stability
  • feature/onboarding/stability/onboarding.stability
  • feature/home/stability/home.stability
  • feature/library/stability/library.stability
  • feature/edit/stability/edit.stability
  • feature/search/stability/search.stability
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.
📚 Learning: 2025-07-31T23:17:40.054Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

Applied to files:

  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt
  • core/designsystem/src/main/res/values/strings.xml
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/step_v2/EmotionItem.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt
📚 Learning: 2025-08-28T12:25:54.058Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 174
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt:128-133
Timestamp: 2025-08-28T12:25:54.058Z
Learning: In BookSearchPresenter.kt, when a guest user tries to register a book and is redirected to login, the bottom sheet (isBookRegisterBottomSheetVisible) and selection state (selectedBookIsbn, selectedBookStatus) are intentionally kept open/preserved so that when the user returns from login, they can continue from where they left off without losing context.

Applied to files:

  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt
  • feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt
  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt
  • feature/detail/stability/detail.stability
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt
📚 Learning: 2025-07-28T18:08:47.298Z
Learnt from: seoyoon513
Repo: YAPP-Github/Reed-Android PR: 72
File: feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/CameraFrame.kt:21-82
Timestamp: 2025-07-28T18:08:47.298Z
Learning: Jetpack Compose에서 scale() 변환은 시각적 변환만 적용하며 레이아웃 좌표계는 변경하지 않는다. 따라서 scale(scaleX = -1f, scaleY = -1f)로 반전된 아이콘에서 padding()은 원래 레이아웃 기준으로 동작하므로, 시각적으로 올바른 위치를 위해서는 변환 전 좌표계 기준으로 padding을 설정해야 한다.

Applied to files:

  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
🧬 Code graph analysis (2)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt (2)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt (1)
  • CollectedSeeds (44-106)
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Theme.kt (1)
  • ReedTheme (14-21)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt (2)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/EmotionAnalyzer.kt (1)
  • analyzeEmotions (17-34)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt (1)
  • getEmotionImageResourceByDisplayName (103-112)
⏰ 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: ci-build
  • GitHub Check: Compose Stability Check
🔇 Additional comments (33)
feature/record/stability/record.stability (1)

1-77: LGTM! Stability 개선이 올바르게 반영되었습니다.

MapPersistentMap 변경으로 인해 모든 RecordRegisterUiState 파라미터가 STABLE로 인식되고, 모든 Composable이 skippable: true가 되었습니다. 이는 불필요한 recomposition을 방지하여 성능 향상에 기여합니다.

참고: 이 파일은 ./gradlew :record:stabilityDump 명령으로 자동 생성되므로, 직접 수정하지 않도록 주의해 주세요.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterUiState.kt (1)

25-27: LGTM! Compose 안정성 향상을 위한 PersistentMap 적용

Map에서 PersistentMap으로 변경하여 Compose의 안정성 추론이 개선됩니다. PersistentMap은 구조적 공유와 불변성을 보장하므로 불필요한 리컴포지션을 방지하는 데 도움이 됩니다.

feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/register/RecordRegisterPresenter.kt (3)

100-102: LGTM! PersistentMap을 사용한 상태 선언

PersistentMap을 사용하여 Compose 안정성이 개선됩니다. rememberRetained와 함께 사용하여 구성 변경 시에도 상태가 유지됩니다.


267-275: LGTM! 감정 세부사항 커밋 로직

OnEmotionDetailCommitted 핸들러가 현재 선택된 감정의 세부사항만 유지하도록 새로운 persistentMapOf를 생성합니다. 단일 감정 선택 플로우에 맞는 올바른 설계입니다.


336-344: 감정 세부사항 API 연동 확인

현재 emotionTags에는 감정 이름만 전송되고 세부 감정 태그(committedEmotionDetails)는 포함되지 않습니다. PR 설명에 따르면 API 변경이 후속 PR에서 처리될 예정이므로, 해당 시점에 세부 감정 데이터도 함께 전송되도록 업데이트가 필요합니다.

Based on learnings, 이 프로젝트는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고 API 연동 시점에 데이터를 교체하는 개발 방식을 사용합니다.

core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt (2)

3-3: 올바른 import 변경입니다.

@Immutable 어노테이션 변경에 맞춰 import가 올바르게 업데이트되었습니다.


13-27: @Immutable 어노테이션 변경이 적절합니다.

불변 데이터 클래스에 @Immutable 어노테이션을 적용하여 Compose의 재구성 최적화를 개선한 좋은 변경입니다. 모든 프로퍼티가 val로 선언되어 있고, emotionTags 등의 List 프로퍼티는 코드베이스 전체에서 변경되지 않습니다. 모든 조작이 .copy() 등의 불변 패턴으로 처리되고 있어 @Immutable 계약을 충분히 만족합니다.

향후 리팩토링 시, kotlinx.collections.immutable의 ImmutableList를 사용하면 컴파일 타임에 불변성을 강제할 수 있습니다:

선택사항: 더 강력한 불변성 보장
import kotlinx.collections.immutable.ImmutableList

@Immutable
data class ReadingRecordModel(
    // ...
    val emotionTags: ImmutableList<String> = persistentListOf(),
    // ...
)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/ReadingRecordsHeader.kt (1)

44-48: LGTM!

총 개수 텍스트 색상을 contentTertiary로 변경한 것은 UI 요구사항에 맞는 적절한 수정입니다.

core/designsystem/src/main/res/values/strings.xml (1)

11-11: LGTM!

ETC 감정에 대한 설명 문자열 리소스가 기존 네이밍 컨벤션(emotion_*_description)을 따르고 있으며, 적절한 설명 텍스트입니다.

core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt (1)

16-30: LGTM!

ETC("기타") enum 상수가 적절하게 추가되었습니다. fromDisplayName 함수는 entries.find를 사용하므로 새로운 ETC 값도 자동으로 처리됩니다.

core/designsystem/stability/designsystem.stability (1)

1-270: LGTM!

Compose Stability Analyzer에 의해 자동 생성된 파일입니다. 모든 컴포저블 함수가 skippable: true로 표시되어 있어 리컴포지션 성능이 최적화되어 있습니다.

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/Emotion.kt (3)

39-46: graphicRes에서 ETC가 placeholder 이미지를 사용 중입니다.

Emotion.ETCR.drawable.img_emotion_warmth를 재사용하고 있습니다. 의도적인 placeholder인지, 아니면 별도의 ETC 이미지가 필요한지 확인해 주세요.

PR 설명에서 API 변경이 후속 PR에서 처리될 예정이라고 언급되어 있으므로, 이것도 추후 반영될 수 있습니다. 기존 learnings에 따르면 API 준비 전 UI를 먼저 구현하는 방식을 사용하고 있습니다.


48-55: LGTM!

새로운 ratioBarColor 속성이 각 감정 타입에 맞는 색상을 깔끔하게 매핑하고 있습니다.


57-64: LGTM!

graphicResV2가 nullable(Int?)로 선언되어 ETC 감정에 대해 null을 반환하는 것은 적절합니다. EmotionItem.kt에서 이 값에 대한 null 체크가 올바르게 처리되고 있습니다.

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt (2)

66-75: Orange 팔레트가 올바르게 추가되었습니다.

Orange 색상 팔레트가 기존 색상 정의 패턴(50-900 스케일)을 일관되게 따르고 있으며, 구문 오류가 없습니다.


77-86: Violet 팔레트가 올바르게 추가되었습니다.

Violet 색상 팔레트가 기존 색상 정의 패턴을 일관되게 따르고 있으며, 구문 오류가 없습니다.

core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt (1)

130-133: v2 엔드포인트 배포 상태와 응답 형식 검증 필요

코드 구현 수준에서는 다음이 확인되었습니다:

  • RecordDetailResponse 매퍼가 완전하게 구현되어 있음 (12개 필드 모두 매핑)
  • 에러 처리가 runSuspendCatching으로 적절히 처리됨
  • 모든 호출 지점에서 응답을 올바르게 처리함

다음 항목의 외부 검증이 필요합니다:

  1. 백엔드 v2 엔드포인트 배포 상태: 프로덕션 환경에 /api/v2/reading-records/detail/{readingRecordId} 엔드포인트가 배포되어 있는지 확인
  2. v1과 v2의 응답 형식 차이: API 문서를 통해 v2 응답이 RecordDetailResponse의 필드 구조(id, userBookId, pageNumber, quote, review, emotionTags, createdAt, updatedAt, bookTitle, bookPublisher, bookCoverImageUrl, author)와 일치하는지 확인
  3. 부분 마이ग레이션 계획: 다른 모든 엔드포인트는 여전히 v1을 사용하고 있으므로, 이 단계적 마이그레이션이 의도된 것인지 확인
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt (3)

286-292: LGTM! 통계 확장 기능이 잘 통합되었습니다.

CollectedSeeds 컴포넌트에 isStatsExpandedonToggleClick 파라미터가 적절히 전달되어 통계 확장/축소 기능이 올바르게 구현되었습니다.


341-343: LGTM! 레코드 아이템 클릭 처리가 명확해졌습니다.

RecordItemonRecordClick 콜백을 추가하여 클릭 이벤트 처리가 더 명시적이고 추적하기 쉬워졌습니다.


377-500: LGTM! 포괄적인 프리뷰로 다양한 UI 상태를 확인할 수 있습니다.

Empty, SeedStats, SeedsStatsExpanded 세 가지 시나리오에 대한 프리뷰를 제공하여 UI 개발과 검증이 용이합니다. 샘플 데이터도 현실적이고 적절합니다.

feature/detail/stability/detail.stability (1)

32-39: LGTM! RecordItem 시그니처 업데이트가 안정성 설정에 올바르게 반영되었습니다.

onRecordClick 파라미터 추가가 stability 설정에 정확하게 반영되었으며, 모든 파라미터가 STABLE로 올바르게 표시되어 있습니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt (2)

30-30: LGTM! 통계 확장 상태 필드가 적절히 추가되었습니다.

isStatsExpanded 필드가 기본값 false로 올바르게 초기화되어 있으며, 통계 섹션의 확장/축소 상태를 추적하는 명확한 목적을 가지고 있습니다.


88-88: LGTM! 통계 토글 이벤트가 명확하게 정의되었습니다.

OnStatsToggleClick 이벤트가 flag 파라미터를 통해 명시적으로 상태를 전달하여 의도가 분명합니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt (3)

82-82: LGTM! 상태가 올바르게 보존됩니다.

rememberRetained를 사용하여 isStatsExpanded 상태가 재구성 간에도 보존되므로 사용자가 화면을 벗어났다가 돌아와도 확장 상태가 유지됩니다.


403-405: LGTM! 이벤트 처리가 간단하고 명확합니다.

토글 이벤트 처리가 직접적이며 부작용이 없어 예측 가능한 동작을 보장합니다.


418-418: LGTM! 상태가 UI에 올바르게 전달됩니다.

isStatsExpandedBookDetailUiState에 포함되어 UI 레이어에서 올바르게 사용될 수 있습니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt (2)

35-43: LGTM! 명시적인 클릭 콜백으로 이벤트 처리가 개선되었습니다.

onRecordClick 파라미터를 추가하고 clickable modifier를 통해 연결하여 레코드 아이템 클릭 이벤트를 명확하게 처리할 수 있게 되었습니다.


57-62: LGTM! 페이지 번호 표시가 간결해졌습니다.

페이지 번호 표시 방식이 명확하며, italic 스타일이 적용되어 시각적으로 구별됩니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt (4)

44-106: 상태 관리와 애니메이션 구현이 적절합니다.

remember를 통한 분석 결과 메모이제이션과 AnimatedVisibility를 사용한 확장/축소 애니메이션이 잘 구현되었습니다. Emotion.entries를 순회하며 누락된 감정에 대해 count 0인 기본값을 생성하는 로직도 올바릅니다.


163-190: 비율 막대 구현이 올바릅니다.

coerceAtLeast(1)을 통한 0으로 나누기 방지와 count가 0보다 큰 감정만 렌더링하는 최적화가 잘 구현되었습니다.


192-228: LGTM!

감정 통계 카드 구현이 명확하고 올바릅니다.


230-282: 프리뷰 구현이 적절합니다.

축소/확장 상태와 최다 감정 중복 시나리오를 포함하여 다양한 UI 상태를 잘 커버하고 있습니다.

app/stability/app.stability (1)

1-13: 자동 생성된 파일의 예상된 변경사항입니다.

이 파일은 Compose Stability Analyzer에 의해 자동 생성되며, targetState 매개변수의 안정성 분류가 업데이트된 버전의 분석기에 의해 더 정확하게 감지되었습니다. 이는 gradle/libs.versions.toml의 도구 업그레이드에 따른 자연스러운 결과입니다.

Comment on lines +125 to +135
topEmotion?.let { emotion ->
Image(
painter = painterResource(id = getEmotionImageResourceByDisplayName(emotion.name.displayName)),
contentDescription = "Seed Image",
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(ReedTheme.colors.basePrimary),
)
Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing2))
}
Copy link

@coderabbitai coderabbitai bot Jan 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for getEmotionImageResourceByDisplayName function
rg "getEmotionImageResourceByDisplayName" -A 10

Repository: YAPP-Github/Reed-Android

Length of output: 6085


🏁 Script executed:

#!/bin/bash
# Look for ETC image resource files
fd -type f \( -name "*etc*" -o -name "*ETC*" \) core/designsystem/src/main/res/drawable*/

Repository: YAPP-Github/Reed-Android

Length of output: 238


🏁 Script executed:

#!/bin/bash
# Search for TODO comments related to ETC or image resource
rg "TODO.*[Ee][Tt][Cc]|TODO.*image.*resource" -A 2 -B 2

Repository: YAPP-Github/Reed-Android

Length of output: 50382


🏁 Script executed:

#!/bin/bash
# Check if EmotionUtils or similar utility already exists
fd -type f -path "*/utils/*" -name "*motion*" -o -path "*/utils/*" -name "*Emotion*"

Repository: YAPP-Github/Reed-Android

Length of output: 238


🏁 Script executed:

#!/bin/bash
# Check imports at top of CollectedSeeds.kt
head -50 feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt | grep -i "import.*[Ee]motion"

Repository: YAPP-Github/Reed-Android

Length of output: 228


🏁 Script executed:

#!/bin/bash
# Check full function definitions in CollectedSeeds.kt to see if it defines its own version
grep -n "fun getEmotionImageResourceByDisplayName" feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt

Repository: YAPP-Github/Reed-Android

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Search for all function definitions in CollectedSeeds.kt
grep -n "^fun " feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt | head -20

Repository: YAPP-Github/Reed-Android

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Get all imports from CollectedSeeds.kt
grep "^import" feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt

Repository: YAPP-Github/Reed-Android

Length of output: 2039


🏁 Script executed:

#!/bin/bash
# Check file size and get full content or key sections
wc -l feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt

Repository: YAPP-Github/Reed-Android

Length of output: 171


🏁 Script executed:

#!/bin/bash
# Check around line 127 where getEmotionImageResourceByDisplayName is used
sed -n '110,145p' feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt

Repository: YAPP-Github/Reed-Android

Length of output: 1422


🏁 Script executed:

#!/bin/bash
# Search for getEmotionImageResourceByDisplayName definition anywhere in the file
grep -B5 -A5 "getEmotionImageResourceByDisplayName" feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt

Repository: YAPP-Github/Reed-Android

Length of output: 574


🏁 Script executed:

#!/bin/bash
# Search for img_etc resource in drawable directories
find core/designsystem/src/main/res -type f \( -name "*etc*" -o -name "*img_etc*" \)

Repository: YAPP-Github/Reed-Android

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Check what drawable resources exist
ls -la core/designsystem/src/main/res/drawable*/img_* 2>/dev/null | head -20

Repository: YAPP-Github/Reed-Android

Length of output: 983


🏁 Script executed:

#!/bin/bash
# Get all drawable files to see full list
ls -1 core/designsystem/src/main/res/drawable/*.webp 2>/dev/null | grep -i "img_" | sort

Repository: YAPP-Github/Reed-Android

Length of output: 575


🏁 Script executed:

#!/bin/bash
# Check if the function is actually in same package without needing import
grep -n "package" feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
grep -n "package" feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt

Repository: YAPP-Github/Reed-Android

Length of output: 189


ETC 감정 이미지 리소스 추가 필요

RecordItem.ktgetEmotionImageResourceByDisplayName 함수에서 "기타" 감정에 대해 R.drawable.img_warm으로 임시 처리하고 있으며, TODO 주석으로 ETC 이미지 리소스 추가가 필요함이 표시되어 있습니다. 실제 img_etc 리소스 파일이 아직 추가되지 않았으므로 이를 먼저 추가한 후 함수를 수정해야 합니다.

🤖 Prompt for AI Agents
In
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt
around lines 125 to 135, the code relies on getEmotionImageResourceByDisplayName
which currently falls back to R.drawable.img_warm for the "기타" (ETC) emotion
while a proper img_etc resource hasn't been added; add a new drawable resource
file named img_etc (or appropriate asset) to res/drawable, then update
getEmotionImageResourceByDisplayName in RecordItem.kt to return
R.drawable.img_etc for the "기타"/ETC case (remove the TODO/fallback), and rebuild
to ensure the new resource is recognized.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the commit details to see what was changed
git show 5014318f --stat --oneline

Length of output: 225


🏁 Script executed:

#!/bin/bash
# Check if img_etc resource has been added
fd -type f "img_etc" core/designsystem/src/main/res/

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Verify the updated getEmotionImageResourceByDisplayName function
rg -A 15 "fun getEmotionImageResourceByDisplayName" feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt

Length of output: 428


🏁 Script executed:

#!/bin/bash
# Fix the command to search for img_etc resource files
fd "img_etc" core/designsystem/src/main/res/

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Also list all img_ drawables to confirm img_etc is among them
ls -1 core/designsystem/src/main/res/drawable*/ | grep "img_etc"

Length of output: 0


🏁 Script executed:

#!/bin/bash
# List all emotion-related drawable files to see what exists
ls -la core/designsystem/src/main/res/drawable/*.webp 2>/dev/null | grep "img_"

Length of output: 915


🏁 Script executed:

#!/bin/bash
# Search for any file containing "etc" in drawable directories
find core/designsystem/src/main/res/drawable* -name "*etc*" -o -name "*ETC*" 2>/dev/null

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if the code will compile by looking for R.drawable references
rg "R\.drawable\.img_etc" feature/detail/

Length of output: 266


@easyhooon, 코드 업데이트는 잘 되었습니다! 👍 getEmotionImageResourceByDisplayName 함수에서 "기타"가 이제 R.drawable.img_etc를 올바르게 참조하고 있습니다.

하지만 한 가지 문제가 있습니다: 실제 img_etc.webp drawable 리소스 파일이 아직 추가되지 않았습니다.

현재 상황:

  • ✅ RecordItem.kt: "기타" -> R.drawable.img_etc (업데이트 완료)
  • ✅ SeedItem.kt: Emotion.ETC -> R.drawable.img_etc (이미 참조 중)
  • core/designsystem/src/main/res/drawable/img_etc.webp (파일 없음)

다른 감정 이미지들은 존재합니다:

  • img_emotion_warmth.webp
  • img_emotion_joy.webp
  • img_emotion_sadness.webp
  • img_emotion_insight.webp

다음 작업이 필요합니다:
core/designsystem/src/main/res/drawable/ 디렉토리에 img_etc.webp 파일을 추가해주세요. 그렇지 않으면 R.drawable.img_etc가 생성되지 않아 컴파일 에러가 발생합니다.


🧬 Code Graph Analysis Results

[feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt] (no direct snippet, see below)

[relevant_snippets]

  1. File: core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/EmotionAnalyzer.kt
    Lines: 17-34
    Content:
fun analyzeEmotions(emotions: List<EmotionModel>): EmotionAnalysisResult {
    val maxCount = emotions.maxOf { it.count }

    // 모든 감정의 count가 0인 경우
    if (maxCount == 0) {
        return EmotionAnalysisResult(emptyList(), EmotionDisplayType.NONE)
    }

    val topEmotions = emotions.filter { it.count == maxCount }

    val displayType = when (topEmotions.size) {
        1 -> EmotionDisplayType.SINGLE
        2 -> EmotionDisplayType.DUAL
        else -> EmotionDisplayType.BALANCED
    }

    return EmotionAnalysisResult(topEmotions, displayType)
}
  1. File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
    Lines: 103-112
    Content:
fun getEmotionImageResourceByDisplayName(displayName: String): Int {
    return when (displayName) {
        "따뜻함" -> R.drawable.img_warm
        "즐거움" -> R.drawable.img_joy
        "슬픔" -> R.drawable.img_sad
        "깨달음" -> R.drawable.img_insight
        "기타" -> R.drawable.img_etc
        else -> R.drawable.img_warm
    }
}

🧠 Learnings used
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

compose-effects = "0.1.4"
compose-shadow = "2.0.4"
compose-stability-analyzer = "0.5.2"
compose-stability-analyzer = "0.6.6"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat gradle/libs.versions.toml | grep -A 2 -B 2 "compose-stability-analyzer"

Repository: YAPP-Github/Reed-Android

Length of output: 458


🌐 Web query:

compose-stability-analyzer gradle plugin latest version release

💡 Result:

The latest published Gradle plugin version is 0.6.0 (published to Maven Central on Nov 24, 2025). [1][2]

Notes:

  • GitHub releases/tags currently show 0.5.3 as the latest release (Nov 18, 2025). [2]

Sources:

  • Maven Central / mvnrepository entry for compose-stability-gradle 0.6.0. [1]
  • GitHub releases for skydoves/compose-stability-analyzer (latest tag 0.5.3). [2]

Compose Stability Analyzer 버전이 존재하지 않습니다.

지정된 버전 0.6.6은 Maven Central이나 GitHub의 공식 저장소에서 찾을 수 없습니다. 현재 사용 가능한 최신 버전은 Maven Central에서 0.6.0(2025년 11월 24일 배포)이고, GitHub에서는 0.5.3(2025년 11월 18일)입니다. 이 버전을 사용하면 빌드가 실패할 수 있으므로, 최신 버전(0.6.0)으로 수정해야 합니다.

🤖 Prompt for AI Agents
In gradle/libs.versions.toml around line 27, the compose-stability-analyzer
version is set to "0.6.6" which does not exist in Maven Central or GitHub;
update the version string to the confirmed latest available version "0.6.0" to
prevent build failures by replacing 0.6.6 with 0.6.0.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
feature/detail/stability/detail.stability (1)

1-5: 자동 생성 파일 관리에 대한 제안.

이 파일은 ./gradlew :detail:stabilityDump 명령으로 자동 생성됩니다. CI/CD 파이프라인에서 stability dump를 자동으로 검증하여 소스 코드 변경 시 stability 파일이 최신 상태인지 확인하는 것을 고려해 보세요. 이를 통해 stability 파일과 실제 코드 간의 불일치를 방지할 수 있습니다.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5014318 and 6f3ff77.

📒 Files selected for processing (3)
  • feature/detail/stability/detail.stability
  • feature/edit/stability/edit.stability
  • feature/record/stability/record.stability
💤 Files with no reviewable changes (1)
  • feature/edit/stability/edit.stability
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.
📚 Learning: 2025-08-28T12:25:54.058Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 174
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt:128-133
Timestamp: 2025-08-28T12:25:54.058Z
Learning: In BookSearchPresenter.kt, when a guest user tries to register a book and is redirected to login, the bottom sheet (isBookRegisterBottomSheetVisible) and selection state (selectedBookIsbn, selectedBookStatus) are intentionally kept open/preserved so that when the user returns from login, they can continue from where they left off without losing context.

Applied to files:

  • feature/detail/stability/detail.stability
  • feature/record/stability/record.stability
📚 Learning: 2025-07-31T23:17:40.054Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

Applied to files:

  • feature/detail/stability/detail.stability
⏰ 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: ci-build
  • GitHub Check: Compose Stability Check
🔇 Additional comments (4)
feature/record/stability/record.stability (1)

93-189: LGTM! 안정성(Stability) 개선이 올바르게 반영되었습니다.

RecordRegisterUiState의 상태 타입이 Map에서 PersistentMap으로 변경됨에 따라, 해당 state를 파라미터로 받는 모든 Composable 함수들이 STABLE로 올바르게 인식되고 있습니다. 이를 통해:

  • skippable: true 유지로 불필요한 리컴포지션 방지
  • Compose 컴파일러의 최적화 활성화

자동 생성 파일이므로 수동 편집 없이 ./gradlew :record:stabilityDump 명령어를 통해 업데이트하는 방식이 적절합니다.

feature/detail/stability/detail.stability (3)

72-80: 안정성 분석 결과가 올바르게 반영되었습니다.

CollectedSeeds 컴포넌트에 추가된 isStatsExpandedonToggleClick 파라미터 모두 STABLE로 마킹되어 있어 recomposition 최적화에 문제가 없습니다.


82-90: CollectedSeedsHeader 안정성 확인 완료.

새로 추가된 topEmotion, isStatsExpanded, onToggleClick 파라미터가 모두 STABLE로 분석되었습니다. topEmotion이 nullable임에도 @Stable 또는 @Immutable 마킹을 통해 안정성이 보장되고 있습니다.


141-159: ReadingRecordsHeader 및 RecordItem 시그니처 변경이 안정성 분석에 정상 반영되었습니다.

  • ReadingRecordsHeader: totalCount, currentRecordSort, onReadingRecordClick 파라미터 모두 STABLE
  • RecordItem: 새로 추가된 onRecordClick 파라미터가 STABLE로 분석됨

PR 목표에서 언급한 대로 ReadingRecordModel@Immutable로 변경되어 recordInfo 파라미터가 STABLE로 유지되고 있습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
.github/workflows/android-ci.yml (1)

87-89: LGTM! 안정성 덤프 단계가 적절하게 추가되었습니다.

stabilityDump 태스크를 stabilityCheck 이전에 실행하는 것은 논리적으로 올바른 순서입니다.

선택 사항: 디버깅 및 가시성 개선을 위한 개선 제안

다음과 같은 개선 사항을 고려해볼 수 있습니다:

  1. 실패 시 디버깅을 위한 스택 트레이스 추가:
 -   name: Compose Stability Dump
-    run: ./gradlew stabilityDump
+    run: ./gradlew stabilityDump --stacktrace
  1. 트러블슈팅을 위해 덤프 출력을 아티팩트로 업로드:
-   name: Compose Stability Dump
    run: ./gradlew stabilityDump

-   name: Upload Stability Reports
    if: always()
    uses: actions/upload-artifact@v4
    with:
        name: stability-reports
        path: '**/build/outputs/compose_stability/**'
        retention-days: 7
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f3ff77 and 8451e75.

📒 Files selected for processing (1)
  • .github/workflows/android-ci.yml
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @gradle/libs.versions.toml:
- Around line 10-11: Updating android-gradle-plugin to "8.12.0" can change R8
SourceFile naming (to r8-map-id-<MAP_ID>) and affect stacktrace retracing and
ProGuard/R8 rules; open the build configuration where android-gradle-plugin is
referenced (the android-gradle-plugin entry in libs.versions.toml) and verify
CI/builds by (1) ensuring Gradle >= 8.13 is used, (2) adding or adjusting
R8/ProGuard settings such as adding -renamesourcefileattribute or appropriate
-keepattributes/-keep rules to preserve source file info, and (3) running a full
build and stack-trace retrace test to confirm that mapping and retracing behave
as expected, updating any retrace scripts or rules if they rely on the old
SourceFile format.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8451e75 and 135ab71.

📒 Files selected for processing (2)
  • gradle/libs.versions.toml
  • gradle/wrapper/gradle-wrapper.properties
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.
📚 Learning: 2025-07-20T12:34:23.786Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 61
File: feature/webview/build.gradle.kts:17-21
Timestamp: 2025-07-20T12:34:23.786Z
Learning: Reed-Android 프로젝트에서는 `booket.android.feature` convention plugin을 사용하여 feature 모듈들의 공통 의존성을 관리한다. 이 plugin은 Circuit, Compose, 그리고 core 모듈들의 의존성을 자동으로 포함하므로, 각 feature 모듈의 build.gradle.kts에서는 특별한 의존성(예: libs.logger, libs.kakao.auth)만 별도로 선언하면 된다.

Applied to files:

  • gradle/libs.versions.toml
⏰ 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: Compose Stability Check
  • GitHub Check: ci-build
🔇 Additional comments (5)
gradle/libs.versions.toml (4)

18-27: AndroidX 및 Compose 라이브러리 버전 업데이트 승인.

DataStore 1.2.0, Camera 1.5.2, Compose BOM 2025.12.01, Stability Analyzer 0.6.6 업데이트가 적절합니다. PR 설명에서 언급한 stability check 개선과 일관성 있는 변경입니다.


73-79: 테스트 및 Firebase 의존성 업데이트 승인.

테스트 라이브러리와 Firebase BOM 업데이트가 적절합니다. Firebase BOM 34.7.0은 firebase-analytics, firebase-messaging, firebase-remote-config 등 새로 추가된 Firebase 라이브러리들과 호환됩니다.


30-33: KSP 2.3.4와 Kotlin 2.3.0은 호환됩니다.

KSP2부터는 특정 Kotlin 컴파일러 버전과 더 이상 강하게 연결되지 않으며, KSP 2.3.x는 Kotlin 2.2 이상(2.3.0 포함)과 함께 작동하도록 설계되었습니다. 현재 버전 지정은 정확하고 문제가 없습니다.


38-39: Metro 0.9.4 업데이트 — 주요 변경사항 없음.

Metro 0.9.4에 breaking change가 없는 것으로 확인되었습니다. 유일한 변경은 ProGuard rule 제거(보안 관련)이며, 기존 DI 설정과 호환성에는 영향이 없습니다. 통상적인 테스트를 수행하여 정상 동작을 확인하면 충분합니다.

gradle/wrapper/gradle-wrapper.properties (1)

4-4: Gradle 8.14.1 업데이트 확인됨 — 버전 호환성 검증 완료.

AGP 8.12.0 및 Kotlin 2.3.0과의 호환성이 확인되었습니다. Gradle 8.14.1은 AGP 8.12.0의 최소 요구사항인 Gradle 8.13을 충족하며, Kotlin 2.3.0도 모든 버전과 호환됩니다. 8.14.1 패치는 AGP 코드 커버리지 이슈도 해결합니다.

Comment on lines 10 to +11
## Android gradle plugin
android-gradle-plugin = "8.9.3"
android-gradle-plugin = "8.12.0"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "libs.versions.toml" -type f | head -5

Repository: YAPP-Github/Reed-Android

Length of output: 95


🌐 Web query:

Android Gradle Plugin 8.12.0 release notes breaking changes

💡 Result:

Key breaking changes in Android Gradle Plugin (AGP) 8.12.0

  • AGP 8.12 requires Gradle 8.13 (minimum/target Gradle version changed). [1]
  • R8: the default emitted SourceFile attribute changes to r8-map-id-<MAP_ID> when retracing is required (affects stack-trace retracing and any workflows that relied on the previous SourceFile value; keep/override with -renamesourcefileattribute or adjust ProGuard/R8 keep rules as needed). [2][1]
  • Max supported Android API level and tool versions updated: AGP 8.12’s maximum platform API = 36 and it lists updated default SDK Build Tools / JDK requirements — verify/adjust compileSdk, build-tools, and JDK to match. [1]

Sources

  • AGP 8.12.0 release notes (Android Developers). [1]
  • AGP preview / R8 notes (change to emitted source file). [2]

🌐 Web query:

AGP 8.12.0 compatibility kotlin 2.3.0

💡 Result:

Yes — Kotlin 2.3.0 is compatible with Android Gradle Plugin 8.12.0. Kotlin's compatibility notes list AGP support range 8.2.2–8.13.0, which includes 8.12.0. [1][2]

Sources:

  • Kotlin 2.3.0 what's new / compatibility notes. [1]
  • Android Gradle Plugin 8.12.0 release notes. [2]

🏁 Script executed:

cat -n ./gradle/libs.versions.toml | head -50

Repository: YAPP-Github/Reed-Android

Length of output: 1335


🏁 Script executed:

find . -name "gradle-wrapper.properties" -o -name "settings.gradle*" | head -10

Repository: YAPP-Github/Reed-Android

Length of output: 166


🏁 Script executed:

cat -n ./gradle/wrapper/gradle-wrapper.properties 2>/dev/null || echo "File not found, checking for gradle version in build files"

Repository: YAPP-Github/Reed-Android

Length of output: 342


AGP 8.12.0 업데이트 시 R8 설정 확인 필요.

AGP 8.12.0의 주요 변경사항:

  • R8의 SourceFile 속성이 r8-map-id-<MAP_ID> 형식으로 변경됨 (스택 트레이스 재추적 및 ProGuard/R8 규칙에 영향)
  • Gradle 8.13 이상 필요 (현재 8.14.1로 충족)

빌드 및 CI 파이프라인에서 스택 트레이스 처리가 정상 동작하는지 확인하고, 필요시 -renamesourcefileattribute 설정이나 ProGuard/R8 keep 규칙을 조정해 주세요.

🤖 Prompt for AI Agents
In @gradle/libs.versions.toml around lines 10 - 11, Updating
android-gradle-plugin to "8.12.0" can change R8 SourceFile naming (to
r8-map-id-<MAP_ID>) and affect stacktrace retracing and ProGuard/R8 rules; open
the build configuration where android-gradle-plugin is referenced (the
android-gradle-plugin entry in libs.versions.toml) and verify CI/builds by (1)
ensuring Gradle >= 8.13 is used, (2) adding or adjusting R8/ProGuard settings
such as adding -renamesourcefileattribute or appropriate -keepattributes/-keep
rules to preserve source file info, and (3) running a full build and stack-trace
retrace test to confirm that mapping and retracing behave as expected, updating
any retrace scripts or rules if they rely on the old SourceFile format.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt (1)

131-140: emotionTags[0] 접근 시 IndexOutOfBoundsException 위험

OnShareRecordClick 이벤트에서 emotionTags[0]을 직접 접근하고 있습니다. emotionTags가 비어있을 경우 런타임 예외가 발생할 수 있습니다. Line 151에서 사용된 것처럼 firstOrNull() 패턴을 적용하는 것이 안전합니다.

🐛 수정 제안
                 is RecordDetailUiEvent.OnShareRecordClick -> {
                     isRecordMenuBottomSheetVisible = false
                     navigator.goTo(
                         RecordCardScreen(
                             quote = recordDetailInfo.quote,
                             bookTitle = recordDetailInfo.bookTitle,
-                            emotion = recordDetailInfo.emotionTags[0],
+                            emotion = recordDetailInfo.emotionTags.firstOrNull() ?: "",
                         ),
                     )
                 }
🤖 Fix all issues with AI agents
In
@core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt:
- Around line 32-35: ReadingRecordsResponse currently declares primaryEmotion:
PrimaryEmotion as non-nullable without a default which will break
deserialization if the API omits or returns null; change the property in
ReadingRecordsResponse (primaryEmotion) to either be nullable (PrimaryEmotion?)
and add null handling where the response is consumed, or give it a safe default
(PrimaryEmotion("", "")) so deserialization always succeeds; update any callers
of primaryEmotion to handle the new nullable type or the new default value and
ensure PrimaryEmotion’s serializer/constructor supports the chosen default.
🧹 Nitpick comments (1)
core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt (1)

5-11: ReadingRecordsModel@Immutable 어노테이션 누락

ReadingRecordModel에는 @Immutable이 적용되어 있지만, ReadingRecordsModel에는 적용되어 있지 않습니다. AI 요약에서는 ReadingRecordsModel에도 @Immutable이 추가되었다고 언급되어 있으나, 실제 코드에는 누락되어 있습니다.

♻️ 일관성을 위한 수정 제안
+@Immutable
 data class ReadingRecordsModel(
     val lastPage: Boolean = true,
     val totalResults: Int = 0,
     val startIndex: Int = 0,
     val itemsPerPage: Int = 0,
-    val readingRecords: List<ReadingRecordModel> = emptyList(),
+    val readingRecords: ImmutableList<ReadingRecordModel> = persistentListOf(),
 )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 135ab71 and 8ed93df.

📒 Files selected for processing (11)
  • core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt
  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt
  • core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt
  • core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt
  • feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditPresenter.kt
  • feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditUi.kt
  • feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.
📚 Learning: 2025-07-31T23:17:40.054Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 88
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt:29-37
Timestamp: 2025-07-31T23:17:40.054Z
Learning: Reed-Android 프로젝트에서는 API가 준비되지 않은 상황에서 UI를 먼저 구현하고, API 연동 시점에 하드코딩된 데이터를 실제 데이터로 교체하는 개발 방식을 사용한다. RecordItem 컴포넌트의 emotionTags 매개변수도 API 연동 시점에 `text = emotionTags.joinToString(separator = "·") { "#$it" }`로 적용될 예정이다.

Applied to files:

  • feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt
  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt
  • feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditUi.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
  • feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditPresenter.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt
  • core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt
📚 Learning: 2025-08-28T12:25:54.058Z
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 174
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt:128-133
Timestamp: 2025-08-28T12:25:54.058Z
Learning: In BookSearchPresenter.kt, when a guest user tries to register a book and is redirected to login, the bottom sheet (isBookRegisterBottomSheetVisible) and selection state (selectedBookIsbn, selectedBookStatus) are intentionally kept open/preserved so that when the user returns from login, they can continue from where they left off without losing context.

Applied to files:

  • core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt
  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt
📚 Learning: 2025-07-28T18:08:47.298Z
Learnt from: seoyoon513
Repo: YAPP-Github/Reed-Android PR: 72
File: feature/record/src/main/kotlin/com/ninecraft/booket/feature/record/ocr/component/CameraFrame.kt:21-82
Timestamp: 2025-07-28T18:08:47.298Z
Learning: Jetpack Compose에서 scale() 변환은 시각적 변환만 적용하며 레이아웃 좌표계는 변경하지 않는다. 따라서 scale(scaleX = -1f, scaleY = -1f)로 반전된 아이콘에서 padding()은 원래 레이아웃 기준으로 동작하므로, 시각적으로 올바른 위치를 위해서는 변환 전 좌표계 기준으로 padding을 설정해야 한다.

Applied to files:

  • feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt
🧬 Code graph analysis (2)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt (2)
feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt (1)
  • CollectedSeeds (44-106)
core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Theme.kt (1)
  • ReedTheme (14-21)
feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditPresenter.kt (2)
core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt (1)
  • editRecord (135-139)
core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultRecordRepository.kt (1)
  • editRecord (41-49)
⏰ 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: ci-build
  • GitHub Check: Compose Stability Check
🔇 Additional comments (17)
feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt (1)

14-14: LGTM!

emotionTags: List<String>에서 primaryEmotionDisplayName: String으로의 변경이 새로운 감정 모델 구조에 맞게 잘 반영되었습니다. @Immutable 어노테이션이 적용되어 Compose 안정성도 보장됩니다.

core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt (2)

235-247: LGTM!

새로운 PrimaryEmotion.toModel()DetailEmotion.toModel() 확장 함수가 명확하게 구현되었습니다. 네트워크 응답에서 도메인 모델로의 매핑이 일관성 있게 처리됩니다.


249-264: 이 리뷰 의견은 잘못되었습니다.

RecordDetailResponseReadingRecord는 서로 다른 API 엔드포인트의 응답 객체입니다. RecordDetailResponseemotionTags: List<String> 필드를 가지고 있으며, 현재 매퍼에서 이를 올바르게 매핑하고 있습니다. ReadingRecord가 새로운 primaryEmotion/detailEmotions 구조를 사용한다고 해서 RecordDetailResponse도 동일하게 업데이트되어야 하는 것은 아닙니다. 각 응답 객체의 구조가 다르므로 현재 구현이 올바릅니다.

feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditUi.kt (1)

187-193: LGTM!

emotionTags.firstOrNull()에서 primaryEmotionDisplayName으로의 변경이 깔끔하게 반영되었습니다. non-nullable String을 직접 사용하여 null 체크가 불필요해졌습니다.

feature/edit/src/main/kotlin/com/ninecraft/booket/feature/edit/record/RecordEditPresenter.kt (2)

64-72: LGTM!

hasChanges 파생 상태에서 primaryEmotionDisplayName 비교 로직이 올바르게 구현되었습니다. 현재 상태와 초기 상태를 비교하여 변경 여부를 정확히 감지합니다.


141-141: 이 리뷰 코멘트는 API 스펙 이해에서 오류가 있습니다.

emotionTags는 emotion code가 아닌 display name을 저장하도록 설계되었습니다. 코드베이스 전체에서 일관되게 display name을 사용하고 있습니다:

  • RecordRegisterPresenter도 동일하게 listOf(it.displayName)으로 전송
  • API 응답의 emotionTagsList<String> 타입으로 display name을 포함
  • RecordDetailPresenter에서 primaryEmotionDisplayName = emotionTags.firstOrNull()로 처리
  • 하드코딩된 테스트 데이터에서도 emotionTags = listOf("따뜻함")처럼 display name 사용

현재 구현(line 141)은 API 설계와 일치하며 다른 부분의 패턴과도 동일합니다.

Likely an incorrect or invalid review comment.

core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt (2)

111-144: LGTM! reading-records 엔드포인트가 v2로 업그레이드되었습니다.

postRecord, getReadingRecords, getRecordDetail, editRecord, deleteRecord 엔드포인트가 API v2로 일관성 있게 업데이트되었습니다. 함수 시그니처는 변경 없이 유지됩니다.


125-128: getSeedsStats는 여전히 v1을 사용하고 있습니다.

다른 reading-records 엔드포인트가 v2로 업그레이드된 반면, getSeedsStatsapi/v1/reading-records/{userBookId}/seed/stats를 유지하고 있습니다. 의도적인 것인지 확인이 필요합니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt (1)

142-159: 수정 화면 네비게이션 로직 확인

emotionTags.firstOrNull()을 사용하여 primaryEmotionDisplayName을 추출하는 방식은 현재 전환 단계에서 적절합니다. API 응답이 primaryEmotion 구조로 완전히 변경되면, recordDetailInfo.primaryEmotion.displayName을 직접 사용하도록 업데이트가 필요합니다. Based on learnings, API 연동 시점에 실제 데이터로 교체될 예정임을 알고 있습니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt (3)

287-293: CollectedSeeds 컴포넌트 확장 구현 확인

isStatsExpanded 상태와 onToggleClick 콜백을 통한 토글 기능이 잘 구현되었습니다. 관련 코드 스니펫(CollectedSeeds.kt lines 43-105)에서 AnimatedVisibility를 사용한 확장/축소 애니메이션도 적절히 적용되어 있습니다.


340-354: RecordItem 클릭 핸들링 개선

onRecordClickonRecordMenuClick 콜백 분리가 잘 되어있습니다. 아이템 전체 클릭과 메뉴 아이콘 클릭을 명확히 구분하여 처리하는 구조입니다.


376-447: 프리뷰 커버리지 개선

빈 상태, 통계 표시 상태, 통계 확장 상태 등 주요 UI 케이스를 커버하는 프리뷰가 추가되었습니다. PrimaryEmotionModel, EmotionModel 등 새로운 모델 구조를 사용한 샘플 데이터가 잘 구성되어 있습니다.

core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt (1)

50-64: 새로운 감정 데이터 클래스 구조

PrimaryEmotionDetailEmotion 데이터 클래스가 적절히 정의되었습니다. @Serializable@SerialName 어노테이션이 일관되게 적용되어 있습니다.

core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt (1)

30-40: 새로운 감정 모델 클래스

PrimaryEmotionModelDetailEmotionModel@Immutable 어노테이션과 함께 적절히 정의되었습니다. 기본값 설정으로 객체 생성 시 유연성을 제공합니다.

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt (3)

35-43: onRecordClick 콜백 추가

아이템 전체 영역 클릭과 메뉴 아이콘 클릭을 분리하여 처리하는 구조가 잘 구현되었습니다. clickable modifier가 Column에 적용되어 전체 영역이 클릭 가능합니다.


88-92: 감정 표시 방식 업데이트

primaryEmotion.displayName을 사용하여 감정 태그를 표시하는 방식이 새로운 모델 구조와 잘 맞습니다. Based on learnings, 이전에 계획했던 emotionTags.joinToString 패턴 대신 단일 primary emotion을 표시하는 방식으로 변경되었습니다.


103-112: 이 함수는 다른 파일에서 활발히 사용 중입니다. ReviewItem.ktCollectedSeeds.kt에서 import되어 호출되고 있으므로 제거하면 안 됩니다.

Likely an incorrect or invalid review comment.

Comment on lines 32 to 35
@SerialName("primaryEmotion")
val primaryEmotion: PrimaryEmotion,
@SerialName("detailEmotions")
val detailEmotions: List<DetailEmotion> = emptyList(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check the actual ReadingRecordsResponse.kt file
fd -e kt -p "*ReadingRecordsResponse*" | head -5

Repository: YAPP-Github/Reed-Android

Length of output: 371


🏁 Script executed:

# Search for primaryEmotion usage patterns
rg -n "primaryEmotion" --type=kt -C 2

Repository: YAPP-Github/Reed-Android

Length of output: 94


🏁 Script executed:

# Look for serialization/deserialization patterns with null handling
rg -n "PrimaryEmotion" --type=kt -C 2

Repository: YAPP-Github/Reed-Android

Length of output: 94


🏁 Script executed:

# Check for any mappers or converters
fd -e kt -p "*[Mm]apper*|*[Cc]onverter*" | head -10

Repository: YAPP-Github/Reed-Android

Length of output: 373


🏁 Script executed:

# Find the ReadingRecordsResponse file using glob
fd --glob "ReadingRecordsResponse.kt"

Repository: YAPP-Github/Reed-Android

Length of output: 165


🏁 Script executed:

# Search for primaryEmotion usage in kotlin files
rg "primaryEmotion" -g "*.kt" -C 2

Repository: YAPP-Github/Reed-Android

Length of output: 15538


🏁 Script executed:

# Search for PrimaryEmotion class definition
rg "PrimaryEmotion" -g "*.kt" -C 2

Repository: YAPP-Github/Reed-Android

Length of output: 11448


🏁 Script executed:

# Get the actual ReadingRecordsResponse.kt content
cat core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt

Repository: YAPP-Github/Reed-Android

Length of output: 1659


primaryEmotion 필드 null 안전성 처리 필요

primaryEmotion이 non-nullable로 선언되어 있으며 기본값이 설정되지 않았습니다. API 응답에서 해당 필드가 null이거나 누락된 경우 역직렬화에 실패합니다. 서버 API가 항상 이 필드를 반환하는지 확인하거나, 안전성을 위해 기본값(PrimaryEmotion("", ""))을 추가하거나 nullable로 변경 후 null 처리를 구현하세요.

🤖 Prompt for AI Agents
In
@core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt
around lines 32 - 35, ReadingRecordsResponse currently declares primaryEmotion:
PrimaryEmotion as non-nullable without a default which will break
deserialization if the API omits or returns null; change the property in
ReadingRecordsResponse (primaryEmotion) to either be nullable (PrimaryEmotion?)
and add null handling where the response is consumed, or give it a safe default
(PrimaryEmotion("", "")) so deserialization always succeeds; update any callers
of primaryEmotion to handle the new nullable type or the new default value and
ensure PrimaryEmotion’s serializer/constructor supports the chosen default.

@easyhooon easyhooon force-pushed the BOOK-481-feature/#242 branch from 8ed93df to 135ab71 Compare January 12, 2026 14:37
@easyhooon
Copy link
Contributor Author

최다 감정 선출에 있어 기획 결정에 따라 서버 API 변경이 필요할 것으로 보여 API 변경 사항 반영시 추가 PR 올려서 대응하도록 하겠습니다

@seoyoon513 API 변경 사항 관련 작업은 #246 해당 PR 혹은 그 이후 PR에서 진행하는게 작업 진행에 있어 충돌 이슈때문도 있고 효율적일것같아 일단 작업 종료하는 것으로 하겠습니다.

Copy link
Contributor

@seoyoon513 seoyoon513 left a comment

Choose a reason for hiding this comment

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

LGTM!!

Emotion.JOY -> JoyTextColor
Emotion.SAD -> SadnessTextColor
Emotion.INSIGHT -> InsightTextColor
Emotion.ETC -> EtcTextColor
Copy link
Contributor

Choose a reason for hiding this comment

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

API 연동하실 때 EmotionCode 기반으로 변경하면 될 것 같습니다!

@seoyoon513
Copy link
Contributor

@easyhooon 변경한 씨앗 리소스가 피그마에서 제공된 리소스랑 다른 것 같아요! 해당 리소스는 기록상세화면에서도 쓰는 리소스라 #246 PR 머지 후 아래 코드에서 끌어다 쓰면 될 것 같습니다~!

:designsystem 모듈 Emotion.kt

val EmotionCode.graphicRes: Int
    get() = when (this) {
        EmotionCode.WARMTH -> R.drawable.img_warmth
        EmotionCode.JOY -> R.drawable.img_joy
        EmotionCode.SADNESS -> R.drawable.img_sadness
        EmotionCode.INSIGHT -> R.drawable.img_insight
        EmotionCode.OTHER -> R.drawable.img_other
    }
image

@easyhooon
Copy link
Contributor Author

변경한 씨앗 리소스가 피그마에서 제공된 리소스랑 다른 것 같아요! 해당 리소스는 기록상세화면에서도 쓰는 리소스라 #246 PR 머지 후 아래 코드에서 끌어다 쓰면 될 것 같습니다~!

메모...

@easyhooon easyhooon merged commit 2a7068c into develop Jan 12, 2026
7 checks passed
@easyhooon easyhooon deleted the BOOK-481-feature/#242 branch January 12, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-481/feat] 도서 상세 감정 축소

3 participants