Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🏷️ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
Walkthrough메인 보드 기능을 구현하는 변경입니다. 4x8 그리드 기반의 Board 페이지와 Boardgame 컴포넌트를 신규 추가했습니다. boardData 상수 모듈을 정의하고, 기존 스탬프 크기를 조정하며, 메인 페이지의 레이아웃, 라우팅, BottomNav를 통합했습니다. Changes
Sequence DiagramsequenceDiagram
actor User
participant Main as Main Page
participant Board as Board Page
participant Boardgame as Boardgame
participant boardData as boardData
User->>Main: 클릭 (보드 항목)
Main->>Board: router.push('/main/Board')
Board->>Board: Header 렌더링
Board->>Boardgame: 렌더링
Boardgame->>boardData: boardData 조회
boardData-->>Boardgame: 그리드 데이터 반환
Boardgame->>Boardgame: 4x8 그리드 렌더링
User->>Boardgame: 셀 클릭 (active=true)
Boardgame->>Boardgame: 셀 라벨 로그 출력
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 분
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🏷️ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
src/pages/main/Board.tsx (1)
8-8: 고정 높이 대신 min-height 사용을 고려하세요.
h-[100vh]와overflow-auto를 함께 사용하면 컨텐츠가 뷰포트를 초과할 때만 스크롤됩니다. 하지만 모바일 브라우저의 주소창 크기 변화나 다양한 뷰포트 높이를 고려하면min-h-screen사용이 더 적절할 수 있습니다.- <div className='relative w-full h-[100vh] bg-[#46d1cd] overflow-auto'> + <div className='relative w-full min-h-screen bg-[#46d1cd] overflow-auto'>src/shared/constants/main/boardData.ts (1)
4-6: 국제화(i18n)를 고려하세요.레이블이 한국어로 하드코딩되어 있습니다. 향후 다국어 지원이 필요할 경우를 대비하여 i18n 키를 사용하거나 별도의 로케일 파일로 분리하는 것을 고려하세요.
Also applies to: 13-13, 17-19, 23-23, 30-30, 37-37
src/pages/main/components/board/Boardgame.tsx (1)
7-7: 중복된 공백을 제거하세요.클래스 이름에 불필요한 이중 공백이 있습니다.
- <div className='relative w-full h-full bg-[#46d1cd] overflow-hidden'> + <div className='relative w-full h-full bg-[#46d1cd] overflow-hidden'>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
public/assets/background_.svgis excluded by!**/*.svgpublic/assets/bannerMain.svgis excluded by!**/*.svg
📒 Files selected for processing (6)
src/pages/main/Board.tsx(1 hunks)src/pages/main/components/board/Boardgame.tsx(1 hunks)src/pages/main/components/stampBoard/Stamp.tsx(1 hunks)src/pages/main/index.tsx(3 hunks)src/shared/components/tab/BottomNav.tsx(2 hunks)src/shared/constants/main/boardData.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/pages/main/components/stampBoard/Stamp.tsx (1)
src/shared/lib/utils.ts (1)
cn(71-73)
src/pages/main/index.tsx (2)
src/shared/lib/utils.ts (1)
cn(71-73)src/shared/components/tab/BottomNav.tsx (1)
BottomNav(21-54)
🔇 Additional comments (3)
src/pages/main/components/stampBoard/Stamp.tsx (1)
15-15: LGTM!스탬프 크기를 일관되게 조정했습니다. 획득/미획득 상태 모두
2.8rem으로 통일되어 UI 일관성이 유지됩니다.Also applies to: 20-20, 27-30
src/pages/main/index.tsx (1)
47-47: StampBoard total 값 변경을 확인하세요.
total속성이 8에서 10으로 증가했습니다. 이 변경이 의도된 것인지, 그리고 실제 스탬프 획득 로직과 일치하는지 확인해주세요.src/shared/components/tab/BottomNav.tsx (1)
26-27: 하단 네비게이션의 위치 변경이 적절하게 조율되었습니다.검증 결과, BottomNav는 메인 페이지(
src/pages/main/index.tsx)에서만 사용되고 있습니다. 따라서 다른 페이지에서의 콘텐츠 겹침 우려는 해당되지 않습니다.현재 상태:
- BottomNav 위치:
bottom-[5.3rem](화면 하단에서 5.3rem 위)- 컨테이너 하단 패딩:
pb-[12rem](충분한 여유 공간 제공)- 좌측 정렬:
left-[2.4rem]이 컨테이너의px-[2.4rem]과 일치위치 변경(2rem → 5.3rem)이 의도적으로 메인 페이지의 padding과 올바르게 조율되어 있습니다.
| import { Header } from '@/shared/components'; | ||
|
|
||
| import Boardgame from '@/pages/main/components/board/Boardgame'; | ||
| import router from 'next/router'; |
There was a problem hiding this comment.
라우터 import 방식이 잘못되었습니다.
Next.js에서 라우터를 사용하려면 useRouter 훅을 import해야 합니다. 현재 코드는 default export로 router를 import하려고 하는데, next/router는 default export를 제공하지 않습니다.
다음과 같이 수정하세요:
-import router from 'next/router';
+import { useRouter } from 'next/router';그리고 컴포넌트 내부에서 훅을 사용하세요:
const Board = () => {
+ const router = useRouter();
+
return (Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/pages/main/Board.tsx around line 4, the code imports a non-existent
default export "router" from 'next/router'; replace that import with the
useRouter hook (import { useRouter } from 'next/router') and then call
useRouter() inside the component to get the router instance (replace any direct
references to the old "router" variable with the result of useRouter()). Ensure
you remove the default import and update any router usage (push, pathname,
query, etc.) to use the hook-returned object.
| if (!cell.active) { | ||
| return <div key={key} className='bg-transparent' />; | ||
| } | ||
|
|
||
| return ( | ||
| <div key={key} onClick={() => console.log(cell.label)}></div> | ||
| ); |
There was a problem hiding this comment.
활성 셀의 접근성을 개선하세요.
활성 셀이 클릭 가능하지만 시각적 피드백, 키보드 접근성, ARIA 레이블이 없습니다. 사용자에게 인터랙티브 요소임을 알리고 접근성을 향상시켜야 합니다.
다음과 같이 개선하세요:
return (
<div
key={key}
- onClick={() => console.log(cell.label)}
- ></div>
+ role="button"
+ tabIndex={0}
+ aria-label={cell.label || '보드 셀'}
+ onClick={() => {
+ // TODO: 클릭 핸들러 구현
+ }}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ // TODO: 키보드 핸들러 구현
+ }
+ }}
+ className="cursor-pointer hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-white"
+ />
);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!cell.active) { | |
| return <div key={key} className='bg-transparent' />; | |
| } | |
| return ( | |
| <div key={key} onClick={() => console.log(cell.label)}></div> | |
| ); | |
| if (!cell.active) { | |
| return <div key={key} className='bg-transparent' />; | |
| } | |
| return ( | |
| <div | |
| key={key} | |
| role="button" | |
| tabIndex={0} | |
| aria-label={cell.label || '보드 셀'} | |
| onClick={() => { | |
| // TODO: 클릭 핸들러 구현 | |
| }} | |
| onKeyDown={(e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| // TODO: 키보드 핸들러 구현 | |
| } | |
| }} | |
| className="cursor-pointer hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-white" | |
| /> | |
| ); |
| } | ||
|
|
||
| return ( | ||
| <div key={key} onClick={() => console.log(cell.label)}></div> |
There was a problem hiding this comment.
프로덕션 코드에서 console.log를 제거하세요.
클릭 핸들러에 console.log만 있습니다. 이는 플레이스홀더 코드로 보이며 프로덕션 환경에서는 제거되어야 합니다. 실제 클릭 동작(예: 상세 페이지 이동, 모달 표시 등)을 구현하거나 TODO 주석을 추가해주세요.
- <div key={key} onClick={() => console.log(cell.label)}></div>
+ <div
+ key={key}
+ onClick={() => {
+ // TODO: 셀 클릭 시 상세 페이지 또는 모달 표시
+ }}
+ ></div>📝 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.
| <div key={key} onClick={() => console.log(cell.label)}></div> | |
| <div | |
| key={key} | |
| onClick={() => { | |
| // TODO: 셀 클릭 시 상세 페이지 또는 모달 표시 | |
| }} | |
| ></div> |
🤖 Prompt for AI Agents
In src/pages/main/components/board/Boardgame.tsx around line 34 the onClick
handler currently only calls console.log(cell.label); remove this console.log
from production code and replace it with a real action or a clear TODO: either
call a passed-in handler prop (e.g., onCellClick(cell) or navigate to a detail
route / open a modal) or add a TODO comment that explains the intended behavior
and throw a noop or guard to prevent accidental logging; ensure the handler uses
a stable function reference (not inline) if you wire it to props or navigation.
| import StampBoard from './components/stampBoard/StampBoard'; | ||
| import { ControlBar } from '@/shared/components'; | ||
| import Image from 'next/image'; | ||
| import router from 'next/router'; |
There was a problem hiding this comment.
라우터 import 방식이 잘못되었습니다.
Board.tsx와 동일한 문제입니다. next/router에서 default export로 router를 import할 수 없습니다. useRouter 훅을 사용해야 합니다.
다음과 같이 수정하세요:
-import router from 'next/router';
+import { useRouter } from 'next/router';그리고 컴포넌트 내부에서:
export default function MainPage() {
+ const router = useRouter();
+
return (Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/pages/main/index.tsx around line 5, the file incorrectly imports a
default "router" from 'next/router'; change the import to use the useRouter hook
(import { useRouter } from 'next/router') and inside the component call the hook
(const router = useRouter()) so existing router usage still works; update any
references if they assumed a module-level router to use the local router
variable returned from the hook.
| const boardData = [ | ||
| [{ active: false }, { active: false }, { active: false }, { active: false }], | ||
| [ | ||
| { active: true, label: '김수환관' }, | ||
| { active: true, label: '부천 아트벙커' }, | ||
| { active: true, label: '한국 만화박물관' }, | ||
| { active: false }, | ||
| ], | ||
| [ | ||
| { active: false }, | ||
| { active: false }, | ||
| { active: false }, | ||
| { active: true, label: '상동 호수 공원' }, | ||
| ], | ||
| [ | ||
| { active: false }, | ||
| { active: true, label: '부천역' }, | ||
| { active: true, label: '다솔관' }, | ||
| { active: true, label: '부천 자유 시장' }, | ||
| ], | ||
| [ | ||
| { active: false }, | ||
| { active: true, label: '중앙도서관' }, | ||
| { active: false }, | ||
| { active: false }, | ||
| ], | ||
| [ | ||
| { active: false }, | ||
| { active: false }, | ||
| { active: true, label: '역곡공원' }, | ||
| { active: false }, | ||
| ], | ||
| [ | ||
| { active: false }, | ||
| { active: false }, | ||
| { active: false }, | ||
| { active: true, label: '부천 식물원' }, | ||
| ], | ||
| [{ active: false }, { active: false }, { active: false }, { active: false }], | ||
| ]; | ||
| export { boardData }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
TypeScript 타입 정의를 추가하세요.
boardData에 명시적인 타입 정의가 없어 타입 안정성이 보장되지 않습니다. 셀 구조와 배열 차원에 대한 타입을 정의하면 실수를 방지하고 코드 품질을 향상시킬 수 있습니다.
다음과 같이 타입을 추가하세요:
+interface BoardCell {
+ active: boolean;
+ label?: string;
+}
+
+type BoardRow = [BoardCell, BoardCell, BoardCell, BoardCell];
+type BoardData = BoardRow[];
+
-const boardData = [
+const boardData: BoardData = [
[{ active: false }, { active: false }, { active: false }, { active: false }],🤖 Prompt for AI Agents
In src/shared/constants/main/boardData.ts lines 1-41, there is no TypeScript
type for boardData which hurts type safety; add a Cell type (e.g. { active:
boolean; label?: string }) and annotate boardData as an array of Cell arrays
(Cell[][]) — optionally export the Cell type — then update the const declaration
to use that type so each cell and the board shape are type-checked.
KongMezu
left a comment
There was a problem hiding this comment.
보드판 많이 연구하신게 눈에 보여요 너무..대단하십니다 간단한 질의 정도 남겨두면서 approve 해두겠습니다 !
| @@ -0,0 +1,43 @@ | |||
| 'use client'; | |||
| import Image from 'next/image'; | |||
| import { boardData } from '@/shared/constants/main/boardData'; | |||
There was a problem hiding this comment.
항상 확장성 용이하게 만드는 코드 진짜 배워갑니다..
| const key = `cell-${r}-${c}`; | ||
|
|
||
| if (!cell.active) { | ||
| return <div key={key} className='bg-transparent' />; |
There was a problem hiding this comment.
!cell.active일 때 null 반환으로 안하고
bg-transparent 한 특별한 이유가 있을까요??
There was a problem hiding this comment.
null로 하게 될 경우 그 자리에 아무 요소도 렌더되지 않기 때문에 그리드의 셀 개수가 줄어들어 전체 배치가 틀어질 수 있어요 grid는 따로 빈 칸이라는 개념이 없어 요소가 없으면 셀 자체도 사라져버립니당
그래서 bg-transparent로 보이지 않는 <div>를 렌더링해서 해당 셀은 비어 있지만 공간은 차지한다는 상태를 만들어 원하는 그리드의 모양 유지했습니다
There was a problem hiding this comment.
셀 자체가 없어지는걸 생각을 못했네요 설명 감사합니다!
| import { Header } from '@/shared/components'; | ||
|
|
||
| import Boardgame from '@/pages/main/components/board/Boardgame'; | ||
| import router from 'next/router'; |
There was a problem hiding this comment.
여기 next/router 보다는
next/navigation 쓰는게 더 좋을거 같아요!
There was a problem hiding this comment.
Pages Router (pages/ 디렉토리)는 전통적인 파일 기반 라우팅 방식으로, 각 파일이 하나의 페이지 컴포넌트 역할을 하고 클라이언트-사이드 라우팅을 기본으로 하며 next/router에서 useRouter() 등을 사용하고
App Router (app/ 디렉토리)는 Next.js 13부터 도입된 방식으로, React Server Components, 중첩 레이아웃, 서버-클라이언트 경계를 고려한 최신 라우팅 구조로 next/navigation에서 useRouter(), usePathname(), useSearchParams()사용해야합니다
두 라우터 방식이 내부적으로 다르게 구현되어 있기 때문에, 라우팅 관련 API 및 훅들도 그 구조에 맞춰야한다고 생각하는데 저희가 Next.js 13 이후 버전을 사용하고 있지만 Pages Router 기반이라 next/router에서 useRouter()를 사용하는게 맞다고 생각이듭니다.
There was a problem hiding this comment.
확인 했습니다! next 더 공부해봐야겠네요.. 겉핥기식으로 알고있다는 느낌을 받아서 답변 감사합니다!
|
타입 명시 추가에 동의합니다!! |

🔥 작업 내용
🤔 추후 작업 사항
🔗 이슈
PR Point (To Reviewer)
보드판
원래는 보드판을 집적 만드려고했는데 괜한 생각이었어요. 디자이너님계 요청하여 그리드 형식으로 만들어달라 요청후 보드판에 맞게 그리드 만들엇습니다.
추가로 보드판 디자인이 바뀔 것을 염두하여 boardData.ts 파일에서 수정하면 그리드 안에 값 유동적으로 바꿀 수 있게 하였습니다.
boardData.ts 파일 위치 에러
원래 이 파일은 pages/main/components/boardData.ts에 위치해 있었는데, Next.js에서 pages 디렉토리 내부의 모든 파일은 자동으로 라우트로 인식되기 때문에 문제가 발생했습니다. boardData.ts는 단순히 보드 데이터 배열을 export하는 모듈 파일일 뿐 컴포넌트를 기본으로 내보내는 페이지가 아니어서 빌드 과정에서 “Found page without a React Component as default export” 오류가 발생했습니다.
Next.js의 라우팅 시스템이 pages 디렉토리 내부 파일을 모두 페이지로 처리하기 때문에 생긴 문제였어요.
그래서 boardData.ts를 pages 디렉토리 밖 예를 들어 src/main/components/main/boardData.ts의 경로로 이동시켰습니다. 이렇게 하면 Next.js가 해당 파일을 라우트로 인식하지 않고 일반 모듈로 처리하게 되면서 오류도 해결했습니다.
Next.js 공식 문서에서도 pages 폴더는 실제 라우트 페이지 전용으로 사용하고, 재사용 가능한 컴포넌트나 데이터, 유틸리티 파일은 별도의 디렉토리로 분리할 것을 권장하고있어요. 이러한 구조는 라우팅 트리를 단순화하고 모듈화와 유지보수성을 높이는 데 도움이 된다고합니다.
넥스트는 처음이라.. 아직 어렵네요 좀 더 공부해 오도록 하겠습니다-!
수민님이 찾아주셨어요
📸 피그마 스크린샷 or 기능 GIF
(작업 내역 스크린샷)
2025-10-26.3.15.14.mov
Summary by CodeRabbit
주요 변경 사항
New Features
Style