[FIX] 로그인, 회원가입 로직 수정 / 설정 화면 구성#20
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 10 minutes and 58 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 (1)
Walkthrough설정 화면과 관련 MVVM을 추가하고 User 모델에 이메일·agencyId·시작/종료일 필드를 확장했으며, 다수의 API·리포지토리에 agencyId 주입 및 인증 검증을 도입하고 Order 흐름에 completeOrder 유스케이스 및 상태 케이스를 추가했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant DashboardView
participant ContentView
participant SettingView
participant SettingViewModel
participant AuthPreferences
participant SignOutUseCase
User->>DashboardView: 설정 버튼 클릭
DashboardView->>ContentView: onSettingClick()
ContentView->>ContentView: NavigationStack에 .settings 푸시
ContentView->>SettingView: SettingView 표시
SettingView->>SettingViewModel: onEvent(.loadProfile)
SettingViewModel->>AuthPreferences: getStoredUser()
AuthPreferences-->>SettingViewModel: User (email, agencyId,...)
SettingViewModel-->>SettingView: user 업데이트
User->>SettingView: 로그아웃 클릭
SettingView->>SettingViewModel: onEvent(.logout)
SettingViewModel->>SignOutUseCase: execute()
SignOutUseCase->>AuthPreferences: clearUser()
SignOutUseCase-->>SettingViewModel: 성공
SettingViewModel-->>SettingView: onNavigateBack()
sequenceDiagram
participant OrderVM
participant OrderRepo
participant OrderAPI
participant CompleteUseCase
OrderVM->>OrderRepo: receiveOrder(orderId)
OrderRepo->>OrderAPI: receiveOrder(agencyId, orderId)
OrderAPI-->>OrderRepo: success
OrderRepo-->>OrderVM: success
OrderVM->>CompleteUseCase: execute(orderId)
CompleteUseCase->>OrderRepo: completeOrder(orderId)
OrderRepo->>OrderAPI: completeOrder(orderId)
OrderAPI-->>OrderRepo: success
OrderRepo-->>OrderVM: 완료 응답
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ 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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (1)
98-104: URL 인코딩 누락으로 프로필 조회가 실패합니다
workspace를 쿼리 스트링에 직접 삽입하면 공백이나 한글·특수문자가 포함된 워크스페이스 값에서URLConvertible이 URL 생성에 실패하여AFError.invalidURL를 던집니다. 멀티테넌트 환경에서 실제 워크스페이스 이름을 전달하면 바로 재현됩니다.parameters로 넘겨 Alamofire가URLEncoding을 적용하도록 바꾸거나, 최소한addingPercentEncoding으로 안전하게 인코딩해 주세요.
예시 수정안:- return try await networkManager.request( - endpoint: "user/profile?workspace=\(workspace)", - method: .get, - parameters: nil, - responseType: GetProfileResponseDTO.self - ) + return try await networkManager.request( + endpoint: "user/profile", + method: .get, + parameters: ["workspace": workspace], + responseType: GetProfileResponseDTO.self + )SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
29-60: saveUser의 원자성 보장은 좋지만 agencyId 검증이 누락되었습니다.롤백 메커니즘으로 부분 저장 실패를 처리하는 것은 좋은 접근 방식입니다. 하지만 agencyId의 유효성 검증이 없습니다.
agencyId 검증을 추가하는 것을 고려하세요:
func saveUser(_ user: User) throws { + guard user.agencyId > 0 else { + throw KeychainError.invalidData + } do { try keychain.save(user.accessToken, for: Keys.accessToken) // ...KeychainError enum에 invalidData 케이스 추가:
enum KeychainError: Error { case unknown(OSStatus) case invalidData }
🧹 Nitpick comments (5)
SampoomManagement.xcodeproj/project.pbxproj (1)
217-217: 문자열 카탈로그 심볼 생성 활성화 승인문자열 카탈로그 심볼 생성을 활성화하여 로컬라이제이션 지원을 강화하는 변경사항이 올바르게 적용되었습니다.
참고: 이 설정이 프로젝트 레벨(lines 217, 275)과 타겟 레벨(lines 306, 341) 모두에 존재합니다. 타겟 레벨 설정이 우선하므로 프로젝트 레벨의 중복 설정은 실질적인 영향이 없지만, 원한다면 프로젝트 레벨의 설정을 제거하여 설정을 단순화할 수 있습니다.
Also applies to: 275-275
SampoomManagement/Core/Resources/StringResources.swift (1)
80-93: 중복 상수 정의를 확인해주세요.
Common.confirm과Common.ok가 모두 "확인"으로 정의되어 있고,Common.close와Navigation.close가 모두 "닫기"로 정의되어 있습니다. 일관성을 위해 하나의 정의만 사용하거나, 서로 다른 용도라면 명확한 네이밍이 필요합니다.SampoomManagement/Features/Outbound/Data/Repository/OutboundRepositoryImpl.swift (1)
20-22: 반복적인 키체인 접근으로 인한 성능 영향을 고려하세요.모든 메서드에서
preferences.getStoredUser()를 호출하여 키체인에 접근하고 있습니다. 키체인 접근은 상대적으로 비용이 높은 작업이며, 사용자가 Outbound 작업을 연속으로 수행할 경우 성능 저하가 발생할 수 있습니다.다음 중 하나를 고려하세요:
- 초기화 시 사용자 정보를 캐싱하고 주기적으로 갱신
- Repository를 actor로 변환하여 캐싱된 사용자 정보를 안전하게 공유
옵션 1: 초기화 시 캐싱
class OutboundRepositoryImpl: OutboundRepository { private let api: OutboundAPI private let preferences: AuthPreferences + private var cachedUser: User? init(api: OutboundAPI, preferences: AuthPreferences) { self.api = api self.preferences = preferences } + + private func getUser() throws -> User { + if let cached = cachedUser { + return cached + } + guard let user = try preferences.getStoredUser() else { + throw NetworkError.unauthorized + } + cachedUser = user + return user + } func getOutboundList() async throws -> OutboundList { - guard let user = try preferences.getStoredUser() else { - throw NetworkError.unauthorized - } + let user = try getUser() let data: [OutboundDto] = try await api.getOutboundList(agencyId: user.agencyId) // ... }Also applies to: 29-31, 36-38, 44-46, 51-53, 58-60
SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (1)
44-45:endedAt필드의 nil 처리 일관성 개선
startedAt은 빈 문자열일 때 nil로 변환하지만,endedAt은 그대로 전달됩니다. 빈 문자열이 들어올 경우 일관성을 위해 nil로 변환하는 것이 좋습니다.다음 diff를 적용하여 일관성을 개선하세요:
agencyId: self.organizationId, startedAt: self.startedAt.isEmpty ? nil : self.startedAt, - endedAt: self.endedAt + endedAt: self.endedAt.isEmpty ? nil : self.endedAtSampoomManagement/Features/Setting/UI/SettingViewModel.swift (1)
35-37: 프로필 편집 기능 구현 예정TODO 주석이 남아있습니다. 이 기능을 구현할 계획이라면 이슈를 생성하여 추적하는 것을 권장합니다.
프로필 편집 기능의 초기 구현 코드를 생성하거나, GitHub 이슈를 생성하여 이 작업을 추적하시겠습니까?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (34)
SampoomManagement.xcodeproj/project.pbxproj(2 hunks)SampoomManagement/App/ContentView.swift(4 hunks)SampoomManagement/Core/DI/AppDependencies.swift(5 hunks)SampoomManagement/Core/Network/TokenRefreshService.swift(1 hunks)SampoomManagement/Core/Resources/StringResources.swift(5 hunks)SampoomManagement/Core/UI/Components/OrderItem.swift(1 hunks)SampoomManagement/Core/UI/Components/StatusChip.swift(2 hunks)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift(4 hunks)SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift(3 hunks)SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift(2 hunks)SampoomManagement/Features/Auth/Data/Remote/DTO/GetProfileResponseDTO.swift(1 hunks)SampoomManagement/Features/Auth/Data/Remote/DTO/LoginRequestDTO.swift(1 hunks)SampoomManagement/Features/Auth/Data/Remote/DTO/LoginResponseDTO.swift(0 hunks)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift(1 hunks)SampoomManagement/Features/Auth/Domain/Models/User.swift(2 hunks)SampoomManagement/Features/Cart/Data/Remote/API/CartAPI.swift(5 hunks)SampoomManagement/Features/Cart/Data/Repository/CartRepositoryImpl.swift(1 hunks)SampoomManagement/Features/Dashboard/UI/DashboardView.swift(3 hunks)SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift(1 hunks)SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift(1 hunks)SampoomManagement/Features/Order/Domain/Models/OrderStatus.swift(2 hunks)SampoomManagement/Features/Order/Domain/Repository/OrderRepository.swift(1 hunks)SampoomManagement/Features/Order/Domain/UseCase/CompleteOrderUseCase.swift(1 hunks)SampoomManagement/Features/Order/UI/OrderDetailContent.swift(1 hunks)SampoomManagement/Features/Order/UI/OrderDetailViewModel.swift(3 hunks)SampoomManagement/Features/Order/UI/OrderListView.swift(1 hunks)SampoomManagement/Features/Outbound/Data/Remote/API/OutboundAPI.swift(6 hunks)SampoomManagement/Features/Outbound/Data/Repository/OutboundRepositoryImpl.swift(1 hunks)SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift(1 hunks)SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift(2 hunks)SampoomManagement/Features/Setting/UI/SettingUiEvent.swift(1 hunks)SampoomManagement/Features/Setting/UI/SettingUiState.swift(1 hunks)SampoomManagement/Features/Setting/UI/SettingView.swift(1 hunks)SampoomManagement/Features/Setting/UI/SettingViewModel.swift(1 hunks)
💤 Files with no reviewable changes (1)
- SampoomManagement/Features/Auth/Data/Remote/DTO/LoginResponseDTO.swift
🧰 Additional context used
🧬 Code graph analysis (21)
SampoomManagement/Features/Setting/UI/SettingUiEvent.swift (2)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (2)
loadProfile(43-49)logout(51-61)SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (1)
logout(72-79)
SampoomManagement/Features/Order/Domain/Repository/OrderRepository.swift (2)
SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift (1)
completeOrder(49-56)SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift (1)
completeOrder(62-64)
SampoomManagement/Features/Order/UI/OrderDetailViewModel.swift (1)
SampoomManagement/Features/Order/Domain/UseCase/CompleteOrderUseCase.swift (1)
execute(17-19)
SampoomManagement/Features/Setting/UI/SettingView.swift (2)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (3)
onEvent(31-41)loadProfile(43-49)logout(51-61)SampoomManagement/Core/Utilities/DateFormatterUtil.swift (1)
formatDate(16-50)
SampoomManagement/Features/Outbound/Data/Repository/OutboundRepositoryImpl.swift (2)
SampoomManagement/Features/Outbound/Data/Remote/API/OutboundAPI.swift (6)
getOutboundList(19-27)processOutbound(44-53)addOutbound(30-41)deleteOutbound(56-65)deleteAllOutbound(82-91)updateOutbound(68-79)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)
SampoomManagement/Features/Cart/Data/Repository/CartRepositoryImpl.swift (2)
SampoomManagement/Features/Cart/Data/Remote/API/CartAPI.swift (5)
getCartList(19-29)addCart(32-43)deleteCart(46-55)deleteAllCart(72-81)updateCart(58-69)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)
SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift (2)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift (2)
getPartList(36-43)searchParts(45-62)
SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift (2)
SampoomManagement/Features/Part/Data/Repository/PartRepositoryImpl.swift (2)
getPartList(27-32)searchParts(34-39)SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (2)
toModel(11-27)toModel(31-47)
SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift (3)
SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift (2)
completeOrder(62-64)receiveOrder(66-71)SampoomManagement/Core/Network/NetworkManager.swift (2)
request(42-106)request(108-181)SampoomManagement/Features/Order/UI/OrderDetailViewModel.swift (1)
receiveOrder(104-127)
SampoomManagement/Features/Order/Domain/UseCase/CompleteOrderUseCase.swift (2)
SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift (1)
completeOrder(49-56)SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift (1)
completeOrder(62-64)
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-69)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(98-138)
SampoomManagement/Features/Cart/Data/Remote/API/CartAPI.swift (4)
SampoomManagement/Features/Cart/Data/Repository/CartRepositoryImpl.swift (4)
getCartList(19-26)addCart(28-34)deleteCart(36-41)deleteAllCart(43-48)SampoomManagement/Core/Network/NetworkManager.swift (2)
request(42-106)request(108-181)SampoomManagement/Features/Outbound/Data/Remote/API/OutboundAPI.swift (1)
toDictionary(96-99)SampoomManagement/Features/Cart/UI/CartListViewModel.swift (2)
deleteCart(181-206)deleteAllCart(227-252)
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(98-138)
SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift (3)
SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift (2)
completeOrder(49-56)receiveOrder(59-66)SampoomManagement/Features/Order/UI/OrderDetailViewModel.swift (1)
receiveOrder(104-127)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)
SampoomManagement/Features/Outbound/Data/Remote/API/OutboundAPI.swift (2)
SampoomManagement/Features/Outbound/Data/Repository/OutboundRepositoryImpl.swift (5)
getOutboundList(19-26)addOutbound(35-41)processOutbound(28-33)deleteOutbound(43-48)deleteAllOutbound(50-55)SampoomManagement/Core/Network/NetworkManager.swift (2)
request(42-106)request(108-181)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (3)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Features/Setting/UI/SettingUiState.swift (1)
copy(19-27)SampoomManagement/Core/Utilities/GlobalMessageHandler.swift (1)
showMessage(21-30)
SampoomManagement/App/ContentView.swift (3)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Core/DI/AppDependencies.swift (1)
makeSettingViewModel(218-224)SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1)
signOut(40-50)
SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (2)
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-69)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(98-138)
SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-69)
SampoomManagement/Core/Resources/StringResources.swift (2)
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (1)
logout(72-79)SampoomManagement/Features/Setting/UI/SettingViewModel.swift (1)
logout(51-61)
SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift (1)
SampoomManagement/Core/Network/NetworkManager.swift (2)
request(42-106)request(108-181)
🪛 SwiftLint (0.57.0)
SampoomManagement/Features/Setting/UI/SettingView.swift
[Warning] 89-89: TODOs should be resolved (Edit profile)
(todo)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift
[Warning] 36-36: TODOs should be resolved (Implement edit profile)
(todo)
🔇 Additional comments (30)
SampoomManagement/Core/UI/Components/OrderItem.swift (1)
31-31: 변경사항이 올바릅니다!
StatusChip에OrderStatus열거형을 직접 전달하도록 변경되어 타입 안정성이 향상되었습니다. 문자열 기반 접근 방식보다 컴파일 타임 검증이 가능합니다.SampoomManagement/Features/Cart/Data/Remote/API/CartAPI.swift (2)
19-29: 멀티테넌트 지원이 올바르게 적용되었습니다!하드코딩된
agency-1대신 동적agencyId파라미터를 사용하도록 변경되어 멀티테넌트 아키텍처를 올바르게 지원합니다. 리포지토리 구현체에서 저장된 사용자의agencyId를 전달하는 것도 확인되었습니다.
32-43: 모든 메서드가 일관되게 업데이트되었습니다.
addCart,deleteCart,updateCart,deleteAllCart메서드 모두 동일한 패턴으로agencyId파라미터를 사용하도록 업데이트되어 일관성이 유지되고 있습니다.Also applies to: 46-55, 58-69, 72-81
SampoomManagement/Features/Outbound/Data/Remote/API/OutboundAPI.swift (2)
19-27: 엔드포인트 업데이트가 올바르게 적용되었습니다.
getOutboundList메서드가 동적agencyId를 사용하도록 올바르게 변경되었습니다.
30-41: 에러 핸들링 개선과 일관된 멀티테넌트 지원이 추가되었습니다!모든 메서드에
agencyId파라미터가 추가되었을 뿐만 아니라, 이전에 누락되었던response.success검증이addOutbound,processOutbound,deleteOutbound,updateOutbound,deleteAllOutbound메서드에 추가되어 에러 핸들링이 개선되었습니다.Also applies to: 44-53, 56-65, 68-79, 82-91
SampoomManagement/Core/Resources/StringResources.swift (3)
187-193: 설정 화면 문자열 리소스가 추가되었습니다.새로운 설정 화면을 위한 문자열 리소스가 올바르게 추가되었습니다.
224-234: 인증 유효성 검증 메시지가 확장되었습니다.비밀번호 복잡성, 길이 제한 등에 대한 상세한 유효성 검증 메시지가 추가되어 사용자 경험이 개선되었습니다. 마침표를 제거하여 메시지 스타일도 일관되게 정리되었습니다.
177-185: 변경사항 검증 완료 - 안전성 확인됨주문 상태 변경이 올바르게 구현되었습니다.
검증 결과:
- ✅ 모든 8개 상태(
pending,confirmed,shipping,delayed,producing,arrived,completed,canceled)가OrderStatus열거형에 정의됨- ✅ 모든 상태가
StatusChip에서StringResources.Order속성으로 매핑됨- ✅ 기존 상태 문자열 "승인대기" 참조 없음 (안전한 변경)
- ✅ 백엔드 데이터(열거형 raw values: "PENDING" 등)와 UI 표시 문자열(StringResources: 한글)이 분리되어 있음
- ✅
OrderStatus의Codable프로토콜로 기존 주문 데이터 역직렬화 문제 없음새로운 상태들(
producing,delayed등)도 UI에서 올바르게 표시됩니다.SampoomManagement/Features/Order/UI/OrderDetailContent.swift (1)
81-81: 타입 안전성 개선이 적용되었습니다!
OrderStatus열거형을 직접 전달하도록 변경되어 다른 파일들과 일관되게 타입 안전성이 개선되었습니다.SampoomManagement/Features/Setting/UI/SettingUiEvent.swift (1)
10-14: 설정 화면 이벤트 정의가 올바릅니다!설정 화면을 위한 UI 이벤트 열거형이 명확하게 정의되었습니다.
SettingViewModel에서 이러한 이벤트를 올바르게 처리하는 것도 확인되었습니다.SampoomManagement/Features/Auth/Data/Remote/DTO/LoginRequestDTO.swift (1)
11-11: 리뷰 코멘트의 전제가 정확하지 않습니다.검색 결과에 따르면
workspace필드는 호출자가 제공하는 값이 아니라AuthAPI.swift:20에서 **하드코딩된 값 "AGENCY"**로 설정됩니다. 따라서 호출하는 쪽에서 빈 문자열이나 유효하지 않은 값을 제공할 수 없으며, 추가 검증이 필요하지 않습니다.현재 구조:
workspace는 AuthAPI 내부에서만 결정되는 상수값 ("AGENCY")- 실제 사용자 입력은
password이며, 이들은 검증 없이 그대로 전달됨만약 입력값 검증이 필요하다면, 이는
workspace가 아닌password필드에 적용되어야 합니다.Likely an incorrect or invalid review comment.
SampoomManagement/Features/Outbound/Data/Repository/OutboundRepositoryImpl.swift (1)
12-16: AuthPreferences 의존성 추가가 적절합니다.멀티테넌트 지원을 위해 AuthPreferences를 주입하여 사용자의 agencyId를 가져오는 접근 방식이 올바릅니다.
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
51-65: 토큰 갱신 시 확장된 사용자 필드를 올바르게 유지합니다.기존 사용자 정보의 email, branch, agencyId, startedAt, endedAt 필드를 새로운 토큰 정보와 함께 올바르게 전파하고 있습니다.
SampoomManagement/Features/Dashboard/UI/DashboardView.swift (1)
15-15: 설정 화면 연동이 잘 구현되었습니다.onSettingClick 콜백을 추가하여 설정 화면으로의 네비게이션을 적절하게 처리하고 있습니다.
Also applies to: 35-35
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (2)
18-18: 새로운 사용자 필드를 위한 키 추가가 적절합니다.userEmail, agencyId, startedAt, endedAt를 키체인에 저장하기 위한 키들이 올바르게 정의되었습니다.
Also applies to: 24-26
75-116: 기존 사용자 데이터 마이그레이션이 필수입니다.
getStoredUser()는agencyId와
앱 상태 불일치:
isSignedIn()은hasToken()만 확인하므로 기존 사용자는 로그인 상태를 유지합니다. 하지만 실제 기능 사용 시 모든 저장소에서getStoredUser()를 호출하면 실패하거나NetworkError.unauthorized또는AuthError.tokenRefreshFailed를 던집니다.사용자 경험 악화: 기존 사용자는 자동으로 로그아웃되지 않고 앱이 열리지만, 주문 조회, 카트 추가 등 대부분의 기능이 중단됩니다.
필요한 조치:
getStoredUser()가 누락된agencyId/- 프로필 API를 호출하여 누락된 데이터를 채워 키체인에 저장하는 마이그레이션 로직을 구현하거나
AuthViewModel.updateLoginState()또는TokenRefreshService에서getStoredUser()실패를 감지하여 자동 로그아웃 처리SampoomManagement/Features/Part/Data/Remote/API/PartAPI.swift (2)
36-43: 멀티테넌트 지원을 위한 agencyId 파라미터 추가가 적절합니다.getPartList 메서드가 agencyId를 받아 agency별로 부품 목록을 조회하도록 올바르게 업데이트되었습니다.
45-62: 검색 엔드포인트에 agencyId 파라미터가 올바르게 추가되었습니다.searchParts 메서드가 agencyId를 받아 agency 범위 내에서 부품을 검색하도록 올바르게 업데이트되었습니다.
SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift (2)
48-56: 주문 완료 처리를 별도 메서드로 분리한 것이 적절합니다.completeOrder 메서드가 주문 완료 상태 변경을 명확하게 처리하며, 적절한 에러 처리를 포함하고 있습니다.
58-66: 대리점별 주문 입고 처리에 agencyId 파라미터가 올바르게 추가되었습니다.receiveOrder 메서드가 대리점 범위로 올바르게 스코핑되었으며, 관련 코드 스니펫(OrderDetailViewModel)에서 두 메서드를 순차적으로 호출하는 것을 확인할 수 있습니다.
SampoomManagement/Features/Auth/Domain/Models/User.swift (1)
13-13: 확장된 사용자 프로필 필드가 적절하게 추가되었습니다.email, agencyId, startedAt, endedAt 필드가 올바르게 추가되었으며, agencyId는 필수, startedAt/endedAt는 선택적으로 적절하게 정의되었습니다.
Also applies to: 22-24
SampoomManagement/Features/Auth/Data/Remote/DTO/GetProfileResponseDTO.swift (1)
12-20: 백엔드 API 스키마 확인이 필수입니다. 현재 필수 필드 변경이 API 계약과 일치하는지 검증할 수 없습니다.저장소에서 API 응답 예시, 테스트 픽스처, 또는 백엔드 API 문서를 찾을 수 없었습니다. 다음을 확인하세요:
- 백엔드 API 문서:
user/profile?workspace=AGENCY엔드포인트가 모든 필드(userName, email, role, position, workspace, branch, organizationId, startedAt)를 항상 반환하는지 확인- 현재 에러 처리:
AuthRepositoryImpl.swift라인 60-69에서 응답이 없거나 디코딩 실패 시 preferences를 초기화하고 에러를 재던짐 (동작 정상)- 테스트: 백엔드가 필드를 누락하는 시나리오에 대한 테스트가 필요한 경우, 옵셔널 필드로 변경하거나 폴백 값 처리 추가
백엔드 팀과 API 응답 스키마를 확인하고, 필드 누락 가능성이 있으면 관련 필드를 옵셔널로 변경하거나 기본값 처리를 추가하세요.
SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift (2)
50-68: LGTM!토큰 정보는 유지하면서 프로필 정보(email, role, agencyId, startedAt, endedAt 포함)를 병합하는 로직이 올바르게 구현되었습니다.
14-25: 로그인 후 agencyId 기본값 관련 우려 - 검증 완료검증 결과, 현재 구현은 실제로 안전하고 의도된 설계입니다:
임시 상태의 분리: LoginResponseDTO.toModel()에서 생성된 agencyId=0인 User 객체는 메모리에만 존재하고 절대 저장되지 않습니다.
필수 병합 과정: 프로필 조회는 로그인 흐름의 필수 과정으로, mergeWith(profile:)에서 profile.agencyId로 실제 값이 덮어씌워집니다 (AuthMappers.swift:63).
완전한 데이터만 저장: preferences.saveUser(mergedUser)로 저장되는 것은 프로필 데이터가 병합된 완전한 User 객체입니다.
저장소 안전성: 모든 Repository의 getStoredUser()는 항상 완전한 프로필 정보를 포함한 User를 반환하므로, guard 문의 보호 아래 agencyId=0이 API 호출로 전달될 수 없습니다.
추가 작업이 필요하지 않습니다.
SampoomManagement/Core/DI/AppDependencies.swift (3)
65-65: LGTM!
completeOrderUseCase가 올바르게 선언되고 초기화되었습니다. 주문 완료 플로우를 지원하는 적절한 추가입니다.Also applies to: 138-138
107-107: LGTM!Part, Outbound, Cart 리포지토리에
authPreferences의존성을 주입하여 멀티테넌트 기능(agencyId 기반 API 호출)을 지원합니다. 일관된 패턴으로 구현되었습니다.Also applies to: 115-115, 125-125
207-216: LGTM!ViewModel 팩토리 메서드들이 올바르게 구현되었습니다:
makeOrderDetailViewModel에completeOrderUseCase의존성 추가- 새로운
makeSettingViewModel팩토리가 필요한 의존성(authPreferences, signOutUseCase, globalMessageHandler)과 함께 적절히 구성됨Also applies to: 218-224
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (3)
12-29: LGTM!
@MainActor로 UI 스레드 안전성을 보장하고, 의존성 주입 패턴이 깔끔하게 구현되었습니다.ObservableObject프로토콜 준수도 적절합니다.
51-61: LGTM!로그아웃 로직이 적절하게 구현되었습니다:
- 비동기 Task를 사용하여 sign-out 실행
NetworkError의errorDescription우선 사용, fallback으로localizedDescription사용GlobalMessageHandler를 통한 사용자 피드백 제공fire-and-forget 방식의 Task 사용은 로그아웃이 뷰 해제 후에도 완료되어야 하므로 적절합니다.
43-49:getStoredUser()의throws선언과 실제 구현이 불일치합니다.
AuthPreferences.getStoredUser()는throws로 선언되어 있지만 내부의 catch 블록(라인 112-115)이 모든 에러를 포착하고nil을 반환하므로, 실제로는 절대 에러를 던지지 않습니다.따라서
SettingViewModel.loadProfile()의try와 catch 블록(라인 46-48)은 실행될 수 없는 데드 코드입니다.해결방법:
getStoredUser()의throws선언을 제거하거나- 에러를 내부에서 처리하지 말고 호출자에게 전파하도록 리팩토링
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (1)
38-40: Deprecated 이벤트 케이스 처리 개선 제안logout 이벤트 케이스가 deprecated로 표시되어 있지만 여전히 switch문에 포함되어 있습니다. 향후 유지보수를 위해 다음 중 하나를 고려해주세요:
- 이 케이스를 완전히 제거하고 SettingUiEvent enum에서도 삭제
- 또는 유지가 필요한 이유를 주석에 명확히 기록
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
SampoomManagement/App/ContentView.swift(4 hunks)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift(4 hunks)SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift(3 hunks)SampoomManagement/Features/Auth/Domain/Models/User.swift(1 hunks)SampoomManagement/Features/Dashboard/UI/DashboardView.swift(3 hunks)SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift(1 hunks)SampoomManagement/Features/Setting/UI/SettingUiState.swift(1 hunks)SampoomManagement/Features/Setting/UI/SettingView.swift(1 hunks)SampoomManagement/Features/Setting/UI/SettingViewModel.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift
- SampoomManagement/Features/Setting/UI/SettingUiState.swift
🧰 Additional context used
🧬 Code graph analysis (6)
SampoomManagement/App/ContentView.swift (3)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Core/DI/AppDependencies.swift (1)
makeSettingViewModel(218-224)SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1)
signOut(40-50)
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-69)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(98-138)
SampoomManagement/Features/Setting/UI/SettingView.swift (2)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (3)
onEvent(31-42)loadProfile(44-50)logout(52-61)SampoomManagement/Core/Utilities/DateFormatterUtil.swift (1)
formatDate(16-50)
SampoomManagement/Features/Auth/Domain/Models/User.swift (2)
SampoomManagement/Core/Network/TokenRefreshService.swift (1)
refreshToken(17-69)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(98-138)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (3)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Features/Setting/UI/SettingUiState.swift (1)
copy(19-34)SampoomManagement/Core/Utilities/GlobalMessageHandler.swift (1)
showMessage(21-30)
SampoomManagement/Features/Order/Data/Repository/OrderRepositoryImpl.swift (3)
SampoomManagement/Features/Order/Data/Remote/API/OrderAPI.swift (2)
completeOrder(49-56)receiveOrder(59-66)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Features/Order/UI/OrderDetailViewModel.swift (1)
receiveOrder(104-127)
🪛 SwiftLint (0.57.0)
SampoomManagement/Features/Setting/UI/SettingView.swift
[Warning] 92-92: TODOs should be resolved (Edit profile)
(todo)
SampoomManagement/Features/Setting/UI/SettingViewModel.swift
[Warning] 36-36: TODOs should be resolved (Implement edit profile)
(todo)
🔇 Additional comments (7)
SampoomManagement/Features/Setting/UI/SettingView.swift (1)
41-48: 비동기 로그아웃 흐름 정돈 감사합니다.
확인 버튼에서viewModel.logout()성공 여부를 기다린 뒤 상위 콜백을 호출하는 구조라, 이전에 보고된 강제 로그아웃 문제가 해소되었습니다.SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (3)
18-26: 새로운 키 정의가 올바르게 추가되었습니다.userEmail, agencyId, startedAt, endedAt 키가 일관된 네이밍 컨벤션으로 추가되었습니다.
35-43: 새로운 필드 저장 로직이 올바르게 구현되었습니다.email, agencyId, startedAt, endedAt 필드가 적절히 저장되고 있으며, 롤백 로직에도 포함되어 있습니다. 다만 42-43번 라인에서 optional 날짜 필드를 빈 문자열로 저장한 후 getStoredUser에서 다시 nil로 변환하는 방식은 작동하지만, 빈 문자열과 nil 간의 의미 차이가 있을 수 있으니 주의가 필요합니다.
151-159: 로그아웃 시 새로운 필드가 올바르게 삭제됩니다.clear() 메서드가 새로운 필드(userEmail, agencyId, startedAt, endedAt)를 모두 삭제하도록 확장되었습니다.
SampoomManagement/Features/Setting/UI/SettingViewModel.swift (3)
12-29: MVVM 구조와 의존성 주입이 잘 구현되었습니다.@mainactor 애노테이션이 적절히 사용되었고, 의존성이 생성자를 통해 주입되어 테스트 가능성이 높습니다. User의 옵셔널 타입도 AuthPreferences.getStoredUser()가 nil을 반환할 수 있는 것과 일관성이 있습니다.
44-50: 프로필 로드 로직이 적절히 구현되었습니다.getStoredUser()의 반환값과 예외를 적절히 처리하고 있습니다. 다만 AuthPreferences.getStoredUser()가 필수 필드(email, agencyId) 누락 시 nil을 반환하는 점을 고려하면, 사용자에게 nil 상태와 오류 상태를 구분하여 표시하는 것이 좋을 수 있습니다.
52-61: 비동기 로그아웃 로직이 잘 구현되었습니다.async/await를 적절히 사용하고 있으며, NetworkError에 대한 특별 처리와 GlobalMessageHandler를 통한 사용자 피드백이 잘 구현되어 있습니다. Bool 반환값으로 호출자가 성공/실패를 처리할 수 있습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
77-95: 마이그레이션 전략에 agencyId 및 email 기본값 검증 필요검증을 통해 마이그레이션 로직의 위험이 확인되었습니다.
문제점:
agencyId=0 기본값 (AuthPreferences.swift 93줄):
- CartRepositoryImpl, OrderRepositoryImpl, PartRepositoryImpl, OutboundRepositoryImpl의 모든 API 호출이
user.agencyId를 매개변수로 사용- 검증 없음: 어떤 리포지토리도 agencyId가 0인지 확인하지 않음
- 결과: 레거시 사용자가 새 필드 없이 저장된 경우, 모든 API 호출이 agencyId=0으로 전송되어 백엔드에서 거부될 가능성이 높음
- 사용자는 명확한 로그아웃이 아닌 애매한 API 오류만 경험
email="" 기본값: UI 유효성 검사(
!email.isEmpty)를 통과하지만 의미상 부정확필수 수정:
- Line 77-95: getStoredUser() 반환 후 또는 각 리포지토리 호출 전에
agencyId > 0검증 추가- 또는 앱 시작 시 누락된 필드 감지 시 프로필 API 재호출 강제 또는 명시적 재로그인 플로우 구현
- email 기본값을 유효한 플레이스홀더나 빈 문자열 대신 null/optional로 변경 고려
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
SampoomManagement/App/ContentView.swift(4 hunks)SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift(5 hunks)SampoomManagement/Features/Auth/UI/AuthViewModel.swift(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
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-69)SampoomManagement/Features/Auth/Data/Repository/AuthRepositoryImpl.swift (1)
refreshToken(98-138)
SampoomManagement/App/ContentView.swift (3)
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (1)
getStoredUser(75-116)SampoomManagement/Core/DI/AppDependencies.swift (1)
makeSettingViewModel(218-224)SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1)
handleSignedOutState(68-71)
🔇 Additional comments (5)
SampoomManagement/Features/Auth/UI/AuthViewModel.swift (1)
48-71: 상태 갱신 헬퍼 분리 👍로그아웃 흐름에서 UI 상태를 전용 메서드로 분리해 SettingView 등에서 재사용할 수 있게 된 점이 깔끔합니다. 중복 호출 우려도 사라졌습니다.
SampoomManagement/App/ContentView.swift (1)
77-78: UI 콜백에서 상태만 처리하도록 정리된 점 좋습니다SettingViewModel이 네트워크 로그아웃을 처리한 뒤에는 여기서 UI 상태만 갱신하면 되기에, 이제 중복 네트워크 호출 걱정 없이 흐름이 명확해졌습니다.
SampoomManagement/Features/Auth/Data/Local/Preferences/AuthPreferences.swift (3)
18-18: 새로운 키 추가 확인userEmail, agencyId, startedAt, endedAt 키가 추가되어 확장된 User 모델과 일치합니다.
Also applies to: 24-26
29-61: saveUser 구현 적절함새로운 필드(email, agencyId, startedAt, endedAt) 저장 및 실패 시 롤백 처리가 일관되게 구현되었습니다. startedAt/endedAt의 nil 처리도 적절합니다.
145-164: clear 구현 적절함로그아웃 시 새로운 필드(userEmail, agencyId, startedAt, endedAt)를 포함한 모든 키가 삭제됩니다.
📝 Summary
로그인, 회원가입 로직 수정 / 설정 화면 구성
🙏 Question & PR point
📬 Reference
Summary by CodeRabbit
릴리스 노트
New Features
UI / 텍스트
Chores