Frontend built with Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS 4. Server state via TanStack Query, schema validation via Zod. Follows FSD (Feature-Sliced Design) architecture.
| Task | Command |
|---|---|
| Dev server | npm run dev |
| Build | npm run build |
| Lint | npm run lint |
| Format | npm run format / format:check |
| Type check | npx tsc --noEmit |
| Unit tests | npm run test (single run) |
| Unit tests (watch) | npm run test:watch |
- Node >= 20.9.0, path alias
@/*→./src/* - The pre-commit hook (husky) runs ESLint/Prettier via lint-staged automatically.
Layer structure under src/. Upper layers may only import from lower layers (no reverse imports, no cross-slice imports within the same layer).
src/
├── app/ # Next.js App Router (routing only, keep thin — re-export from views)
├── views/ # Page composition layer (FSD's "pages" — renamed to avoid Next.js conflict)
├── widgets/ # Large standalone UI blocks (header, sidebar, etc.)
├── features/ # User-action-level features (like, search, login form, etc.)
├── entities/ # Business entities (user, match, team, etc.)
└── shared/ # Shared code (no domain knowledge)
├── ui/ # Design-system-like shared components
├── lib/ # Utilities, hooks
├── api/ # API client, fetch wrappers
└── config/ # Constants, configuration
- Layer dependency direction:
app → views → widgets → features → entities → shared. Never import upward. - No cross-slice imports within the same layer (e.g.
features/loginmust not importfeatures/search). If code needs sharing, move it to a lower layer. - Public API: each slice exposes itself only through
index.ts. Never import a slice's internal files directly.- Good:
import { LoginForm } from '@/features/login' - Bad:
import { LoginForm } from '@/features/login/ui/LoginForm'
- Good:
- Segments inside a slice: use only what's needed among
ui/,model/,api/,lib/. - No business logic in
src/app/(Next.js routing). Implement page components inviews/and only import them fromapp/. views/is composition-only: each view slice is a singleindex.tsxthat only assembles widgets/features/entities/shared components. No business logic, no state management, no API calls, no standalone UI implementation inviews/— anything reusable or logic-bearing lives in a lower layer and gets imported.views/signup/index.tsx ← the whole slice: composes <SignupForm/> etc. from features
Format: type: 짧은 한글 설명 — a single short subject line in Korean, nothing else (no body, no trailers). Split changes into logical units.
- Types:
featfixrefactorstyletestdocscichore - Examples:
feat: 로그인 폼 추가,fix: CI 액션 버전 최신화
Never push unless the user explicitly asks. Committing autonomously is fine.
- Base branch:
develop(fall back tomainif absent) - The PR template is mandatory: always read
.github/PULL_REQUEST_TEMPLATE.mdand follow its structure exactly, written in Korean. Fill every applicable section; delete sections that don't apply. (If the template file doesn't exist yet, use the structure in the/prskill.) - Before opening a PR, verify locally:
npm run lint,npm run format:check,npx tsc --noEmit,npm run test,npm run build.
- Unit tests are colocated next to their target file as
*.test.tsx(e.g.page.test.tsx) - Vitest + Testing Library, jsdom environment
Detailed conventions live in .claude/rules/ and load automatically per file pattern:
typescript.md— strict typing rules (noany, noas, noenum, ...)react.md— Server Components first, component structure, Next.js primitivesstyling.md— Tailwind 4 token usage, no hardcoded valuestesting.md— Vitest/Testing Library and Playwright conventionsfsd.md— slice structure, naming, layer placement heuristics