Skip to content

Conversation

wjdrjs00
Copy link
Member

@wjdrjs00 wjdrjs00 commented Jul 29, 2025

[ PR Content ]

추천 루틴 화면 api 연동 작업을 진행했습니다.
추가로 ui 개발 과정에서 받은 리뷰 사항도 같이 반영했습니다. → #34 (comment)

Related issue

Screenshot 📸

api.mp4

Work Description

  • 추천 루틴 조회 api 연동

To Reviewers 📢

  • 루틴 등록/수정 하기로 네비게이션간에 추천 루틴 id 또는 일반 루틴 id값을 인자로 넘겨받는 상황인데 서로 타입이 Int와 String으로 다르다는 사실을 발견해버렸습니다.. 우선 해당 pr에서는 해결하지 않았는데 추후 반영해서 수정해야할거 같습니다요!
  • 추가로 각 카테고리별 추천루틴에 항상 모든 난이도가 존재하지 않기에 필터링 시 빈화면이 나오는 경우가 존재합니다! <- 이 부분은 기획측에서 현재 엠티뷰 없이 진행하고, 추후 다양한 추천 루틴을 제공하는 방향으로 결정되었기에 참고해주시면 좋겠습니다요!
  • 이외 궁금,의문, 질문 등등 다양한 리뷰 남겨주시면 확인하겠습니다요!

Summary by CodeRabbit

  • 신규 기능

    • 추천 루틴 데이터를 실제 서버에서 비동기로 불러오도록 기능이 추가되었습니다.
    • 추천 루틴의 카테고리, 레벨, 감정 구슬 타입 등 다양한 분류와 필터링이 지원됩니다.
    • 추천 루틴 및 서브루틴의 상세 정보와 UI 모델이 추가되어 화면에서 다양한 정보를 확인할 수 있습니다.
  • UI 개선

    • 추천 루틴 목록에서 설명이 한 줄로 표시되고, 길 경우 말줄임표(...)가 적용됩니다.
    • 추천 난이도 선택 UI가 '추천 레벨' 선택으로 변경되었으며, 관련 바텀시트 및 필터링 동작이 개선되었습니다.
    • 감정 기반 추천 버튼이 조건에 따라 노출됩니다.
  • 리팩터링 및 구조 개선

    • 기존의 난이도(difficulty) 관련 타입과 코드를 추천 레벨(level) 및 카테고리로 전면 교체하였습니다.
    • 더미 데이터 사용을 제거하고, 실제 데이터를 사용하는 구조로 변경되었습니다.
    • UI 상태 및 Intent 구조가 단순화되고, 도메인 모델과 일관성 있게 정비되었습니다.
  • 삭제

    • 기존의 난이도 및 카테고리 관련 임시 타입, 모델, 더미 데이터가 삭제되었습니다.

wjdrjs00 added 30 commits July 26, 2025 16:16
- ProgressBarGradientStartColor 추가
- ProgressBarGradientEndColor 추가
- HomeGradientStartColor 추가
- HomeGradientEndColor 추가
- CoolGray7 색상값 수정
- ProgressBarGradientStartColor 추가
- ProgressBarGradientEndColor 추가
- HomeGradientStartColor 추가
- HomeGradientEndColor 추가
- CoolGray7 색상값 수정
- 감정 선택 화면 UI를 디자인에 맞게 수정
- 각 감정에 해당하는 아이콘을 적용
- 기존의 Box로 구현되어 있던 TopBar들을 BitnagilTopBar로 대체
- FloatingActionButton, FloatingActionMenu 컴포넌트 추가
- 기존: MyPageScreen 및 SettingScreen에서 BitnagilOptionButton 사용 시 padding을 적용
- 변경: BitnagilOptionButton 컴포넌트 내부에서 padding 적용
- RecommendedRoutineDto, RecommendedSubRoutineDto, RecommendRoutinesDto 모델을 추가
@wjdrjs00 wjdrjs00 requested a review from l5x5l July 29, 2025 10:00
@wjdrjs00 wjdrjs00 self-assigned this Jul 29, 2025
@wjdrjs00 wjdrjs00 added ✨ Feature 새로운 기능 구현 🧤 대현 labels Jul 29, 2025
Copy link

coderabbitai bot commented Jul 29, 2025

"""

Walkthrough

추천 루틴(Recommend Routine) API 연동을 위해 도메인, 데이터, 프레젠테이션 계층 전반에 걸쳐 새로운 모델, 데이터 소스, 레포지토리, 서비스, 유즈케이스, UI 모델, ViewModel 로직, 그리고 DI 모듈이 추가 및 수정되었습니다. 기존 더미 데이터 및 난이도/카테고리 관련 타입은 도메인 기반으로 대체되었습니다.

Changes

Cohort / File(s) Change Summary
DI 모듈 및 서비스 바인딩
app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt, app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt, app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt
RecommendRoutineDataSource, RecommendRoutineRepository, RecommendRoutineService에 대한 DI 바인딩 및 프로바이더 추가
데이터 계층: API/DTO/구현
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/datasource/RecommendRoutineDataSource.kt, .../datasourceImpl/RecommendRoutineDataSourceImpl.kt, .../model/response/RecommendRoutinesDto.kt, .../model/response/RecommendedRoutineDto.kt, .../model/response/RecommendedSubRoutineDto.kt, .../repositoryImpl/RecommendRoutineRepositoryImpl.kt, .../service/RecommendRoutineService.kt
추천 루틴 조회 API 연동을 위한 데이터 소스, DTO, 서비스, 레포지토리 구현 및 도메인 변환 확장 함수 추가
도메인 계층: 모델/레포지토리/유즈케이스
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/EmotionMarbleType.kt, .../RecommendCategory.kt, .../RecommendLevel.kt, .../RecommendRoutine.kt, .../RecommendRoutines.kt, .../RecommendSubRoutine.kt, .../repository/RecommendRoutineRepository.kt, .../usecase/FetchRecommendRoutinesUseCase.kt
추천 루틴 도메인 모델, 카테고리, 레벨, 감정 구슬 타입, 레포지토리 인터페이스, 유즈케이스 신규 정의
프레젠테이션 계층: ViewModel/Intent/State/UI 모델/컴포넌트
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineViewModel.kt, .../model/RecommendRoutineIntent.kt, .../model/RecommendRoutineState.kt, .../model/RecommendRoutineUiModel.kt, .../model/RecommendRoutinesUiModel.kt, .../model/RecommendSubRoutineUiModel.kt, .../component/template/RecommendLevelBottomSheet.kt, .../component/block/RecommendRoutineItem.kt, .../RecommendRoutineScreen.kt
ViewModel이 실제 API를 호출하도록 변경, Intent/State/UI모델을 도메인 기반으로 전환, 난이도 관련 UI/로직을 추천 레벨 기반으로 대체, 리스트 아이템 UI 개선(말줄임 등)
프레젠테이션 계층: 타입/모델 삭제
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutine.kt, .../type/RecommendRoutineCategory.kt, .../type/RecommendRoutineDifficulty.kt
기존 더미 데이터용 모델, 카테고리, 난이도 enum 등 삭제

Sequence Diagram(s)

sequenceDiagram
    participant UI
    participant ViewModel
    participant UseCase
    participant Repository
    participant DataSource
    participant Service
    participant API

    UI->>ViewModel: 화면 진입/새로고침
    ViewModel->>UseCase: fetchRecommendRoutinesUseCase()
    UseCase->>Repository: fetchRecommendRoutines()
    Repository->>DataSource: fetchRecommendRoutines()
    DataSource->>Service: fetchRecommendRoutines()
    Service->>API: GET /api/v1/recommend-routines
    API-->>Service: BaseResponse<RecommendRoutinesDto>
    Service-->>DataSource: RecommendRoutinesDto
    DataSource-->>Repository: Result<RecommendRoutinesDto>
    Repository-->>UseCase: Result<RecommendRoutines>
    UseCase-->>ViewModel: Result<RecommendRoutines>
    ViewModel-->>UI: 상태 갱신(추천 루틴 목록, 감정 구슬 등)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Assessment against linked issues

Objective Addressed Explanation
추천 루틴 조회 API 연동 (#50)

Assessment against linked issues: Out-of-scope changes

(해당 PR의 모든 변경 사항은 명시된 이슈(#50)의 "추천 루틴 조회 API 연동" 목적에 부합합니다. 범위를 벗어난 기능적 코드 변경은 발견되지 않았습니다.)

Possibly related PRs

  • [Feature/#30] 추천 루틴 화면 구현 #34: 기존 더미 데이터 기반 추천 루틴 화면 및 상태 관리 코드를 도메인 기반 모델과 실제 API 연동으로 대체하는 PR로, 본 PR과 직접적으로 기능 및 코드 수준에서 연결되어 있습니다.

Poem

🐇
추천 루틴 API와 함께
토끼는 춤을 추네
더미 데이터는 안녕,
진짜 루틴이 반짝!
감정 구슬도 반겨주고
새로운 카테고리와 레벨로
오늘도 코드를 달려가네!

"""

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#50-recommend-routine-api

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

wjdrjs00 added 2 commits July 29, 2025 19:00
[UI/#45] 디자인 컴포넌트 구현 및 ui 화면 퍼블리싱 작업 진행
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: 4

🧹 Nitpick comments (5)
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendCategory.kt (1)

18-21: 문자열 변환 로직이 안전합니다.

entries.find를 사용한 안전한 변환과 기본값(UNKNOWN) 처리가 올바릅니다.

향후 다국어 지원을 고려한다면 displayName을 리소스 ID나 키 값으로 변경하는 것을 검토해보세요:

enum class RecommendCategory(
    val categoryName: String,
    val displayNameResId: Int, // 또는 displayNameKey: String
    val isVisible: Boolean = true,
)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (2)

87-88: TODO 주석: 리플 효과 제거

리플 효과를 제거하려면 clickable modifier에 indication = null을 추가하세요.

         modifier = modifier
-            // todo: 리플효과 제거하기
-            .clickable { onClick() }
+            .clickable(
+                indication = null,
+                interactionSource = remember { MutableInteractionSource() }
+            ) { onClick() }
             .padding(vertical = 8.dp),

필요한 import를 추가하세요:

import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.remember

99-100: TODO 주석 처리 필요

아이콘 변경에 대한 TODO가 있습니다. 디자인 시스템의 아이콘을 사용하거나 이슈로 트래킹하는 것을 권장합니다.

이 TODO를 처리하기 위한 이슈를 생성하시겠습니까?

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineViewModel.kt (1)

33-33: 동시성 문제 가능성

recommendRoutinesvar로 선언되어 있어 동시성 문제가 발생할 수 있습니다.

thread-safe한 방식으로 변경하는 것을 권장합니다:

-private var recommendRoutines: RecommendRoutinesUiModel = RecommendRoutinesUiModel()
+private val recommendRoutines = MutableStateFlow(RecommendRoutinesUiModel())

또는 @Volatile 어노테이션을 추가하세요:

+@Volatile
 private var recommendRoutines: RecommendRoutinesUiModel = RecommendRoutinesUiModel()
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (1)

143-144: TODO 주석 처리 필요

다음 TODO 항목들이 남아있습니다:

  • 리플 효과 제거 (line 143)
  • 아이콘 변경 (line 155)

이슈 트래킹을 위해 별도 태스크로 관리하는 것을 권장합니다.

Also applies to: 155-155

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c836a38 and b8dbc2c.

📒 Files selected for processing (31)
  • app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt (2 hunks)
  • app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt (2 hunks)
  • app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt (2 hunks)
  • app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/datasource/RecommendRoutineDataSource.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/datasourceImpl/RecommendRoutineDataSourceImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendRoutinesDto.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedSubRoutineDto.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/repositoryImpl/RecommendRoutineRepositoryImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/recommendroutine/service/RecommendRoutineService.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/EmotionMarbleType.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendCategory.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutine.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutines.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendSubRoutine.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/repository/RecommendRoutineRepository.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/usecase/FetchRecommendRoutinesUseCase.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (6 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineViewModel.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (4 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutine.kt (0 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineIntent.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineState.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutinesUiModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/type/RecommendRoutineCategory.kt (0 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/type/RecommendRoutineDifficulty.kt (0 hunks)
💤 Files with no reviewable changes (3)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/type/RecommendRoutineDifficulty.kt
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/type/RecommendRoutineCategory.kt
  • presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutine.kt
🧰 Additional context used
🧠 Learnings (6)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (1)

Learnt from: l5x5l
PR: #38
File: presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/component/atom/textbutton/TextButton.kt:30-35
Timestamp: 2025-07-21T10:38:49.104Z
Learning: presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/component/atom/textbutton/TextButton.kt의 TextButton 컴포넌트는 임시로 구현된 컴포넌트로, 디자인 시스템 구현시 대체 예정입니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (2)

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, @parcelize annotation works correctly without explicitly adding Parcelable implementation.

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, @parcelize annotation works correctly without explicitly adding Parcelable implementation.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt (2)

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, @parcelize annotation works correctly without explicitly adding Parcelable implementation.

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, @parcelize annotation works correctly without explicitly adding Parcelable implementation.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineViewModel.kt (1)

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageIntent.kt:6-6
Timestamp: 2025-07-23T13:32:26.263Z
Learning: In the Bitnagil Android project's MVI architecture, Intent classes like LoadMyPageSuccess are named to represent successful API response results that carry loaded data, not just user actions. This naming convention is used for future API integration where the intent will be triggered when my page data loading succeeds from the server.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutinesUiModel.kt (2)

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, @parcelize annotation works correctly without explicitly adding Parcelable implementation.

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, @parcelize annotation works correctly without explicitly adding Parcelable implementation.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineIntent.kt (1)

Learnt from: l5x5l
PR: #41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageIntent.kt:6-6
Timestamp: 2025-07-23T13:32:26.263Z
Learning: In the Bitnagil Android project's MVI architecture, Intent classes like LoadMyPageSuccess are named to represent successful API response results that carry loaded data, not just user actions. This naming convention is used for future API integration where the intent will be triggered when my page data loading succeeds from the server.

🧬 Code Graph Analysis (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineViewModel.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/mviviewmodel/MviViewModel.kt (1)
  • sendIntent (30-37)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
  • RecommendLevelBottomSheet (25-75)
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/datasourceImpl/RecommendRoutineDataSourceImpl.kt (1)
data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (1)
  • safeApiCall (10-25)
🔇 Additional comments (32)
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutine.kt (1)

3-10: 도메인 모델 구조가 잘 설계되었습니다.

불변 데이터 클래스로 구현되어 있고, RecommendLevelRecommendSubRoutine 같은 도메인 특화 타입을 사용하여 타입 안정성을 확보했습니다. 클린 아키텍처 원칙에 잘 부합하는 구현입니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendSubRoutine.kt (1)

3-6: 간결하고 명확한 도메인 모델입니다.

서브루틴을 표현하는 최소한의 필수 속성만 포함되어 있으며, 불변 설계로 일관성이 유지되고 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (2)

19-19: 텍스트 오버플로우 처리를 위한 적절한 import 추가입니다.


63-64: 루틴 설명 텍스트의 오버플로우 처리가 잘 구현되었습니다.

maxLines = 1overflow = Ellipsis를 통해 긴 설명 텍스트가 레이아웃을 깨뜨리지 않도록 하고, 일관된 단일 라인 표시를 보장합니다. UI 일관성 측면에서 좋은 개선입니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/EmotionMarbleType.kt (1)

3-10: 감정 상태를 나타내는 enum이 명확하게 정의되었습니다.

6가지 감정 상태(CALM, VITALITY, LETHARGY, ANXIETY, SATISFACTION, FATIGUE)가 직관적이고 의미가 명확한 이름으로 정의되어 있어 타입 안전성을 제공합니다.

data/src/main/java/com/threegap/bitnagil/data/recommendroutine/datasource/RecommendRoutineDataSource.kt (1)

5-7: 데이터 소스 인터페이스가 잘 설계되었습니다.

suspend 함수를 사용한 비동기 처리와 Result 타입을 통한 에러 핸들링이 적절하게 구현되어 있습니다. 레포지토리와 실제 데이터 가져오기 구현 사이의 깔끔한 추상화를 제공합니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/repository/RecommendRoutineRepository.kt (1)

1-7: 잘 설계된 도메인 레포지토리 인터페이스입니다.

클린 아키텍처 원칙을 잘 따르고 있으며, Result 패턴을 사용한 에러 핸들링과 suspend 함수를 통한 비동기 처리가 적절합니다.

app/src/main/java/com/threegap/bitnagil/di/data/ServiceModule.kt (2)

6-6: 적절한 import 추가입니다.

새로운 RecommendRoutineService에 대한 import가 올바르게 추가되었습니다.


53-56: 일관된 DI 패턴을 따르고 있습니다.

기존 서비스 프로바이더들과 동일한 패턴을 따라 @Auth Retrofit 인스턴스를 사용하여 RecommendRoutineService를 제공하고 있습니다. 적절한 싱글톤 스코프와 어노테이션이 적용되었습니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/usecase/FetchRecommendRoutinesUseCase.kt (1)

7-12: 깔끔한 유즈케이스 구현입니다.

단일 책임 원칙을 잘 따르고 있으며, Kotlin의 operator fun invoke() 컨벤션을 적절히 활용했습니다. 현재는 레포지토리에 대한 단순한 위임이지만, 향후 추가적인 도메인 로직이 필요할 때 확장하기 좋은 구조입니다.

app/src/main/java/com/threegap/bitnagil/di/data/RepositoryModule.kt (2)

6-6: 필요한 import가 적절히 추가되었습니다.

RecommendRoutineRepositoryImpl과 RecommendRoutineRepository에 대한 import가 올바르게 추가되었습니다.

Also applies to: 12-12


45-47: 일관된 레포지토리 바인딩 패턴입니다.

기존 레포지토리 바인딩들과 동일한 패턴을 따라 @BINDS@singleton 어노테이션을 적절히 사용하여 RecommendRoutineRepository 인터페이스를 구현체에 바인딩하고 있습니다.

app/src/main/java/com/threegap/bitnagil/di/data/DataSourceModule.kt (2)

11-12: 적절한 import 추가입니다.

RecommendRoutineDataSource 인터페이스와 구현체에 대한 import가 올바르게 추가되었습니다.


51-53: 일관된 데이터 소스 바인딩 패턴입니다.

기존 데이터 소스 바인딩들과 동일한 패턴을 따라 @BINDS@singleton 어노테이션을 적절히 사용하여 RecommendRoutineDataSource 인터페이스를 구현체에 바인딩하고 있습니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutines.kt (1)

3-6: 깔끔한 도메인 모델 설계입니다.

추천 루틴을 카테고리별로 그룹화하는 Map 구조와 선택적 감정 마블 타입을 포함한 도메인 모델이 잘 설계되었습니다. 비즈니스 로직을 명확하게 표현하고 있습니다.

data/src/main/java/com/threegap/bitnagil/data/recommendroutine/service/RecommendRoutineService.kt (1)

7-10: Retrofit 서비스 인터페이스가 적절하게 구현되었습니다.

suspend 함수를 사용한 코루틴 지원과 BaseResponse 래퍼를 통한 일관된 API 응답 처리가 잘 되어 있습니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt (2)

12-15: fromString 메서드의 기본값 처리를 검토해보세요.

enum 설계는 잘 되어 있지만, fromString에서 매칭되지 않는 경우 LEVEL1로 기본값을 반환하는 것이 적절한지 검토가 필요합니다. 잘못된 데이터를 마스킹할 수 있습니다.

다음과 같이 예외를 던지거나 nullable을 반환하는 것을 고려해보세요:

 companion object {
-    fun fromString(levelName: String): RecommendLevel =
-        entries.find { it.level == levelName } ?: LEVEL1
+    fun fromString(levelName: String): RecommendLevel =
+        entries.find { it.level == levelName } 
+            ?: throw IllegalArgumentException("Unknown level: $levelName")
 }

또는:

 companion object {
+    fun fromStringOrNull(levelName: String): RecommendLevel? =
+        entries.find { it.level == levelName }
+        
     fun fromString(levelName: String): RecommendLevel =
-        entries.find { it.level == levelName } ?: LEVEL1
+        fromStringOrNull(levelName) ?: LEVEL1
 }

7-9: 한국어 표시명이 잘 정의되어 있습니다.

각 레벨의 의미를 명확하게 전달하는 한국어 표시명이 사용자 친화적으로 작성되었습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt (2)

7-11: @parcelize를 사용한 UI 모델이 적절하게 구현되었습니다.

Android의 컴포넌트 간 데이터 전달을 위한 Parcelable 구현이 깔끔하게 되어 있습니다.


13-17: 도메인 모델에서 UI 모델로의 변환 로직이 명확합니다.

확장 함수를 통한 도메인 모델 변환이 간결하고 이해하기 쉽게 구현되었습니다.

data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedSubRoutineDto.kt (2)

7-13: DTO 구조가 적절하게 설계되었습니다.

@serializable과 @SerialName을 사용한 JSON 직렬화 설정이 올바르게 구현되어 있고, API 응답의 상세한 필드명과 도메인 모델의 간결한 필드명 간의 분리가 잘 되어 있습니다.


15-19: 도메인 변환 로직이 명확합니다.

toDomain() 확장 함수를 통한 DTO에서 도메인 모델로의 변환이 간단하고 명확하게 구현되었습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutinesUiModel.kt (2)

9-13: UI 모델 구조가 잘 설계되었습니다.

데이터 클래스의 구조가 명확하고, @Parcelize 어노테이션을 통한 Parcelable 구현이 적절합니다. Map을 사용하여 카테고리별 루틴 그룹핑이 효율적으로 구현되었습니다.


15-21: 도메인-UI 모델 변환 로직이 올바릅니다.

확장 함수를 통한 변환 패턴이 Clean Architecture의 원칙에 잘 부합하며, mapValues를 사용한 컬렉션 변환이 효율적입니다.

data/src/main/java/com/threegap/bitnagil/data/recommendroutine/datasourceImpl/RecommendRoutineDataSourceImpl.kt (1)

9-17: 데이터소스 구현이 올바릅니다.

DI 패턴과 safeApiCall을 활용한 안전한 API 호출 구현이 적절합니다. 코드가 간결하고 책임이 명확히 분리되어 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (2)

8-16: UI 모델 정의가 적절합니다.

모든 필요한 속성들이 명확히 정의되어 있고, @Parcelize를 통한 Parcelable 구현이 올바릅니다. 도메인 레벨 타입(RecommendLevel)을 적절히 활용하고 있습니다.


18-26: 도메인-UI 변환 로직이 완전합니다.

모든 속성이 올바르게 매핑되고 있으며, 서브루틴 리스트의 변환도 적절히 처리되었습니다. 확장 함수 패턴이 일관되게 적용되었습니다.

domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendCategory.kt (1)

3-16: enum 설계가 잘 되어 있습니다.

카테고리별 속성 정의가 명확하고, isVisible 플래그를 통한 가시성 제어가 효과적입니다. 숨겨진 카테고리들(OUTING_REPORT, UNKNOWN)의 처리도 적절합니다.

data/src/main/java/com/threegap/bitnagil/data/recommendroutine/repositoryImpl/RecommendRoutineRepositoryImpl.kt (1)

9-15: Repository 구현이 깔끔합니다.

Repository 패턴이 올바르게 적용되었고, Result.map을 사용한 DTO-도메인 모델 변환이 효율적입니다. 데이터소스에 위임하는 방식으로 책임이 명확히 분리되어 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineIntent.kt (1)

7-15: Intent 클래스 구조가 적절합니다

MVI 아키텍처에 맞게 Intent가 잘 정의되었습니다. 도메인 모델 타입을 사용하고 명명 규칙이 일관성 있게 적용되었습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineState.kt (1)

3-6: 도메인 모델로의 마이그레이션이 적절합니다 👍

프레젠테이션 레이어에서 도메인 모델(RecommendCategory, RecommendLevel, EmotionMarbleType)을 직접 사용하는 것은 클린 아키텍처 원칙을 잘 따르고 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (1)

45-45: 네비게이션 파라미터 타입 불일치 문제

PR 설명에서 언급하신 대로 추천 루틴 ID(Int)와 일반 루틴 ID(String)의 타입 불일치가 확인됩니다. 향후 일관성 있는 타입으로 통일이 필요합니다.

@l5x5l
Copy link
Contributor

l5x5l commented Jul 29, 2025

고생하셨습니다! 미리보기로 올려주신 영상에서 ripple 클릭 효과가 보이는데 이 부분만 제거해주시면 좋을 것 같습니다!

@wjdrjs00
Copy link
Member Author

고생하셨습니다! 미리보기로 올려주신 영상에서 ripple 클릭 효과가 보이는데 이 부분만 제거해주시면 좋을 것 같습니다!

해당 부분은 디벨롭 브랜치로 병합되면 자연스럽게 해결될거같습니다요!

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 (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)

138-147: 색상 명시가 불필요할 수 있습니다

BitnagilTheme.colors.black을 명시적으로 지정했는데, 이미 테마의 기본값일 가능성이 높습니다.

다음과 같이 간소화할 수 있습니다:

-                    Text(
-                        text = "버전 ",
-                        color = BitnagilTheme.colors.black,
-                        style = BitnagilTheme.typography.body1Regular,
-                    )
-                    Text(
-                        text = state.version,
-                        color = BitnagilTheme.colors.black,
-                        style = BitnagilTheme.typography.body1SemiBold,
-                    )
+                    Text(
+                        text = "버전 ",
+                        style = BitnagilTheme.typography.body1Regular,
+                    )
+                    Text(
+                        text = state.version,
+                        style = BitnagilTheme.typography.body1SemiBold,
+                    )
presentation/src/main/java/com/threegap/bitnagil/presentation/home/component/atom/EmotionBall.kt (1)

27-52: 코드 중복을 줄이고 일관성을 개선해보세요.

두 분기에서 Image 컴포넌트가 중복되고 있으며, 하드코딩된 크기와 fillMaxSize() 조합이 혼재되어 있습니다.

다음과 같이 리팩토링하여 중복을 줄일 수 있습니다:

@Composable
fun EmotionBall(
    emotionType: EmotionBallType?,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
+    val ballSize = 172.dp
+    val painter = if (emotionType != null) {
+        painterResource(emotionType.drawableId)
+    } else {
+        painterResource(R.drawable.default_ball)
+    }
+    
+    val imageModifier = modifier
+        .size(ballSize)
+        .clickableWithoutRipple { onClick() }
+        .let {
+            if (emotionType != null) {
+                it.padding(10.dp)
+                  .fillMaxSize()
+                  .shadow(
+                      elevation = 24.dp,
+                      shape = CircleShape,
+                      ambientColor = emotionType.ambientColor,
+                      spotColor = emotionType.spotColor,
+                  )
+            } else {
+                it.fillMaxSize()
+            }
+        }
+
-    if (emotionType != null) {
-        Image(
-            painter = painterResource(emotionType.drawableId),
-            contentDescription = null,
-            modifier = modifier
-                .size(172.dp)
-                .clickableWithoutRipple { onClick() }
-                .padding(10.dp)
-                .fillMaxSize()
-                .shadow(
-                    elevation = 24.dp,
-                    shape = CircleShape,
-                    ambientColor = emotionType.ambientColor,
-                    spotColor = emotionType.spotColor,
-                ),
-        )
-    } else {
-        Image(
-            painter = painterResource(R.drawable.default_ball),
-            contentDescription = null,
-            modifier = modifier
-                .size(172.dp)
-                .clickableWithoutRipple { onClick() }
-                .fillMaxSize(),
-        )
-    }
+    Image(
+        painter = painter,
+        contentDescription = null,
+        modifier = imageModifier,
+    )
}
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilCheckBox.kt (1)

25-56: 체크박스 컴포넌트 구현이 잘 되어 있습니다.

컴포넌트의 구조와 로직이 명확하고, ripple 효과 제거를 통한 일관된 UI 경험을 제공합니다. 하지만 몇 가지 개선사항을 제안합니다:

  1. modifier 파라미터가 두 번 사용되고 있습니다 (line 35, 53)
  2. 접근성을 위한 contentDescription 개선이 필요합니다

다음과 같이 개선할 수 있습니다:

 @Composable
 fun BitnagilCheckBox(
     checked: Boolean,
     modifier: Modifier = Modifier,
     onCheckedChange: ((Boolean) -> Unit)? = null,
     colors: BitnagilCheckBoxColor = BitnagilCheckBoxColor.terms(),
+    contentDescription: String? = null,
 ) {
     val iconColor = if (checked) colors.checkedColor else colors.uncheckedColor

     Box(
         modifier = modifier
             .let {
                 if (onCheckedChange != null) {
                     it.clickableWithoutRipple { onCheckedChange(!checked) }
                 } else {
                     it
                 }
             }
             .background(
                 color = BitnagilTheme.colors.white,
                 shape = RoundedCornerShape(6.dp),
             ),
         contentAlignment = Alignment.Center,
     ) {
         Image(
             imageVector = ImageVector.vectorResource(R.drawable.ic_check),
-            contentDescription = null,
+            contentDescription = contentDescription ?: if (checked) "선택됨" else "선택 안됨",
             colorFilter = ColorFilter.tint(iconColor),
-            modifier = modifier.size(24.dp),
+            modifier = Modifier.size(24.dp),
         )
     }
 }

@wjdrjs00 wjdrjs00 merged commit 373c39d into develop Jul 29, 2025
2 checks passed
@wjdrjs00 wjdrjs00 deleted the feature/#50-recommend-routine-api branch July 29, 2025 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ Feature 새로운 기능 구현 🧤 대현
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] 추천 루틴 API 연동
2 participants