[Feat-T3-130] 홈 API 연동 (루틴 조회, 루틴 상세 보기)#31
Conversation
Walkthrough이 변경사항은 루틴 및 사용자 데이터 도메인 전반에 걸쳐 새로운 엔터티, DTO, 레포지토리, 유즈케이스, 엔드포인트, 의존성 등록, 그리고 프레젠테이션 계층의 모델/뷰/뷰모델 연결을 대대적으로 추가 및 개편합니다. 또한 이미지 리소스와 디자인 시스템 아이콘, 날짜 변환 확장, UI 컴포넌트의 접근성 및 레이아웃, 그리고 데이터 흐름을 위한 Combine 퍼블리셔도 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant View as HomeView
participant VM as HomeViewModel
participant UC as RoutineUseCase
participant Repo as RoutineRepository
participant Net as NetworkService
View->>VM: fetchRoutines(for: date)
VM->>UC: fetchRoutines(startDate, endDate)
UC->>Repo: fetchRoutines(from, to)
Repo->>Net: request(RoutineEndpoint.fetchRoutines)
Net-->>Repo: RoutineDictionaryDTO
Repo-->>UC: [String: [RoutineEntity]]
UC-->>VM: [String: [RoutineEntity]]
VM-->>View: Output.routinesPublisher.send()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45분 Possibly related PRs
Suggested reviewers
Poem
✨ 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. 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)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (8)
Projects/Presentation/Sources/Login/Model/TermsType.swift (1)
22-23: URL 형식 불일치 및 영구성 검증 필요두 URL에서 날짜 형식이 일치하지 않습니다:
- Line 22:
2025-7-20- Line 23:
2025-07-20또한 URL에 특정 날짜가 포함되어 있어 향후 문서 업데이트 시 링크가 깨질 수 있습니다.
-case .service: URL(string: "https://complex-wombat-99f.notion.site/2025-7-20-236f4587491d8071833adfaf8115bce2") -case .privacy: URL(string: "https://complex-wombat-99f.notion.site/2025-07-20-236f4587491d80308016eb810692d18b") +case .service: URL(string: "https://complex-wombat-99f.notion.site/2025-07-20-236f4587491d8071833adfaf8115bce2") +case .privacy: URL(string: "https://complex-wombat-99f.notion.site/2025-07-20-236f4587491d80308016eb810692d18b")Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift (1)
10-12: Protocol 설계가 잘되어 있으나 반환 타입 명확화 필요
RoutineUseCaseProtocol의 설계가 간결하고 적절합니다.async throws패턴도 네트워크 호출에 적합합니다.하지만 반환 타입
[String: [RoutineEntity]]에서String키가 무엇을 의미하는지 명확하지 않습니다. 날짜 문자열인지, 카테고리인지 등을 문서화하거나 타입 별칭을 사용하는 것을 고려해보세요.예시:
public typealias DateString = String func fetchRoutines(startDate: Date, endDate: Date) async throws -> [DateString: [RoutineEntity]]Projects/Presentation/Sources/Home/Model/MainRoutine.swift (1)
20-30: 도메인 엔티티에서 프레젠테이션 모델로의 변환 로직이 잘 구현됨
RoutineEntity에서MainRoutine으로의 변환 로직이 적절합니다:
compactMap을 사용한repeatDay변환으로 잘못된 값들을 필터링Date.convertToDate의 실패 시 기본값Date()사용- 서브루틴 엔티티들의 적절한 매핑
다만
executionTime파싱 실패 시 현재 시간을 기본값으로 사용하는 것이 적절한지 검토해보세요. 파싱 실패 시 더 명확한 기본값(예: 특정 시간)을 사용하는 것도 고려해볼 수 있습니다.Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
22-23: 불필요한 var 사용
routineEntities변수가 수정되지 않으므로let을 사용하는 것이 더 적절합니다.- var routineEntities = try await routineRepository.fetchRoutines(from: start, to: end) - return routineEntities + let routineEntities = try await routineRepository.fetchRoutines(from: start, to: end) + return routineEntities또는 더 간결하게:
- var routineEntities = try await routineRepository.fetchRoutines(from: start, to: end) - return routineEntities + return try await routineRepository.fetchRoutines(from: start, to: end)Projects/DataSource/Sources/Endpoint/UserEndpoint.swift (1)
17-19: path와 baseURL의 중복현재
path가baseURL과 동일한 값을 반환하고 있습니다. 일반적으로path는 리소스 경로만 포함하고, 실제 요청 URL은baseURL + path로 구성됩니다.다음과 같이 수정하는 것을 고려해보세요:
var baseURL: String { - return AppProperties.baseURL + "/api/v1/users/infos" + return AppProperties.baseURL } var path: String { - return baseURL + return "/api/v1/users/infos" }Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (1)
17-21: path와 baseURL의 중복
UserEndpoint와 동일한 패턴으로,path가baseURL과 같은 값을 반환하고 있습니다. 일관성을 위해 분리하는 것을 고려해보세요.var baseURL: String { - return AppProperties.baseURL + "/api/v1/routines" + return AppProperties.baseURL } var path: String { switch self { - case .fetchRoutines: baseURL + case .fetchRoutines: "/api/v1/routines" } }Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift (1)
43-43:compactMap대신map사용을 권장합니다.
toRecommendedSubRoutineEntity()메서드는 옵셔널이 아닌 값을 반환하므로,compactMap대신map을 사용하는 것이 더 명확합니다.- subRoutines: subRoutines.compactMap({ $0.toRecommendedSubRoutineEntity() })) + subRoutines: subRoutines.map({ $0.toRecommendedSubRoutineEntity() }))Projects/Presentation/Sources/Home/View/HomeView.swift (1)
514-516: TODO: 루틴 삭제 기능 구현이 필요합니다.루틴 삭제 기능이 아직 구현되지 않았습니다. 서버 API 연동과 UI 업데이트 로직이 필요합니다.
루틴 삭제 기능 구현을 도와드릴까요? ViewModel에 삭제 액션을 추가하고 UI를 업데이트하는 코드를 생성해드릴 수 있습니다.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (15)
Projects/Presentation/Resources/Images.xcassets/edit_icon.imageset/edit_icon.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/edit_icon.imageset/edit_icon@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/edit_icon.imageset/edit_icon@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/repeat_icon.imageset/repeat_icon.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/repeat_icon.imageset/repeat_icon@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/repeat_icon.imageset/repeat_icon@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/routine_icon.imageset/routine_icon.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/routine_icon.imageset/routine_icon@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/routine_icon.imageset/routine_icon@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/subRoutine_icon.imageset/subRoutine_icon.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/subRoutine_icon.imageset/subRoutine_icon@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/subRoutine_icon.imageset/subRoutine_icon@3x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/trash_icon.imageset/trash_icon.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/trash_icon.imageset/trash_icon@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/trash_icon.imageset/trash_icon@3x.pngis excluded by!**/*.png
📒 Files selected for processing (41)
Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift(1 hunks)Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift(3 hunks)Projects/DataSource/Sources/DTO/RecommendedSubRoutineDTO.swift(1 hunks)Projects/DataSource/Sources/DTO/RoutineResponseDTO.swift(1 hunks)Projects/DataSource/Sources/DTO/SubRoutineResponseDTO.swift(1 hunks)Projects/DataSource/Sources/DTO/UserDataResponseDTO.swift(1 hunks)Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift(1 hunks)Projects/DataSource/Sources/Endpoint/UserEndpoint.swift(1 hunks)Projects/DataSource/Sources/Repository/RoutineRepository.swift(1 hunks)Projects/DataSource/Sources/Repository/UserDataRepository.swift(1 hunks)Projects/Domain/Sources/DomainDependencyAssembler.swift(1 hunks)Projects/Domain/Sources/Entity/RecommendedRoutineEntity.swift(2 hunks)Projects/Domain/Sources/Entity/RoutineEntity.swift(1 hunks)Projects/Domain/Sources/Entity/SubRoutineEntity.swift(1 hunks)Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift(1 hunks)Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift(1 hunks)Projects/Domain/Sources/Protocol/UseCase/RoutineUseCaseProtocol.swift(1 hunks)Projects/Domain/Sources/Protocol/UseCase/UserDataUseCaseProtocol.swift(1 hunks)Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift(1 hunks)Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift(1 hunks)Projects/Presentation/Resources/Images.xcassets/edit_icon.imageset/Contents.json(1 hunks)Projects/Presentation/Resources/Images.xcassets/repeat_icon.imageset/Contents.json(1 hunks)Projects/Presentation/Resources/Images.xcassets/routine_icon.imageset/Contents.json(1 hunks)Projects/Presentation/Resources/Images.xcassets/subRoutine_icon.imageset/Contents.json(1 hunks)Projects/Presentation/Resources/Images.xcassets/trash_icon.imageset/Contents.json(1 hunks)Projects/Presentation/Sources/Common/Component/CustomBottomSheet.swift(1 hunks)Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift(1 hunks)Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift(1 hunks)Projects/Presentation/Sources/Home/Model/MainRoutine.swift(1 hunks)Projects/Presentation/Sources/Home/Model/SubRoutine.swift(1 hunks)Projects/Presentation/Sources/Home/View/Component/DateView.swift(1 hunks)Projects/Presentation/Sources/Home/View/Component/MainRoutineView.swift(0 hunks)Projects/Presentation/Sources/Home/View/Component/RoutineView.swift(2 hunks)Projects/Presentation/Sources/Home/View/HomeView.swift(14 hunks)Projects/Presentation/Sources/Home/View/RoutineDetailView.swift(1 hunks)Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift(3 hunks)Projects/Presentation/Sources/Login/Model/TermsType.swift(1 hunks)Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift(2 hunks)Projects/Presentation/Sources/RoutineCreation/Model/Week.swift(1 hunks)Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationView.swift(1 hunks)Projects/Shared/Sources/Extension/Date+.swift(2 hunks)
💤 Files with no reviewable changes (1)
- Projects/Presentation/Sources/Home/View/Component/MainRoutineView.swift
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
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/Common/PresentationDependencyAssembler.swiftProjects/Domain/Sources/DomainDependencyAssembler.swiftProjects/Presentation/Sources/Home/View/RoutineDetailView.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: datepickerview에서 locale을 "en_us"로 설정하는 것은 한국어 앱이지만 시간을 am/pm 형식으로 표시하기 위한 의도적인 ux 설계 결정이다. 한국 로케일은 기...
Learnt from: taipaise
PR: YAPP-Github/Bitnagil-iOS#26
File: Projects/Presentation/Sources/DatePicker/View/DatePickerView.swift:33-53
Timestamp: 2025-07-27T13:07:40.469Z
Learning: DatePickerView에서 locale을 "en_US"로 설정하는 것은 한국어 앱이지만 시간을 AM/PM 형식으로 표시하기 위한 의도적인 UX 설계 결정이다. 한국 로케일은 기본적으로 24시간 형식을 사용하기 때문이다.
Applied to files:
Projects/Presentation/Sources/Home/View/Component/RoutineView.swiftProjects/Shared/Sources/Extension/Date+.swift
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Applied to files:
Projects/Presentation/Sources/Home/View/RoutineDetailView.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
🧬 Code Graph Analysis (14)
Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (1)
register(14-16)
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (2)
register(14-16)resolve(18-25)
Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (4)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
loadNickname(59-68)Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)
loadNickname(17-23)Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift (1)
loadNickname(15-18)Projects/DataSource/Sources/Repository/AuthRepository.swift (1)
loadNickname(148-154)
Projects/Domain/Sources/Protocol/UseCase/UserDataUseCaseProtocol.swift (3)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
loadNickname(59-68)Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)
loadNickname(17-23)Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift (1)
loadNickname(15-18)
Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift (3)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (1)
fetchRoutines(13-23)Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
fetchRoutines(18-24)Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (2)
fetchRoutines(70-94)fetchRoutines(96-111)
Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift (2)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
loadNickname(59-68)Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)
loadNickname(17-23)
Projects/Presentation/Sources/Home/View/Component/RoutineView.swift (1)
Projects/Shared/Sources/Extension/Date+.swift (1)
convertToString(11-17)
Projects/Domain/Sources/DomainDependencyAssembler.swift (1)
Projects/Shared/Sources/DIContainer/DIContainer.swift (2)
register(14-16)resolve(18-25)
Projects/DataSource/Sources/Repository/UserDataRepository.swift (3)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
loadNickname(59-68)Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift (1)
loadNickname(15-18)Projects/DataSource/Sources/NetworkService/NetworkService.swift (1)
request(24-47)
Projects/DataSource/Sources/DTO/RoutineResponseDTO.swift (1)
Projects/DataSource/Sources/DTO/SubRoutineResponseDTO.swift (1)
toSubRoutineEntity(22-32)
Projects/Presentation/Sources/Home/Model/MainRoutine.swift (2)
Projects/Shared/Sources/Extension/Date+.swift (1)
convertToDate(19-25)Projects/Presentation/Sources/Home/Model/SubRoutine.swift (1)
toSubRoutine(18-24)
Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift (1)
Projects/DataSource/Sources/DTO/RecommendedSubRoutineDTO.swift (1)
toRecommendedSubRoutineEntity(21-23)
Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (3)
Projects/DataSource/Sources/Repository/RoutineRepository.swift (1)
fetchRoutines(13-23)Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
fetchRoutines(18-24)Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (2)
fetchRoutines(70-94)fetchRoutines(96-111)
Projects/DataSource/Sources/Endpoint/UserEndpoint.swift (3)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
loadNickname(59-68)Projects/DataSource/Sources/Repository/UserDataRepository.swift (1)
loadNickname(17-23)Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift (1)
loadNickname(15-18)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Home/View/HomeView.swift
[Warning] 515-515: TODOs should be resolved (루틴 삭제)
(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 (40)
Projects/Presentation/Resources/Images.xcassets/subRoutine_icon.imageset/Contents.json (1)
1-23: 표준적인 이미지 애셋 구조로 올바르게 설정됨Xcode의 표준 이미지 애셋 JSON 구조를 올바르게 따르고 있으며, 1x, 2x, 3x 스케일 변형이 적절히 정의되어 있습니다.
Projects/Presentation/Resources/Images.xcassets/repeat_icon.imageset/Contents.json (1)
1-23: 표준적인 이미지 애셋 구조로 올바르게 설정됨Xcode의 표준 이미지 애셋 JSON 구조를 올바르게 따르고 있으며, 모든 필요한 스케일 변형이 적절히 정의되어 있습니다.
Projects/Presentation/Resources/Images.xcassets/trash_icon.imageset/Contents.json (1)
1-23: 일관된 이미지 애셋 구조 유지다른 아이콘 애셋들과 동일한 표준 구조를 유지하고 있어 일관성이 좋습니다.
Projects/Shared/Sources/Extension/Date+.swift (3)
11-11: 모듈 간 사용을 위한 적절한 접근성 변경Domain과 Presentation 레이어에서 날짜 포맷팅 기능을 사용하기 위해 public으로 변경한 것이 적절합니다.
19-25: 문자열 파싱을 위한 유용한 메서드 추가기존 convertToString 메서드와 대칭적인 기능을 제공하며, 파싱 실패 가능성을 고려하여 Optional을 반환하는 것이 적절합니다.
27-48: DateType enum 확장 및 접근성 개선public 접근성 변경과 .time case 추가가 루틴 시간 표시 기능을 위해 적절하게 구현되었습니다.
Projects/Presentation/Sources/Home/View/Component/DateView.swift (1)
8-8: Date 확장 기능 사용을 위한 필수 import 추가DateView에서 Date+.swift의 convertToString 메서드를 사용하고 있으므로 Shared 모듈 import가 필요합니다.
Projects/Presentation/Resources/Images.xcassets/routine_icon.imageset/Contents.json (1)
1-24: LGTM!표준 iOS 애셋 카탈로그 구조를 올바르게 따르고 있으며, 1x, 2x, 3x 스케일 변형과 적절한 파일명 규칙을 사용하고 있습니다.
Projects/Presentation/Resources/Images.xcassets/edit_icon.imageset/Contents.json (1)
1-24: LGTM!표준 iOS 애셋 카탈로그 구조를 올바르게 따르고 있습니다.
Projects/Presentation/Sources/RoutineCreation/View/RoutineCreationView.swift (1)
430-430: Week enum 변경사항에 대한 올바른 적응
Weekenum의rawValue에서id속성으로의 변경을 올바르게 반영했습니다. enum이 String 기반으로 변경되면서 UI 인덱싱을 위한id속성을 사용하는 것이 적절합니다.Projects/Domain/Sources/Protocol/UseCase/UserDataUseCaseProtocol.swift (1)
8-10: 잘 설계된 사용자 데이터 UseCase 프로토콜단일 책임 원칙을 따르는 깔끔한 프로토콜 설계입니다. async/await 패턴을 사용하여 현대적인 Swift 비동기 처리를 지원하고, Clean Architecture의 도메인 계층에 적절하게 위치합니다.
관련 코드를 확인한 결과, 레포지토리 → UseCase → ViewModel로 이어지는 의존성 체인이 올바르게 구현되어 있습니다.
Projects/DataSource/Sources/DTO/UserDataResponseDTO.swift (1)
8-10: 단순하고 명확한 DTO 구조입니다.서버 응답을 위한 기본적인 DTO 구현이 적절합니다. 향후 유지보수성을 위해 프로퍼티에 대한 간단한 문서화를 고려해보세요.
Projects/Presentation/Sources/Common/Component/CustomBottomSheet.swift (1)
124-124: 외부에서 바텀시트를 닫을 수 있도록 접근 레벨이 적절히 변경되었습니다.HomeView에서 루틴 편집/삭제 시 바텀시트를 프로그래밍 방식으로 닫을 수 있도록 하는 변경사항이 PR 목적에 부합합니다.
Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift (1)
51-56: 루틴 상세 아이콘들이 일관된 패턴으로 잘 추가되었습니다.기존 아이콘들과 동일한 방식으로 구현되었고, 섹션 주석으로 깔끔하게 정리되어 있습니다. RoutineDetailView의 기능에 필요한 아이콘들이 적절히 추가되었습니다.
Projects/Domain/Sources/Protocol/Repository/UserDataRepositoryProtocol.swift (1)
12-12:loadNickname()async/await 패턴 적용 검증 완료
전체 코드베이스에서 비동기 호출이 일관되게 적용되었음을 확인했습니다.
- MypageViewModel:
let nickname = try await userDataRepository.loadNickname()- HomeViewModel:
Task { let nickname = try await userDataUseCase.loadNickname() nicknameSubject.send(nickname) }- Domain UseCase 및 Repository(Protocol‒구현체) 모두
func loadNickname() async throws -> String로 일치로컬 저장소 전용
AuthRepository의 privateloadNickname()는 별도 로직이므로 영향을 받지 않습니다.
해당 변경 사항을 승인합니다.Projects/DataSource/Sources/Common/DataSourceDependencyAssembler.swift (1)
36-38: 루틴 레포지토리 의존성 등록이 기존 패턴과 일관되게 추가되었습니다.다른 레포지토리 등록과 동일한 방식으로 구현되어 있으며, 루틴 관련 기능을 위한 DI 설정이 적절히 완료되었습니다.
Projects/Presentation/Sources/Home/View/Component/RoutineView.swift (2)
40-40: LGTM! 모델 변경에 맞춘 타입 일관성 유지
SubRoutine의id타입이Int에서String으로 변경됨에 따라 dictionary의 키 타입도 일치하도록 업데이트되었습니다. 타입 안전성을 보장하는 좋은 변경입니다.
68-68: UI 최적화를 위한 시간 포맷 개선
.amPmTime에서.amPmTimeShort로 변경하여 더 간결한 시간 표시가 가능해졌습니다. UI 공간 효율성 측면에서 좋은 개선입니다.Projects/Presentation/Sources/MyPage/ViewModel/MypageViewModel.swift (2)
41-48: 비동기 처리로 개선된 사용자 데이터 로딩
loadNickname()메서드가 비동기로 변경됨에 따라Task를 사용하여 적절히 대응했습니다. 메인 스레드 블로킹을 방지하는 좋은 개선입니다.
63-63: 실제 서비스 URL로 업데이트임시 구글/네이버 URL에서 실제 Notion 페이지 URL로 변경되어 기능이 완성되었습니다.
Also applies to: 67-67
Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift (1)
21-29: HomeViewModel의 의존성 주입 업데이트 완료
UserDataUseCaseProtocol과RoutineUseCaseProtocol두 개의 use case를 주입하도록 올바르게 업데이트되었습니다. 기존 코드와 동일한 패턴을 사용하여 일관성을 유지하고, missing dependency에 대한 적절한 에러 처리도 포함되어 있습니다.Projects/Presentation/Sources/RoutineCreation/Model/Week.swift (1)
8-27: 서버 API 호환성을 위한 우수한 설계 변경
Weekenum의 raw type을String으로 변경하면서도idcomputed property를 추가하여 기존 정수 인덱스 기능을 유지한 것이 훌륭합니다. 이 방식으로 서버 API 호환성과 기존 코드의 backward compatibility를 모두 확보했습니다.대문자 요일 이름 사용도 일반적인 API 컨벤션과 일치합니다.
Projects/Presentation/Sources/Home/Model/SubRoutine.swift (2)
8-15: 구조체 변경사항이 적절합니다.
id타입을String으로 변경하고sortIndex속성을 추가한 것이 도메인 엔터티와 일치합니다. API 응답 구조에 맞게 잘 정의되었습니다.
17-25: 도메인-프레젠테이션 변환 메서드가 잘 구현되었습니다.
SubRoutineEntity에서SubRoutine으로의 변환 로직이 명확하고 클린 아키텍처 원칙을 잘 따르고 있습니다. 속성 매핑도 적절합니다.Projects/Domain/Sources/UseCase/UserData/UserDataUseCase.swift (1)
8-19: UseCase 구현이 클린 아키텍처 패턴을 잘 따르고 있습니다.의존성 주입과 에러 전파가 적절하게 구현되어 있으며, 도메인 계층의 역할을 명확히 수행하고 있습니다.
Projects/DataSource/Sources/DTO/RecommendedSubRoutineDTO.swift (2)
10-18: DTO 구조가 적절하게 정의되었습니다.
Decodable프로토콜 준수와CodingKeys를 통한 JSON 키 매핑이 올바르게 구현되어 있습니다.
20-24: 도메인 엔터티 변환 메서드가 잘 구현되었습니다.속성 매핑이 명확하고 메서드명이 직관적입니다.
Projects/Domain/Sources/Protocol/Repository/RoutineRepositoryProtocol.swift (1)
8-15: Repository 프로토콜이 잘 설계되었습니다.메서드 시그니처가 적절하고 한국어 문서화가 명확합니다. 날짜 범위로 루틴을 조회하는 기능이 효율적으로 정의되어 있습니다.
Projects/Domain/Sources/DomainDependencyAssembler.swift (1)
56-68: 의존성 등록이 기존 패턴과 일관성 있게 구현되었습니다.
UserDataUseCaseProtocol과RoutineUseCaseProtocol등록이 기존 코드와 동일한 패턴을 따르고 있으며, 에러 메시지도 적절합니다.Projects/Domain/Sources/Entity/RecommendedRoutineEntity.swift (2)
14-14: 추천 루틴과 일반 루틴의 서브루틴 엔티티 분리가 적절함
RecommendedSubRoutineEntity로의 타입 변경이 도메인 모델의 책임 분리를 개선합니다. 추천 루틴의 서브루틴은 실제 사용자 루틴보다 단순한 구조(id, title만)가 적절합니다.Also applies to: 22-22
33-41: RecommendedSubRoutineEntity 구조가 간결하고 적절함추천 서브루틴 엔티티가 필수 정보(id, title)만 포함하여 간결하게 설계되었습니다.
Projects/DataSource/Sources/DTO/SubRoutineResponseDTO.swift (2)
10-19: 서브루틴 응답 DTO 구조가 포괄적이고 적절함서브루틴의 모든 필요한 속성들(ID, 이름, 완료 상태, 정렬 순서, 수정 여부 등)을 포함하여 잘 설계되었습니다.
21-33: 엔티티 변환 메서드가 올바르게 구현됨
toSubRoutineEntity()메서드가 모든 프로퍼티를 직접 매핑하여 DTO에서 도메인 엔티티로의 변환을 정확하게 수행합니다.Projects/Domain/Sources/UseCase/Routine/RoutineUseCase.swift (1)
11-25: LGTM!Clean Architecture 패턴을 잘 따르고 있으며, 의존성 주입과 비동기 처리가 적절히 구현되어 있습니다. Date를 String으로 변환하는 로직도 명확합니다.
Projects/DataSource/Sources/Endpoint/UserEndpoint.swift (1)
8-46: 엔드포인트 구현이 적절합니다Endpoint 프로토콜을 올바르게 구현하고 있으며, 인증이 필요한 GET 요청에 적합한 설정을 사용하고 있습니다.
Projects/Domain/Sources/Entity/RoutineEntity.swift (1)
8-43: LGTM!도메인 엔티티가 잘 설계되어 있습니다. 불변성을 유지하고 있으며,
repeatDay의 옵셔널 처리도 적절합니다. 프로퍼티 네이밍과 구조가 명확하고 일관성 있게 구현되어 있습니다.Projects/DataSource/Sources/Endpoint/RoutineEndpoint.swift (2)
37-44: 쿼리 파라미터 구현이 적절합니다날짜 범위를 쿼리 파라미터로 전달하는 방식이 GET 요청에 적합하며, 구현이 명확합니다.
8-53: 전체적으로 잘 구현된 엔드포인트입니다루틴 조회를 위한 엔드포인트가 적절히 구현되어 있으며, 날짜 범위 파라미터 처리가 올바릅니다.
Projects/Domain/Sources/Entity/SubRoutineEntity.swift (1)
8-37: LGTM!서브루틴 도메인 엔티티가 잘 설계되어 있습니다.
Decodable프로토콜 준수와 불변성 유지, 명확한 초기화 패턴이 적절히 구현되어 있습니다.RoutineEntity와 일관된 구조를 가지고 있어 좋습니다.Projects/DataSource/Sources/DTO/RoutineResponseDTO.swift (1)
8-41: LGTM!DTO 구조가 적절히 정의되었고, 도메인 엔티티로의 변환 메서드도 깔끔하게 구현되었습니다.
| func fetchRoutines(from startDate: String, to endDate: String) async throws -> [String: [RoutineEntity]] { | ||
| let endpoint = RoutineEndpoint.fetchRoutines(startDate: startDate, endDate: endDate) | ||
| guard let response = try await networkService.request(endpoint: endpoint, type: RoutineDictionaryDTO.self) | ||
| else { return [:] } | ||
|
|
||
| var result: [String: [RoutineEntity]] = [:] | ||
| for (date, routineDTO) in response.routines { | ||
| result[date] = routineDTO.compactMap({ $0.toRoutineEntity() }) | ||
| } | ||
| return result | ||
| } |
There was a problem hiding this comment.
네트워크 응답 실패 시 에러 처리 개선 필요
UserDataRepository와 동일한 패턴으로, 네트워크 응답이 nil일 때 빈 딕셔너리를 반환하고 있습니다. 메서드가 async throws로 선언되어 있으므로 네트워크 실패 시 명시적으로 에러를 던지는 것이 더 적절합니다.
func fetchRoutines(from startDate: String, to endDate: String) async throws -> [String: [RoutineEntity]] {
let endpoint = RoutineEndpoint.fetchRoutines(startDate: startDate, endDate: endDate)
- guard let response = try await networkService.request(endpoint: endpoint, type: RoutineDictionaryDTO.self)
- else { return [:] }
+ guard let response = try await networkService.request(endpoint: endpoint, type: RoutineDictionaryDTO.self)
+ else {
+ throw NetworkError.noData
+ }
var result: [String: [RoutineEntity]] = [:]
for (date, routineDTO) in response.routines {
result[date] = routineDTO.compactMap({ $0.toRoutineEntity() })
}
return result
}일관된 에러 처리 패턴을 위해 실패 시 적절한 에러를 던져주세요.
📝 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.
| func fetchRoutines(from startDate: String, to endDate: String) async throws -> [String: [RoutineEntity]] { | |
| let endpoint = RoutineEndpoint.fetchRoutines(startDate: startDate, endDate: endDate) | |
| guard let response = try await networkService.request(endpoint: endpoint, type: RoutineDictionaryDTO.self) | |
| else { return [:] } | |
| var result: [String: [RoutineEntity]] = [:] | |
| for (date, routineDTO) in response.routines { | |
| result[date] = routineDTO.compactMap({ $0.toRoutineEntity() }) | |
| } | |
| return result | |
| } | |
| func fetchRoutines(from startDate: String, to endDate: String) async throws -> [String: [RoutineEntity]] { | |
| let endpoint = RoutineEndpoint.fetchRoutines(startDate: startDate, endDate: endDate) | |
| guard let response = try await networkService.request( | |
| endpoint: endpoint, | |
| type: RoutineDictionaryDTO.self | |
| ) else { | |
| throw NetworkError.noData | |
| } | |
| var result: [String: [RoutineEntity]] = [:] | |
| for (date, routineDTO) in response.routines { | |
| result[date] = routineDTO.compactMap { $0.toRoutineEntity() } | |
| } | |
| return result | |
| } |
🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/RoutineRepository.swift around lines
13 to 23, the fetchRoutines method currently returns an empty dictionary when
the network response is nil instead of throwing an error. Since the method is
declared as async throws, modify the code to throw a specific error when the
networkService.request call returns nil, ensuring consistent error handling with
UserDataRepository and making failure cases explicit.
| func loadNickname() async throws -> String { | ||
| let endpoint = UserEndpoint.loadNickname | ||
| guard let user = try await networkService.request(endpoint: endpoint, type: UserDataResponseDTO.self) | ||
| else { return "" } | ||
|
|
||
| return nickname | ||
| return user.nickname | ||
| } |
There was a problem hiding this comment.
네트워크 실패 시 에러 처리 방식 개선 필요
현재 구현에서 네트워크 요청이 실패하거나 nil을 반환할 때 빈 문자열을 반환하고 있습니다. 하지만 메서드 시그니처가 async throws로 선언되어 있어, 호출자는 실패 시 예외가 발생할 것으로 예상합니다.
func loadNickname() async throws -> String {
let endpoint = UserEndpoint.loadNickname
- guard let user = try await networkService.request(endpoint: endpoint, type: UserDataResponseDTO.self)
- else { return "" }
+ guard let user = try await networkService.request(endpoint: endpoint, type: UserDataResponseDTO.self)
+ else {
+ throw NetworkError.noData // 또는 적절한 에러 타입
+ }
return user.nickname
}네트워크 실패를 숨기는 대신 명시적으로 에러를 던져 호출자가 적절히 처리할 수 있도록 해주세요.
📝 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.
| func loadNickname() async throws -> String { | |
| let endpoint = UserEndpoint.loadNickname | |
| guard let user = try await networkService.request(endpoint: endpoint, type: UserDataResponseDTO.self) | |
| else { return "" } | |
| return nickname | |
| return user.nickname | |
| } | |
| func loadNickname() async throws -> String { | |
| let endpoint = UserEndpoint.loadNickname | |
| guard let user = try await networkService.request(endpoint: endpoint, type: UserDataResponseDTO.self) | |
| else { | |
| throw NetworkError.noData // 또는 적절한 에러 타입 | |
| } | |
| return user.nickname | |
| } |
🤖 Prompt for AI Agents
In Projects/DataSource/Sources/Repository/UserDataRepository.swift lines 17 to
23, the loadNickname() method currently returns an empty string when the network
request fails or returns nil, which hides errors despite the method being async
throws. Modify the method to throw an explicit error instead of returning an
empty string when the network request fails or the user data is nil, so that
callers can properly handle the failure.
| private func updateSubRoutineView() { | ||
| for i in 0..<routine.subRoutines.count { | ||
| if i == 0 { | ||
| subRoutineTitleLabel.text = routine.subRoutines[i].title | ||
| } else { | ||
| let addedSubRoutineLabel = UILabel() | ||
| addedSubRoutineLabel.text = routine.subRoutines[i].title | ||
| addedSubRoutineLabel.font = BitnagilFont(style: .body2, weight: .semiBold).font | ||
| addedSubRoutineLabel.textColor = BitnagilColor.gray10 | ||
| addedSubRoutineLabel.textAlignment = .right | ||
| subRoutineStackView.addArrangedSubview(addedSubRoutineLabel) | ||
| addedSubRoutineLabel.snp.makeConstraints { make in | ||
| make.horizontalEdges.equalToSuperview() | ||
| make.height.equalTo(Layout.subRoutineLabelHeight) | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
서브루틴이 없는 경우 처리 로직을 추가하세요.
현재 코드는 서브루틴이 있을 때만 라벨을 업데이트하고 있습니다. 서브루틴이 없을 때 기본값 "세부 루틴 없음"이 표시되도록 처리가 필요합니다.
private func updateSubRoutineView() {
+ if routine.subRoutines.isEmpty {
+ subRoutineTitleLabel.text = "세부 루틴 없음"
+ return
+ }
+
for i in 0..<routine.subRoutines.count {
if i == 0 {
subRoutineTitleLabel.text = routine.subRoutines[i].title📝 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.
| private func updateSubRoutineView() { | |
| for i in 0..<routine.subRoutines.count { | |
| if i == 0 { | |
| subRoutineTitleLabel.text = routine.subRoutines[i].title | |
| } else { | |
| let addedSubRoutineLabel = UILabel() | |
| addedSubRoutineLabel.text = routine.subRoutines[i].title | |
| addedSubRoutineLabel.font = BitnagilFont(style: .body2, weight: .semiBold).font | |
| addedSubRoutineLabel.textColor = BitnagilColor.gray10 | |
| addedSubRoutineLabel.textAlignment = .right | |
| subRoutineStackView.addArrangedSubview(addedSubRoutineLabel) | |
| addedSubRoutineLabel.snp.makeConstraints { make in | |
| make.horizontalEdges.equalToSuperview() | |
| make.height.equalTo(Layout.subRoutineLabelHeight) | |
| } | |
| } | |
| } | |
| } | |
| private func updateSubRoutineView() { | |
| if routine.subRoutines.isEmpty { | |
| subRoutineTitleLabel.text = "세부 루틴 없음" | |
| return | |
| } | |
| for i in 0..<routine.subRoutines.count { | |
| if i == 0 { | |
| subRoutineTitleLabel.text = routine.subRoutines[i].title | |
| } else { | |
| let addedSubRoutineLabel = UILabel() | |
| addedSubRoutineLabel.text = routine.subRoutines[i].title | |
| addedSubRoutineLabel.font = BitnagilFont(style: .body2, weight: .semiBold).font | |
| addedSubRoutineLabel.textColor = BitnagilColor.gray10 | |
| addedSubRoutineLabel.textAlignment = .right | |
| subRoutineStackView.addArrangedSubview(addedSubRoutineLabel) | |
| addedSubRoutineLabel.snp.makeConstraints { make in | |
| make.horizontalEdges.equalToSuperview() | |
| make.height.equalTo(Layout.subRoutineLabelHeight) | |
| } | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Home/View/RoutineDetailView.swift around lines
317 to 334, the updateSubRoutineView function does not handle the case when
routine.subRoutines is empty. Add a check at the start of the function to see if
subRoutines is empty, and if so, set subRoutineTitleLabel.text to "세부 루틴 없음" and
clear any arranged subviews from subRoutineStackView to properly reflect the
absence of subroutines.
| } catch { | ||
|
|
||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 처리 로직을 추가하세요.
현재 catch 블록이 비어있어 에러 발생 시 원인 파악이 어렵습니다. 최소한 에러 로깅을 추가하는 것이 좋습니다.
} catch {
-
+ print("Failed to load nickname: \(error)")
}fetchRoutines 메서드에서도 동일합니다:
} catch {
-
+ print("Failed to fetch routines: \(error)")
+ fetchRoutineResultSubject.send(false)
}Also applies to: 90-92
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift at lines
64-66 and 90-92, the catch blocks are empty, which prevents error diagnosis when
exceptions occur. Add error handling by logging the caught error using an
appropriate logging mechanism or print statement to record the error details for
debugging purposes.
| guard let dailyRoutines = routines[dateKey] else { | ||
| routinesSubject.send([]) | ||
| fetchRoutines() | ||
| return | ||
| } |
There was a problem hiding this comment.
재귀 호출로 인한 잠재적 문제가 있습니다.
routines[dateKey]가 nil일 때 fetchRoutines()를 호출하면 무한 재귀가 발생할 수 있습니다. 또한 빈 배열과 nil을 구분하여 처리해야 합니다.
let dateKey = date.convertToString(dateType: .yearMonthDate)
- guard let dailyRoutines = routines[dateKey] else {
- fetchRoutines()
- return
- }
- routinesSubject.send(dailyRoutines)
+ if let dailyRoutines = routines[dateKey] {
+ routinesSubject.send(dailyRoutines)
+ } else {
+ // 빈 배열로 초기화하여 무한 재귀 방지
+ routinesSubject.send([])
+ // 필요시 해당 날짜만 다시 fetch
+ fetchRoutines()
+ }🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift around lines
106 to 109, the current code calls fetchRoutines() recursively when
routines[dateKey] is nil, which can cause infinite recursion. To fix this, avoid
calling fetchRoutines() directly inside this guard clause. Instead, handle the
nil case by distinguishing between an empty array and nil, possibly by returning
an empty array or triggering fetchRoutines() outside this guard without
recursion.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (5)
Projects/Presentation/Sources/Home/View/RoutineDetailView.swift (1)
322-339: 서브루틴이 없는 경우 처리 로직을 추가하세요.이전 리뷰 코멘트에서 지적된 것처럼, 현재 코드는 서브루틴이 있을 때만 라벨을 업데이트하고 있습니다. 서브루틴이 없을 때 기본값 "세부 루틴 없음"이 표시되도록 처리가 필요합니다.
private func updateSubRoutineView() { + if routine.subRoutines.isEmpty { + subRoutineTitleLabel.text = "세부 루틴 없음" + return + } + for i in 0..<routine.subRoutines.count { if i == 0 { subRoutineTitleLabel.text = routine.subRoutines[i].titleProjects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (3)
21-21: 오타가 수정되었습니다.이전 리뷰에서 지적된
fetchRoutineReulstPublisher오타가fetchRoutineResultPublisher로 올바르게 수정되었습니다.Also applies to: 28-28, 41-41
64-66: 에러 처리 로직을 추가하세요.이전 리뷰에서 지적된 것처럼 현재 catch 블록이 비어있어 에러 발생 시 원인 파악이 어렵습니다. 최소한 에러 로깅을 추가하는 것이 좋습니다.
} catch { - + print("Failed to load nickname: \(error)") }fetchRoutines 메서드에서도 동일합니다:
} catch { - + print("Failed to fetch routines: \(error)") + fetchRoutineResultSubject.send(false) }Also applies to: 90-92
106-109: 재귀 호출 문제가 여전히 존재합니다.이전 리뷰에서 지적된 무한 재귀 호출 문제가 해결되지 않았습니다.
routines[dateKey]가 nil일 때fetchRoutines()를 호출하면 무한 재귀가 발생할 수 있습니다.let dateKey = date.convertToString(dateType: .yearMonthDate) - guard let dailyRoutines = routines[dateKey] else { - fetchRoutines() - return - } - routinesSubject.send(dailyRoutines) + if let dailyRoutines = routines[dateKey] { + routinesSubject.send(dailyRoutines) + } else { + // 빈 배열로 초기화하여 무한 재귀 방지 + routinesSubject.send([]) + // 필요시 해당 날짜만 다시 fetch + fetchRoutines() + }Projects/Presentation/Sources/Home/View/HomeView.swift (1)
303-310: 오타가 수정되었습니다.이전 리뷰에서 지적된
fetchRoutineReulstPublisher오타가fetchRoutineResultPublisher로 올바르게 수정되었습니다.
🧹 Nitpick comments (2)
Projects/Presentation/Sources/Home/View/HomeView.swift (2)
142-149: 루틴 생성 뷰 네비게이션 로직이 중복됩니다.EmptyView에서 루틴 생성으로 이동하는 로직이 나중에 FloatingMenu에서도 동일하게 반복됩니다. 공통 메서드로 추출하는 것을 고려해보세요.
private func navigateToRoutineCreation() { guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self) else { fatalError("routineCreationViewModel 의존성이 등록되지 않았습니다.") } let routineCreationView = RoutineCreationView(viewModel: routineCreationViewModel) routineCreationView.hidesBottomBarWhenPushed = true self.navigationController?.pushViewController(routineCreationView, animated: true) }그리고 중복되는 부분들을 이 메서드로 대체하세요.
514-516: TODO 항목을 해결하세요.정적 분석 도구에서 지적한 것처럼 루틴 삭제 기능이 TODO로 남아있습니다. 기능 완성을 위해 구현이 필요합니다.
루틴 삭제 기능 구현을 도와드릴까요? HomeViewModel에 삭제 액션을 추가하고 도메인 계층과 연동하는 코드를 생성해드릴 수 있습니다.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Projects/Presentation/Sources/Home/View/HomeView.swift(14 hunks)Projects/Presentation/Sources/Home/View/RoutineDetailView.swift(1 hunks)Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift(3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
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/Home/View/RoutineDetailView.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Applied to files:
Projects/Presentation/Sources/Home/View/RoutineDetailView.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Home/View/HomeView.swift
[Warning] 515-515: TODOs should be resolved (루틴 삭제)
(todo)
🔇 Additional comments (9)
Projects/Presentation/Sources/Home/View/RoutineDetailView.swift (5)
12-15: 프로토콜 정의가 명확합니다.
RoutineDetailViewDelegate프로토콜이 루틴 편집과 삭제 기능을 위한 명확한 인터페이스를 제공합니다. 델리게이트 패턴을 통해 뷰와 상위 컨트롤러 간의 책임이 잘 분리되어 있습니다.
124-127: 기본값 설정이 적절합니다.서브루틴이 없을 때 "세부 루틴 없음" 기본값을 설정한 것이 좋습니다. 이는 사용자에게 명확한 상태를 전달합니다.
313-319: 서브루틴 업데이트 조건문이 적절합니다.서브루틴이 비어있지 않을 때만
updateSubRoutineView()를 호출하도록 조건을 설정한 것이 좋습니다. 이는 불필요한 UI 업데이트를 방지합니다.
341-343: 반복 루틴 표시 로직이 효율적입니다.
koreanValue를 사용하여 요일을 한국어로 표시하고 쉼표로 구분하는 로직이 사용자 친화적입니다.
81-85: RoutineDetailView는 BaseViewController를 상속하지 않습니다.
현재RoutineDetailView는UIViewController를 직접 상속하고 있어, BaseViewController의viewDidLoad()호출 패턴이 적용되지 않습니다. 따라서bind()가 정의되어 있지 않은 것도 정상이며, 추가 조치는 필요치 않습니다.Likely an incorrect or invalid review comment.
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
36-38: 의존성 주입 패턴이 잘 구현되었습니다.도메인 계층의 UseCase 프로토콜들을 주입받아 관심사 분리와 테스트 가능성을 향상시켰습니다. 이는 클린 아키텍처 원칙에 부합합니다.
Projects/Presentation/Sources/Home/View/HomeView.swift (3)
69-75: 새로운 UI 컴포넌트들이 잘 구성되었습니다.플로팅 버튼, 메뉴, 툴팁, 딤드 뷰 등 새로운 UI 컴포넌트들이 체계적으로 선언되어 있습니다. 각 컴포넌트의 역할이 명확하게 구분되어 있어 좋습니다.
453-460: CustomBottomSheet 관리 방식이 효과적입니다.PR 목표에서 언급된 대로
presentCustomBottomSheet확장 메서드 대신 직접CustomBottomSheet인스턴스를 생성하고 관리하는 방식이 프로그래매틱 제어를 가능하게 합니다. 이는 편집/삭제 시 bottom sheet를 적절히 해제할 수 있게 해줍니다.
477-483: 주간 뷰 델리게이트 연동이 적절합니다.주간 이동과 날짜 선택 시
fetchDailyRoutines액션을 호출하여 해당 날짜의 루틴을 가져오는 로직이 잘 구현되어 있습니다. 사용자 인터랙션과 데이터 업데이트가 원활하게 연결됩니다.
| if date <= oldestDate { | ||
| oldestDate = calendar.date(byAdding: .weekOfYear, value: -1, to: date) ?? date | ||
| latestDate = calendar.date(byAdding: .day, value: -1, to: date) ?? date | ||
| } else if date >= latestDate { | ||
| oldestDate = calendar.date(byAdding: .day, value: 1, to: date) ?? date | ||
| latestDate = calendar.date(byAdding: .weekOfYear, value: 1, to: date) ?? date | ||
| } |
There was a problem hiding this comment.
날짜 범위 확장 로직에 논리적 오류가 있습니다.
날짜 범위 확장 로직에서 oldestDate와 latestDate 업데이트가 잘못되었습니다. 과거 날짜를 요청할 때는 oldestDate만 확장하고 latestDate는 유지해야 하며, 미래 날짜를 요청할 때는 latestDate만 확장해야 합니다.
private func fetchRoutines(for date: Date) {
if date <= oldestDate {
oldestDate = calendar.date(byAdding: .weekOfYear, value: -1, to: date) ?? date
- latestDate = calendar.date(byAdding: .day, value: -1, to: date) ?? date
} else if date >= latestDate {
- oldestDate = calendar.date(byAdding: .day, value: 1, to: date) ?? date
latestDate = calendar.date(byAdding: .weekOfYear, value: 1, to: date) ?? date
}📝 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.
| if date <= oldestDate { | |
| oldestDate = calendar.date(byAdding: .weekOfYear, value: -1, to: date) ?? date | |
| latestDate = calendar.date(byAdding: .day, value: -1, to: date) ?? date | |
| } else if date >= latestDate { | |
| oldestDate = calendar.date(byAdding: .day, value: 1, to: date) ?? date | |
| latestDate = calendar.date(byAdding: .weekOfYear, value: 1, to: date) ?? date | |
| } | |
| private func fetchRoutines(for date: Date) { | |
| if date <= oldestDate { | |
| // only expand the lower bound when requesting past dates | |
| oldestDate = calendar.date(byAdding: .weekOfYear, value: -1, to: date) ?? date | |
| } else if date >= latestDate { | |
| // only expand the upper bound when requesting future dates | |
| latestDate = calendar.date(byAdding: .weekOfYear, value: 1, to: date) ?? date | |
| } | |
| // …rest of implementation… | |
| } |
🤖 Prompt for AI Agents
In Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift around lines
97 to 103, the logic for expanding the date range incorrectly updates both
oldestDate and latestDate regardless of whether the date is in the past or
future. To fix this, update only oldestDate when the date is earlier than or
equal to oldestDate, and update only latestDate when the date is later than or
equal to latestDate, leaving the other date unchanged in each case.
There was a problem hiding this comment.
흠 ...
과거 날짜를 요청할 때에는 과거의 1주 전 ~ 현재 하루 전까지 요청하고,
미래 날짜를 요청할 때에는 현재 하루 뒤 ~ 현재 1주까지 요청하는거니까 두 경우 모두 oldestDate, latestDate를 둘 다 변경해줘야 하는게 맞지 않아 ??
There was a problem hiding this comment.
@choijungp 맞습니다! 제가 로직을 잘못 이해했네요. 죄송합니다.
다시 살펴보니 현재 구현이 올바른 것 같습니다:
- 과거 날짜 요청 시:
date로부터 1주 전 ~ 1일 전까지의 새로운 데이터 범위를 설정 - 미래 날짜 요청 시:
date로부터 1일 후 ~ 1주 후까지의 새로운 데이터 범위를 설정
이는 기존 캐시된 범위를 확장하는 게 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위한 로직이군요.
현재 구현이 의도한 대로 동작하는 것 같습니다. 제가 처음에 잘못 분석했습니다.
✏️ Learnings added
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.642Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: 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 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 온보딩 선택지를 변경한 후 돌아올 때 새로운 선택지로 다시 등록해야 하기 때문. 홈 뷰에서는 이 뷰로 돌아올 수 없어서 중복 호출 문제가 발생하지 않음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
75-83: 에러 처리 로직 개선이 필요합니다.현재 catch 블록이 비어있어 에러 발생 시 사용자에게 피드백을 제공할 수 없습니다.
이전 리뷰 코멘트와 동일한 이슈입니다. 사용자가 CustomAlertView를 통한 에러 처리 개선을 계획하고 있다는 점을 고려하여, 임시적으로라도 로깅 추가를 권장합니다.
Projects/Presentation/Sources/Home/View/HomeView.swift (1)
447-460: 탭 제스처 처리에 좌표계 문제가 있습니다.이전 리뷰에서 지적된 것과 동일한 문제입니다.
location은contentView기준 좌표인데informationButton.frame은 다른 좌표계를 사용합니다.-let location = gesture.location(in: contentView) +let location = gesture.location(in: view)
🧹 Nitpick comments (5)
Projects/DataSource/Sources/Repository/EmotionRepository.swift (1)
25-25: 오타 수정이 필요합니다.에러 메시지에서 "Reponse"를 "Response"로 수정해주세요.
- else { throw NetworkError.unknown(description: "Emotion Reponse를 받아오지 못했습니다.") } + else { throw NetworkError.unknown(description: "Emotion Response를 받아오지 못했습니다.") }Projects/DataSource/Sources/DTO/EmotionResponseDTO.swift (1)
24-35: URL 초기화 실패 케이스를 고려해보세요.현재 구현은
type,name,imageUrl이 모두 non-nil인지만 확인하지만,URL(string:)초기화는 여전히 실패할 수 있습니다.func toEmotionEntity() -> EmotionEntity? { guard let type, let name, - let imageUrl + let imageUrl, + let url = URL(string: imageUrl) else { return nil } return EmotionEntity( emotionType: type, emotionName: name, - emotionImageUrl: URL(string: imageUrl)) + emotionImageUrl: url) }Projects/Presentation/Sources/Home/View/HomeView.swift (2)
371-382: 감정 구슬 업데이트 로직이 안전하게 구현되었습니다.옵셔널 바인딩과 Kingfisher를 활용한 이미지 로딩, 기본 이미지 처리가 적절합니다.
TODO 주석을 해결해주세요:
- // TODO: 토스트뷰 보여주기 + // 감정 등록 완료 시 토스트 메시지 표시 예정
537-539: TODO 주석을 구체적으로 개선해주세요.정적 분석 도구에서 지적한 TODO 주석을 해결하거나 더 구체적으로 작성해주세요.
- // TODO: 루틴 삭제 + // 루틴 삭제 API 연동 및 UI 업데이트 예정Projects/Presentation/Sources/Login/View/IntroView.swift (1)
17-17: 사용되지 않는 상수가 추가되었습니다.
labelHeight상수가 정의되었지만 실제 제약 조건에서는 사용되지 않고 있습니다 (라인 71에서graphViewWidth사용). 사용되지 않는 상수는 제거하거나 의도된 용도로 사용해야 합니다.- static let labelHeight: CGFloat = 60
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
Projects/App/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/default_emotion_graphic.imageset/default_emotion_graphic.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/default_emotion_graphic.imageset/default_emotion_graphic@2x.pngis excluded by!**/*.pngProjects/Presentation/Resources/Images.xcassets/default_emotion_graphic.imageset/default_emotion_graphic@3x.pngis excluded by!**/*.png
📒 Files selected for processing (16)
Projects/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json(1 hunks)Projects/DataSource/Sources/DTO/EmotionResponseDTO.swift(2 hunks)Projects/DataSource/Sources/Endpoint/EmotionEndpoint.swift(3 hunks)Projects/DataSource/Sources/Repository/EmotionRepository.swift(1 hunks)Projects/Domain/Sources/Protocol/Repository/EmotionRepositoryProtocol.swift(1 hunks)Projects/Domain/Sources/Protocol/UseCase/EmotionUseCaseProtocol.swift(1 hunks)Projects/Domain/Sources/UseCase/Emotion/EmotionUseCase.swift(2 hunks)Projects/Presentation/Resources/Images.xcassets/default_emotion_graphic.imageset/Contents.json(1 hunks)Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift(1 hunks)Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift(1 hunks)Projects/Presentation/Sources/Home/View/Component/RoutineView.swift(4 hunks)Projects/Presentation/Sources/Home/View/Component/SubRoutineButton.swift(2 hunks)Projects/Presentation/Sources/Home/View/HomeView.swift(19 hunks)Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift(2 hunks)Projects/Presentation/Sources/Login/View/IntroView.swift(2 hunks)Projects/Presentation/Sources/Login/View/LoginView.swift(3 hunks)
✅ Files skipped from review due to trivial changes (3)
- Projects/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
- Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift
- Projects/Presentation/Resources/Images.xcassets/default_emotion_graphic.imageset/Contents.json
🚧 Files skipped from review as they are similar to previous changes (2)
- Projects/Presentation/Sources/Home/View/Component/RoutineView.swift
- Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.642Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Applied to files:
Projects/Presentation/Sources/Login/View/LoginView.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
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/Login/View/LoginView.swiftProjects/Presentation/Sources/Home/ViewModel/HomeViewModel.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: choijungp의 homeviewmodel에서 fetchroutines(for date: date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.642Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.
Applied to files:
Projects/DataSource/Sources/Repository/EmotionRepository.swiftProjects/Presentation/Sources/Home/ViewModel/HomeViewModel.swiftProjects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: choijungp는 onboardingusecase의 converttodictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음....
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
Applied to files:
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift
📚 Learning: choijungp는 추후 customalertview를 만들어서 에러 처리를 ui로 해결하도록 수정할 계획을 가지고 있음....
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Applied to files:
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift
📚 Learning: choijungp는 현재 테스트 단계에서 refreshtokenplugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함....
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Applied to files:
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift
🧬 Code Graph Analysis (1)
Projects/DataSource/Sources/Endpoint/EmotionEndpoint.swift (3)
Projects/DataSource/Sources/Repository/EmotionRepository.swift (1)
fetchEmotion(22-29)Projects/Domain/Sources/UseCase/Emotion/EmotionUseCase.swift (1)
fetchEmotion(23-28)Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (1)
fetchEmotion(127-137)
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Home/View/HomeView.swift
[Warning] 381-381: TODOs should be resolved (토스트뷰 보여주기)
(todo)
[Warning] 538-538: TODOs should be resolved (루틴 삭제)
(todo)
🔇 Additional comments (31)
Projects/Presentation/Sources/Home/View/Component/SubRoutineButton.swift (2)
8-8: SnapKit 임포트 추가 승인레이아웃 제약 조건 관리를 위한 SnapKit 라이브러리 임포트가 적절합니다. 다른 Home 모듈의 UI 업데이트와 일관성을 유지하고 있습니다.
61-61: trailing 제약 조건 추가로 레이아웃 개선
routineLabel에 trailing 제약 조건을 추가하여 적절한 수평 정렬을 보장하는 것이 좋습니다. 이는 레이블이 버튼의 전체 너비를 활용할 수 있도록 하여 더 나은 UI 레이아웃을 제공합니다.Projects/Domain/Sources/Protocol/Repository/EmotionRepositoryProtocol.swift (1)
14-17: 새로운 메서드 추가가 깔끔합니다.날짜별 감정 조회 메서드가 기존 패턴과 일관성 있게 추가되었습니다. 문서화도 잘 되어있고 날짜 형식까지 명시되어 좋습니다.
Projects/Domain/Sources/Protocol/UseCase/EmotionUseCaseProtocol.swift (2)
8-8: Foundation import가 적절합니다.Date 타입 사용을 위한 Foundation import가 추가되었습니다.
15-18: 도메인 계층에서 Date 타입 사용이 좋은 추상화입니다.Use Case에서는 Date 타입을, Repository에서는 String 타입을 사용하여 각 계층의 책임이 잘 분리되었습니다.
Projects/Domain/Sources/UseCase/Emotion/EmotionUseCase.swift (2)
8-9: 필요한 import 문이 적절히 추가되었습니다.Foundation과 Shared 모듈 import가 Date 타입과 날짜 변환 유틸리티 사용을 위해 추가되었습니다.
23-28: 날짜 변환과 Repository 위임이 깔끔합니다.Date를 문자열로 변환하여 Repository에 위임하는 구조가 명확하고, 기존 패턴과 일관성이 있습니다.
Projects/DataSource/Sources/Endpoint/EmotionEndpoint.swift (3)
10-10: 새로운 엔드포인트 케이스가 적절합니다.날짜별 감정 조회를 위한 새로운 enum case가 추가되었습니다.
20-27: URL 경로 구성이 RESTful 패턴을 잘 따릅니다.
/api/v1/emotion-marbles/{date}형태의 URL 패턴이 REST API 규칙에 맞게 구성되었습니다.
33-33: GET 메서드와 빈 body 파라미터가 적절합니다.조회 API에 맞게 GET 메서드를 사용하고 body 파라미터를 비워둔 것이 올바릅니다.
Also applies to: 54-55
Projects/DataSource/Sources/Repository/EmotionRepository.swift (2)
22-29: 에러 처리 일관성을 고려해보세요.
fetchEmotions()는 응답이 nil일 때 빈 배열을 반환하지만,fetchEmotion()은 예외를 던집니다. 이는 의도된 동작인지 확인이 필요합니다. 단일 감정 조회 실패 시 예외를 던지는 것이 비즈니스 로직에 맞다면 현재 구현이 적절합니다.
22-29: 네트워크 요청 구현이 올바릅니다.새로운 엔드포인트를 사용한 네트워크 요청과 DTO 변환 로직이 기존 패턴과 일관성 있게 구현되었습니다.
Projects/DataSource/Sources/DTO/EmotionResponseDTO.swift (1)
12-14: 옵셔널 처리가 적절하게 적용되었습니다.서버 응답에서 감정 데이터가 부분적으로 누락될 수 있는 상황을 고려한 좋은 변경사항입니다. 옵셔널 타입으로 변경하여 런타임 안정성을 높였습니다.
Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift (3)
42-49: 의존성 주입 구조가 잘 설계되었습니다.도메인 계층의 유즈케이스들을 적절히 주입받아 관심사를 분리했습니다. 테스트 가능성과 유지보수성이 향상되었습니다.
97-107: 비동기 루틴 데이터 페칭 로직이 잘 구현되었습니다.도메인 유즈케이스를 통한 데이터 페칭과 딕셔너리 기반 캐싱 구조가 적절합니다. 성공 시
fetchRoutineResultSubject를 통해 UI 업데이트를 트리거하는 것도 좋은 접근입니다.다만 에러 처리 개선이 필요합니다:
} catch { - + print("Failed to fetch routines: \(error)") + fetchRoutineResultSubject.send(false) }
127-137: 감정 데이터 페칭 구현이 깔끔합니다.옵셔널 체이닝을 통해 안전하게 도메인 엔터티를 프레젠테이션 모델로 변환하는 로직이 잘 구현되었습니다.
Projects/Presentation/Sources/Home/View/HomeView.swift (6)
58-58: 이미지 로딩을 위한 UIImageView 변경이 적절합니다.감정 구슬 이미지를 동적으로 로딩하기 위해 UIView에서 UIImageView로 변경한 것이 좋은 결정입니다.
72-77: 플로팅 메뉴 관련 프로퍼티들이 잘 정의되었습니다.상태 관리를 위한 불리언 플래그와 UI 컴포넌트들의 구성이 적절합니다. CustomBottomSheet의 옵셔널 참조도 메모리 관리 측면에서 좋습니다.
115-122: 툴팁 토글 로직이 깔끔하게 구현되었습니다.버튼 선택 상태와 툴팁 표시를 연동하는 로직이 직관적이고 사용자 친화적입니다.
321-327: 감정 데이터 바인딩이 적절히 구현되었습니다.Combine 퍼블리셔를 통한 반응형 UI 업데이트가 잘 구현되었습니다.
426-441: 플로팅 메뉴 토글 애니메이션이 부드럽게 구현되었습니다.상태 관리와 페이드 애니메이션이 적절히 조합되어 좋은 사용자 경험을 제공합니다. 툴팁 자동 숨김 처리도 UX 측면에서 좋습니다.
476-483: 루틴 상세 뷰 표시 로직이 잘 구현되었습니다.서브루틴 개수에 따른 동적 높이 계산과 CustomBottomSheet 관리가 적절합니다. 프로그래매틱한 dismiss를 위한 bottomSheet 참조 저장도 좋은 접근입니다.
Projects/Presentation/Sources/Login/View/LoginView.swift (9)
18-23: 레이아웃 상수 추가가 적절합니다.새로운 UI 요소들을 위한 레이아웃 상수들이 기존 코드 스타일과 일관되게 추가되었고, 값들도 적절해 보입니다.
29-30: 프로퍼티 타입 변경이 적절합니다.
loginLabel추가와logoView의UIView에서UIImageView로의 타입 변경이 UI 개선 목적에 맞게 올바르게 구현되었습니다.
54-57: 환영 라벨 구성이 잘 구현되었습니다.라벨 텍스트, 폰트, 색상 설정이 앱의 디자인 시스템과 일관되게 적용되었습니다.
58-58: 로고 이미지 설정이 올바릅니다.
BitnagilGraphic.introGraphic에셋을 사용한 이미지 할당이 적절하게 구현되었습니다.
73-73: 레이아웃 구성이 잘 구현되었습니다.새로운
loginLabel과 기존logoView의 제약 조건들이 적절하게 설정되어 있으며, safe area를 고려한 레이아웃 구성이 iOS 모범 사례를 따르고 있습니다.Also applies to: 78-82, 84-89
18-23: 레이아웃 상수 정의가 잘 구성되어 있습니다.새로운 라벨과 로고 위치 조정을 위한 상수들이 명확하고 일관성 있게 정의되어 있습니다.
29-30: UI 컴포넌트 업그레이드가 적절합니다.
logoView를UIView에서UIImageView로 변경하고 환영 메시지를 위한loginLabel을 추가한 것이 사용자 경험 개선에 도움이 됩니다.
54-58: 라벨과 이미지 설정이 적절합니다.환영 메시지의 텍스트, 폰트, 색상 설정과 로고 이미지 설정이 디자인 시스템을 잘 활용하고 있습니다.
78-89: 레이아웃 제약 조건이 잘 구성되어 있습니다.새로운 라벨의 중앙 정렬과 로고의 위치 조정이 적절하게 설정되어 있어 UI가 균형잡힌 모습을 보일 것입니다.
taipaise
left a comment
There was a problem hiding this comment.
아하~! 제가 이해한 것이 맞다면, bottomSheet안에서 content로 표시될 VC가 bottomSheet의 child로 등록되기 때문에, childVC에서 dismiss를 해도 bottomSheet가 닫히지 않는거군요
그래서 dimmed 영역을 터치하는 방법으로만 닫을 수 있다~ 가 맞을까요?
확실히 구조를 조금 변경할 필요가 있을 것 같아요!! 커스텀 bottom sheet 진짜 잘 만드셨다 생각했는데 요런 부분은 예상하지 못했네요 ㅠ
추후 sheetPresentationController 이용해서 수정하면 좋을 것 같습니다! 그럼 지금처럼 bottomSheet에서 직접 dismiss하고 delegate 함수 하나만으로 데이터를 전달하면 될것 같아요!! (아마도..)
네이밍이나 구조 같은 것도 시간이 굉장히 굉장히 역대급 빡빡!! 한데 이정도면 훌륭하지 않나 싶어요.!.! 일단은 구현해서 돌아가는게 중요하니까요 🥹
안그래도 거의 대부분을 혼자 개발하고 쳐내고 계셔서 부담이 많이 되실텐데 정말 정말 나중에 하나씩 고쳐나가도 좋지 않을까 합니다!! (9월부터는 괜찮을거에요…)
특히 네이밍 부분은 일단은 서버 쪽 response와 똑같이 맞춰두면 coding key를 따로 설정해줄 필요도 없으니까 오히려 빠르게 DTO 객체 만들 수 있다 생각해서 좋습니다! (엄청 나쁜 네이밍도 아니라 생각하구요!)
오늘도 많이 많이 고생하셨습니다 🫡🫡🫡
|
|
||
| // 서브 루틴 | ||
| subRoutineInfoStackView.snp.makeConstraints { make in | ||
| make.height.equalTo(Layout.subRoutineInfoStackViewHeight) |
There was a problem hiding this comment.
혹시 이렇게 해도 stackView의 높이가 안에 subroutineLabel을 추가할 때마다 그에 맞게 유동적으로 조절될까요????
There was a problem hiding this comment.
핫 !!!! 그 화면이 StackView가 진짜 많은 녀석이라 .... 네이밍이 참 어려웠는데 ...
결론적으로 말씀드리면 subRoutineInfoStackView는 subroutineLabel이 추가되는 stackView가 아니고
세부 루틴 아이콘 + 세부 루틴 + 세부 루틴 첫번째 (혹은 없음) 이 있는 StackView입니다 !!!!
그래서 이 녀석은 추가될 일이 없어서 높이를 지정해놓긴 했습니다 !!!!
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
Projects/Presentation/Sources/Home/View/HomeView.swift (2)
371-382: 감정 구슬 업데이트 로직이 잘 구현되어 있습니다.Kingfisher를 사용한 비동기 이미지 로딩과 감정이 등록된 경우 버튼을 비활성화하는 로직이 적절합니다. 기본 이미지 처리도 올바르게 되어 있습니다.
TODO 주석에 대해 향후 토스트 뷰 구현 시 도움이 필요하시면 말씀해 주세요.
476-540: 델리게이트 구현이 일관성 있게 잘 되어 있습니다.루틴 상세 보기를 위한 바텀 시트 관리와 의존성 주입을 통한 화면 전환 패턴이 일관성 있게 적용되었습니다. 바텀 시트의 적절한 정리 처리도 좋습니다.
서버 통신과 루틴 삭제 기능 구현 시 도움이 필요하시면 말씀해 주세요.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Projects/Presentation/Sources/Home/View/HomeView.swift(19 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.687Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#30
File: Projects/DataSource/Sources/NetworkService/Plugin/RefreshTokenPlugin.swift:42-43
Timestamp: 2025-07-30T03:56:18.617Z
Learning: choijungp는 현재 테스트 단계에서 RefreshTokenPlugin의 토큰 갱신 로직 디버깅을 위해 액세스 토큰과 리프레시 토큰의 실제 값을 로그에 기록하는 것을 선호함.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
📚 Learning: choijungp의 homeviewmodel에서 fetchroutines(for date: date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 ...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#31
File: Projects/Presentation/Sources/Home/ViewModel/HomeViewModel.swift:97-103
Timestamp: 2025-08-01T06:38:18.687Z
Learning: choijungp의 HomeViewModel에서 fetchRoutines(for date: Date) 메서드는 기존 캐시 범위를 확장하는 것이 아니라, 요청된 날짜 주변의 새로운 데이터 청크를 가져오기 위해 oldestDate와 latestDate를 모두 업데이트한다. 과거 날짜 요청 시에는 해당 날짜의 1주 전~1일 전, 미래 날짜 요청 시에는 해당 날짜의 1일 후~1주 후 범위로 설정한다.
Applied to files:
Projects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: choijungp는 추후 customalertview를 만들어서 에러 처리를 ui로 해결하도록 수정할 계획을 가지고 있음....
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift:0-0
Timestamp: 2025-07-16T10:57:16.559Z
Learning: choijungp는 추후 CustomAlertView를 만들어서 에러 처리를 UI로 해결하도록 수정할 계획을 가지고 있음.
Applied to files:
Projects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: choijungp는 onboardingusecase의 converttodictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음....
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Domain/Sources/UseCase/Onboarding/OnboardingUseCase.swift:0-0
Timestamp: 2025-07-16T10:58:30.858Z
Learning: choijungp는 OnboardingUseCase의 convertToDictionary 함수에서 에러 처리 개선을 추후 고민하고 반영할 계획을 가지고 있음.
Applied to files:
Projects/Presentation/Sources/Home/View/HomeView.swift
📚 Learning: onboardingrecommendedroutineview에서 viewwillappear에 registeronboarding 호출하는 것이 적절한 이유: 사용자가 이전 페이지에서 ...
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/Home/View/HomeView.swift
📚 Learning: baseviewcontroller의 viewdidload() 메서드에서 이미 configureattribute(), configurelayout(), bind()를 호출하므로, 하...
Learnt from: choijungp
PR: YAPP-Github/Bitnagil-iOS#19
File: Projects/Presentation/Sources/Login/View/TermsAgreementView.swift:44-46
Timestamp: 2025-07-16T09:09:13.869Z
Learning: BaseViewController의 viewDidLoad() 메서드에서 이미 configureAttribute(), configureLayout(), bind()를 호출하므로, 하위 클래스에서 super.viewDidLoad()를 호출하면 이 메서드들이 자동으로 호출된다. 따라서 하위 클래스에서 추가로 호출할 필요가 없다.
Applied to files:
Projects/Presentation/Sources/Home/View/HomeView.swift
🪛 SwiftLint (0.57.0)
Projects/Presentation/Sources/Home/View/HomeView.swift
[Warning] 381-381: TODOs should be resolved (토스트뷰 보여주기)
(todo)
[Warning] 538-538: TODOs should be resolved (루틴 삭제)
(todo)
🔇 Additional comments (5)
Projects/Presentation/Sources/Home/View/HomeView.swift (5)
9-53: 레이아웃 상수와 임포트가 잘 구성되어 있습니다.Kingfisher 임포트와 새로운 UI 컴포넌트들을 위한 레이아웃 상수가 체계적으로 추가되었습니다. 명명 규칙이 일관성 있게 적용되어 있어 좋습니다.
58-101: UI 컴포넌트 선언과 생명주기 메서드가 적절합니다.
emotionOrbView를UIImageView로 변경한 것과 새로운 UI 컴포넌트들의 추가가 기능 요구사항에 맞게 잘 구현되었습니다.viewWillAppear에서 감정 데이터를 가져오는 것도 적절한 타이밍입니다.
115-186: UI 설정이 체계적으로 구현되어 있습니다.정보 버튼의 툴팁 토글 기능과 의존성 주입을 통한 화면 전환 로직이 잘 구현되었습니다. 제스처 처리와 딤드 뷰 설정도 적절합니다.
194-327: 레이아웃과 바인딩 구현이 올바릅니다.새로운 UI 컴포넌트들의 제약 조건 설정과 Combine을 통한 데이터 바인딩이 적절하게 구현되었습니다.
emotionOrbView의 정렬 수정도 UI 요구사항에 맞게 잘 적용되었습니다.
426-465: 플로팅 버튼과 제스처 처리 로직이 잘 구현되어 있습니다.애니메이션을 포함한 UI 상태 전환 로직과 좌표계를 올바르게 사용한 제스처 처리가 적절합니다.
hideTooltipView헬퍼 메서드로 코드 재사용성도 좋습니다.
🌁 Background
홈 화면에서 루틴 목록 조회 (기간 별), 루틴 상세 보기 페이지를 구현하였습니다.
다음 PR에서 루틴 완료, 삭제를 구현하면 .. 홈 진짜 끝 .................. 제발 🙏. (+ 감정 구슬)
📱 Screenshot
👩💻 Contents
📝 Review Note
1. 루틴 조회하는 방법에 대하여 .....
홈 화면에서 사용자가 등록한 루틴을 조회합니다. !!!
서버에서 루틴을 조회할 때 조회 시작 날짜 - 조회 종료 날짜를 기준으로 데이터를 받아올 수 있습니다.
그래서 어떻게 구현했냐면
뭔가 .. 더 개선할 수 있을 것 같긴한데 ........ 일단 ..빠르게 처리해봤습니다 ㅜㅜ
추가적으로 RoutineDTO, RoutineEntity에서 서버에서 불러오는 데이터 변수명을 하나도 안바꿨어요 ...................
또 이번 PR이 너무 길어질 것 같아서 (+ 약간 지쳤음 이슈... ) 로 냅뒀는데 .. 다음 PR에 꼭 리팩해서 올릴게요 ㅠㅠ
2. CustomBottomSheet의 dismiss
RoutineDetailView를 CustomBottomSheet로 보여주기 위해 기존에 UIViewController extension에 구현한 presentCustomBottomSheet로 보여주려고 했습니다.
다만 .. 이때 문제는 현재 뷰 (ex. HomeView)는 CustomBottomSheet 자체를 갖고 있는 것이 아니라 임의로 CustomBottomSheet를 내릴 수 없었습니다. (단지 CustomBottomSheet의 dimmedView를 터치해서 내리는 방법 밖에는 ... 없었슨 )
하지만 HomeView -> RoutineDetailView (CustomBottomSheet로 보여주기) -> 루틴 수정 혹은 삭제 버튼 tap
그러면 CustomBottomSheet로 보여준 RoutineDetailView를 내리고 다음 뷰로 이동해야 합니다. (수정 : 루틴 등록 뷰, 삭제 : 삭제 AlertView)
어떻게.. CustomBottomSheet를 dimmedView 터치가 아닌 HomeView에서 내려줄 수 있을까 를 생각해봤을 때
UIViewController+에 구현되어 있는
presentCustomBottomSheet함수로 CustomBottomSheet를 보여주는 것이 아닌직접 CustomBottomSheet를 생성하고 CustomBottomSheet 내부에 있는 dismiss 함수의 접근을 허용하여 내릴 수 있도록 하였습니다.
넘 구린 방법일까요 ??
이번 PR이 대체적으로 퀄리티가 낮아서 정말 죄송합니다 .......... 😭😭😭😭
📣 Related Issue
Summary by CodeRabbit
신규 기능
기능 개선
리팩터/정리
문서/리소스