Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5d98f88
feat: Refresh icon component 추가
leeleeleeleejun Nov 3, 2025
751745c
refactor: getCategories 함수에서 데이터 구조 분해(destructuring) 방식 개선
leeleeleeleejun Nov 3, 2025
625e3d4
refactor: places API 엔드포인트 경로 일부 수정
leeleeleeleejun Nov 3, 2025
ed23179
refactor: api 요청 함수에서 데이터 구조 분해(destructuring) 방식 개선
leeleeleeleejun Nov 3, 2025
9d85026
refactor: PlaceDetailPage에서 Banner 컴포넌트 조건부 렌더링 추가
leeleeleeleejun Nov 3, 2025
dcd22b7
refactor: CampusButtonBax 컴포넌트의 props 변경 및 centerMapToCampus 함수 추가
leeleeleeleejun Nov 3, 2025
d058887
refactor: RefreshButton 컴포넌트 추가 및 스타일 적용
leeleeleeleejun Nov 3, 2025
f1fd0f8
refactor: MapComponent에 RefreshButton 추가 및 맵 갱신 로직 개선
leeleeleeleejun Nov 3, 2025
9a21ca0
refactor: BY_CATEGORY 경로의 URL 쿼리 파라미터 구문 수정
leeleeleeleejun Nov 3, 2025
47a5bd2
refactor: 연결된 api 모킹 handler 주석 처리
leeleeleeleejun Nov 3, 2025
bc6a31d
refactor: menu 타입에 menuId 추가 및 photos 타입에 photoId를 nullable로 변경
leeleeleeleejun Nov 8, 2025
492ceb4
refactor: getSearchPlaceByKakao의 응답 형식 수정
leeleeleeleejun Nov 8, 2025
de95eb5
refactor: 가격 포맷팅 로직 공통함수로 분리 및 Menu, RecommendedMenu 컴포넌트에서 사용
leeleeleeleejun Nov 9, 2025
fa0b30f
feat: 맛집 등록을 위한 createNewPlace 함수 및 NewPlaceResponse 타입 추가
leeleeleeleejun Nov 9, 2025
ede094d
refactor: PlacePreview 컴포넌트에 getValues prop 추가 및 카카오 장소 ID로 api 요청 적용
leeleeleeleejun Nov 9, 2025
3025941
feat: 및 useCreateNewPlace mutation 적용 및 Category 컴포넌트에 isLoading pro…
leeleeleeleejun Nov 9, 2025
00752bc
refactor: next.config.ts의 remotePatterns 추가
leeleeleeleejun Nov 9, 2025
4afcef2
feat: success, fail을 step이 아닌 페이지로 관리
leeleeleeleejun Nov 9, 2025
b5b3705
refactor: CampusButtonBax에서 CampusButtonBox으로 이름 수정
leeleeleeleejun Nov 9, 2025
edfb9f4
refactor: 캠퍼스 이름을 대문자로 변경 및 useCampusStore의 초기값 수정
leeleeleeleejun Nov 9, 2025
49b9ea3
refactor: 천안 캠퍼스 이름 오타 수정
leeleeleeleejun Nov 9, 2025
c8562f5
refactor: campus name 수정에 따른 toUpperCase 함수 실행 제거
leeleeleeleejun Nov 9, 2025
6930dd9
refactor: photoId가 nullable로 변경에 따라 displayOrder를 photo key로 fallback
leeleeleeleejun Nov 9, 2025
b529cd7
refactor: api 요청 함수에서 데이터 구조 분해(destructuring) 방식 개선
leeleeleeleejun Nov 9, 2025
83ba81f
refactor: PlaceMarker 컴포넌트의 handlePreviewPlace prop을 optional로 수정
leeleeleeleejun Nov 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions apps/web/app/_apis/mutations/useCreateNewPlace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useMutation } from '@tanstack/react-query'
import type { NewPlaceRequest } from '@/_apis/schemas/place'
import { createNewPlace } from '@/_apis/services/place'
import { useRouter } from 'next/navigation'
import { CLIENT_PATH } from '@/_constants/path'

export const useCreateNewPlace = () => {
const { replace } = useRouter()

return useMutation({
mutationFn: async (placeData: NewPlaceRequest) =>
await createNewPlace(placeData),
onSuccess: (res) => {
if (res.status === 'OK') {
replace(CLIENT_PATH.PLACE_NEW_SUCCESS)
} else {
replace(CLIENT_PATH.PLACE_NEW_FAIL)
}
},
onError: (error) => {
console.error(error)
replace(CLIENT_PATH.PLACE_NEW_FAIL)
},
})
}
5 changes: 3 additions & 2 deletions apps/web/app/_apis/queries/place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export const PlaceQueryKeys = {
[...PlaceQueryKeys.all(), 'ranking', sort, campus] as const,
byCategory: (id: string, campus: CampusType) =>
[...PlaceQueryKeys.all(), 'category', id, campus] as const,
byMap: () => [...PlaceQueryKeys.all(), 'map'] as const,
byMap: (bounds: MapBounds | null) =>
[...PlaceQueryKeys.all(), 'map', bounds] as const,
byPreview: (kakaoPlaceId: string) =>
[...PlaceQueryKeys.all(), 'preview', kakaoPlaceId] as const,
byLike: () => [...PlaceQueryKeys.all(), 'like'] as const,
Expand All @@ -44,7 +45,7 @@ export const usePlaceQueries = {

byMap: (bounds: MapBounds | null) => {
return queryOptions({
queryKey: PlaceQueryKeys.byMap(),
queryKey: PlaceQueryKeys.byMap(bounds),
queryFn: () => {
if (!bounds) return Promise.resolve([])
return getPlacesByMap(bounds)
Expand Down
6 changes: 5 additions & 1 deletion apps/web/app/_apis/schemas/place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const location = z.object({

const menus = z.array(
z.object({
menuId: z.number(),
name: z.string(),
price: z.number(),
isRecommended: z.boolean(),
Expand All @@ -17,7 +18,7 @@ const menus = z.array(

const photos = z.array(
z.object({
photoId: z.number().transform(String),
photoId: z.nullable(z.number().transform(String)),
Copy link

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

photoId의 nullable 변경 확인 필요

photoId가 nullable로 변경되었습니다. 이는 기존에 photoId를 사용하는 모든 컴포넌트에서 null 케이스를 처리하는지 확인이 필요합니다.

다음 스크립트로 photoId 사용처를 확인하세요:


🏁 Script executed:

#!/bin/bash
# photoId를 사용하는 코드 검색
rg -nP --type=ts --type=tsx -C3 'photoId' -g '!**/node_modules/**' -g '!**/_mocks/**'

Length of output: 27


Now I'll run a corrected script to search for photoId usage:


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for photoId usage in .ts and .tsx files with context
rg -nP 'photoId' -g '*.ts*' -C3 -g '!node_modules/**' -g '!_mocks/**'

Length of output: 11259


이제 PreviewPlace 컴포넌트의 상세 코드를 검토하여 null 처리 여부를 확인하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# PreviewPlace 컴포넌트의 전체 photos.map 섹션 확인
cat apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx | head -60 | tail -30

Length of output: 879


PreviewPlace 컴포넌트의 타입 정의와 전체 구조를 확인하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# PreviewPlace 컴포넌트의 props 타입 및 전체 구조 확인
cat apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx

Length of output: 2009


이제 place.ts 파일의 PlaceByMap 타입 정의를 확인하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# place.ts 파일에서 PlaceByMap 타입 정의 확인
cat apps/web/app/_apis/schemas/place.ts

Length of output: 1935


PreviewPlace.tsx에서 nullable photoId 처리 필수

photoId가 nullable로 변경되었으나 PreviewPlace.tsx 40줄에서 React 키로 직접 사용되고 있습니다. photo.photoIdnull이면 React는 유효하지 않은 키 경고를 발생시키고 런타임 오류가 발생합니다.

  • apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx 40줄: key={photo.photoId} - null 체크 필수
  • apps/admin/src/app/request/[id]/_api/types.ts 4줄: photoId: string 타입도 nullable로 업데이트 필요

null인 사진을 필터링하거나, displayOrder 같은 다른 고유 값을 키로 사용하거나, null 체크 로직을 추가해야 합니다.

🤖 Prompt for AI Agents
In apps/web/app/_apis/schemas/place.ts line 21 and related UI, photoId was
changed to nullable but PreviewPlace.tsx
(apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx around line 40)
still uses key={photo.photoId} directly and will break when null; update
PreviewPlace to either filter out photos with null photoId before rendering or
use a safe key (e.g. fallback to a stable alternative like displayOrder or a
composite string using index when photoId is null) and add an explicit
null-check when accessing photo.photoId; additionally update
apps/admin/src/app/request/[id]/_api/types.ts (around line 4) to make photoId
nullable (string | null) to match the schema.

photoUrl: z.url(),
displayOrder: z.number(),
}),
Expand Down Expand Up @@ -77,3 +78,6 @@ export type PlaceByMap = z.infer<typeof PlaceByMapSchema>
export type PlaceDetail = z.infer<typeof PlaceDetailSchema>
export type PlaceByPreview = z.infer<typeof PlaceByPreviewSchema>
export type NewPlaceRequest = z.infer<typeof NewPlaceRequestSchema>
export type NewPlaceResponse = {
status: 'OK' | 'ERROR'
}
3 changes: 2 additions & 1 deletion apps/web/app/_apis/services/category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { API_PATH } from '@/_constants/path'
import { CategorySchema, Category } from '../schemas/category'

export const getCategories = async (): Promise<Category[]> => {
const { data } = await axiosInstance.get(API_PATH.CATEGORY)
const { data: response } = await axiosInstance.get(API_PATH.CATEGORY)
const { data } = response
return CategorySchema.array().parse(data)
}
Comment on lines 5 to 9
Copy link

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

중첩된 API 응답 구조를 올바르게 처리하고 있습니다.

백엔드 응답 형식 변경에 맞춰 2단계 데이터 추출 로직이 정확하게 구현되었습니다. Zod 스키마를 통한 런타임 검증도 유지되고 있어 타입 안정성이 보장됩니다.

다만, PR 설명에 따르면 이 변경사항이 여러 API 서비스에 영향을 미친다고 하니, 다른 API 서비스 파일들도 동일한 패턴으로 업데이트되었는지 확인해주세요.

다음 스크립트를 실행하여 다른 API 서비스 파일들의 일관성을 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: 다른 API 서비스 파일들이 동일한 중첩 응답 구조를 처리하는지 확인

# API 서비스 파일들에서 axiosInstance.get/post/put/delete 호출 패턴 검색
echo "=== Checking API service files for response handling pattern ==="
fd -e ts -e tsx --full-path '_apis/services' --exec rg -n -A 3 -B 1 'axiosInstance\.(get|post|put|delete|patch)' {}

echo ""
echo "=== Checking for old pattern (direct data destructuring) ==="
# 이전 패턴 (const { data } = await axiosInstance...)을 직접 사용하는 경우 검색
fd -e ts -e tsx --full-path '_apis/services' --exec rg -n 'const\s*\{\s*data\s*\}\s*=\s*await\s+axiosInstance' {}

Length of output: 3516


API 서비스 파일들 간 응답 처리 패턴이 일관되지 않습니다. 즉각적인 수정이 필요합니다.

검증 결과 일부 함수는 새로운 중첩 응답 구조(데이터 2단계 추출)로 업데이트되었으나, 다른 함수들은 여전히 이전 패턴(직접 데이터 추출)을 사용하고 있습니다. 백엔드 응답 형식이 실제로 변경되었다면, 다음 함수들은 런타임에 실패할 것입니다:

수정 필요 위치:

  • place.ts: addLike (10줄), removeLike (15줄), createNewPlace (113줄)
  • request.ts: getRequests (11줄), getRequestDetail (16줄)
  • event.ts: getEventInfo (11줄), postEventParticipations (19줄), getEventResult (24줄)

이 모든 함수를 category.ts의 getCategories와 동일한 패턴으로 업데이트하세요:

const { data: response } = await axiosInstance.get(...)
const { data } = response
return Schema.parse(data)
🤖 Prompt for AI Agents
In apps/web/app/_apis/services/category.ts around lines 5 to 9, getCategories
currently extracts data twice (const { data: response } = await
axiosInstance.get(...); const { data } = response) and parses via
CategorySchema.array().parse(data); update the other listed API functions to
follow this exact response-unwrapping and parsing pattern: for each call first
destructure as { data: response } = await axiosInstance.<method>(...), then
extract const { data } = response, and finally pass that data into the
appropriate Zod schema's parse method (e.g., Schema.parse(data) or
Schema.array().parse(data) as applicable) for these locations — place.ts
(addLike at line 10, removeLike at 15, createNewPlace at 113), request.ts
(getRequests at 11, getRequestDetail at 16), and event.ts (getEventInfo at 11,
postEventParticipations at 19, getEventResult at 24).

12 changes: 9 additions & 3 deletions apps/web/app/_apis/services/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,25 @@ import {
} from '@/_apis/schemas/event'

export const getEventInfo = async (): Promise<Event> => {
const { data } = await axiosInstance.get(API_PATH.EVENT.INFO)
const { data: response } = await axiosInstance.get(API_PATH.EVENT.INFO)
const { data } = response
return EventSchema.parse(data)
}

export const participationEvent = async (body: {
eventId: string
ticketsCount: number
}) => {
const { data } = await axiosInstance.post(API_PATH.EVENT.PARTICIPATIONS, body)
const { data: response } = await axiosInstance.post(
API_PATH.EVENT.PARTICIPATIONS,
body,
)
const { data } = response
return data
}

export const getEventResult = async (): Promise<EventResult | null> => {
const { data } = await axiosInstance.get(API_PATH.EVENT.RESULT)
const { data: response } = await axiosInstance.get(API_PATH.EVENT.RESULT)
const { data } = response
return EventResultSchema.parse(data)
}
8 changes: 6 additions & 2 deletions apps/web/app/_apis/services/like.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ type Response = {
}

export const addLike = async (placeId: string): Promise<Response> => {
const { data } = await axiosInstance.post(API_PATH.PLACES.LIKE.POST(placeId))
const { data: response } = await axiosInstance.post(
API_PATH.PLACES.LIKE.POST(placeId),
)
const { data } = response
return data
}

export const removeLike = async (placeId: string): Promise<Response> => {
const { data } = await axiosInstance.delete(
const { data: response } = await axiosInstance.delete(
API_PATH.PLACES.LIKE.DELETE(placeId),
)
const { data } = response
return data
}
34 changes: 26 additions & 8 deletions apps/web/app/_apis/services/place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
type MapBounds,
type PlaceByMap,
type PlaceByPreview,
type NewPlaceRequest,
type NewPlaceResponse,
BasePlaceSchema,
PlaceByMapSchema,
PlaceDetailSchema,
Expand All @@ -23,19 +25,21 @@ export const getPlacesByRanking = async (
sort: RankingPlaceSort,
campus: CampusType,
): Promise<BasePlace[]> => {
const { data } = await axiosInstance.get(
const { data: response } = await axiosInstance.get(
API_PATH.PLACES.BY_RANKING(sort, campus),
)
const { data } = response
return BasePlaceSchema.array().parse(data)
}

export const getPlacesByCategory = async (
id: string,
campus: CampusType,
): Promise<BasePlace[]> => {
const { data } = await axiosInstance.get(
const { data: response } = await axiosInstance.get(
API_PATH.PLACES.BY_CATEGORY(id, campus),
)
const { data } = response
return BasePlaceSchema.array().parse(data)
}

Expand All @@ -45,20 +49,21 @@ export const getPlacesByMap = async ({
maxLatitude,
maxLongitude,
}: MapBounds): Promise<PlaceByMap[]> => {
const { data } = await axiosInstance.get(
const { data: response } = await axiosInstance.get(
API_PATH.PLACES.BY_MAP({
minLatitude,
minLongitude,
maxLatitude,
maxLongitude,
}),
)

const { data } = response
return PlaceByMapSchema.array().parse(data)
}

export const getPlaceDetail = async (id: string): Promise<PlaceDetail> => {
const { data } = await axiosInstance.get(API_PATH.PLACES.DETAIL(id))
const { data: response } = await axiosInstance.get(API_PATH.PLACES.DETAIL(id))
const { data } = response
return PlaceDetailSchema.parse(data)
}

Expand All @@ -78,20 +83,33 @@ export const getSearchPlaceByKakao = async ({
},
},
)

return data
}

export const getPlaceByPreview = async (
kakaoPlaceId: string,
): Promise<PlaceByPreview> => {
const { data } = await axiosInstance.get(
const { data: response } = await axiosInstance.get(
API_PATH.PLACES.NEW.PREVIEW(kakaoPlaceId),
)
const { data } = response
return PlaceByPreviewSchema.parse(data)
}

export const getPlacesByLike = async (): Promise<BasePlace[]> => {
const { data } = await axiosInstance.get(API_PATH.PLACES.LIKE.GET)
const { data: response } = await axiosInstance.get(API_PATH.PLACES.LIKE.GET)
const { data } = response
return BasePlaceSchema.array().parse(data)
}

export const createNewPlace = async (
placeData: NewPlaceRequest,
): Promise<NewPlaceResponse> => {
const dataSet = {
...placeData,
tagIds: placeData.tagIds.map(Number),
categoryIds: placeData.categoryIds.map(Number),
}
const { data } = await axiosInstance.post(API_PATH.PLACES.NEW.CREATE, dataSet)
return data
}
8 changes: 6 additions & 2 deletions apps/web/app/_apis/services/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import {
} from '@/_apis/schemas/request'

export const getRequests = async (): Promise<Request[]> => {
const { data } = await axiosInstance.get(API_PATH.REQUEST.LIST)
const { data: response } = await axiosInstance.get(API_PATH.REQUEST.LIST)
const { data } = response
return RequestSchema.array().parse(data)
}

export const getRequestDetail = async (id: string): Promise<RequestDetail> => {
const { data } = await axiosInstance.get(API_PATH.REQUEST.DETAIL(id))
const { data: response } = await axiosInstance.get(
API_PATH.REQUEST.DETAIL(id),
)
const { data } = response
return RequestDetailSchema.parse(data)
}
18 changes: 9 additions & 9 deletions apps/web/app/_constants/campus.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant'

export const CAMPUS = {
singwan: '신관',
cheonan: '천안',
yesan: '예산',
SINGWAN: '신관',
CHEONAN: '천안',
YESAN: '예산',
} as const

export const CAMPUS_LOCATION = {
singwan: { latitude: 36.469483428385914, longitude: 127.14059828594706 },
cheonan: { latitude: 36.85101236046876, longitude: 127.15093333537096 },
yesan: { latitude: 36.66990298154316, longitude: 126.85937468290652 },
SINGWAN: { latitude: 36.469483428385914, longitude: 127.14059828594706 },
CHEONAN: { latitude: 36.85101236046876, longitude: 127.15093333537096 },
YESAN: { latitude: 36.66990298154316, longitude: 126.85937468290652 },
} as const

export const CAMPUS_COLOR = {
singwan: COLOR_VARIANTS.red,
cheonan: COLOR_VARIANTS.blue,
yesan: COLOR_VARIANTS.yellow,
SINGWAN: COLOR_VARIANTS.red,
CHEONAN: COLOR_VARIANTS.blue,
YESAN: COLOR_VARIANTS.yellow,
} as const
export type CampusType = keyof typeof CAMPUS
export const CAMPUS_LIST = Object.keys(CAMPUS) as CampusType[]
7 changes: 5 additions & 2 deletions apps/web/app/_constants/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const API_PATH = {
CATEGORY: '/categories',
PLACES: {
BY_CATEGORY: (id: string, campus: CampusType) =>
`/places?categoryId=${id}&campus=${campus}`,
`/categories/${id}/places?campus=${campus}`,
BY_RANKING: (sort: RankingPlaceSort, campus: CampusType) =>
`/places/ranking?sort=${sort}&campus=${campus}`,
BY_MAP: ({
Expand All @@ -14,11 +14,12 @@ export const API_PATH = {
maxLatitude,
maxLongitude,
}: MapBounds) =>
`/places?northEastLatitude=${maxLatitude}&northEastLongitudede=${maxLongitude}&southWestLatitude=${minLatitude}&southWestLongitude=${minLongitude}`,
`/places?maxLat=${maxLatitude}&maxLng=${maxLongitude}&minLat=${minLatitude}&minLng=${minLongitude}`,
DETAIL: (id: string) => `/places/${id}`,
NEW: {
PREVIEW: (kakaoPlaceId: string) =>
`/places/preview?kakaoPlaceId=${kakaoPlaceId}`,
CREATE: '/places',
},
LIKE: {
GET: '/places/like',
Expand All @@ -45,6 +46,8 @@ export const CLIENT_PATH = {
MAIN: '/',
MAP: '/map',
PLACE_NEW: '/places/new',
PLACE_NEW_SUCCESS: '/places/new/success',
PLACE_NEW_FAIL: '/places/new/fail',
PLACE_SEARCH: '/places/search',
PLACE_DETAIL: (id: string | number) => `/places/${id}`,
CATEGORY_DETAIL: (id: string | number) => `/categories/${id}`,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/_mocks/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { CategoryHandlers } from './categoryHandlers'
// import { CategoryHandlers } from './categoryHandlers'
import { PlaceHandlers } from './placeHandlers'
import { EventHandlers } from './eventHandlers'
import { LikeHandlers } from './likeHandlers'
import { RequestHandlers } from './requestHandlers'

export const handlers = [
...CategoryHandlers,
// ...CategoryHandlers,
Comment on lines +1 to +8
Copy link

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

CategoryHandlers 비활성화 확인 필요

CategoryHandlers가 주석 처리되어 있습니다. 카테고리 관련 기능이 여전히 정상적으로 작동하는지 확인이 필요합니다.

다음 스크립트로 카테고리 API 사용처를 확인하여 영향 범위를 검증하세요:


🏁 Script executed:

#!/bin/bash
# Description: 카테고리 관련 API 호출 및 의존성 확인

# 카테고리 API 서비스 호출 확인
echo "=== Category API 사용처 확인 ==="
rg -n --type=ts --type=tsx 'from.*@/_apis/services/category' -A 3

# 카테고리 쿼리 훅 사용 확인
echo -e "\n=== Category Query 사용처 확인 ==="
rg -n --type=ts --type=tsx 'useCategoryQueries' -A 2

Length of output: 114


스크립트가 tsx 파일 타입 인식 문제로 실패했습니다. 수정된 명령어로 다시 검증하겠습니다.


🏁 Script executed:

#!/bin/bash
# Description: 카테고리 관련 API 호출 및 의존성 확인 (수정)

# 카테고리 핸들러 사용처 확인
echo "=== CategoryHandlers 사용처 확인 ==="
rg -n 'CategoryHandlers' --type=ts

# 카테고리 API 서비스 호출 확인 (다양한 패턴)
echo -e "\n=== Category API 서비스 사용처 확인 ==="
rg -n 'category' --type=ts -i | grep -E '(import|from|service|api)' | head -20

# categoryHandlers 파일 존재 확인
echo -e "\n=== categoryHandlers 파일 검색 ==="
fd categoryHandlers

# 전체 handlers 파일 내용 확인
echo -e "\n=== handlers 파일 전체 내용 ==="
cat apps/web/app/_mocks/handlers/index.ts

Length of output: 3116


categoryHandlers 파일의 내용과 useCategoryQueries 구현을 확인하여 영향 범위를 정확하게 파악하겠습니다.


🏁 Script executed:

#!/bin/bash
# Description: 카테고리 핸들러와 쿼리 훅 상세 분석

# categoryHandlers.ts 파일 내용 확인
echo "=== categoryHandlers.ts 파일 내용 ==="
cat apps/web/app/_mocks/handlers/categoryHandlers.ts

# useCategoryQueries 구현 확인
echo -e "\n=== useCategoryQueries 훅 구현 확인 ==="
fd category -type f | grep -E '(queries|query)' | head -10

# category 쿼리 파일 찾기
echo -e "\n=== category 쿼리 파일 정확한 위치 ==="
find . -name "*category*" -type f | grep -E '(queries|query)' | grep -v node_modules

# category 쿼리 파일 내용
echo -e "\n=== category 쿼리 구현 ==="
cat apps/web/app/_apis/queries/category.ts 2>/dev/null || cat apps/web/app/_apis/queries/category/index.ts 2>/dev/null || echo "File not found in expected locations"

Length of output: 1135


getCategories 서비스 구현을 확인하여 실제 API 호출 endpoint를 검증하겠습니다.


🏁 Script executed:

#!/bin/bash
# Description: getCategories 서비스 구현 확인

# category 서비스 파일 확인
echo "=== category 서비스 구현 ==="
cat apps/web/app/_apis/services/category.ts 2>/dev/null || echo "File not found"

# API_PATH 상수 확인 (CATEGORY endpoint)
echo -e "\n=== API_PATH.CATEGORY 정의 확인 ==="
rg -n 'CATEGORY' apps/web/app/_constants/path.ts

# 테스트 파일에서 categoryHandlers 사용 여부 확인
echo -e "\n=== 테스트 파일에서 categoryHandlers 사용 여부 ==="
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx | xargs rg -l 'CategoryHandlers\|category.*handlers\|Category.*mock' 2>/dev/null || echo "테스트 파일에서 CategoryHandlers 직접 사용 없음"

# handlers 등록 파일 자체가 어디서 사용되는지 확인
echo -e "\n=== handlers 파일(index.ts) 사용처 ==="
rg -n 'from.*_mocks/handlers' --type=ts | head -20

Length of output: 687


CategoryHandlers 주석 처리로 인한 카테고리 기능 중단 문제 확인

검증 결과, CategoryHandlers 주석 처리는 실제 문제입니다:

  • getCategories 서비스는 GET /categories 엔드포인트를 호출합니다
  • CategoryHandlers는 정확히 이 엔드포인트를 모킹합니다
  • 5개 컴포넌트(app/page.tsx, places/new/_components/Step/Category/Category.tsx, _components/Categories/Categories.tsx, events/food-slot/page.tsx, categories/[id]/page.tsx)가 useCategoryQueries.list()를 통해 카테고리 데이터를 사용합니다

CategoryHandlers의 주석을 제거하고 활성화하세요:

import { CategoryHandlers } from './categoryHandlers'
...
export const handlers = [
  ...CategoryHandlers,
  ...PlaceHandlers,
  ...EventHandlers,
  ...LikeHandlers,
  ...RequestHandlers,
]
🤖 Prompt for AI Agents
In apps/web/app/_mocks/handlers/index.ts lines 1-8, CategoryHandlers was
commented out which disables the mocked GET /categories endpoint and breaks
components relying on useCategoryQueries.list(); restore and include
CategoryHandlers in the handlers array by uncommenting its import and adding
...CategoryHandlers as the first spread in the exported handlers array so the
categories mock is registered alongside PlaceHandlers, EventHandlers,
LikeHandlers, and RequestHandlers.

...PlaceHandlers,
...EventHandlers,
...LikeHandlers,
Expand Down
62 changes: 31 additions & 31 deletions apps/web/app/_mocks/handlers/placeHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,41 @@ import { http, HttpResponse } from 'msw'
import { API_PATH } from '@/_constants/path'
import { addBaseUrl } from './addBaseUrl'
import {
PlaceDetail,
// PlaceDetail,
Places,
PlacesWithLocation,
PlacePreview,
// PlacesWithLocation,
// PlacePreview,
} from '../data/place'

export const PlaceHandlers = [
http.get(
addBaseUrl(
API_PATH.PLACES.BY_MAP({
maxLatitude: 36.4756348,
maxLongitude: 127.1454263,
minLatitude: 36.4633315,
minLongitude: 127.1357703,
}),
),
() => {
return HttpResponse.json(PlacesWithLocation)
},
),
http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('likes', 'singwan')), () => {
return HttpResponse.json(Places)
}),
http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('views', 'singwan')), () => {
return HttpResponse.json(Places)
}),
http.get(addBaseUrl(API_PATH.PLACES.BY_CATEGORY('1', 'singwan')), () => {
return HttpResponse.json(Places)
}),
http.get(addBaseUrl(API_PATH.PLACES.DETAIL('1')), () => {
return HttpResponse.json(PlaceDetail)
}),
http.get(addBaseUrl(API_PATH.PLACES.NEW.PREVIEW('1')), () => {
return HttpResponse.json(PlacePreview)
}),
// http.get(
// addBaseUrl(
// API_PATH.PLACES.BY_MAP({
// maxLatitude: 36.4756348,
// maxLongitude: 127.1454263,
// minLatitude: 36.4633315,
// minLongitude: 127.1357703,
// }),
// ),
// () => {
// return HttpResponse.json(PlacesWithLocation)
// },
// ),
// http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('likes', 'singwan')), () => {
// return HttpResponse.json(Places)
// }),
// http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('views', 'singwan')), () => {
// return HttpResponse.json(Places)
// }),
// http.get(addBaseUrl(API_PATH.PLACES.BY_CATEGORY('1', 'singwan')), () => {
// return HttpResponse.json(Places)
// }),
// http.get(addBaseUrl(API_PATH.PLACES.DETAIL('1')), () => {
// return HttpResponse.json(PlaceDetail)
// }),
// http.get(addBaseUrl(API_PATH.PLACES.NEW.PREVIEW('1')), () => {
// return HttpResponse.json(PlacePreview)
// }),
http.get(addBaseUrl(API_PATH.PLACES.LIKE.GET), () => {
return HttpResponse.json(Places)
}),
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/_store/campus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ type CampusStore = {
}

export const useCampusStore = create<CampusStore>((set) => ({
campus: 'singwan',
campus: 'SINGWAN',
setCampus: (campus: CampusType) => set({ campus: campus }),
}))
Loading