Conversation
WalkthroughRecommendedRoutine 데이터 흐름에 routineType 추가(DTO→Entity→Presentation Model). Domain 엔터티에 type 필드 도입 및 이니셜라이저 갱신. Presentation에서 새로운 RoutineCardView 도입 및 델리게이트 패턴 적용, 기존 RecommendedRoutineCardView 제거. ViewController는 새 카드/델리게이트로 교체하고 레이아웃·스크롤 동작 일부 조정. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Card as RoutineCardView
participant VC as RecommendedRoutineViewController
participant Nav as UINavigationController
participant RC as RoutineCreationViewController
User->>Card: Tap Plus
Card-->>VC: routineCardView(_:didTapPlusButton: routine)
VC->>Nav: push RC(routine.id)
Nav->>RC: show
sequenceDiagram
participant API as API/JSON
participant DTO as RecommendedRoutineDTO
participant Entity as RecommendedRoutineEntity
participant Model as RecommendedRoutine
API-->>DTO: recommendedRoutineType (String)
DTO->>Entity: type = RoutineCategoryType(rawValue) ?? .rest
Entity-->>Model: routineType = type<br/>subRoutines = map(.title)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 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 unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineViewController.swift (1)
81-83: 메모리 릭 가능성: 버튼 액션 클로저에서 self 강한 캡처
levelButton이UIAction을 강하게 보유하므로, 현재 구현은 VC ↔︎ 버튼 간 강한 참조 사이클을 만들 수 있습니다. 다른 곳에서처럼[weak self]를 사용하세요.- levelButton.addAction(UIAction { _ in - self.showBottomSheet() - }, for: .touchUpInside) + levelButton.addAction(UIAction { [weak self] _ in + self?.showBottomSheet() + }, for: .touchUpInside)
🧹 Nitpick comments (4)
Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift (1)
15-15: 서버 필드 누락 대비: routineType을 Optional로 디코딩 고려v2에서 항상 내려온다면 현재도 문제는 없지만, 회귀/실험 플래그 등으로 필드가 누락될 경우 디코딩 자체가 실패합니다. 디코딩을 옵셔널로 완화하고 폴백을 유지하면 회복력이 좋아집니다.
백엔드에서
recommendedRoutineType필드가 항상 보장되는지 확인 부탁드립니다. 누락 가능성이 있다면 아래와 같이 변경을 제안합니다.- let routineType: String + let routineType: String? @@ - type: RoutineCategoryType(rawValue: routineType) ?? .rest, + type: RoutineCategoryType(rawValue: routineType ?? "") ?? .rest,Also applies to: 23-23, 44-45
Projects/Presentation/Sources/Common/Component/RoutineCardView.swift (1)
38-39: 미사용 프로퍼티 정리 권장 (edit/delete 버튼)
editButton,deleteButton이 선언만 있고 사용되지 않습니다. 향후 사용 계획이 없다면 제거하여 뷰의 책임을 명확히 하고 노이즈를 줄이는 것을 권장합니다.- private let editButton = UIButton() - private let deleteButton = UIButton()Projects/Presentation/Sources/Onboarding/Model/RecommendedRoutine.swift (1)
19-35: 불필요한 멤버와이즈 이니셜라이저 제거 제안Swift는 동일 시그니처의 멤버와이즈 이니셜라이저를 자동 합성합니다. 커스텀 로직이 없다면 제거하여 보일러플레이트를 줄이는 것이 좋습니다. SwiftLint 경고(unneeded_synthesized_initializer)와도 일치합니다.
- init( - id: Int, - mainTitle: String, - subTitle: String?, - subRoutines: [String], - routineCategory: RoutineCategoryType, - routineType: RoutineCategoryType, - routineLevel: RoutineLevelType - ) { - self.id = id - self.mainTitle = mainTitle - self.subTitle = subTitle - self.subRoutines = subRoutines - self.routineCategory = routineCategory - self.routineType = routineType - self.routineLevel = routineLevel - }Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineViewController.swift (1)
226-231: 정리 제안: 스택뷰 초기화 시 불필요한 예외 조건 제거
registerEmotionButton은 스택뷰의arrangedSubviews에 포함되지 않습니다. 조건문을 제거하고 단순화할 수 있습니다.- recommendedRoutineStackView.arrangedSubviews.forEach { view in - if view != registerEmotionButton { - recommendedRoutineStackView.removeArrangedSubview(view) - view.removeFromSuperview() - } - } + recommendedRoutineStackView.arrangedSubviews.forEach { view in + recommendedRoutineStackView.removeArrangedSubview(view) + view.removeFromSuperview() + }
📜 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.
📒 Files selected for processing (6)
Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift(2 hunks)Projects/Domain/Sources/Entity/RecommendedRoutineEntity.swift(2 hunks)Projects/Presentation/Sources/Common/Component/RoutineCardView.swift(5 hunks)Projects/Presentation/Sources/Onboarding/Model/RecommendedRoutine.swift(2 hunks)Projects/Presentation/Sources/RecommendedRoutine/View/Component/RecommendedRoutineCardView.swift(0 hunks)Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineViewController.swift(8 hunks)
💤 Files with no reviewable changes (1)
- Projects/Presentation/Sources/RecommendedRoutine/View/Component/RecommendedRoutineCardView.swift
🧰 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/RecommendedRoutine/View/RecommendedRoutineViewController.swift
🧬 Code Graph Analysis (2)
Projects/Presentation/Sources/Common/Component/RoutineCardView.swift (2)
Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineViewController.swift (1)
routineCardView(334-341)Projects/Presentation/Sources/Common/Extension/UIImage+.swift (1)
resizeAspectFit(20-35)
Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineViewController.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
resolve(18-25)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Onboarding/Model/RecommendedRoutine.swift
[Warning] 19-19: This memberwise initializer would be synthesized automatically - you do not need to define it
(unneeded_synthesized_initializer)
🔇 Additional comments (6)
Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift (1)
44-46: Enum 변환 실패 시 .rest 기본값 적용, 도메인 불변성 보장서버 값이 미정의/미일치일 때
.rest로 폴백하는 처리가 깔끔합니다.Domain의type이 비-옵셔널인 제약과도 잘 맞습니다.Projects/Domain/Sources/Entity/RecommendedRoutineEntity.swift (1)
13-15: Domain에 비-옵셔널 type 추가 방향 적절
type을 비-옵셔널로 올린 설계가 DTO→Entity→Presentation 전파와도 잘 맞고, 후단 UI 로직 단순화에도 도움됩니다.Also applies to: 22-24, 30-32
Projects/Presentation/Sources/Common/Component/RoutineCardView.swift (1)
35-37: routineType 기반 카테고리 아이콘 연동 좋습니다
routine.routineType을 사용해 아이콘/배경색을 결정하는 구조가 데이터 흐름과 잘 맞습니다.Projects/Presentation/Sources/Onboarding/Model/RecommendedRoutine.swift (1)
14-17: 모델 확장(서브루틴/루틴 타입) 전파 OK
subRoutines와routineType추가 및 Entity→Model 매핑이 일관적입니다. 이후 UI 컴포넌트(카드 뷰)에서도 해당 데이터를 잘 활용하고 있습니다.Also applies to: 24-26, 31-34, 44-47
Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineViewController.swift (2)
236-240: 새 카드 뷰 적용 및 델리게이트 연결 정상
RoutineCardView(routine:)생성, 캐시 딕셔너리 저장, 델리게이트 연결, 스택뷰 추가까지 흐름이 간결하고 명확합니다.
200-203: 필터 변경 시 스크롤 상단 복귀 UX 반영 👍카테고리/난이도 변경 시 상단으로 스크롤 이동하는 처리가 사용자 경험에 적합합니다.
Also applies to: 303-304
| plusButton.addAction( | ||
| UIAction { [weak self] _ in | ||
| guard let self else { return } | ||
| delegate?.routineCardView(self, didTapPlusButton: routine) | ||
| }, for: .touchUpInside) | ||
|
|
There was a problem hiding this comment.
컴파일 오류: 클로저 내부에서 self 명시 및 프로퍼티 접근자 누락
클로저 내부에서 self를 명시해야 하며, delegate/routine 접근 시에도 self.가 필요합니다. 현재 코드는 컴파일되지 않습니다.
아래와 같이 수정해주세요.
- plusButton.addAction(
- UIAction { [weak self] _ in
- guard let self else { return }
- delegate?.routineCardView(self, didTapPlusButton: routine)
- }, for: .touchUpInside)
+ plusButton.addAction(
+ UIAction { [weak self] _ in
+ guard let self = self else { return }
+ self.delegate?.routineCardView(self, didTapPlusButton: self.routine)
+ },
+ for: .touchUpInside
+ )📝 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.
| plusButton.addAction( | |
| UIAction { [weak self] _ in | |
| guard let self else { return } | |
| delegate?.routineCardView(self, didTapPlusButton: routine) | |
| }, for: .touchUpInside) | |
| plusButton.addAction( | |
| UIAction { [weak self] _ in | |
| guard let self = self else { return } | |
| self.delegate?.routineCardView(self, didTapPlusButton: self.routine) | |
| }, | |
| for: .touchUpInside | |
| ) |
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Common/Component/RoutineCardView.swift around
lines 74–79, the closure uses an implicit guard let self and accesses
delegate/routine without self, causing a compile error; change the guard to
"guard let self = self else { return }" and prefix property accesses with self
(e.g., self.delegate and self.routine) inside the closure so the code compiles
and captures self weakly correctly.
taipaise
left a comment
There was a problem hiding this comment.
행복한 PR이군요~ 이게 바로 '정상화' 아닐까요? 가능하면 항상 pr을 1000줄 이하로 올리고 싶은데 요즘 너무 작업에 치이기도 하고, UI 쪽 작업이 커서 그러기 쉽지 않았던것 같아요
말씀해주신 엔티티는 하나의 추천 루틴을 구분하기 위해서라면 어쩔 수 없지 않을까 합니다~!~!
오늘도 고생하셨습니다!
|
|
||
| private let headerInfoStackView = UIStackView() | ||
| private let categoryIconView = RoutineCategoryIcon(routineCategory: .connection) | ||
| private lazy var categoryIconView = RoutineCategoryIcon(routineCategory: routine.routineType) |
There was a problem hiding this comment.
새로 수정된 코드에서 이 categoryIconView 관련 코드를 찾아볼 수 없었는데요! 혹시 lazy var로 설정해주신 이유를 알 수 있을까요?!?
There was a problem hiding this comment.
RoutineCardView에서만 CategoryIconView를 사용할 것 같아서 같은 파일 내부에 fileprivate으로 구현해두었습니다 !!!
그리고 RoutineCardView의 생성자로 들어온 routine의 카테고리 값으로 초기화 해야 해가주구 lazy var로 설정했습니다 !!!
🌁 Background
이전 추천 루틴 화면 디자인 v2 PR에 이어서 서버 연동을 했습니다 ~
이렇게 가벼울 줄 알았다면 ... 저번 PR에 합쳐서 하는것이었는데요 .. ㅠㅠ
너무너무너무 가벼운 PR이니 매우매우매우 가볍게 봐주세요 !! 미리감사 ~~
📱 Screenshot
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-08-18.at.15.16.39.mp4
👩💻 Contents
📝 Review Note
RoutineCategory vs. RoutineType
RecommendedRoutineEntity를 보면 category랑 type 값이 있는데요, 둘이 상당부분 많이 비슷합니다 ㅠㅠ !!!
타입도 걍 똑같죠 ???
category와 type이랑 뭐가 다른 것이냐 하면 ..
category는 추천 루틴 상단에 카테고리를 뜻합니다 !! 그래서 맞춤 추천 값이 들어올 수 있어요 !!!
하지만 type은 해당 추천 루틴의 타입 값을 의미합니다.
그래서 맞춤 추천일 때에 어떤 루틴 타입인지 보여주기 위해 2개의 값이 모두 필요해요 ㅠㅠ
이거슨 어쩔 수 없겟죠 ?? ...
📣 Related Issue
Summary by CodeRabbit