[FEAT] 대시보드 UI 구성#18
Conversation
|
Warning Rate limit exceeded@Sangyoon98 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 25 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (3)
Walkthrough대시보드 UI를 ViewModel 기반으로 재구성하고, 사용자 프로필에 position/workspace/branch 필드를 추가했으며, CommonTextField에 제출 제어를 도입하고 주문 항목 컴포넌트 및 관련 대시보드 상태·이벤트 파일을 추가했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ContentView
participant DashboardView
participant DashboardViewModel
participant GetOrderUseCase
User->>ContentView: 앱 로드
ContentView->>DashboardView: DashboardView 표시 (NavigationStack)
DashboardView->>DashboardViewModel: init(getOrderUseCase)
DashboardViewModel->>GetOrderUseCase: execute()
GetOrderUseCase-->>DashboardViewModel: 주문 목록 반환
DashboardViewModel->>DashboardView: @Published uiState 변경
DashboardView->>User: 주문 목록 렌더링
User->>DashboardView: Pull-to-Refresh
DashboardView->>DashboardViewModel: onEvent(.loadDashboard)
DashboardViewModel->>GetOrderUseCase: execute()
DashboardViewModel-->>DashboardView: 업데이트된 uiState
DashboardView->>User: 화면 갱신
sequenceDiagram
participant User
participant AuthRepositoryImpl
participant AuthAPI
participant AuthPreferences
participant AuthMappers
User->>AuthRepositoryImpl: signIn(credentials)
AuthRepositoryImpl->>AuthAPI: login()
AuthAPI-->>AuthRepositoryImpl: LoginResponseDTO
AuthRepositoryImpl->>AuthPreferences: 토큰 저장 (access/refresh)
rect rgb(220,235,255)
note right of AuthRepositoryImpl: 프로필 조회 및 병합 흐름
AuthRepositoryImpl->>AuthAPI: getProfile()
AuthAPI-->>AuthRepositoryImpl: GetProfileResponseDTO
AuthRepositoryImpl->>AuthMappers: profile.toModel() / mergeWith(...)
AuthMappers-->>AuthRepositoryImpl: 병합된 User
end
AuthRepositoryImpl->>AuthPreferences: 병합된 User 저장
AuthRepositoryImpl-->>User: 완성된 User 반환
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
SampoomManagement/Features/Auth/UI/SignUpView.swift (1)
150-155: 마지막 필드가 적절하게 구현되었습니다.비밀번호 확인 필드에서
.done레이블을 사용하고 제출 시 키보드를 닫는 것은 적절한 UX 패턴입니다. 사용자가 명시적으로 회원가입 버튼을 눌러 제출하도록 하는 현재 디자인은 실수로 인한 제출을 방지하는 장점이 있습니다.선택적으로, 마지막 필드에서 자동으로 폼을 제출하려면 다음과 같이 수정할 수 있습니다:
- onSubmit: { focusedField = nil } + onSubmit: { + focusedField = nil + if viewModel.uiState.isValid { + viewModel.submit() + } + }다만 현재의 명시적 버튼 클릭 방식도 유효한 디자인 선택입니다.
SampoomManagement/Features/Auth/Domain/Models/User.swift (1)
18-20: 프로필 필드의 옵셔널 타입 사용 고려새로 추가된
position,workspace,branch필드가 non-optionalString으로 정의되었습니다. 하지만GetProfileResponseDTO에서는 이 필드들이 옵셔널이며,AuthPreferences에서는 누락된 경우 빈 문자열로 기본값 처리하고 있습니다."값이 설정되지 않음"과 "빈 문자열"을 구분해야 하는 경우, 이 필드들을
String?으로 변경하는 것을 고려해보세요. 현재 구현은 일관성 있게 빈 문자열 기본값 전략을 사용하고 있지만, 의미적으로 nil이 더 명확할 수 있습니다.SampoomManagement/Features/Dashboard/UI/DashboardViewModel.swift (1)
30-47: 매직 넘버를 상수로 추출하는 것을 고려해보세요.Line 36에서 최근 주문 목록을 5개로 제한하는데, 이 값을 상수로 추출하면 유지보수가 더 쉬워집니다.
다음과 같이 리팩토링할 수 있습니다:
@MainActor class DashboardViewModel: ObservableObject { @Published var uiState = DashboardUiState() private let getOrderUseCase: GetOrderUseCase + private let maxRecentOrders = 5 init(getOrderUseCase: GetOrderUseCase) { self.getOrderUseCase = getOrderUseCase loadOrderList() }그리고 사용 부분:
let orderList = try await getOrderUseCase.execute() uiState = uiState.copy( - orderList: Array(orderList.items.prefix(5)), + orderList: Array(orderList.items.prefix(maxRecentOrders)), dashboardLoading: false, dashboardError: nil )SampoomManagement/Features/Dashboard/UI/DashboardView.swift (2)
27-32: TODO 주석을 해결해야 합니다.역할 기반 직원 버튼 기능이 아직 구현되지 않았습니다.
이 TODO를 추적하기 위한 이슈를 생성하도록 도와드릴까요?
43-46: 임시 로그아웃 버튼을 적절한 위치로 이동하세요.로그아웃 버튼이 임시로 메인 컨텐츠 영역에 배치되어 있습니다. 일반적으로 설정 화면이나 프로필 메뉴로 이동하는 것이 좋습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
SampoomManagement/Resources/Assets.xcassets/money.imageset/money.svgis excluded by!**/*.svgSampoomManagement/Resources/Assets.xcassets/warning.imageset/warning.svgis excluded by!**/*.svg
📒 Files selected for processing (19)
SampoomManagement/App/ContentView.swift(1 hunks)SampoomManagement/App/Screens/DashboardScreen.swift(0 hunks)SampoomManagement/Core/Network/TokenRefreshService.swift(1 hunks)SampoomManagement/Core/UI/Components/CommonTextField.swift(4 hunks)SampoomManagement/Core/UI/Components/OrderItem.swift(1 hunks)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift(5 hunks)SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift(1 hunks)SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift(1 hunks)SampoomManagement/Features/Auth/Data/Remote/DTO/GetProfileResponseDTO.swift(1 hunks)SampoomManagement/Features/Auth/Data/Remote/DTO/LoginResponseDTO.swift(1 hunks)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift(2 hunks)SampoomManagement/Features/Auth/Domain/Models/User.swift(1 hunks)SampoomManagement/Features/Auth/UI/SignUpView.swift(7 hunks)SampoomManagement/Features/Dashboard/UI/DashboardUiEvent.swift(1 hunks)SampoomManagement/Features/Dashboard/UI/DashboardUiState.swift(1 hunks)SampoomManagement/Features/Dashboard/UI/DashboardView.swift(1 hunks)SampoomManagement/Features/Dashboard/UI/DashboardViewModel.swift(1 hunks)SampoomManagement/Resources/Assets.xcassets/money.imageset/Contents.json(1 hunks)SampoomManagement/Resources/Assets.xcassets/warning.imageset/Contents.json(1 hunks)
💤 Files with no reviewable changes (1)
- SampoomManagement/App/Screens/DashboardScreen.swift
🧰 Additional context used
🧬 Code graph analysis (9)
SampoomManagement/Core/UI/Components/OrderItem.swift (1)
SampoomManagement/Core/Utilities/OrderFormatter.swift (1)
buildOrderTitle(11-38)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (3)
SampoomManagement/Features/Auth/Data/Local/Preferences/KeychainManager.swift (3)
save(24-43)delete(73-85)get(45-71)SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-65)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(89-125)
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (1)
SampoomManagement/Core/Network/NetworkManager.swift (1)
request(21-59)
SampoomManagement/Features/Dashboard/UI/DashboardViewModel.swift (1)
SampoomManagement/Features/Dashboard/UI/DashboardUiState.swift (1)
copy(25-35)
SampoomManagement/Features/Dashboard/UI/DashboardView.swift (1)
SampoomManagement/Features/Dashboard/UI/DashboardViewModel.swift (1)
onEvent(23-28)
SampoomManagement/App/ContentView.swift (2)
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
signOut(77-87)SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1)
signOut(40-50)
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (4)
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (2)
login(19-33)getProfile(97-104)SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (3)
toModel(11-23)toModel(27-39)mergeWith(43-55)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (2)
saveToken(51-61)saveUser(25-49)SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-65)
SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (2)
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-65)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(89-125)
SampoomManagement/Features/Auth/UI/SignUpView.swift (1)
SampoomManagement/Features/Auth/UI/SignUpViewModel.swift (6)
updateName(23-26)updateBranch(29-32)updatePosition(35-38)updateEmail(41-44)updatePassword(47-53)updatePasswordCheck(56-59)
🪛 SwiftLint (0.57.0)
SampoomManagement/Features/Dashboard/UI/DashboardView.swift
[Warning] 27-27: TODOs should be resolved (role-based employee button)
(todo)
🔇 Additional comments (25)
SampoomManagement/Resources/Assets.xcassets/warning.imageset/Contents.json (1)
1-12: 검증 완료 - 이슈 없음SVG 파일(
warning.svg)이 올바른 위치에 존재하며, Contents.json에서 정확히 참조되고 있습니다. DashboardView.swift(line 94)에서 해당 자산이 대시보드 UI에 실제로 사용되고 있음이 확인되었습니다. 모든 구조와 참조가 정상입니다.SampoomManagement/Resources/Assets.xcassets/money.imageset/Contents.json (1)
1-12: 모든 확인 사항 검증 완료 - 코드 승인Contents.json의 JSON 구조가 정확하며, 참조하는 money.svg 파일이 올바른 위치에 존재합니다. DashboardView.swift의 line 95에서 이 asset이 주문 금액 표시 UI의 iconName으로 실제 사용되고 있음을 확인했습니다. 별도 조치가 필요하지 않습니다.
SampoomManagement/Core/UI/Components/CommonTextField.swift (3)
62-63: 제출 컨트롤 속성이 올바르게 추가되었습니다.키보드의 return 키 레이블과 제출 동작을 제어하는 새로운 속성들이 명확하게 정의되었습니다. 이는 SignUpView의 포커스 관리 흐름을 지원합니다.
72-74: 초기화 함수가 적절하게 확장되었습니다.새 파라미터들에 합리적인 기본값이 제공되어 기존 코드와의 호환성이 유지됩니다.
.next를 기본 submitLabel로 사용하는 것은 대부분의 폼 필드에 적합한 선택입니다.Also applies to: 83-84
94-95: 제출 수정자가 일관되게 적용되었습니다.SecureField와 TextField 모두에 동일한 submitLabel과 onSubmit 수정자가 적용되어, 비밀번호 필드와 일반 텍스트 필드가 통일된 제출 동작을 갖습니다.
Also applies to: 103-104
SampoomManagement/Features/Auth/UI/SignUpView.swift (6)
20-20: 포커스 관리 기반이 올바르게 구축되었습니다.FocusState와 Field enum을 사용한 키보드 포커스 관리 구조가 명확하게 정의되었습니다. 모든 입력 필드를 포함하며 체계적인 포커스 전환을 가능하게 합니다.
Also applies to: 26-28
52-57: 이름 필드의 포커스 흐름이 올바르게 구현되었습니다.첫 번째 필드로서 적절하게 구성되었으며, return 키 입력 시 지점 필드로 자연스럽게 전환됩니다. ViewModel과의 연동도 정확합니다.
71-76: 지점 필드가 포커스 체인에 올바르게 통합되었습니다.이전 필드(이름)로부터 포커스를 받고 다음 필드(직급)로 전환하는 흐름이 정확하게 구현되었습니다.
90-95: 직급 필드가 포커스 체인에 올바르게 통합되었습니다.지점 필드로부터 포커스를 받고 이메일 필드로 전환하는 흐름이 정확하게 구현되었습니다.
110-115: 이메일 필드가 포커스 체인에 올바르게 통합되었습니다.직급 필드로부터 포커스를 받고 비밀번호 필드로 전환하는 흐름이 정확하게 구현되었습니다.
130-135: 비밀번호 필드가 포커스 체인에 올바르게 통합되었습니다.이메일 필드로부터 포커스를 받고 비밀번호 확인 필드로 전환하는 흐름이 정확하게 구현되었습니다.
SampoomManagement/Features/Dashboard/UI/DashboardUiEvent.swift (1)
10-13: LGTM!대시보드 이벤트 타입이 명확하게 정의되어 있습니다.
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (1)
96-104: LGTM!프로필 조회 API가 기존 패턴을 따라 올바르게 구현되었습니다.
SampoomManagement/Features/Auth/Data/Remote/DTO/GetProfileResponseDTO.swift (1)
10-16: LGTM!프로필 응답 DTO가 적절하게 정의되어 있습니다. 모든 필드가 옵셔널로 처리되어 API의 유연성을 확보했습니다.
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
51-61: LGTM!토큰 갱신 시 사용자 프로필 필드(position, workspace, branch)를 올바르게 보존하고 있습니다.
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (3)
25-49: LGTM!새 프로필 필드를 저장하고 실패 시 롤백하는 로직이 올바르게 구현되었습니다.
63-95: LGTM!프로필 필드가 누락된 경우 빈 문자열로 기본값 처리하는 로직이 적절하며, User 모델의 non-optional 필드 정의와 일관성 있게 구현되었습니다.
124-139: LGTM!로그아웃 시 새 프로필 필드를 포함한 모든 키를 올바르게 삭제하고 있습니다.
SampoomManagement/Features/Auth/Data/Remote/DTO/LoginResponseDTO.swift (1)
12-12: userName 옵셔널 처리 확인됨
AuthMappers.swift의toModel()메서드에서self.userName ?? ""를 사용하여 nil 값을 안전하게 빈 문자열로 처리하고 있습니다. 매퍼가 옵셔널 처리를 올바르게 구현하고 있으므로 추가 조치가 필요하지 않습니다.SampoomManagement/Core/UI/Components/OrderItem.swift (1)
10-39: 잘 구현되었습니다!OrderItem 컴포넌트가 깔끔하게 구현되었습니다. 옵셔널 처리와 레이아웃 구성이 적절합니다.
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
111-114: 토큰 갱신 시 프로필 필드가 올바르게 유지됩니다.새로운 프로필 필드(position, workspace, branch)가 토큰 갱신 후에도 적절히 보존되고 있습니다.
SampoomManagement/Features/Dashboard/UI/DashboardViewModel.swift (1)
12-21: ViewModel 구조가 깔끔합니다.
@MainActor사용과 상태 관리 패턴이 적절하게 구현되어 있습니다.SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (3)
10-24: 로그인 응답 매핑이 적절합니다.새로운 프로필 필드에 대한 기본값 처리가 적절하며, 옵셔널 처리도 올바릅니다.
26-40: 프로필 응답 매핑이 올바릅니다.프로필 DTO에 인증 토큰 정보가 없으므로 빈 값으로 설정하고, 나중에
mergeWith메서드로 병합하는 접근이 적절합니다.
42-56: 병합 로직이 깔끔하게 구현되었습니다.인증 데이터는 원본에서, 프로필 데이터는 파라미터에서 가져오는 불변 병합 패턴이 올바르게 구현되었습니다.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
SampoomManagement/Features/Dashboard/UI/DashboardView.swift (1)
32-38: 빈 액션 핸들러들을 구현하거나 TODO 주석으로 표시하세요.상단 바의 직원 및 설정 버튼(lines 32, 36)과 모든 통계 카드들(lines 90, 93-94, 97-98)이 빈 액션 핸들러
{}를 가지고 있습니다. Line 30에 직원 버튼에 대한 TODO 주석이 있지만, 다른 버튼들에는 구현 계획이 명시되어 있지 않습니다.향후 작업을 추적하기 위해 각 빈 액션 핸들러에 TODO 주석을 추가하거나, 필요하다면 이슈로 등록하는 것을 고려하세요.
이러한 미구현 기능들을 추적할 이슈를 생성하는 데 도움이 필요하신가요?
Also applies to: 90-99
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
SampoomManagement/App/ContentView.swift(3 hunks)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift(2 hunks)SampoomManagement/Features/Dashboard/UI/DashboardUiState.swift(1 hunks)SampoomManagement/Features/Dashboard/UI/DashboardView.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- SampoomManagement/App/ContentView.swift
🧰 Additional context used
🧬 Code graph analysis (2)
SampoomManagement/Features/Dashboard/UI/DashboardView.swift (1)
SampoomManagement/Features/Dashboard/UI/DashboardViewModel.swift (1)
onEvent(23-28)
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (4)
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (2)
login(19-33)getProfile(97-104)SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (3)
toModel(11-23)toModel(27-39)mergeWith(43-55)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (3)
saveToken(51-61)clear(124-139)saveUser(25-49)SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-65)
🪛 SwiftLint (0.57.0)
SampoomManagement/Features/Dashboard/UI/DashboardView.swift
[Warning] 30-30: TODOs should be resolved (role-based employee button)
(todo)
🔇 Additional comments (6)
SampoomManagement/Features/Dashboard/UI/DashboardUiState.swift (1)
25-35: 이전 리뷰의 critical issue가 수정되었습니다! 👍
copy메서드의dashboardError파라미터가 이제 nil-coalescing을 사용하여 기존 에러 메시지를 올바르게 보존합니다. 이전 리뷰에서 지적된 문제가 정확히 해결되었습니다.SampoomManagement/Features/Dashboard/UI/DashboardView.swift (3)
65-85: 이전 리뷰의 하드코딩 문제가 해결되었습니다! 👍
titleSection이 이제 하드코딩된 "가산디지털단지점"과 "홍길동" 대신workspace,branch,userName파라미터를 동적으로 사용하고 있습니다. 이전 리뷰에서 지적된 문제가 정확히 수정되었습니다.
131-164: orderListSection의 상태 처리 구현이 우수합니다! 👍로딩, 에러, 비어있음, 데이터 표시의 네 가지 UI 상태를 모두 적절히 처리하고 있으며,
viewModel.uiState와의 바인딩도 올바르게 구현되어 있습니다.ForEach에서 안정적인 식별자(orderId)를 사용하고 있고, 네비게이션 콜백 처리도 깔끔합니다.
104-129: buttonCard 헬퍼 함수가 잘 구현되었습니다.재사용 가능한 컴포넌트로 깔끔하게 추출되어 있으며, 선택적 테두리 기능과 함께 일관된 디자인 시스템을 사용하고 있습니다. 파라미터 기본값 처리도 적절합니다.
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (2)
41-84: 이전 리뷰에서 제기된 불완전한 상태 문제가 해결되었습니다.이전 리뷰에서 제기된 "프로필 조회 실패 시 토큰은 저장되었지만 프로필 정보가 없는 불완전한 상태" 문제가 현재 코드에서 적절히 해결되었습니다:
- Line 62, 68: 프로필 조회 실패 시
preferences.clear()를 호출하여 저장된 토큰을 롤백합니다.- Line 48의 주석: 토큰을 먼저 저장하는 이유(getProfile 호출 시 Authorization 헤더에 필요)가 명확히 설명되어 있습니다.
- 에러 처리: 각 단계에서 실패 시 적절한 롤백 및 에러 전파가 이루어집니다.
현재 구현은 다음과 같은 견고한 흐름을 제공합니다:
- 로그인 → 토큰 저장
- 프로필 조회 (실패 시 토큰 롤백)
- 데이터 병합
- 최종 사용자 정보 저장
선택적 개선 사항: 네트워크 일시적 오류를 대비한 프로필 조회 재시도 로직을 추가하는 것을 고려해볼 수 있습니다.
120-123: 새로운 프로필 필드가 올바르게 전파됩니다.토큰 갱신 시
position,workspace,branch필드를 기존 사용자 정보에서 새로운 사용자 객체로 올바르게 복사하고 있습니다. 이는 토큰 갱신 시 프로필 데이터가 손실되지 않도록 보장합니다.
📝 Summary
🙏 Question & PR point
📬 Reference
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항
UI/UX