Skip to content

๐Ÿ”—API & โœจFeat : ์ด๋ฒคํŠธ ํŽ˜์ด์ง€ api ์—ฐ๊ฒฐ#123

Merged
KongMezu merged 33 commits intodevelopfrom
api/#114/event-api
Nov 15, 2025
Merged

๐Ÿ”—API & โœจFeat : ์ด๋ฒคํŠธ ํŽ˜์ด์ง€ api ์—ฐ๊ฒฐ#123
KongMezu merged 33 commits intodevelopfrom
api/#114/event-api

Conversation

@KongMezu
Copy link
Copy Markdown
Contributor

@KongMezu KongMezu commented Nov 14, 2025

๐Ÿ”ฅ ์ž‘์—… ๋‚ด์šฉ

  • ์ด๋ฒคํŠธ ํŽ˜์ด์ง€ api ์—ฐ๊ฒฐ

๐Ÿค” ์ถ”ํ›„ ์ž‘์—… ์‚ฌํ•ญ

  • ๋งˆ์ดํŽ˜์ด์ง€ - ์ €์žฅํ•œ ํ–‰์‚ฌ ๋™๊ธฐํ™”
  • ๋ถ๋งˆํฌ ui ๋™๊ธฐํ™” ํ™•์ธ
  • ๊ด€๋ จํ–‰์‚ฌ ui ํ™•์ธ..

๐Ÿ”—ย ์ด์Šˆ

PR Point (To Reviewer)

ํ–‰์‚ฌ ํŽ˜์ด์ง€ api ์—ฐ๊ฒฐ

ํ–‰์‚ฌ ํŽ˜์ด์ง€ & ์ €์žฅํ•œ ํ–‰์‚ฌ ์—ฐ๊ฒฐ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.
์ƒ์„ธ ํ–‰์‚ฌ ํŽ˜์ด์ง€์—์„œ๋Š” ๋Œ์•„๊ฐ€๋ฉด ์กฐํšŒํ•˜๋˜ ๋‚ ์งœ๋กœ ๋Œ์•„๊ฐ€๋„๋ก ํ•ด๋†จ๊ณ 
๊ทธ์™ธ ๊ด€๋ จ ๋‹ค์Œ ํ–‰์‚ฌ ๋“ฑ ๋ฐ›์•„์™”์Šต๋‹ˆ๋‹ค. ์ž‘์€ ํ–‰์‚ฌ ์นด๋“œ ๋ˆ„๋ฅด๋ฉด ๊ทธ ํ–‰์‚ฌ ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค

๋‹ค๋งŒ.. ์ง€๊ธˆ ์˜์ƒ๋ณด์‹œ๋ฉด ์••๋‹ˆ๋‹ค..
์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ๋ถ๋งˆํฌ UI๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.. (๋งˆ์ดํŽ˜์ด์ง€๋Š” ๋˜๋Š”๋ฐ)
๊ทธ๋ž˜์„œ ์ด ๋ถ€๋ถ„ ์ˆ˜์ •์ด ์ข€ ํ•„์š”ํ•ด๋ณด์ด๊ณ  ๋‚˜๋จธ์ง€๋Š”..์ •์ƒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ธย ํ”ผ๊ทธ๋งˆ ์Šคํฌ๋ฆฐ์ƒท or ๊ธฐ๋Šฅ GIF

-.Clipchamp.6.mp4

Summary by CodeRabbit

  • ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ

    • ํ–‰์‚ฌ ๋ถ๋งˆํฌ ํ† ๊ธ€(๋กœ๊ทธ์ธ ์œ ๋„ ํฌํ•จ)๊ณผ ๋ถ๋งˆํฌ ์ƒํƒœ ๋ฐ˜์˜๋œ ์ด๋ฒคํŠธ ์นด๋“œ
    • ์ด๋ฒคํŠธ ๋ชฉ๋ก/์ƒ์„ธ์šฉ ๋ฐ์ดํ„ฐ ํ›… ๋ฐ ์„œ๋ฒ„ ๊ธฐ๋ฐ˜ ์ด๋ฒคํŠธ/์ƒ์„ธ/๋ถ๋งˆํฌ API ์—ฐ๋™
    • ๊ด€๋ จ ํ–‰์‚ฌ ๋ณด์™„ ๋กœ์ง(ํ•ญ๋ชฉ ๋ถ€์กฑ ์‹œ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ์ œ๊ณต)
  • ์Šคํƒ€์ผ

    • ๋‚ ์งœ ์„ ํƒ๊ธฐ ๋ฒ„ํŠผ/์•„์ด์ฝ˜/์ƒ‰์ƒ ์กฐ์ • ๋ฐ ์ด๋ฏธ์ง€ ์ ‘๊ทผ์„ฑ ๊ฐœ์„ (alt/ํ”Œ๋ ˆ์ด์Šคํ™€๋”)
    • ํŒ์—…ยท์˜ค๋ฒ„๋ ˆ์ด z-์ธ๋ฑ์Šค ์ƒํ–ฅ
  • ๋ฆฌํŒฉํ† ๋ง

    • ์ •์  ์ƒ˜ํ”Œ ์ด๋ฒคํŠธ ์ œ๊ฑฐ ๋ฐ API ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง ์ „ํ™˜
    • ์ด๋ฒคํŠธ ์นด๋“œ ๊ณต๊ฐœ props ํ™•์žฅ(eventId, liked, imageSrc ๋“ฑ)
  • ๊ธฐํƒ€

    • ์›๊ฒฉ ์ด๋ฏธ์ง€ ๋กœ๋“œ ํ—ˆ์šฉ์„ ์œ„ํ•œ ์ด๋ฏธ์ง€ ์„ค์ • ํ™•์žฅ (์›๊ฒฉ ํ˜ธ์ŠคํŠธ ์ถ”๊ฐ€)

@KongMezu KongMezu self-assigned this Nov 14, 2025
@vercel
Copy link
Copy Markdown

vercel bot commented Nov 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
af-fe Ready Ready Preview Comment Nov 15, 2025 9:26am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 14, 2025

Walkthrough

์ด PR์€ ์ด๋ฒคํŠธ ๊ธฐ๋Šฅ์„ ์ •์  ๋ฐ์ดํ„ฐ์—์„œ API ๊ธฐ๋ฐ˜ ํ›…(์กฐํšŒยท์ƒ์„ธยท๋ถ๋งˆํฌ)๊ณผ ํƒ€์ž…ยท๋งคํผยท์œ ํ‹ธ๋กœ ์ „ํ™˜ํ•˜๊ณ , EventCard์— ๋ถ๋งˆํฌ/๋กœ๊ทธ์ธ ํ๋ฆ„์„ ํ†ตํ•ฉํ•˜๋ฉฐ Next.js ์ด๋ฏธ์ง€ remotePatterns ๋ฐ ์ผ๋ถ€ UI z-index๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

Changes

Cohort / File(s) ๋ณ€๊ฒฝ ์‚ฌํ•ญ
Next.js ์ด๋ฏธ์ง€ ์„ค์ •
next.config.ts
images.remotePatterns ๋ฐฐ์—ด ์ถ”๊ฐ€(3๊ฐœ ํ˜ธ์ŠคํŠธ), images.domains์— www.bucheon.go.kr, www.bcf.or.kr ์ถ”๊ฐ€ ๋ฐ ํฌ๋งท ์กฐ์ •
API ๋ ˆ์ด์–ด (events)
src/shared/api/events/event.ts
์ด๋ฒคํŠธ ๋ชฉ๋ก/์ƒ์„ธ/๋ถ๋งˆํฌ API ํ—ฌํผ ์ถ”๊ฐ€: fetchEvents, fetchEventDetail, postBookmark, deleteBookmark, fetchBookmarkStatus (per-item ์ƒ์„ธ ๋ณด๊ฐ• ํฌํ•จ)
React Query ํ›…
src/shared/hooks/events/useEvents.ts, src/shared/hooks/events/useEventDetail.ts, src/shared/hooks/events/useBookmark.ts
์ด๋ฒคํŠธ ๋ชฉ๋ก/์ƒ์„ธ ์กฐํšŒ ๋ฐ ๋ถ๋งˆํฌ ๊ด€๋ฆฌ ํ›… ์ถ”๊ฐ€(์ฟผ๋ฆฌํ‚ค, ์ธ์ฆ ๋ถ„๊ธฐ, ์บ์‹œ ๋ฌดํšจํ™”/์žฌ์š”์ฒญ ํฌํ•จ)
ํƒ€์ž…ยท๋งคํผยท์œ ํ‹ธ
src/shared/types/eventtypes.ts, src/shared/hooks/events/EventMapper.ts, src/shared/utils/buildNextEventList.ts
EventResponse/EventData/EventDetail ๋“ฑ ํƒ€์ž… ์ถ”๊ฐ€, ์‘๋‹ตโ†’๋„๋ฉ”์ธ ๋งคํ•‘ mapEvent, ๊ด€๋ จ ์ด๋ฒคํŠธ ๋ฆฌ์ŠคํŠธ ๊ธธ์ด ๋ณด์ • ์œ ํ‹ธ buildNextEventList ์ถ”๊ฐ€
์ปดํฌ๋„ŒํŠธ: EventCard
src/shared/components/container/EventCard.tsx
props์— eventId:number, liked?:boolean, onClick?:() ์ถ”๊ฐ€. useBookmark ํ†ตํ•ฉ์œผ๋กœ ๋ถ๋งˆํฌ ํ† ๊ธ€ยท๋กœ๊ทธ์ธ ํ๋ฆ„๊ณผ ์นด๋“œ ๋ ˆ์ด์•„์›ƒยทํ•˜ํŠธ ํ† ๊ธ€ ๋ Œ๋”๋ง ๋ณ€๊ฒฝ
ํŽ˜์ด์ง€ ๋ฆฌํŒฉํ† ๋ง: ์ด๋ฒคํŠธ ๋ชฉ๋ก/์ƒ์„ธ
src/pages/events/index.tsx, src/pages/events/[id].tsx
์ •์  ๋ฐ์ดํ„ฐ ์ œ๊ฑฐ, useEvents/useEventDetail ํ›…์œผ๋กœ ๋ฐ์ดํ„ฐ ์ฃผ์ž…ยท๋กœ๋”ฉยท์—๋Ÿฌยท๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋กœ์ง ๋„์ž…, EventCard์— eventId/liked/imageSrc ์ „๋‹ฌ ๋ฐ ๋‚ ์งœ ์ฟผ๋ฆฌ ๋ณด์กด
ํŽ˜์ด์ง€ ๋ฆฌํŒฉํ† ๋ง: ๋งˆ์ดํŽ˜์ด์ง€
src/pages/mypage/index.tsx, src/pages/mypage/events/[id].tsx
๋ถ๋งˆํฌ ์ด๋ฒคํŠธ ๋ Œ๋”๋ง๊ณผ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ useEventDetail ๊ธฐ๋ฐ˜์œผ๋กœ ์ „ํ™˜, EventCard prop ๋งคํ•‘ ์—…๋ฐ์ดํŠธ(imageSrc, liked ํฌํ•จ) ๋ฐ ๋ผ์šฐํŒ… ๋™์ž‘ ์ถ”๊ฐ€
์ •์  ๋ฐ์ดํ„ฐ ์ œ๊ฑฐ
src/shared/constants/events/eventsData.ts
๊ธฐ์กด ์ƒ˜ํ”Œ EventData ์ธํ„ฐํŽ˜์ด์Šค์™€ eventData ์ƒ์ˆ˜ ์‚ญ์ œ
DatePicker / UI ์กฐ์ •
src/shared/components/datePicker/DatePicker.tsx
๋ฒ„ํŠผ ๋„ˆ๋น„/์ƒ‰/์•„์ด์ฝ˜ ๋ณ€๊ฒฝ, ์บ˜๋ฆฐ๋” ๋‚ ์งœ ๋น„ํ™œ์„ฑํ™” ์ œ์•ฝ ์ฃผ์„ ์ฒ˜๋ฆฌ(์ œํ•œ ์™„ํ™”)
z-index ์กฐ์ •
src/shared/components/set/PopupSet.tsx, src/shared/overlay/Overlay.tsx
PopupSet z-index 50โ†’100, Overlay z-index 40โ†’99 ๋ณ€๊ฒฝ
๋ฉค๋ฒ„ ํƒ€์ž…/์ฟผ๋ฆฌ ์˜ต์…˜ ๋ณ€๊ฒฝ
src/shared/types/membertypes.ts, src/shared/api/member.ts
MyPage ์‘๋‹ต ๋‚ด bookmarkedEvents ํƒ€์ž…์„ ๋” ๊ด€๋Œ€ํ•˜๊ฒŒ ๋ณ€๊ฒฝ, ์ผ๋ถ€ React Query staleTime ์˜ต์…˜ ์ œ๊ฑฐ

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant Page as "ํŽ˜์ด์ง€ (๋ชฉ๋ก/์ƒ์„ธ)"
    participant Hook as "useEvents / useEventDetail"
    participant RQ as "React Query"
    participant API as "Events API"
    participant BK as "useBookmark"

    User->>Page: ์ ‘๊ทผ (date ๋˜๋Š” eventId ํฌํ•จ)
    Page->>Hook: ์š”์ฒญ (date ๋˜๋Š” eventId)
    Hook->>RQ: useQuery ์‹คํ–‰
    RQ->>API: fetchEvents / fetchEventDetail ํ˜ธ์ถœ
    API-->>RQ: ์‘๋‹ต ๋ฐ˜ํ™˜
    RQ-->>Hook: ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
    Hook-->>Page: ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ ์ œ๊ณต
    Page->>Page: ๋ Œ๋” (Image, EventCard, ๊ด€๋ จ ์ด๋ฒคํŠธ)

    alt ๋ถ๋งˆํฌ ํ† ๊ธ€
        User->>Page: ๋ถ๋งˆํฌ ํด๋ฆญ
        Page->>BK: toggleBookmark()
        BK->>API: postBookmark / deleteBookmark
        API-->>BK: ์‘๋‹ต
        BK->>RQ: ์บ์‹œ ๋ฌดํšจํ™” ['events'], ['eventDetail', id], ['myPage']
        RQ->>API: ์žฌ์š”์ฒญ
        API-->>RQ: ์ตœ์‹  ์‘๋‹ต
        RQ-->>Page: UI ์—…๋ฐ์ดํŠธ
    end
Loading

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~45๋ถ„

์ฃผ์˜๊ฐ€ ํ•„์š”ํ•œ ํŒŒ์ผ/์˜์—ญ:

  • src/shared/hooks/events/useBookmark.ts: ์ธ์ฆ(ํ† ํฐ) ๋ถ„๊ธฐ, ์—๋Ÿฌ ์ฒ˜๋ฆฌ, React Query ์บ์‹œ ๋ฌดํšจํ™”/์žฌ์š”์ฒญ ๊ฒ€์ฆ
  • src/shared/api/events/event.ts: fetchEvents ๋‚ด per-item enrichment ๋™์‹œ์„ฑยท์„ฑ๋Šฅยท์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
  • src/shared/components/container/EventCard.tsx: props ๋ณ€๊ฒฝ๊ณผ ๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ ์—ฐ๋™ ์˜ํ–ฅ ์ ๊ฒ€
  • src/pages/events/*, src/pages/mypage/*: ๋ผ์šฐํ„ฐ ์ฟผ๋ฆฌ(date) ๋™๊ธฐํ™”ยท๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์กฐ๊ฑดยท์ฟผ๋ฆฌ ํ™œ์„ฑํ™” ํƒ€์ด๋ฐ

Possibly related issues

Possibly related PRs

Suggested labels

feat

Suggested reviewers

  • skyblue1232
  • jjangminii

Poem

๐Ÿ‡
๊นก์ถฉ๊นก์ถฉ API ๊ธธ์„ ๊ฑด๋„ˆ์„œ,
ํ•˜ํŠธ๋Š” ๋ถ๋งˆํฌ๋กœ ๋ฐ˜์ง์ด๊ณ ,
ํ›…์ด ์†์žก์•„ ๋ฐ์ดํ„ฐ๋ฅผ ์ „ํ•˜๋‹ˆ,
์นด๋“œ๋“ค์ด ์ƒˆ ์˜ท์„ ์ž…์—ˆ๋„ค์š”,
๊นก์ถฉ โ€” ๋ฐฐํฌ๋ฅผ ์ถ•ํ•˜ํ•ด์š”!

Pre-merge checks and finishing touches

โŒ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage โš ๏ธ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check โ“ Inconclusive PR ์„ค๋ช…์ด ํ•„์ˆ˜ ์„น์…˜ ๋Œ€๋ถ€๋ถ„์„ ํฌํ•จํ•˜๊ณ  ์žˆ์œผ๋‚˜, ๐Ÿ”— ์ด์Šˆ ์„น์…˜์˜ ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š๊ณ  ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท ์„น์…˜์ด ๋ถˆ์™„์ „ํ•ฉ๋‹ˆ๋‹ค. ์ด์Šˆ ๋ฒˆํ˜ธ ํ˜•์‹์„ ์ˆ˜์ •ํ•˜๊ณ (ํ˜„์žฌ: #https://... โ†’ ์ˆ˜์ •: #114), ๐Ÿ“ธ ์„น์…˜์— ์‹ค์ œ ์Šคํฌ๋ฆฐ์ƒท์ด๋‚˜ GIF๋ฅผ ์ฒจ๋ถ€ํ•˜๊ฑฐ๋‚˜ ๋งํฌ๋ฅผ ๋ช…ํ™•ํžˆ ํ‘œ๊ธฐํ•ด์ฃผ์„ธ์š”. ๋˜ํ•œ ๋ฏธ์™„๋ฃŒ๋œ ์ถ”ํ›„ ์ž‘์—… ์‚ฌํ•ญ๋“ค์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์„ค๋ช…์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.
โœ… Passed checks (1 passed)
Check name Status Explanation
Title check โœ… Passed PR ์ œ๋ชฉ์ด '์ด๋ฒคํŠธ ํŽ˜์ด์ง€ api ์—ฐ๊ฒฐ'์ด๋ผ๋Š” ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฐ˜์˜ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์‹ค์ œ ๋ณ€๊ฒฝ ๋‚ด์šฉ(API ์—ฐ๊ฒฐ, ํ›… ์ถ”๊ฐ€, ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋“ฑ)๊ณผ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
โœจ Finishing touches
  • ๐Ÿ“ Generate docstrings
๐Ÿงช Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch api/#114/event-api

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.

@github-actions
Copy link
Copy Markdown

๐Ÿท๏ธ Labeler has automatically applied labels based on your PR title, branch name, or commit message.
Please verify that they are correct before merging.

@github-actions github-actions bot added api api ์—ฐ๊ฒฐ comment ํ•„์š”ํ•œ ์ฃผ์„ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ labels Nov 14, 2025
@github-actions
Copy link
Copy Markdown

๐Ÿท๏ธ Labeler has automatically applied labels based on your PR title, branch name, or commit message.
Please verify that they are correct before merging.

Copy link
Copy Markdown
Contributor

@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: 5

Caution

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

โš ๏ธ Outside diff range comments (1)
src/shared/components/container/LocationCard.tsx (1)

24-28: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์ƒํƒœ ์ œ๊ฑฐ ํ•„์š”

liked ์ƒํƒœ์™€ handleLikeClick ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์ง€๋งŒ ๋” ์ด์ƒ ๋ Œ๋”๋ง๋œ ์š”์†Œ์™€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค(Lines 90-103์˜ ์•„์ด์ฝ˜์ด ์ฃผ์„ ์ฒ˜๋ฆฌ๋จ). ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋Š” ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

-  const [liked, setLiked] = useState(false);
-
-  const handleLikeClick = () => {
-    setLiked((prev) => !prev);
-  };
๐Ÿงน Nitpick comments (11)
src/shared/components/set/PopupSet.tsx (1)

27-27: LGTM! Overlay์™€์˜ z-index ๊ณ„์ธต ๊ตฌ์กฐ๊ฐ€ ์˜ฌ๋ฐ”๋ฆ…๋‹ˆ๋‹ค

PopupSet(z-100)์ด Overlay(z-99) ์œ„์— ๋ Œ๋”๋ง๋˜๋„๋ก z-index๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์กฐ์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ์œ„ํ•ด Overlay.tsx์™€ ๋™์ผํ•œ z-index ์ƒ์ˆ˜๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

// ๊ณต์œ  ์ƒ์ˆ˜ ํŒŒ์ผ์—์„œ
import { Z_INDEX } from '@/shared/constants/z-index';

// PopupSet์—์„œ ์‚ฌ์šฉ
className={cn(
  `fixed inset-0 z-[${Z_INDEX.POPUP}]`,
  'flex items-center justify-center',
)}
src/shared/overlay/Overlay.tsx (1)

12-12: z-index ๊ตฌ์กฐ ๊ฐœ์„  ๋ฐ ์ƒ์ˆ˜ํ™” ๊ถŒ์žฅ

z-index๊ฐ€ 99๋กœ ๋ณ€๊ฒฝ๋˜์–ด PopupSet(z-100)๋ณด๋‹ค ์•„๋ž˜์— ์œ„์น˜ํ•˜๋Š” ๊ฒƒ์ด ํ™•์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋‹ค์Œ ์‚ฌํ•ญ์„ ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค:

  • Header์™€ PopupSet ์ถฉ๋Œ: ๋‘ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋‘ z-[100]์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด, ๊ฐ™์€ ์Šคํƒ ์ปจํ…์ŠคํŠธ์—์„œ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ ˆ์ด์–ด๋ง ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒ์ˆ˜ํ™” ๋ถ€์žฌ: ํ˜„์žฌ z-index ๊ฐ’๋“ค์ด ์—ฌ๋Ÿฌ ํŒŒ์ผ์— ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค(src/shared/components/header/Header.tsx, src/shared/components/set/PopupSet.tsx, src/shared/components/set/LocationBubbleSet.tsx ๋“ฑ). ์ผ๊ด€์„ฑ ์žˆ๋Š” ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์ˆ˜ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ธฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค:
// src/shared/constants/zIndex.ts
export const Z_INDEX = {
  OVERLAY: 99,
  POPUP: 100,
  HEADER: 101,
  CONTROL_BAR: 50,
  LOCATION_BUBBLE: 50,
} as const;

์ดํ›„ ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๊ฐ’ ๋Œ€์‹  ์ƒ์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜๋„๋ก ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.

src/shared/components/container/LocationCard.tsx (1)

90-103: ๋ถ๋งˆํฌ ๊ธฐ๋Šฅ ํ†ตํ•ฉ ๊ณ ๋ ค ํ•„์š”

ํ•˜ํŠธ ์•„์ด์ฝ˜์ด ์ฃผ์„ ์ฒ˜๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. PR์˜ ๋‹ค๋ฅธ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ณด๋ฉด EventCard์—์„œ useBookmark ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ๋งˆํฌ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, LocationCard์—๋„ ๋™์ผํ•œ ํŒจํ„ด์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”. PR ์„ค๋ช…์˜ "๋ถ๋งˆํฌ UI ๋™๊ธฐํ™” ํ™•์ธ (todo)"์™€ ๊ด€๋ จ์ด ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

EventCard์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ถ๋งˆํฌ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•ด๋“œ๋ฆด๊นŒ์š”?

src/shared/hooks/events/useEventDetail.ts (1)

10-13: ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€ ๊ถŒ์žฅ

API ํ˜ธ์ถœ ์‹คํŒจ ์‹œ ๋” ๋‚˜์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

const fetchEventDetail = async (eventId: number): Promise<EventDetail> => {
  try {
    const res = await apiWithToken.get<EventDetailResponse>(`/api/events/${eventId}`);
    return res.data.data;
  } catch (error) {
    console.error('Failed to fetch event detail:', error);
    throw error;
  }
};
src/shared/hooks/events/useEvents.ts (1)

12-12: Non-null assertion ์‚ฌ์šฉ

Line 12์—์„œ formattedDate!์— non-null assertion์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. enabled: !!formattedDate ๊ฐ€๋“œ ๋•๋ถ„์— ์•ˆ์ „ํ•˜์ง€๋งŒ, ๋” ๋ช…์‹œ์ ์ธ ํƒ€์ž… ์ฒดํฌ๋ฅผ ์„ ํ˜ธํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  const { data, isLoading, isError } = useQuery({
    queryKey: ['events', formattedDate],
-   queryFn: () => fetchEvents(formattedDate!),
+   queryFn: () => formattedDate ? fetchEvents(formattedDate) : Promise.resolve({ data: [] }),
    enabled: !!formattedDate,
  });
next.config.ts (1)

19-26: domains ์„ค์ •์„ remotePatterns๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ถŒ์žฅ

Next.js 14๋ถ€ํ„ฐ domains ์˜ต์…˜์ด deprecated๋˜์—ˆ์œผ๋ฉฐ, remotePatterns๋Š” protocol, hostname, port, path๋ฅผ ๋ชจ๋‘ ์ œํ•œํ•  ์ˆ˜ ์žˆ์–ด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ์ผ๊ด€์„ฑ๊ณผ ๋ณด์•ˆ์„ ์œ„ํ•ด ๋ชจ๋“  ์ด๋ฏธ์ง€ ๋„๋ฉ”์ธ์„ remotePatterns๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜์„ธ์š”.

  images: {
-  domains: ['maps.googleapis.com'],
   remotePatterns: [
+    { protocol: 'https', hostname: 'maps.googleapis.com' },
     { protocol: 'https', hostname: 'mblogthumb-phinf.pstatic.net' },
     { protocol: 'https', hostname: 'blogfiles.pstatic.net' },
     { protocol: 'https', hostname: 'postfiles.pstatic.net' },
   ],
 },
src/shared/types/eventtypes.ts (1)

1-40: EventResponse โ†” EventData ํ•„๋“œ ๋„ค์ด๋ฐ/ํ•„์ˆ˜ ์—ฌ๋ถ€ ์ •ํ•ฉ์„ฑ ํ™•์ธ ์ œ์•ˆ

EventResponse์™€ EventData ์‚ฌ์ด์— ์•„๋ž˜์™€ ๊ฐ™์ด ์˜๋ฏธ๊ฐ€ ๊ฐ™์€๋ฐ ์ด๋ฆ„/ํ•„์ˆ˜ ์—ฌ๋ถ€๊ฐ€ ๋‹ค๋ฅธ ํ•„๋“œ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค (Line 1โ€“21):

  • title โ†” name
  • body โ†” description
  • isBookmarked? โ†” liked (ํ•„์ˆ˜)
  • address?, startDate?, endDate? โ†” address, startDate, endDate (ํ•„์ˆ˜)

๋งคํ•‘ ์œ ํ‹ธ(์˜ˆ: mapEvent)์—์„œ ์ด ์ฐจ์ด๋ฅผ ๋ชจ๋‘ ์˜๋„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ•œ ๋ฒˆ๋งŒ ๋” ํ™•์ธํ•ด ์ฃผ์„ธ์š”. ํŠนํžˆ API์—์„œ ํ•„๋“œ๊ฐ€ ๋ˆ„๋ฝ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ(์ฃผ์†Œ/๋‚ ์งœ/๋ถ๋งˆํฌ ์—ฌ๋ถ€ ๋“ฑ)์— ํ”„๋ก ํŠธ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ๊ธฐ๋ณธ๊ฐ’์„ ๋„ฃ๊ณ  ์žˆ๋Š”์ง€ ์ฒดํฌํ•ด ๋‘๋ฉด ์ถ”ํ›„ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

src/pages/events/index.tsx (1)

38-48: DatePicker ์ ‘๊ทผ์„ฑ: label ์—ฐ๋™ ์—ฌ๋ถ€ ํ™•์ธ ํ•„์š”

Line 40โ€“47์—์„œ SR-only <label htmlFor='event-date'>๋ฅผ ๋‘๊ณ  ์žˆ๋Š”๋ฐ, DatePicker์—๋Š” id๋‚˜ inputId ๊ฐ™์€ prop์ด ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. DatePicker ๋‚ด๋ถ€์—์„œ ์‹ค์ œ <input>์˜ id๊ฐ€ 'event-date'๋กœ ์„ค์ •๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด ๋ผ๋ฒจ์€ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์™€ ์—ฐ๋™๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • DatePicker๊ฐ€ id๋ฅผ ๋ฐ›๋„๋ก ๋˜์–ด ์žˆ๋‹ค๋ฉด ์ด ํŽ˜์ด์ง€์—์„œ id='event-date'๋ฅผ ๋„˜๊ฒจ ์ฃผ์„ธ์š”.
  • ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด aria-labelledby ๋˜๋Š” aria-label๋งŒ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ชฝ์œผ๋กœ ๊ตฌ์กฐ๋ฅผ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ ‘๊ทผ์„ฑ ๋ณด์žฅ์„ ์œ„ํ•ด ํ•œ ๋ฒˆ ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

src/shared/hooks/events/useBookmark.ts (1)

1-36: ๋ถ๋งˆํฌ ํ›… ์ƒํƒœ ๋™๊ธฐํ™” ๋ฐ ํด๋ผ์ด์–ธํŠธ ์ง€์‹œ์–ด ๊ด€๋ จ ์ œ์•ˆ

  • Line 1์˜ 'use client'๋Š” ์ด ํ›…์ด App Router ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ง์ ‘ import๋˜๋Š” ์ƒํ™ฉ์ด ์•„๋‹ˆ๋ผ๋ฉด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๊ฐ€ Pages Router ๊ธฐ๋ฐ˜์ด๋ผ๋ฉด ์ œ๊ฑฐํ•ด๋„ ๋ฌด๋ฐฉํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.
  • Line 8โ€“10์—์„œ initialState๋กœ isBookmarked๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์ง€๋งŒ, ๋ถ€๋ชจ์—์„œ ๋‚ด๋ ค์ฃผ๋Š” liked ๊ฐ’์ด ๋‚˜์ค‘์— ๋ฐ”๋€Œ์–ด๋„ ๋‚ด๋ถ€ ์ƒํƒœ๋Š” ํ•œ ๋ฒˆ ์ •ํ•ด์ง€๋ฉด ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๋™์ผ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ํ† ๊ธ€ํ–ˆ์„ ๋•Œ, ์ƒˆ๋กœ๊ณ ์นจ ์—†์ด ์ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๋งˆ์šดํŠธ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์„œ๋ฒ„ ์ƒํƒœ์™€ UI ์‚ฌ์ด์— ๋ถˆ์ผ์น˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜์ฒ˜๋Ÿผ initialState ๋ณ€๊ฒฝ ์‹œ ๋™๊ธฐํ™”ํ•˜๋Š” useEffect๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useEffect(() => {
  setIsBookmarked(initialState);
}, [initialState]);
  • Line 28โ€“29์—์„œ React Query์˜ invalidateQueries๋กœ ['events'], ['eventDetail', eventId]๋ฅผ ๋ฌดํšจํ™”ํ•˜๋Š” ์ ‘๊ทผ์€ useEvents/useEventDetail ์ฟผ๋ฆฌ ํ‚ค ๊ตฌ์„ฑ๊ณผ ์ž˜ ๋งž์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ํ† ๊ธ€ ์‹คํŒจ ์‹œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ”ผ๋“œ๋ฐฑ์ด ์—†๋Š” ์ƒํƒœ๋ผ ํ•„์š”ํ•˜๋‹ค๋ฉด ํ›„์† ์ž‘์—…(TODO) ์ •๋„๋Š” ๋‚จ๊ฒจ ๋‘์–ด๋„ ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.
src/pages/events/[id].tsx (1)

31-39: ํ—ค๋” ํƒ€์ดํ‹€ ๋ฐ ๋กœ๋”ฉ/์—๋Ÿฌ ์ƒํƒœ UX ๊ฐœ์„  ์ œ์•ˆ

  • Line 35โ€“37์—์„œ Header์˜ title์„ ํ•ญ์ƒ 'ํ–‰์‚ฌ๋ช…'์œผ๋กœ ๋„˜๊ธฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ Line 28โ€“29์—์„œ title์„ ๊ตฌ์กฐ ๋ถ„ํ•ดํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ์‹ค์ œ ํ–‰์‚ฌ๋ช…์„ ๋ณด์—ฌ์ฃผ๋ ค๋ฉด title={title}๋กœ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด ์ž์—ฐ์Šค๋Ÿฌ์šธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ํ˜„์žฌ๋Š” if (!eventId) return null; ๋ฐ if (isError || !eventDetail) return null;๋กœ ์ธํ•ด ๋กœ๋”ฉ/์—๋Ÿฌ ์ƒํ™ฉ์—์„œ ํ™”๋ฉด์— ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”๋ง๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ useEffect๋กœ /events๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋Š” ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๋„คํŠธ์›Œํฌ๊ฐ€ ๋А๋ฆฐ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๋Š” ๋นˆ ํ™”๋ฉด๋งŒ ์ž ๊น ๋ณด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
    ๊ฐ„๋‹จํ•œ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋‚˜ โ€œํ–‰์‚ฌ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹คโ€ ์ •๋„์˜ ํ…์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด UX๊ฐ€ ์กฐ๊ธˆ ๋” ์ข‹์•„์งˆ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Also applies to: 79-97

src/shared/api/events/event.ts (1)

14-25: ๋ชฉ๋ก ์กฐํšŒ ์‹œ ์ด๋ฒคํŠธ๋ณ„ ์ƒ์„ธ API N+1 ํ˜ธ์ถœ๋กœ ์ธํ•œ ์„ฑ๋Šฅ ์ด์Šˆ ๊ฐ€๋Šฅ์„ฑ

ํ˜„์žฌ fetchEvents์—์„œ res.data.data์˜ ๊ฐ ์•„์ดํ…œ๋งˆ๋‹ค /api/events/{eventId}๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์–ด, ์ด๋ฒคํŠธ ์ˆ˜๊ฐ€ ๋งŽ์„ ๋•Œ N+1 ํŒจํ„ด์œผ๋กœ ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜๊ฐ€ ์ปค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ imageUrl, isBookmarked๋ฅผ ๋ชฉ๋ก API์—์„œ ํ•จ๊ป˜ ๋‚ด๋ ค๋ฐ›๊ฑฐ๋‚˜, ์„œ๋ฒ„์— ๋ฐฐ์น˜/์š”์•ฝ์šฉ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉํ–ฅ๋„ ํ•œ ๋ฒˆ ๊ณ ๋ คํ•ด ๋ณด์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 81659e0 and 389e160.

๐Ÿ“’ Files selected for processing (17)
  • next.config.ts (1 hunks)
  • src/pages/events/[id].tsx (2 hunks)
  • src/pages/events/index.tsx (2 hunks)
  • src/shared/api/events/event.ts (1 hunks)
  • src/shared/api/instance.ts (1 hunks)
  • src/shared/components/container/EventCard.tsx (2 hunks)
  • src/shared/components/container/LocationCard.tsx (2 hunks)
  • src/shared/components/datePicker/DatePicker.tsx (3 hunks)
  • src/shared/components/set/PopupSet.tsx (1 hunks)
  • src/shared/constants/events/eventsData.ts (0 hunks)
  • src/shared/hooks/events/EventMapper.ts (1 hunks)
  • src/shared/hooks/events/useBookmark.ts (1 hunks)
  • src/shared/hooks/events/useEventDetail.ts (1 hunks)
  • src/shared/hooks/events/useEvents.ts (1 hunks)
  • src/shared/overlay/Overlay.tsx (1 hunks)
  • src/shared/types/eventtypes.ts (1 hunks)
  • src/shared/utils/buildNextEventList.ts (1 hunks)
๐Ÿ’ค Files with no reviewable changes (1)
  • src/shared/constants/events/eventsData.ts
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.

Applied to files:

  • src/pages/events/index.tsx
  • src/pages/events/[id].tsx
๐Ÿงฌ Code graph analysis (11)
src/shared/utils/buildNextEventList.ts (1)
src/shared/types/eventtypes.ts (1)
  • RelatedEvent (23-27)
src/shared/components/container/EventCard.tsx (5)
src/shared/hooks/events/useBookmark.ts (1)
  • useBookmark (8-36)
src/pages/mypage/components/LoginRequired.tsx (1)
  • LoginRequired (10-21)
src/shared/components/container/Card.tsx (1)
  • Card (32-48)
src/shared/lib/utils.ts (1)
  • cn (71-73)
src/shared/icons/components/icon.tsx (1)
  • Icon (58-130)
src/shared/api/instance.ts (1)
src/pages/auth/callback.tsx (2)
  • res (19-35)
  • AuthCallbackPage (7-41)
src/shared/hooks/events/useBookmark.ts (2)
src/shared/utils/token.ts (1)
  • getAccessToken (8-9)
src/shared/api/events/event.ts (2)
  • deleteBookmark (54-59)
  • postBookmark (46-51)
src/shared/components/datePicker/DatePicker.tsx (1)
src/shared/icons/components/icon.tsx (1)
  • Icon (58-130)
src/shared/hooks/events/useEventDetail.ts (3)
src/shared/api/events/event.ts (1)
  • fetchEventDetail (34-43)
src/shared/types/eventtypes.ts (2)
  • EventDetail (37-40)
  • EventDetailResponse (29-35)
src/shared/api/instance.ts (1)
  • apiAuth (20-23)
src/shared/api/events/event.ts (4)
src/shared/types/authtypes.ts (1)
  • ApiResponse (1-6)
src/shared/types/eventtypes.ts (2)
  • EventData (12-21)
  • EventResponse (1-11)
src/shared/api/instance.ts (1)
  • apiWithToken (14-17)
src/shared/hooks/events/EventMapper.ts (1)
  • mapEvent (3-12)
src/pages/events/index.tsx (1)
src/shared/hooks/events/useEvents.ts (1)
  • useEvents (7-21)
src/shared/hooks/events/useEvents.ts (2)
src/shared/utils/date.ts (1)
  • formatDateToISO (1-6)
src/shared/api/events/event.ts (1)
  • fetchEvents (7-31)
src/shared/hooks/events/EventMapper.ts (1)
src/shared/types/eventtypes.ts (2)
  • EventResponse (1-11)
  • EventData (12-21)
src/pages/events/[id].tsx (4)
src/shared/hooks/events/useEventDetail.ts (1)
  • useEventDetail (15-21)
src/shared/utils/buildNextEventList.ts (1)
  • buildNextEventList (3-11)
src/shared/lib/utils.ts (1)
  • cn (71-73)
src/pages/events/components/DateTag.tsx (1)
  • DateTag (24-44)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
๐Ÿ”‡ Additional comments (8)
src/shared/api/instance.ts (1)

95-95: ์ธ์ฆ ์‹คํŒจ ์‹œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๊ฒฝ๋กœ ๋ณ€๊ฒฝ ํ™•์ธ๋จ

ํ† ํฐ ๊ฐฑ์‹  ์‹คํŒจ ์‹œ /auth๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•˜๋„๋ก ๋ณ€๊ฒฝํ•œ ๊ฒƒ์ด auth callback ํŽ˜์ด์ง€์˜ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

src/shared/components/datePicker/DatePicker.tsx (2)

14-14: ์ ‘๊ทผ์„ฑ ๊ฐœ์„ 

ariaLabel prop ์ถ”๊ฐ€๋กœ ์ ‘๊ทผ์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


88-89: ๊ณผ๊ฑฐ ๋‚ ์งœ ์„ ํƒ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํ™•์ธ ํ•„์š”

๋‚ ์งœ ์ œํ•œ(disabled, fromDate)์ด ์ฃผ์„ ์ฒ˜๋ฆฌ๋˜์–ด ์ด์ œ ๊ณผ๊ฑฐ ๋‚ ์งœ ์„ ํƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ๊ฒ€์ƒ‰ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ๊ณผ๊ฑฐ ์ด๋ฒคํŠธ ์กฐํšŒ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋ผ๋ฉด ์˜๋„๋œ ๋ณ€๊ฒฝ์ด์ง€๋งŒ, ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ์ œํ•œ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

src/shared/hooks/events/EventMapper.ts (1)

3-12: LGTM!

EventResponse๋ฅผ EventData๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊น”๋”ํ•œ ๋งคํ•‘ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. nullish coalescing์„ ์‚ฌ์šฉํ•œ ๊ธฐ๋ณธ๊ฐ’ ์ฒ˜๋ฆฌ๋„ ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

src/pages/events/index.tsx (1)

1-4: Pages Router ํ™˜๊ฒฝ์—์„œ 'use client' ๋ฐ ์ดˆ๊ธฐ ์ƒํƒœ UX ์ ๊ฒ€ ์ œ์•ˆ

  • Line 1์˜ 'use client'๋Š” Next.js Pages Router ํŽ˜์ด์ง€์—์„œ๋Š” ์˜๋ฏธ๊ฐ€ ์—†๊ณ , ๊ธฐ์กด ํ•™์Šต ์ปจํ…์ŠคํŠธ(์ด ํ”„๋กœ์ ํŠธ๋Š” Pages Router์—์„œ use client๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)์™€๋„ ๋งž์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ œ๊ฑฐํ•ด๋„ ๋™์ž‘์—๋Š” ์˜ํ–ฅ์ด ์—†์œผ๋‹ˆ, ์ปจ๋ฒค์…˜์— ๋งž๊ฒŒ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
  • Line 18โ€“20์—์„œ const filteredEvents = events;๋กœ ๋ณ„๋„ ๋ณ€์ˆ˜๋งŒ ๋‘๊ณ  ์žˆ์–ด ์ถ”๊ฐ€ ํ•„ํ„ฐ๋ง์ด ์—†๋‹ค๋ฉด ๋ฐ”๋กœ events๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
  • ํ˜„์žฌ ๋กœ์ง์—์„œ๋Š” ๋‚ ์งœ๋ฅผ ์•„์ง ์„ ํƒํ•˜์ง€ ์•Š์€ ์ฒซ ์ง„์ž… ์‹œ์—๋„ events๊ฐ€ ๋นˆ ๋ฐฐ์—ด์ด๋ผ ๊ณง๋ฐ”๋กœ โ€œํ–‰์‚ฌ๊ฐ€ ์—†์–ด์š”!โ€ ๋นˆ ์ƒํƒœ UI(Line 81โ€“95)๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. UX ์ƒ โ€œ๋‚ ์งœ๋ฅผ ๋จผ์ € ์„ ํƒํ•ด ์ฃผ์„ธ์š”โ€์™€ ๊ฐ™์€ ์•ˆ๋‚ด๋ฅผ ๋ณด์—ฌ์ฃผ๊ฑฐ๋‚˜, ๋‚ ์งœ๊ฐ€ ์„ ํƒ๋˜์—ˆ์„ ๋•Œ๋งŒ ๋นˆ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์กฐ๊ฑด(date ์กด์žฌ ์—ฌ๋ถ€)์„ ์ถ”๊ฐ€ํ• ์ง€ ํ•œ ๋ฒˆ ๊ณ ๋ คํ•ด ๋ณด์…”๋„ ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

Also applies to: 13-21

src/pages/events/[id].tsx (2)

55-77: ์ด๋ฏธ์ง€ fallback ๋ฒ„๊ทธ ๋ฐ ๊ด€๋ จ ํ–‰์‚ฌ ์•„์ดํ…œ ํƒ€์ž… ์•ˆ์ „์„ฑ

  1. ์ด๋ฏธ์ง€ fallback์—์„œ ์ •์˜๋˜์ง€ ์•Š์€ ๋ณ€์ˆ˜ ์‚ฌ์šฉ

    • Line 71โ€“75์—์„œ:

      aria-label={`${name} ์ด๋ฏธ์ง€๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.`}

      ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด ์Šค์ฝ”ํ”„์—๋Š” name ๋ณ€์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. TypeScript ๊ธฐ์ค€์œผ๋กœ๋Š” ์ปดํŒŒ์ผ ์—๋Ÿฌ, JS ๊ธฐ์ค€์œผ๋กœ๋Š” ReferenceError๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. title์„ ์‚ฌ์šฉํ•˜๋ ค๋˜ ์˜๋„๋กœ ๋ณด์ด๋ฏ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • aria-label={${name} ์ด๋ฏธ์ง€๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.}
  • aria-label={${title} ์ด๋ฏธ์ง€๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.}
    
    
  1. nextList ์•„์ดํ…œ์˜ isEmpty ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ

    • Line 104โ€“121์—์„œ nextList.map ๋‚ด๋ถ€์—์„œ item.isEmpty๋กœ ๋ถ„๊ธฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. buildNextEventList ๊ตฌํ˜„์„ ๋ณด๋ฉด ์‹ค์ œ ์ด๋ฒคํŠธ(RelatedEvent)์™€ { isEmpty: true }๋ฅผ ์„ž์–ด์„œ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ํƒ€์ž… ์ƒ์œผ๋กœ๋Š” RelatedEvent | { isEmpty: boolean } ์œ ๋‹ˆ์˜จ์ด ๋ฉ๋‹ˆ๋‹ค.

    • ํ˜„์žฌ์ฒ˜๋Ÿผ ๋‹จ์ˆœํžˆ item.isEmpty์— ์ ‘๊ทผํ•˜๋ฉด TypeScript์—์„œ๋Š” "Property 'isEmpty' does not exist on type 'RelatedEvent'" ์—๋Ÿฌ๊ฐ€ ๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      ํƒ€์ž…์„ ์•„๋ž˜์ฒ˜๋Ÿผ ์ •๋ฆฌํ•˜๋ฉด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

      type NextEventItem = RelatedEvent & { isEmpty?: boolean };
      
      export const buildNextEventList = (nextEvents: RelatedEvent[]): NextEventItem[] => {
        const MAX = 2;
        if (nextEvents.length >= MAX) return nextEvents.slice(0, MAX);
        const emptyItems: NextEventItem[] = Array(MAX - nextEvents.length).fill({ isEmpty: true } as NextEventItem);
        return [...nextEvents, ...emptyItems];
      };

      ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ฒ˜์—์„œ๋Š” item.isEmpty๋ฅผ ๊ทธ๋Œ€๋กœ ์“ฐ๋Š” ํ˜„์žฌ ์ฝ”๋“œ๊ฐ€ ํƒ€์ž… ์ƒ์œผ๋กœ๋„ ์•ˆ์ „ํ•ด์ง‘๋‹ˆ๋‹ค.

Also applies to: 87-122


1-3: ๋ฐ์ดํ„ฐ ํŽ˜์น˜/๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋กœ์ง ๋ฐ 'use client' ์‚ฌ์šฉ ์ ๊ฒ€

  • Line 1์˜ 'use client'๋Š” ์ด ํŒŒ์ผ์ด Pages Router์˜ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์ธ ์ ์„ ๊ณ ๋ คํ•˜๋ฉด ์‹ค์งˆ์ ์ธ ํšจ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ํ•™์Šต ๋‚ด์šฉ(์ด ๋ ˆํฌ์—์„œ๋Š” Pages Router์—์„œ use client๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)์„ ๋”ฐ๋ฅด๋ ค๋ฉด ์ œ๊ฑฐํ•˜๋Š” ํŽธ์ด ์ผ๊ด€๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Line 16โ€“18์—์„œ eventId = Number(id)๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค useEventDetail(eventId)๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. useEventDetail ๋‚ด๋ถ€ ๊ตฌํ˜„ ์ƒ fetchEventDetail์ด API ๋ž˜ํผ ์ „์ฒด({ success, code, message, data, timestamp })๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€, ์•„๋‹ˆ๋ฉด data๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ Line 28โ€“29์˜ ๊ตฌ์กฐ ๋ถ„ํ•ด:

    const { title, body, address, startDate, endDate, imageUrl, nextEvents } =
      eventDetail;

    ๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์ „๋ถ€ undefined๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. useEvents์—์„œ data?.data || []๋ฅผ ์“ฐ๋Š” ํŒจํ„ด์„ ๋ณด๋ฉด fetchEventDetail๋„ ์œ ์‚ฌํ•œ ๋ž˜ํผ๋ฅผ ์“ธ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์œผ๋ฏ€๋กœ, ์ด ๋ถ€๋ถ„์€ ์‹ค์ œ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ด ์ฃผ์„ธ์š” (eventDetail.data์—์„œ ๊ตฌ์กฐ ๋ถ„ํ•ดํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค).

  • Line 19โ€“23์˜ useEffect์—์„œ !isLoading && (isError || !eventDetail)์ด๋ฉด /events๋กœ replaceํ•˜๋Š”๋ฐ, eventId๊ฐ€ ์ˆซ์ž๋กœ ์ œ๋Œ€๋กœ ํŒŒ์‹ฑ๋˜์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ(NaN)์—๋„ enabled: !!eventId ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ eventDetail์ด ํ•ญ์ƒ undefined๋ผ ๊ณง๋ฐ”๋กœ ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€๋กœ ํŠ•๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ํ•„์š”ํ•˜๋‹ค๋ฉด id๊ฐ€ ์œ ํšจํ•œ ์ •์ˆ˜์ธ์ง€ ๋จผ์ € ๊ฒ€์ฆํ•œ ๋’ค(if (!id || Array.isArray(id) || Number.isNaN(eventId))) ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ•ด ๋ณด์…”๋„ ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

Also applies to: 8-27

src/shared/api/events/event.ts (1)

45-67: ๋ถ๋งˆํฌ ๊ด€๋ จ API ๋ž˜ํผ ๊ตฌํ˜„์€ ๋ช…ํ™•ํ•˜๊ณ  ์ผ๊ด€์ ์ž…๋‹ˆ๋‹ค

postBookmark, deleteBookmark, fetchBookmarkStatus ๋ชจ๋‘ REST ๊ฒฝ๋กœ์™€ HTTP ๋ฉ”์„œ๋“œ๊ฐ€ ์ง๊ด€์ ์œผ๋กœ ์ž˜ ๋งคํ•‘๋˜์–ด ์žˆ๊ณ , ๋ฐ˜ํ™˜ ํƒ€์ž…๋„ ApiResponse๋กœ ํ†ต์ผ๋˜์–ด ์žˆ์–ด ์‚ฌ์šฉ์„ฑ์ด ์ข‹์•„ ๋ณด์ž…๋‹ˆ๋‹ค.

Comment on lines +34 to +42
export const fetchEventDetail = async (
eventId: number,
): Promise<ApiResponse<EventData>> => {
const res = await apiWithToken.get(`/api/events/${eventId}`);

return {
...res.data,
data: res.data.data,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

ํ–‰์‚ฌ ์ƒ์„ธ ์‘๋‹ต์„ EventData๋กœ ๋งคํ•‘ํ•˜์ง€ ์•Š์•„ ํƒ€์ž…/ํ•„๋“œ ๋ถˆ์ผ์น˜ ๊ฐ€๋Šฅ์„ฑ

EventResponse์™€ EventData ํƒ€์ž… ์ •์˜๋ฅผ ๋ณด๋ฉด ๋ชฉ๋ก ์กฐํšŒ์—์„œ๋Š” mapEvent๋กœ ํ•„๋“œ๋ช…์„ ๋ณ€ํ™˜ํ•˜์ง€๋งŒ, ์ƒ์„ธ ์กฐํšŒ์—์„œ๋Š” res.data.data๋ฅผ ๊ทธ๋Œ€๋กœ EventData๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ์—์„œ ์ด๋ฏธ EventData ๋ชจ์–‘์œผ๋กœ ๋‚ด๋ ค์ฃผ์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ƒ์„ธ ํ™”๋ฉด์—์„œ id, name ๋“ฑ์˜ ํ•„๋“œ๋ฅผ ๊ธฐ๋Œ€ํ•  ๋•Œ eventId, title๋งŒ ์กด์žฌํ•ด ๊นจ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์ฒ˜๋Ÿผ ์ƒ์„ธ ์‘๋‹ต๋„ mapEvent๋ฅผ ๊ฑฐ์น˜๋„๋ก ์ˆ˜์ •ํ•˜๋Š” ์ชฝ์ด ๋” ์•ˆ์ „ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.

 export const fetchEventDetail = async (
   eventId: number,
 ): Promise<ApiResponse<EventData>> => {
   const res = await apiWithToken.get(`/api/events/${eventId}`);

   return {
-    ...res.data,
-    data: res.data.data,
+    ...res.data,
+    data: mapEvent(res.data.data as EventResponse),
   };
 };
๐Ÿ“ 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 fetchEventDetail = async (
eventId: number,
): Promise<ApiResponse<EventData>> => {
const res = await apiWithToken.get(`/api/events/${eventId}`);
return {
...res.data,
data: res.data.data,
};
export const fetchEventDetail = async (
eventId: number,
): Promise<ApiResponse<EventData>> => {
const res = await apiWithToken.get(`/api/events/${eventId}`);
return {
...res.data,
data: mapEvent(res.data.data as EventResponse),
};
};
๐Ÿค– Prompt for AI Agents
In src/shared/api/events/event.ts around lines 34 to 42, the event detail
response returns res.data.data directly which can mismatch EventData fields;
update the function to map the returned event through mapEvent before returning
(i.e., import mapEvent if missing and replace data: res.data.data with data:
mapEvent(res.data.data)) so the detail response has the same normalized shape as
the list endpoint.

Comment on lines +18 to +35
const EventCard = ({
eventId,
name,
address,
description,
variant = 'gray',
size = 'medium',
imageSrc = '',
liked = false,
}: EventCardProps) => {
const [liked, setLiked] = useState(false);
const { isBookmarked, toggleBookmark, requireLogin, setRequireLogin } =
useBookmark(eventId, liked);


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

๋ถ๋งˆํฌ ๋ฒ„ํŠผ ์ ‘๊ทผ์„ฑ(a11y) ๋ณด์™„ ํ•„์š”

์ค‘๊ฐ„/๋Œ€ํ˜• ์นด๋“œ์—์„œ ํ•˜ํŠธ ์•„์ด์ฝ˜ ๋ฒ„ํŠผ(Line 104โ€“123, 150โ€“169)์€ ์‹œ๊ฐ์ ์œผ๋กœ๋Š” ๋ช…ํ™•ํ•˜์ง€๋งŒ, <button> ์•ˆ์— ํ…์ŠคํŠธ๊ฐ€ ์—†๊ณ  aria-label๋„ ์—†์–ด ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์—์„œ๋Š” ๋‹จ์ˆœํžˆ โ€œ๋ฒ„ํŠผโ€์œผ๋กœ๋งŒ ์ฝํž ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ Icon ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ณธ๊ฐ’์ด ariaHidden์ธ ์ ์„ ๊ณ ๋ คํ•˜๋ฉด ์ด๋ฆ„ ์—†๋Š” ๋ฒ„ํŠผ์ด ๋ฉ๋‹ˆ๋‹ค.

  • ๋ฒ„ํŠผ์— aria-label์„ ๋ถ€์—ฌํ•˜๊ฑฐ๋‚˜,
  • ํ˜น์€ ๋ฒ„ํŠผ ๋‚ด๋ถ€์— sr-only ํ…์ŠคํŠธ(๋ถ๋งˆํฌ ์ถ”๊ฐ€, ๋ถ๋งˆํฌ ํ•ด์ œ ๋“ฑ)๋ฅผ ์ถ”๊ฐ€ํ•ด ์˜๋ฏธ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

<button
  className="cursor-pointer p-1"
  onClick={(e) => { e.stopPropagation(); toggleBookmark(); }}
  aria-label={isBookmarked ? '๋ถ๋งˆํฌ ํ•ด์ œ' : '๋ถ๋งˆํฌ ์ถ”๊ฐ€'}
>
  <Icon
    name="HeartStraight"
    size={20}
    color={isBookmarked ? 'red-400' : variant === 'mint' ? 'mint-400' : 'gray-300'}
    fillColor={isBookmarked ? 'red-300' : undefined}
  />
</button>

๋˜ํ•œ ๋™์ผํ•œ ํ•˜ํŠธ ๋ฒ„ํŠผ ๋กœ์ง์ด medium/large ์นด๋“œ์— ์ค‘๋ณต๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์†Œ๊ทœ๋ชจ๋ผ๋„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ BookmarkButton ์„œ๋ธŒ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋” ์‰ฌ์›Œ์งˆ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Also applies to: 48-124, 137-180

๐Ÿค– Prompt for AI Agents
In src/shared/components/container/EventCard.tsx around lines 22 to 35 (and
apply same changes at 48-124, 137-180), the heart bookmark buttons lack
accessible names because the Icon is aria-hidden and the button has no
aria-label or visible text; update the button to include an appropriate
aria-label that reflects state (e.g., aria-label={isBookmarked ? '๋ถ๋งˆํฌ ํ•ด์ œ' : '๋ถ๋งˆํฌ
์ถ”๊ฐ€'}) or add an sr-only text node inside the button, preserve existing onClick
behavior (stopPropagation then toggleBookmark), and refactor the duplicated
bookmark button markup into a small reusable BookmarkButton subcomponent that
accepts isBookmarked, onToggle, variant props and handles aria-label/sr-only and
styling so both medium and large card variants use the same accessible
implementation.

Comment on lines +7 to +21
export const useEvents = (date?: Date) => {
const formattedDate = formatDateToISO(date);

const { data, isLoading, isError } = useQuery({
queryKey: ['events', formattedDate],
queryFn: () => fetchEvents(formattedDate!),
enabled: !!formattedDate,
});

return {
events: data?.data || [],
isLoading,
isError,
};
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

fetchEvents์˜ N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ ํ™•์ธ ํ•„์š”

๊ด€๋ จ ์ฝ”๋“œ(src/shared/api/events/event.ts)๋ฅผ ๋ณด๋ฉด fetchEvents๊ฐ€ ๊ฐ ์ด๋ฒคํŠธ๋งˆ๋‹ค ๊ฐœ๋ณ„์ ์œผ๋กœ ์ƒ์„ธ ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(Lines 13-16). ์ด๋Š” N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•˜์—ฌ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐฑ์—”๋“œ API๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์ด๋ฒคํŠธ ๋ชฉ๋ก ์กฐํšŒ ์‹œ ํ•„์š”ํ•œ ๋ชจ๋“  ์ •๋ณด(imageUrl, isBookmarked ํฌํ•จ)๋ฅผ ํ•œ ๋ฒˆ์— ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋˜๋Š” GraphQL์ด๋‚˜ ๋ฐฐ์น˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

๋‹ค์Œ ์Šคํฌ๋ฆฝํŠธ๋กœ fetchEvents์˜ ๊ตฌํ˜„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:


๐Ÿ Script executed:

#!/bin/bash
# fetchEvents ๊ตฌํ˜„ ํ™•์ธ
rg -A 20 "export const fetchEvents" --type ts

Length of output: 1201


fetchEvents์—์„œ N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ ํ™•์ธ๋จ

ํ™•์ธ๋œ ๋ฐ”์™€ ๊ฐ™์ด src/shared/api/events/event.ts์˜ fetchEvents ํ•จ์ˆ˜๋Š” ์ด๋ฒคํŠธ ๋ชฉ๋ก์„ ์กฐํšŒํ•œ ํ›„, ๊ฐ ์ด๋ฒคํŠธ๋งˆ๋‹ค ์ƒ์„ธ ์ •๋ณด(imageUrl, isBookmarked)๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์š”์ฒญํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” N๊ฐœ์˜ ์ด๋ฒคํŠธ๊ฐ€ ์žˆ์„ ๋•Œ ์ด N+1๊ฐœ์˜ API ํ˜ธ์ถœ์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

๋ฐฑ์—”๋“œ API๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์ดˆ๊ธฐ ์ด๋ฒคํŠธ ๋ชฉ๋ก ์กฐํšŒ ์‘๋‹ต์— ํ•„์š”ํ•œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ฑฐ๋‚˜, ๋ฐฐ์น˜ ์กฐํšŒ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค– Prompt for AI Agents
In src/shared/hooks/events/useEvents.ts around lines 7-21, useQuery currently
calls fetchEvents which (per review) issues per-event detail requests causing an
N+1 API call problem; fix this by changing fetchEvents
(src/shared/api/events/event.ts) to return all required fields in one network
call (either by adding an includeDetails flag to the events endpoint or by
introducing a batch-details endpoint that accepts an array of event IDs), update
the queryFn here to call that new/updated fetchEvents signature (passing
formattedDate and the include/details flag or performing a single batch-details
request inside fetchEvents), and ensure the returned data structure already
contains imageUrl and isBookmarked so useEvents no longer triggers per-event
requests.

Copy link
Copy Markdown
Contributor

@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: 1

โ™ป๏ธ Duplicate comments (1)
src/pages/mypage/postcard/[id].tsx (1)

3-5: ์—ฝ์„œ ์ƒ์„ธ ํŽ˜์ด์ง€๊ฐ€ ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋กœ ๋Œ€์ฒด๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

PostcardPage ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ํ…์ŠคํŠธ๋งŒ ๋ Œ๋”๋งํ•˜์—ฌ /mypage/postcard/[id] ๋ผ์šฐํŠธ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฆฌ๋ทฐ์—์„œ๋„ ์ง€์ ๋œ ๊ฒƒ์ฒ˜๋Ÿผ, ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค:

  1. ์ •๋ฏผ๋‹˜์˜ ๊ตฌํ˜„์„ ๋ฐ›๊ธฐ ์ „๊นŒ์ง€ ์ด ํŒŒ์ผ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ PR์—์„œ ์ œ์™ธ
  2. ์ž„์‹œ๋กœ "์ค€๋น„์ค‘" ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ ์ ˆํ•œ UI ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„
  3. ํ•ด๋‹น ๋ผ์šฐํŠธ๋กœ์˜ ์ ‘๊ทผ์„ ๋ง‰๋Š” ๋กœ์ง ์ถ”๊ฐ€

์ž„์‹œ ๊ตฌํ˜„ ์˜ˆ์‹œ:

 export default function PostcardPage() {
-  return <div>์ •๋ฏผ์ด๊ฑฐ ๋ฐ›์•„์˜ค๊ธฐ</div>;
+  return (
+    <div className="flex items-center justify-center min-h-screen">
+      <p className="text-gray-500">์ค€๋น„์ค‘์ž…๋‹ˆ๋‹ค.</p>
+    </div>
+  );
 }
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between e730c25 and b2de4d1.

๐Ÿ“’ Files selected for processing (1)
  • src/pages/mypage/postcard/[id].tsx (1 hunks)
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.

Applied to files:

  • src/pages/mypage/postcard/[id].tsx
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy

Copy link
Copy Markdown
Contributor

@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: 6

Caution

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

โš ๏ธ Outside diff range comments (1)
src/pages/events/index.tsx (1)

1-1: Pages Router์—์„œ๋Š” 'use client' ์ง€์‹œ์ž๊ฐ€ ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๊ฐ€ Next.js Pages Router๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ 'use client' ์ง€์‹œ์ž๋ฅผ ์ œ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Based on learnings

-'use client';
-
 import { useState } from 'react';
โ™ป๏ธ Duplicate comments (1)
src/shared/api/events/event.ts (1)

41-50: ์ด์ „ ๋ฆฌ๋ทฐ ์ง€์ ์‚ฌํ•ญ: fetchEventDetail์—์„œ mapEvent๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ ํƒ€์ž… ๋ถˆ์ผ์น˜ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

fetchEventDetail์ด EventData๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ ์–ธ๋˜์–ด ์žˆ์ง€๋งŒ mapEvent๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ๊ฐ€ EventData ํ˜•์‹์„ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, eventId/title ๊ฐ™์€ ํ•„๋“œ๊ฐ€ id/name์œผ๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š์•„ ํƒ€์ž… ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 export const fetchEventDetail = async (
   eventId: number,
 ): Promise<ApiResponse<EventData>> => {
   const res = await apiWithToken.get(`/api/events/${eventId}`);
 
   return {
     ...res.data,
-    data: res.data.data,
+    data: mapEvent(res.data.data as EventResponse),
   };
 };
๐Ÿงน Nitpick comments (2)
src/shared/hooks/events/useBookmark.ts (1)

13-15: ๋ถˆํ•„์š”ํ•œ useEffect๋ฅผ ์ œ๊ฑฐํ•˜์„ธ์š”.

useState(initialState)๊ฐ€ ์ด๋ฏธ ์ดˆ๊ธฐ๊ฐ’์„ ์„ค์ •ํ•˜๋ฏ€๋กœ, useEffect๋กœ ๋‹ค์‹œ ๋™๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์€ ์ค‘๋ณต์ž…๋‹ˆ๋‹ค. initialState๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ํ•„์š”ํ•˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ props๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ๋งˆ์šดํŠธ๋ฉ๋‹ˆ๋‹ค.

-  useEffect(() => {
-    setIsBookmarked(initialState);
-  }, [initialState]);
src/pages/events/index.tsx (1)

21-21: ๋ถˆํ•„์š”ํ•œ ๋ณ€์ˆ˜ ํ• ๋‹น์„ ์ œ๊ฑฐํ•˜์„ธ์š”.

filteredEvents๊ฐ€ ๋‹จ์ˆœํžˆ events๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์‹ค์ œ ํ•„ํ„ฐ๋ง ๋กœ์ง์ด ์—†์Šต๋‹ˆ๋‹ค. ์ง์ ‘ events๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ํ–ฅํ›„ ํ•„ํ„ฐ๋ง ๋กœ์ง์„ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ด๋ผ๋ฉด TODO ์ฃผ์„์„ ๋‚จ๊ธฐ์„ธ์š”.

-  const filteredEvents = events;

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์—์„œ events๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ:

-        {filteredEvents.length > 0 ? (
+        {events.length > 0 ? (
           <section
             aria-label='์ด๋ฒคํŠธ ๋ชฉ๋ก'
             className={cn(
               'grid w-full mt-[1.4rem]',
               'grid-cols-2 gap-x-[1.4rem] gap-y-[1.4rem]',
             )}
           >
-             {filteredEvents.map((event: EventData) => (
+             {events.map((event: EventData) => (
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between b2de4d1 and 6098283.

๐Ÿ“’ Files selected for processing (8)
  • next.config.ts (1 hunks)
  • src/pages/events/[id].tsx (2 hunks)
  • src/pages/events/index.tsx (2 hunks)
  • src/shared/api/events/event.ts (1 hunks)
  • src/shared/hooks/events/EventMapper.ts (1 hunks)
  • src/shared/hooks/events/useBookmark.ts (1 hunks)
  • src/shared/types/eventtypes.ts (1 hunks)
  • src/shared/utils/buildNextEventList.ts (1 hunks)
๐Ÿšง Files skipped from review as they are similar to previous changes (3)
  • src/shared/hooks/events/EventMapper.ts
  • next.config.ts
  • src/shared/utils/buildNextEventList.ts
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.

Applied to files:

  • src/pages/events/index.tsx
  • src/pages/events/[id].tsx
๐Ÿงฌ Code graph analysis (4)
src/shared/hooks/events/useBookmark.ts (2)
src/shared/utils/token.ts (1)
  • getAccessToken (8-9)
src/shared/api/events/event.ts (2)
  • deleteBookmark (61-66)
  • postBookmark (53-58)
src/pages/events/index.tsx (3)
src/shared/hooks/events/useEvents.ts (1)
  • useEvents (7-21)
src/shared/components/datePicker/DatePicker.tsx (1)
  • DatePicker (17-97)
src/shared/types/eventtypes.ts (1)
  • EventData (12-21)
src/pages/events/[id].tsx (4)
src/shared/types/eventtypes.ts (1)
  • RelatedEventOrEmpty (42-42)
src/shared/hooks/events/useEventDetail.ts (1)
  • useEventDetail (15-21)
src/shared/utils/buildNextEventList.ts (1)
  • buildNextEventList (5-17)
src/pages/events/components/DateTag.tsx (1)
  • DateTag (24-44)
src/shared/api/events/event.ts (3)
src/shared/types/authtypes.ts (1)
  • ApiResponse (1-6)
src/shared/types/eventtypes.ts (2)
  • EventData (12-21)
  • EventResponse (1-11)
src/shared/api/instance.ts (1)
  • apiWithToken (14-17)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
๐Ÿ”‡ Additional comments (2)
src/shared/api/events/event.ts (1)

4-4: mapEvent๋ฅผ importํ–ˆ์ง€๋งŒ fetchEventDetail์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

mapEvent๋ฅผ importํ–ˆ์ง€๋งŒ fetchEventDetail ํ•จ์ˆ˜์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ fetchEventDetail ์ˆ˜์ •์‚ฌํ•ญ์„ ์ ์šฉํ•˜๋ฉด ์ด import๊ฐ€ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

src/shared/types/eventtypes.ts (1)

1-42: ํƒ€์ž… ์ •์˜์™€ ๋งคํ•‘ ๊ตฌํ˜„์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒ€์ฆ ๊ฒฐ๊ณผ, mapEvent ํ•จ์ˆ˜๊ฐ€ EventResponse์˜ optional ํ•„๋“œ๋“ค์„ EventData์˜ required ํ•„๋“œ๋“ค๋กœ ์ •ํ™•ํ•˜๊ฒŒ ๋ณ€ํ™˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

  • address, startDate, endDate: ๋นˆ ๋ฌธ์ž์—ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
  • imageSrc: thumbnailUrl์„ ์šฐ์„ ์œผ๋กœ, ์—†์„ ๊ฒฝ์šฐ imageUrl ์‚ฌ์šฉ (๋ช…ํ™•ํ•œ ํด๋ฐฑ ๋กœ์ง)
  • liked: false ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์ฒ˜๋ฆฌ

ํƒ€์ž… ๊ตฌ์กฐ์™€ ๊ตฌํ˜„ ๋กœ์ง์ด ์ผ๊ด€์„ฑ ์žˆ๊ณ  ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ์ถ”๊ฐ€ ์ˆ˜์ •์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

imageSrc={item.imageUrl}
variant="gray"
size="small"
liked={false}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

๊ด€๋ จ ํ–‰์‚ฌ์˜ ๋ถ๋งˆํฌ ์ƒํƒœ ํ™•์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ด€๋ จ ํ–‰์‚ฌ์˜ liked ์†์„ฑ์ด false๋กœ ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. PR ๋ชฉํ‘œ์— "๋ถ๋งˆํฌ UI ๋™๊ธฐํ™” ํ™•์ธ (todo)"๊ฐ€ ์–ธ๊ธ‰๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ถ”ํ›„ ์ž‘์—…์œผ๋กœ ์‹ค์ œ ๋ถ๋งˆํฌ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ RelatedEvent ํƒ€์ž…์— isBookmarked ํ•„๋“œ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”:


๐Ÿ Script executed:

#!/bin/bash
# RelatedEvent ํƒ€์ž… ์ •์˜ ํ™•์ธ
ast-grep --pattern $'interface RelatedEvent {
  $$$
}'

Length of output: 259


๐Ÿ Script executed:

#!/bin/bash
# Check the actual code context around line 123 in [id].tsx
sed -n '115,130p' src/pages/events/[id].tsx

Length of output: 445


๐Ÿ Script executed:

#!/bin/bash
# Search for RelatedEvent component usage and bookmark-related props
rg -n "liked\s*=" src/pages/events/[id].tsx -A 2 -B 2

Length of output: 313


๐Ÿ Script executed:

#!/bin/bash
# Check if there's an Event type with bookmark state that should be mapped to RelatedEvent
rg -n "interface Event|type Event" src/shared/types/ -A 10

Length of output: 1937


RelatedEvent ํƒ€์ž…์— isBookmarked ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฒ€์ฆ ๊ฒฐ๊ณผ:

  • RelatedEvent ์ธํ„ฐํŽ˜์ด์Šค์— isBookmarked ํ•„๋“œ๊ฐ€ ์‹ค์ œ๋กœ ์—†์Œ (src/shared/types/eventtypes.ts:23-27)
  • ๋ฉ”์ธ ํ–‰์‚ฌ๋Š” eventDetail.isBookmarked๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ (line 100)
  • ๊ด€๋ จ ํ–‰์‚ฌ๋Š” liked={false}๋กœ ํ•˜๋“œ์ฝ”๋”ฉ๋จ (line 123)

ํ•„์š”ํ•œ ์ž‘์—…:

  1. src/shared/types/eventtypes.ts์˜ RelatedEvent ์ธํ„ฐํŽ˜์ด์Šค์— isBookmarked?: boolean ํ•„๋“œ ์ถ”๊ฐ€
  2. API ์‘๋‹ต์—์„œ ๊ด€๋ จ ํ–‰์‚ฌ์˜ ๋ถ๋งˆํฌ ์ƒํƒœ ๋ฐ์ดํ„ฐ ํฌํ•จ ํ™•์ธ
  3. src/pages/events/[id].tsx:123์˜ liked={false}๋ฅผ liked={item.isBookmarked ?? false}๋กœ ๋ณ€๊ฒฝ

์ด๋Š” PR ๋ชฉํ‘œ์ธ "๋ถ๋งˆํฌ UI ๋™๊ธฐํ™” ํ™•์ธ (todo)"๋ฅผ ์™„๋ฃŒํ•˜๋Š” ๋ฐ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

๐Ÿค– Prompt for AI Agents
In src/pages/events/[id].tsx around line 123 and src/shared/types/eventtypes.ts
around lines 23-27: RelatedEvent is missing an isBookmarked field and the
related-event component is hardcoding liked to false; add an optional boolean
isBookmarked? field to the RelatedEvent interface in
src/shared/types/eventtypes.ts, ensure the API response for related events
includes the bookmark state (update backend/serialization or mapper as needed),
and replace the hardcoded liked={false} in src/pages/events/[id].tsx with a prop
that uses the related itemโ€™s isBookmarked value with a safe fallback (use
item.isBookmarked if present, otherwise false).

Comment on lines +14 to +30
const mapped = await Promise.all(
res.data.data.map(async (item: EventResponse) => {
const detail = await apiWithToken.get(`/api/events/${item.eventId}`);
const detailData = detail.data.data;

return {
id: item.eventId,
name: item.title,
description: item.body,
address: item.address ?? '',
startDate: item.startDate ?? '',
endDate: item.endDate ?? '',
imageSrc: detailData.imageUrl ?? '',
liked: item.isBookmarked ?? false,
} satisfies EventData;
}),
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

์‹ฌ๊ฐํ•œ N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๊ฐ ์ด๋ฒคํŠธ๋งˆ๋‹ค ๊ฐœ๋ณ„์ ์œผ๋กœ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ API ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ๊ฐ€ N๊ฐœ๋ผ๋ฉด ์ด N+1๋ฒˆ์˜ API ํ˜ธ์ถœ์ด ๋ฐœ์ƒํ•˜์—ฌ ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ: ์ด๋ฒคํŠธ 50๊ฐœ โ†’ 51๋ฒˆ์˜ API ํ˜ธ์ถœ (1๋ฒˆ ๋ชฉ๋ก + 50๋ฒˆ ์ƒ์„ธ)

๋ฐฑ์—”๋“œ ํŒ€๊ณผ ํ˜‘์˜ํ•˜์—ฌ ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”:

  1. ๋ชฉ๋ก API ์‘๋‹ต์— imageUrl๊ณผ isBookmarked ํฌํ•จ
  2. ์ผ๊ด„ ์ƒ์„ธ ์กฐํšŒ API ์ถ”๊ฐ€ (์˜ˆ: POST /api/events/batch)
  3. GraphQL ๋˜๋Š” ํ•„๋“œ ์„ ํƒ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ์ง€์›

์ž„์‹œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์ธก ์บ์‹ฑ์ด๋‚˜ ๋ฐฐ์น˜ ์š”์ฒญ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ์€ ๋ฐฑ์—”๋“œ API ๊ฐœ์„ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

// ์ž„์‹œ ํ•ด๊ฒฐ์ฑ… ์˜ˆ์‹œ (๋ฐฑ์—”๋“œ ๋ณ€๊ฒฝ์ด ์šฐ์„ )
export const fetchEvents = async (
  date: string,
): Promise<ApiResponse<EventData[]>> => {
  const res = await apiWithToken.get('/api/events', {
    params: { date },
  });

  // ์ƒ์„ธ ์ •๋ณด๊ฐ€ ์ด๋ฏธ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •
  const mapped = res.data.data.map((item: EventResponse) => ({
    id: item.eventId,
    name: item.title,
    description: item.body,
    address: item.address ?? '',
    startDate: item.startDate ?? '',
    endDate: item.endDate ?? '',
    imageSrc: item.imageUrl ?? '',  // ๋ชฉ๋ก API์—์„œ ์ œ๊ณต
    liked: item.isBookmarked ?? false,
  }));

  return {
    ...res.data,
    data: mapped,
  };
};

Copy link
Copy Markdown
Contributor

@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: 2

๐Ÿงน Nitpick comments (1)
src/shared/types/membertypes.ts (1)

5-13: ํƒ€์ž… ์•ˆ์ •์„ฑ ๊ฐœ์„  ์ œ์•ˆ

๋ชจ๋“  ํ•„๋“œ(eventId ์ œ์™ธ)๊ฐ€ optional๋กœ ์ •์˜๋˜์–ด ์žˆ์–ด ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ์•ฝํ™”๋ฉ๋‹ˆ๋‹ค. API ์‘๋‹ต์—์„œ ํ•ญ์ƒ ์ œ๊ณต๋˜๋Š” ํ•„๋“œ๋Š” required๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, PR ์š”์•ฝ์—์„œ ์–ธ๊ธ‰๋œ "๋ถ๋งˆํฌ UI ๋™๊ธฐํ™”"๋ฅผ ์œ„ํ•ด์„œ๋Š” liked ๋˜๋Š” isBookmarked ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API ์‘๋‹ต ์ŠคํŽ™์„ ํ™•์ธํ•œ ํ›„, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜์„ธ์š”:

bookmarkedEvents: {
  eventId: number;
  name: string;  // ํ•ญ์ƒ ์ œ๊ณต๋˜๋ฉด required๋กœ
  address: string;
  description: string;
  imageSrc?: string;  // ์„ ํƒ์ ์œผ๋กœ ์ œ๊ณต๋˜๋ฉด optional
  startDate?: string;
  endDate?: string;
  isBookmarked?: boolean;  // ๋ถ๋งˆํฌ ์ƒํƒœ ์ถ”์ ์šฉ
}[];
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 6098283 and 1b116df.

๐Ÿ“’ Files selected for processing (4)
  • src/pages/mypage/index.tsx (3 hunks)
  • src/shared/api/member.ts (0 hunks)
  • src/shared/hooks/events/useBookmark.ts (1 hunks)
  • src/shared/types/membertypes.ts (1 hunks)
๐Ÿ’ค Files with no reviewable changes (1)
  • src/shared/api/member.ts
๐Ÿšง Files skipped from review as they are similar to previous changes (2)
  • src/shared/hooks/events/useBookmark.ts
  • src/pages/mypage/index.tsx
๐Ÿงฐ Additional context used
๐Ÿงฌ Code graph analysis (1)
src/shared/types/membertypes.ts (3)
src/shared/constants/events/eventsData.ts (1)
  • EventData (1-9)
src/shared/components/container/EventCard.tsx (2)
  • EventCardProps (7-16)
  • EventCardProps (18-164)
src/pages/events/index.tsx (2)
  • EventPage (16-98)
  • event (59-74)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy

Comment on lines +5 to 13
bookmarkedEvents: {
eventId: number;
eventName: string;
eventImageUrl: string;
title?: string;
body?: string;
address?: string;
imageUrl?: string;
eventName?: string;
eventImageUrl?: string;
}[];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

์ค‘๋ณต๋˜๋Š” ํ•„๋“œ๋ช…์œผ๋กœ ์ธํ•œ ํ˜ผ๋ž€ ๊ฐ€๋Šฅ์„ฑ

๋™์ผํ•œ ๊ฐœ๋…์„ ๋‚˜ํƒ€๋‚ด๋Š” ํ•„๋“œ๊ฐ€ ์ค‘๋ณต ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค:

  • title๊ณผ eventName (lines 7, 11)
  • imageUrl๊ณผ eventImageUrl (lines 10, 12)

์ด๋Š” ์†Œ๋น„ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ์–ด๋–ค ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ• ์ง€ ํ˜ผ๋ž€์„ ์•ผ๊ธฐํ•˜๊ณ  ๋ฒ„๊ทธ์˜ ์›์ธ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ diff๋ฅผ ์ ์šฉํ•˜์—ฌ ์ค‘๋ณต ํ•„๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  EventData ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜์„ธ์š”:

  bookmarkedEvents: {
    eventId: number;
-   title?: string;
-   body?: string;
+   name?: string;
+   description?: string;
    address?: string;
-   imageUrl?: string;
-   eventName?: string;
-   eventImageUrl?: string;
+   imageSrc?: string;
+   startDate?: string;
+   endDate?: string;
  }[];
๐Ÿ“ 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
bookmarkedEvents: {
eventId: number;
eventName: string;
eventImageUrl: string;
title?: string;
body?: string;
address?: string;
imageUrl?: string;
eventName?: string;
eventImageUrl?: string;
}[];
bookmarkedEvents: {
eventId: number;
name?: string;
description?: string;
address?: string;
imageSrc?: string;
startDate?: string;
endDate?: string;
}[];
๐Ÿค– Prompt for AI Agents
In src/shared/types/membertypes.ts around lines 5 to 13, the bookmarkedEvents
item defines duplicated fields (title vs eventName and imageUrl vs
eventImageUrl) causing ambiguity; remove the duplicate fields so the shape
matches the canonical EventData interface (pick the canonical names used
elsewhere โ€” e.g., eventName and eventImageUrl), keep eventId, address, body and
optional canonical fields, and update any consuming code/tests to use the chosen
names to maintain consistency.

โš ๏ธ Potential issue | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

EventData ์ธํ„ฐํŽ˜์ด์Šค์™€์˜ ๋ถˆ์ผ์น˜

bookmarkedEvents์˜ ํ•„๋“œ๋ช…์ด src/shared/constants/events/eventsData.ts์˜ EventData ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค:

  • body โ†” description
  • imageUrl/eventImageUrl โ†” imageSrc
  • eventId โ†” id
  • title/eventName โ†” name

๋˜ํ•œ EventData์— ์žˆ๋Š” startDate์™€ endDate ํ•„๋“œ๊ฐ€ ๋ˆ„๋ฝ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•„๋“œ๋“ค์€ src/pages/events/index.tsx์˜ ์ด๋ฒคํŠธ ํ•„ํ„ฐ๋ง ๋กœ์ง์—์„œ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ, ๋ถ๋งˆํฌ๋œ ์ด๋ฒคํŠธ๋ฅผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ EventData ํƒ€์ž…์˜ ์ •์˜์™€ ์‚ฌ์šฉ์ฒ˜๋ฅผ ํ™•์ธํ•˜์„ธ์š”:


๐Ÿ Script executed:

#!/bin/bash
# EventData ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ ํ™•์ธ
ast-grep --pattern 'interface EventData {
  $$$
}'

# bookmarkedEvents ์‚ฌ์šฉ์ฒ˜ ํ™•์ธ
rg -nP --type=ts --type=tsx -C3 'bookmarkedEvents'

Length of output: 548


Based on my verification of the EventData interface in the codebase, I can confirm the findings. The review comment correctly identifies field name mismatches, though it references the wrong file location. Here's the rewritten comment:

bookmarkedEvents ํƒ€์ž…์ด EventData์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š์Œ

EventData ์ธํ„ฐํŽ˜์ด์Šค(src/shared/types/eventtypes.ts)์˜ ํ•„๋“œ ๊ตฌ์กฐ์™€ bookmarkedEvents์˜ ํ•„๋“œ๋ช…์ด ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. TypeScript์˜ ๊ตฌ์กฐ์  ํƒ€์ดํ•‘์— ๋”ฐ๋ฅด๋ฉด ํ•„๋“œ๋ช…์ด ์ •ํ™•ํžˆ ์ผ์น˜ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋‹ค์Œ ํ•„๋“œ๋“ค์„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • eventId โ†’ id
  • title/eventName โ†’ name
  • body โ†’ description
  • imageUrl/eventImageUrl โ†’ imageSrc
  • ๋ˆ„๋ฝ: startDate, endDate, liked

src/shared/types/membertypes.ts์˜ bookmarkedEvents ํƒ€์ž…์„ EventData ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ผ์น˜ํ•˜๋„๋ก ์ •๋ ฌํ•˜์„ธ์š”.

๐Ÿค– Prompt for AI Agents
In src/shared/types/membertypes.ts around lines 5 to 13, the bookmarkedEvents
type does not match the EventData interface: rename eventId โ†’ id, title and
eventName โ†’ name, body โ†’ description, imageUrl and eventImageUrl โ†’ imageSrc; add
the missing fields startDate, endDate, and liked with the same types as in
EventData; ensure the resulting bookmarkedEvents array type exactly mirrors the
EventData interface (field names and types) defined in
src/shared/types/eventtypes.ts.

Copy link
Copy Markdown
Contributor

@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: 2

โ™ป๏ธ Duplicate comments (5)
src/pages/mypage/index.tsx (1)

78-78: ํ”Œ๋ ˆ์ด์Šคํ™€๋” ์ด๋ฒคํŠธ ID ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ

์ด์ „ ๋ฆฌ๋ทฐ์—์„œ ์ด๋ฏธ ์ง€์ ๋œ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ํ•˜๋“œ์ฝ”๋”ฉ๋œ ID(99, 98)๊ฐ€ ์‹ค์ œ API ์ด๋ฒคํŠธ ID์™€ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์Œ์ˆ˜ ์ƒ์ˆ˜ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

src/shared/components/container/EventCard.tsx (1)

107-126: ๋ถ๋งˆํฌ ๋ฒ„ํŠผ ์ ‘๊ทผ์„ฑ ๋ฐ ์ค‘๋ณต ์ฝ”๋“œ

์ด์ „ ๋ฆฌ๋ทฐ์—์„œ ์ง€์ ๋œ ์ ‘๊ทผ์„ฑ ๋ฌธ์ œ๊ฐ€ ์—ฌ์ „ํžˆ ๋‚จ์•„์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ๋งˆํฌ ๋ฒ„ํŠผ์— aria-label์ด ์—†์–ด ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ถˆ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋กœ, medium(Lines 107-126)๊ณผ large(Lines 153-172) ์นด๋“œ์˜ ๋ถ๋งˆํฌ ๋ฒ„ํŠผ ๋กœ์ง์ด ๊ฑฐ์˜ ๋™์ผํ•˜๊ฒŒ ์ค‘๋ณต๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ BookmarkButton ์ปดํฌ๋„ŒํŠธ๋กœ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค:

const BookmarkButton = ({ 
  isBookmarked, 
  variant, 
  onToggle 
}: { 
  isBookmarked: boolean; 
  variant: 'gray' | 'mint'; 
  onToggle: () => void;
}) => (
  <button
    className="cursor-pointer p-1"
    onClick={(e) => {
      e.stopPropagation();
      onToggle();
    }}
    aria-label={isBookmarked ? '๋ถ๋งˆํฌ ํ•ด์ œ' : '๋ถ๋งˆํฌ ์ถ”๊ฐ€'}
  >
    <Icon
      name="HeartStraight"
      size={20}
      color={
        isBookmarked
          ? 'red-400'
          : variant === 'mint'
          ? 'mint-400'
          : 'gray-300'
      }
      fillColor={isBookmarked ? 'red-300' : undefined}
    />
  </button>
);
src/pages/mypage/events/[id].tsx (3)

16-20: ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋กœ ์ธํ•œ UI ๊นœ๋นก์ž„

์ด์ „ ๋ฆฌ๋ทฐ์—์„œ ์ง€์ ๋œ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ๋กœ๋”ฉ ์ค‘ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋กœ์ง์ด ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์‹œ ๊นœ๋นก์ž„์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋กœ๋”ฉ ์ƒํƒœ์ผ ๋•Œ ์Šค์ผˆ๋ ˆํ†ค UI๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์€ UX๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


55-61: Image ์ปดํฌ๋„ŒํŠธ์— sizes ์†์„ฑ ๋ˆ„๋ฝ

์ด์ „ ๋ฆฌ๋ทฐ์—์„œ ์ง€์ ๋œ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. fill ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” Image ์ปดํฌ๋„ŒํŠธ์— sizes ์†์„ฑ์ด ์—†์–ด ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  <Image
    src={imageUrl}
    alt={`${title} ์ด๋ฏธ์ง€`}
    fill
+   sizes="(max-width: 354px) 100vw, 354px"
    className='object-cover rounded-[2rem]'
  />

86-86: ๋ถ๋งˆํฌ ์ƒํƒœ ํด๋ฐฑ ๋กœ์ง ์žฌ๊ฒ€ํ†  ํ•„์š”

์ด์ „ ๋ฆฌ๋ทฐ์—์„œ ์ง€์ ๋œ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. eventDetail.isBookmarked ?? true๋Š” isBookmarked๊ฐ€ undefined์ผ ๋•Œ true๋กœ ํด๋ฐฑํ•˜๋ฏ€๋กœ, ์ €์žฅํ•œ ํ–‰์‚ฌ ํŽ˜์ด์ง€๋ผ๋ฉด ํ•ญ์ƒ true๋กœ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜ false๋กœ ํด๋ฐฑํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿงน Nitpick comments (1)
src/shared/components/container/EventCard.tsx (1)

1-1: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” useEffect import ์ œ๊ฑฐ

useEffect๊ฐ€ import๋˜์—ˆ์ง€๋งŒ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ถˆํ•„์š”ํ•œ import๋Š” ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

-import { useState, useEffect } from 'react';
+import { useState } from 'react';
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 1b116df and 7a954e9.

๐Ÿ“’ Files selected for processing (3)
  • src/pages/mypage/events/[id].tsx (4 hunks)
  • src/pages/mypage/index.tsx (5 hunks)
  • src/shared/components/container/EventCard.tsx (2 hunks)
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.

Applied to files:

  • src/pages/mypage/events/[id].tsx
  • src/pages/mypage/index.tsx
๐Ÿงฌ Code graph analysis (2)
src/shared/components/container/EventCard.tsx (4)
src/shared/hooks/events/useBookmark.ts (1)
  • useBookmark (8-47)
src/pages/mypage/components/LoginRequired.tsx (1)
  • LoginRequired (10-21)
src/shared/components/container/Card.tsx (1)
  • Card (32-48)
src/shared/icons/components/icon.tsx (1)
  • Icon (58-130)
src/pages/mypage/events/[id].tsx (3)
src/shared/hooks/events/useEventDetail.ts (1)
  • useEventDetail (15-21)
src/pages/events/index.tsx (2)
  • EventPage (16-98)
  • event (59-74)
src/pages/events/[id].tsx (3)
  • router (8-111)
  • router (25-25)
  • e (12-12)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
๐Ÿ”‡ Additional comments (4)
src/shared/components/container/EventCard.tsx (2)

34-35: ๋ถ๋งˆํฌ ํ›… ํ†ตํ•ฉ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

useBookmark ํ›…์„ ํ†ตํ•œ ๋ถ๋งˆํฌ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ์ ์ ˆํ•˜๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. eventId์™€ liked props๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ํ•„์š”ํ•œ ์ƒํƒœ ๋ฐ ํ•จ์ˆ˜๋ฅผ ์ •ํ™•ํžˆ ์ถ”์ถœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.


40-41: ๋กœ๊ทธ์ธ ํ•„์ˆ˜ ์ฒ˜๋ฆฌ๊ฐ€ ์ ์ ˆํ•˜๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ถ๋งˆํฌ ์•ก์…˜์— ๋Œ€ํ•œ ๋กœ๊ทธ์ธ ๊ฒŒ์ดํŒ…์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. requireLogin ์ƒํƒœ์— ๋”ฐ๋ผ LoginRequired ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋งํ•˜๊ณ , ๋‹ซ๊ธฐ ์‹œ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋กœ์ง์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

src/pages/mypage/events/[id].tsx (2)

13-14: API ํ›… ํ†ตํ•ฉ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

useEventDetail ํ›…์„ ์‚ฌ์šฉํ•œ ์ด๋ฒคํŠธ ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ๊ฐ€ ์ ์ ˆํ•˜๊ฒŒ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ผ์šฐํ„ฐ ์ฟผ๋ฆฌ์—์„œ eventId๋ฅผ ์ถ”์ถœํ•˜๊ณ  ํ›…์— ์ „๋‹ฌํ•˜๋Š” ๋กœ์ง์ด ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค.


25-25: ์ด๋ฒคํŠธ ์ƒ์„ธ ๋ฐ์ดํ„ฐ ๋งคํ•‘์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

API ์‘๋‹ต์—์„œ ํ•„์š”ํ•œ ํ•„๋“œ๋“ค์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ถ”์ถœํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ปดํฌ๋„ŒํŠธ ์ „๋ฐ˜์— ๊ฑธ์ณ ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

@@ -1,6 +1,7 @@
'use client';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸก Minor

Pages Router์—์„œ๋Š” 'use client' ์ง€์‹œ๋ฌธ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด ํ”„๋กœ์ ํŠธ๋Š” Next.js Pages Router๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ 'use client' ์ง€์‹œ๋ฌธ์„ ์ œ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ง€์‹œ๋ฌธ์€ App Router ์ „์šฉ์ž…๋‹ˆ๋‹ค.

Based on learnings

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์„ธ์š”:

-'use client';
-
 import React, { useEffect } from 'react';
๐Ÿ“ 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
'use client';
import React, { useEffect } from 'react';
๐Ÿค– Prompt for AI Agents
src/pages/mypage/index.tsx around lines 1 to 1: remove the top-level "'use
client';" directive because this file is in the Next.js Pages Router where the
directive is not needed; delete that line so the file does not include the App
Routerโ€“specific client directive.

Comment on lines +103 to +111
eventId={event.eventId}
name={event.title ?? event.eventName ?? ''}
address={event.address ?? ''}
description={event.body ?? ''}
variant='gray'
size='large'
imageSrc={event.eventImageUrl}
imageSrc={event.imageUrl}
liked={true}
onClick={() => router.push(`/mypage/events/${event.eventId}`)}
Copy link
Copy Markdown
Contributor

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

๋ถ๋งˆํฌ๋œ ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ ๋งคํ•‘ ํ™•์ธ

EventCard์— ์ „๋‹ฌ๋˜๋Š” props๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋งคํ•‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ Line 104์˜ event.title ?? event.eventName ?? '' ํด๋ฐฑ ์ฒด์ธ์€ API ์‘๋‹ต์˜ ํ•„๋“œ๋ช…์ด ์ผ๊ด€๋˜์ง€ ์•Š์„ ๊ฐ€๋Šฅ์„ฑ์„ ์‹œ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

API ์‘๋‹ต ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•˜์—ฌ title๊ณผ eventName ํ•„๋“œ๊ฐ€ ๋ชจ๋‘ ํ•„์š”ํ•œ์ง€ ๊ฒ€์ฆํ•˜์„ธ์š”:


๐Ÿ Script executed:

#!/bin/bash
# ๋ถ๋งˆํฌ ์ด๋ฒคํŠธ API ์‘๋‹ต ํƒ€์ž… ์ •์˜ ํ™•์ธ
rg -nP --type=ts "bookmarkedEvents|BookmarkedEvent" -A5 -B2

Length of output: 1728


event.eventName ํ•„๋“œ๊ฐ€ ํƒ€์ž… ์ •์˜์— ์กด์žฌํ•˜์ง€ ์•Š์Œ

src/shared/types/membertypes.ts์˜ BookmarkedEvent ํƒ€์ž… ์ •์˜์—์„œ eventName ํ•„๋“œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Line 104์˜ ํด๋ฐฑ ์ฒด์ธ์—์„œ event.eventName์„ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์€ ๋ถˆํ•„์š”ํ•œ ๋ฐฉ์–ด ์ฝ”๋”ฉ์ž…๋‹ˆ๋‹ค.

ํƒ€์ž… ์ •์˜์— ๋”ฐ๋ฅด๋ฉด title ํ•„๋“œ๋งŒ ์กด์žฌํ•˜๋ฏ€๋กœ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹จ์ˆœํ™”ํ•˜์„ธ์š”:

name={event.title ?? ''}
๐Ÿค– Prompt for AI Agents
In src/pages/mypage/index.tsx around lines 103 to 111, the fallback chain
references event.eventName which is not defined in the BookmarkedEvent type;
replace the name prop expression with only the typed field (name={event.title ??
''}) and remove the unnecessary event.eventName fallback to match the type
definition.

Copy link
Copy Markdown
Contributor

@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: 2

โ™ป๏ธ Duplicate comments (3)
src/pages/events/[id].tsx (3)

1-12: Pages Router์—์„œ๋Š” 'use client'๊ฐ€ ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŒŒ์ผ์€ pages ๋ผ์šฐํ„ฐ์šฉ ํŽ˜์ด์ง€๋ผ์„œ Line 1์˜ 'use client'; ์ง€์‹œ์ž๋Š” ์˜๋ฏธ๊ฐ€ ์—†๊ณ  ํ˜ผ๋™๋งŒ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ์ œ๊ฑฐํ•ด๋„ ๋™์ž‘์—๋Š” ์˜ํ–ฅ์ด ์—†์Šต๋‹ˆ๋‹ค.

-'use client';
-
 import { useEffect } from 'react';

53-57: startDate!, endDate! non-null ๋‹จ์–ธ์€ ์ œ๊ฑฐํ•˜๋Š” ํŽธ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

eventDetail์˜ startDate, endDate๊ฐ€ ์‹ค์ œ๋กœ optional์ผ ์ˆ˜ ์žˆ๋Š”๋ฐ, non-null ๋‹จ์–ธ(!)์„ ์“ฐ๋ฉด ํƒ€์ž… ์ฒดํฌ์—์„œ ๋ˆ„๋ฝ๋œ ์ผ€์ด์Šค๋ฅผ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DateTag๋Š” ์ด๋ฏธ undefined๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋‘˜ ๋‹ค ์—†์œผ๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ(๊ด€๋ จ ํŒŒ์ผ ์ฐธ๊ณ ), ๋‹จ์–ธ ์—†์ด ๊ทธ๋Œ€๋กœ ๋„˜๊ฒจ๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ฆฌ๋ทฐ์—์„œ ์ง€์ ๋œ ๋‚ด์šฉ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

-          <DateTag startDate={startDate!} endDate={endDate!} />
+          <DateTag startDate={startDate} endDate={endDate} />

109-137: ๊ด€๋ จ ํ–‰์‚ฌ ์นด๋“œ: ๋ถ๋งˆํฌ/ํ‚ค/ํด๋ฆญ ์ฒ˜๋ฆฌ ๊ฐœ์„  ์—ฌ์ง€

๊ด€๋ จ ํ–‰์‚ฌ ๋ Œ๋”๋ง ์ „๋ฐ˜์€ ์ž˜ ๋™์ž‘ํ•˜๊ฒ ์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ๋ณด์™„ ํฌ์ธํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๋ถ๋งˆํฌ ์ƒํƒœ ํ•˜๋“œ์ฝ”๋”ฉ

    • ํ˜„์žฌ ๊ด€๋ จ ํ–‰์‚ฌ ์นด๋“œ์—์„œ liked={false}๋กœ ๊ณ ์ •๋˜์–ด ์žˆ์–ด ์‹ค์ œ ๋ถ๋งˆํฌ ์ƒํƒœ๊ฐ€ UI์— ๋ฐ˜์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • PR ์„ค๋ช…์—๋„ โ€œ๋ถ๋งˆํฌ UI ๋™๊ธฐํ™” ํ™•์ธ: ๋ฏธ์™„๋ฃŒโ€๊ฐ€ ์žˆ์–ด์„œ, ์ถ”ํ›„์—๋Š” ๊ด€๋ จ ํ–‰์‚ฌ ํƒ€์ž…/์‘๋‹ต์— ๋ถ๋งˆํฌ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์—ฌ๊ธฐ์„œ๋„ liked={item.isBookmarked ?? false} ํ˜•ํƒœ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ํ›„์† ์ž‘์—…์ด ํ•„์š”ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค. (๊ธฐ์กด ๋ฆฌ๋ทฐ์—์„œ ์–ธ๊ธ‰๋œ ๋‚ด์šฉ๊ณผ ๊ฐ™์€ ๋ฐฉํ–ฅ์ž…๋‹ˆ๋‹ค.)
  2. placeholder ์นด๋“œ์˜ onClick

    • ๋นˆ ์Šฌ๋กฏ์šฉ ์นด๋“œ์— onClick={() => null}์„ ๋„˜๊ธฐ๊ณ  ์žˆ์–ด, ๊ฒ‰์œผ๋กœ๋Š” ํด๋ฆญ ๊ฐ€๋Šฅํ•ด ๋ณด์ด๋Š”๋ฐ ์•„๋ฌด ๋™์ž‘์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ์•„์˜ˆ onClick์„ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ํด๋ฆญ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ์Šคํƒ€์ผ์„ ์ฃผ๋Š” ํŽธ์ด ๋” ๋ช…ํ™•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. index ๊ธฐ๋ฐ˜ key

    • ํ˜„์žฌ key={idx}๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ๋ฆฌ์ŠคํŠธ ํฌ๊ธฐ๊ฐ€ ๊ณ ์ •(2๊ฐœ)๋ผ ์‹ค์ œ ๋ฌธ์ œ๋Š” ํฌ์ง€ ์•Š์ง€๋งŒ, ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด isEmpty ์—ฌ๋ถ€์™€ eventId๋ฅผ ์กฐํ•ฉํ•œ ํ‚ค๋ฅผ ์“ฐ๋ฉด ๋ฆฌ๋ Œ๋”๋ง ์•ˆ์ •์„ฑ์ด ์กฐ๊ธˆ ๋” ์ข‹์•„์ง‘๋‹ˆ๋‹ค.

์˜ˆ์‹œ:

-            {nextList.map((item: RelatedEventOrEmpty, idx) => (
-              <div key={idx} className={cn('w-[17rem]')}>
+            {nextList.map((item: RelatedEventOrEmpty, idx) => (
+              <div
+                key={isEmptyItem(item) ? `empty-${idx}` : item.eventId}
+                className={cn('w-[17rem]')}
+              >
...
-                    liked={false}
-                    onClick={() => null}
+                    liked={false}
๐Ÿงน Nitpick comments (4)
src/pages/events/[id].tsx (2)

16-31: ์ž˜๋ชป๋œ id ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ ๋ณด์™„์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

ํ˜„์žฌ ๋กœ์ง์—์„œ๋Š”:

  • const eventId = Number(id);
  • if (!eventId) return null;

๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์–ด, /events/abc์ฒ˜๋Ÿผ ์ˆซ์ž๊ฐ€ ์•„๋‹Œ id๋กœ ์ ‘๊ทผํ•˜๋ฉด ํ™”๋ฉด์ด ๊ทธ๋ƒฅ ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ(null ๋ Œ๋”๋ง)๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์„ฑ์„ ์œ„ํ•ด ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋„ /events๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•˜๊ฑฐ๋‚˜ ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๋…ธ์ถœํ•˜๋Š” ์ชฝ์ด ๋” ๋‚ซ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด:

-  const eventId = Number(id);
+  const eventId = Number(id);
+
+  if (!router.isReady) return null;
+  if (!Number.isFinite(eventId) || eventId <= 0) {
+    router.replace('/events');
+    return null;
+  }
-
-  if (!eventId) return null;
-  if (isError || !eventDetail) return null;
-  if (!router.isReady) return null;
+  if (isError || !eventDetail) return null;

์ฒ˜๋Ÿผ router.isReady์™€ eventId ์œ ํšจ์„ฑ์„ ๋จผ์ € ๊ฒ€์‚ฌํ•˜๋Š” ํŒจํ„ด์„ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


39-43: ํ—ค๋” ํƒ€์ดํ‹€์„ ์‹ค์ œ ํ–‰์‚ฌ๋ช…์œผ๋กœ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

ํ˜„์žฌ ํ—ค๋” title์ด ํ•˜๋“œ์ฝ”๋”ฉ๋œ 'ํ–‰์‚ฌ๋ช…'์ด๋ผ, ์ƒ์„ธ์—์„œ ์‹ค์ œ ํ–‰์‚ฌ๋ช…์ด ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Line 33์—์„œ title์„ ์ด๋ฏธ ๊ตฌ์กฐ ๋ถ„ํ•ดํ•˜๊ณ  ์žˆ์œผ๋‹ˆ, ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ฐ”๊พธ๋ฉด UX๊ฐ€ ๋” ์ž์—ฐ์Šค๋Ÿฌ์šธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

-      <Header
-        title='ํ–‰์‚ฌ๋ช…'
+      <Header
+        title={title}
         onClick={() => router.push(`/events${date ? `?date=${date}` : ''}`)}
src/pages/events/index.tsx (2)

1-14: Pages Router์—์„œ๋Š” use client ์ง€์‹œ์–ด๊ฐ€ ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŒŒ์ผ์€ src/pages ํ•˜์œ„์˜ Pages Router ํŽ˜์ด์ง€๋ผ ๊ธฐ๋ณธ์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ, ์ตœ์ƒ๋‹จ์˜ 'use client';๋Š” ์ œ๊ฑฐํ•ด๋„ ๋™์ž‘์— ๋ณ€ํ™”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Pages Router ์ฝ”๋“œ์—๋งŒ ์ด ์ง€์‹œ์–ด๊ฐ€ ์„ž์—ฌ ์žˆ์œผ๋ฉด ๋‚˜์ค‘์— App Router ๋„์ž… ์‹œ ํ˜ผ๋™์„ ์ค„ ์ˆ˜ ์žˆ์–ด ๊ฐ€๊ธ‰์  ์ •๋ฆฌํ•˜๋Š” ํŽธ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. useRouter๋ฅผ next/router์—์„œ importํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ useEvents/EventData import ๊ตฌ์„ฑ์€ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ์ปจ๋ฒค์…˜๊ณผ ์ž˜ ๋งž์Šต๋‹ˆ๋‹ค.

Based on learnings


67-69: router.push์—์„œ selectedDateString์ด ๋น„์–ด ์žˆ์„ ๋•Œ๋ฅผ ๋ฐฉ์–ดํ•ด ๋‘๋ฉด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ตฌํ˜„์—์„œ๋Š” ํ•ญ์ƒ /events/${event.id}?date=${selectedDateString} ํ˜•ํƒœ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ ๊ตฌ์กฐ์ƒ ์นด๋“œ๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ์‹œ์ ์—๋Š” date๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์–ด ์‹ค์ œ๋กœ date=๋งŒ ๋น„์–ด ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†๊ฒ ์ง€๋งŒ, ์ถ”ํ›„ ๊ธฐ๋ณธ ์ด๋ฒคํŠธ ๋ชฉ๋ก์„ ๋กœ๋“œํ•˜๋„๋ก ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ ๋นˆ ์ฟผ๋ฆฌ(?date=)๊ฐ€ ๋‚ด๋ ค๊ฐ€๋ฉด์„œ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ date ํŒŒ์‹ฑ ์‹œ edge case๊ฐ€ ์ƒ๊ธธ ์—ฌ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํžˆ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ ์•ˆ์—์„œ ๋‚ ์งœ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์ฟผ๋ฆฌ๋ฅผ ๋ถ™์ด๋Š” ์‹์œผ๋กœ ๋ฐฉ์–ดํ•ด ๋‘๋Š” ๊ฑธ ์ œ์•ˆ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

-                onClick={() =>
-                  router.push(`/events/${event.id}?date=${selectedDateString}`)
-                }
+                onClick={() => {
+                  if (selectedDateString) {
+                    router.push(
+                      `/events/${event.id}?date=${selectedDateString}`,
+                    );
+                  } else {
+                    router.push(`/events/${event.id}`);
+                  }
+                }}

ํ•„์ˆ˜ ์ˆ˜์ •์€ ์•„๋‹ˆ์ง€๋งŒ, ์ดํ›„ ์š”๊ตฌ์‚ฌํ•ญ ๋ณ€๊ฒฝ์— ์กฐ๊ธˆ ๋” ๊ฒฌ๊ณ ํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 7a954e9 and 27e232b.

๐Ÿ“’ Files selected for processing (2)
  • src/pages/events/[id].tsx (1 hunks)
  • src/pages/events/index.tsx (3 hunks)
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.

Applied to files:

  • src/pages/events/index.tsx
  • src/pages/events/[id].tsx
๐Ÿงฌ Code graph analysis (2)
src/pages/events/index.tsx (4)
src/shared/hooks/events/useEvents.ts (1)
  • useEvents (7-21)
src/shared/components/datePicker/DatePicker.tsx (1)
  • DatePicker (17-97)
src/shared/types/eventtypes.ts (1)
  • EventData (12-21)
src/shared/components/container/EventCard.tsx (1)
  • EventCardProps (18-164)
src/pages/events/[id].tsx (5)
src/shared/types/eventtypes.ts (1)
  • RelatedEventOrEmpty (42-42)
src/shared/hooks/events/useEventDetail.ts (1)
  • useEventDetail (15-21)
src/shared/utils/buildNextEventList.ts (1)
  • buildNextEventList (5-17)
src/pages/events/components/DateTag.tsx (1)
  • DateTag (24-44)
src/shared/components/container/EventCard.tsx (3)
  • EventCardProps (18-164)
  • EventCardProps (7-16)
  • prev (29-29)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
๐Ÿ”‡ Additional comments (5)
src/pages/events/[id].tsx (4)

13-14: isEmptyItem ํƒ€์ž… ๊ฐ€๋“œ ๊ตฌํ˜„ ์ข‹์Šต๋‹ˆ๋‹ค.

RelatedEventOrEmpty์—์„œ { isEmpty: true } ์ผ€์ด์Šค๋ฅผ 'isEmpty' in item์œผ๋กœ ์ขํžˆ๋Š” ๋ฐฉ์‹์ด ๋ช…ํ™•ํ•˜๊ณ , ์ดํ›„ JSX ๋ถ„๊ธฐ์—์„œ๋„ ์ž˜ ํ™œ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ„๋‹ค๋ฅธ ์ˆ˜์ • ์˜๊ฒฌ ์—†์Šต๋‹ˆ๋‹ค.


33-37: eventDetail ๊ตฌ์กฐ ๋ถ„ํ•ด์™€ buildNextEventList ์‚ฌ์šฉ์€ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์ž˜ ์ •๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ƒ์„ธ ์ •๋ณด์—์„œ ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ๊ตฌ์กฐ ๋ถ„ํ•ดํ•œ ๋’ค, buildNextEventList๋กœ ๊ด€๋ จ ํ–‰์‚ฌ ๋ฆฌ์ŠคํŠธ๋ฅผ 2๊ฐœ ๊ณ ์ •์œผ๋กœ ๋งž์ถ”๋Š” ๊ตฌ์กฐ๊ฐ€ ๋ช…ํ™•ํ•˜๊ณ  ์žฌ์‚ฌ์šฉ์„ฑ๋„ ์ข‹์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•ด๋„ ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.


61-82: ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ ์„น์…˜์˜ ์ ‘๊ทผ์„ฑ ์ฒ˜๋ฆฌ ์ข‹์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ์ง€๊ฐ€ ์žˆ์„ ๋•Œ๋Š” alt={${title} ์ด๋ฏธ์ง€}๋กœ, ์—†์„ ๋•Œ๋Š” role="img"์™€ aria-label๋กœ ๋Œ€์ฒด ํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํŒจํ„ด์ด ๋ช…ํ™•ํ•˜๊ณ  ์ ‘๊ทผ์„ฑ ์ธก๋ฉด์—์„œ๋„ ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค. Image ์ปดํฌ๋„ŒํŠธ์™€ fallback div ๋†’์ด/๋น„์œจ๋„ ์ผ๊ด€๋˜์–ด UX๊ฐ€ ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค.


92-101: ๋ฉ”์ธ ํ–‰์‚ฌ ์นด๋“œ์— ๋ถ๋งˆํฌ ์ƒํƒœ๋ฅผ ์—ฐ๊ฒฐํ•œ ๋ถ€๋ถ„ ์ข‹์Šต๋‹ˆ๋‹ค.

eventId, name, description์— ๋”ํ•ด liked={eventDetail.isBookmarked ?? false}๋กœ ์ƒ์„ธ์˜ ๋ถ๋งˆํฌ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๊ณ  ์žˆ์–ด, ๋ถ๋งˆํฌ UI์™€ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ ์‹ฑํฌ๋  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ์š”๊ตฌ์‚ฌํ•ญ์— ์ž˜ ๋งž๋Š” ๊ตฌํ˜„ ๊ฐ™์Šต๋‹ˆ๋‹ค.

src/pages/events/index.tsx (1)

38-38: ์ƒ๋‹จ ControlBar๋ฅผ fixed๋กœ ์˜ฌ๋ฆฐ ๋ณ€๊ฒฝ์€ UX ์ธก๋ฉด์—์„œ ์ข‹์•„ ๋ณด์ž…๋‹ˆ๋‹ค.

์Šคํฌ๋กค ์‹œ์—๋„ ์ปจํŠธ๋กค ๋ฐ”๊ฐ€ ํ•ญ์ƒ ๋ณด์ด๋„๋ก ๋˜์–ด ์ด๋ฒคํŠธ ๋‚ ์งœ/ํ•„ํ„ฐ ์กฐ์ž‘์ด ์‰ฌ์›Œ์งˆ ๊ฒƒ ๊ฐ™๊ณ , z-index์™€ padding ์„ค์ •๋„ ๋‹ค๋ฅธ ์š”์†Œ๋“ค๊ณผ ์ถฉ๋Œ ์—†์–ด ๋ณด์ž…๋‹ˆ๋‹ค.

Copy link
Copy Markdown
Contributor

@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: 1

โ™ป๏ธ Duplicate comments (4)
src/pages/events/index.tsx (4)

57-102: ๋กœ๋”ฉ/์—๋Ÿฌ/๋‚ ์งœ ๋ฏธ์„ ํƒ ์ƒํƒœ๊ฐ€ ๋นˆ ๋ฐ์ดํ„ฐ์™€ ๊ตฌ๋ถ„๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด์ „ ๋ฆฌ๋ทฐ ์ฝ”๋ฉ˜ํŠธ์—์„œ ์ง€์ ๋œ ๋‚ด์šฉ์ด ์•„์ง ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ filteredEvents.length > 0 ์กฐ๊ฑด๋งŒ์œผ๋กœ ๋ถ„๊ธฐํ•˜๊ณ  ์žˆ์–ด์„œ ๋‹ค์Œ ์ƒํ™ฉ๋“ค์ด ๋ชจ๋‘ ๋™์ผํ•˜๊ฒŒ "์„ ํƒํ•˜์‹  ๋‚ ์€ ํ–‰์‚ฌ๊ฐ€ ์—†์–ด์š”!" ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:

  • API ํ˜ธ์ถœ ์ค‘(๋กœ๋”ฉ)
  • API ํ˜ธ์ถœ ์‹คํŒจ(์—๋Ÿฌ)
  • ๋‚ ์งœ๋ฅผ ์•„์ง ์„ ํƒํ•˜์ง€ ์•Š์Œ
  • ์‹ค์ œ๋กœ ํ•ด๋‹น ๋‚ ์งœ์— ํ–‰์‚ฌ๊ฐ€ ์—†์Œ

20๋ฒˆ ๋ผ์ธ์—์„œ isLoading, isError๋ฅผ ํ•จ๊ป˜ ๊ตฌ์กฐ ๋ถ„ํ•ดํ•˜๊ณ , ๋ Œ๋”๋ง ๋กœ์ง์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ์„ ํ•ด ์ฃผ์„ธ์š”:

         {/* ํ–‰์‚ฌ์นด๋“œ & ๋นˆํ™”๋ฉด */}
-        {filteredEvents.length > 0 ? (
+        {isLoading ? (
+          <div className='flex items-center justify-center mt-[15rem]'>
+            <p className='text-label-lg text-gray-500'>ํ–‰์‚ฌ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค...</p>
+          </div>
+        ) : isError ? (
+          <div className='flex flex-col items-center justify-center text-center mt-[15rem]'>
+            <p className='text-label-lg text-gray-700'>
+              ํ–‰์‚ฌ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ–ˆ์–ด์š”.
+              <br />
+              ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.
+            </p>
+          </div>
+        ) : date && filteredEvents.length > 0 ? (
           <section
             aria-label='์ด๋ฒคํŠธ ๋ชฉ๋ก'
             className={cn(
@@ -83,7 +95,7 @@
               ))}
             </section>
-        ) : (
+        ) : date ? (
           <div
             className='flex flex-col items-center justify-center text-center mt-[15rem]'
             role='status'
@@ -99,6 +111,7 @@
               ํ–‰์‚ฌ๊ฐ€ ์—†์–ด์š”!
             </p>
           </div>
+        ) : null}
         )}

๋˜ํ•œ 19๋ฒˆ ๋ผ์ธ์˜ date ์ดˆ๊ธฐ๊ฐ’์„ new Date()๋กœ ์„ค์ •ํ•˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ฒ˜์Œ ์ง„์ž…ํ•  ๋•Œ ์˜ค๋Š˜ ๋‚ ์งœ์˜ ํ–‰์‚ฌ๋ฅผ ๋ฐ”๋กœ ๋ณผ ์ˆ˜ ์žˆ์–ด UX๊ฐ€ ๋” ์ข‹์•„์ง‘๋‹ˆ๋‹ค.


19-21: ๋กœ๋”ฉ/์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ „ ๋ฆฌ๋ทฐ ์ฝ”๋ฉ˜ํŠธ์—์„œ ์ง€์ ๋œ ๋‚ด์šฉ์ด ์•„์ง ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. useEvents ํ›…์€ isLoading๊ณผ isError๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ํ˜„์žฌ๋Š” events๋งŒ ๊ตฌ์กฐ ๋ถ„ํ•ดํ•˜๊ณ  ์žˆ์–ด์„œ ๋กœ๋”ฉ ์ค‘์ด๊ฑฐ๋‚˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋„ ๋™์ผํ•˜๊ฒŒ ๋นˆ ์ƒํƒœ UI๊ฐ€ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”:

-  const { events } = useEvents(date);
+  const { events, isLoading, isError } = useEvents(date);

๊ทธ๋ฆฌ๊ณ  ๋ Œ๋”๋ง ๋กœ์ง์—์„œ ์ด ์ƒํƒœ๋“ค์„ ํ™œ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์•„๋ž˜ ๋ณ„๋„ ์ฝ”๋ฉ˜ํŠธ ์ฐธ์กฐ).


46-53: label์˜ htmlFor๊ฐ€ ์‹ค์ œ DOM ์š”์†Œ์™€ ๋งค์นญ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด์ „ ๋ฆฌ๋ทฐ ์ฝ”๋ฉ˜ํŠธ์—์„œ ์ง€์ ๋œ ๋‚ด์šฉ์ด ์•„์ง ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. htmlFor='event-date'๋กœ ์ง€์ •๋˜์–ด ์žˆ์ง€๋งŒ, DatePicker๋Š” id prop์„ ๋ฐ›์ง€ ์•Š์•„์„œ ์‹ค์ œ DOM์— id="event-date"์ธ ์š”์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ด ์ฃผ์„ธ์š”:

  1. ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•: label์„ ์ œ๊ฑฐํ•˜๊ณ  ariaLabel๋งŒ ์œ ์ง€
-          <label htmlFor='event-date' className='sr-only'>
-            ํ–‰์‚ฌ ๋‚ ์งœ ์„ ํƒ
-          </label>
           <DatePicker
             ariaLabel='ํ–‰์‚ฌ ๋‚ ์งœ ์„ ํƒ'
  1. ์™„์ „ํ•œ ๋ฐฉ๋ฒ•: DatePicker ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™•์žฅํ•ด id prop์„ ๋ฐ›๋„๋ก ์ˆ˜์ •ํ•˜๊ณ , ์—ฌ๊ธฐ์„œ id='event-date'๋ฅผ ์ „๋‹ฌ

24-28: ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ๋ถ€ํ„ฐ ํŒŒ์‹ฑํ•œ ๋‚ ์งœ์˜ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์ด ์—†์Šต๋‹ˆ๋‹ค.

dateQuery๋ฅผ new Date(String(dateQuery))๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ, ์ž˜๋ชป๋œ ๋‚ ์งœ ๋ฌธ์ž์—ด์ด ๋“ค์–ด์˜ค๋ฉด Invalid Date ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ UI๊ฐ€ ์˜๋„์น˜ ์•Š๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ ์งœ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜๋„๋ก ๊ฐœ์„ ํ•ด ์ฃผ์„ธ์š”:

   useEffect(() => {
     if (router.isReady && dateQuery) {
-      setDate(new Date(String(dateQuery)));
+      const parsed = new Date(String(dateQuery));
+      if (!Number.isNaN(parsed.getTime())) {
+        setDate(parsed);
+      }
     }
   }, [router.isReady, dateQuery]);
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 27e232b and dc6070d.

๐Ÿ“’ Files selected for processing (1)
  • src/pages/events/index.tsx (3 hunks)
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.

Applied to files:

  • src/pages/events/index.tsx
๐Ÿงฌ Code graph analysis (1)
src/pages/events/index.tsx (4)
src/shared/hooks/events/useEvents.ts (1)
  • useEvents (7-21)
src/shared/utils/date.ts (1)
  • formatDateToISO (1-6)
src/shared/components/datePicker/DatePicker.tsx (1)
  • DatePicker (17-97)
src/shared/types/eventtypes.ts (1)
  • EventData (12-21)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
๐Ÿ”‡ Additional comments (1)
src/pages/events/index.tsx (1)

39-39: z-50 z-index ๊ฐ’์€ ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

๊ฒ€์ฆ ๊ฒฐ๊ณผ, ํ”„๋กœ์ ํŠธ์˜ z-index ๊ณ„์ธต ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • z-50: ControlBar ๋ฐ ๊ณ ์ • ํ—ค๋” (์˜๋„๋œ ๋ฐฐ์น˜)
  • z-[99]: Overlay ์ปดํฌ๋„ŒํŠธ
  • z-[100]: PopupSet, Header (์ตœ์ƒ์œ„)

ControlBar์˜ z-50์€ ๋ชจ๋‹ฌ๊ณผ ํŒ์—…(z-[99], z-[100])๋ณด๋‹ค ์•„๋ž˜์— ์œ„์น˜ํ•˜๋„๋ก ์˜๋„์ ์œผ๋กœ ์„ค์ •๋˜์—ˆ์œผ๋ฉฐ, ์ด๋Š” ๊ณ ์ • ํ—ค๋”์˜ ์˜ฌ๋ฐ”๋ฅธ ๋™์ž‘์ž…๋‹ˆ๋‹ค. UI ์š”์†Œ ๊ฐ„ ๋ ˆ์ด์–ด ์ถฉ๋Œ์ด ์—†์Šต๋‹ˆ๋‹ค.

@@ -1,6 +1,6 @@
'use client';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

Pages Router์—์„œ๋Š” 'use client' ์ง€์‹œ์–ด๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๋Š” Next.js Pages Router๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, 'use client' ์ง€์‹œ์–ด๋Š” App Router ์ „์šฉ์ž…๋‹ˆ๋‹ค. Pages Router์˜ pages/ ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด ํŒŒ์ผ๋“ค์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ํ•˜์ด๋“œ๋ ˆ์ด์…˜๋˜๋ฏ€๋กœ ์ด ์ง€์‹œ์–ด๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋ฉฐ, ์˜คํžˆ๋ ค ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ œ๊ฑฐํ•ด ์ฃผ์„ธ์š”:

-'use client';
-
 import { useState, useEffect } from 'react';

Based on learnings

๐Ÿ“ 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
'use client';
import { useState, useEffect } from 'react';
๐Ÿค– Prompt for AI Agents
In src/pages/events/index.tsx lines 1-1, remove the top-level 'use client'
directive (delete that first line). If this file relies on client-only hooks or
browser APIs, move those parts into a separate client component under
pages/events or convert the specific components to client components (with 'use
client') inside a subfile; otherwise simply delete the directive so the page
uses the Pages Router default behavior.

Copy link
Copy Markdown
Contributor

@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: 0

โ™ป๏ธ Duplicate comments (1)
src/shared/components/container/EventCard.tsx (1)

105-124: ๋ถ๋งˆํฌ ๋ฒ„ํŠผ a11y ๋ณด์™„ ๋ฐ ์ค‘๋ณต ์ฝ”๋“œ ์ •๋ฆฌ ๊ถŒ์žฅ (์ด์ „ ๋ฆฌ๋ทฐ์™€ ๋™์ผ ์ด์Šˆ)

Line [105][124], [151][170]์˜ ํ•˜ํŠธ ๋ถ๋งˆํฌ ๋ฒ„ํŠผ์€ ์‹œ๊ฐ์ ์œผ๋กœ๋Š” ๋ช…ํ™•ํ•˜์ง€๋งŒ, ํ˜„์žฌ <button>์— aria-label์ด ์—†๊ณ , ๋‚ด๋ถ€ Icon ์ปดํฌ๋„ŒํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ariaHidden์ด๊ธฐ ๋•Œ๋ฌธ์— ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์—์„œ โ€œ์ด ๋ฒ„ํŠผ์ด ๋ฌด์—‡์„ ํ•˜๋Š”์ง€โ€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฆฌ๋ทฐ์—์„œ๋„ ์ง€์ ๋˜์—ˆ๋˜ ๋ถ€๋ถ„์ธ๋ฐ ์•„์ง ๋ฐ˜์˜๋˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

  1. ์ ‘๊ทผ์„ฑ(a11y) ์ธก๋ฉด ํ•„์ˆ˜ ์ˆ˜์ • ์ œ์•ˆ

๊ฐ ๋ฒ„ํŠผ์— ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” aria-label์„ ๋ถ€์—ฌํ•˜๋ฉด ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์—์„œ ์˜๋ฏธ๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— type='button'์„ ๋ช…์‹œํ•ด ์ถ”ํ›„ form ๋‚ด์— ๋“ค์–ด๊ฐˆ ๊ฐ€๋Šฅ์„ฑ๊นŒ์ง€ ๋ฐฉ์–ดํ•˜๋Š” ํŽธ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ diff:

-              <button
-                className='cursor-pointer p-1'
-                onClick={(e) => {
-                  e.stopPropagation();
-                  toggleBookmark();
-                }}
-              >
+              <button
+                type='button'
+                className='cursor-pointer p-1'
+                onClick={(e) => {
+                  e.stopPropagation();
+                  toggleBookmark();
+                }}
+                aria-label={isBookmarked ? '๋ถ๋งˆํฌ ํ•ด์ œ' : '๋ถ๋งˆํฌ ์ถ”๊ฐ€'}
+              >
@@
-                <Icon
+                <Icon
                   name='HeartStraight'
                   size={20}
                   color={
                     isBookmarked
                       ? 'red-400'
                       : variant === 'mint'
                       ? 'mint-400'
                       : 'gray-300'
                   }
                   fillColor={isBookmarked ? 'red-300' : undefined}
                 />
               </button>

Large ์นด๋“œ ์ชฝ ๋ฒ„ํŠผ(Line [151]~[170])์—๋„ ๋™์ผํ•˜๊ฒŒ type='button'๊ณผ aria-label์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. ์ค‘๋ณต๋œ ๋ฒ„ํŠผ ๋งˆํฌ์—…์€ ์†Œ๊ทœ๋ชจ ์„œ๋ธŒ์ปดํฌ๋„ŒํŠธ๋กœ ์ถ”์ถœ ๊ถŒ์žฅ

medium/large ์นด๋“œ์—์„œ ๋ฒ„ํŠผ ๋งˆํฌ์—…๊ณผ ์ƒ‰์ƒ ๋กœ์ง์ด ๊ฑฐ์˜ ๋™์ผํ•˜๊ฒŒ ๋ฐ˜๋ณต๋˜๊ณ  ์žˆ์–ด, ์ถ”ํ›„ ์Šคํƒ€์ผ ๋ณ€๊ฒฝ ์‹œ ์ค‘๋ณต ์ˆ˜์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ, ์˜ˆ๋ฅผ ๋“ค์–ด BookmarkButton ๊ฐ™์€ ์†Œ๊ทœ๋ชจ ์„œ๋ธŒ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฝ‘์•„์„œ

  • isBookmarked, variant, onToggle ์ •๋„๋ฅผ props๋กœ ๋ฐ›๊ณ 
  • ๋‚ด๋ถ€์—์„œ stopPropagation, aria-label ์ฒ˜๋ฆฌ์™€ ์ƒ‰์ƒ/Icon ๋ Œ๋”๋ง์„ ๊ณตํ†ตํ™”

ํ•˜๋Š” ํ˜•ํƒœ๋กœ ์ •๋ฆฌํ•˜๋ฉด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋” ์ˆ˜์›”ํ•ด์งˆ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Also applies to: 151-170

๐Ÿงน Nitpick comments (1)
src/shared/components/container/EventCard.tsx (1)

49-75: small ์นด๋“œ์—์„œ ๋ถ๋งˆํฌ ๋ฏธ๋…ธ์ถœ์ด ๊ธฐํš๊ณผ ๋งž๋Š”์ง€๋งŒ ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค

Line [49]~[75]์˜ small ์นด๋“œ ๋ ˆ์ด์•„์›ƒ์—๋Š” ์ด๋ฏธ์ง€์™€ ์ œ๋ชฉ๋งŒ ์žˆ๊ณ , medium/large ์นด๋“œ์— ์žˆ๋Š” ํ•˜ํŠธ(๋ถ๋งˆํฌ) ๋ฒ„ํŠผ์€ ๋ฐฐ์น˜๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐํš์ƒ small ์นด๋“œ๋Š” ๋ถ๋งˆํฌ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋””์ž์ธ์ด๋ผ๋ฉด ๊ทธ๋Œ€๋กœ ๋‘์–ด๋„ ๋˜์ง€๋งŒ, ์นด๋“œ ์‚ฌ์ด์ฆˆ์— ๊ด€๊ณ„์—†์ด ์ผ๊ด€๋œ ๋ถ๋งˆํฌ ๊ฒฝํ—˜์„ ์›ํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด๋ผ๋ฉด ์ด ๋ถ€๋ถ„๋„ ์ถ”ํ›„ ํ™•์žฅ ๋Œ€์ƒ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ PR ๋ชฉ์ (์ด๋ฒคํŠธ ํŽ˜์ด์ง€ API ์—ฐ๋™) ๊ด€์ ์—์„œ๋Š” blocker๋Š” ์•„๋‹ˆ์ง€๋งŒ, ๊ธฐํš/๋””์ž์ธ๊ณผ ์˜๋„๋œ ๋™์ž‘์ด ๋งž๋Š”์ง€๋งŒ ํ•œ ๋ฒˆ ํ™•์ธํ•ด ๋ณด์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between fcbaee2 and cdc4f5c.

๐Ÿ“’ Files selected for processing (1)
  • src/shared/components/container/EventCard.tsx (3 hunks)
๐Ÿงฐ Additional context used
๐Ÿงฌ Code graph analysis (1)
src/shared/components/container/EventCard.tsx (4)
src/shared/hooks/events/useBookmark.ts (1)
  • useBookmark (8-47)
src/pages/mypage/components/LoginRequired.tsx (1)
  • LoginRequired (10-21)
src/shared/components/container/Card.tsx (1)
  • Card (32-48)
src/shared/icons/components/icon.tsx (1)
  • Icon (58-130)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
๐Ÿ”‡ Additional comments (3)
src/shared/components/container/EventCard.tsx (3)

9-35: EventCard props ํ™•์žฅ ๋ฐ useBookmark ์—ฐ๋™ ๊ตฌ์กฐ๋Š” ๋ฌด๋‚œํ•ฉ๋‹ˆ๋‹ค

Line [9]~[35]์—์„œ eventId, liked, onClick๋ฅผ props๋กœ ํ™•์žฅํ•˜๊ณ , useBookmark(eventId, liked)๋กœ ๋ถ๋งˆํฌ ์ƒํƒœ๋ฅผ ์œ„์ž„ํ•œ ๊ตฌ์กฐ๊ฐ€ ์ „์ฒด ์ด๋ฒคํŠธ/์ƒ์„ธ/๋งˆ์ดํŽ˜์ด์ง€ ์บ์‹œ ์ „๋žต๊ณผ ์ž˜ ๋งž์Šต๋‹ˆ๋‹ค. liked ๊ธฐ๋ณธ๊ฐ’์„ false๋กœ ๋‘” ๊ฒƒ๋„ API ์‘๋‹ต์ด ์ง€์—ฐ๋˜๊ฑฐ๋‚˜ ๋ˆ„๋ฝ๋  ๋•Œ์˜ ์ดˆ๊ธฐ ๋ Œ๋” ์•ˆ์ •์„ฑ ์ธก๋ฉด์—์„œ ๊ดœ์ฐฎ์•„ ๋ณด์ž…๋‹ˆ๋‹ค.

๋ณ„๋„ ์ด์Šˆ๋Š” ์—†์–ด ๋ณด์ž…๋‹ˆ๋‹ค.


38-48: ๋กœ๊ทธ์ธ ์š”๊ตฌ ํ”Œ๋กœ์šฐ์™€ Card ๋ž˜ํ•‘ ๋ฐฉ์‹ ์ ์ ˆ

Line [38]~[48]์—์„œ requireLogin์ด true์ผ ๋•Œ LoginRequired๋ฅผ ๋ Œ๋”ํ•˜๊ณ , onClose={() => setRequireLogin(false)}๋กœ ์ƒํƒœ๋ฅผ ์ •๋ฆฌํ•œ ๋’ค Card๋ฅผ ๊ฐ์‹ธ๋Š” ๊ตฌ์กฐ๋Š” ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค.

Card์— onClick์„ ๊ทธ๋Œ€๋กœ ๋„˜๊ธฐ๋˜, ๋ถ๋งˆํฌ ์•„์ด์ฝ˜ ์ชฝ์—์„œ stopPropagation์„ ์“ฐ๊ณ  ์žˆ์–ด์„œ ์นด๋“œ ์ „์ฒด ํด๋ฆญ ์•ก์…˜๊ณผ ๋ถ๋งˆํฌ ํ† ๊ธ€์ด ์ž˜ ๋ถ„๋ฆฌ๋  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.


41-47: Card์˜ size/customHeight ์กฐํ•ฉ์—์„œ small ์ฒ˜๋ฆฌ๋งŒ ํ•œ ๋ฒˆ ํ™•์ธ ํ•„์š”

Line [41]~[47]์—์„œ size === 'small' ? undefined : size๋กœ Card์— size๋ฅผ ๋„˜๊ธฐ์ง€ ์•Š๊ณ , ๋Œ€์‹  customHeight๋กœ๋งŒ ๋†’์ด๋ฅผ ์ œ์–ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Card ๋‚ด๋ถ€์—์„œ size๊ฐ€ undefined์ผ ๋•Œ ์–ด๋–ค ์Šคํƒ€์ผ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š”์ง€์— ๋”ฐ๋ผ small ์นด๋“œ๊ฐ€ medium ์Šคํƒ€์ผ์„ ์ผ๋ถ€ ๋ฌผ๋ ค๋ฐ›์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ, ๋””์ž์ธ ์˜๋„์™€ ์‹ค์ œ ์นด๋“œ ์Šคํƒ€์ผ์ด ์ผ์น˜ํ•˜๋Š”์ง€๋งŒ ํ•œ ๋ฒˆ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

๊ธฐ๋Šฅ์ ์ธ ๋ฌธ์ œ๋Š” ์—†์–ด ๋ณด์ด๊ณ , ํ•„์š”ํ•˜๋‹ค๋ฉด Card ์ชฝ cardStyle ์„ค์ •๋งŒ ์žฌ๊ฒ€ํ† ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Copy link
Copy Markdown
Contributor

@skyblue1232 skyblue1232 left a comment

Choose a reason for hiding this comment

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

ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜ ์ˆ˜๊ณ ํ•˜์…จ์Šต๋‹ˆ๋‹ค!

@KongMezu KongMezu merged commit 239afb3 into develop Nov 15, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api api ์—ฐ๊ฒฐ comment ํ•„์š”ํ•œ ์ฃผ์„ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[api&feat] ํ–‰์‚ฌํŽ˜์ด์ง€ api ์—ฐ๊ฒฐ

2 participants