Skip to content

Feat/#107 이벤트 흐름 변경에 따른 기능 구현 및 리팩토링#119

Merged
leeleeleeleejun merged 43 commits intodevelopfrom
feat/#107
Feb 27, 2026
Merged

Feat/#107 이벤트 흐름 변경에 따른 기능 구현 및 리팩토링#119
leeleeleeleejun merged 43 commits intodevelopfrom
feat/#107

Conversation

@leeleeleeleejun
Copy link
Member

@leeleeleeleejun leeleeleeleejun commented Feb 27, 2026

#️⃣연관된 이슈

📝작업 내용

이벤트 도메인의 전반적인 비즈니스 요구사항 및 기획 변경에 맞춰 프론트엔드 로직과 UI를 대대적으로 개편했습니다

1. 당첨자 정보 입력 기능 구현

당첨 시 전화번호와 필수 약관 동의(서비스 이용, 개인정보 수집)를 받는 WinnerInfoForm 컴포넌트 추가
약관 내용 모달 컴포넌트(TermAgreementRow) 추가 및 가독성 높은 UI 적용
당첨자 정보 제출(Submit)을 위한 Mutation 로직 연동

2. 종료된 이벤트 목록 기능 추가

기존 진행 중인 이벤트 외에, 응모 내역 기반으로 '종료된 이벤트'를 조회할 수 있는 기능 추가

3. 이벤트 상세 뷰 리팩토링 (회원/비회원 분리)

middleware.ts에 luckdraw 페이지 제외하고, 인증 상태에 따라 보여지는 UI(뷰)를 명확히 분리하여 렌더링하도록 구조 변경

스크린샷 (선택)

image image image

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 행운 복권 당첨자용 휴대폰 번호 및 약관 제출 폼(WinnerInfoForm) 추가
    • 이벤트 결과 전용 페이지 및 클라이언트 결과 UI 추가
    • 회원/비회원 분리된 이벤트 화면(GuestView, MemberView) 및 새로운 레이아웃 제공
    • 완료된 이벤트 목록 및 빈 상태 컴포넌트(EmptyEventState) 추가
    • 진행 중/완료 탭과 이벤트 항목 요약 및 상세 이동 지원
  • 버그 수정

    • 이벤트 참여/제출 에러 메시지 개선 및 사용자 안내 강화

- '응모 하기' → '진행중인 이벤트'로 변경
- '응모 결과' → '종료된 이벤트'로 변경
- participation → inProgress (진행중인 이벤트)
- result → finished (종료된 이벤트)
- StepType 타입 정의 및 모든 참조 업데이트
Participation을 InProgressEvent로 변경하면서 하위 컴포넌트들도 Participation 접두사를 제거하고 각 역할을 명확히 표현하도록 변경

- Participation → InProgressEvent (진행중인 이벤트)
- ParticipationAction → EventEntryAction (이벤트 참가 액션)
- ParticipationCountdown → EventCountdown (카운트다운)
- ParticipationModal → EntryTicketModal (응모권 선택 모달)
- ParticipationPrize → PrizeInfo (상품 정보)
- 종료된 이벤트 목록에 스크롤 가능하도록 overflow-y-auto 추가
- flex 컨테이너 오버플로우 방지를 위해 min-h-0 추가
- 이벤트 스키마에 nullable 타입 추가 (eventId, prize, eventEndDate)
- 진행 중인 이벤트가 없을 때 EmptyEventState 컴포넌트 표시
구조 분해 할당을 null 체크 이후로 이동하여 prize, eventEndDate가 null일 때 발생하는 에러 해결
서로 다른 이벤트 결과가 같은 캐시를 공유하는 문제 해결
- MSW handler에서 동적 라우트 파라미터 처리
- EventWelcome에서 nullable prize 처리 및 리다이렉트 추가
- console.log 제거
- 미사용 변수 경고 해결 (eslint-disable 주석 추가)
- AGENTS.md 규칙에 따라 import 순서 정리 (Node/built-in → External → Absolute → Relative)
- 에러 로깅 추가 (console.error)
- AxiosError를 통한 서버 에러 메시지 표시
- 중복 color/severity prop 제거
- queryKey spread 연산자 제거
- WinnerInfoForm 컴포넌트로 전화번호 입력 폼 구현
- useSubmitWinnerPhoneNumber mutation hook 추가
- WinnerPhoneNumberSchema로 전화번호 유효성 검증 (010-XXXX-XXXX 형식)
- submitWinnerPhoneNumber API 서비스 함수 추가
- ResultModal에 eventId prop 전달 및 폼 통합
- 성공/실패 시 토스트 메시지 표시
- MemberView 폴더 내부로 이동
@leeleeleeleejun leeleeleeleejun self-assigned this Feb 27, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e00a65 and 61575ce.

📒 Files selected for processing (1)
  • apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx

Walkthrough

이 PR은 lucky-draw 이벤트 기능을 재구조화합니다. API 쿼리/서비스를 공개/비공개/항목 엔드포인트로 분리하고, UI를 GuestView/MemberView로 분할하며, 이벤트 결과 페이지와 당첨자 제출 양식(및 관련 뮤테이션/스키마/경로)을 추가합니다.

Changes

Cohort / File(s) Summary
API 스키마 및 타입 재정의
apps/web/app/_apis/schemas/event.ts
상세한 EventPrizeSchema/BaseEventSchema 도입 및 EventByPublic/EventByPrivate/EventByEntry/EventResult/EventWinnerForm 등 스키마·타입 재구성
API 쿼리 키 재조직화
apps/web/app/_apis/queries/event.ts
EventQueryKeys 및 useEventQueries: public/private/entry 키로 재명명(byPublic/byPrivate/byEntry) 및 result(eventId)로 파라미터화
API 서비스 함수 리팩토링
apps/web/app/_apis/services/event.ts
getEventByPublic/getEventByPrivate/getEventByEntries/getEventResult(eventId) 추가, submitWinnerForm(eventId, data) 추가, participation POST 경로 ENTRIES로 변경
뮤테이션 훅
apps/web/app/_apis/mutations/useParticipationEvent.ts, apps/web/app/_apis/mutations/useSubmitWinnerForm.ts
useParticipationEvent: AxiosError 검사 및 에러 메시지 파싱 추가; useSubmitWinnerForm: 신규 뮤테이션 훅 추가(성공/오류 토스트 및 query invalidation)
경로 상수 업데이트
apps/web/app/_constants/path.ts
API_PATH.EVENT.PARTICIPATIONS → ENTRIES, API_PATH.EVENT.RESULT → 함수(eventId), API_PATH.EVENT.APPLY 추가, CLIENT_PATH.EVENTS_RESULT 추가
목 데이터 및 핸들러
apps/web/app/_mocks/data/event.ts, apps/web/app/_mocks/handlers/eventHandlers.ts
목 타입을 EventByPrivate로 변경, EVENT_ENTRIES 목데이터 추가, ENTRIES GET 핸들러 활성화
레이아웃 및 페이지 분리
apps/web/app/events/lucky-draw/layout.tsx, apps/web/app/events/lucky-draw/page.tsx, apps/web/app/events/lucky-draw/LuckyDraw.tsx
LuckyDrawLayout 추가, page.tsx를 async로 변경하여 쿠키 기반으로 MemberView/GuestView 조건부 렌더링, 기존 LuckyDraw 컴포넌트 삭제
게스트/멤버 뷰 및 하위 컴포넌트
apps/web/app/events/lucky-draw/_components/Pages/GuestView/*, .../MemberView/*, .../EmptyEventState.tsx
GuestView, MemberView 추가; InProgressEvent/FinishedEvent/EmptyEventState 등 컴포넌트 추가·이름 변경(Participation→InProgressEvent 등) 및 인덱스 재내보내기 업데이트
결과 페이지·당첨자 폼
apps/web/app/events/lucky-draw/result/[id]/*
EventResultClient, ResultModal API/props 변경(이벤트 ID·isPhoneSubmitted 추가, onAnimationStop으로 이름 변경), WinnerInfoForm/TermAgreementCheckbox/약관 상수 추가 및 결과 페이지 라우트 생성
기타 변경
apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx, apps/web/middleware.ts
useEventQueries.byPublic 사용으로 전환 및 이벤트 부재시 리다이렉트 추가, middleware matcher를 /events/lucky-draw/result/:path*로 수정

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser
  participant ClientComp as EventResultClient
  participant Modal as ResultModal/WinnerInfoForm
  participant API as Server(API)
  Browser->>ClientComp: 페이지 로드 (eventId)
  ClientComp->>API: GET /events/{eventId}/entries (useSuspenseQuery)
  API-->>ClientComp: EventResult 데이터
  Browser->>ClientComp: 사용자 클릭 "추첨 시작"
  ClientComp->>Modal: 모달 열기 (애니메이션 시작)
  alt 당첨 & 전화 미제출
    Modal->>Browser: WinnerInfoForm 표시
    Browser->>Modal: 폼 제출(phone, agreements)
    Modal->>API: POST /events/{eventId}/apply (submitWinnerForm)
    API-->>Modal: 200 OK
    Modal-->>ClientComp: onAnimationStop / 닫기
  else 비당첨 또는 제출 완료
    Modal-->>ClientComp: onAnimationStop / 닫기
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

✨ Feature, 🔨 Refactor

Suggested reviewers

  • jcw1031
  • gihhyeon

Poem

🐰 행운 복권이 새옷을 입었네,
게스트는 구경, 회원은 참여하고,
당첨자 폼엔 전화와 약관 한 줄,
서버로 폼 날리면 빵! 알림이 와,
토끼도 깡총, 코드도 깔끔해 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: implementing features and refactoring following event flow changes (Feat/#107 이벤트 흐름 변경에 따른 기능 구현 및 리팩토링). It accurately reflects the PR's comprehensive updates to event domain logic and UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#107

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.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx (1)

26-57: ⚠️ Potential issue | 🟠 Major

모달 재오픈 또는 잔여 응모권 변경 시 ticketsCount가 동기화되지 않습니다.

Line 26의 useState(remainingTicketsCount)는 초기 렌더에서만 반영됩니다. 이후 잔여 수량이 바뀌어도 이전 값이 남아 잘못된 ticketsCount가 Line 30에서 전송될 수 있습니다.

🔧 제안 수정안
-import { useState } from 'react'
+import { useEffect, useState } from 'react'
@@
   const [ticketsCount, setTicketsCount] = useState(remainingTicketsCount)
   const { mutate: participationEvent } = useParticipationEvent()
+
+  useEffect(() => {
+    if (isOpen) {
+      setTicketsCount(remainingTicketsCount)
+    }
+  }, [isOpen, remainingTicketsCount])
@@
               <NumberInput
-                defaultValue={remainingTicketsCount}
                 minValue={1}
                 maxValue={remainingTicketsCount}
                 label={'응모권 개수'}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx`
around lines 26 - 57, The local state initialized with
useState(remainingTicketsCount) (ticketsCount / setTicketsCount) is not kept in
sync when remainingTicketsCount or the modal open state changes; add an effect
in EntryTicketModal that calls setTicketsCount(remainingTicketsCount) whenever
remainingTicketsCount or isOpen changes (or when the modal opens) so the
NumberInput value and the payload sent by participationEvent({ eventId,
ticketsCount }) are always current; ensure you still keep controlled value prop
on NumberInput and avoid relying only on defaultValue.
🧹 Nitpick comments (14)
apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventCountdown.tsx (1)

7-7: 반환 타입을 명시해 주세요.

Line 7의 EventCountdown도 반환 타입이 빠져 있습니다. 컴포넌트 시그니처를 명시하면 타입 안정성이 높아집니다.

🔧 제안 수정안
-export const EventCountdown = ({ eventEndDate }: { eventEndDate: string }) => {
+export const EventCountdown = ({
+  eventEndDate,
+}: {
+  eventEndDate: string
+}): JSX.Element => {

As per coding guidelines **/*.{ts,tsx}: Use TypeScript in strict mode with noUncheckedIndexedAccess enabled and return types required on functions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventCountdown.tsx`
at line 7, The EventCountdown function component lacks an explicit return type;
update the component signature (export const EventCountdown = ({ eventEndDate }:
{ eventEndDate: string }) => ...) to include a return type such as : JSX.Element
or : React.ReactElement (e.g., export const EventCountdown = (...) : JSX.Element
=> ...) so the component has an explicit TypeScript return type and satisfies
strict/noUncheckedIndexedAccess rules; keep the existing prop type for
eventEndDate and ensure any necessary React types are imported if not already.
apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx (1)

20-30: 컴포넌트/핸들러 반환 타입을 명시해 주세요.

Line 20의 EntryTicketModal과 Line 29의 onPress에 반환 타입을 명시하면 strict 환경에서 시그니처 안정성이 좋아집니다.

🔧 제안 수정안
-export const EntryTicketModal = ({
+export const EntryTicketModal = ({
   isOpen,
   onOpenChange,
   eventId,
   remainingTicketsCount,
-}: Props) => {
+}: Props): JSX.Element => {
@@
-  const onPress = () => {
+  const onPress = (): void => {
     participationEvent({ eventId, ticketsCount })
   }

As per coding guidelines **/*.{ts,tsx}: Use TypeScript in strict mode with noUncheckedIndexedAccess enabled and return types required on functions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx`
around lines 20 - 30, Add explicit return types: annotate the EntryTicketModal
component to return JSX.Element (e.g., change the component signature to return
JSX.Element) and annotate the onPress handler to return void (e.g., onPress: ()
=> void). Update the function signatures for EntryTicketModal and onPress
(referencing the EntryTicketModal declaration and the onPress function) so
TypeScript strict mode knows the expected return types.
apps/web/app/events/lucky-draw/layout.tsx (1)

8-8: 컴포넌트 반환 타입을 명시해 주세요.

Line 8의 LuckyDrawLayout은 반환 타입이 생략되어 있습니다. strict 설정에서 추론 변화로 인한 회귀를 줄이기 위해 명시 타입을 두는 편이 안전합니다.

🔧 제안 수정안
-const LuckyDrawLayout = ({ children }: { children: React.ReactNode }) => {
+const LuckyDrawLayout = ({
+  children,
+}: {
+  children: React.ReactNode
+}): JSX.Element => {

As per coding guidelines **/*.{ts,tsx}: Use TypeScript in strict mode with noUncheckedIndexedAccess enabled and return types required on functions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/layout.tsx` at line 8, The LuckyDrawLayout
component is missing an explicit return type; update its signature (function
LuckyDrawLayout) to declare a return type (e.g., : React.ReactElement or :
JSX.Element) while keeping the existing props type ({ children }: { children:
React.ReactNode }) so the function has an explicit React return type to satisfy
strict/noUncheckedIndexedAccess rules.
apps/web/app/_mocks/handlers/eventHandlers.ts (1)

7-18: 주석 처리된 핸들러 정리 고려

주석 처리된 mock 핸들러들이 여러 개 있습니다. 나중에 사용할 계획이라면 TODO 주석을 추가하고, 그렇지 않다면 삭제하는 것이 좋습니다.

♻️ 제안된 정리
 export const EventHandlers = [
-  // http.get(addBaseUrl(API_PATH.EVENT.INFO), () => {
-  //   return HttpResponse.json(event)
-  // }),
-  // http.post(addBaseUrl(API_PATH.EVENT.ENTRIES), () => {
-  //   return HttpResponse.json({ message: '성공' })
-  // }),
-  // http.get(addBaseUrl('/events/:eventId/entries'), () => {
-  //   return HttpResponse.json(eventResult)
-  // }),
   http.get(addBaseUrl(API_PATH.EVENT.ENTRIES), () => {
     return HttpResponse.json(EVENT_ENTRIES)
   }),
+  // TODO: 추가 핸들러 필요시 구현
 ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_mocks/handlers/eventHandlers.ts` around lines 7 - 18, The file
contains several commented-out mock handlers (the http.get/http.post blocks
referencing API_PATH.EVENT.INFO, API_PATH.EVENT.ENTRIES,
'/events/:eventId/entries' and variables event/eventResult) that should be
either removed or annotated; decide whether you need them later and if not
delete these commented handlers, otherwise replace each commented block with a
concise TODO comment explaining its intended future use (e.g., "TODO: re-enable
mock for EVENT.INFO when implementing X") next to the related symbol
(http.get/http.post, API_PATH.EVENT.INFO, API_PATH.EVENT.ENTRIES,
'/events/:eventId/entries', event, eventResult) so the purpose is clear.
apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx (1)

9-27: JSX 속성에 일관된 따옴표 사용 권장

코딩 가이드라인에 따르면 jsxSingleQuote: true가 설정되어 있어 JSX 속성값에 작은따옴표를 사용해야 합니다. 일부 className에서 큰따옴표가 사용되고 있습니다.

♻️ 제안된 수정
       <Flex className={'gap-1'}>
-        <Text variant='title1' className='text-gray-300'>
+        <Text variant={'title1'} className={'text-gray-300'}>
           현재 진행 중인 럭키드로우가 없습니다
         </Text>
         <Icon type={'cry'} />
       </Flex>

       <Column className={'items-center'}>
         <Text
-          variant='body1'
-          className='whitespace-pre-wrap break-words text-center text-gray-300'
+          variant={'body1'}
+          className={'whitespace-pre-wrap break-words text-center text-gray-300'}
         >
           맛집 리뷰를 작성하고 응모권을 모아보세요
         </Text>
         <Text
-          variant='body1'
-          className='whitespace-pre-wrap break-words text-center text-gray-300'
+          variant={'body1'}
+          className={'whitespace-pre-wrap break-words text-center text-gray-300'}
         >
           다음 럭키드로우 이벤트에서 행운의 주인공이 되실 수 있습니다!
         </Text>
       </Column>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx` around
lines 9 - 27, Some JSX attributes in EmptyEventState.tsx use double quotes for
className while project enforces jsxSingleQuote: true; update all JSX attribute
quotes to single quotes (e.g., change className="..." to className='...') across
the components in this file—specifically adjust the className props on <Flex>,
<Column>, and the two <Text> elements (and any other JSX attributes like Icon
type if inconsistent) to use single quotes, then run your formatter/ESLint to
confirm consistency.
apps/web/app/_apis/mutations/useSubmitWinnerForm.ts (2)

4-6: import 순서를 가이드라인 순서로 맞춰주세요.

현재 상대 경로 import가 절대 경로 import보다 먼저 와 있습니다. 정렬 규칙에 맞추면 파일 가독성이 올라갑니다.

As per coding guidelines **/*.{ts,tsx,js,jsx}: Organize imports in the following order: Node/built-in, external packages, absolute imports, relative imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_apis/mutations/useSubmitWinnerForm.ts` around lines 4 - 6,
Reorder the import statements in useSubmitWinnerForm.ts so they follow the
repository guideline (Node/built-in, external packages, absolute imports, then
relative imports): move the absolute imports (EventWinnerForm from
'@/_apis/schemas/event' and EventQueryKeys from '@/_apis/queries/event') above
the relative import (submitWinnerForm from '../services/event'), keeping the
same imported symbols and names.

18-20: queryKey는 스프레드 없이 키 팩토리 반환값을 그대로 사용하세요.

EventQueryKeys.result(eventId)는 이미 올바른 키 튜플이라, 스프레드는 불필요합니다.

✍️ 제안 수정
       queryClient.invalidateQueries({
-        queryKey: [...EventQueryKeys.result(eventId)],
+        queryKey: EventQueryKeys.result(eventId),
       })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_apis/mutations/useSubmitWinnerForm.ts` around lines 18 - 20,
The invalidate call is spreading the key tuple unnecessarily; update the call to
pass the key factory result directly by replacing
queryClient.invalidateQueries({ queryKey: [...EventQueryKeys.result(eventId)] })
with queryClient.invalidateQueries({ queryKey: EventQueryKeys.result(eventId)
}), referencing the existing queryClient.invalidateQueries and
EventQueryKeys.result symbols.
apps/web/app/events/lucky-draw/page.tsx (1)

5-14: 페이지 레벨에서 HydrationBoundaryPage 패턴을 유지하는 게 안전합니다.

GuestView/MemberView가 suspense query를 사용하는 구조라면, 이 페이지에서도 SSR prefetch + hydration 경계를 두는 쪽이 로딩/일관성 측면에서 더 안정적입니다.

Based on learnings Applies to **/(page|layout).{ts,tsx} : Use HydrationBoundaryPage for server-side rendering with prefetching.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/page.tsx` around lines 5 - 14, The page
currently returns GuestView or MemberView directly in Page, which breaks the
HydrationBoundaryPage SSR+prefetch pattern; wrap the conditional render in the
HydrationBoundaryPage component and perform any server-side prefetching before
returning so both MemberView and GuestView are hydrated consistently—update the
Page function to fetch cookies/accessToken as now, run required prefetches for
data used by MemberView/GuestView, then return
<HydrationBoundaryPage>{accessToken ? <MemberView/> :
<GuestView/>}</HydrationBoundaryPage> (use the HydrationBoundaryPage wrapper and
call the components' prefetch hooks or loaders where applicable).
apps/web/app/events/lucky-draw/result/[id]/page.tsx (1)

3-7: 페이지 함수에 명시적 반환 타입을 추가해주세요.

현재 시그니처에 반환 타입이 없어, 프로젝트 타입 규칙과 어긋납니다.

✍️ 제안 수정
-const EventResultPage = async ({
+const EventResultPage = async ({
   params,
 }: {
   params: Promise<{ id: string }>
-}) => {
+}): Promise<JSX.Element> => {

As per coding guidelines **/*.{ts,tsx}: Use TypeScript in strict mode with noUncheckedIndexedAccess enabled and return types required on functions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/result/`[id]/page.tsx around lines 3 - 7, The
EventResultPage async function lacks an explicit return type; update its
signature to include a Promise return type (e.g., Promise<JSX.Element> or
Promise<React.ReactElement>) so it conforms to the project's strict TypeScript
rules. Locate the EventResultPage constant (the async function taking params)
and annotate it with the chosen return type, ensuring any JSX returned inside
matches that type and imports React types if necessary.
apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx (1)

39-86: Title/Prize UI는 공용 컴포넌트 추출을 고려해주세요.

apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx의 동일한 블록과 중복이 커서, 문구/스타일 변경 시 동시 수정 포인트가 늘어납니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx`
around lines 39 - 86, Duplicate UI blocks Title and Prize should be extracted
into a shared component to avoid duplicated maintenance; create a reusable
component (e.g., EventHeader or GiftPrize) that accepts props for texts and
imageUrl and move the JSX from Title and Prize into that component, export it,
then replace the local Title and Prize with imports and prop usages in GuestView
(symbols: Title, Prize) and the other file where the same block appears (symbol:
EventWelcome) so both use the single shared component; ensure props cover
variant/styles and accessibility attributes (alt, priority) and update
imports/exports accordingly.
apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx (2)

14-14: 타입 전용 import에 type 키워드를 사용하세요.

EventResult는 타입으로만 사용되므로, strict TypeScript 모드에서는 import type을 사용하는 것이 권장됩니다.

♻️ 제안된 수정
-import { EventResult } from '@/_apis/schemas/event'
+import type { EventResult } from '@/_apis/schemas/event'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/result/`[id]/EventResultClient.tsx at line 14,
The import of EventResult in EventResultClient.tsx is used only as a type;
change the statement `import { EventResult } from '@/_apis/schemas/event'` to a
type-only import by using `import type { EventResult } from
'@/_apis/schemas/event'` so it compiles under strict TypeScript settings and
avoids runtime import emissions.

41-46: 컴포넌트 언마운트 시 setTimeout 정리가 필요할 수 있습니다.

onClick 핸들러의 setTimeout이 컴포넌트 언마운트 후에도 실행될 수 있어, 언마운트된 컴포넌트의 상태를 업데이트하려고 시도할 수 있습니다. 현재 800ms의 짧은 지연이므로 실제 문제 발생 가능성은 낮지만, 방어적 코딩을 위해 cleanup을 고려할 수 있습니다.

♻️ useRef를 사용한 cleanup 예시
const timerRef = useRef<NodeJS.Timeout | null>(null)

useEffect(() => {
  return () => {
    if (timerRef.current) clearTimeout(timerRef.current)
  }
}, [])

const onClick = () => {
  setIsRunning(true)
  timerRef.current = setTimeout(() => {
    onOpen()
  }, LOTTERY_ANIMATION_DURATION_MS)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/result/`[id]/EventResultClient.tsx around
lines 41 - 46, The setTimeout in the onClick handler can fire after the
component unmounts and should be cleaned up: add a timer ref (e.g., timerRef via
useRef<NodeJS.Timeout | null>) in the EventResultClient component, assign the
timeout ID to timerRef when calling setTimeout inside onClick, and add a
useEffect cleanup that calls clearTimeout(timerRef.current) (and sets it to
null) on unmount; also consider clearing any existing timer before setting a new
one in onClick to avoid overlapping timers.
apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx (1)

35-40: 탭 전환 시 Suspense 경계 구조를 개선할 수 있습니다.

현재 두 개의 Suspense 경계가 항상 렌더링되고 내부에서 조건부로 컴포넌트를 마운트합니다. 탭 전환 시 컴포넌트 상태를 유지할 필요가 없다면, key prop을 사용하여 단일 Suspense로 단순화할 수 있습니다:

♻️ 제안된 리팩토링
-      <Suspense fallback={<Spinner className={'m-auto'} />}>
-        {currentTab === 'inProgress' && <InProgressEvent />}
-      </Suspense>
-      <Suspense fallback={<Spinner className={'m-auto'} />}>
-        {currentTab === 'finished' && <FinishedEvent />}
-      </Suspense>
+      <Suspense key={currentTab} fallback={<Spinner className={'m-auto'} />}>
+        {currentTab === 'inProgress' ? <InProgressEvent /> : <FinishedEvent />}
+      </Suspense>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx`
around lines 35 - 40, Two separate Suspense boundaries always render and
conditionally mount InProgressEvent/FinishedEvent; simplify by using a single
Suspense wrapping the tab content and use currentTab as a key so the correct
component is mounted/cleared on tab switch. Replace the two Suspense blocks with
one Suspense fallback={<Spinner className={'m-auto'} />} that renders either
<InProgressEvent /> or <FinishedEvent /> based on currentTab and assign
key={currentTab} to the rendered child to ensure the non-active tab is unmounted
and state is not preserved.
apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx (1)

32-62: EventSummary 컴포넌트 중복에 대한 TODO가 있습니다.

TODO 주석에서 언급한 대로, EventSummaryEventResultClient.tsx에도 유사하게 구현되어 있습니다. 날짜 포맷팅 로직(eventEndDate.slice(0, 10).replace(/-/g, '.'))도 동일합니다. 리팩토링 시 공통 컴포넌트와 날짜 포맷 유틸리티 함수로 추출하는 것을 권장합니다.

공통 EventSummary 컴포넌트와 날짜 포맷팅 유틸리티를 생성하는 것을 도와드릴까요? 또는 이 작업을 추적하기 위한 이슈를 생성할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx`
around lines 32 - 62, EventSummary is duplicated (also in EventResultClient.tsx)
and repeats date formatting logic; extract a shared EventSummary component
(replace the local EventSummary in FinishedEvent.tsx and the one in
EventResultClient.tsx) and move the date formatting into a small utility (e.g.,
formatEventDate) used by both; update FinishedEvent's EventSummary usage to
import the shared component and call formatEventDate(eventEndDate) instead of
eventEndDate.slice(...).replace(...) so both files share the same UI and
formatting logic.
🤖 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/web/app/_apis/schemas/event.ts`:
- Around line 31-35: EventResultSchema currently extends BaseEventSchema which
forces result responses to include BaseEventSchema fields (e.g., prize,
totalWinnersCount, participantsCount, eventEndDate) and causes parsing/type
failures against the eventResult mock; change EventResultSchema to be a
standalone schema that only validates result-specific fields (isWinner,
usedTicketsCount, isPhoneSubmitted) or create a separate Result-only schema and
use that for response parsing instead of BaseEventSchema.extend; update
references to EventResultSchema where result responses are validated so they
consume the new result-only schema and ensure compatibility with
apps/web/app/_mocks/data/event.ts's eventResult shape.

In `@apps/web/app/_constants/path.ts`:
- Around line 39-41: Add explicit return types to the arrow functions in the
path constants: change RESULT and APPLY to include a : string return type (i.e.,
RESULT: (eventId: string): string => `/events/${eventId}/entries` and APPLY:
(eventId: string): string => `/events/${eventId}/apply`) so the TypeScript files
comply with the rule that functions must declare their return type.

In
`@apps/web/app/events/lucky-draw/result/`[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx:
- Line 56: In WinnerInfoForm, the HeroUI Input uses the wrong prop name: replace
the Input prop "disabled={isSubmitting}" with "isDisabled={isSubmitting}" so it
matches the component API and stays consistent with the existing "isInvalid"
usage; update the Input instance in the WinnerInfoForm component (where
isSubmitting is referenced) to use isDisabled.

---

Outside diff comments:
In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx`:
- Around line 26-57: The local state initialized with
useState(remainingTicketsCount) (ticketsCount / setTicketsCount) is not kept in
sync when remainingTicketsCount or the modal open state changes; add an effect
in EntryTicketModal that calls setTicketsCount(remainingTicketsCount) whenever
remainingTicketsCount or isOpen changes (or when the modal opens) so the
NumberInput value and the payload sent by participationEvent({ eventId,
ticketsCount }) are always current; ensure you still keep controlled value prop
on NumberInput and avoid relying only on defaultValue.

---

Nitpick comments:
In `@apps/web/app/_apis/mutations/useSubmitWinnerForm.ts`:
- Around line 4-6: Reorder the import statements in useSubmitWinnerForm.ts so
they follow the repository guideline (Node/built-in, external packages, absolute
imports, then relative imports): move the absolute imports (EventWinnerForm from
'@/_apis/schemas/event' and EventQueryKeys from '@/_apis/queries/event') above
the relative import (submitWinnerForm from '../services/event'), keeping the
same imported symbols and names.
- Around line 18-20: The invalidate call is spreading the key tuple
unnecessarily; update the call to pass the key factory result directly by
replacing queryClient.invalidateQueries({ queryKey:
[...EventQueryKeys.result(eventId)] }) with queryClient.invalidateQueries({
queryKey: EventQueryKeys.result(eventId) }), referencing the existing
queryClient.invalidateQueries and EventQueryKeys.result symbols.

In `@apps/web/app/_mocks/handlers/eventHandlers.ts`:
- Around line 7-18: The file contains several commented-out mock handlers (the
http.get/http.post blocks referencing API_PATH.EVENT.INFO,
API_PATH.EVENT.ENTRIES, '/events/:eventId/entries' and variables
event/eventResult) that should be either removed or annotated; decide whether
you need them later and if not delete these commented handlers, otherwise
replace each commented block with a concise TODO comment explaining its intended
future use (e.g., "TODO: re-enable mock for EVENT.INFO when implementing X")
next to the related symbol (http.get/http.post, API_PATH.EVENT.INFO,
API_PATH.EVENT.ENTRIES, '/events/:eventId/entries', event, eventResult) so the
purpose is clear.

In `@apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx`:
- Around line 9-27: Some JSX attributes in EmptyEventState.tsx use double quotes
for className while project enforces jsxSingleQuote: true; update all JSX
attribute quotes to single quotes (e.g., change className="..." to
className='...') across the components in this file—specifically adjust the
className props on <Flex>, <Column>, and the two <Text> elements (and any other
JSX attributes like Icon type if inconsistent) to use single quotes, then run
your formatter/ESLint to confirm consistency.

In `@apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx`:
- Around line 39-86: Duplicate UI blocks Title and Prize should be extracted
into a shared component to avoid duplicated maintenance; create a reusable
component (e.g., EventHeader or GiftPrize) that accepts props for texts and
imageUrl and move the JSX from Title and Prize into that component, export it,
then replace the local Title and Prize with imports and prop usages in GuestView
(symbols: Title, Prize) and the other file where the same block appears (symbol:
EventWelcome) so both use the single shared component; ensure props cover
variant/styles and accessibility attributes (alt, priority) and update
imports/exports accordingly.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx`:
- Around line 32-62: EventSummary is duplicated (also in EventResultClient.tsx)
and repeats date formatting logic; extract a shared EventSummary component
(replace the local EventSummary in FinishedEvent.tsx and the one in
EventResultClient.tsx) and move the date formatting into a small utility (e.g.,
formatEventDate) used by both; update FinishedEvent's EventSummary usage to
import the shared component and call formatEventDate(eventEndDate) instead of
eventEndDate.slice(...).replace(...) so both files share the same UI and
formatting logic.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx`:
- Around line 20-30: Add explicit return types: annotate the EntryTicketModal
component to return JSX.Element (e.g., change the component signature to return
JSX.Element) and annotate the onPress handler to return void (e.g., onPress: ()
=> void). Update the function signatures for EntryTicketModal and onPress
(referencing the EntryTicketModal declaration and the onPress function) so
TypeScript strict mode knows the expected return types.

In
`@apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventCountdown.tsx`:
- Line 7: The EventCountdown function component lacks an explicit return type;
update the component signature (export const EventCountdown = ({ eventEndDate }:
{ eventEndDate: string }) => ...) to include a return type such as : JSX.Element
or : React.ReactElement (e.g., export const EventCountdown = (...) : JSX.Element
=> ...) so the component has an explicit TypeScript return type and satisfies
strict/noUncheckedIndexedAccess rules; keep the existing prop type for
eventEndDate and ensure any necessary React types are imported if not already.

In `@apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx`:
- Around line 35-40: Two separate Suspense boundaries always render and
conditionally mount InProgressEvent/FinishedEvent; simplify by using a single
Suspense wrapping the tab content and use currentTab as a key so the correct
component is mounted/cleared on tab switch. Replace the two Suspense blocks with
one Suspense fallback={<Spinner className={'m-auto'} />} that renders either
<InProgressEvent /> or <FinishedEvent /> based on currentTab and assign
key={currentTab} to the rendered child to ensure the non-active tab is unmounted
and state is not preserved.

In `@apps/web/app/events/lucky-draw/layout.tsx`:
- Line 8: The LuckyDrawLayout component is missing an explicit return type;
update its signature (function LuckyDrawLayout) to declare a return type (e.g.,
: React.ReactElement or : JSX.Element) while keeping the existing props type ({
children }: { children: React.ReactNode }) so the function has an explicit React
return type to satisfy strict/noUncheckedIndexedAccess rules.

In `@apps/web/app/events/lucky-draw/page.tsx`:
- Around line 5-14: The page currently returns GuestView or MemberView directly
in Page, which breaks the HydrationBoundaryPage SSR+prefetch pattern; wrap the
conditional render in the HydrationBoundaryPage component and perform any
server-side prefetching before returning so both MemberView and GuestView are
hydrated consistently—update the Page function to fetch cookies/accessToken as
now, run required prefetches for data used by MemberView/GuestView, then return
<HydrationBoundaryPage>{accessToken ? <MemberView/> :
<GuestView/>}</HydrationBoundaryPage> (use the HydrationBoundaryPage wrapper and
call the components' prefetch hooks or loaders where applicable).

In `@apps/web/app/events/lucky-draw/result/`[id]/EventResultClient.tsx:
- Line 14: The import of EventResult in EventResultClient.tsx is used only as a
type; change the statement `import { EventResult } from '@/_apis/schemas/event'`
to a type-only import by using `import type { EventResult } from
'@/_apis/schemas/event'` so it compiles under strict TypeScript settings and
avoids runtime import emissions.
- Around line 41-46: The setTimeout in the onClick handler can fire after the
component unmounts and should be cleaned up: add a timer ref (e.g., timerRef via
useRef<NodeJS.Timeout | null>) in the EventResultClient component, assign the
timeout ID to timerRef when calling setTimeout inside onClick, and add a
useEffect cleanup that calls clearTimeout(timerRef.current) (and sets it to
null) on unmount; also consider clearing any existing timer before setting a new
one in onClick to avoid overlapping timers.

In `@apps/web/app/events/lucky-draw/result/`[id]/page.tsx:
- Around line 3-7: The EventResultPage async function lacks an explicit return
type; update its signature to include a Promise return type (e.g.,
Promise<JSX.Element> or Promise<React.ReactElement>) so it conforms to the
project's strict TypeScript rules. Locate the EventResultPage constant (the
async function taking params) and annotate it with the chosen return type,
ensuring any JSX returned inside matches that type and imports React types if
necessary.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b597ab2 and 1e00a65.

📒 Files selected for processing (43)
  • apps/web/app/_apis/mutations/useParticipationEvent.ts
  • apps/web/app/_apis/mutations/useSubmitWinnerForm.ts
  • apps/web/app/_apis/queries/event.ts
  • apps/web/app/_apis/schemas/event.ts
  • apps/web/app/_apis/services/event.ts
  • apps/web/app/_constants/path.ts
  • apps/web/app/_mocks/data/event.ts
  • apps/web/app/_mocks/handlers/eventHandlers.ts
  • apps/web/app/events/lucky-draw/LuckyDraw.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/EmptyEventState.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/GuestView/GuestView.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/GuestView/index.ts
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/FinishedEvent.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/FinishedEvent/index.ts
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EntryTicketModal.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventCountdown.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/EventEntryAction.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/InProgressEvent.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/PrizeInfo.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/RemainingTickets.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/InProgressEvent/index.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/MemberView.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/NavBarItem/NavBarItem.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/NavBarItem/index.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/MemberView/index.ts
  • apps/web/app/events/lucky-draw/_components/Pages/Participation/index.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Result/NoResult.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Result/Result.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Result/index.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/index.tsx
  • apps/web/app/events/lucky-draw/layout.tsx
  • apps/web/app/events/lucky-draw/page.tsx
  • apps/web/app/events/lucky-draw/result/[id]/EventResultClient.tsx
  • apps/web/app/events/lucky-draw/result/[id]/ResultModal.tsx
  • apps/web/app/events/lucky-draw/result/[id]/_components/LottoBalls/LottoBalls.tsx
  • apps/web/app/events/lucky-draw/result/[id]/_components/LottoBalls/index.tsx
  • apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/TermAgreementCheckbox.tsx
  • apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/WinnerInfoForm.tsx
  • apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/constants.ts
  • apps/web/app/events/lucky-draw/result/[id]/_components/WinnerInfoForm/index.ts
  • apps/web/app/events/lucky-draw/result/[id]/page.tsx
  • apps/web/app/places/new/_components/Step/EventWelcome/EventWelcome.tsx
  • apps/web/middleware.ts
💤 Files with no reviewable changes (6)
  • apps/web/app/events/lucky-draw/_components/Pages/index.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Result/Result.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Result/NoResult.tsx
  • apps/web/app/events/lucky-draw/LuckyDraw.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Participation/index.tsx
  • apps/web/app/events/lucky-draw/_components/Pages/Result/index.tsx

Comment on lines +31 to +35
export const EventResultSchema = BaseEventSchema.extend({
isWinner: z.boolean(),
participantsCount: z.number(),
usedTicketsCount: z.number(),
isPhoneSubmitted: z.boolean(),
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

EventResultSchema가 결과 응답 계약을 과도하게 강제하고 있습니다.

Line 31에서 BaseEventSchema.extend(...)를 사용하면 결과 응답에 prize, totalWinnersCount, participantsCount, eventEndDate가 모두 필수가 됩니다. 현재 PR의 apps/web/app/_mocks/data/event.tseventResult 형태와도 충돌해서 타입/파싱 실패 위험이 큽니다. 결과 전용 스키마를 별도로 두는 게 안전합니다.

🔧 제안 수정안
-export const EventResultSchema = BaseEventSchema.extend({
-  isWinner: z.boolean(),
-  usedTicketsCount: z.number(),
-  isPhoneSubmitted: z.boolean(),
-})
+export const EventResultSchema = z.object({
+  eventId: z.number().transform(String),
+  isWinner: z.boolean(),
+  participantsCount: z.number(),
+  usedTicketsCount: z.number(),
+  isPhoneSubmitted: z.boolean().default(false),
+})
📝 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
export const EventResultSchema = BaseEventSchema.extend({
isWinner: z.boolean(),
participantsCount: z.number(),
usedTicketsCount: z.number(),
isPhoneSubmitted: z.boolean(),
})
export const EventResultSchema = z.object({
eventId: z.number().transform(String),
isWinner: z.boolean(),
participantsCount: z.number(),
usedTicketsCount: z.number(),
isPhoneSubmitted: z.boolean().default(false),
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_apis/schemas/event.ts` around lines 31 - 35, EventResultSchema
currently extends BaseEventSchema which forces result responses to include
BaseEventSchema fields (e.g., prize, totalWinnersCount, participantsCount,
eventEndDate) and causes parsing/type failures against the eventResult mock;
change EventResultSchema to be a standalone schema that only validates
result-specific fields (isWinner, usedTicketsCount, isPhoneSubmitted) or create
a separate Result-only schema and use that for response parsing instead of
BaseEventSchema.extend; update references to EventResultSchema where result
responses are validated so they consume the new result-only schema and ensure
compatibility with apps/web/app/_mocks/data/event.ts's eventResult shape.

Comment on lines +39 to +41
ENTRIES: '/events/entries',
RESULT: (eventId: string) => `/events/${eventId}/entries`,
APPLY: (eventId: string) => `/events/${eventId}/apply`,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n apps/web/app/_constants/path.ts

Repository: knu-matzip/Matzip-Client

Length of output: 2991


🏁 Script executed:

rg -n "API_PATH\.EVENT\.RESULT" --type ts --type tsx -A 2 -B 2

Repository: knu-matzip/Matzip-Client

Length of output: 95


🏁 Script executed:

rg -n "API_PATH\.EVENT\.RESULT[^(]" --type ts --type tsx

Repository: knu-matzip/Matzip-Client

Length of output: 95


🏁 Script executed:

rg "API_PATH\.EVENT\.RESULT" -A 2 -B 2

Repository: knu-matzip/Matzip-Client

Length of output: 450


🏁 Script executed:

rg "EVENT\.RESULT" --type ts

Repository: knu-matzip/Matzip-Client

Length of output: 179


🏁 Script executed:

fd -t ts -t tsx . | head -20

Repository: knu-matzip/Matzip-Client

Length of output: 237


🏁 Script executed:

rg "EVENT\.RESULT" -i

Repository: knu-matzip/Matzip-Client

Length of output: 179


🏁 Script executed:

cat -n apps/web/app/_apis/services/event.ts

Repository: knu-matzip/Matzip-Client

Length of output: 1842


함수에 명시적 반환 타입 추가 필요

코딩 가이드라인에서 "TypeScript 파일의 함수는 반환 타입이 필수"라고 명시되어 있으나, 현재 파일의 모든 매개변수를 받는 함수들이 명시적 반환 타입을 선언하지 않고 있습니다.

예시:

  • Line 40: RESULT: (eventId: string) => /events/${eventId}/entries`` → RESULT: (eventId: string): string => ... 필요
  • Line 41: APPLY: (eventId: string) => /events/${eventId}/apply`` → APPLY: (eventId: string): string => ... 필요
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/_constants/path.ts` around lines 39 - 41, Add explicit return
types to the arrow functions in the path constants: change RESULT and APPLY to
include a : string return type (i.e., RESULT: (eventId: string): string =>
`/events/${eventId}/entries` and APPLY: (eventId: string): string =>
`/events/${eventId}/apply`) so the TypeScript files comply with the rule that
functions must declare their return type.

@leeleeleeleejun leeleeleeleejun merged commit ecfd80b into develop Feb 27, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant