Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const RequestListItem = ({
<li>
<Column
as={Link}
prefetch={false}
href={CLIENT_PATH.REQUEST_DETAIL(placeId)}
className={cn('gap-1', 'py-3.5', 'border-b-1 border-gray-50')}
>
Expand Down
20 changes: 15 additions & 5 deletions apps/web/app/_apis/queries/event.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
import { queryOptions } from '@tanstack/react-query'
import { getEventInfo, getEventResult } from '@/_apis/services/event'
import {
getPublicEventInfo,
getPrivateEventInfo,
getEventResult,
} from '@/_apis/services/event'

export const EventQueryKeys = {
all: () => ['event'] as const,
info: () => [...EventQueryKeys.all(), 'info'] as const,
publicInfo: () => [...EventQueryKeys.all(), 'info', 'public'] as const,
privateInfo: () => [...EventQueryKeys.all(), 'info', 'private'] as const,
result: () => [...EventQueryKeys.all(), 'result'] as const,
}

export const useEventQueries = {
info: () =>
publicInfo: () =>
queryOptions({
queryKey: EventQueryKeys.info(),
queryFn: getEventInfo,
queryKey: EventQueryKeys.publicInfo(),
queryFn: getPublicEventInfo,
}),
privateInfo: () =>
queryOptions({
queryKey: EventQueryKeys.privateInfo(),
queryFn: getPrivateEventInfo,
}),
result: () =>
queryOptions({
Expand Down
12 changes: 10 additions & 2 deletions apps/web/app/_apis/schemas/event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { z } from 'zod'

export const EventSchema = z.object({
export const PublicEventSchema = z.object({
prize: z.object({
description: z.string(),
imageUrl: z.string(),
}),
})

export const PrivateEventSchema = z.object({
eventId: z.string(),
prize: z.object({
description: z.string(),
Expand All @@ -20,5 +27,6 @@ export const EventResultSchema = z.object({
usedTicketsCount: z.number(),
})

export type Event = z.infer<typeof EventSchema>
export type PublicEvent = z.infer<typeof PublicEventSchema>
export type PrivateEvent = z.infer<typeof PrivateEventSchema>
export type EventResult = z.infer<typeof EventResultSchema>
16 changes: 12 additions & 4 deletions apps/web/app/_apis/services/event.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import axiosInstance from '@/_lib/axiosInstance'
import { API_PATH } from '@/_constants/path'
import {
EventSchema,
PrivateEventSchema,
PublicEventSchema,
EventResultSchema,
type Event,
type EventResult,
type PrivateEvent,
type PublicEvent,
} from '@/_apis/schemas/event'

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

export const getPrivateEventInfo = async (): Promise<PrivateEvent> => {
const { data: response } = await axiosInstance.get(API_PATH.EVENT.INFO)
const { data } = response
return PrivateEventSchema.parse(data)
}

export const participationEvent = async (body: {
Expand Down
17 changes: 9 additions & 8 deletions apps/web/app/_components/CampusSelector/CampusSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client'

import type { Selection } from '@heroui/react'
import type { SharedSelection } from '@heroui/system'
import {
Dropdown,
DropdownTrigger,
Expand All @@ -18,17 +17,20 @@ export const CampusSelector = () => {
const { campus, setCampus } = useCampusStore()
const selectedKeys = new Set([campus]) as Selection

const onSelectionChange = ({ currentKey }: SharedSelection) => {
if (isCampusType(currentKey)) setCampus(currentKey)
const onSelectionChange = (keys: Selection) => {
const newKey = Array.from(keys)[0] as string | undefined
if (isCampusType(newKey)) {
setCampus(newKey)
}
}

return (
<Dropdown>
<DropdownTrigger className='flex items-center gap-1'>
<Text
as={'button'}
fontSize='base'
fontWeight='semibold'
fontSize={'base'}
fontWeight={'semibold'}
className='text-gray-300'
>
<Icon type='swapArrow' size={18} />
Expand All @@ -41,7 +43,7 @@ export const CampusSelector = () => {
selectedKeys={selectedKeys}
selectionMode='single'
variant='flat'
onSelectionChange={onSelectionChange}
onSelectionChange={onSelectionChange} // 5. [수정됨] 이제 이 핸들러가 올바르게 작동합니다.
>
{CAMPUS_LIST.map((campusKey) => (
<DropdownItem key={campusKey}>{CAMPUS[campusKey]}</DropdownItem>
Expand All @@ -53,6 +55,5 @@ export const CampusSelector = () => {

function isCampusType(value: string | undefined): value is CampusType {
if (value === undefined) return false

return value in CAMPUS
return (CAMPUS_LIST as readonly string[]).includes(value)
}
1 change: 1 addition & 0 deletions apps/web/app/_components/Categories/CategoryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Column } from '@repo/ui/components/Layout'
export const CategoryItem = ({ id, name, iconKey }: Category) => (
<Column
as={Link}
prefetch={false}
href={CLIENT_PATH.CATEGORY_DETAIL(id)}
className={'items-center gap-1'}
>
Expand Down
1 change: 1 addition & 0 deletions apps/web/app/_components/PlaceListItem/PlaceListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const PlaceListItem = ({
<li>
<Column
as={Link}
prefetch={false}
className={cn('gap-1', 'pb-4 pt-2.5', {
'border-b-1 border-gray-50': showBorder,
})}
Expand Down
8 changes: 4 additions & 4 deletions apps/web/app/_mocks/handlers/eventHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { http, HttpResponse } from 'msw'
import { event, eventResult } from '../data/event'
import { eventResult } from '../data/event'
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

새로운 public/private 엔드포인트에 대한 mock 핸들러 추가를 고려하세요.

event import를 제거한 것은 주석 처리된 핸들러와 일관성이 있습니다. 그러나 PR 목표에 따르면 이벤트 정보 API가 public/private 버전으로 분리되고 있습니다. 개발 및 테스트 환경에서 새로운 엔드포인트들이 제대로 작동하도록 하려면, API_PATH.EVENT.PUBLIC_INFOAPI_PATH.EVENT.PRIVATE_INFO(또는 유사한 경로)에 대한 mock 핸들러를 추가해야 할 수 있습니다.

다음 스크립트를 실행하여 새로운 public/private 엔드포인트가 정의되어 있는지 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: Check for new public/private event info API paths and their usage

# Search for new API path definitions
rg -n "EVENT\.(PUBLIC_INFO|PRIVATE_INFO)" --type=ts

# Search for services using these new endpoints
rg -n "(getPublicEventInfo|getPrivateEventInfo)" --type=ts -A 3

Length of output: 1516



Wait, I need to actually check the repository. Let me correct that:




Let me also search for where eventResult mock data is defined:



🏁 Script executed:

#!/bin/bash
# Description: View the full eventHandlers.ts file to see current state

cat apps/web/app/_mocks/handlers/eventHandlers.ts

Length of output: 527


🏁 Script executed:

#!/bin/bash
# Description: View the full eventHandlers.ts file to see current state and check for mock handlers

cat apps/web/app/_mocks/handlers/eventHandlers.ts

Length of output: 527


🏁 Script executed:

#!/bin/bash
# Description: Find mock data files

fd -t f "event" apps/web/app/_mocks/data/ --type f

Length of output: 33



---

🏁 Script executed:

```shell
#!/bin/bash
# Description: View mock data file to see available mock structures

cat apps/web/app/_mocks/data/event.ts

Length of output: 703


API_PATH.EVENT.INFO 핸들러 복구 필요

event import 제거와 핸들러 주석 처리가 불일치합니다. 주석 처리된 핸들러(라인 7-9)의 event 변수가 정의되지 않습니다. 새 서비스 함수들(getPublicEventInfo, getPrivateEventInfo)은 동일한 API_PATH.EVENT.INFO 엔드포인트를 호출하므로, 이 핸들러가 복구되어야 mock 환경에서 작동합니다.

필요한 조치:

  • 핸들러 주석 제거 후 event import 복구 (라인 2에 추가)
  • 또는 의도적으로 이 엔드포인트를 mock하지 않으려면, 주석을 완전히 제거하고 주석 처리된 코드를 정리

현재 상태에서는 주석 처리된 라인이 남아 있어 혼동의 여지가 있습니다.

🤖 Prompt for AI Agents
In apps/web/app/_mocks/handlers/eventHandlers.ts around line 2, the file removed
the `event` import but left the API_PATH.EVENT.INFO handler commented out (lines
~7-9) causing an undefined `event` reference and inconsistent mock behavior;
restore the `event` import at line 2 and uncomment the API_PATH.EVENT.INFO
handler so it uses the new service functions
(getPublicEventInfo/getPrivateEventInfo) against the same endpoint, or if you
intend to not mock this endpoint remove the commented handler entirely to avoid
confusion — do one or the other and keep the file consistent.

import { API_PATH } from '@/_constants/path'
import { addBaseUrl } from './addBaseUrl'

export const EventHandlers = [
http.get(addBaseUrl(API_PATH.EVENT.INFO), () => {
return HttpResponse.json(event)
}),
// http.get(addBaseUrl(API_PATH.EVENT.INFO), () => {
// return HttpResponse.json(event)
// }),
http.post(addBaseUrl(API_PATH.EVENT.PARTICIPATIONS), () => {
return HttpResponse.json({ message: '성공' })
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { RemainingTickets } from './RemainingTickets'

export const Participation = () => {
const { isOpen, onOpen, onOpenChange } = useDisclosure()
const { data } = useSuspenseQuery(useEventQueries.info())
const { data } = useSuspenseQuery(useEventQueries.privateInfo())
const {
eventId,
prize,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/events/lucky-draw/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const Page = () => {
<HydrationBoundaryPage
prefetch={async (queryClient) => {
await Promise.all([
queryClient.prefetchQuery(useEventQueries.info()),
queryClient.prefetchQuery(useEventQueries.privateInfo()),
queryClient.prefetchQuery(useEventQueries.result()),
])
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Props = {
}

export const EventWelcome = ({ nextStep }: Props) => {
const { data } = useSuspenseQuery(useEventQueries.info())
const { data } = useSuspenseQuery(useEventQueries.publicInfo())
const { prize } = data

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const PlaceListItem = ({
<li>
<JustifyBetween
as={Link}
prefetch={false}
href={CLIENT_PATH.REQUEST_DETAIL('1')}
className={cn('gap-1', 'py-3.5', 'border-b-1 border-gray-50')}
>
Expand Down
5 changes: 5 additions & 0 deletions apps/web/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ const nextConfig: NextConfig = {
hostname: 'blog.kakaocdn.net',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'example.com',
pathname: '/**',
},
Comment on lines +33 to +37
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

프로덕션 환경에서 작동하지 않을 가능성이 높은 도메인입니다.

example.com은 RFC 2606에서 정의한 문서화 및 예제용 예약 도메인입니다. 실제 상품 이미지가 이 도메인에서 호스팅되지 않으므로, 프로덕션 환경에서 이미지 로딩이 실패할 것입니다.

실제 이미지가 호스팅되는 도메인(예: CDN, 클라우드 스토리지)으로 교체해야 합니다.

다음 스크립트를 실행하여 코드베이스에서 example.com이 다른 곳에서도 사용되는지 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: example.com 도메인 사용처 확인

# example.com 참조 검색
rg -n "example\.com" --type ts --type tsx -C 3

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Description: example.com 도메인 사용처 확인 (수정된 버전)

# 전체 저장소에서 example.com 검색 (파일 타입 제한 없이)
rg -n "example\.com" -C 3

Length of output: 306


🏁 Script executed:

#!/bin/bash
# Description: next.config.ts 파일 전체 내용 확인

# next.config.ts 파일 확인
cat apps/web/next.config.ts

Length of output: 785


🏁 Script executed:

#!/bin/bash
# Description: remotePatterns 섹션 주변 코드 확인

# remotePatterns 관련 코드 검색
rg -n "remotePatterns" apps/web/next.config.ts -A 20 -B 5

Length of output: 589


example.com은 실제 프로덕션 도메인이 아니며 교체가 필요합니다.

코드베이스 전체 검색 결과, example.comapps/web/next.config.ts 줄 35에서만 나타납니다. 다른 설정된 도메인들(pstatic.net, daumcdn.net, blog.kakaocdn.net)은 실제 한국 CDN 제공자들인 반면, example.com은 RFC 2606에서 정의한 예제용 예약 도메인으로 프로덕션 환경에서 작동하지 않습니다.

실제 상품 이미지가 호스팅되는 CDN 또는 클라우드 스토리지 도메인으로 교체하세요.

🤖 Prompt for AI Agents
In apps/web/next.config.ts around lines 33 to 37, the remotePatterns entry uses
the placeholder domain "example.com" which is not a real production CDN; replace
it with your actual CDN or cloud storage hostname (e.g., mycdn.example.net) or
reference an environment variable so the real domain can be injected
per-deployment (e.g., process.env.ASSET_HOST) and keep the protocol/ path
pattern intact (protocol: 'https', hostname: <real-host-or-env>, pathname:
'/**'); ensure the chosen hostname is added consistently to any other
image/asset configuration and deploy-time envs/secrets so production can resolve
assets.

],
},
}
Expand Down