Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughKakao 주소 검색 통합과 지도/리스트를 모의 데이터에서 API 기반 실시간 페칭으로 전환합니다. 주소 검색 UI, 다수의 React Query 훅, 지도 마커/바텀시트 연동, 상세 조회 훅 및 타입·모델 확장이 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant MainPageClient as MainPageClient<br/>(Client)
participant AddressSheet as AddressSearchBottomSheet<br/>(UI)
participant KakaoAPI as Kakao<br/>Geocoder
participant QueryHooks as Store Query Hooks
participant KakaoMap as KakaoMap<br/>Component
participant MainList as MainListView<br/>Component
User->>MainPageClient: 앱 로드 / 위치 권한 수락
MainPageClient->>QueryHooks: useStores(lat, lon) (기본 호출)
QueryHooks-->>MainPageClient: 근처 가게 목록 반환
User->>AddressSheet: 주소 입력
AddressSheet->>KakaoAPI: searchAddressByKakao(keyword)
KakaoAPI-->>AddressSheet: 주소 결과
User->>AddressSheet: 주소 선택
AddressSheet->>MainPageClient: onSelectAddress(item)
MainPageClient->>QueryHooks: useStoresByAddress(address) (전환)
QueryHooks-->>MainPageClient: 주소 기반 가게 목록
MainPageClient->>KakaoMap: selectedAddress + filteredStores
KakaoMap->>KakaoMap: 선택 마커/모든 마커 업데이트
MainPageClient->>MainList: stores + filters
MainList-->>User: 리스트 렌더링
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50분 Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts (1)
1-8:⚠️ Potential issue | 🔴 Critical제거된 타입으로 인해 TypeScript 컴파일이 실패합니다.
store-detail.ts에서DayKey만 export하고 있으나, 아래 6개 파일에서 존재하지 않는StoreBusinessHour,StoreMenuItem,StoreDetailItem을 import하고 있습니다:
apps/customer/src/app/(tabs)/main/store/_constants/mockStoreDetail.tsapps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsxapps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal.tsx이들 타입을
store-detail.ts에 추가하거나, 다른 파일에서 재정의한 후 import 경로를 수정해야 합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/store/_types/store-detail.ts around lines 1 - 8, The build fails because only DayKey is exported from store-detail.ts while consumers expect types StoreBusinessHour, StoreMenuItem, and StoreDetailItem; restore (or add) those missing type definitions and export them from store-detail.ts with the exact names imported by StoreMenuCard, StoreDetailContent, PurchaseContent, PurchaseOrderCard, and PurchaseCompleteModal, or alternatively define matching types in those files and update their import paths to the new location; ensure the shapes (fields) match how those components use the types and export them alongside DayKey.
🧹 Nitpick comments (6)
apps/customer/src/app/(tabs)/main/_apis/searchAddressByKakao.ts (1)
52-53: ID 생성 방식 검토.
id: \${item.x}-${item.y}-${index}`로 ID를 생성하고 있습니다. 동일한 좌표를 가진 결과가 다른 검색에서 반환될 경우 index가 다르면 다른 ID가 생성됩니다. 현재 사용 맥락에서는 문제가 없어 보이지만, ID의 일관성이 필요한 경우item.address_name`을 포함하는 것을 고려해 보세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/_apis/searchAddressByKakao.ts around lines 52 - 53, The current ID construction in the mapping (id: `${item.x}-${item.y}-${index}`) can vary across searches for identical coordinates; update the ID generation inside the function that maps Kakao results (the mapping callback where item is used) to include a stable field such as item.address_name (e.g., combine item.x, item.y and item.address_name) instead of the index so IDs remain consistent across searches; ensure you normalize or trim item.address_name if necessary to avoid unsafe characters.apps/customer/src/app/(tabs)/main/_components/KakaoMap.tsx (1)
143-152: 중복 헬퍼 함수 통합을 권장합니다.
mapServerTagToMainCategory:MainPageClient.tsx의 동일 함수와 중복readTodayBusinessHours:MainStoreCard.tsx의formatBusinessHours,StoreDetailContent.tsx의 business hours 로직과 유사공통 유틸리티 모듈로 추출하여 일관성과 유지보수성을 높이는 것을 권장합니다.
Also applies to: 158-184
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/_components/KakaoMap.tsx around lines 143 - 152, The code duplicates helper logic (e.g., mapServerTagToMainCategory in KakaoMap.tsx and MainPageClient.tsx, and business-hours logic like readTodayBusinessHours / formatBusinessHours used in MainStoreCard.tsx and StoreDetailContent.tsx); extract these helpers into a shared utility module (e.g., storeUtils or businessHoursUtils), implement and export functions such as mapServerTagToMainCategory and readTodayBusinessHours there, update KakaoMap.tsx, MainPageClient.tsx, MainStoreCard.tsx, and StoreDetailContent.tsx to import and use the shared utilities, and remove the duplicated local implementations to ensure a single source of truth.apps/customer/src/app/(tabs)/main/_components/MainListView.tsx (1)
48-54: 로딩 상태와 빈 결과 처리.로딩 중 상태 표시가 있으나, 로딩 완료 후
stores가 빈 배열일 경우에 대한 빈 상태(empty state) UI가 없습니다. 사용자 경험 개선을 위해 검색 결과가 없을 때 안내 메시지 표시를 고려해 보세요.💡 빈 상태 UI 추가 제안
{isLoading ? ( <div className="body2-r text-gray-600">로딩중...</div> +) : stores.length === 0 ? ( + <div className="body2-r text-gray-600">검색 결과가 없습니다.</div> ) : ( stores.map((item) => ( <MainStoreCard key={item.storeId} item={item} /> )) )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/_components/MainListView.tsx around lines 48 - 54, The component rendering in MainListView currently shows a loading indicator when isLoading is true and otherwise maps stores to MainStoreCard, but it lacks an empty-state UI when stores is an empty array; update the render logic in MainListView (the conditional using isLoading, stores, and MainStoreCard) to check for stores.length === 0 after loading completes and render a user-friendly empty message/component (e.g., a div with guidance or a dedicated EmptyState component) instead of rendering nothing; ensure the key path still uses item.storeId and preserve existing loading behavior.apps/customer/src/app/(tabs)/main/_components/MainStoreCard.tsx (2)
26-35: 이미지 렌더링 개선이 적절합니다.
storeImage존재 여부에 따른 조건부 렌더링과 Gift 아이콘 폴백 처리가 잘 구현되어 있습니다. 다만, Next.js 프로젝트에서 외부 이미지를 렌더링할 때는next/image의Image컴포넌트 사용을 고려해 보세요. 이미지 최적화와 레이아웃 시프트 방지에 도움이 됩니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/_components/MainStoreCard.tsx around lines 26 - 35, MainStoreCard currently uses a plain <img> tag to render item.storeImage; in a Next.js app replace this with next/image's Image component (import Image from 'next/image') inside the same conditional so you still render Icon as a fallback when item.storeImage is falsey, provide explicit width and height (or use fill with parent position:relative) to avoid layout shift, set objectFit via style/className to maintain cover behavior, and ensure external image domains are allowed in next.config.js if the URLs are remote; keep alt={item.storeName} and preserve the surrounding container and accessibility attributes.
72-102: 영업시간 포맷 로직이 여러 파일에 중복됩니다.
formatBusinessHours함수가KakaoMap.tsx의readTodayBusinessHours,StoreDetailContent.tsx의normalizeBusinessHours와 유사한 로직을 구현하고 있습니다. 공통 유틸리티 함수로 추출하여 재사용성을 높이는 것을 권장합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/_components/MainStoreCard.tsx around lines 72 - 102, Extract the duplicated business-hour parsing logic into a single shared utility (e.g., create a function like formatBusinessHours or getTodayBusinessHours in a common utils module) and replace the similar implementations in formatBusinessHours, readTodayBusinessHours, and normalizeBusinessHours with calls to that utility; ensure the new utility accepts the same input shape (raw?: unknown or Record<string,string>), handles invalid input by returning the "영업시간 정보 없음" fallback, maps weekday strings (Mon..Sun) to keys (mon..sun) using the same Asia/Seoul timeZone logic, and returns the same output string format (e.g., "영업중 {hours}") so existing consumers need only swap their local logic for the shared call.apps/customer/src/app/(tabs)/main/_components/MainPageClient.tsx (1)
109-111: "추천순" 정렬 로직 확인이 필요합니다.현재 "추천순"이
storeId내림차순으로 구현되어 있습니다. 이는 실제 추천 로직이 아닌 임시 구현으로 보입니다. 추후 실제 추천 알고리즘이나 서버 기반 정렬이 필요한 경우 변경이 필요할 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/_components/MainPageClient.tsx around lines 109 - 111, The "추천순" branch in MainPageClient.tsx currently sorts by storeId (items.sort((a, b) => b.storeId - a.storeId)), which is a placeholder and incorrect; update the selectedSort === "추천순" handling to use a real recommendation metric (e.g., a recommendationScore field on item) or call the server API to return pre-sorted results instead of using storeId, and if that field/API is not yet available add a clear TODO comment and fall back to a stable default (e.g., no-op or sort by createdAt/popularity) in the sort branch (refer to selectedSort check and the items.sort call in MainPageClient.tsx).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/customer/src/app/`(tabs)/main/_components/AddressSearchBottomSheet.tsx:
- Around line 50-60: The async search block can be overwritten by out-of-order
responses; add a request identifier or cancellation flag to ensure only the
latest request updates state: create a ref like latestRequestIdRef and increment
it before calling searchAddressByKakao (or create an AbortController if
searchAddressByKakao supports aborting) then capture the current id in the async
callback and, before calling setResults/setIsSearching, verify the id matches
latestRequestIdRef and that the sheet is still open (use an isOpen or isMounted
ref); also ensure any previous pending request is aborted or ignored when the
sheet closes. Use the existing debounceRef and
trimmedKeyword/searchAddressByKakao/setResults/setIsSearching identifiers to
locate where to add this logic.
In `@apps/customer/src/app/`(tabs)/main/_components/KakaoMap.tsx:
- Around line 154-156: The function readStoreAddress is defined but never used;
remove the unused function declaration (readStoreAddress) from the KakaoMap
component file to eliminate dead code and keep the module clean, or if it is
intended for future use, add a clear TODO and export/usage where applicable
(e.g., replace inline address logic with a call to readStoreAddress in the
rendering or helper that builds store addresses).
In `@apps/customer/src/app/`(tabs)/main/_components/MainPageClient.tsx:
- Around line 158-167: The function mapServerTagToMainCategory has indentation
issues and is duplicated in KakaoMap.tsx; fix the formatting in this file
(correct function indentation and ensure exhaustive switch handling) and extract
the function into a shared utility (e.g., create and export
mapServerTagToMainCategory from a new/common util module) then import and use
that exported function in both MainPageClient.tsx and KakaoMap.tsx, keeping the
same type signatures (StoreTag and MainCategory) and updating exports/imports
accordingly.
In `@apps/customer/src/app/`(tabs)/main/_components/MainStoreCard.tsx:
- Around line 61-70: The function mapServerTagToLabel has inconsistent
indentation violating the project's 2-space style; reformat the function body
(including the switch, case labels "CAFE", "BAKERY", "RESTAURANT", and their
return lines) to use 2-space indentation throughout to satisfy ESLint rules for
apps/customer/src/app/(tabs)/main/_components/MainStoreCard.tsx and ensure no
other spacing errors remain in mapServerTagToLabel.
In `@apps/customer/src/app/`(tabs)/main/_components/MapStoreBottomSheet.tsx:
- Around line 76-78: The span in MapStoreBottomSheet currently renders {email ??
"이메일 정보 없음"} which only handles null/undefined; update the conditional to also
treat empty-string (and optionally whitespace) as missing by checking email (or
email?.trim()) and falling back to "이메일 정보 없음" so empty values won't render
blank text; change the expression around the span (the email variable usage) to
use this combined check.
- Around line 88-90: In MapStoreBottomSheet.tsx update the UI text that
currently forces "영업중" when businessHours exists: instead of unconditionally
showing "영업중 {businessHours}" in the span, either compute real open/closed state
(using the component's business hours parsing logic) and render "영업중" only when
currently open, or—if you do not implement state calculation now—change the copy
to a neutral label like "영업시간 {businessHours}" so the UI does not mislead;
locate the span rendering in MapStoreBottomSheet and adjust the displayed string
accordingly.
In `@apps/customer/src/app/`(tabs)/main/store/_components/StoreDetailContent.tsx:
- Around line 224-237: The StoreMenuCard is missing required fields when mapping
RandomBoxRespDTO → StoreMenuItem in StoreDetailContent: include remainingCount,
pickupStartTime, and pickupEndTime in the object passed to StoreMenuCard inside
the menus.map so StoreMenuCard doesn't render "undefined"; use the corresponding
RandomBoxRespDTO properties (e.g., menu.remainingCount, menu.pickupStartTime,
menu.pickupEndTime) or provide sensible fallbacks (0 for remainingCount and
empty string or formatted defaults for pickupStartTime/pickupEndTime) when those
fields are absent, and adjust the StoreMenuItem typing if needed so
StoreMenuCard and RandomBoxRespDTO align.
- Around line 255-263: The readJibunAddress function contains unnecessary
defensive casting and an incorrect typo variant (jibunAddres) that doesn't exist
elsewhere; simplify it by removing the extra type assertions and the fallback to
the misspelled property and return store?.jibunAddress ?? "" directly, keeping
the function signature and using the StoreRespDTO type.
In `@apps/customer/src/app/`(tabs)/main/store/[id]/page.tsx:
- Around line 16-20: The current check using Number.isNaN allows invalid values
like -1, 1.5, or Infinity to pass; update the validation around storeId (the
value passed into StoreDetailContent) to ensure it's a finite positive integer
by verifying Number.isFinite(storeId) && Number.isInteger(storeId) && storeId >
0, and call notFound() when that condition fails instead of only checking
Number.isNaN; adjust the logic where storeId is derived/parsed so the same
validation is applied before rendering StoreDetailContent.
In `@apps/customer/src/shared/types/kakao.d.ts`:
- Around line 87-103: 빈 인터페이스 선언(KakaoLatLng, KakaoMarkerImage, KakaoSize,
KakaoPoint)을 타입 별칭으로 바꿔주세요: 각 interface 대신 "type <Name> = object" 또는 "type
<Name> = unknown" 형태로 선언하고 기존 참조(예: KakaoMapInstance.setCenter,
KakaoMarker.setPosition 등)는 그대로 유지해서 타입 호환이 되도록 합니다; 빈 interface 선언문은 제거하고 필요하면
린트 규칙 비활성화 대신 타입 별칭을 사용하세요.
In `@packages/api/src/models/store.ts`:
- Around line 23-32: RandomBoxRespDTO is missing fields (remainingCount,
pickupStartTime, pickupEndTime) that StoreMenuCard and StoreDetailContent.tsx
expect, and StoreMenuItem is not defined in store-detail.ts despite being
imported elsewhere; fix by updating the RandomBoxRespDTO interface to include
remainingCount:number, pickupStartTime:string|Date, pickupEndTime:string|Date
(or nullable types matching API), or create a conversion function that maps
RandomBoxRespDTO -> StoreMenuItem populating those fields, and add a proper
StoreMenuItem type definition in store-detail.ts (or a shared types file) that
matches StoreMenuCard's props so all components import the correct type; ensure
StoreDetailContent.tsx uses the converter or the extended DTO to avoid passing
undefined values at runtime.
In `@packages/design-system/src/components/Header/Header.tsx`:
- Line 43: The input currently only handles mouse interaction via onInputClick;
add a keyboard handler so read-only inputs acting as selection triggers are
usable via keyboard: add an onKeyDown on the same input that checks for Enter or
Space when inputReadOnly is true, preventDefault for Space, and invoke
onInputClick (casting the KeyboardEvent to the expected event type or adapting
the handler call) so keyboard users can activate the control; reference the
inputReadOnly prop and the onInputClick handler in Header.tsx and ensure the new
onKeyDown logic coexists with the existing onClick.
---
Outside diff comments:
In `@apps/customer/src/app/`(tabs)/main/store/_types/store-detail.ts:
- Around line 1-8: The build fails because only DayKey is exported from
store-detail.ts while consumers expect types StoreBusinessHour, StoreMenuItem,
and StoreDetailItem; restore (or add) those missing type definitions and export
them from store-detail.ts with the exact names imported by StoreMenuCard,
StoreDetailContent, PurchaseContent, PurchaseOrderCard, and
PurchaseCompleteModal, or alternatively define matching types in those files and
update their import paths to the new location; ensure the shapes (fields) match
how those components use the types and export them alongside DayKey.
---
Nitpick comments:
In `@apps/customer/src/app/`(tabs)/main/_apis/searchAddressByKakao.ts:
- Around line 52-53: The current ID construction in the mapping (id:
`${item.x}-${item.y}-${index}`) can vary across searches for identical
coordinates; update the ID generation inside the function that maps Kakao
results (the mapping callback where item is used) to include a stable field such
as item.address_name (e.g., combine item.x, item.y and item.address_name)
instead of the index so IDs remain consistent across searches; ensure you
normalize or trim item.address_name if necessary to avoid unsafe characters.
In `@apps/customer/src/app/`(tabs)/main/_components/KakaoMap.tsx:
- Around line 143-152: The code duplicates helper logic (e.g.,
mapServerTagToMainCategory in KakaoMap.tsx and MainPageClient.tsx, and
business-hours logic like readTodayBusinessHours / formatBusinessHours used in
MainStoreCard.tsx and StoreDetailContent.tsx); extract these helpers into a
shared utility module (e.g., storeUtils or businessHoursUtils), implement and
export functions such as mapServerTagToMainCategory and readTodayBusinessHours
there, update KakaoMap.tsx, MainPageClient.tsx, MainStoreCard.tsx, and
StoreDetailContent.tsx to import and use the shared utilities, and remove the
duplicated local implementations to ensure a single source of truth.
In `@apps/customer/src/app/`(tabs)/main/_components/MainListView.tsx:
- Around line 48-54: The component rendering in MainListView currently shows a
loading indicator when isLoading is true and otherwise maps stores to
MainStoreCard, but it lacks an empty-state UI when stores is an empty array;
update the render logic in MainListView (the conditional using isLoading,
stores, and MainStoreCard) to check for stores.length === 0 after loading
completes and render a user-friendly empty message/component (e.g., a div with
guidance or a dedicated EmptyState component) instead of rendering nothing;
ensure the key path still uses item.storeId and preserve existing loading
behavior.
In `@apps/customer/src/app/`(tabs)/main/_components/MainPageClient.tsx:
- Around line 109-111: The "추천순" branch in MainPageClient.tsx currently sorts by
storeId (items.sort((a, b) => b.storeId - a.storeId)), which is a placeholder
and incorrect; update the selectedSort === "추천순" handling to use a real
recommendation metric (e.g., a recommendationScore field on item) or call the
server API to return pre-sorted results instead of using storeId, and if that
field/API is not yet available add a clear TODO comment and fall back to a
stable default (e.g., no-op or sort by createdAt/popularity) in the sort branch
(refer to selectedSort check and the items.sort call in MainPageClient.tsx).
In `@apps/customer/src/app/`(tabs)/main/_components/MainStoreCard.tsx:
- Around line 26-35: MainStoreCard currently uses a plain <img> tag to render
item.storeImage; in a Next.js app replace this with next/image's Image component
(import Image from 'next/image') inside the same conditional so you still render
Icon as a fallback when item.storeImage is falsey, provide explicit width and
height (or use fill with parent position:relative) to avoid layout shift, set
objectFit via style/className to maintain cover behavior, and ensure external
image domains are allowed in next.config.js if the URLs are remote; keep
alt={item.storeName} and preserve the surrounding container and accessibility
attributes.
- Around line 72-102: Extract the duplicated business-hour parsing logic into a
single shared utility (e.g., create a function like formatBusinessHours or
getTodayBusinessHours in a common utils module) and replace the similar
implementations in formatBusinessHours, readTodayBusinessHours, and
normalizeBusinessHours with calls to that utility; ensure the new utility
accepts the same input shape (raw?: unknown or Record<string,string>), handles
invalid input by returning the "영업시간 정보 없음" fallback, maps weekday strings
(Mon..Sun) to keys (mon..sun) using the same Asia/Seoul timeZone logic, and
returns the same output string format (e.g., "영업중 {hours}") so existing
consumers need only swap their local logic for the shared call.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7733de87-779c-49c4-93e8-fe3c1cfdccce
⛔ Files ignored due to path filters (3)
packages/design-system/src/icons/generated/iconNames.tsis excluded by!**/generated/**packages/design-system/src/icons/generated/spriteSymbols.tsis excluded by!**/generated/**packages/design-system/src/icons/source/Pin.svgis excluded by!**/*.svg
📒 Files selected for processing (24)
apps/customer/src/app/(tabs)/main/_apis/searchAddressByKakao.tsapps/customer/src/app/(tabs)/main/_components/AddressSearchBottomSheet.tsxapps/customer/src/app/(tabs)/main/_components/KakaoMap.tsxapps/customer/src/app/(tabs)/main/_components/MainListView.tsxapps/customer/src/app/(tabs)/main/_components/MainMapView.tsxapps/customer/src/app/(tabs)/main/_components/MainPageClient.tsxapps/customer/src/app/(tabs)/main/_components/MainStoreCard.tsxapps/customer/src/app/(tabs)/main/_components/MapStoreBottomSheet.tsxapps/customer/src/app/(tabs)/main/_types/address-search.tsapps/customer/src/app/(tabs)/main/store/[id]/page.tsxapps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsxapps/customer/src/app/(tabs)/main/store/_types/store-detail.tsapps/customer/src/shared/api/api.tsapps/customer/src/shared/queries/query/store/useStoreDetail.tsapps/customer/src/shared/queries/query/store/useStoreSimple.tsapps/customer/src/shared/queries/query/store/useStores.tsapps/customer/src/shared/queries/query/store/useStoresByAddress.tsapps/customer/src/shared/types/kakao.d.tspackages/api/src/models/store.tspackages/design-system/src/components/BottomSheet/BottomSheet.tsxpackages/design-system/src/components/Header/Header.tsxpackages/design-system/src/components/Header/Header.types.tspackages/design-system/src/libs/utils.tspackages/tailwind-config/theme.css
| debounceRef.current = window.setTimeout(async () => { | ||
| try { | ||
| setIsSearching(true); | ||
| const searched = await searchAddressByKakao(trimmedKeyword, 10); | ||
| setResults(searched); | ||
| } catch (error) { | ||
| console.error(error); | ||
| setResults([]); | ||
| } finally { | ||
| setIsSearching(false); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "AddressSearchBottomSheet.tsx" | head -5Repository: CUK-Compasser/FE
Length of output: 136
🏁 Script executed:
cat -n apps/customer/src/app/\(tabs\)/main/_components/AddressSearchBottomSheet.tsx | sed -n '1,100p'Repository: CUK-Compasser/FE
Length of output: 3180
🏁 Script executed:
find . -type f -name "*searchAddressByKakao*" -o -path "*_apis*" -name "*search*" | head -10Repository: CUK-Compasser/FE
Length of output: 125
🏁 Script executed:
cat -n apps/customer/src/app/\(tabs\)/main/_apis/searchAddressByKakao.ts 2>/dev/null | head -50Repository: CUK-Compasser/FE
Length of output: 1772
🏁 Script executed:
rg "window\.setTimeout.*async" apps/customer/src --type ts --type tsx -A 3 | head -50Repository: CUK-Compasser/FE
Length of output: 87
🏁 Script executed:
rg "setTimeout.*async" apps/ --type ts --type tsx -c | head -20Repository: CUK-Compasser/FE
Length of output: 87
🏁 Script executed:
rg "setTimeout.*async" apps/ -A 3 2>/dev/null | head -60Repository: CUK-Compasser/FE
Length of output: 532
🏁 Script executed:
rg "debounce|setIsSearching" apps/customer/src --max-count 20Repository: CUK-Compasser/FE
Length of output: 1331
검색 응답 경쟁 상태(race condition) 방어가 필요합니다.
Line 50-60의 비동기 API 호출에서 이전 요청의 응답이 늦게 도착하면 최신 키워드 결과를 덮어쓸 수 있습니다. 사용자가 "A" 검색 후 "B"를 검색할 때 "A"의 응답이 "B"의 응답보다 나중에 도착하면 잘못된 결과가 표시됩니다. 또한 시트를 닫은 직후에도 이전 요청의 응답으로 상태가 갱신될 수 있습니다. 요청 식별자 또는 취소 플래그를 사용하여 최신 요청만 반영해 주세요.
제안 코드
const debounceRef = useRef<number | null>(null);
+ const requestSeqRef = useRef(0);
useEffect(() => {
+ let cancelled = false;
if (!open) {
return;
}
@@
- debounceRef.current = window.setTimeout(async () => {
+ const requestId = ++requestSeqRef.current;
+ debounceRef.current = window.setTimeout(async () => {
try {
setIsSearching(true);
const searched = await searchAddressByKakao(trimmedKeyword, 10);
- setResults(searched);
+ if (!cancelled && requestId === requestSeqRef.current) {
+ setResults(searched);
+ }
} catch (error) {
- console.error(error);
- setResults([]);
+ if (!cancelled && requestId === requestSeqRef.current) {
+ console.error(error);
+ setResults([]);
+ }
} finally {
- setIsSearching(false);
+ if (!cancelled && requestId === requestSeqRef.current) {
+ setIsSearching(false);
+ }
}
}, 250);
return () => {
+ cancelled = true;
if (debounceRef.current) {
window.clearTimeout(debounceRef.current);
}
};
}, [keyword, open]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/_components/AddressSearchBottomSheet.tsx
around lines 50 - 60, The async search block can be overwritten by out-of-order
responses; add a request identifier or cancellation flag to ensure only the
latest request updates state: create a ref like latestRequestIdRef and increment
it before calling searchAddressByKakao (or create an AbortController if
searchAddressByKakao supports aborting) then capture the current id in the async
callback and, before calling setResults/setIsSearching, verify the id matches
latestRequestIdRef and that the sheet is still open (use an isOpen or isMounted
ref); also ensure any previous pending request is aborted or ignored when the
sheet closes. Use the existing debounceRef and
trimmedKeyword/searchAddressByKakao/setResults/setIsSearching identifiers to
locate where to add this logic.
| function readStoreAddress(store: SimpleStoreInfoDTO) { | ||
| return store.roadAddress || store.jibunAddress || ""; | ||
| } |
There was a problem hiding this comment.
사용되지 않는 함수입니다.
readStoreAddress 함수가 정의되어 있지만 파일 내 어디에서도 호출되지 않습니다. 불필요한 코드는 제거해 주세요.
🗑️ 제거 제안
-function readStoreAddress(store: SimpleStoreInfoDTO) {
- return store.roadAddress || store.jibunAddress || "";
-}
-📝 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.
| function readStoreAddress(store: SimpleStoreInfoDTO) { | |
| return store.roadAddress || store.jibunAddress || ""; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/_components/KakaoMap.tsx around lines 154
- 156, The function readStoreAddress is defined but never used; remove the
unused function declaration (readStoreAddress) from the KakaoMap component file
to eliminate dead code and keep the module clean, or if it is intended for
future use, add a clear TODO and export/usage where applicable (e.g., replace
inline address logic with a call to readStoreAddress in the rendering or helper
that builds store addresses).
| function mapServerTagToMainCategory(tag: StoreTag): MainCategory { | ||
| switch (tag) { | ||
| case "CAFE": | ||
| return "카페"; | ||
| case "BAKERY": | ||
| return "베이커리"; | ||
| case "RESTAURANT": | ||
| return "식당"; | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
ESLint 들여쓰기 오류 및 함수 중복.
- 정적 분석 도구에서 들여쓰기 오류가 감지되었습니다.
mapServerTagToMainCategory함수가KakaoMap.tsx에도 동일하게 존재합니다. 공통 유틸리티로 추출을 권장합니다.
🔧 들여쓰기 수정 제안
function mapServerTagToMainCategory(tag: StoreTag): MainCategory {
switch (tag) {
- case "CAFE":
- return "카페";
- case "BAKERY":
- return "베이커리";
- case "RESTAURANT":
- return "식당";
+ case "CAFE":
+ return "카페";
+ case "BAKERY":
+ return "베이커리";
+ case "RESTAURANT":
+ return "식당";
}
}📝 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.
| function mapServerTagToMainCategory(tag: StoreTag): MainCategory { | |
| switch (tag) { | |
| case "CAFE": | |
| return "카페"; | |
| case "BAKERY": | |
| return "베이커리"; | |
| case "RESTAURANT": | |
| return "식당"; | |
| } | |
| } | |
| function mapServerTagToMainCategory(tag: StoreTag): MainCategory { | |
| switch (tag) { | |
| case "CAFE": | |
| return "카페"; | |
| case "BAKERY": | |
| return "베이커리"; | |
| case "RESTAURANT": | |
| return "식당"; | |
| } | |
| } |
🧰 Tools
🪛 ESLint
[error] 160-160: Expected indentation of 2 spaces but found 4.
(indent)
[error] 161-161: Expected indentation of 4 spaces but found 6.
(indent)
[error] 162-162: Expected indentation of 2 spaces but found 4.
(indent)
[error] 163-163: Expected indentation of 4 spaces but found 6.
(indent)
[error] 164-164: Expected indentation of 2 spaces but found 4.
(indent)
[error] 165-165: Expected indentation of 4 spaces but found 6.
(indent)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/_components/MainPageClient.tsx around
lines 158 - 167, The function mapServerTagToMainCategory has indentation issues
and is duplicated in KakaoMap.tsx; fix the formatting in this file (correct
function indentation and ensure exhaustive switch handling) and extract the
function into a shared utility (e.g., create and export
mapServerTagToMainCategory from a new/common util module) then import and use
that exported function in both MainPageClient.tsx and KakaoMap.tsx, keeping the
same type signatures (StoreTag and MainCategory) and updating exports/imports
accordingly.
| function mapServerTagToLabel(tag: StoreTag) { | ||
| switch (tag) { | ||
| case "CAFE": | ||
| return "카페"; | ||
| case "BAKERY": | ||
| return "베이커리"; | ||
| case "RESTAURANT": | ||
| return "식당"; | ||
| } | ||
| } |
There was a problem hiding this comment.
ESLint 들여쓰기 오류 수정이 필요합니다.
정적 분석 도구에서 들여쓰기 오류가 감지되었습니다. 프로젝트 설정에 맞게 2스페이스 들여쓰기로 변경해 주세요.
🔧 들여쓰기 수정 제안
function mapServerTagToLabel(tag: StoreTag) {
switch (tag) {
- case "CAFE":
- return "카페";
- case "BAKERY":
- return "베이커리";
- case "RESTAURANT":
- return "식당";
+ case "CAFE":
+ return "카페";
+ case "BAKERY":
+ return "베이커리";
+ case "RESTAURANT":
+ return "식당";
}
}📝 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.
| function mapServerTagToLabel(tag: StoreTag) { | |
| switch (tag) { | |
| case "CAFE": | |
| return "카페"; | |
| case "BAKERY": | |
| return "베이커리"; | |
| case "RESTAURANT": | |
| return "식당"; | |
| } | |
| } | |
| function mapServerTagToLabel(tag: StoreTag) { | |
| switch (tag) { | |
| case "CAFE": | |
| return "카페"; | |
| case "BAKERY": | |
| return "베이커리"; | |
| case "RESTAURANT": | |
| return "식당"; | |
| } | |
| } |
🧰 Tools
🪛 ESLint
[error] 63-63: Expected indentation of 2 spaces but found 4.
(indent)
[error] 64-64: Expected indentation of 4 spaces but found 6.
(indent)
[error] 65-65: Expected indentation of 2 spaces but found 4.
(indent)
[error] 66-66: Expected indentation of 4 spaces but found 6.
(indent)
[error] 67-67: Expected indentation of 2 spaces but found 4.
(indent)
[error] 68-68: Expected indentation of 4 spaces but found 6.
(indent)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/_components/MainStoreCard.tsx around lines
61 - 70, The function mapServerTagToLabel has inconsistent indentation violating
the project's 2-space style; reformat the function body (including the switch,
case labels "CAFE", "BAKERY", "RESTAURANT", and their return lines) to use
2-space indentation throughout to satisfy ESLint rules for
apps/customer/src/app/(tabs)/main/_components/MainStoreCard.tsx and ensure no
other spacing errors remain in mapServerTagToLabel.
| <span className="body2-r text-gray-700"> | ||
| {email ?? "이메일 정보 없음"} | ||
| </span> |
There was a problem hiding this comment.
이메일 fallback에 빈 문자열 케이스도 포함해 주세요.
현재는 null/undefined만 대체되어 ""면 빈 텍스트가 노출됩니다.
제안 코드
- {email ?? "이메일 정보 없음"}
+ {email?.trim() ? email : "이메일 정보 없음"}📝 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.
| <span className="body2-r text-gray-700"> | |
| {email ?? "이메일 정보 없음"} | |
| </span> | |
| <span className="body2-r text-gray-700"> | |
| {email?.trim() ? email : "이메일 정보 없음"} | |
| </span> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/_components/MapStoreBottomSheet.tsx around
lines 76 - 78, The span in MapStoreBottomSheet currently renders {email ?? "이메일
정보 없음"} which only handles null/undefined; update the conditional to also treat
empty-string (and optionally whitespace) as missing by checking email (or
email?.trim()) and falling back to "이메일 정보 없음" so empty values won't render
blank text; change the expression around the span (the email variable usage) to
use this combined check.
| function readJibunAddress(store?: StoreRespDTO) { | ||
| if (!store) { | ||
| return ""; | ||
| } | ||
|
|
||
| return (store as StoreRespDTO & { jibunAddres?: string }).jibunAddress | ||
| ?? (store as StoreRespDTO & { jibunAddres?: string }).jibunAddres | ||
| ?? ""; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 백엔드 응답에서 jibunAddres 오타가 실제로 존재하는지 확인
rg -n 'jibunAddres[^s]' --type=tsRepository: CUK-Compasser/FE
Length of output: 360
🏁 Script executed:
# Find StoreRespDTO definition
rg -n 'type StoreRespDTO|interface StoreRespDTO' --type=ts -A 20Repository: CUK-Compasser/FE
Length of output: 1282
🏁 Script executed:
# Search for all occurrences of jibunAddres (the typo) across the entire codebase
rg -n 'jibunAddres' --type=tsRepository: CUK-Compasser/FE
Length of output: 1041
🏁 Script executed:
# Check if there are any API response examples or documentation
fd -t f -e json -e md | xargs grep -l "jibunAddres" 2>/dev/null || echo "No JSON/MD files found with jibunAddres"Repository: CUK-Compasser/FE
Length of output: 99
🏁 Script executed:
# Search for jibunAddress (correct spelling) to understand its usage
rg -n 'jibunAddress' --type=ts -B 2 -A 2Repository: CUK-Compasser/FE
Length of output: 3685
불필요한 방어 코딩 제거.
StoreRespDTO에는 jibunAddress만 정의되어 있으며, 코드베이스 전체에서 jibunAddres (오타)는 이 함수 내에서만 나타납니다. 백엔드가 실제로 이 오타를 반환한다는 증거가 없으므로, 불필요한 타입 단언과 방어 코딩을 제거하고 다음과 같이 단순화해야 합니다:
function readJibunAddress(store?: StoreRespDTO) {
return store?.jibunAddress ?? "";
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/store/_components/StoreDetailContent.tsx
around lines 255 - 263, The readJibunAddress function contains unnecessary
defensive casting and an incorrect typo variant (jibunAddres) that doesn't exist
elsewhere; simplify it by removing the extra type assertions and the fallback to
the misspelled property and return store?.jibunAddress ?? "" directly, keeping
the function signature and using the StoreRespDTO type.
| if (Number.isNaN(storeId)) { | ||
| notFound(); | ||
| } | ||
|
|
||
| return <StoreDetailContent store={store} />; | ||
| return <StoreDetailContent storeId={storeId} />; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/customer/src/app/\(tabs\)/main/store/\[id\]/page.tsxRepository: CUK-Compasser/FE
Length of output: 649
🏁 Script executed:
find . -name "*StoreDetailContent*" -type fRepository: CUK-Compasser/FE
Length of output: 136
🏁 Script executed:
cat -n ./apps/customer/src/app/\(tabs\)/main/store/_components/StoreDetailContent.tsxRepository: CUK-Compasser/FE
Length of output: 10800
🏁 Script executed:
find . -name "*useStoreDetail*" -type fRepository: CUK-Compasser/FE
Length of output: 124
🏁 Script executed:
cat -n ./apps/customer/src/shared/queries/query/store/useStoreDetail.tsRepository: CUK-Compasser/FE
Length of output: 403
🏁 Script executed:
find . -path "*shared/api*" -name "*.ts" -o -name "*.tsx" | head -20Repository: CUK-Compasser/FE
Length of output: 1511
🏁 Script executed:
find . -type f -path "*shared/api*" -name "*.ts" -o -path "*shared/api*" -name "*.tsx"Repository: CUK-Compasser/FE
Length of output: 132
🏁 Script executed:
cat -n ./apps/customer/src/shared/api/api.ts | head -150Repository: CUK-Compasser/FE
Length of output: 1408
URL 파라미터 검증을 강화해야 합니다.
현재 Number.isNaN 체크만으로는 -1, 1.5, Infinity 같은 값이 통과합니다. 라우트 파라미터로 사용되는 storeId는 양의 정수로 제한하세요.
제안 코드
- if (Number.isNaN(storeId)) {
+ if (!Number.isFinite(storeId) || !Number.isInteger(storeId) || storeId <= 0) {
notFound();
}📝 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.
| if (Number.isNaN(storeId)) { | |
| notFound(); | |
| } | |
| return <StoreDetailContent store={store} />; | |
| return <StoreDetailContent storeId={storeId} />; | |
| if (!Number.isFinite(storeId) || !Number.isInteger(storeId) || storeId <= 0) { | |
| notFound(); | |
| } | |
| return <StoreDetailContent storeId={storeId} />; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/app/`(tabs)/main/store/[id]/page.tsx around lines 16 - 20,
The current check using Number.isNaN allows invalid values like -1, 1.5, or
Infinity to pass; update the validation around storeId (the value passed into
StoreDetailContent) to ensure it's a finite positive integer by verifying
Number.isFinite(storeId) && Number.isInteger(storeId) && storeId > 0, and call
notFound() when that condition fails instead of only checking Number.isNaN;
adjust the logic where storeId is derived/parsed so the same validation is
applied before rendering StoreDetailContent.
| interface KakaoLatLng {} | ||
|
|
||
| interface KakaoMapInstance { | ||
| relayout: () => void; | ||
| setCenter: (latlng: KakaoLatLng) => void; | ||
| } | ||
|
|
||
| interface KakaoMarker { | ||
| setMap: (map: KakaoMapInstance | null) => void; | ||
| setPosition: (position: KakaoLatLng) => void; | ||
| } | ||
|
|
||
| interface KakaoMarkerImage {} | ||
|
|
||
| interface KakaoSize {} | ||
|
|
||
| interface KakaoPoint {} |
There was a problem hiding this comment.
빈 인터페이스를 타입 별칭으로 변경하세요.
ESLint에서 빈 인터페이스 선언에 대해 경고하고 있습니다. Kakao SDK의 불투명(opaque) 타입을 표현하기 위해 object 타입 별칭을 사용하거나, 린트 규칙을 비활성화하는 것을 권장합니다.
🔧 타입 별칭으로 변경 제안
-interface KakaoLatLng {}
+type KakaoLatLng = object;
interface KakaoMapInstance {
relayout: () => void;
setCenter: (latlng: KakaoLatLng) => void;
}
interface KakaoMarker {
setMap: (map: KakaoMapInstance | null) => void;
setPosition: (position: KakaoLatLng) => void;
}
-interface KakaoMarkerImage {}
+type KakaoMarkerImage = object;
-interface KakaoSize {}
+type KakaoSize = object;
-interface KakaoPoint {}
+type KakaoPoint = object;📝 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.
| interface KakaoLatLng {} | |
| interface KakaoMapInstance { | |
| relayout: () => void; | |
| setCenter: (latlng: KakaoLatLng) => void; | |
| } | |
| interface KakaoMarker { | |
| setMap: (map: KakaoMapInstance | null) => void; | |
| setPosition: (position: KakaoLatLng) => void; | |
| } | |
| interface KakaoMarkerImage {} | |
| interface KakaoSize {} | |
| interface KakaoPoint {} | |
| type KakaoLatLng = object; | |
| interface KakaoMapInstance { | |
| relayout: () => void; | |
| setCenter: (latlng: KakaoLatLng) => void; | |
| } | |
| interface KakaoMarker { | |
| setMap: (map: KakaoMapInstance | null) => void; | |
| setPosition: (position: KakaoLatLng) => void; | |
| } | |
| type KakaoMarkerImage = object; | |
| type KakaoSize = object; | |
| type KakaoPoint = object; |
🧰 Tools
🪛 ESLint
[error] 87-87: An empty interface declaration allows any non-nullish value, including literals like 0 and "".
- If that's what you want, disable this lint rule with an inline comment or configure the 'allowInterfaces' rule option.
- If you want a type meaning "any object", you probably want
objectinstead. - If you want a type meaning "any value", you probably want
unknowninstead.
(@typescript-eslint/no-empty-object-type)
[error] 99-99: An empty interface declaration allows any non-nullish value, including literals like 0 and "".
- If that's what you want, disable this lint rule with an inline comment or configure the 'allowInterfaces' rule option.
- If you want a type meaning "any object", you probably want
objectinstead. - If you want a type meaning "any value", you probably want
unknowninstead.
(@typescript-eslint/no-empty-object-type)
[error] 101-101: An empty interface declaration allows any non-nullish value, including literals like 0 and "".
- If that's what you want, disable this lint rule with an inline comment or configure the 'allowInterfaces' rule option.
- If you want a type meaning "any object", you probably want
objectinstead. - If you want a type meaning "any value", you probably want
unknowninstead.
(@typescript-eslint/no-empty-object-type)
[error] 103-103: An empty interface declaration allows any non-nullish value, including literals like 0 and "".
- If that's what you want, disable this lint rule with an inline comment or configure the 'allowInterfaces' rule option.
- If you want a type meaning "any object", you probably want
objectinstead. - If you want a type meaning "any value", you probably want
unknowninstead.
(@typescript-eslint/no-empty-object-type)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/customer/src/shared/types/kakao.d.ts` around lines 87 - 103, 빈 인터페이스
선언(KakaoLatLng, KakaoMarkerImage, KakaoSize, KakaoPoint)을 타입 별칭으로 바꿔주세요: 각
interface 대신 "type <Name> = object" 또는 "type <Name> = unknown" 형태로 선언하고 기존 참조(예:
KakaoMapInstance.setCenter, KakaoMarker.setPosition 등)는 그대로 유지해서 타입 호환이 되도록 합니다;
빈 interface 선언문은 제거하고 필요하면 린트 규칙 비활성화 대신 타입 별칭을 사용하세요.
packages/api/src/models/store.ts
Outdated
| export interface RandomBoxRespDTO { | ||
| boxId: number; | ||
| storeId: number; | ||
| boxName: string; | ||
| stock: number; | ||
| price: number; | ||
| buyLimit: number; | ||
| content: string; | ||
| saleStatus: string; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# StoreMenuItem 타입 정의 확인
ast-grep --pattern 'interface StoreMenuItem {
$$$
}'
# StoreMenuCard에서 사용하는 필드 확인
rg -n 'item\.(remainingCount|pickupStartTime|pickupEndTime)' --type=tsRepository: CUK-Compasser/FE
Length of output: 313
🏁 Script executed:
# Find StoreMenuItem interface definition
rg -n 'interface StoreMenuItem' --type=ts -A 15
# Find where RandomBoxRespDTO is imported/used
rg -n 'RandomBoxRespDTO' --type=ts -B 2 -A 5
# Check StoreDetailContent.tsx for the mapping
fd 'StoreDetailContent' --type=ts && cat -n "apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx"Repository: CUK-Compasser/FE
Length of output: 6802
🏁 Script executed:
# Search for StoreMenuItem interface definition (corrected syntax)
rg -n 'interface StoreMenuItem' -A 15
# Find StoreDetailContent.tsx
fd 'StoreDetailContent'
# Get complete RandomBoxRespDTO definition
rg -n 'export interface RandomBoxRespDTO' -A 12Repository: CUK-Compasser/FE
Length of output: 1443
🏁 Script executed:
# Search for StoreMenuItem interface
rg -n 'interface StoreMenuItem' -A 12
# Read StoreDetailContent.tsx to see how it uses RandomBoxRespDTO
cat -n "apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx" | head -100Repository: CUK-Compasser/FE
Length of output: 3448
🏁 Script executed:
# Continue reading StoreDetailContent.tsx
cat -n "apps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsx" | tail -n +100
# Search for StoreMenuItem interface definition
rg -n 'interface StoreMenuItem' -A 15Repository: CUK-Compasser/FE
Length of output: 7419
🏁 Script executed:
# Search for StoreMenuItem interface
rg -n 'type StoreMenuItem|interface StoreMenuItem' -A 15
# Read StoreMenuCard.tsx to see what it expects
cat -n "apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx" | head -50Repository: CUK-Compasser/FE
Length of output: 1826
🏁 Script executed:
# Find the store-detail types file
fd 'store-detail' -path '*_types*'
# Read the file
cat -n "apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts"Repository: CUK-Compasser/FE
Length of output: 393
🏁 Script executed:
# Search for StoreMenuItem type definition
rg -n 'type StoreMenuItem|interface StoreMenuItem'
# Check if it's maybe defined in the same directory differently
fd '\.ts' "apps/customer/src/app/(tabs)/main/store/_types/"
# Also check what's in store-detail.ts completely
cat "apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts"Repository: CUK-Compasser/FE
Length of output: 214
🏁 Script executed:
# Search entire codebase for StoreMenuItem definition
rg -n 'type StoreMenuItem\s*=|interface StoreMenuItem\s*{'
# Also search for just StoreMenuItem as variable/export
rg -n 'StoreMenuItem'
# Check imports in StoreMenuCard again more carefully
grep -n 'import.*StoreMenuItem' "apps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsx"Repository: CUK-Compasser/FE
Length of output: 1398
🏁 Script executed:
# Search for where StoreMenuItem might be defined
rg -n 'export type StoreMenuItem|export interface StoreMenuItem'
# Look in all _types files
find . -path '*_types*' -name '*.ts' -o -name '*.tsx'
# Check git history for store-detail.ts to see if StoreMenuItem was removed
git log --oneline "apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts" | head -10Repository: CUK-Compasser/FE
Length of output: 26765
🏁 Script executed:
# Check if StoreMenuItem exists anywhere in the codebase outside of imports
rg 'export (type|interface) StoreMenuItem'
# Check git diff for recent changes to store-detail.ts
git log -p -n 3 -- "apps/customer/src/app/(tabs)/main/store/_types/store-detail.ts" | head -100Repository: CUK-Compasser/FE
Length of output: 42
RandomBoxRespDTO와 StoreMenuItem 간 필드 불일치가 확인되었습니다.
RandomBoxRespDTO는 remainingCount, pickupStartTime, pickupEndTime 필드를 포함하지 않습니다. StoreMenuCard는 이 세 필드를 사용하여 렌더링하므로, StoreDetailContent.tsx에서 RandomBoxRespDTO를 매핑할 때 undefined 값이 전달되어 런타임 오류가 발생합니다.
더 심각한 문제로, StoreMenuItem 타입이 store-detail.ts에 정의되지 않았는데 여러 컴포넌트에서 임포트되고 있습니다. 필드 누락은 물론, 타입 정의 자체가 결여되어 있습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/api/src/models/store.ts` around lines 23 - 32, RandomBoxRespDTO is
missing fields (remainingCount, pickupStartTime, pickupEndTime) that
StoreMenuCard and StoreDetailContent.tsx expect, and StoreMenuItem is not
defined in store-detail.ts despite being imported elsewhere; fix by updating the
RandomBoxRespDTO interface to include remainingCount:number,
pickupStartTime:string|Date, pickupEndTime:string|Date (or nullable types
matching API), or create a conversion function that maps RandomBoxRespDTO ->
StoreMenuItem populating those fields, and add a proper StoreMenuItem type
definition in store-detail.ts (or a shared types file) that matches
StoreMenuCard's props so all components import the correct type; ensure
StoreDetailContent.tsx uses the converter or the extended DTO to avoid passing
undefined values at runtime.
| name={inputName} | ||
| value={value} | ||
| onChange={onChange} | ||
| onClick={onInputClick} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# location-search 헤더 사용처와 입력 속성 확인
rg -n --type=tsx 'variant\s*=\s*["'\'']location-search["'\'']|onInputClick\s*=|inputReadOnly\s*='Repository: CUK-Compasser/FE
Length of output: 87
🏁 Script executed:
#!/bin/bash
# Header.tsx 파일 확인
cat packages/design-system/src/components/Header/Header.tsxRepository: CUK-Compasser/FE
Length of output: 3838
🏁 Script executed:
#!/bin/bash
# 수정된 패턴 검색 - 파일 타입 제한 없음
rg -n 'location-search|onInputClick|inputReadOnly' packages/design-system/src/components/Header/Repository: CUK-Compasser/FE
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# 유사 입력 필드 구현 패턴 찾기
rg -n -A3 -B3 'readOnly.*input|readonly.*input' packages/design-system/src/components/ --type js --type jsxRepository: CUK-Compasser/FE
Length of output: 87
읽기 전용 입력 필드의 키보드 접근성 확보
라인 43의 input에 onClick={onInputClick}만 연결되어 있어 마우스 사용자만 상호작용 가능합니다. inputReadOnly가 true일 때 이 필드가 "선택 트리거"로 동작한다면, 키보드 사용자가 Tab으로 포커스한 후 Enter 또는 Space로 상호작용할 수 있도록 onKeyDown 핸들러를 추가해주세요.
<input
onKeyDown={(e) => {
if ((e.key === 'Enter' || e.key === ' ') && inputReadOnly) {
onInputClick?.(e as unknown as React.MouseEvent<HTMLInputElement>);
}
}}
// ... 기존 props
/>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/design-system/src/components/Header/Header.tsx` at line 43, The
input currently only handles mouse interaction via onInputClick; add a keyboard
handler so read-only inputs acting as selection triggers are usable via
keyboard: add an onKeyDown on the same input that checks for Enter or Space when
inputReadOnly is true, preventDefault for Space, and invoke onInputClick
(casting the KeyboardEvent to the expected event type or adapting the handler
call) so keyboard users can activate the control; reference the inputReadOnly
prop and the onInputClick handler in Header.tsx and ensure the new onKeyDown
logic coexists with the existing onClick.
✅ 작업 내용
📝 Description
🚀 설계 의도 및 개선점
📎 기타 참고사항
Fixes #83
Summary by CodeRabbit
릴리스 노트
New Features
Style