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재고 조회 기능을 위해 UI 컴포넌트(카드/검색/메인 뷰), 데이터·도메인 계층(API 모델, 리포지토리, 프로토콜) 및 InventoryViewModel을 추가·연동하고, MainTabView에 탭 재선택 트리거를 도입했으며 LoginView의 미사용 주석 속성 제거가 포함되어 있습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor 사용자 as User
participant SearchView as InventorySearchView
participant VM as InventoryViewModel
participant Repo as InventoryRepositoryImpl
participant API as InventoryApi
participant Server
User->>SearchView: 검색어 입력 / 필터 선택
SearchView->>VM: searchByName / toggleCategory/Trim/Model / loadMore
VM->>Repo: findByName() or getInventoryList()/getUnderLimitList()
Repo->>API: build & execute GET request (query params)
API->>Server: HTTP 요청
Server-->>API: ApiResponse<InventoryPageData>
API-->>Repo: 응답 전달
Repo-->>VM: AppResult<ApiResponse<InventoryPageData>>
VM-->>SearchView: @Published 업데이트 (items, loading, hasMore)
SearchView->>User: LazyVStack 렌더링 (InventoryCardView)
Note right of SearchView: 리스트 끝 onAppear → VM에 다음 페이지 로드 요청
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
StockMate/StockMate/app/feature/auth/ui/LoginView.swift (1)
104-114: 인증 우회 디버그 버튼을#if DEBUG로 보호해야 합니다검증 결과,
LoginView.swift:106의 버튼은 현재 모든 빌드에서 활성화되어 있으며#if DEBUG가드가 없습니다. 이는 프로덕션 빌드에서도 인증을 우회할 수 있다는 의미입니다.다른 곳의 authState 변경은 모두
AuthViewModel.swift의 정당한 로그인 로직에서만 발생하므로, 이 UI 버튼이 유일한 보안 위험입니다.제시된 diff 수정안(#if DEBUG로 감싸기)을 적용해주세요.
🧹 Nitpick comments (16)
StockMate/StockMate/app/feature/auth/ui/LoginView.swift (1)
14-17: 미사용 상태 변수 정리email, password, showPassword는 사용되지 않습니다. 제거하여 가독성과 유지보수성을 개선하세요.
- @State private var email = "" - @State private var password = "" - @State private var showPassword = falseStockMate/StockMate/app/feature/inventory/data/InventoryApi.swift (4)
52-64: 쿼리 문자열 직접 연결 대신 URLComponents 사용 권장문자열 결합과 urlQueryAllowed 인코딩은 &/+ 등의 예약문자 처리에 취약할 수 있습니다. URLComponents/URLQueryItem으로 안전하게 구성하세요. 반복 파라미터도 QueryItem을 여러 개 추가하면 됩니다.
- var url = ApiClient.baseURL + "api/v1/store/search?page=\(page)&size=\(size)" - - for c in categoryNames { - url += "&categoryName=\(c.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" - } - for t in trims { - url += "&trim=\(t.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" - } - for m in models { - url += "&model=\(m.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" - } - - return ApiClient.shared.request(url, method: .get) + var components = URLComponents(string: ApiClient.baseURL + "api/v1/store/search")! + var items: [URLQueryItem] = [ + URLQueryItem(name: "page", value: String(page)), + URLQueryItem(name: "size", value: String(size)) + ] + items += categoryNames.map { URLQueryItem(name: "categoryName", value: $0) } + items += trims.map { URLQueryItem(name: "trim", value: $0) } + items += models.map { URLQueryItem(name: "model", value: $0) } + components.queryItems = items + let url = components.url! + return ApiClient.shared.request(url, method: .get)서버가 같은 키 반복(categoryName=...&categoryName=...)을 기대하는지, 배열 형태(categoryNames[]=...)를 기대하는지 API 명세와 확인 부탁드립니다.
69-74: under-limit API도 URLComponents로 통일일관성과 안전한 인코딩을 위해 동일하게 리팩터링하세요.
- var url = ApiClient.baseURL + "api/v1/store/under-limit?page=\(page)&size=\(size)" - if let categoryName = categoryName, !categoryName.isEmpty { - url += "&categoryName=\(categoryName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" - } - return ApiClient.shared.request(url, method: .get) + var components = URLComponents(string: ApiClient.baseURL + "api/v1/store/under-limit")! + var items = [ + URLQueryItem(name: "page", value: String(page)), + URLQueryItem(name: "size", value: String(size)) + ] + if let categoryName, !categoryName.isEmpty { + items.append(URLQueryItem(name: "categoryName", value: categoryName)) + } + components.queryItems = items + return ApiClient.shared.request(components.url!, method: .get)
77-81: find-name API도 동일하게 리팩터링직접 인코딩 대신 URLQueryItem 사용을 권장합니다.
- let encodedName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" - let url = ApiClient.baseURL + "api/v1/store/find-name?name=\(encodedName)&page=\(page)&size=\(size)" - return ApiClient.shared.request(url, method: .get) + var components = URLComponents(string: ApiClient.baseURL + "api/v1/store/find-name")! + components.queryItems = [ + URLQueryItem(name: "name", value: name), + URLQueryItem(name: "page", value: String(page)), + URLQueryItem(name: "size", value: String(size)) + ] + return ApiClient.shared.request(components.url!, method: .get)
12-17: 미사용 타입 정리: InventoryResponseRepository 계층이 ApiResponse로 디코딩하므로 InventoryResponse는 사용되지 않습니다. 혼선을 줄이기 위해 제거를 권장합니다.
StockMate/StockMate/app/core/components/InventoryListSection.swift (1)
10-18: 플레이스홀더 뷰 처리실사용 계획이 없으면 파일 제거를, 사용할 예정이면 실제 섹션 콘텐츠로 교체를 제안합니다.
StockMate/StockMate/app/core/components/InventoryCardView.swift (1)
18-21: 다크 모드/접근성 대응 색상 사용 권장하드코딩된 .black/.white는 다크 모드에서 대비 문제가 생길 수 있습니다. Color.primary/secondary 및 시스템 배경 색상(Material/UIColor.systemBackground 등)로 교체를 권장합니다. 상태 뱃지에는 접근성 라벨/트레이트 추가도 고려해주세요.
예)
- 텍스트: .foregroundColor(.primary)
- 카드 배경: Color(.systemBackground)
- 섀도우는 환경에 따라 강도 조절 또는 제거
Also applies to: 39-41, 47-50, 86-90
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
68-107: 필터 정의 상수의 중복 관리 최소화카테고리/트림/모델 상수가 뷰 내부에 하드코딩되어 있습니다. ViewModel 또는 별도 Constants/Dictionary로 이동해 단일 소스로 관리하면 유지보수가 쉬워집니다.
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (8)
45-48: 페이징 가드 누락으로 불필요한 API 호출 가능inventory/search 모두 hasMore/searchHasMore 체크 없이 호출되어 마지막 페이지 이후에도 요청이 반복될 수 있습니다. reset일 때만 우회하고, 그 외에는 가드하세요.
적용 예시:
@@ - guard !isLoading else { return } + guard !isLoading, (hasMore || reset) else { return } isSearching = false // ✅ 검색 모드 해제 isLoading = true@@ - guard !isLoading else { return } + guard !isLoading, (searchHasMore || reset) else { return } isLoading = true isSearching = trueAlso applies to: 156-161
96-97: 토글 시 Task가 self를 강하게 캡처하고 중복 요청을 유발할 수 있습니다약한 참조로 전환하고(메모리 안전), 연속 토글 시 이전 작업 취소를 고려하세요(최신 상태 우선).
최소 변경:
- Task { await resetAndLoad() } + Task { [weak self] in await self?.resetAndLoad() }추가로 작업 병합까지 원하면:
+ private var reloadTask: Task<Void, Never>? @@ - Task { [weak self] in await self?.resetAndLoad() } + reloadTask?.cancel() + reloadTask = Task { [weak self] in await self?.resetAndLoad() }Also applies to: 105-106, 114-115
21-23: 단일 isLoading으로 서로 다른 섹션이 블로킹됩니다메인 재고/부족재고/검색은 독립 UI 섹션일 가능성이 높습니다. 전역 isLoading 대신 섹션별 로딩 플래그를 분리하면 사용자 경험과 동시성 안전성이 개선됩니다.
예시(개념 스케치):
- @Published var isLoading = false + @Published private(set) var isInventoryLoading = false + @Published private(set) var isUnderLimitLoading = false + @Published private(set) var isSearchLoading = false @@ inventory - guard !isLoading, (hasMore || reset) else { return } - isLoading = true + guard !isInventoryLoading, (hasMore || reset) else { return } + isInventoryLoading = true @@ under-limit - guard !isLoading, (underLimitHasMore || reset) else { return } - isLoading = true + guard !isUnderLimitLoading, (underLimitHasMore || reset) else { return } + isUnderLimitLoading = true @@ search - guard !isLoading, (searchHasMore || reset) else { return } - isLoading = true + guard !isSearchLoading, (searchHasMore || reset) else { return } + isSearchLoading = trueAlso applies to: 119-121, 156-160
47-47: 성공 후 이전 오류 메시지가 남을 수 있음 — 요청 시작 시 message 초기화 권장요청이 성공해도 이전 오류가 UI에 남을 수 있습니다. 시작 시 message를 비워주세요.
isSearching = false // ✅ 검색 모드 해제 isLoading = true + message = ""- isLoading = true + isLoading = true + message = ""- isLoading = true + isLoading = true + message = ""Also applies to: 121-121, 158-158
90-97: 카테고리 변경 시 부족재고 리스트도 동기화 필요부족재고 API는 categoryName(단일)만 지원하지만, 현재 토글은 메인 재고만 갱신합니다. 필터 변경 시 부족재고도 reset 로드를 트리거하도록 UI 또는 VM 흐름을 맞춰주세요.
16-19: 필터 컨테이너를 Array → Set으로 변경 권장(멤버십 체크 O(1))대량 목록 필터링에서 contains 비용이 큽니다. Set으로 전환하면 토글/필터 성능이 개선됩니다. API 호출 시만 Array로 변환하세요.
개념 예시:
- @Published var selectedCategories: [String] = [] - @Published var selectedTrims: [String] = [] - @Published var selectedModels: [String] = [] + @Published var selectedCategories: Set<String> = [] + @Published var selectedTrims: Set<String> = [] + @Published var selectedModels: Set<String> = [] @@ toggle - if selectedCategories.contains(name) { - selectedCategories.removeAll { $0 == name } - } else { - selectedCategories.append(name) - } + if !selectedCategories.insert(name).inserted { + selectedCategories.remove(name) + } @@ repo call - categoryNames: selectedCategories, - trims: selectedTrims, - models: selectedModels + categoryNames: Array(selectedCategories), + trims: Array(selectedTrims), + models: Array(selectedModels)Also applies to: 190-207
209-222: 검색어 트리밍 범위 확장(개행 포함) 및 로컬 검색 필드 검토개행이 포함된 입력이 그대로 남습니다. 또한 현재 korName만 검색합니다(주석된 다른 필드 고려 가능).
- guard !keyword.trimmingCharacters(in: .whitespaces).isEmpty else { + guard !keyword.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { // 검색어 비면 원래 리스트 그대로 표시 isSearching = false return }
190-207: UI 바인딩 확인: 검색 모드에서는 filteredSearchResults 사용서버/로컬 검색 결과에 필터를 재적용하려면 isSearching == true일 때 UI가 searchResults가 아닌 filteredSearchResults를 사용해야 합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
StockMate/StockMate/app/core/components/InventoryCardView.swift(1 hunks)StockMate/StockMate/app/core/components/InventoryListSection.swift(1 hunks)StockMate/StockMate/app/feature/auth/ui/LoginView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift(1 hunks)StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift(1 hunks)StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift(1 hunks)StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (7)
searchInFilteredList(209-222)toggleCategory(90-97)toggleTrim(99-106)toggleModel(108-115)resetFilters(224-244)searchByName(156-188)loadInventoryList(41-83)
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift (2)
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift (3)
getInventoryList(45-65)getUnderLimitList(68-74)findByName(77-81)StockMate/StockMate/app/core/common/SafeApi.swift (1)
safeApi(31-63)
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift (2)
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift (3)
getInventoryList(45-65)getUnderLimitList(68-74)findByName(77-81)StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift (3)
getInventoryList(12-27)getUnderLimitList(29-36)findByName(39-46)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (2)
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift (3)
getInventoryList(45-65)getUnderLimitList(68-74)findByName(77-81)StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift (3)
getInventoryList(12-27)getUnderLimitList(29-36)findByName(39-46)
🔇 Additional comments (4)
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift (1)
10-32: 프로토콜 설계 깔끔합니다데이터 모델 및 구현과 정합성 좋습니다. 비동기 반환 타입도 일관적입니다.
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift (1)
11-46: 구현 일관성 및 예외 처리 경로 적절API 빌드 → safeApi 디코딩 플로우가 명확합니다. 현재 범위에서 충분히 합리적입니다.
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (2)
10-38: 전반 구현은 명확하고 MainActor 상태 관리가 잘 되어 있습니다.의존성 주입, 페이지네이션 상태, 에러/인증 처리 흐름이 일관적입니다. 큰 방향성 좋습니다.
71-73: totalPages 기준(0/1‑based) 확인 필요hasMore 계산이 0‑based(totalPages = 실제 페이지 수) 전제로 구현되어 있습니다. 백엔드 계약이 1‑based라면 경계 오차가 납니다.
백엔드 스펙에서 totalPages의 정의(0/1‑based)를 확인해 주세요. 1‑based라면 비교식을 currentPage < data.totalPages - 1 형태로 조정해야 합니다.
Also applies to: 177-179
@coderabbitai review |
|
@rhkr8521, I'll review the changes in this PR. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
StockMate/StockMate/app/feature/auth/ui/LoginView.swift (1)
123-124: 비밀번호 유효성 검증이 비활성화되었습니다비밀번호 길이 검증이 계산되지만(Line 123) 실제 폼 유효성 검사에서는 무시되고 있습니다(Line 124). 이로 인해:
- 빈 비밀번호나 8자 미만의 약한 비밀번호로도 로그인 시도가 가능해집니다
- 불필요한 네트워크 요청이 발생하여 UX가 저하됩니다
- pwError가 계산되더라도 사용자에게 즉각적인 피드백이 제공되지 않습니다
또한 이 변경사항은 재고 조회 기능 구현이라는 PR의 범위를 벗어납니다. 별도 PR로 분리하거나 원래 검증 로직을 복구해주세요.
다음과 같이 복구하세요:
- return emailError == nil //&& pwError == nil + return emailError == nil && pwError == nilStockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (2)
16-26: 이전 리뷰 이슈 해결 확인 — 키 불일치 수정됨trims의 "수소/전기"와 trimToModels의 키가 일치합니다. 이전 커밋의 불일치 버그가 해소되었습니다. 좋습니다.
135-141: 이전 리뷰 이슈 해결 확인 — 로딩 인디케이터 조건검색 모드에서는 searchHasMore, 일반 모드에서는 hasMore를 사용하는 조건으로 반영되었습니다. 적절합니다.
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift (1)
30-31: 디버그 로그 완전 제거 또는 DEBUG 빌드 한정주석 처리된 print는 제거하거나 DEBUG 조건으로 감싸 주세요. 배포 코드에 남겨둘 필요 없습니다.
- //print("📏 Scroll offsetY:", newValue) // 테스트용, 화면 안정화 후 제거 + // #if DEBUG + // print("📏 Scroll offsetY:", newValue) + // #endif
🧹 Nitpick comments (5)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (3)
11-16: 검색 입력 디바운스 + 서버 검색으로 일관화현재 onChange는 로컬 필터만 호출합니다. 초기 검색 결과가 서버에서 오지 않아 UX가 분절되고, 위 무한 스크롤 조건과도 비일관적입니다. 300–400ms 디바운스로 서버 검색(reset: true) 트리거를 권장합니다. 빈 문자열일 땐 전체 목록을 reset 로드하세요.
struct InventorySearchView: View { @StateObject private var inventoryViewModel = InventoryViewModel() @State private var searchText = "" + @State private var searchTask: Task<Void, Never>? = nil @@ - TextField("부품을 검색하세요.", text: $searchText) + TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) - .onChange(of: searchText) { newValue in - inventoryViewModel.searchInFilteredList(keyword: newValue) - } + .onChange(of: searchText) { newValue in + searchTask?.cancel() + let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines) + searchTask = Task { + try? await Task.sleep(nanoseconds: 350_000_000) + if Task.isCancelled { return } + if term.isEmpty { + await inventoryViewModel.loadInventoryList(reset: true) + } else { + await inventoryViewModel.searchByName(name: term, reset: true) + } + } + }참고: 즉시 반응성이 필요하면 searchInFilteredList를 병행해도 되지만, isSearching/state 일관성 검토가 필요합니다.
Also applies to: 46-54
16-26: 하드코딩된 카테고리/트림/모델 상수의 서버 스키마 드리프트 위험서버 필터 파라미터와 불일치가 발생하기 쉽습니다. 상수를 View에 두기보다 공통 소스(서버 제공 메타데이터, 혹은 Domain/Config 모듈)로 집중 관리하세요.
173-180: 제목 수동 절삭 대신 시스템 트렁케이션 활용 권장다국어/조합형 문자에서 글자수 절삭은 깨질 수 있습니다. lineLimit(1)+.truncationMode(.tail)만으로 충분합니다.
- var truncatedTitle: String { - // 글자 6자까지만 표시, 이후 "..." 처리 - if displayTitle.count > 6 { - let prefix = displayTitle.prefix(5) - return "\(prefix)…" - } - return displayTitle - } + var truncatedTitle: String { displayTitle } @@ Text(truncatedTitle) .font(.system(size: 13)) .foregroundColor(isActive ? .blue : .black) .lineLimit(1) .truncationMode(.tail)Also applies to: 199-215
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift (2)
61-66: 불필요한 Task 생성 방지: hasMore 가드 추가VM에서 가드하긴 하지만, View에서도 underLimitHasMore를 확인하면 중복 Task 제출을 줄일 수 있습니다.
- if item.id == inventoryViewModel.underLimitItems.last?.id { + if item.id == inventoryViewModel.underLimitItems.last?.id, + inventoryViewModel.underLimitHasMore { Task { await inventoryViewModel.loadUnderLimitList() } }
124-132: AnyView 과다 사용은 타입 소실/성능에 불리 — 필요 시 제네릭/enum 라우팅 고려작은 규모에선 영향이 미미하지만, 확장 시에는 enum 기반 라우팅이나 제네릭 컨테이너로 AnyView 사용을 줄이는 편이 좋습니다.
Also applies to: 142-179
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
StockMate/StockMate/app/feature/auth/ui/LoginView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift(1 hunks)StockMate/StockMate/app/navigation/MainTabView.swift(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
🧰 Additional context used
🧬 Code graph analysis (2)
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift (2)
StockMate/StockMate/app/feature/auth/data/TokenStore.swift (1)
clear(24-28)StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (1)
loadUnderLimitList(118-154)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (7)
searchInFilteredList(210-223)toggleCategory(90-97)toggleTrim(99-106)toggleModel(108-115)resetFilters(225-245)searchByName(157-189)loadInventoryList(41-83)
🔇 Additional comments (3)
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift (1)
114-118: 탭 재선택 시 상단 스크롤 UX 적절부모의 tabTappedTrigger 바인딩을 활용해 자연스럽게 최상단으로 스크롤합니다. 구현 깔끔합니다.
StockMate/StockMate/app/navigation/MainTabView.swift (2)
47-53: 동일 탭 재탭 시 트리거 토글 로직 적절현재 탭과 동일할 때만 tabTappedTrigger를 토글합니다. InventoryView의 onChange와 잘 맞습니다.
22-23: 바인딩 전달 OK — 명확한 의도 드러남InventoryView에 selectedTab과 tabTappedTrigger 바인딩을 전달하는 방식, 의도 명확하고 결합도 적절합니다.
@coderabbitai review |
|
@Yoo-Hyuna, 리뷰를 시작하겠습니다. 재고 조회 기능 구현에 대한 상세한 코드 리뷰를 진행하겠습니다. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (4)
71-78: filteredModels 중복/정렬 보장여러 트림을 선택하면 동일 모델이 중복될 수 있습니다. UI 일관성을 위해 중복 제거 및 정렬을 권장합니다.
- private var filteredModels: [String] { - if inventoryViewModel.selectedTrims.isEmpty { - return trimToModels.values.flatMap { $0 } - } else { - return inventoryViewModel.selectedTrims - .flatMap { trimToModels[$0] ?? [] } - } - } + private var filteredModels: [String] { + let models: [String] + if inventoryViewModel.selectedTrims.isEmpty { + models = trimToModels.values.flatMap { $0 } + } else { + models = inventoryViewModel.selectedTrims.flatMap { trimToModels[$0] ?? [] } + } + return Array(Set(models)).sorted() + }
11-14: 뷰모델 주입 가능하도록 초기화자 제공테스트/프리뷰/DI를 위해 InventoryViewModel을 주입할 수 있게 두는 것이 좋습니다. 현재는 고정 생성(@StateObject)만 가능.
-struct InventorySearchView: View { - @StateObject private var inventoryViewModel = InventoryViewModel() +struct InventorySearchView: View { + @StateObject private var inventoryViewModel: InventoryViewModel + init(viewModel: InventoryViewModel = InventoryViewModel()) { + _inventoryViewModel = StateObject(wrappedValue: viewModel) + }
88-89: 문자열 하드코딩 i18n 대응라벨/플레이스홀더가 하드코딩되어 있습니다. LocalizedStringKey 또는 문자열 리소스로 분리해 i18n 대비를 권장합니다.
예:
-TextField("부품을 검색하세요.", text: $searchText) +TextField(LocalizedStringKey("inventory.search.placeholder"), text: $searchText) ... -Text("카테고리") +Text(LocalizedStringKey("inventory.filter.category")) ... -Text("분류") +Text(LocalizedStringKey("inventory.filter.trim")) ... -Text("모델") +Text(LocalizedStringKey("inventory.filter.model")) ... -Text("초기화") +Text(LocalizedStringKey("common.reset")) ... -.navigationTitle("재고 조회") +.navigationTitle(LocalizedStringKey("inventory.title"))Also applies to: 115-116, 122-123, 129-130, 142-143, 199-199
108-108: 다크 모드 대응을 위해 정적 색상을 시스템 동적 색상으로 교체 필요검증 결과, Color.swift의
Color.Light는 고정 hex값(#F7F7F7)으로 정의되어 있어 다크 모드에서 대응되지 않습니다. InventorySearchView.swift의 해당 라인들도 시스템 정적 색상들을 사용하고 있어 다크 모드 호환성 문제가 확인되었습니다.다음 변경을 권장합니다:
- 108번 줄:
Color(.white)→Color(.secondarySystemBackground)- 255번 줄:
.black→.primary(텍스트 색상)- 262번 줄:
.gray→.secondary(텍스트 색상)- InventorySearchView.swift 198번 줄:
Color.Light사용 부분도 함께 검토 필요
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (7)
searchInFilteredList(210-223)toggleCategory(90-97)toggleTrim(99-106)toggleModel(108-115)resetFilters(225-245)searchByName(157-189)loadInventoryList(41-83)
🔇 Additional comments (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
158-186: 검색 모드 페이징 트리거·로딩 인디케이터 로직 보완 LGTM
- 검색 모드일 때 filteredSearchResults의 마지막 셀 기준으로 페이징 트리거하는 부분과,
- 로딩 인디케이터가 isSearching ? searchHasMore : hasMore를 따르는 부분
둘 다 이전 피드백이 잘 반영됐습니다. 👍
Also applies to: 188-194
| var truncatedTitle: String { | ||
| // 글자 6자까지만 표시, 이후 "..." 처리 | ||
| if displayTitle.count > 6 { | ||
| let prefix = displayTitle.prefix(5) | ||
| return "\(prefix)…" | ||
| } | ||
| return displayTitle | ||
| } |
There was a problem hiding this comment.
FilterMenu 제목 자르기 off‑by‑one
주석은 “6자까지 표시”인데, 실제 코드는 5자만 표시합니다. prefix(6)으로 수정하세요.
- // 글자 6자까지만 표시, 이후 "..." 처리
- if displayTitle.count > 6 {
- let prefix = displayTitle.prefix(5)
- return "\(prefix)…"
- }
+ // 글자 6자까지만 표시, 이후 "…" 처리
+ if displayTitle.count > 6 {
+ let prefix = displayTitle.prefix(6)
+ return "\(prefix)…"
+ }🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift around
lines 226 to 233, the logic claims to show up to 6 characters but currently
takes a 5-character prefix then adds an ellipsis; change the truncation to use
displayTitle.prefix(6) so it actually shows 6 characters before the ellipsis (no
other behavior changes needed).
@coderabbitai review |
|
@Yoo-Hyuna, 재고 조회 기능 구현에 대한 상세한 코드 리뷰를 시작하겠습니다. PR에 포함된 다층 아키텍처 변경사항들(UI 컴포넌트, API 계층, 도메인 프로토콜, 저장소 구현, 뷰모델)을 검토하고, 필터링/페이지네이션/검색 로직, 그리고 사전 점검에서 확인된 LoginView.swift의 범위 외 변경사항에 대해서도 상세히 리뷰하겠습니다. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
235-241: FilterMenu 제목 자르기 off‑by‑one 수정주석/의도와 구현이 불일치합니다. 6자까지 보여주고 그 이후에 "…" 처리하도록 맞춰주세요.
- // 글자 5자까지만 표시, 이후 "..." 처리 - if displayTitle.count > 6 { - let prefix = displayTitle.prefix(5) - return "\(prefix)…" - } + // 글자 6자까지만 표시, 이후 "…" 처리 + if displayTitle.count > 6 { + let prefix = displayTitle.prefix(6) + return "\(prefix)…" + }
🧹 Nitpick comments (2)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (2)
116-116: 다크 모드 대응: 검색창 배경색 시스템 배경으로 교체고정 흰색 배경은 다크 모드에서 눈부심/대비 문제를 유발합니다. 시스템 배경으로 교체하세요.
- .background(Color(.white)) + .background(Color(.systemBackground))
121-141: 하드코딩된 레이블/필터 항목 로컬라이즈 권장"카테고리/분류/모델/초기화" 등 사용자 노출 문자열을 Localizable.strings로 분리하세요. 향후 i18n과 카피 변경이 용이해집니다.
Also applies to: 150-151
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (6)
loadInventoryList(41-83)searchByName(157-189)toggleCategory(90-97)toggleTrim(99-106)toggleModel(108-115)resetFilters(225-245)
🔇 Additional comments (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
80-211: 전체 구조는 명확하고 이전 이슈들이 잘 반영되었습니다검색/필터/무한 스크롤/인디케이터 분기 로직이 정돈되었고, filteredSearchResults 기준 사용 및 searchHasMore 분기 등 과거 피드백이 반영되었습니다. 제안 사항만 반영되면 안정적으로 보입니다.
| .onChange(of: searchText) { newValue in | ||
| let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| if term.isEmpty { | ||
| Task { await inventoryViewModel.loadInventoryList(reset: true) } | ||
| } else { | ||
| Task { await inventoryViewModel.searchByName(name: term, reset: true) } | ||
| } | ||
| } |
There was a problem hiding this comment.
검색 onChange → 디바운스 가능한 .task(id:)로 전환 (마지막 입력 누락·과호출 방지)
onChange마다 네트워크 호출됩니다. ViewModel의 isLoading 가드로 인해 타이핑 도중 마지막 입력이 호출되지 않고 이전 결과가 남을 수 있습니다. 300ms 디바운스 + 자동 취소되는 .task(id:)로 전환하세요.
- .onChange(of: searchText) { newValue in
- let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
- if term.isEmpty {
- Task { await inventoryViewModel.loadInventoryList(reset: true) }
- } else {
- Task { await inventoryViewModel.searchByName(name: term, reset: true) }
- }
- }
+ .task(id: searchText) {
+ let term = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
+ try? await Task.sleep(nanoseconds: 300_000_000) // 300ms 디바운스
+ guard term == searchText.trimmingCharacters(in: .whitespacesAndNewlines) else { return }
+ if term.isEmpty {
+ await inventoryViewModel.loadInventoryList(reset: true)
+ } else {
+ await inventoryViewModel.searchByName(name: term, reset: true)
+ }
+ }선택사항: TextField에 .submitLabel(.search)와 .onSubmit { … }를 추가해 명시적 검색도 지원하면 UX가 좋아집니다.
🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift around
lines 94–101, replace the per-keystroke .onChange network calls with a
debounced, auto-cancelable .task(id: term) pattern: derive a trimmed term
variable, start a Task that awaits Task.sleep for ~300_000_000 (300ms) and then
checks Task.isCancelled before calling inventoryViewModel.searchByName(name:
term, reset: true) for non-empty term or
inventoryViewModel.loadInventoryList(reset: true) for empty term; use the task
id bound to the trimmed term so previous tasks cancel automatically on new
input. Optionally add .submitLabel(.search) and .onSubmit { /* immediate search:
call viewModel.searchByName with trimmed term */ } to support explicit searches.
| Button(action: { | ||
| inventoryViewModel.resetFilters(with: searchText) | ||
| }) { | ||
| HStack(spacing: 4) { | ||
| Image(systemName: "arrow.counterclockwise") | ||
| .font(.system(size: 13)) | ||
| Text("초기화") | ||
| .font(.system(size: 13, weight: .medium)) |
There was a problem hiding this comment.
🧩 Analysis chain
resetFilters() 경유 시 검색어 trim 전달 여부 확인 필요
현재 ViewModel.resetFilters는 공백 여부 판단만 trim하고, 실제 searchByName 호출에는 원문 searchText를 전달하는 것으로 보입니다. 공백 포함 쿼리 전송을 막기 위해 trim한 값을 넘기는지 확인/수정 바랍니다. (ViewModel 변경 사항)
권장 수정(참고 — ViewModel 내부):
// else 분기 내부
Task { await searchByName(name: searchText.trimmingCharacters(in: .whitespacesAndNewlines), reset: true) }🏁 Script executed:
#!/bin/bash
# resetFilters가 searchByName(name:)에 어떤 값을 전달하는지 확인
rg -nC6 'func resetFilters\(' StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
rg -n 'searchByName\s*\(\s*name:\s*searchText\b' StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swiftLength of output: 577
resetFilters()에서 공백 제거 없이 searchText를 직접 전달하는 이슈 발견
검증 결과, InventoryViewModel.swift의 243번 라인에서 searchByName(name: searchText, reset: true)으로 호출될 때 searchText가 trim되지 않고 그대로 전달됩니다. 이로 인해 공백만 포함된 쿼리가 검색 요청으로 전송될 수 있습니다.
수정 필요 (InventoryViewModel.swift 243번 라인):
Task { await searchByName(name: searchText.trimmingCharacters(in: .whitespacesAndNewlines), reset: true) }🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift around
lines 144 to 151, the reset button calls inventoryViewModel.resetFilters(with:
searchText) and the view model then forwards searchText untrimmed; update
InventoryViewModel.swift at/near line 243 so the call uses a trimmed string:
pass searchText.trimmingCharacters(in: .whitespacesAndNewlines) into
searchByName(..., reset: true) to avoid sending queries that are only
whitespace.
| .onAppear { | ||
| if inventoryViewModel.isSearching { | ||
| if item.id == inventoryViewModel.filteredSearchResults.last?.id, | ||
| inventoryViewModel.searchHasMore { | ||
| Task { | ||
| await inventoryViewModel | ||
| .searchByName( | ||
| name: searchText.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
무한 스크롤: 빈 검색어로 서버 호출 방지 가드 추가
검색어를 trim해도 빈 문자열이면 호출하지 않도록 가드가 필요합니다. 현재는 isSearching 중 clear 직후 타이밍에 빈 쿼리로 호출될 수 있습니다.
- if item.id == inventoryViewModel.filteredSearchResults.last?.id,
- inventoryViewModel.searchHasMore {
- Task {
- await inventoryViewModel
- .searchByName(
- name: searchText.trimmingCharacters(in: .whitespacesAndNewlines)
- )
- }
- }
+ if item.id == inventoryViewModel.filteredSearchResults.last?.id,
+ inventoryViewModel.searchHasMore {
+ let term = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !term.isEmpty else { return }
+ Task {
+ await inventoryViewModel.searchByName(name: term)
+ }
+ }🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift around
lines 173–183, the infinite-scroll onAppear triggers searchByName even when the
trimmed searchText is empty (e.g., immediately after clear), so add a guard:
compute let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
and only launch the Task calling await inventoryViewModel.searchByName(name:
query) if !query.isEmpty (preserve existing checks for isSearching and
searchHasMore).
@coderabbitai review |
|
@Yoo-Hyuna, 재고 조회 기능 구현에 대한 코드 리뷰를 시작하겠습니다. PR의 변경사항들을 검토하겠습니다:
참고로, 사전 점검에서 LoginView.swift의 변경사항(onLoginSuccess 제거 및 비밀번호 검증 제거)이 이슈 #5의 범위를 벗어난다는 경고가 있습니다. 이 부분에 대한 검토도 포함하여 상세한 리뷰를 진행하겠습니다. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (2)
178-185: 주석과 코드 불일치Line 179의 주석은 "6자까지만 표시"라고 명시하고 있지만, Line 180-181의 실제 코드는 8자를 사용하고 있습니다. 주석과 구현을 일치시켜야 합니다.
의도가 8자라면 다음과 같이 주석을 수정하세요:
var truncatedTitle: String { - // 글자 6자까지만 표시, 이후 "..." 처리 + // 글자 8자까지만 표시, 이후 "…" 처리 if displayTitle.count > 8 { let prefix = displayTitle.prefix(8) return "\(prefix)…" } return displayTitle }또는 의도가 6자라면 코드를 수정하세요:
var truncatedTitle: String { // 글자 6자까지만 표시, 이후 "..." 처리 - if displayTitle.count > 8 { - let prefix = displayTitle.prefix(8) + if displayTitle.count > 6 { + let prefix = displayTitle.prefix(6) return "\(prefix)…" } return displayTitle }
52-62: Clear 버튼 동작 개선 필요Line 54-55에서
searchText를 비우고isSearching을 직접false로 설정하고 있습니다. 그러나searchText를 비웠을 때 전체 재고 목록을 다시 로드하는 로직이 없어 화면에 이전 검색 결과나 빈 목록이 남을 수 있습니다.다음과 같이 수정하세요:
if !searchText.isEmpty { Button(action: { searchText = "" - inventoryViewModel.isSearching = false + Task { + await inventoryViewModel.loadInventoryList(reset: true) + } }) { Image(systemName: "xmark") .foregroundColor(.gray) } .buttonStyle(.plain) .padding(.trailing,3) }
🧹 Nitpick comments (2)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (2)
107-111: 부족 재고 필터링 시 단일 카테고리만 사용하는 제약 문서화 필요
selectedCategories.first를 사용하여 첫 번째 카테고리만 API에 전달하고 있습니다. 이것이 백엔드 API의 제약사항이라면 해당 메서드에 주석을 추가하여 다중 카테고리 필터링이 지원되지 않음을 명시하는 것이 좋습니다.예시:
// 여기서 under-limit API 호출 // ⚠️ 현재 백엔드 API는 단일 카테고리 필터만 지원 let result = await repo.getUnderLimitList( categoryName: selectedCategories.first, // 첫 번째 카테고리만 적용 page: underLimitPage, size: size )
273-281: 공백 문자 집합 일관성 개선Line 276에서
.whitespaces를 사용하고 있지만, 다른 곳(예: Line 258, Line 269 권장 수정)에서는.whitespacesAndNewlines를 사용합니다. 코드베이스 전체에서 일관된 문자 집합을 사용하는 것이 좋습니다.다음과 같이 수정하여 일관성을 유지하세요:
func loadMore(searchText: String) async { if isSearching { - guard !searchText.trimmingCharacters(in: .whitespaces).isEmpty else { return } + guard !searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return } await searchByName(name: searchText) } else { await loadInventoryList() } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
StockMate/StockMate/app/feature/auth/ui/LoginView.swift(0 hunks)StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift(1 hunks)StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift(1 hunks)
💤 Files with no reviewable changes (1)
- StockMate/StockMate/app/feature/auth/ui/LoginView.swift
🧰 Additional context used
🧬 Code graph analysis (2)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (7)
searchByName(134-166)toggleCategory(203-218)toggleTrim(220-233)toggleModel(235-248)resetFilters(251-271)loadMore(274-281)loadInventoryList(48-87)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift (2)
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift (3)
getInventoryList(45-65)getUnderLimitList(68-74)findByName(77-81)StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift (3)
getInventoryList(12-27)getUnderLimitList(29-36)findByName(39-46)
🪛 SwiftLint (0.57.0)
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
[Warning] 94-94: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
🔇 Additional comments (1)
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift (1)
143-146: 로딩 인디케이터 구현 확인로딩 인디케이터가
isLoading상태만 확인하도록 단순화되어 있습니다. 이전 리뷰에서hasMore와searchHasMore를 구분하여 확인하도록 제안했지만, 현재 구현이 더 단순하고 명확합니다. 로딩 중일 때 항상 인디케이터를 표시하는 것이 합리적입니다.
| TextField("부품을 검색하세요.", text: $searchText) | ||
| .textFieldStyle(PlainTextFieldStyle()) | ||
| .onSubmit { | ||
| Task { | ||
| await inventoryViewModel.searchByName(name: searchText, reset: true) | ||
| } | ||
| } |
There was a problem hiding this comment.
검색 실행 시 공백 제거 누락
Line 48에서 searchByName에 searchText를 trim하지 않고 전달하고 있습니다. 사용자가 공백을 포함한 검색어를 입력할 경우 불필요한 서버 호출이나 의도하지 않은 결과가 발생할 수 있습니다.
다음과 같이 수정하세요:
TextField("부품을 검색하세요.", text: $searchText)
.textFieldStyle(PlainTextFieldStyle())
.onSubmit {
+ let term = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !term.isEmpty else { return }
Task {
- await inventoryViewModel.searchByName(name: searchText, reset: true)
+ await inventoryViewModel.searchByName(name: term, reset: true)
}
}🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift around
lines 44 to 50, the searchText is passed directly to
inventoryViewModel.searchByName allowing leading/trailing whitespace; trim
searchText before invoking the search and skip the call if the trimmed string is
empty. Concretely, inside the onSubmit Task create a local let query =
searchText.trimmingCharacters(in: .whitespacesAndNewlines), guard !query.isEmpty
else { return } and then await inventoryViewModel.searchByName(name: query,
reset: true).
| await loadInventoryList(reset: true) | ||
| } | ||
|
|
||
| // MARK: - 부족 재고 로드 |
There was a problem hiding this comment.
MARK 주석 형식 수정 필요
SwiftLint에서 MARK 주석 형식 오류를 감지했습니다. 공백이 하나 부족합니다.
다음과 같이 수정하세요:
- // MARK: - 부족 재고 로드
+ // MARK: - 부족 재고 로드📝 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.
| // MARK: - 부족 재고 로드 | |
| // MARK: - 부족 재고 로드 |
🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 94-94: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'
(mark)
🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
around line 94, the MARK comment spacing doesn't match SwiftLint expectations;
update the comment to use the canonical MARK format with a single space after
the hyphen so it reads with exactly one space between the hyphen and the section
title (i.e., adjust the spacing in the MARK line).
| // MARK: - 검색어 입력 시 로컬 검색 전환 | ||
| func searchInFilteredList(keyword: String) { | ||
| guard !keyword.trimmingCharacters(in: .whitespaces).isEmpty else { | ||
| // 검색어 비면 원래 리스트 그대로 표시 | ||
| isSearching = false | ||
| return | ||
| } | ||
| isSearching = true | ||
| searchResults = inventoryItems.filter { | ||
| //$0.name.localizedCaseInsensitiveContains(keyword) || | ||
| $0.korName.localizedCaseInsensitiveContains(keyword) // || | ||
| //$0.engName.localizedCaseInsensitiveContains(keyword) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
미사용 메서드 제거 검토
searchInFilteredList 메서드가 로컬 필터링을 수행하지만, 현재 검색 흐름에서는 서버 측 검색(searchByName)을 사용하고 있어 이 메서드가 호출되지 않는 것으로 보입니다. 또한 주석 처리된 필터 조건들이 있어 미완성 코드처럼 보입니다.
다음 스크립트를 실행하여 이 메서드의 사용 여부를 확인하세요:
만약 사용되지 않는다면 제거하고, 향후 사용할 계획이 있다면 TODO 주석으로 명시하는 것을 권장합니다.
🏁 Script executed:
#!/bin/bash
# searchInFilteredList 메서드 호출 위치 검색
rg -n 'searchInFilteredList' --type swiftLength of output: 129
추가 검증을 위해 전체 프로젝트에서 더 광범위한 검색을 진행하겠습니다.
🏁 Script executed:
#!/bin/bash
# searchInFilteredList 메서드가 정의된 곳 제외하고 모든 참조 확인
rg -n 'searchInFilteredList' --type swift -C 3Length of output: 802
🏁 Script executed:
#!/bin/bash
# isSearching과 searchResults 변수가 다른 메서드에서 설정되는지 확인
rg -n 'isSearching\s*=' --type swift -C 2 | head -50Length of output: 4764
🏁 Script executed:
#!/bin/bash
# 서버 측 검색 메서드(searchByName) 사용 확인
rg -n 'searchByName' --type swift -C 2Length of output: 2368
🏁 Script executed:
#!/bin/bash
# 검색 관련 메서드 목록 확인
rg -n 'func.*search' --type swift -C 1Length of output: 1476
🏁 Script executed:
#!/bin/bash
# InventoryViewModel.swift 파일 전체 구조 확인
fd -e swift -x wc -l {} \; | grep -i inventoryLength of output: 685
🏁 Script executed:
#!/bin/bash
# InventoryViewModel.swift에서 search 관련 메서드 모두 찾기
rg -n 'func.*[Ss]earch' StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swiftLength of output: 240
🏁 Script executed:
#!/bin/bash
# searchInFilteredList 정의 주변 코드 확인
rg -n 'searchInFilteredList' --type swift -A 20 -B 3Length of output: 2765
🏁 Script executed:
#!/bin/bash
# 전체 파일에서 searchInFilteredList 참조 수 확인
rg 'searchInFilteredList' --type swift | wc -lLength of output: 1
🏁 Script executed:
#!/bin/bash
# searchResults 변수 초기화 및 할당 패턴 확인
rg -n 'searchResults\s*=' StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift -C 2Length of output: 474
🏁 Script executed:
#!/bin/bash
# 다른 검색 메서드와 비교: searchByName 정의 확인
rg -n 'func searchByName' --type swift -A 15Length of output: 1793
🏁 Script executed:
#!/bin/bash
# 바인딩 또는 클로저에서의 searchInFilteredList 참조 확인
rg -n 'searchInFilteredList' StockMate/StockMate/app/feature/inventory/viewmodel/ -r 'N/A'Length of output: 112
🏁 Script executed:
#!/bin/bash
# InventoryViewModel.swift 전체 메서드 목록
ast-grep --pattern 'func $FUNC_NAME($$$) { $$$ }'Length of output: 50274
🏁 Script executed:
#!/bin/bash
# searchInFilteredList가 실제로 호출되는 곳 최종 확인 (전체 프로젝트)
rg -n 'searchInFilteredList\(' . --type swiftLength of output: 131
🏁 Script executed:
#!/bin/bash
# 혹시 문자열 중에 메서드명이 있는지 확인
rg -n '"searchInFilteredList"' . --type swiftLength of output: 0
🏁 Script executed:
#!/bin/bash
# 메서드 포인터나 셀렉터로 참조되는지 확인
rg -n '#selector.*searchInFilteredList' . --type swiftLength of output: 0
🏁 Script executed:
#!/bin/bash
# 최종 정리: 실제 호출 지점 확인
rg 'searchInFilteredList' StockMate --type swift -g '!*ViewModel.swift' Length of output: 0
searchInFilteredList 메서드 제거 권장
검증 결과, searchInFilteredList 메서드는 정의되어 있지만 코드 전체에서 호출되지 않는 미사용 메서드입니다. 현재 검색은 searchByName()의 서버 측 검색으로 완전히 이동되었으며, 주석 처리된 필터 조건들(name, engName)은 미완성 상태를 나타냅니다.
권장 조치:
- 라인 187-200의
searchInFilteredList메서드 제거 - 향후 로컬 필터링이 필요하면 새로운 메서드로 구현
🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
around lines 187 to 200, remove the unused method searchInFilteredList(keyword:)
which is never called and contains commented-out filter fields; delete the
entire method block (lines 187–200) and, if local filtering is needed later, add
a clean, well-scoped implementation then call it from the appropriate UI/search
flow.
| // MARK: - 필터 초기화 | ||
| func resetFilters(with searchText: String) { | ||
| // 1. 필터 관련 선택 초기화 | ||
| selectedCategories.removeAll() | ||
| selectedTrims.removeAll() | ||
| selectedModels.removeAll() | ||
|
|
||
| // 2. 검색어가 없을 경우 → 전체 재고 다시 | ||
| if searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { | ||
| isSearching = false | ||
| searchResults.removeAll() | ||
| // searchPage = 0 | ||
| Task { await loadInventoryList(reset: true) } | ||
| } | ||
| // 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색 | ||
| else { | ||
| isSearching = true | ||
| //searchPage = 0 | ||
| searchResults.removeAll() | ||
| Task { await searchByName(name: searchText, reset: true) } | ||
| } | ||
| } |
There was a problem hiding this comment.
검색어 공백 제거 누락
Line 269에서 searchByName에 searchText를 그대로 전달하고 있습니다. Line 258에서는 공백 검사를 위해 trim하지만, 실제 API 호출에는 trim되지 않은 값을 전달하여 공백만 포함된 쿼리가 서버로 전송될 수 있습니다.
다음과 같이 수정하세요:
// 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색
else {
isSearching = true
//searchPage = 0
searchResults.removeAll()
- Task { await searchByName(name: searchText, reset: true) }
+ Task {
+ await searchByName(
+ name: searchText.trimmingCharacters(in: .whitespacesAndNewlines),
+ reset: true
+ )
+ }
}📝 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.
| // MARK: - 필터 초기화 | |
| func resetFilters(with searchText: String) { | |
| // 1. 필터 관련 선택 초기화 | |
| selectedCategories.removeAll() | |
| selectedTrims.removeAll() | |
| selectedModels.removeAll() | |
| // 2. 검색어가 없을 경우 → 전체 재고 다시 | |
| if searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { | |
| isSearching = false | |
| searchResults.removeAll() | |
| // searchPage = 0 | |
| Task { await loadInventoryList(reset: true) } | |
| } | |
| // 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색 | |
| else { | |
| isSearching = true | |
| //searchPage = 0 | |
| searchResults.removeAll() | |
| Task { await searchByName(name: searchText, reset: true) } | |
| } | |
| } | |
| // MARK: - 필터 초기화 | |
| func resetFilters(with searchText: String) { | |
| // 1. 필터 관련 선택 초기화 | |
| selectedCategories.removeAll() | |
| selectedTrims.removeAll() | |
| selectedModels.removeAll() | |
| // 2. 검색어가 없을 경우 → 전체 재고 다시 | |
| if searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { | |
| isSearching = false | |
| searchResults.removeAll() | |
| // searchPage = 0 | |
| Task { await loadInventoryList(reset: true) } | |
| } | |
| // 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색 | |
| else { | |
| isSearching = true | |
| //searchPage = 0 | |
| searchResults.removeAll() | |
| Task { | |
| await searchByName( | |
| name: searchText.trimmingCharacters(in: .whitespacesAndNewlines), | |
| reset: true | |
| ) | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
around lines 250 to 271, the code trims whitespace only for the empty check but
passes the untrimmed searchText to searchByName, which can send whitespace-only
queries to the server; fix by computing a single trimmed variable (e.g., let
trimmed = searchText.trimmingCharacters(in: .whitespacesAndNewlines)), use
trimmed for the isEmpty check, set isSearching based on trimmed, clear/reset
searchResults as before, and call searchByName(name: trimmed, reset: true) (and
pass trimmed when invoking any other search-related APIs) so the API always
receives the trimmed query.
📣 Related Issue
📝 Summary
Summary by CodeRabbit
재고 관리 기능 업데이트
새로운 기능
개선사항
기타