Skip to content

[Refactor] 루틴 등록/수정 로직 변경#53

Merged
taipaise merged 4 commits intodevelopfrom
feat/routine-creation
Aug 18, 2025
Merged

[Refactor] 루틴 등록/수정 로직 변경#53
taipaise merged 4 commits intodevelopfrom
feat/routine-creation

Conversation

@taipaise
Copy link
Copy Markdown
Collaborator

@taipaise taipaise commented Aug 18, 2025

🌁 Background

  • 오랜만에 돌아온 가벼운 PR입니다!!(아마도)

📱 Screenshot

  • UI 변경 사항 없음

👩‍💻 Contents

  • 변경된 루틴 등록/수정 entity에 맞게 로직을 변경하였습니다.
    • RoutineUseCase 수정: 이전에는 subRoutine에 대한 정보를 제어해줬어야 했는데, 해당 부분이 사라져서 훨씬 간단해졌습니다.
    • Routine관련 Repository, Endpoint 수정
    • 루틴 등록/수정 확정 버튼 활성화 로직 수정

📝 Review Note

  • 루틴을 등록/수정하는 화면인 RoutineCreationViewController의 생성자는 두 개 있습니다.
    init(
        viewModel: RoutineCreationViewModel,
        updateInfo: (routineId: String, updateType: RoutineUpdateApplyDateType)? = nil
    ) {
        navigationTitle = updateInfo?.routineId == nil ? "루틴 등록" : "루틴 수정"
        registerButtonTitle = updateInfo?.routineId == nil ? "등록하기" : "수정하기"

        super.init(viewModel: viewModel)

        registerButton.setTitle(registerButtonTitle, for: .normal)
        if let updateInfo {
            viewModel.action(input: .fetchRoutine(id: updateInfo.routineId))
        }
    }
  • 첫번째 생성자 입니다. 이 생성자는 루틴을 생성하거나, 수정할 때 사용하는 생성자 입니다.
  • 생성할 때는 updateInfonil로 설정하여 생성합니다.
  • V2부터 수정 시 오늘부터 적용할지, 내일부터 적용할 지 선택해야합니다. 즉 다시 말해 루틴을 수정하는 경우 기존 루틴의 ID와 언제부터 적용할 지 여부를 알고 있어야 합니다. updateInfo에 해당 정보를 튜플로 넣어주면 됩니다!
    init(viewModel: RoutineCreationViewModel, recommendRoutineId: Int) {
        navigationTitle = "루틴 등록"
        registerButtonTitle = "등록하기"

        super.init(viewModel: viewModel)

        registerButton.setTitle(registerButtonTitle, for: .normal)
        viewModel.action(input: .fetchRecommendedRoutine(id: recommendRoutineId))
    }

  • 두 번째 생성자입니다. 이 생성자는 추천 루틴으로 루틴을 생성할 때 사용하는 생성자입니다.
  • 추천 루틴 id를 함께 넘겨주면 됩니다!

Summary by CodeRabbit

  • New Features
    • 루틴 생성/수정 흐름을 단일 양식으로 통합하고, 적용 시점(오늘/내일) 선택 옵션을 추가했습니다.
    • 추천 루틴으로부터 바로 생성 가능하며, 추천 유형이 반영됩니다.
    • 기간 선택 UI에 시작/종료일 미설정 시 안내 문구 표시 및 미선택 시 등록 버튼 비활성화 기능 추가.
    • 이름 입력 필드와 실시간 유효성 검사로 등록 버튼 상태와 문구가 동적으로 변경됩니다.
    • 기존 루틴 편집 시 관련 정보가 자동으로 불러와집니다.

@taipaise taipaise requested a review from choijungp August 18, 2025 13:49
@taipaise taipaise self-assigned this Aug 18, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 18, 2025

Walkthrough

루틴 생성/수정 흐름을 단일 엔티티(RoutineCreationEntity) 중심으로 통합하고 DTO/Endpoint를 v2로 맞추며, Presentation에서는 기간을 옵셔널로 처리하고 유효성 바인딩을 추가했습니다. Domain에 RoutineCreationEntity와 RoutineUpdateApplyDateType이 추가되었습니다.

Changes

Cohort / File(s) Change summary
DTO 및 매핑 추가
Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift
DTO에 routineId, updateApplyDate, routineStartDate, routineEndDate, recommendedRoutineType 필드 추가 및 toRoutineCreationEntity() 변환 메서드 추가(Week 및 RoutineCategoryType 매핑, applyDateType nil 고정).
엔드포인트 라우팅 조정
Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift
updateRoutine 케이스가 RoutineCreationDTO 사용으로 변경. createRoutine/updateRoutine 요청을 v2(/api/v2/routines)로 라우팅하도록 baseURL 로직 조정; 본문 바인딩을 DTO 기반으로 유지.
저장소 인터페이스 단일화
Projects/DataSource/Sources/Repository/RoutineRepository.swift
createRoutine/updateRoutine 시그니처를 RoutineCreationEntity 단일 인자로 변경. DTO 구성 로직을 RoutineCreationEntity 필드로 재작성하고 subRoutineInfos 제거, subroutines 이름 배열 사용.
도메인 타입 추가
Projects/Domain/Sources/Entity/Enum/RoutineUpdateApplyDateType.swift
RoutineUpdateApplyDateType: String enum 추가 (TODAY, TOMORROW).
도메인 엔티티 도입
Projects/Domain/Sources/Entity/RoutineCreationEntity.swift
RoutineCreationEntity 구조체 추가: id, name, repeatDay, startDate, endDate, executionTime, subroutines, recommendedRoutineType, applyDateType 및 초기화자 제공.
도메인 프로토콜 정합
Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift
createRoutine/updateRoutine 시그니처를 RoutineCreationEntity 단일 인자로 변경.
유스케이스 프로토콜 정합
Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift
saveRoutine 시그니처를 RoutineCreationEntity 단일 인자로 변경(이전의 여러 인자 제거).
유스케이스 구현 갱신
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift
saveRoutineRoutineCreationEntity 기반으로 동작하도록 변경. 공백 서브루틴 필터링 및 id 유무로 create/update 분기.
프레젠테이션 - 기간 뷰
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift
Dependency의 dates를 (start: Date?, end: Date?)로 변경. nil인 경우 버튼에 플레이스홀더 텍스트 표시하도록 분기 처리 추가.
프레젠테이션 - 뷰컨트롤러
Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift
텍스트필드 명칭을 nameTextField로 변경. 생성자 시그니처를 updateInfo: (routineId: String, updateType: RoutineUpdateApplyDateType)?로 변경하고 recommendRoutineId용 초기화자 추가. 등록 버튼 액션/타이틀을 상태 기반으로 바인딩. name/isValid 퍼블리셔 바인딩 추가.
프레젠테이션 - 뷰모델
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift
periodPublisher(Date?, Date?)로 변경. 내부 기간 상태를 옵셔널로 전환. routineType, updateType 상태 추가. 유효성 체크에 기간 필수화. registerRoutine에서 RoutineCreationEntity 생성 후 saveRoutine(routine:) 호출로 전환.
뷰 구성자 명칭 변경(경량)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift
configure 파라미터 레이블을 dependenciesdependency로 변경 및 전달 변경.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant V as ViewController
  participant VM as ViewModel
  participant UC as UseCase
  participant RP as Repository
  participant EP as Endpoint
  participant API as Routines API

  V->>VM: 사용자 입력(이름/반복/기간/시간/추천유형 등)
  VM->>VM: 유효성 검사 (이름, 시작/종료, 시간)
  V->>VM: 등록 버튼 탭
  VM->>UC: saveRoutine(routine: RoutineCreationEntity)
  alt routine.id == nil
    UC->>RP: createRoutine(routine)
    RP->>EP: createRoutine(RoutineCreationDTO) @ v2
    EP->>API: POST /api/v2/routines
  else
    UC->>RP: updateRoutine(routine)
    RP->>EP: updateRoutine(RoutineCreationDTO) @ v2
    EP->>API: PATCH /api/v2/routines/{id}
  end
  API-->>EP: 응답
  EP-->>RP: 전달
  RP-->>UC: 전달
  UC-->>VM: 완료 이벤트
  VM-->>V: UI 업데이트 / 네비게이션
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • choijungp

Poem

새벽 코드밭에 당근을 심었네, 토끼 발자국 또르르 🥕
한데 묶인 루틴, v2 길로 샥슥슥—
시작일·종료일은 가능성으로 두고, 이름은 또렷하게!
버튼을 눌러 저장하면 루틴이 폴짝, 호들갑 없이 찰칵 🐇✨

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 feat/routine-creation

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 or @coderabbitai 요약 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.

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
Copy Markdown

@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

🔭 Outside diff range comments (1)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (1)

48-51: 초기화 시 임의 날짜 주입이 새 플레이스홀더 로직을 무력화합니다

init에서 현재 날짜로 버튼 타이틀을 강제로 설정하고 있어(“TODO: 추후 삭제”) nil일 때 플레이스홀더(“시작 일자 설정”, “종료 일자 설정”)가 표시되지 않습니다. 실제 동작이 기획과 반대로 보일 수 있으므로 즉시 제거를 권장합니다.

아래 변경으로 임의 주입을 제거하세요:

     // TODO: - 추후 삭제
-    configureButon(buttonType: .start, date: Date())
-    configureButon(buttonType: .end, date: Date())
+    // 초기 타이틀은 configure(dependency:)에서 설정됩니다.

Also applies to: 68-80

🧹 Nitpick comments (18)
Projects/Domain/Sources/Entity/Enum/RoutineUpdateApplyDateType.swift (1)

9-12: Codable/CaseIterable 채택으로 직렬화·유틸리티성 강화 제안

서버와의 바인딩에 rawValue를 사용 중이므로 Codable 채택을 추가하면 테스트/디버깅/스토리지 연계에 유리합니다. CaseIterable은 화면 선택지 노출 등에 유용합니다.

아래처럼 확장하는 것을 권장합니다:

-public enum RoutineUpdateApplyDateType: String {
+public enum RoutineUpdateApplyDateType: String, Codable, CaseIterable {
     case today = "TODAY"
     case tomorrow = "TOMORROW"
 }
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (1)

153-158: 오타: configureButon → configureButton (명명 일관성)

메서드명 오타는 오랜 기간 누락되기 쉽고 검색성/가독성을 해칩니다. 내부 사용만 예상되더라도 정정 권장합니다.

-    func configureButon(buttonType: ButtonType, date: Date) {
+    func configureButton(buttonType: ButtonType, date: Date) {
         let button = buttonType == .start ? startButton : endButton
 
         let dateString = date.convertToString(dateType: .yearMonthDate)
         button.setTitle(dateString, for: .normal)
     }

호출부가 외부에 존재한다면 리네이밍 대응도 함께 필요합니다. 원하시면 검색 스크립트를 드리겠습니다.

Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (2)

75-81: 중복 제거: 생성/수정 바디 매핑 공통화

create/update 모두 동일한 DTO.dictionary를 반환하므로 하나로 묶어 간결화 가능합니다.

     var bodyParameters: [String : Any] {
         switch self {
-        case .createRoutine(let routine):
-            return routine.dictionary
-        case .updateRoutine(let routine):
-            return routine.dictionary
+        case .createRoutine(let routine), .updateRoutine(let routine):
+            return routine.dictionary
         case .deleteDailyRoutine(let routine):
             return routine.dictionary
         case .updateRoutineCompletion(let routines):
             return routines.dictionary
         default:
             return [:]
         }
     }

56-62: 헤더 accept 값 명확화 제안

서버가 JSON을 반환한다면 accept를 “application/json”으로 제한하는 편이 의도와 보안(콘텐츠 협상) 측면에서 명확합니다.

-        let headers: [String: String] = [
-            "Content-Type": "application/json",
-            "accept": "*/*"
-        ]
+        let headers: [String: String] = [
+            "Content-Type": "application/json",
+            "accept": "application/json"
+        ]
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)

31-47: 가독성 개선(변수 섀도잉 제거) + 생성 시 applyDateType 제거 로직 권장

  • 매개변수와 동일한 이름의 지역변수(routine) 재정의는 가독성을 떨어뜨립니다. 명확한 이름으로 변경을 권장합니다.
  • updateApplyDate는 “수정”에만 의미가 있을 가능성이 높습니다. 생성(create) 시에는 nil로 강제하여 불필요한 필드 전송을 피하는 것이 안전합니다(백엔드 스펙과 상이할 경우 400 방지).

아래처럼 정리하면 의도가 명확해집니다:

-    public func saveRoutine(routine: RoutineCreationEntity) async throws {
-        let routine = RoutineCreationEntity(
+    public func saveRoutine(routine: RoutineCreationEntity) async throws {
+        let sanitizedRoutine = RoutineCreationEntity(
             id: routine.id,
             name: routine.name,
             repeatDay: routine.repeatDay,
             startDate: routine.startDate,
             endDate: routine.endDate,
             executionTime: routine.executionTime,
             subroutines: routine.subroutines.filter { !$0.isEmpty },
             recommendedRoutineType: routine.recommendedRoutineType,
-            applyDateType: routine.applyDateType)
+            applyDateType: routine.id == nil ? nil : routine.applyDateType)
 
-        if routine.id == nil { // 루틴 아이디가 있으면 수정, 없으면 생성
-            try await createRoutine(routine: routine)
-        } else {
-            try await updateRoutine(routine: routine)
-        }
+        // 루틴 아이디가 없으면 생성, 있으면 수정
+        if sanitizedRoutine.id == nil {
+            try await createRoutine(routine: sanitizedRoutine)
+        } else {
+            try await updateRoutine(routine: sanitizedRoutine)
+        }
     }

추가로, 실제 서버 스펙이 “생성 시 updateApplyDate 허용/무시”인지 확인 부탁드립니다. 필요 시 Repository에서 생성 DTO의 updateApplyDate를 무조건 nil로 설정하도록 강제하는 것도 고려해볼 수 있습니다.

Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift (1)

28-30: updateRoutine의 계약(Contract) 명시 필요

수정 시 routine.id가 필수임을 문서 주석에 명확히 드러내면 사용 측 혼란을 줄일 수 있습니다.

적용 제안:

-    ///   - routine: 수정할 루틴
+    ///   - routine: 수정할 루틴 (id는 필수)
     func updateRoutine(routine: RoutineCreationEntity) async throws
Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift (1)

22-37: nil 처리 개선 및 누락된 매핑 보완 제안

  • recommendedRoutineType: nil일 때 빈 문자열을 넘기는 방식보다 flatMap 초기화를 쓰는 편이 안전합니다.
  • updateApplyDate: 현재 Entity의 applyDateType을 항상 nil로 고정하고 있어 응답 → Domain 변환 시 정보가 소실됩니다. 필요 시 타입 매핑을 추가하세요.

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

 extension RoutineCreationDTO {
     func toRoutineCreationEntity() -> RoutineCreationEntity {
         let weeks = repeatDay.compactMap { Week(rawValue: $0) }
-        let routineCategoryType = RoutineCategoryType(rawValue: recommendedRoutineType ?? "")
+        let routineCategoryType = recommendedRoutineType.flatMap { RoutineCategoryType(rawValue: $0) }
+        let applyDateType = updateApplyDate.flatMap { RoutineUpdateApplyDateType(rawValue: $0) }

         return RoutineCreationEntity(
             id: routineId,
             name: routineName,
             repeatDay: weeks,
             startDate: routineStartDate,
             endDate: routineEndDate,
             executionTime: executionTime,
             subroutines: subRoutineName,
             recommendedRoutineType: routineCategoryType,
-            applyDateType: nil)
+            applyDateType: applyDateType)
     }
 }
Projects/DataSource/Sources/Repository/RoutineRepository.swift (2)

13-24: DTO 구성 중복 제거 제안

create/update 모두 동일한 DTO 구성 로직이 반복됩니다. private 헬퍼로 추출하면 유지보수성이 좋아집니다.

다음처럼 교체를 제안합니다:

-        let routineCreationDTO = RoutineCreationDTO(
-            routineId: nil,
-            updateApplyDate: routine.applyDateType?.rawValue,
-            routineName: routine.name,
-            repeatDay: routine.repeatDay.map { $0.rawValue },
-            routineStartDate: routine.startDate,
-            routineEndDate: routine.endDate,
-            executionTime: routine.executionTime,
-            subRoutineName: routine.subroutines,
-            recommendedRoutineType: routine.recommendedRoutineType?.rawValue)
+        let routineCreationDTO = buildRoutineCreationDTO(from: routine, includeId: false)

헬퍼 추가(파일 내 적절한 위치):

private extension RoutineRepository {
    func buildRoutineCreationDTO(from routine: RoutineCreationEntity, includeId: Bool) -> RoutineCreationDTO {
        RoutineCreationDTO(
            routineId: includeId ? routine.id : nil,
            updateApplyDate: routine.applyDateType?.rawValue,
            routineName: routine.name,
            repeatDay: routine.repeatDay.map { $0.rawValue },
            routineStartDate: routine.startDate,
            routineEndDate: routine.endDate,
            executionTime: routine.executionTime,
            subRoutineName: routine.subroutines,
            recommendedRoutineType: routine.recommendedRoutineType?.rawValue
        )
    }
}

49-59: updateRoutine 호출 시 id 사전 검증(방어적 코드)

UseCase에서 분기하고 있어 실사용상 문제는 없겠지만, Repository 레이어에서도 id가 없는 업데이트 호출을 방지하는 방어 로직을 권장합니다.

다음 한 줄을 DTO 구성 앞에 추가:

 func updateRoutine(routine: RoutineCreationEntity) async throws {
+        precondition(routine.id != nil, "updateRoutine(routine:) 호출 시 id는 필수입니다.")
         let routineUpdateDTO = RoutineCreationDTO(
             routineId: routine.id,
             updateApplyDate: routine.applyDateType?.rawValue,
             routineName: routine.name,
             repeatDay: routine.repeatDay.map { $0.rawValue },
             routineStartDate: routine.startDate,
             routineEndDate: routine.endDate,
             executionTime: routine.executionTime,
             subRoutineName: routine.subroutines,
             recommendedRoutineType: routine.recommendedRoutineType?.rawValue)

또한, 위의 중복 제거 헬퍼를 사용한다면:

-        let routineUpdateDTO = RoutineCreationDTO( ... )
+        let routineUpdateDTO = buildRoutineCreationDTO(from: routine, includeId: true)
Projects/Domain/Sources/Entity/RoutineCreationEntity.swift (1)

21-31: 초기화 편의성 개선(기본값 부여) 제안

id/recommendedRoutineType/applyDateType는 선택적 성격이 강하므로 기본값(nil)과 subroutines의 기본값([])을 제공하면 호출부가 간결해집니다. 테스트 작성에도 유리합니다.

-    public init(
-        id: String?,
+    public init(
+        id: String? = nil,
         name: String,
         repeatDay: [Week],
         startDate: String,
         endDate: String,
         executionTime: String,
-        subroutines: [String],
-        recommendedRoutineType: RoutineCategoryType?,
-        applyDateType: RoutineUpdateApplyDateType?
+        subroutines: [String] = [],
+        recommendedRoutineType: RoutineCategoryType? = nil,
+        applyDateType: RoutineUpdateApplyDateType? = nil
     ) {

추가로, Equatable/Sendable 컨폼을 검토하면 테스트/동시성 안전성에 도움이 됩니다.

Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (1)

50-51: 기간 유효성 추가 검증 권장(시작 ≤ 종료)

현재는 nil 여부만 확인합니다. 시작일이 종료일보다 늦은 경우를 방지하는 검증을 추가하세요.

아래와 같이 updateIsRoutineValid 내 조건을 보강할 수 있습니다:

     private func updateIsRoutineValid() {
         guard
             let name = nameSubject.value,
             !name.isEmpty,
             executionTimeSubject.value.startAt != nil,
-            periodStartSubject.value != nil,
-            periodEndSubject.value != nil
+            let start = periodStartSubject.value,
+            let end = periodEndSubject.value,
+            start <= end
         else {
             checkRoutinePublisher.send(false)
             return
         }
         
         checkRoutinePublisher.send(true)
     }
Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (7)

76-77: 조건식 간결화: updateInfo?.routineId == nil 대신 updateInfo == nil 사용 권장

튜플의 routineId가 비옵셔널이므로 옵셔널 체이닝이 불필요합니다. 의도를 더 명확히 표현해 주세요.

적용 diff:

-        navigationTitle = updateInfo?.routineId == nil ? "루틴 등록" : "루틴 수정"
-        registerButtonTitle = updateInfo?.routineId == nil ? "등록하기" : "수정하기"
+        navigationTitle = updateInfo == nil ? "루틴 등록" : "루틴 수정"
+        registerButtonTitle = updateInfo == nil ? "등록하기" : "수정하기"

93-95: 중복된 버튼 타이틀 설정 제거 제안

두 이니셜라이저에서 동일한 registerButton.setTitle 호출이 반복됩니다. 중복 제거를 위해 private 메서드로 추출하거나 공통 초기화 경로에서 한 번만 세팅하는 편이 깔끔합니다.

원하시면 applyStaticTexts() 같은 헬퍼로 정리한 diff를 제안드릴게요.


113-118: 텍스트필드 UX/접근성 보완 (returnKey/접근성 식별자 추가)

엔터 키 타입과 접근성 식별자를 지정하면 UX와 UI 테스트 신뢰도가 개선됩니다.

적용 diff:

         nameTextField.attributedPlaceholder = NSAttributedString(string: "루틴 제목을 입력해주세요.", attributes: attributes)
         nameTextField.font = BitnagilFont(style: .title3, weight: .semiBold).font
         nameTextField.textColor = BitnagilColor.gray10
+        nameTextField.returnKeyType = .done
+        nameTextField.accessibilityIdentifier = "routineNameTextField"
         nameTextField.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: .editingChanged)
         nameTextField.delegate = self

131-135: 중복 탭 방지: 등록 버튼 탭 시 즉시 비활성화 권장

네트워크 요청 동안 빠른 연속 탭으로 중복 등록이 발생할 수 있습니다. 탭 시점을 기준으로 즉시 버튼을 비활성화하고, 완료/에러 시 다시 활성화하는 흐름을 추천합니다. 완료/에러 통지는 ViewModel의 별도 output(예: isRegistering 또는 registerCompleted)으로 처리하는 것이 가장 깔끔합니다.

예시 diff(비활성화만 선반영 — 재활성화는 ViewModel output과 바인딩 필요):

         registerButton.addAction(
             UIAction { [weak self] _ in
-                self?.viewModel.action(input: .registerRoutine)
+                guard let self = self else { return }
+                self.registerButton.isEnabled = false
+                self.viewModel.action(input: .registerRoutine)
             },
             for: .touchUpInside)

추가로, ViewModel에 output.isRegistering가 있다면 bind()에서 해당 값을 받아 registerButton.isEnabled와 배경색을 토글하도록 도와드릴 수 있습니다.


145-148: 클리어 버튼은 UITextField.rightView 활용을 권장

현재 클리어 버튼이 별도 서브뷰로 오버레이되어 있어 텍스트가 버튼 아래로 흐를 수 있습니다. UITextField.rightView를 사용하면 터치 영역/레이아웃 관리가 더 안전하고, 접근성/포커스 이동도 일관됩니다.

예시(대체안):

nameTextField.rightView = clearButton
nameTextField.rightViewMode = .whileEditing
// 오토레이아웃 대신 버튼 자체 크기만 지정
clearButton.snp.remakeConstraints { make in
  make.size.equalTo(Layout.clearButtonSize)
}

원하시면 이 방식으로 레이아웃/속성 변경까지 포함한 전체 diff를 제안드릴게요.

Also applies to: 170-174


211-217: IME/커서 점프 방지: 동일 값이면 text 세팅 생략

입력 중 ViewModel -> View 바인딩이 같은 값으로 다시 설정되면 한글/일본어 입력 IME에서 조합이 끊기는 등 UX 이슈가 생길 수 있습니다. 값이 변한 경우에만 적용해 주세요.

적용 diff:

-            .sink { [weak self] name in
-                self?.nameTextField.text = name
-            }
+            .sink { [weak self] name in
+                guard let self = self else { return }
+                if self.nameTextField.text != name {
+                    self.nameTextField.text = name
+                }
+            }

274-287: 초기 버튼 상태 플리커 방지: 초기 비활성화 스타일 지정 권장

유효성 퍼블리셔가 최초 방출되기 전까지 버튼의 초기 상태/스타일이 미정일 수 있습니다. configureAttribute()에서 기본을 비활성화(회색 배경)로 명시해두면 플리커/오동작을 줄일 수 있습니다.

예시(추가 코드 — configureAttribute 내 초기화 시점에 배치):

registerButton.isEnabled = false
registerButton.backgroundColor = BitnagilColor.gray95
registerButton.setTitleColor(.white, for: .normal)

원하시면 해당 위치에 정확히 삽입할 수 있도록 패치도 제안드리겠습니다.

📜 Review details

Configuration used: .coderabbit.yaml
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 4db6969 and cc73d3c.

📒 Files selected for processing (11)
  • Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift (1 hunks)
  • Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (1 hunks)
  • Projects/DataSource/Sources/Repository/RoutineRepository.swift (2 hunks)
  • Projects/Domain/Sources/Entity/Enum/RoutineUpdateApplyDateType.swift (1 hunks)
  • Projects/Domain/Sources/Entity/RoutineCreationEntity.swift (1 hunks)
  • Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift (2 hunks)
  • Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift (1 hunks)
  • Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1 hunks)
  • Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (2 hunks)
  • Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (8 hunks)
  • Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-16T09:21:15.038Z
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.

Applied to files:

  • Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift
🧬 Code Graph Analysis (7)
Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift (1)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
  • saveRoutine (31-48)
Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift (2)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (2)
  • createRoutine (13-28)
  • updateRoutine (49-63)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (2)
  • createRoutine (50-52)
  • updateRoutine (54-56)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (1)
Projects/Shared/Sources/Extension/Date+.swift (1)
  • convertToString (16-22)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (2)
  • createRoutine (13-28)
  • updateRoutine (49-63)
Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (2)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (2)
  • updateRoutine (49-63)
  • createRoutine (13-28)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (2)
  • updateRoutine (54-56)
  • createRoutine (50-52)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (1)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (2)
  • createRoutine (50-52)
  • updateRoutine (54-56)
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (2)
Projects/Shared/Sources/Extension/Date+.swift (1)
  • convertToString (16-22)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
  • saveRoutine (31-48)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift

[Warning] 134-134: TODOs should be resolved (- routine 엔티티 변경 이후 시작일자, 종료 일...)

(todo)

🔇 Additional comments (14)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (1)

33-34: 의존성 dates 옵셔널화 방향 OK

빈 날짜 상태를 표현하기 위한 옵셔널 전환이 UI/도메인 변경사항과 일관됩니다. ViewModel/바인딩 쪽에서도 nil 상태가 정상적으로 전달되는지만 함께 확인해 주세요.

Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift (1)

15-15: saveRoutine 시그니처 변경 호출부 일괄 확인 완료
rg 검색 결과, 구 시그니처 호출이나 관련 잔존 토큰이 없습니다. 모두 func saveRoutine(routine: RoutineCreationEntity) async throws 형태로 업데이트된 것을 확인했습니다.

Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (2)

12-12: 업데이트 DTO 교체(…UpdateDTO → RoutineCreationDTO) 방향 OK

업데이트도 생성과 동일한 페이로드 구조를 사용하도록 통합한 점 일관성 좋습니다. 서버 스펙과 필드명이 정확히 일치하는지만 한 번 더 확인 부탁드립니다.


20-25: 절대 URL 사용 방식 확인 완료
URLRequest(urlString:) 초기화에서 URLComponents(string:)로 절대 URL을 파싱하므로, path에 이미 포함된 AppProperties.baseURL을 그대로 넘겨도 중복 호스트나 잘못된 URL 생성 이슈가 없습니다.

Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)

50-56: Repository 위임 로직 단순·명확, OK

create/update를 각각 1:1 위임하는 구조로 정리되어 이해하기 쉽습니다.

Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift (1)

12-14: 단일 엔티티로의 전환(LGTM)

createRoutine가 RoutineCreationEntity 하나로 통합된 방향성 좋습니다. UseCase/Repository 전반의 API와 일관적입니다.

Projects/DataSource/Sources/DTO/RoutineCreationDTO.swift (2)

8-9: Domain 의존성 추가(LGTM)

DTO ↔ Domain 매핑을 위한 Domain 모듈 import 적절합니다.


11-20: 필드 추가 정렬(LGTM)

서버 페이로드 필드와 Domain 엔티티에 필요한 값들이 잘 정렬되었습니다.

Projects/Domain/Sources/Entity/RoutineCreationEntity.swift (1)

10-42: 엔티티 도입 방향성 적절(LGTM)

생성/수정 공용 페이로드로 RoutineCreationEntity를 둔 설계가 간단하고 명확합니다.

Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (4)

41-43: 기간 옵셔널화(LGTM)

periodPublisher를 (Date?, Date?)로 변경한 것은 UI 유효성 판단과 일치합니다. 다운스트림 구독자들이 옵셔널 대응하도록 확인만 부탁드립니다.


260-271: RoutineCreationEntity 구성(LGTM)

  • repeatDay 계산, 기간/시간 문자열화, 추천 루틴 타입/적용일자 전달 등 저장 페이로드 작성 흐름이 명확합니다. UseCase에서 공백 서브루틴 필터링도 보완되어 있어 안정적입니다.

247-250: 서버 포맷 확인: DateType.yearMonthDate("yyyy-MM-dd"), DateType.time("HH:mm:ss")
현재 코드에서

  • .yearMonthDate"yyyy-MM-dd"
  • .time"HH:mm:ss"
    로 설정되어 있습니다.
    서버 API가 위 포맷을 정확히 기대하는지 다시 한 번 확인해 주세요.
    불일치 시 DateType 또는 formatString을 조정해 주시기 바랍니다.

134-135: RoutineEntity에 startDate·endDate 필드 없어 TODO 적용 불가

  • Domain/Sources/Entity/RoutineEntity.swift 에 시작·종료일(startDate/endDate)이 정의되어 있지 않습니다.
  • RoutineResponseDTO 역시 날짜 정보가 포함되어 있지 않아 ViewModel(fetchRoutine)에서 periodStartSubject/periodEndSubject를 설정할 수 없습니다.
  • 만약 UI에서 기존 루틴의 기간을 보여줘야 한다면, 백엔드 API와 DTO/Entity에 해당 날짜 필드를 추가한 뒤 매핑 로직을 구현해야 합니다.
  • 현 시점에서는 해당 TODO 주석을 제거하거나, 별도 이슈로 등록해 백엔드 지원 후 처리하시기 바랍니다.

Likely an incorrect or invalid review comment.

Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (1)

120-127: 클리어 액션이 ViewModel과 동기화되는 점 좋습니다

텍스트 삭제 후 즉시 .configureName("")를 호출하여 상태를 일관되게 유지하는 부분이 좋습니다.

Copy link
Copy Markdown

@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

♻️ Duplicate comments (2)
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (1)

23-24: updateType 주입 경로 추가로 applyDateType 미전달 이슈 해결

.configureUpdateType 인풋과 해당 분기 처리가 추가되어 ViewController에서 받은 업데이트 적용 시점을 VM에 정상 전달할 수 있게 되었습니다. 지난 코멘트에서 제기된 누락 문제를 해결합니다.

Also applies to: 80-84

Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (1)

72-85: updateInfo(updateType) → ViewModel 전달까지 일관 연결 완료

초기화 시 updateInfo에 따라 fetch 호출과 configureUpdateType 전달을 모두 수행하여 수정 플로우의 “적용 시점”이 유실되지 않습니다. 지난 리뷰 지적 사항을 잘 반영했습니다.

🧹 Nitpick comments (2)
Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (2)

132-136: 등록 버튼의 초기 상태를 명시적으로 비활성화하여 깜빡임/초기 스타일 불일치 방지

초기 진입 시점에는 아직 isRoutineValid 발행 전일 수 있으므로 버튼을 기본 disabled 스타일로 세팅해 두는 것이 안전합니다.

아래처럼 초기 상태를 추가해 주세요.

 registerButton.titleLabel?.font = BitnagilFont.init(style: .body1, weight: .semiBold).font
+// 초기 상태: 비활성화
+registerButton.isEnabled = false
+registerButton.backgroundColor = BitnagilColor.gray95
+registerButton.setTitleColor(.white, for: .disabled)
 registerButton.addAction(
     UIAction { [weak self] _ in
         self?.viewModel.action(input: .registerRoutine)
     },
     for: .touchUpInside)

275-288: UIButton 상태별 타이틀 색상 지정 누락(Disabled 상태 반영 필요)

현재 .normal 상태 색상만 설정하고 있어, disabled 시 시스템 기본 색으로 보일 수 있습니다. 의도대로 비활성화 시에도 흰색 유지가 필요하면 .disabled에 대해서도 지정해 주세요.

아래처럼 두 분기 모두에 .disabled 색을 추가하는 방식을 권장합니다.

                 if isRoutineValid {
                     self?.registerButton.isEnabled = true
                     self?.registerButton.backgroundColor = BitnagilColor.gray10
                     self?.registerButton.setTitleColor(.white, for: .normal)
+                    self?.registerButton.setTitleColor(.white, for: .disabled)
                 } else {
                     self?.registerButton.isEnabled = false
                     self?.registerButton.backgroundColor = BitnagilColor.gray95
                     self?.registerButton.setTitleColor(.white, for: .normal)
+                    self?.registerButton.setTitleColor(.white, for: .disabled)
                 }
📜 Review details

Configuration used: .coderabbit.yaml
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 cc73d3c and 03406d6.

📒 Files selected for processing (3)
  • Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift (1 hunks)
  • Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (11 hunks)
  • Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (8 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-16T09:21:15.038Z
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Onboarding/View/OnboardingRecommendedRoutineView.swift:57-59
Timestamp: 2025-07-16T09:21:15.038Z
Learning: OnboardingRecommendedRoutineView에서 viewWillAppear에 registerOnboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.

Applied to files:

  • Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift
🧬 Code Graph Analysis (3)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift (4)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (1)
  • configure (67-81)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineRepeatContentView.swift (1)
  • configure (63-67)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineTimeContentView.swift (1)
  • configure (65-79)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineNameContentView.swift (1)
  • configure (73-92)
Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (8)
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (4)
  • action (80-112)
  • fetchRoutine (114-150)
  • configureName (169-171)
  • registerRoutine (239-279)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
  • fetchRoutine (18-21)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (1)
  • fetchRoutine (30-35)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineNameContentView.swift (2)
  • textFieldEditingChanged (242-244)
  • configure (73-92)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift (3)
  • configure (69-76)
  • configure (179-204)
  • configure (206-208)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutinePeriodContentView.swift (1)
  • configure (67-81)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineRepeatContentView.swift (1)
  • configure (63-67)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineTimeContentView.swift (1)
  • configure (65-79)
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (2)
Projects/Shared/Sources/Extension/Date+.swift (1)
  • convertToString (16-22)
Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
  • saveRoutine (31-48)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift

[Warning] 137-137: TODOs should be resolved (- routine 엔티티 변경 이후 시작일자, 종료 일...)

(todo)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
Projects/Presentation/Sources/RoutineCreation/View/Component/RoutineCreationCardView.swift (1)

206-207: 의도된 파라미터명/타입 변경 잘 반영됨

configure(dependency:)로의 시그니처/호출 정리가 ContentView.Dependency 흐름과 일관됩니다. 다른 ContentView들과의 인터페이스도 맞춰졌습니다.

Projects/Presentation/Sources/RoutineCreation/ViewModel/RoutineCreationViewModel.swift (3)

42-42: 기간 옵셔널화 및 Publisher 시그니처 변경 적절

periodPublisher(Date?, Date?)로, 내부 subject를 Date?로 바꾼 점은 UI의 플레이스홀더/비선택 상태를 표현하기에 적합합니다.

Also applies to: 51-53


224-231: 기간을 제출 유효성의 필수 항목으로 강제 — 요구사항 최종 확인 요청

현재는 이름/시간/시작일/종료일이 모두 채워져야만 버튼이 활성화됩니다. PR 요약에서 “Presentation에서는 기간을 옵셔널로 처리”라고 했던 만큼, 실제 요구사항이 “기간 선택은 UI에서만 옵셔널, 제출 시에는 필수”인지 확인이 필요합니다. 만약 제출도 옵셔널 허용이라면 이 조건을 조정해야 합니다.


242-275: RoutineCreationEntity 단일 페이로드 구성으로 일관성 확보

반복 요일 재계산, 날짜/시간 포맷 변환, 추천 타입/적용 시점 포함 등 엔티티 구성 로직이 명확하고 도메인/레포/유즈케이스 변경과 잘 맞물립니다.

Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationViewController.swift (1)

211-218: ContentView.Dependency 기반 바인딩이 전반적으로 정확함

  • 이름: namePublishernameTextField 반영
  • 세부루틴: subRoutines 의존성/서브타이틀 구성 분리
  • 반복: 타입별 의존성 구성(.none/.daily/.weekly) 및 표시 텍스트 정렬
  • 기간/시간: 옵셔널 값 허용하는 의존성으로 업데이트

새로운 의존성 모델로의 마이그레이션이 일관되게 잘 적용되었습니다.

Also applies to: 222-223, 241-242, 248-249, 256-258, 264-265

Comment on lines +137 to +138
// TODO: - routine 엔티티 변경 이후 시작일자, 종료 일자 설정 필요 + 추천 타입 있으면 추천 타입도 설정 필요

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Aug 18, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

수정 화면에서 기간(시작/종료) 프리필 누락 — 즉시 매핑 추가 필요

루틴 수정 진입 시 시작/종료 일자가 프리필되지 않으면, 현재 유효성 검사(라인 224-231)로 인해 등록/수정 버튼이 비활성화 상태를 유지할 수 있습니다. 실사용에 직접적인 영향이 있으니 이 TODO는 본 PR에서 처리하는 편이 안전합니다.

아래처럼 서버에서 받은 날짜 문자열을 Date로 변환해 subject에 반영해 주세요. 추천 타입도 엔티티에 있다면 같이 세팅하면 좋습니다.

-                // TODO: - routine 엔티티 변경 이후 시작일자, 종료 일자 설정 필요 + 추천 타입 있으면 추천 타입도 설정 필요
+                // 시작/종료 일자 프리필
+                let start = Date.convertToDate(from: routine.startDate, dateType: .yearMonthDate)
+                let end = Date.convertToDate(from: routine.endDate, dateType: .yearMonthDate)
+                periodStartSubject.send(start)
+                periodEndSubject.send(end)
+                // 추천 루틴 타입 (존재 시)
+                if let type = routine.recommendedRoutineType {
+                    routineType = type
+                }

참고: routine.startDate/routine.endDate/recommendedRoutineType 프로퍼티 명은 실제 엔티티 정의에 맞게 조정해 주세요. 필요하시면 해당 부분까지 전체 패치 제안 드리겠습니다.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 137-137: TODOs should be resolved (- routine 엔티티 변경 이후 시작일자, 종료 일...)

(todo)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

루틴 엔티티에 해당 프로퍼티들이 새로 추가될 예정입니다. 일주일 내로 pr에 반영하겠습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@taipaise 이해했습니다! 엔티티 레벨에서 프로퍼티가 추가된 후 매핑 로직을 구현하는 것이 더 적절하겠네요. 일주일 내로 반영 예정이라니 기대하겠습니다. 👍

엔티티 변경 작업 완료되면 해당 부분 리뷰도 도움이 필요하시면 언제든 말씀해 주세요!

Copy link
Copy Markdown
Contributor

@choijungp choijungp left a comment

Choose a reason for hiding this comment

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

리뷰 노투 확인했듭니다 ~~~~
수정 버튼 누를때 같이 보내도록 할게용 ~~~~~~ 감사 띵 !!!

짱푸루부 ~~ ☃︎👍🏻

Comment on lines +277 to +287
.sink { [weak self] isRoutineValid in
if isRoutineValid {
self?.registerButton.isEnabled = true
self?.registerButton.backgroundColor = BitnagilColor.gray10
self?.registerButton.setTitleColor(.white, for: .normal)
} else {
self?.registerButton.isEnabled = false
self?.registerButton.backgroundColor = BitnagilColor.gray95
self?.registerButton.setTitleColor(.white, for: .normal)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

추후에 PrimaryButton 사용해서 updateButtonState(buttonState: ButtonState)로 처리하면 더 easy 할 듯 합니다 ~~~ 굿쟙 !!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

확인했습니다!! 재활용 고고곡

startDate: routineStartDate,
endDate: routineEndDate,
executionTime: executionTime,
subroutines: subRoutineName,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요기서는 왜 subroutines의 r이 소문자인가용 ???
사실 PR 넘 완벽해서 사소한게 보이는 것일 뿐 ~~ 나중 수정두 괜찮슴니다 !!!

Copy link
Copy Markdown
Collaborator Author

@taipaise taipaise Aug 18, 2025

Choose a reason for hiding this comment

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

루틴 리스트 작업하실때 일단 이번 작업 내용이 있는게 편하실거 같아서!!! 일단 머지 하구 오늘 따로 pr 올리겠습니다!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

완존 정답 ..........

@taipaise taipaise merged commit b02122b into develop Aug 18, 2025
2 checks passed
@taipaise taipaise deleted the feat/routine-creation branch February 17, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants