Skip to content

Conversation

l5x5l
Copy link
Contributor

@l5x5l l5x5l commented Aug 21, 2025

[ PR Content ]

루틴 작성시 누락된 추천 루틴 타입을 추가합니다.

Related issue

Screenshot 📸

KakaoTalk_Video_2025-08-21-21-11-49.mp4

Work Description

  • 루틴 작성 API 호출시 추천 루틴 타입을 같이 전송하도록 수정
  • "등록하기"버튼을 빠르게 2번 클릭시 동일한 내용의 루틴이 2번 이상 등록될 수 있었던 문제 수정

To Reviewers 📢

  • 궁금한 점 있으면 코멘트 부탁드립니다!

Summary by CodeRabbit

  • 새로운 기능
    • 루틴 등록 시 추천 루틴 유형을 선택/전달할 수 있도록 지원합니다. 추천 루틴을 불러오면 해당 유형이 자동 반영됩니다.
  • 버그 수정
    • 루틴 등록 중 중복 제출을 방지하여 동일 루틴이 여러 번 생성되는 문제를 예방했습니다.
  • 개선
    • 루틴 작성 흐름에서 불필요한 상태 업데이트를 줄여 전환 및 내비게이션 응답성을 향상했습니다.

@l5x5l l5x5l requested a review from wjdrjs00 August 21, 2025 12:15
@l5x5l l5x5l self-assigned this Aug 21, 2025
@l5x5l l5x5l added 🐞 Fix 버그 수정 세환 labels Aug 21, 2025
Copy link

coderabbitai bot commented Aug 21, 2025

Walkthrough

등록 플로우 전반에 recommendedRoutineType(String?)을 추가했습니다. 프레젠테이션(MVI 상태/인텐트/뷰모델), 도메인(UseCase/Repository 인터페이스), 데이터(RepositoryImpl/Request DTO) 계층까지 파라미터를 전달하도록 시그니처와 요청 모델을 확장했습니다. 뷰모델은 일부 리듀서 반환 타입과 등록 중복 방지 로직이 조정되었습니다.

Changes

Cohort / File(s) Summary
Data: Request DTO
data/.../writeroutine/model/request/RegisterRoutineRequest.kt
RegisterRoutineRequest에 @SerialName("recommendedRoutineType") val recommendedRoutineType: String? 필드 추가. 직렬화 페이로드 확장.
Data: Repository 구현
data/.../writeroutine/repositoryImpl/WriteRoutineRepositoryImpl.kt
registerRoutine 시그니처에 recommendedRoutineType: String? 추가 및 Request로 전달. 나머지 로직 동일.
Domain: Repository 인터페이스
domain/.../writeroutine/repository/WriteRoutineRepository.kt
registerRoutine에 recommendedRoutineType: String? 파라미터 추가. 반환 타입 변화 없음.
Domain: UseCase
domain/.../writeroutine/usecase/RegisterRoutineUseCase.kt
invoke에 recommendedRoutineType: String? 추가, Repository로 전달.
Presentation: Intent/State
presentation/.../writeroutine/model/mvi/WriteRoutineIntent.kt, presentation/.../writeroutine/model/mvi/WriteRoutineState.kt
SetRoutine 인텐트와 WriteRoutineState에 recommendedRoutineType: String? 추가. 초기값 null 설정.
Presentation: ViewModel
presentation/.../writeroutine/WriteRoutineViewModel.kt
추천 루틴 로드 시 categoryName을 상태에 설정, 일반 편집 시 null. Register 호출 시 상태의 recommendedRoutineType 전달. reduceState 반환 타입을 WriteRoutineState?로 변경, 등록 중복 방지 가드 추가, 성공 시 상태 미변경(사이드이펙트 기반).

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant VM as WriteRoutineViewModel
  participant UC as RegisterRoutineUseCase
  participant Repo as WriteRoutineRepository
  participant DS as RemoteDataSource/API

  User->>VM: 추천 루틴 기반 등록 시작
  VM->>VM: state.recommendedRoutineType = routine.recommendedRoutineType.categoryName
  VM->>UC: invoke(name, repeatDay, startTime, dates, subRoutines, recommendedRoutineType)
  UC->>Repo: registerRoutine(..., recommendedRoutineType)
  Repo->>DS: POST RegisterRoutineRequest{..., recommendedRoutineType}
  DS-->>Repo: Result<Unit>
  Repo-->>UC: Result<Unit>
  UC-->>VM: Result<Unit>
  VM->>VM: 성공 시 사이드이펙트로 네비게이션(상태 유지)
Loading
sequenceDiagram
  participant VM as WriteRoutineViewModel
  Note over VM: 등록 중복 방지 가드(loading==true 시 조기 종료)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
루틴 등록 시 추천 루틴 타입 추가 및 전달 일원화 (#120)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
reduceState의 반환 타입을 WriteRoutineState → WriteRoutineState?로 변경 및 성공 시 상태 미변경 처리 (presentation/.../writeroutine/WriteRoutineViewModel.kt) 추천 루틴 타입 전달과 직접 관련 없는 MVI 제어 흐름 변경입니다.
등록 중복 방지 가드 추가(loading 체크) (presentation/.../writeroutine/WriteRoutineViewModel.kt) 버그 목표 범위를 넘어서는 동작 제어 로직 추가로 보입니다.

Poem

귀 쫑긋, 폴짝! 코드밭 달려가서
타입 한 알, 살포시 주머니에 넣었지 🥕
루틴은 이제 제 자리 찾고,
아이콘도 꽤나 뿌듯해졌네.
깡총, 추천의 길 따라 반짝!

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/#120-write_routine_with_recommend_routine_type

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

Copy link
Member

@wjdrjs00 wjdrjs00 left a comment

Choose a reason for hiding this comment

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

깔끔합니다요! 머지 ㄱㄱ

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

Caution

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

⚠️ Outside diff range comments (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/WriteRoutineViewModel.kt (1)

476-497: Mutex로 임계 구역 보호 적용 예시(등록 경로)

중복 등록을 근본적으로 차단하려면 네트워크 호출/인텐트 전송 구간을 뮤텍스로 감싸 주세요.

-                WriteRoutineType.Add -> {
-                    sendIntent(WriteRoutineIntent.RegisterRoutineLoading)
+                WriteRoutineType.Add -> {
+                    sendIntent(WriteRoutineIntent.RegisterRoutineLoading)
                     val subRoutines = if (currentState.selectNotUseSubRoutines) emptyList() else currentState.subRoutineNames.filter { it.isNotEmpty() }
                     val noRepeatRoutine = repeatDay.isEmpty()
-
-                    val registerRoutineResult = registerRoutineUseCase(
-                        name = currentState.routineName,
-                        repeatDay = repeatDay,
-                        startTime = startTime.toDomainTime(),
-                        startDate = if (noRepeatRoutine) Date.now().toDomainDate() else currentState.startDate.toDomainDate(),
-                        endDate = if (noRepeatRoutine) Date.now().toDomainDate() else currentState.endDate.toDomainDate(),
-                        subRoutines = subRoutines,
-                        recommendedRoutineType = currentState.recommendedRoutineType,
-                    )
+                    val registerRoutineResult = try {
+                        registerRoutineUseCase(
+                            name = currentState.routineName,
+                            repeatDay = repeatDay,
+                            startTime = startTime.toDomainTime(),
+                            startDate = if (noRepeatRoutine) Date.now().toDomainDate() else currentState.startDate.toDomainDate(),
+                            endDate = if (noRepeatRoutine) Date.now().toDomainDate() else currentState.endDate.toDomainDate(),
+                            subRoutines = subRoutines,
+                            recommendedRoutineType = currentState.recommendedRoutineType,
+                        )
+                    } finally {
+                        registerMutex.unlock()
+                    }

추가로 필요한 보일러플레이트(파일 상단/클래스 필드):

// import
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.tryLock

// class body
private val registerMutex = Mutex()
🧹 Nitpick comments (9)
data/src/main/java/com/threegap/bitnagil/data/writeroutine/model/request/RegisterRoutineRequest.kt (1)

20-21: Json 설정 확인 완료 및 enum 전환 권장

– NetworkModule.kt(39–46)에서 이미

Json {
    ignoreUnknownKeys = true
    prettyPrint = true
    explicitNulls = false
}

로 설정되어 있어, nullable 필드가 null일 경우 JSON에 포함되지 않습니다.
– data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt 에서 사용하는 Json { ignoreUnknownKeys = true } 인스턴스는 기본 explicitNulls = true를 따르지만, 이는 에러 응답 파싱 전용으로 보이므로 요청 직렬화에는 영향이 없습니다.
• 만약 다른 곳에서 별도 Json 인스턴스를 생성하여 직렬화에 사용 중이라면, 동일하게 explicitNulls = false로 맞춰 주시기 바랍니다.
– recommendedRoutineType 필드에 허용 값이 제한적이라면, String 대신 enum 클래스로 전환해 타입 세이프티를 강화하는 것을 권장드립니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/model/mvi/WriteRoutineIntent.kt (1)

25-26: SetRoutine 생성자에 recommendedRoutineType 기본값 null 추가 권장

WriteRoutineIntent.SetRoutine 생성자에 새로 추가된 recommendedRoutineType: String? 파라미터에 기본값을 두면, 현재 두 곳의 호출부를 수정할 필요 없이 코드를 컴파일할 수 있습니다.

  • 대상 호출부:
    • presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/WriteRoutineViewModel.kt (85, 114행)

제안하는 경미한 수정:

--- a/presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/model/mvi/WriteRoutineIntent.kt
+++ b/presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/model/mvi/WriteRoutineIntent.kt
@@ -22,7 +22,7 @@ sealed class WriteRoutineIntent {
     val repeatDays: List<Day>,
-    val recommendedRoutineType: String?,
+    val recommendedRoutineType: String? = null,
 ) : WriteRoutineIntent()

이렇게 하면 호출부에 recommendedRoutineType 인자를 명시하지 않아도 기본값(null)이 적용되어 호출부 변경 없이도 컴파일됩니다.

data/src/main/java/com/threegap/bitnagil/data/writeroutine/repositoryImpl/WriteRoutineRepositoryImpl.kt (1)

36-37: 빈 문자열 방지: 서버로는 null만 보내고 공백/빈 문자열은 제거 권장

UI에서 값이 공백으로 들어올 가능성이 있다면, 서버에는 null로 통일하는 편이 안전합니다. 직렬화 시 불필요한 빈 문자열 전송을 방지합니다.

다음 변경을 제안합니다:

-            recommendedRoutineType = recommendedRoutineType,
+            recommendedRoutineType = recommendedRoutineType?.takeIf { it.isNotBlank() },

또한 위와 별개로, 앞선 코멘트처럼 JSON 설정에서 explicitNulls = false가 아니라면 null이 명시적으로 전송됩니다. 서버 계약에 맞춰 전역 설정/전송 형태를 재확인해 주세요.

domain/src/main/java/com/threegap/bitnagil/domain/writeroutine/usecase/RegisterRoutineUseCase.kt (1)

19-20: 기본값 추가 및 타입 세이프티 개선 제안

  • RegisterRoutineUseCase.invoke 시그니처를
    suspend operator fun invoke(
        …,
        recommendedRoutineType: String? = null,
    ): Result<Unit>
    처럼 기본값 = null으로 변경하면 호출부 수정 없이 점진적인 마이그레이션이 가능합니다.
  • recommendedRoutineTypeString? 그대로 두기보다
    enum class RecommendedRoutineType { … }
    혹은 value class(@JvmInline value class)로 래핑해 도메인 계층에서의 문자열 직접 노출을 최소화하고, infra 계층에서만 매핑하도록 구조를 개선하세요.
  • 허용 가능한 문자열 집합(예: 서버 스키마 카테고리 키)이 정해져 있다면, require() 또는 전용 매퍼 내에서 사전 검증 로직을 추가해 런타임 오류를 조기에 방지할 것을 권장합니다.

호출부 확인 결과, 현재 registerRoutineUseCase를 사용하는 곳은

  • WriteRoutineViewModel.kt 482–490행 한 곳뿐이므로
    기본값 추가 시점에서도 다른 호출부에 영향이 없습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/WriteRoutineViewModel.kt (5)

96-97: 편집 진입 시 recommendedRoutineType을 null로 초기화하는 의도 확인 필요

Edit 플로우에서 recommendedRoutineType = null로 고정하면, 기존에 추천 기반으로 생성된 루틴을 편집할 때 타입 정보가 손실될 수 있습니다. 편집 API가 타입을 받지 않는다면 전송은 필요 없지만, UI 상에서 아이콘/라벨 표시용으로 유지가 필요할 수 있습니다. 도메인 모델에 해당 값이 존재한다면 보존하는 쪽을 검토해 주세요.

예시(모델이 있다면):

-                            recommendedRoutineType = null,
+                            recommendedRoutineType = routine.recommendedRoutineType?.categoryName,

125-126: NPE 방지: 안전한 호출로 변경 권장

추천 루틴 응답에서 recommendedRoutineType이 누락될 가능성에 대비해 안전 연산자로 방어 코드를 추가하는 편이 안전합니다.

-                            recommendedRoutineType = routine.recommendedRoutineType.categoryName,
+                            recommendedRoutineType = routine.recommendedRoutineType?.categoryName,

136-140: reduceState의 nullable 반환 계약 명시/검증 필요

reduceState(...): WriteRoutineState?로 변경해 null을 반환하는 경우 상태 갱신을 생략하는 패턴을 도입하셨습니다. 사용 중인 MVI 베이스(MviViewModel/Orbit)에서 null을 적절히 no-op 처리하는지 확실히 해두는 것이 좋습니다. 계약이 불명확하면 유지보수자가 오해하기 쉽습니다. 간단한 KDoc 주석으로 “null 반환 시 상태 미갱신” 의도를 명시해 주세요.


223-227: 성공 시 loading 해제 누락으로 인한 잔상 가능성

RegisterRoutineSuccess에서 사이드 이펙트만 발행하고 return null로 상태 갱신을 생략하고 있어, 화면 전환이 지연/취소되거나 테스트 환경에서 화면이 잔류할 때 로딩 스피너가 그대로 남을 수 있습니다. EditRoutineSuccessloading = false로 정리하는 것과 일관되게 처리하는 편이 안전합니다.

-            WriteRoutineIntent.RegisterRoutineSuccess -> {
-                sendSideEffect(WriteRoutineSideEffect.MoveToPreviousScreen)
-
-                return null
-            }
+            WriteRoutineIntent.RegisterRoutineSuccess -> {
+                sendSideEffect(WriteRoutineSideEffect.MoveToPreviousScreen)
+                return state.copy(loading = false)
+            }

256-258: 상태에 recommendedRoutineType 전달 OK

추천 루틴 타입을 상태로 보존해 등록 시점까지 안전하게 전달하는 흐름이 명확합니다. 이후 상태 저장/복원(SavedStateHandle) 고려가 필요하면 동일 키로 보존하는 것도 검토해 주세요.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7e67356 and 1cc90db.

📒 Files selected for processing (7)
  • data/src/main/java/com/threegap/bitnagil/data/writeroutine/model/request/RegisterRoutineRequest.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/writeroutine/repositoryImpl/WriteRoutineRepositoryImpl.kt (2 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/writeroutine/repository/WriteRoutineRepository.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/writeroutine/usecase/RegisterRoutineUseCase.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/WriteRoutineViewModel.kt (7 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/model/mvi/WriteRoutineIntent.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/model/mvi/WriteRoutineState.kt (2 hunks)
🔇 Additional comments (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/model/mvi/WriteRoutineState.kt (2)

32-33: 상태에 recommendedRoutineType 추가: LGTM

UI 상태에 추천 루틴 타입을 포함하는 방향은 요구사항과 일치합니다. 직렬화(@parcelize) 관점에서도 필드가 말단에 추가되어 호환성 이슈가 낮습니다.


83-84: Init에서 null 초기화 적절

초기 진입 시 추천 루틴이 아닐 수 있으므로 기본값 null 설정은 합리적입니다. 이후 추천 루틴 진입 경로에서만 실제 값으로 세팅하면 됩니다.

data/src/main/java/com/threegap/bitnagil/data/writeroutine/repositoryImpl/WriteRoutineRepositoryImpl.kt (1)

27-28: 레포지토리 시그니처 확장: LGTM

도메인 인터페이스 변경과 일치하며, 파이프라인이 자연스럽게 Request DTO로 전달되도록 구성되어 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/WriteRoutineViewModel.kt (1)

488-490: UseCase로의 recommendedRoutineType 전파 확인

등록 호출에 recommendedRoutineType이 누락 없이 전달됩니다. PR 목표와 일치합니다.

@l5x5l l5x5l merged commit 4a845fc into develop Aug 21, 2025
2 checks passed
@l5x5l l5x5l deleted the fix/#120-write_routine_with_recommend_routine_type branch August 21, 2025 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

세환 🐞 Fix 버그 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FIX] 루틴 등록시 추천 루틴 타입 누락 수정

2 participants