⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@easyhooon
Copy link
Contributor

@easyhooon easyhooon commented Jan 15, 2026

🔗 관련 이슈

📙 작업 설명

  • 도서 상세 API 변경 사항 반영 및 V2 suffix 제거
  • 대표 감정을 산출하는 로직 및 컴포넌트 제거
  • Model 모듈내 model들 @Stable -> @Immutable로 어노테이션 적용 변경

🧪 테스트 내역 (선택)

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

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

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

  • 독서 기록 목록 조회 V2 API내에 독서 기록 DTO의 도서 정보가 누락되어 내려오는 문제가 있어 현재 기록 수정 화면등에 진입하면 책 정보가 제대로 기입되지 않는 문제가 있습니다.(수정 필요)
  • 서버에서 내려오는 감정관련 DTO가 v1/v2 의 간극때문에 타입이 나뉘어있는데 앱내 Domain Model에서는 EmotionCode를 통해 동일한 모델로 처리할 수 있도록 했습니다.(displayName 추가)
  • 대표 감정을 산출하는 로직이 서버로 옮겨짐에 따라, 네이티브에서 대표 감정을 산출하는 로직 및 컴포넌트 제거하였습니다.
  • Domain Model들 내부 프로퍼티들이 수정될 가능성이 없다고 판단되어 @Stable -> @Immutable로 변경했습니다. 이전에 개발시엔 이 두 어노테이션의 차이를 잘 몰라서, @Immutable사용이 조심스러워 사용하지 않았으나, 이제는 사용해도 된다고 판단하였습니다.

Summary by CodeRabbit

  • New Features

    • 대표 감정(representativeEmotion) 및 주요/상세 감정(primary/detailEmotions) 노출과 UI 반영
  • Bug Fixes

    • 페이지 번호를 nullable로 처리해 페이지 정보 없는 기록도 정상 표시
  • Refactor

    • 독서 기록 데이터 구조 및 네트워크 응답 간소화 및 v2 경로 반영으로 UI 연동 정비
    • 감정 코드 체계화 및 색·아이콘 연동 개선
  • Chores

    • 불필요 유틸/컴포저블 제거 및 상태 타입 패키지 정리

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Walkthrough

ReadingRecord 관련 모델·네트워크·매퍼·UI가 V2 규격(대표 감정 primaryEmotion, detailEmotions, nullable pageNumber)로 통합·갱신되었고, 감정 분석 유틸·여러 컴포저블 안정성 덤프 항목이 제거/재분류되었습니다.

Changes

Cohort / File(s) 변경 사항
Core API / Repository
core/data/api/.../RecordRepository.kt
반환 타입을 Result<ReadingRecordModelV2>Result<ReadingRecordModel>로 교체 (post/getDetail/edit).
Core Network / Service
core/network/.../ReedService.kt, core/network/.../response/ReadingRecordsResponse.kt
네트워크 DTO 재정의: ReadingRecordV2/PrimaryEmotion 도입, pageNumber nullable화, 일부 응답 모델 삭제(RecordDetailResponse, RecordRegisterResponse), 엔드포인트 경로 v1 → v2 조정.
Core Data Impl / Mapper
core/data/impl/.../ResponseToModel.kt
매핑 로직 정리: ReadingRecordsResponse.toModel()representativeEmotion 포함, Category→EmotionCode 기반 매핑으로 변경, 레거시 V2 매핑 제거.
Core Model
core/model/.../ReadingRecordsModel.kt, core/model/.../EmotionModel.kt, core/model/.../SeedModel.kt, core/model/state/*
ReadingRecordModel 구조 변경(primaryEmotion, detailEmotions, pageNumber?), ReadingRecordModelV2 제거, EmotionEmotionCode(displayName)로 전환, 일부 enum/상태 타입을 state 패키지로 이동 및 어노테이션(@Stable/@immutable) 조정.
Feature Detail (UI / Presenter / Components)
feature/detail/.../BookDetailPresenter.kt, BookDetailUi.kt, RecordItem.kt, CollectedSeeds.kt, SeedItem.kt, RecordDetailPresenter.kt, RecordDetailUi*.kt, BookDetailUiState.kt
UI·프리뷰 갱신: emotionTagsprimaryEmotion 사용, representativeEmotion 필드 추가·전파, CollectedSeeds 시그니처 변경(대표 감정 파라미터 추가), nullable pageNumber 처리, EmotionCode/PrimaryEmotionModel 기반 리소스·색상·텍스트 매핑, 감정 분석 컴포저블 제거.
Common / Utilities
core/common/.../EmotionAnalyzer.kt
감정 분석 유틸(EmotionAnalyzer 및 관련 타입/함수) 전체 삭제.
Design System / Theme
core/designsystem/.../EmotionCode.kt, core/designsystem/.../theme/Color.kt
색·리소스 매핑을 EmotionCode 기반으로 이전, Etc*Other* 명칭 변경.
Stability / Generated API Dumps
feature/*/*.stability, core/designsystem/stability
다수 composable/스테이빌리티 항목 삭제 또는 파라미터·스테이빌리티 재분류(스킵불가/UNSTABLE).
Imports / Small refactors
여러 파일 (AuthRepository.kt, UserRepository.kt, presenters 등)
AutoLoginState, OnboardingState, UserState 등 타입의 import 경로를 com.ninecraft.booket.core.model.state로 이동 및 중복 import 정리.

Sequence Diagram(s)

sequenceDiagram
  participant UI as UI (Presenter/Composables)
  participant Repo as RecordRepository
  participant Service as ReedService
  participant Network as Network (HTTP)
  participant Mapper as ResponseToModel

  UI->>Repo: 요청 (post/get/edit) {userBookId / readingRecordId, payload}
  Repo->>Service: 호출 (api/v2/reading-records/...)
  Service->>Network: HTTP request
  Network-->>Service: HTTP response (ReadingRecord / ReadingRecordV2 shape)
  Service-->>Repo: 응답 DTO
  Repo->>Mapper: response.toModel()
  Mapper-->>Repo: ReadingRecordModel (primaryEmotion, detailEmotions, pageNumber?)
  Repo-->>UI: Result<ReadingRecordModel>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 깡총깡총, 모델들이 새로 피었네,
감정은 중심으로, 디테일은 꽃잎처럼,
페이지는 가볍게 nullable로 춤추고,
네트워크는 v2로 길을 바꿨네,
토끼가 당근 들고 새 코드를 축하하네 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Pull request title clearly describes the main change: migrating book detail API to V2 and removing V2 suffix from DTOs/Models.
Linked Issues check ✅ Passed Code changes fully implement the requirements from issue #250: migrated DTOs/Models to V2, removed V2 suffix, normalized emotion handling with EmotionCode.
Out of Scope Changes check ✅ Passed All changes align with the linked issue objectives. Model annotation updates (@Stable to @Immutable), state package reorganization, and stability file updates are supporting changes for the main migration work.

✏️ 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.

@easyhooon easyhooon marked this pull request as draft January 16, 2026 06:09
@easyhooon
Copy link
Contributor Author

독서 기록 목록 조회 V2 API내에 독서 기록 DTO의 도서 정보가 누락되어 내려오는 문제가 있어 현재 기록 수정 화면등에 진입하면 책 정보가 제대로 기입되지 않는 문제가 있습니다.(수정 필요)

@seoyoon513 이거 생각해보니, 화면내에 모든 기록들은 같은 도서 정보를 가지고 있으므로 도서 상세화면 진입시 받아온 도서 정보를 매핑해서 문제 해결할 수 있었습니다. 해당 커밋의 방법처럼 fallback 처리만 적용하면 문제 없이 정상 동작하여 작업 마무리 했습니다! 0575085

@easyhooon easyhooon marked this pull request as ready for review January 22, 2026 13:19
@seoyoon513
Copy link
Contributor

@easyhooon /seed/stats API에서 기타 감정 개수가 내려오지 않는 것 같은데 확인 된 부분일까요?

{
  "categories": [
    {
      "name": "따뜻함",
      "count": 0
    },
    {
      "name": "즐거움",
      "count": 2
    },
    {
      "name": "슬픔",
      "count": 2
    },
    {
      "name": "깨달음",
      "count": 0
    }
  ]
}

@Serializable
data class ReadingRecordsResponse(
@SerialName("representativeEmotion")
val representativeEmotion: PrimaryEmotion,
Copy link
Contributor

Choose a reason for hiding this comment

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

기록 없이 책만 등록했을 때, 해당 필드 null로 내려와서 null 체크 필요할 것 같습니다~!

Copy link
Contributor Author

@easyhooon easyhooon Jan 24, 2026

Choose a reason for hiding this comment

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

아 그렇겠네요. 기록이 없을때가 있군요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

c012c78
반영 완료!

책만 등록하고 기록을 안 할 경우, 대표 감정은 null로 내려옴
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: 2

🤖 Fix all issues with AI agents
In
`@core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt`:
- Around line 277-281: Category.toEmotionModel() currently returns null when
EmotionCode.fromDisplayName(name) fails, which causes mapNotNull to drop that
category and lose stats; change this to fall back to a default/OTHER EmotionCode
(e.g., EmotionCode.OTHER) instead of returning null and construct the
EmotionModel with that fallback so unknown names are aggregated under OTHER;
update references in EmotionModel and tests if any to expect OTHER for unknown
display names.

In
`@feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt`:
- Around line 286-290: state.representativeEmotion is being force-unwrapped with
!! when calling CollectedSeeds which can crash if null; change the call site in
BookDetailUi (inside the if (state.hasEmotionData()) block) to handle null
safely: either skip rendering CollectedSeeds when representativeEmotion == null,
pass a safe default (e.g., an Emotion.empty or neutral value) via
representativeEmotion ?: defaultEmotion, or update CollectedSeeds to accept a
nullable representativeEmotion and handle it internally; ensure you touch the
CollectedSeeds call (seedsStats = state.seedsStats, representativeEmotion =
state.representativeEmotion, isStatsExpanded = state.isStatsExpanded) and
corresponding CollectedSeeds signature/usage so no !! is used.

Comment on lines 277 to 281
internal fun Category.toEmotionModel(): EmotionModel? {
val emotion = Emotion.fromDisplayName(name) ?: return null
return EmotionModel(
name = emotion,
code = EmotionCode.fromDisplayName(name) ?: return null,
count = count,
)
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

알 수 없는 감정명이 들어오면 통계가 누락될 수 있습니다.
fromDisplayName 실패 시 null을 반환해 mapNotNull에서 제거되므로 새로운 감정(또는 “기타”)이 통째로 빠질 수 있습니다. 최소한 OTHER로 폴백하는 편이 안전합니다.

✅ 폴백 처리 예시
- return EmotionModel(
-     code = EmotionCode.fromDisplayName(name) ?: return null,
-     count = count,
- )
+ val code = EmotionCode.fromDisplayName(name) ?: EmotionCode.OTHER
+ return EmotionModel(
+     code = code,
+     count = count,
+ )
🤖 Prompt for AI Agents
In
`@core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt`
around lines 277 - 281, Category.toEmotionModel() currently returns null when
EmotionCode.fromDisplayName(name) fails, which causes mapNotNull to drop that
category and lose stats; change this to fall back to a default/OTHER EmotionCode
(e.g., EmotionCode.OTHER) instead of returning null and construct the
EmotionModel with that fallback so unknown names are aggregated under OTHER;
update references in EmotionModel and tests if any to expect OTHER for unknown
display names.

Comment on lines 286 to 290
if (state.hasEmotionData()) {
CollectedSeeds(
seedsStats = state.seedsStats,
representativeEmotion = state.representativeEmotion!!,
isStatsExpanded = state.isStatsExpanded,
Copy link

@coderabbitai coderabbitai bot Jan 24, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

대표 감정 null 가능성에 대비한 안전 처리 필요
representativeEmotion이 nullable인데 !!로 강제 언래핑하고 있습니다. 서버가 null을 내려주거나 hasEmotionData()가 대표 감정의 null 여부를 보장하지 않으면 런타임 크래시가 발생할 수 있습니다. 안전하게 가드하거나 CollectedSeeds가 nullable을 수용하도록 조정하는 편이 안전합니다.

🛠️ 안전 가드 예시
- if (state.hasEmotionData()) {
-     CollectedSeeds(
-         seedsStats = state.seedsStats,
-         representativeEmotion = state.representativeEmotion!!,
-         isStatsExpanded = state.isStatsExpanded,
-         onToggleClick = { ... },
-     )
- } else {
-     Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing10))
- }
+ if (state.hasEmotionData() && state.representativeEmotion != null) {
+     CollectedSeeds(
+         seedsStats = state.seedsStats,
+         representativeEmotion = state.representativeEmotion,
+         isStatsExpanded = state.isStatsExpanded,
+         onToggleClick = { ... },
+     )
+ } else {
+     Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing10))
+ }
📝 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.

Suggested change
if (state.hasEmotionData()) {
CollectedSeeds(
seedsStats = state.seedsStats,
representativeEmotion = state.representativeEmotion!!,
isStatsExpanded = state.isStatsExpanded,
if (state.hasEmotionData() && state.representativeEmotion != null) {
CollectedSeeds(
seedsStats = state.seedsStats,
representativeEmotion = state.representativeEmotion,
isStatsExpanded = state.isStatsExpanded,
🤖 Prompt for AI Agents
In
`@feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt`
around lines 286 - 290, state.representativeEmotion is being force-unwrapped
with !! when calling CollectedSeeds which can crash if null; change the call
site in BookDetailUi (inside the if (state.hasEmotionData()) block) to handle
null safely: either skip rendering CollectedSeeds when representativeEmotion ==
null, pass a safe default (e.g., an Emotion.empty or neutral value) via
representativeEmotion ?: defaultEmotion, or update CollectedSeeds to accept a
nullable representativeEmotion and handle it internally; ensure you touch the
CollectedSeeds call (seedsStats = state.seedsStats, representativeEmotion =
state.representativeEmotion, isStatsExpanded = state.isStatsExpanded) and
corresponding CollectedSeeds signature/usage so no !! is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

state.hasEmotionData() 감정이 있다는건 대표 감정이 있다는 의미라 !! 붙혀도 괜찮아

Copy link

Choose a reason for hiding this comment

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

@easyhooon, 알겠습니다! hasEmotionData()representativeEmotion의 null 여부를 이미 보장하고 있다면 !! 사용이 안전하네요. 도메인 로직에 대한 설명 감사합니다. 👍


✏️ Learnings added
Learnt from: easyhooon
Repo: YAPP-Github/Reed-Android PR: 253
File: feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt:286-290
Timestamp: 2026-01-25T07:21:02.190Z
Learning: In BookDetailUi.kt (Reed-Android project), the `hasEmotionData()` function guarantees that `state.representativeEmotion` is not null when it returns true, so force-unwrapping with `!!` is safe within the `if (state.hasEmotionData())` block.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
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.

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

@easyhooon 씨앗 통계 API 이슈 확인 되면 머지 진행하시죠~!! 고생하셨습니다bb

@easyhooon
Copy link
Contributor Author

easyhooon commented Jan 27, 2026

씨앗 통계 API 이슈 확인 되면 머지 진행하시죠~!! 고생하셨습니다bb

@seoyoon513 씨앗통계 API endpoint v2로 업데이트 되어 기타 감정까지 잘 내려오는 것 확인했습니다~

{"categories":[{"name":"따뜻함","count":0},{"name":"즐거움","count":0},{"name":"슬픔","count":0},{"name":"깨달음","count":0},{"name":"기타","count":2}]}

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-490/feat] 도서 상세 API 변경 사항 반영 및 V2 suffix 제거

3 participants