Skip to content

[FEAT] 재고 조회 기능 구현#6

Merged
Yoo-Hyuna merged 11 commits into
devfrom
feat/#5
Oct 20, 2025
Merged

[FEAT] 재고 조회 기능 구현#6
Yoo-Hyuna merged 11 commits into
devfrom
feat/#5

Conversation

@Yoo-Hyuna

@Yoo-Hyuna Yoo-Hyuna commented Oct 17, 2025

Copy link
Copy Markdown
Collaborator

📣 Related Issue

📝 Summary

  • 재고 조회 API 연결
  • 재고 관리 탭 부족재고 API 연동 및 UI 수정
  • 재고관리 탭 부품이름 검색 API 연결 및 UI 수정

Summary by CodeRabbit

재고 관리 기능 업데이트

  • 새로운 기능

    • 카드형 재고 아이템 표시(이미지·이름·설명·상태)
    • 통합 검색 화면: 실시간 이름 검색, 카테고리·규격·모델 필터, 필터 메뉴
    • 재고 부족 전용 리스트 및 페이징/무한스크롤
  • 개선사항

    • 재고 메인 화면 레이아웃·그리드 메뉴 재설계, 스크롤-맨위 버튼·로딩 표시 개선
  • 기타

    • 로그인 뷰의 불필요한 주석 제거 및 일부 테스트용 섹션 추가

@Yoo-Hyuna Yoo-Hyuna requested a review from rhkr8521 October 17, 2025 03:45
@Yoo-Hyuna Yoo-Hyuna self-assigned this Oct 17, 2025
@Yoo-Hyuna Yoo-Hyuna added the FEAT label Oct 17, 2025
@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

재고 조회 기능을 위해 UI 컴포넌트(카드/검색/메인 뷰), 데이터·도메인 계층(API 모델, 리포지토리, 프로토콜) 및 InventoryViewModel을 추가·연동하고, MainTabView에 탭 재선택 트리거를 도입했으며 LoginView의 미사용 주석 속성 제거가 포함되어 있습니다.

Changes

Cohort / File(s) Summary
UI — 카드/리스트 컴포넌트
StockMate/StockMate/app/core/components/InventoryCardView.swift, StockMate/StockMate/app/core/components/InventoryListSection.swift
재고 항목 카드 뷰(이미지, 명칭, 설명, 상태 배지) 추가 및 리스트 섹션 플레이스홀더 추가
UI — 검색/필터 컴포넌트
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift
검색바, 다중 필터(FilterMenu), 동적 모델 옵션, 무한 스크롤/페이지네이션, 로딩 상태 처리하는 InventorySearchView 및 FilterMenu 추가
UI — 재고 메인 뷰 리팩토링
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift
ScrollViewReader 기반 레이아웃, 스크롤 감지(상단 버튼), GridMenu 재구성, under-limit 항목 표시 및 InventoryViewModel 연동; StockShortageCard 제거
탭 네비게이션 변경
StockMate/StockMate/app/navigation/MainTabView.swift
탭 재선택 트리거(tabTappedTrigger) 추가 및 InventoryView에 바인딩 전달해 탭 재선택 시 동작 트리거 가능
도메인 프로토콜
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift
재고 조회용 프로토콜 추가(목록, under-limit, 이름 기반 검색)
API 레이어
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift
페이지네이션 재고 목록, 수량 부족 목록, 이름 검색용 API 헬퍼와 데이터 모델(InventoryResponse, InventoryPageData, InventoryItem) 추가
저장소 구현
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift
InventoryRepositoryProtocol 구현체 추가 — API 요청 생성 및 safeApi 통해 응답 디코딩/반환
뷰모델
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift
재고 데이터·필터·검색·페이지네이션 및 인증 오류 처리(401/403) 등을 담당하는 ObservableObject 뷰모델 추가
인증 UI 변경
StockMate/StockMate/app/feature/auth/ui/LoginView.swift
onLoginSuccess 콜백 제거(주석 속성 정리)

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에 다음 페이지 로드 요청
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • rhkr8521

Poem

🐰 재고를 찾아 폴짝 폴짝 왔네,
필터로 고르고 검색해 냉큼 찾네,
부족하면 빨갛게, 넉넉하면 푸르게,
스크롤 끝엔 더 불러오는 꿈,
당근 들고 오늘도 정리하자!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning LoginView.swift의 변경사항이 Issue #5의 범위를 벗어나 발견되었습니다. onLoginSuccess 콜백 제거 및 비밀번호 길이 검증 제거는 재고 조회 기능 구현과 직접적인 관련이 없으며, 별도의 인증 기능 개선에 해당합니다. 이는 재고 조회 페이지 UI 구현 및 API 연동이라는 Issue #5의 정의된 범위에 포함되지 않습니다. LoginView.swift의 변경사항을 별도의 PR로 분리하거나, 이러한 변경이 재고 조회 기능 구현의 필수 요소인지 명확히 문서화하여 제시할 것을 권고합니다. 현재 상태에서는 범위 외 변경으로 인해 PR의 목적이 모호해질 수 있으므로 분리 또는 정당성 설명이 필요합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목 "[FEAT] 재고 조회 기능 구현"은 변경사항의 주요 목적을 명확하고 간결하게 표현합니다. 제목은 InventoryApi, InventoryViewModel, InventorySearchView, InventoryCardView 등을 통해 구현된 재고 조회 기능의 핵심을 정확하게 반영하며, 추가적인 노이즈 없이 개발자가 스캔할 때 변경의 주요 의도를 이해하기에 충분합니다.
Linked Issues Check ✅ Passed 연결된 Issue #5의 요구사항이 완전히 충족되었습니다. 요구사항 중 "지점별 재고 조회 API 연동"은 InventoryApi의 getInventoryList, getUnderLimitList, findByName 메서드와 InventoryRepositoryProtocol/InventoryRepositoryImpl을 통해 구현되었으며, "재고 조회 페이지 UI 구현"은 InventorySearchView(검색 및 다중 필터), InventoryCardView, InventoryListSection, 리팩토링된 InventoryView(스크롤 감지, 상단 버튼, 동적 그리드, 부족수량 표시)를 통해 구현되었습니다. InventoryViewModel은 필터링, 검색, 페이지네이션, 상태 관리를 통합하여 기능을 완성합니다.

Comment @coderabbitai help to get the list of available commands and usage tips.

@rhkr8521

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = false
StockMate/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: 미사용 타입 정리: InventoryResponse

Repository 계층이 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 = true

Also 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 = true

Also 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2c09be6 and f9f066c.

📒 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

Comment thread StockMate/StockMate/app/feature/auth/ui/LoginView.swift Outdated
Comment thread StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift Outdated
Comment thread StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift
@rhkr8521

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

재고 조회 기능을 구현하기 위해 UI 컴포넌트, 데이터 계층, 도메인 계층, 뷰 모델을 추가하고 필터링, 페이지네이션, 검색 기능과 기존 로그인 검증 로직을 통합합니다.

Changes

Cohort / File(s) Summary
UI Components - 재고 카드 및 리스트
StockMate/StockMate/app/core/components/InventoryCardView.swift, StockMate/StockMate/app/core/components/InventoryListSection.swift 재고 항목 카드 뷰 추가 (상품 이미지, 이름, 설명, 수량 상태 표시) 및 플레이스홀더 리스트 섹션 컴포넌트 신규 생성
재고 검색 UI
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift 검색창, 카테고리/트림/모델 다중 필터 메뉴, 무한 스크롤 기능을 갖춘 재고 검색 뷰 및 재사용 가능한 FilterMenu 컴포넌트 신규 추가
재고 관리 뷰 업데이트
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift 스크롤 감지, 상단 스크롤 버튼, 동적 그리드 메뉴, 수량 부족 항목 동적 표시 등으로 레이아웃 대폭 개선 및 InventoryViewModel 통합
API 및 데이터 모델
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift 페이지네이션 재고 목록, 수량 부족 항목, 이름 기반 검색 API 메서드 및 InventoryResponse, InventoryItem 데이터 모델 신규 정의
도메인 계층
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift 재고 데이터 조회를 위한 InventoryRepositoryProtocol 프로토콜 신규 정의 (3가지 조회 메서드)
저장소 구현
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift InventoryRepositoryProtocol 구현체로 API 요청 실행 및 응답 디코딩 처리
뷰 모델
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift 재고 데이터, 필터, 페이지네이션, 검색 상태 관리 및 비동기 데이터 로드, 필터 토글, 검색 기능 제공
인증 로직 수정
StockMate/StockMate/app/feature/auth/ui/LoginView.swift 로그인 폼 검증에서 비밀번호 길이 검증 제거 (이메일 형식 검증만 유지)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant InventorySearchView
    participant InventoryViewModel
    participant InventoryRepositoryImpl
    participant InventoryApi
    participant Server

    User->>InventorySearchView: 검색어 입력 또는 필터 선택
    InventorySearchView->>InventoryViewModel: searchByName() 또는<br/>toggleCategory/Trim/Model()
    
    InventoryViewModel->>InventoryRepositoryImpl: findByName() 또는<br/>getInventoryList()
    InventoryRepositoryImpl->>InventoryApi: API 요청 생성<br/>(쿼리 파라미터 포함)
    InventoryApi->>Server: GET /inventory/search 또는<br/>GET /inventory/list
    
    Server-->>InventoryApi: ApiResponse<InventoryPageData>
    InventoryRepositoryImpl->>InventoryRepositoryImpl: safeApi로<br/>응답 디코딩
    InventoryRepositoryImpl-->>InventoryViewModel: AppResult 반환
    
    InventoryViewModel->>InventoryViewModel: 필터링된 검색 결과<br/>업데이트
    InventoryViewModel-->>InventorySearchView: @Published 상태 변경
    
    InventorySearchView->>InventorySearchView: LazyVStack로<br/>InventoryCardView 렌더링
    InventorySearchView-->>User: 검색 결과 표시
    
    User->>InventorySearchView: 리스트 끝 도달
    InventorySearchView->>InventoryViewModel: 다음 페이지 로드
    InventoryViewModel->>InventoryRepositoryImpl: page 증가하여<br/>재요청
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

사유: 다층 아키텍처 변경 (UI, 뷰 모델, 저장소, 도메인), 복잡한 필터링 및 페이지네이션 로직, InventoryView의 대규모 UI 리팩토링, 비동기 데이터 플로우 관리, 다중 상태 관리 포함으로 각 계층별 상세 검토 필요.

Possibly related PRs

Poem

🐰 재고를 찾아 헤매던 날들이여,
이제 필터와 검색으로 명확하게,
수량 부족을 한눈에 알아채고,
스크롤 끝에 더 많은 물품들이 기다리네! ✨
재고 관리, 이제 정말 쉬워졌어! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning LoginView.swift의 비밀번호 길이 검증 제거 변경사항이 발견되었습니다. 이 변경사항은 재고 조회 기능 구현(이슈 #5)의 범위와 무관하며, 인증(로그인) 관련 기능 수정입니다. 나머지 모든 변경사항(InventoryApi, InventoryRepositoryImpl, InventoryRepositoryProtocol, InventoryViewModel, InventorySearchView, InventoryView, InventoryCardView)은 재고 조회 기능 구현과 직접 관련이 있습니다. LoginView.swift에서 비밀번호 검증 로직 제거 부분을 별도의 PR로 분리하거나, 이 변경사항이 다른 연결된 이슈의 요구사항인 경우 해당 이슈를 현재 PR에 연결하여 명확히 해주시기 바랍니다. 현재 PR은 재고 조회 기능 구현에 집중하도록 범위를 유지하는 것이 권장됩니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 56.25% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목 "[FEAT] 재고 조회 기능 구현"은 변경 사항의 주요 목적을 정확하게 요약합니다. 대부분의 변경사항(90% 이상)이 재고 조회 기능 구현과 직접적으로 관련되어 있으며, InventoryApi, InventoryViewModel, InventorySearchView, InventoryView 등의 주요 컴포넌트와 저장소 계층의 구현이 포함됩니다. 제목은 간결하고 명확하며 팀 동료가 변경 이력을 스캔할 때 기본 의도를 이해할 수 있습니다.
Linked Issues Check ✅ Passed 연결된 이슈 #5의 요구사항들이 코딩 관점에서 구현되었습니다. 재고 조회 API 연동 요구사항은 InventoryApi.swift의 getInventoryList, getUnderLimitList, findByName 메서드로 완성되었습니다. 부족재고 API 연동 및 UI 수정은 InventoryView 개선사항과 InventoryViewModel의 loadUnderLimitList 메서드로 구현되었습니다. 부품이름 검색 API 연결 및 UI 수정은 InventorySearchView와 InventoryViewModel의 searchByName 메서드로 적용되었습니다. 전체적으로 필터링, 검색, 페이지네이션, 동적 로딩 기능이 모두 구현되어 있습니다.
Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown

@rhkr8521, I'll review the changes in this PR.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 == nil
StockMate/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

📥 Commits

Reviewing files that changed from the base of the PR and between f9f066c and 05d7375.

📒 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 바인딩을 전달하는 방식, 의도 명확하고 결합도 적절합니다.

Comment thread StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift Outdated
@Yoo-Hyuna

Copy link
Copy Markdown
Collaborator Author

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

재고 조회 기능을 위해 UI 컴포넌트, 데이터/도메인 계층, 저장소 구현 및 뷰모델을 추가하고 검색·필터링·페이지네이션과 스크롤/탭 상호작용을 통합했습니다. 로그인 뷰의 폼 유효성 검사에서 비밀번호 길이 검증이 제거되었습니다.

Changes

Cohort / File(s) Summary
UI — 카드/리스트 컴포넌트
StockMate/StockMate/app/core/components/InventoryCardView.swift, StockMate/StockMate/app/core/components/InventoryListSection.swift 재고 항목 카드 뷰(이미지, 명칭, 설명, 상태 배지) 추가 및 리스트 섹션 플레이스홀더 컴포넌트 추가
UI — 검색 및 필터
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift 검색바, 다중 필터(FilterMenu), 동적 모델 리스트, 무한 스크롤/페이지네이션 및 로딩 상태를 처리하는 InventorySearchView 및 FilterMenu 추가
UI — 재고 메인 뷰 리팩토링
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift ScrollViewReader 기반 레이아웃, 스크롤 감지와 상단 스크롤 버튼, GridMenu 재구성, under-limit 아이템 표시 및 InventoryViewModel 연동으로 대규모 UI 재구성
네비게이션 변경
StockMate/StockMate/app/navigation/MainTabView.swift 탭 재선택 트리거(tabTappedTrigger) 추가 및 InventoryView에 바인딩 전달하여 탭 재선택 동작 처리
도메인 프로토콜
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift 재고 조회용 프로토콜 추가(목록, under-limit, 이름 기반 검색)
API 레이어
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift 페이지네이션 재고 목록, 수량 부족 목록, 이름 검색용 API 헬퍼와 데이터 모델(InventoryResponse, InventoryPageData, InventoryItem) 추가
저장소 구현
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift InventoryRepositoryProtocol 구현체 추가 — Api 요청 생성 및 safeApi를 통한 응답 디코딩/반환
뷰모델
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift 재고 데이터·필터·검색·페이지네이션 및 인증 오류 처리(401/403) 등을 담당하는 ObservableObject 뷰모델 추가
인증 UI 변경
StockMate/StockMate/app/feature/auth/ui/LoginView.swift onLoginSuccess 콜백 제거 및 폼 유효성 검사에서 비밀번호 길이 검증 제외(이메일 오류만 검증에 반영)

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor 사용자 as User
    participant SearchView as InventorySearchView
    participant ViewModel as InventoryViewModel
    participant Repo as InventoryRepositoryImpl
    participant API as InventoryApi
    participant Server

    User->>SearchView: 검색어 입력 / 필터 선택
    SearchView->>ViewModel: searchByName() / toggleCategory/Trim/Model()
    ViewModel->>Repo: findByName() 또는 getInventoryList()
    Repo->>API: 요청 생성 (쿼리 파라미터 포함)
    API->>Server: GET /inventory...
    Server-->>API: ApiResponse<InventoryPageData>
    API-->>Repo: 응답 전달
    Repo-->>ViewModel: AppResult 반환 (디코딩 완료)
    ViewModel->>SearchView: @Published 상태 갱신 (결과/로딩/hasMore)
    SearchView->>User: 결과 렌더링 (LazyVStack → InventoryCardView)
    User->>SearchView: 리스트 끝 도달
    SearchView->>ViewModel: 다음 페이지 로드 요청
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 재고를 찾아 폴짝폴짝 왔네,
필터와 검색으로 길을 열고,
부족한 수량은 빨갛게 알리고,
스크롤 끝엔 더 많은 항목들이 반겨,
당근 한 움큼 들고 이제 관리하자!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning LoginView.swift의 변경사항이 이슈 #5의 범위를 벗어납니다. onLoginSuccess 콜백 제거와 isValidForm에서 pwError 검증 제거는 재고 조회 기능 구현과 무관한 인증 관련 변경입니다. PR의 walkthrough에서도 "Minor auth change"로 언급되었으며, 재고 조회 기능과 직접적인 연관이 없으므로 분리되거나 별도의 정당화가 필요합니다. 나머지 변경사항들(InventoryCardView, InventorySearchView, InventoryApi, InventoryViewModel 등)은 모두 재고 조회 기능 구현과 관련된 범위 내 변경입니다. LoginView.swift의 password 검증 제거 변경사항을 제거하거나 별도의 PR로 분리하는 것을 권장합니다. 만약 이 변경이 필요한 경우 변경의 정당한 이유와 함께 별도의 이슈를 작성하거나 이 변경사항이 왜 현재 PR에 포함되어야 하는지 설명하는 코멘트를 추가해야 합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목 "[FEAT] 재고 조회 기능 구현"은 changeset의 주요 목표를 명확하게 설명합니다. 제목은 변경사항의 핵심인 재고 조회 기능 구현을 직접 언급하며, InventoryCardView, InventorySearchView, InventoryViewModel 등 새로운 재고 조회 컴포넌트들의 추가와 InventoryView의 검색, 필터링, 무한 스크롤 기능 추가를 모두 포함하고 있습니다. 제목은 간결하고 구체적이며, 팀원이 변경 사항을 빠르게 파악할 수 있습니다.
Linked Issues Check ✅ Passed PR의 모든 코딩 관련 요구사항이 충족되었습니다. 이슈 #5에서 요청한 "지점별 재고 조회 API 연동"은 InventoryApi (getInventoryList, getUnderLimitList, findByName)와 InventoryRepositoryProtocol/InventoryRepositoryImpl을 통해 구현되었고, "재고 조회 페이지 UI 구현"은 InventoryCardView, InventoryListSection, InventorySearchView, 그리고 업데이트된 InventoryView를 통해 검색창, 필터링, 무한 스크롤과 함께 완성되었습니다. InventoryViewModel은 필터링, 페이지네이션, 검색 및 상태 관리를 모두 제공합니다.
Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown

@Yoo-Hyuna, 리뷰를 시작하겠습니다. 재고 조회 기능 구현에 대한 상세한 코드 리뷰를 진행하겠습니다.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 05d7375 and b202240.

📒 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

Comment thread StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift Outdated
Comment on lines +226 to +233
var truncatedTitle: String {
// 글자 6자까지만 표시, 이후 "..." 처리
if displayTitle.count > 6 {
let prefix = displayTitle.prefix(5)
return "\(prefix)…"
}
return displayTitle
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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).

@Yoo-Hyuna

Copy link
Copy Markdown
Collaborator Author

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

재고 조회 기능을 위해 UI 컴포넌트(카드/검색/메인 뷰), 데이터·도메인 계층(API 모델, 리포지토리, 프로토콜) 및 InventoryViewModel을 추가·연동하고, 네비게이션 탭 재선택 동작과 로그인 뷰의 폼 유효성 변경을 포함한 리팩터를 적용했습니다.

Changes

Cohort / File(s) Summary
UI — 카드/리스트 컴포넌트
StockMate/StockMate/app/core/components/InventoryCardView.swift, StockMate/StockMate/app/core/components/InventoryListSection.swift 재고 항목 카드 뷰(이미지, 명칭, 설명, 상태 배지) 추가 및 간단한 리스트 섹션 플레이스홀더 컴포넌트 추가
UI — 검색/필터 컴포넌트
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift 검색바, 다중 필터(FilterMenu), 동적 모델 옵션, 무한 스크롤/페이지네이션, 로딩 상태 처리하는 InventorySearchView 및 FilterMenu 추가
UI — 재고 메인 뷰 리팩토링
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift ScrollViewReader 기반 레이아웃, 스크롤 감지(상단 버튼), GridMenu 재구성, under-limit 항목 표시 및 InventoryViewModel 연동으로 UI 구조 변경
네비게이션 변경
StockMate/StockMate/app/navigation/MainTabView.swift 탭 재선택 트리거(tabTappedTrigger) 추가 및 InventoryView에 바인딩 전달해 탭 재선택 동작 처리
도메인 프로토콜
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift 재고 조회용 프로토콜 추가(목록, under-limit, 이름 기반 검색)
API 레이어
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift 페이지네이션 재고 목록, 수량 부족 목록, 이름 검색용 API 헬퍼와 데이터 모델(InventoryResponse, InventoryPageData, InventoryItem) 추가
저장소 구현
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift InventoryRepositoryProtocol 구현체 추가 — API 요청 생성 및 safeApi 통해 응답 디코딩/반환
뷰모델
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift 재고 데이터·필터·검색·페이지네이션 및 인증 오류 처리(401/403) 등을 담당하는 ObservableObject 뷰모델 추가
인증 UI 변경
StockMate/StockMate/app/feature/auth/ui/LoginView.swift onLoginSuccess 콜백 제거 및 폼 유효성 검사에서 비밀번호 길이 검증 제외(이메일 오류만 유효성에 반영)

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor 사용자 as User
    participant SearchView as InventorySearchView
    participant ViewModel as InventoryViewModel
    participant Repo as InventoryRepositoryImpl
    participant API as InventoryApi
    participant Server

    User->>SearchView: 검색어 입력 / 필터 선택
    SearchView->>ViewModel: searchByName(name) / toggleFilter(...)
    ViewModel->>Repo: findByName() or getInventoryList()/getUnderLimitList()
    Repo->>API: build request (query params)
    API->>Server: GET /inventory...
    Server-->>API: ApiResponse<InventoryPageData>
    API-->>Repo: DataRequest / 응답 전달
    Repo-->>ViewModel: AppResult<ApiResponse<InventoryPageData>>
    ViewModel->>SearchView: @Published 업데이트 (items, loading, hasMore)
    SearchView->>User: LazyVStack 렌더링 (InventoryCardView)
    Note right of SearchView: 리스트 끝 도달 시 onAppear → 다음 페이지 로드 트리거
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • rhkr8521

Poem

🐰 재고 찾아 폴짝 폴짝 왔네,
필터로 골라 검색해 빛나네,
부족하면 빨갛게 알려주고,
스크롤 끝엔 더 불러오는 꿈,
당근 들고 오늘도 정리하자!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR contains out-of-scope changes in LoginView.swift that are unrelated to the inventory search feature defined in linked issue #5. Specifically, the removal of the onLoginSuccess callback property and the disabling of password-length validation in the isValidForm method fall outside the scope of implementing inventory search functionality. These authentication-related modifications appear to be unrelated to the core objective of issue #5, which focuses solely on implementing the 재고 조회 기능 (inventory search feature). While the PR review notes flagged this as a warning, these changes represent a clear deviation from the stated issue scope. To maintain scope clarity and follow the single-responsibility principle for pull requests, the LoginView.swift modifications should be extracted into a separate PR with its own issue or justification. If these changes are necessary dependencies for the inventory search feature, that relationship should be explicitly documented. Alternatively, these changes can be reverted from this PR to keep it focused on the inventory search implementation objective.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "[FEAT] 재고 조회 기능 구현" (Implement inventory search feature) directly aligns with the main body of changes in the changeset. The majority of modifications—including InventoryApi.swift, InventoryRepositoryProtocol.swift, InventoryRepositoryImpl.swift, InventoryViewModel.swift, InventorySearchView.swift, InventoryView.swift, and related UI components—are all focused on implementing the inventory search functionality. The title is concise, clear, and accurately reflects the primary objective of the PR. While the PR also includes a minor modification to LoginView.swift (removing password validation and the onLoginSuccess callback), this represents a secondary change that does not contradict or obscure the main intent captured by the title.
Linked Issues Check ✅ Passed The PR successfully addresses the objectives defined in linked issue #5. The issue specifies two requirements: API integration for branch-specific inventory search (marked complete in the issue) and UI implementation for the inventory search page. The PR fulfills both through: API integration via InventoryApi.swift with methods for getInventoryList, getUnderLimitList, and findByName; data layer implementation with InventoryRepositoryProtocol and InventoryRepositoryImpl; state management through InventoryViewModel supporting filtering, pagination, and search; and comprehensive UI implementation with InventorySearchView (including search bar, FilterMenu component, and infinite scroll), InventoryView refactoring (scroll detection, scroll-to-top button, dynamic grid), and supporting components like InventoryCardView. All changes work together to enable the inventory search functionality required by the issue.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown

@Yoo-Hyuna, 재고 조회 기능 구현에 대한 상세한 코드 리뷰를 시작하겠습니다.

PR에 포함된 다층 아키텍처 변경사항들(UI 컴포넌트, API 계층, 도메인 프로토콜, 저장소 구현, 뷰모델)을 검토하고, 필터링/페이지네이션/검색 로직, 그리고 사전 점검에서 확인된 LoginView.swift의 범위 외 변경사항에 대해서도 상세히 리뷰하겠습니다.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between b202240 and 3b00a93.

📒 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 분기 등 과거 피드백이 반영되었습니다. 제안 사항만 반영되면 안정적으로 보입니다.

Comment on lines +94 to +101
.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) }
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

검색 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.

Comment on lines +144 to +151
Button(action: {
inventoryViewModel.resetFilters(with: searchText)
}) {
HStack(spacing: 4) {
Image(systemName: "arrow.counterclockwise")
.font(.system(size: 13))
Text("초기화")
.font(.system(size: 13, weight: .medium))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.swift

Length 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.

Comment on lines +173 to +183
.onAppear {
if inventoryViewModel.isSearching {
if item.id == inventoryViewModel.filteredSearchResults.last?.id,
inventoryViewModel.searchHasMore {
Task {
await inventoryViewModel
.searchByName(
name: searchText.trimmingCharacters(in: .whitespacesAndNewlines)
)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

무한 스크롤: 빈 검색어로 서버 호출 방지 가드 추가

검색어를 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).

@Yoo-Hyuna

Copy link
Copy Markdown
Collaborator Author

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

재고 조회 기능을 위해 UI 컴포넌트(카드/검색/메인 뷰), 데이터·도메인 계층(API 모델, 리포지토리, 프로토콜) 및 InventoryViewModel을 추가·연동하고, 네비게이션 탭 재선택 트리거와 로그인 뷰의 폼 유효성 변경(패스워드 검증 제외)을 포함한 리팩터를 적용했습니다.

Changes

Cohort / File(s) Summary
UI — 카드/리스트 컴포넌트
StockMate/StockMate/app/core/components/InventoryCardView.swift, StockMate/StockMate/app/core/components/InventoryListSection.swift 재고 항목 카드 뷰(이미지, 명칭, 설명, 상태 배지) 추가 및 간단한 리스트 섹션 플레이스홀더 컴포넌트 추가
UI — 검색/필터 컴포넌트
StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift 검색바, 다중 필터(FilterMenu), 동적 모델 옵션, 무한 스크롤/페이지네이션, 로딩 상태 처리하는 InventorySearchView 및 FilterMenu 추가
UI — 재고 메인 뷰 리팩토링
StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift ScrollViewReader 기반 레이아웃, 스크롤 감지(상단 버튼), GridMenu 재구성, under-limit 항목 표시 및 InventoryViewModel 연동으로 UI 구조 변경; StockShortageCard 제거
탭 네비게이션 변경
StockMate/StockMate/app/navigation/MainTabView.swift 탭 재선택 트리거(tabTappedTrigger) 추가 및 InventoryView에 바인딩 전달해 탭 재선택 시 스크롤/포커스 동작 트리거 가능
도메인 프로토콜
StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift 재고 조회용 프로토콜 추가(목록, under-limit, 이름 기반 검색)
API 레이어
StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift 페이지네이션 재고 목록, 수량 부족 목록, 이름 검색용 API 헬퍼와 데이터 모델(InventoryResponse, InventoryPageData, InventoryItem) 추가
저장소 구현
StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift InventoryRepositoryProtocol 구현체 추가 — API 요청 생성 및 safeApi 통해 응답 디코딩/반환
뷰모델
StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift 재고 데이터·필터·검색·페이지네이션 및 인증 오류 처리(401/403) 등을 담당하는 ObservableObject 뷰모델 추가
인증 UI 변경
StockMate/StockMate/app/feature/auth/ui/LoginView.swift onLoginSuccess 콜백 제거 및 폼 유효성 검사에서 비밀번호 길이 검증 제외(이메일 오류만 유효성에 반영)

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(name) / toggleCategory/Trim/Model
    VM->>Repo: findByName() or getInventoryList()/getUnderLimitList()
    Repo->>API: build & execute request (query params)
    API->>Server: GET /inventory...
    Server-->>API: ApiResponse<InventoryPageData>
    API-->>Repo: DataRequest / 응답 전달
    Repo-->>VM: AppResult<ApiResponse<InventoryPageData>>
    VM-->>SearchView: @Published 업데이트 (items, loading, hasMore)
    SearchView->>User: LazyVStack 렌더링 (InventoryCardView)
    Note right of SearchView: 리스트 끝 onAppear → VM에 다음 페이지 로드 요청
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • rhkr8521

Poem

🐰 재고 찾아 폴짝 폴짝 왔네,
필터로 골라 검색해 빛나네,
부족하면 빨갛게 알려주고,
스크롤 끝엔 더 불러오는 꿈,
당근 들고 오늘도 정리하자!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning PR은 주로 재고 조회 기능 구현과 관련된 변경사항을 포함하고 있으나, LoginView.swift에서 이슈 범위와 무관한 변경사항이 발견됩니다. 구체적으로 onLoginSuccess 콜백 제거, 비밀번호 길이 검증 제거(이메일 형식 검증만 유지)가 포함되어 있습니다. 이러한 인증 UI 변경사항은 재고 조회 기능 구현(이슈 #5)과 직접적인 관련이 없으며, PR 리뷰 코멘트에서도 "Out of Scope Changes Warning"으로 지적되었습니다. LoginView.swift의 변경사항(onLoginSuccess 제거 및 비밀번호 검증 제거)을 별도의 PR로 분리하거나, 이러한 변경이 필요한 이유를 명확히 문서화하고 관련 이슈 또는 요구사항을 링크해야 합니다. 현재 상태에서는 이 변경사항들이 재고 조회 기능 구현의 필수 요소가 아니므로 범위 외 변경으로 간주됩니다.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목 "[FEAT] 재고 조회 기능 구현"은 변경사항의 주요 목표를 명확하게 반영합니다. 풀 리퀘스트는 재고 조회 기능을 구현하기 위해 API 연동(InventoryApi), 데이터 모델, 리포지토리 계층, UI 컴포넌트(InventoryCardView, InventorySearchView, InventoryView 리팩토링), 뷰모델, 그리고 네비게이션 업데이트를 포함하고 있습니다. 제목은 간결하고 구체적이며 가독성이 좋아서, 팀원이 히스토리를 스캔할 때 이 PR의 주요 변경사항을 쉽게 이해할 수 있습니다.
Linked Issues Check ✅ Passed 이슈 #5의 요구사항은 "재고 조회 페이지 UI 구현"과 "지점별 재고 조회 API 연동" 두 가지입니다. 본 PR은 두 요구사항을 모두 충족합니다. API 연동 측면에서는 InventoryApi.swift에서 getInventoryList, getUnderLimitList, findByName 메서드를 구현하고, InventoryRepositoryProtocol과 InventoryRepositoryImpl을 통해 도메인 계층을 완성했습니다. UI 구현 측면에서는 InventoryCardView, InventoryListSection, InventorySearchView를 신규 추가하고, InventoryView를 리팩토링하여 검색, 필터링, 무한 스크롤을 지원하는 인벤토리 조회 페이지를 완성했습니다. 또한 InventoryViewModel을 통해 상태 관리, 필터링, 페이지네이션, 검색 기능을 모두 구현했습니다.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 20, 2025

Copy link
Copy Markdown

@Yoo-Hyuna, 재고 조회 기능 구현에 대한 코드 리뷰를 시작하겠습니다.

PR의 변경사항들을 검토하겠습니다:

  • UI 컴포넌트 (InventoryCardView, InventorySearchView, InventoryView)
  • 데이터/도메인 계층 (InventoryApi, RepositoryProtocol/Impl)
  • InventoryViewModel의 상태 관리 및 비즈니스 로직
  • 필터링, 페이지네이션, 검색 기능 구현

참고로, 사전 점검에서 LoginView.swift의 변경사항(onLoginSuccess 제거 및 비밀번호 검증 제거)이 이슈 #5의 범위를 벗어난다는 경고가 있습니다. 이 부분에 대한 검토도 포함하여 상세한 리뷰를 진행하겠습니다.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3b00a93 and 8b8b5ba.

📒 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 상태만 확인하도록 단순화되어 있습니다. 이전 리뷰에서 hasMoresearchHasMore를 구분하여 확인하도록 제안했지만, 현재 구현이 더 단순하고 명확합니다. 로딩 중일 때 항상 인디케이터를 표시하는 것이 합리적입니다.

Comment on lines +44 to +50
TextField("부품을 검색하세요.", text: $searchText)
.textFieldStyle(PlainTextFieldStyle())
.onSubmit {
Task {
await inventoryViewModel.searchByName(name: searchText, reset: true)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

검색 실행 시 공백 제거 누락

Line 48에서 searchByNamesearchText를 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: - 부족 재고 로드

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
// 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).

Comment on lines +187 to +200
// 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)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

미사용 메서드 제거 검토

searchInFilteredList 메서드가 로컬 필터링을 수행하지만, 현재 검색 흐름에서는 서버 측 검색(searchByName)을 사용하고 있어 이 메서드가 호출되지 않는 것으로 보입니다. 또한 주석 처리된 필터 조건들이 있어 미완성 코드처럼 보입니다.

다음 스크립트를 실행하여 이 메서드의 사용 여부를 확인하세요:

만약 사용되지 않는다면 제거하고, 향후 사용할 계획이 있다면 TODO 주석으로 명시하는 것을 권장합니다.


🏁 Script executed:

#!/bin/bash
# searchInFilteredList 메서드 호출 위치 검색
rg -n 'searchInFilteredList' --type swift

Length of output: 129


추가 검증을 위해 전체 프로젝트에서 더 광범위한 검색을 진행하겠습니다.


🏁 Script executed:

#!/bin/bash
# searchInFilteredList 메서드가 정의된 곳 제외하고 모든 참조 확인
rg -n 'searchInFilteredList' --type swift -C 3

Length of output: 802


🏁 Script executed:

#!/bin/bash
# isSearching과 searchResults 변수가 다른 메서드에서 설정되는지 확인
rg -n 'isSearching\s*=' --type swift -C 2 | head -50

Length of output: 4764


🏁 Script executed:

#!/bin/bash
# 서버 측 검색 메서드(searchByName) 사용 확인
rg -n 'searchByName' --type swift -C 2

Length of output: 2368


🏁 Script executed:

#!/bin/bash
# 검색 관련 메서드 목록 확인
rg -n 'func.*search' --type swift -C 1

Length of output: 1476


🏁 Script executed:

#!/bin/bash
# InventoryViewModel.swift 파일 전체 구조 확인
fd -e swift -x wc -l {} \; | grep -i inventory

Length of output: 685


🏁 Script executed:

#!/bin/bash
# InventoryViewModel.swift에서 search 관련 메서드 모두 찾기
rg -n 'func.*[Ss]earch' StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift

Length of output: 240


🏁 Script executed:

#!/bin/bash
# searchInFilteredList 정의 주변 코드 확인
rg -n 'searchInFilteredList' --type swift -A 20 -B 3

Length of output: 2765


🏁 Script executed:

#!/bin/bash
# 전체 파일에서 searchInFilteredList 참조 수 확인
rg 'searchInFilteredList' --type swift | wc -l

Length of output: 1


🏁 Script executed:

#!/bin/bash
# searchResults 변수 초기화 및 할당 패턴 확인
rg -n 'searchResults\s*=' StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift -C 2

Length of output: 474


🏁 Script executed:

#!/bin/bash
# 다른 검색 메서드와 비교: searchByName 정의 확인
rg -n 'func searchByName' --type swift -A 15

Length 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 swift

Length of output: 131


🏁 Script executed:

#!/bin/bash
# 혹시 문자열 중에 메서드명이 있는지 확인
rg -n '"searchInFilteredList"' . --type swift

Length of output: 0


🏁 Script executed:

#!/bin/bash
# 메서드 포인터나 셀렉터로 참조되는지 확인
rg -n '#selector.*searchInFilteredList' . --type swift

Length 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.

Comment on lines +250 to +271
// 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) }
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

검색어 공백 제거 누락

Line 269에서 searchByNamesearchText를 그대로 전달하고 있습니다. 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.

Suggested change
// 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.

@Yoo-Hyuna Yoo-Hyuna merged commit 4568a32 into dev Oct 20, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants