Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthrough알림 기능을 구현하는 PR로, API 엔드포인트 연결, 저장소 계층, ViewModel, 그리고 UI 뷰를 추가합니다. 홈뷰에 알림 배지, 알림 목록 페이지, 개별/전체 읽음 기능이 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant HomeView
participant NotificationViewModel
participant NotificationApi
participant Backend
participant NotificationListView
User->>HomeView: 화면 진입
activate HomeView
HomeView->>NotificationViewModel: fetchUnreadCount() 호출
activate NotificationViewModel
NotificationViewModel->>NotificationApi: getUnreadCount() 요청
activate NotificationApi
NotificationApi->>Backend: GET /api/v1/order/store/notifications/unread/count
activate Backend
Backend-->>NotificationApi: unreadCount 반환
deactivate Backend
deactivate NotificationApi
NotificationViewModel->>NotificationViewModel: `@Published` unreadCount 갱신
Note over HomeView: 배지에 읽지 않음 개수 표시
deactivate NotificationViewModel
deactivate HomeView
User->>HomeView: 알림 아이콘 탭
activate HomeView
HomeView->>NotificationListView: NavigationLink로 이동
deactivate HomeView
activate NotificationListView
NotificationListView->>NotificationViewModel: fetchNotifications() 호출
activate NotificationViewModel
NotificationViewModel->>NotificationApi: getAllNotifications() 요청
activate NotificationApi
NotificationApi->>Backend: GET /api/v1/order/store/notifications/
activate Backend
Backend-->>NotificationApi: [NotificationItem] 반환
deactivate Backend
deactivate NotificationApi
NotificationViewModel->>NotificationViewModel: 알림 목록 정렬 및 `@Published` notifications 갱신
Note over NotificationListView: 알림 목록 렌더링
deactivate NotificationViewModel
deactivate NotificationListView
User->>NotificationListView: 특정 알림 탭
activate NotificationListView
NotificationListView->>NotificationViewModel: markAsRead(notificationId) 호출
activate NotificationViewModel
NotificationViewModel->>NotificationApi: PATCH /api/v1/order/store/notifications/read?notificationId=...
activate NotificationApi
NotificationApi->>Backend: 알림 읽음 처리
activate Backend
Backend-->>NotificationApi: 성공 응답
deactivate Backend
deactivate NotificationApi
NotificationViewModel->>NotificationViewModel: 로컬 상태 갱신 (read=true, unreadCount--)
Note over NotificationListView: UI 업데이트 (읽음 표시기 제거)
deactivate NotificationViewModel
deactivate NotificationListView
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 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 |
@coderabbitai review |
|
@Yoo-Hyuna: 알림 기능 구현에 대한 리뷰를 시작하겠습니다. 잠시만 기다려주세요! 🔍 ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (4)
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift (1)
11-11: 불필요한 nil 초기화를 제거하세요.Optional 변수를 nil로 초기화하는 것은 중복입니다. SwiftLint도 이를 경고하고 있습니다.
다음 diff를 적용하세요:
- @State private var selectedOrderId: Int? = nil + @State private var selectedOrderId: Int?StockMate/StockMate/app/feature/notification/data/NotificationApi.swift (3)
11-18: createdAt를 Date 타입으로 고려해보세요.
createdAt이 String으로 정의되어 있어 날짜 정렬이나 포매팅 시 추가 변환이 필요합니다. 서버에서 ISO 8601 형식으로 제공한다면 문자열 정렬이 가능하지만, Date 타입을 사용하면 더 안전하고 명확한 날짜 처리가 가능합니다.Date 타입을 사용하려면 다음과 같이 커스텀 디코딩을 추가할 수 있습니다:
struct NotificationItem: Decodable, Identifiable { let id: Int let orderId: Int let orderNumber: String let message: String let createdAt: Date let read: Bool enum CodingKeys: String, CodingKey { case id, orderId, orderNumber, message, createdAt, read } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) orderId = try container.decode(Int.self, forKey: .orderId) orderNumber = try container.decode(String.self, forKey: .orderNumber) message = try container.decode(String.self, forKey: .message) read = try container.decode(Bool.self, forKey: .read) let dateString = try container.decode(String.self, forKey: .createdAt) let formatter = ISO8601DateFormatter() createdAt = formatter.date(from: dateString) ?? Date() } }
23-26: URL 구성 방식을 개선할 수 있습니다.문자열 연결을 통한 URL 구성은 동작하지만, URLComponents를 사용하면 더 안전하고 유지보수가 쉬운 코드가 됩니다. 특히 쿼리 파라미터가 있는 경우 자동으로 URL 인코딩을 처리해줍니다.
static func getAllNotifications() -> DataRequest { var components = URLComponents(string: ApiClient.baseURL + "api/v1/order/store/notifications/")! return ApiClient.shared.request(components.url!, method: .get) }
47-50: 쿼리 파라미터 처리를 개선하세요.쿼리 파라미터를 URL 문자열 보간으로 처리하고 있습니다. Int 타입이므로 안전하지만, URLComponents를 사용하면 더 명확하고 확장 가능한 코드가 됩니다.
static func markAsRead(notificationId: Int) -> DataRequest { var components = URLComponents(string: ApiClient.baseURL + "api/v1/order/store/notifications/read")! components.queryItems = [URLQueryItem(name: "notificationId", value: String(notificationId))] return ApiClient.shared.request(components.url!, method: .patch) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
StockMate/StockMate/resources/Assets.xcassets/notiImage.imageset/notiImage.svgis excluded by!**/*.svg
📒 Files selected for processing (10)
StockMate/StockMate/app/feature/auth/ui/HomeView.swift(3 hunks)StockMate/StockMate/app/feature/notification/data/NotificationApi.swift(1 hunks)StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift(1 hunks)StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift(1 hunks)StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift(1 hunks)StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift(1 hunks)StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift(1 hunks)StockMate/StockMate/app/feature/user/ui/ProfileView.swift(1 hunks)StockMate/StockMate/app/navigation/MainTabView.swift(2 hunks)StockMate/StockMate/resources/Assets.xcassets/notiImage.imageset/Contents.json(1 hunks)
🧰 Additional context used
🪛 SwiftLint (0.57.0)
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift
[Warning] 11-11: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (9)
StockMate/StockMate/resources/Assets.xcassets/notiImage.imageset/Contents.json (1)
1-21: SVG 에셋이 모든 스케일에서 올바르게 렌더링되는지 확인하세요.1x 스케일만 SVG 파일을 지정하고 2x, 3x는 비어있습니다. SVG는 해상도 독립적이므로 이것이 의도된 것일 수 있지만, 모든 디바이스 스케일에서 알림 아이콘이 올바르게 표시되는지 테스트해보시기 바랍니다.
StockMate/StockMate/app/feature/user/ui/ProfileView.swift (1)
45-46: LGTM!알림 화면으로의 네비게이션이 올바르게 구현되었습니다.
StockMate/StockMate/app/navigation/MainTabView.swift (1)
21-34: LGTM!HomeView와 ProfileView의 NavigationStack 래핑을 제거한 것은 적절합니다. 각 뷰에서 자체적으로 네비게이션을 관리하는 구조로 변경된 것으로 보입니다.
StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift (1)
11-37: LGTM!Repository 구현이 일관된 패턴을 따르고 있으며, 각 메서드가 명확한 책임을 가지고 있습니다.
StockMate/StockMate/app/feature/auth/ui/HomeView.swift (1)
47-66: 알림 배지 UI 구현이 잘 되어 있습니다.NavigationLink를 활용하여 알림 아이콘에 읽지 않은 알림 개수를 배지로 표시하는 구현이 적절합니다.
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift (1)
42-42: OrderViewModel 인스턴스 공유 방식을 재검토하세요.네비게이션 시 새로운
OrderViewModel()인스턴스를 생성하고 있습니다. 만약 앱의 다른 부분에서 OrderViewModel의 상태를 공유해야 한다면, 이 방식은 문제가 될 수 있습니다.OrderDetailView가 주문 데이터를 독립적으로 fetch하는 경우라면 현재 구현이 적절하지만, 주문 목록과 상태를 공유해야 한다면 environmentObject나 다른 방식으로 기존 인스턴스를 전달하는 것을 고려하세요.
StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift (1)
12-27: LGTM!프로토콜 정의가 명확하며, 알림 기능에 필요한 모든 작업이 잘 정의되어 있습니다. 각 메서드가 async로 선언되어 있어 비동기 처리가 적절히 고려되었습니다.
StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift (2)
33-41: 코드가 잘 작성되었습니다.nil coalescing을 사용하여 안전하게 데이터를 처리하고 있으며, 에러 핸들링도 적절합니다.
26-26: 해당 리뷰 의견은 이 코드베이스에 적용되지 않습니다.코드를 검증한 결과,
createdAt은 ISO 8601 형식(YYYY-MM-DDTHH:MM:SS)으로 저장되고 있습니다. 이 형식은 사전순 문자열 비교로도 올바르게 정렬되므로, 현재 구현은 문제없습니다.Likely an incorrect or invalid review comment.
| @StateObject private var inventoryViewModel = InventoryViewModel() | ||
| // @EnvironmentObject var dashboardViewModel: DashboardViewModel //preview 용 | ||
| @StateObject private var dashboardViewModel = DashboardViewModel() | ||
| @StateObject private var notificationViewModel = NotificationViewModel() // 🔴 추가 |
There was a problem hiding this comment.
🧩 Analysis chain
NotificationViewModel 인스턴스 관리를 확인하세요.
HomeView에서 NotificationViewModel을 생성하고 NotificationListView로 environmentObject를 통해 전달하고 있지만, NotificationListView.swift의 Line 10에서 자체적으로 @StateObject로 새로운 인스턴스를 생성하고 있습니다. 이로 인해 두 개의 독립적인 ViewModel 인스턴스가 존재하게 되어, HomeView의 unread count와 NotificationListView의 데이터가 동기화되지 않습니다.
다음 스크립트를 실행하여 NotificationViewModel 사용 패턴을 확인하세요:
🏁 Script executed:
#!/bin/bash
# NotificationViewModel이 어떻게 사용되는지 확인
rg -n "NotificationViewModel" --type swift -C 3Length of output: 2869
HomeView에서 notificationViewModel을 어떻게 사용하는지 그리고 NotificationListView가 환경 객체를 받는지 확인하겠습니다.
이제 HomeView에서 실제로 notificationViewModel을 environmentObject로 전달하는지, 그리고 NotificationListView가 어떻게 선언되어 있는지 확인하겠습니다.
🏁 Script executed:
#!/bin/bash
# HomeView에서 notificationViewModel 사용 패턴 확인 (line 47-66 근처)
sed -n '45,70p' StockMate/StockMate/app/feature/auth/ui/HomeView.swiftLength of output: 1370
🏁 Script executed:
#!/bin/bash
# NotificationListView 선언 부분 확인 (line 8-12 근처)
sed -n '7,15p' StockMate/StockMate/app/feature/notification/ui/NotificationListView.swiftLength of output: 236
NotificationListView에서 @EnvironmentObject를 사용하여 HomeView의 ViewModel 인스턴스를 받으세요.
HomeView에서 notificationViewModel을 .environmentObject(notificationViewModel)로 전달하고 있으나, NotificationListView의 Line 10에서 @StateObject로 새로운 인스턴스를 생성하고 있습니다. SwiftUI에서는 @StateObject이 @EnvironmentObject보다 우선순위가 높으므로, NotificationListView는 전달받은 환경 객체를 무시하고 자신의 독립적인 인스턴스만 사용합니다. 이로 인해 두 뷰의 데이터가 동기화되지 않습니다.
필수 수정:
NotificationListView.swift, Line 10:
@StateObject private var viewModel = NotificationViewModel()
↓
@EnvironmentObject var notificationViewModel: NotificationViewModel
🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/auth/ui/HomeView.swift around line 17 and
NotificationListView.swift at line 10: HomeView currently creates and injects
notificationViewModel with .environmentObject(notificationViewModel) but
NotificationListView is creating its own @StateObject, which overrides the
environment object and prevents shared state; replace the @StateObject in
NotificationListView with an @EnvironmentObject property to receive the injected
instance (update the property declaration and any internal references to use
that environment object), and ensure HomeView continues to call
.environmentObject(notificationViewModel) when presenting NotificationListView
so both views share the same NotificationViewModel instance.
| import SwiftUI | ||
|
|
||
| struct NotificationListView: View { | ||
| @StateObject private var viewModel = NotificationViewModel() |
There was a problem hiding this comment.
ViewModel을 environmentObject로 주입받도록 수정하세요.
현재 NotificationListView에서 자체적으로 @StateObject로 NotificationViewModel을 생성하고 있습니다. 하지만 HomeView.swift의 Line 48에서 이미 생성된 ViewModel을 environmentObject로 전달하고 있습니다.
이로 인해 두 가지 문제가 발생합니다:
- HomeView와 NotificationListView가 서로 다른 ViewModel 인스턴스를 사용하여 데이터가 동기화되지 않음
- HomeView의 unread count 배지가 알림을 읽어도 업데이트되지 않음
다음 diff를 적용하여 수정하세요:
- @StateObject private var viewModel = NotificationViewModel()
+ @EnvironmentObject var viewModel: NotificationViewModel📝 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.
| @StateObject private var viewModel = NotificationViewModel() | |
| @EnvironmentObject var viewModel: NotificationViewModel |
🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift
around line 10, the view currently creates its own NotificationViewModel with
@StateObject which causes a different instance than the one passed from HomeView
via environmentObject; replace the @StateObject declaration with an
@EnvironmentObject var viewModel: NotificationViewModel so the view uses the
shared instance provided by HomeView, and remove any local initialization;
ensure previews or parent views inject the environmentObject where needed.
| func markAsRead(_ id: Int) async { | ||
| let result = await repository.markAsRead(notificationId: id) | ||
| switch result { | ||
| case .success: | ||
| if let index = notifications.firstIndex(where: { $0.id == id }) { | ||
| notifications[index] = NotificationItem( | ||
| id: notifications[index].id, | ||
| orderId: notifications[index].orderId, | ||
| orderNumber: notifications[index].orderNumber, | ||
| message: notifications[index].message, | ||
| createdAt: notifications[index].createdAt, | ||
| read: true | ||
| ) | ||
| } | ||
| unreadCount = max(0, unreadCount - 1) // 🔴 카운트 즉시 반영 | ||
| case .failure(let error): | ||
| print("❌ 알림 읽음 처리 실패:", error.localizedDescription) | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
NotificationItem 업데이트 로직을 개선하세요.
Lines 49-56에서 NotificationItem의 모든 필드를 수동으로 복사하여 새 인스턴스를 생성하고 있습니다. 이 패턴은 NotificationItem에 필드가 추가되거나 변경될 때 유지보수가 어렵고 실수하기 쉽습니다.
NotificationItem에 업데이트 메서드를 추가하는 것을 권장합니다:
NotificationApi.swift에 다음을 추가:
extension NotificationItem {
func markingAsRead() -> NotificationItem {
return NotificationItem(
id: id,
orderId: orderId,
orderNumber: orderNumber,
message: message,
createdAt: createdAt,
read: true
)
}
}그런 다음 ViewModel에서 다음과 같이 사용:
func markAsRead(_ id: Int) async {
let result = await repository.markAsRead(notificationId: id)
switch result {
case .success:
if let index = notifications.firstIndex(where: { $0.id == id }) {
- notifications[index] = NotificationItem(
- id: notifications[index].id,
- orderId: notifications[index].orderId,
- orderNumber: notifications[index].orderNumber,
- message: notifications[index].message,
- createdAt: notifications[index].createdAt,
- read: true
- )
+ notifications[index] = notifications[index].markingAsRead()
}
unreadCount = max(0, unreadCount - 1)
case .failure(let error):
print("❌ 알림 읽음 처리 실패:", error.localizedDescription)
}
}🤖 Prompt for AI Agents
StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift
lines 44-62: the ViewModel manually re-creates NotificationItem by copying every
field which is brittle; add a convenience instance method (e.g.,
markingAsRead()) on NotificationItem (place in NotificationApi.swift or the
model file) that returns a copy with read set to true, then replace the manual
construction with notifications[index] = notifications[index].markingAsRead();
keep the rest of the logic (index lookup and unreadCount decrement) unchanged.
| func markAllAsRead() async { | ||
| let result = await repository.markAllAsRead() | ||
| switch result { | ||
| case .success: | ||
| for i in 0..<notifications.count { | ||
| notifications[i] = NotificationItem( | ||
| id: notifications[i].id, | ||
| orderId: notifications[i].orderId, | ||
| orderNumber: notifications[i].orderNumber, | ||
| message: notifications[i].message, | ||
| createdAt: notifications[i].createdAt, | ||
| read: true | ||
| ) | ||
| } | ||
| unreadCount = 0 // 🔴 전체 읽음 시 0으로 초기화 | ||
| case .failure(let error): | ||
| print("❌ 전체 읽음 실패:", error.localizedDescription) | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
markAsRead와 동일한 리팩토링을 적용하세요.
Lines 70-77에서도 NotificationItem을 수동으로 재생성하는 동일한 패턴이 반복됩니다. 앞서 제안한 markingAsRead() 메서드를 사용하면 이 메서드도 간결하게 개선할 수 있습니다.
func markAllAsRead() async {
let result = await repository.markAllAsRead()
switch result {
case .success:
- for i in 0..<notifications.count {
- notifications[i] = NotificationItem(
- id: notifications[i].id,
- orderId: notifications[i].orderId,
- orderNumber: notifications[i].orderNumber,
- message: notifications[i].message,
- createdAt: notifications[i].createdAt,
- read: true
- )
- }
+ notifications = notifications.map { $0.markingAsRead() }
unreadCount = 0
case .failure(let error):
print("❌ 전체 읽음 실패:", error.localizedDescription)
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift
around lines 65 to 83, the method markAllAsRead repeats the manual
NotificationItem reconstruction loop (lines 70–77); replace that loop with the
previously introduced markingAsRead() refactor by mapping each notification to
its markingAsRead() variant and assign the resulting array back to
notifications, then set unreadCount = 0 on success; keep the existing repository
call and failure handling unchanged.
@coderabbitai review |
|
@Yoo-Hyuna: 알림 기능 구현에 대한 리뷰를 시작하겠습니다! 🔍 ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift (1)
10-10: [중요] ViewModel을 environmentObject로 주입받도록 수정하세요.현재
NotificationListView에서 자체적으로@StateObject로NotificationViewModel을 생성하고 있습니다. 하지만 AI 요약에 따르면HomeView.swift에서 이미 생성된 ViewModel을environmentObject로 전달하고 있습니다.이로 인해 두 가지 문제가 발생합니다:
- HomeView와 NotificationListView가 서로 다른 ViewModel 인스턴스를 사용하여 데이터가 동기화되지 않음
- HomeView의 읽지 않은 알림 배지가 알림을 읽어도 업데이트되지 않음
다음 diff를 적용하여 수정하세요:
- @StateObject private var notificationViewModel = NotificationViewModel() + @EnvironmentObject var notificationViewModel: NotificationViewModelStockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift (2)
50-57: NotificationItem 업데이트 로직을 개선하세요.NotificationItem의 모든 필드를 수동으로 복사하여 새 인스턴스를 생성하고 있습니다. 이 패턴은 NotificationItem에 필드가 추가되거나 변경될 때 유지보수가 어렵고 실수하기 쉽습니다.
NotificationItem에 업데이트 메서드를 추가하는 것을 권장합니다. NotificationApi.swift에 다음 extension을 추가하세요:
extension NotificationItem { func markingAsRead() -> NotificationItem { return NotificationItem( id: id, orderId: orderId, orderNumber: orderNumber, message: message, createdAt: createdAt, read: true ) } }그런 다음 ViewModel에서 다음과 같이 사용:
if let index = notifications.firstIndex(where: { $0.id == id }) { - notifications[index] = NotificationItem( - id: notifications[index].id, - orderId: notifications[index].orderId, - orderNumber: notifications[index].orderNumber, - message: notifications[index].message, - createdAt: notifications[index].createdAt, - read: true - ) + notifications[index] = notifications[index].markingAsRead() }
71-78: markAsRead와 동일한 리팩토링을 적용하세요.
markAllAsRead에서도 NotificationItem을 수동으로 재생성하는 동일한 패턴이 반복됩니다. 앞서 제안한markingAsRead()메서드를 사용하면 이 메서드도 간결하게 개선할 수 있습니다.다음과 같이 수정하세요:
case .success: - for i in 0..<notifications.count { - notifications[i] = NotificationItem( - id: notifications[i].id, - orderId: notifications[i].orderId, - orderNumber: notifications[i].orderNumber, - message: notifications[i].message, - createdAt: notifications[i].createdAt, - read: true - ) - } + notifications = notifications.map { $0.markingAsRead() } unreadCount = 0
🧹 Nitpick comments (2)
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift (1)
11-11: 중복된 nil 초기화를 제거하세요.옵셔널 변수는 기본적으로
nil로 초기화되므로 명시적으로 지정할 필요가 없습니다.다음과 같이 수정하세요:
- @State private var selectedOrderId: Int? = nil + @State private var selectedOrderId: Int?StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift (1)
29-29: 사용자에게 에러를 알리는 메커니즘을 추가하는 것을 고려하세요.현재 모든 에러 케이스에서
다음 옵션 중 하나를 고려하세요:
- Published 에러 상태 추가:
@Published var errorMessage: String? // 사용 예시 case .failure(let error): errorMessage = "알림을 불러오는데 실패했습니다." print("❌ 알림 조회 실패:", error.localizedDescription)
- Alert 표시를 위한 flag 추가:
@Published var showError = false @Published var errorMessage: String = "" // View에서 .alert(isPresented: $viewModel.showError) 사용이렇게 하면 사용자가 네트워크 오류나 API 실패를 명확하게 인지할 수 있습니다.
Also applies to: 40-40, 61-61, 82-82
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
StockMate/StockMate/app/feature/auth/ui/HomeView.swift(3 hunks)StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift(1 hunks)StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- StockMate/StockMate/app/feature/auth/ui/HomeView.swift
🧰 Additional context used
🪛 SwiftLint (0.57.0)
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift
[Warning] 11-11: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (1)
StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift (1)
41-43: OrderViewModel 인스턴스 생성 방식이 일관성 없이 구현되어 있습니다.코드베이스에서 OrderViewModel 사용이 다음과 같이 불일치합니다:
OrderListView→OrderDetailView: 부모의@StateObjectorderViewModel 공유NotificationListView→OrderDetailView: 새OrderViewModel()인스턴스 생성InOutHistoryView→OrderDetailView: 새OrderViewModel()인스턴스 생성
cancelOrder()메서드 호출 후 상태 업데이트(isLoading 등)가 해당 인스턴스에만 반영되므로, 새로 생성된 인스턴스와 기존 상태가 동기화되지 않습니다. 의도적인 설계라면 명확히 하고, 그렇지 않다면 다음 중 하나로 통일하세요:
- 공유 상태: 모든 곳에서 부모의 shared
orderViewModel사용- 독립적 상태: 각 뷰가 필요한 데이터만
@ObservedObject로 받고 새 인스턴스 생성
| Text(item.orderNumber) | ||
| .font(.system(size: 13, weight: .regular)) | ||
| .foregroundColor(.gray) | ||
| Text(formattedDate(item.createdAt)) |
There was a problem hiding this comment.
🧩 Analysis chain
formattedDate 함수가 정의되지 않았습니다.
formattedDate(item.createdAt) 함수를 호출하고 있지만, 이 함수가 현재 파일에서 정의되지 않았습니다. 이는 컴파일 에러를 발생시킬 수 있습니다.
다음을 확인하세요:
formattedDate함수가 extension이나 다른 파일에 정의되어 있는지 확인- 정의되지 않았다면, 날짜 포맷팅 함수를 추가해야 합니다
다음 스크립트로 함수 정의를 찾을 수 있습니다:
만약 함수가 정의되지 않았다면, 다음과 같이 추가하세요:
// NotificationCardView 외부에 추가
private func formattedDate(_ date: String) -> String {
let inputFormatter = ISO8601DateFormatter()
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "yyyy.MM.dd HH:mm"
outputFormatter.locale = Locale(identifier: "ko_KR")
if let date = inputFormatter.date(from: date) {
return outputFormatter.string(from: date)
}
return date
}🏁 Script executed:
#!/bin/bash
# formattedDate 함수 정의 검색
rg -n 'func formattedDate' --type=swiftLength of output: 359
🏁 Script executed:
#!/bin/bash
# NotificationListView.swift 파일에서 formattedDate 관련 내용 검색
rg -n -C5 'formattedDate' StockMate/StockMate/app/feature/notification/ui/NotificationListView.swiftLength of output: 513
🏁 Script executed:
#!/bin/bash
# NotificationListView.swift 파일에서 import 문 및 함수 정의 확인
rg -n '^(import|func formattedDate|private func|extension)' StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift --type=swiftLength of output: 16
🏁 Script executed:
#!/bin/bash
# ReceiptView.swift의 formattedDate 함수 스코프 확인
rg -n -B2 'func formattedDate\(' StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift --type=swiftLength of output: 189
🏁 Script executed:
#!/bin/bash
# ReceiptView.swift의 formattedDate 함수 정의 주변 상세 확인
rg -n -A10 'func formattedDate\(' StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift --type=swiftLength of output: 940
🏁 Script executed:
#!/bin/bash
# NotificationListView.swift 전체 파일 구조 확인
cat StockMate/StockMate/app/feature/notification/ui/NotificationListView.swiftLength of output: 2968
formattedDate 함수가 NotificationListView.swift에 정의되지 않았습니다. 컴파일 에러가 발생합니다.
line 70에서 formattedDate(item.createdAt) 함수를 호출하고 있지만, 이 함수가 현재 파일에 정의되지 않았습니다.
다음과 같이 NotificationCardView 구조체 외부에 함수를 추가해야 합니다:
private func formattedDate(_ date: String) -> String {
let inputFormatter = ISO8601DateFormatter()
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "yyyy.MM.dd HH:mm"
outputFormatter.locale = Locale(identifier: "ko_KR")
if let date = inputFormatter.date(from: date) {
return outputFormatter.string(from: date)
}
return date
}🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift
around line 70, the call to formattedDate(item.createdAt) fails because
formattedDate is not defined; add a private helper function outside the
NotificationCardView struct named formattedDate(_ date: String) -> String that
uses ISO8601DateFormatter to parse the input string, a DateFormatter with format
"yyyy.MM.dd HH:mm" and locale "ko_KR" to format the parsed date, returns the
formatted string when parsing succeeds and returns the original input string on
failure; place this function below the NotificationCardView declaration so the
view can call it.
| notifications = (response.data ?? []).sorted { $0.createdAt > $1.createdAt } | ||
| // notifications = response.data!.sorted { $0.createdAt > $1.createdAt } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
주석 처리된 코드를 제거하세요.
Line 26에서 안전한 처리로 수정된 것은 좋습니다. 하지만 Line 27의 주석 처리된 이전 코드는 더 이상 필요하지 않으므로 제거해야 합니다.
다음 diff를 적용하세요:
notifications = (response.data ?? []).sorted { $0.createdAt > $1.createdAt }
-// notifications = response.data!.sorted { $0.createdAt > $1.createdAt }
case .failure(let error):📝 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.
| notifications = (response.data ?? []).sorted { $0.createdAt > $1.createdAt } | |
| // notifications = response.data!.sorted { $0.createdAt > $1.createdAt } | |
| notifications = (response.data ?? []).sorted { $0.createdAt > $1.createdAt } | |
| case .failure(let error): |
🤖 Prompt for AI Agents
In
StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift
around lines 26-27, remove the leftover commented-out line "notifications =
response.data!.sorted { $0.createdAt > $1.createdAt }" so only the safe
nil-coalescing/sorted assignment remains; delete the commented code and ensure
the file compiles and formatting is unchanged.
📣 Related Issue
📝 Summary
알림 기능 구현
Summary by CodeRabbit
릴리스 노트