This file provides guidance to Claude Code (claude.ai/code) when working with this repository.
Omechu (오메추 - "오늘 뭐 먹지?") is a food recommendation web application that provides personalized menu and restaurant recommendations based on user preferences, context, and conditions.
- Framework: Next.js 16 (App Router, Turbopack)
- Language: TypeScript (strict mode)
- Styling: Tailwind CSS 4 (CSS-first config in
globals.css) - State: Zustand (client), TanStack React Query (server)
- Forms: React Hook Form + Zod 4
- API: Axios with JWT auth interceptors
- Monitoring: Sentry + Vercel Analytics + Vercel Speed Insights
- Package Manager: pnpm (only —
npm/yarn금지)
All commands run from omechu-app/ directory:
pnpm dev # 개발 서버 (Turbopack) — http://localhost:3000
pnpm build # 프로덕션 빌드
pnpm start # 프로덕션 서버
pnpm lint # ESLint 검사
pnpm lint:fix # ESLint 자동 수정
pnpm format # Prettier 포맷팅
pnpm format:check # Prettier 검사만
pnpm typecheck # TypeScript 타입 검사
pnpm validate # lint + typecheck + format 전체 검증
pnpm prepare # Husky hooks 설치omechu-app/src/
├── app/ # Next.js App Router — pages, layouts, route handlers
├── widgets/ # 조합형 UI 블록 (Header, BottomNav 등)
├── entities/ # 비즈니스 엔티티 (user, menu, restaurant 등)
└── shared/ # 공용 레이어
├── api/ # Axios instance, fetch 유틸
├── config/ # 상수, 메뉴 설정
├── constants/ # 사이트 상수 (URL, 테마 등)
├── lib/ # 유틸리티
├── store/ # Zustand stores
└── ui/ # 디자인 시스템 컴포넌트
app → widgets, entities, shared
widgets → entities, shared
entities → shared
shared → (external packages only)
- 상위 레이어가 하위 레이어를 import: OK
- 하위 레이어가 상위 레이어를 import: NEVER — ESLint가 error로 차단
- 같은 레이어 간 cross-import: NEVER
"@/*" → "./src/*"
"@/app/*" → "./src/app/*"
"@/entities/*" → "./src/entities/*"
"@/widgets/*" → "./src/widgets/*"
"@/shared/*" → "./src/shared/*"entity/
├── api/ # API calls
├── model/ # State management (hooks, stores)
├── ui/ # Components
├── lib/ # Utils & helpers
├── types/ # Type definitions
└── index.ts # Barrel exports (cross-layer import 진입점)
| 대상 | 규칙 | 예시 |
|---|---|---|
| 컴포넌트 (.tsx) | PascalCase | MenuCard.tsx, UserProfile.tsx |
| Next.js 예약 파일 | lowercase | page.tsx, layout.tsx, loading.tsx |
| 훅 | camelCase (use 접두사) | useAuth.ts, useMenuList.ts |
| 유틸/라이브러리 | camelCase | axiosInstance.ts, formatDate.ts |
| 타입 | camelCase | menu.ts, user.ts |
ESLint check-file 플러그인이 자동으로 강제함.
- function declaration 사용 (arrow function export 금지)
'use client'는 필요한 곳에만 최소한으로- Server Component 우선
// Good
export default function MenuCard({ name }: MenuCardProps) {
return <div>{name}</div>;
}
// Bad
const MenuCard = ({ name }: MenuCardProps) => <div>{name}</div>;
export default MenuCard;// 1. React / Next.js
import { useState } from "react";
import Image from "next/image";
// 2. 외부 패키지
import { useQuery } from "@tanstack/react-query";
// 3. 내부 레이어 (FSD 순서)
import { SomeWidget } from "@/widgets/some-widget";
import { useAuth } from "@/entities/user";
import { Button } from "@/shared/ui";
// 4. 상대 경로
import { helper } from "./helper";Prettier commit 시 자동 정렬. 수동으로 순서 맞출 필요 없음.
- Tailwind 유틸리티 클래스 사용
- 인라인 스타일 금지
cn()(clsx+tailwind-merge) 으로 조건부 클래스 병합- CVA로 variants 관리 (
shared/ui컴포넌트) - 모바일 고정 레이아웃:
max-w-120 min-w-93.75(375px 기준)
console.log금지 —console.warn,console.error만 허용 (ESLint 강제)- API 에러는 명시적으로 처리
any타입 최소화 (ESLint warn)
- JWT-based with automatic token refresh
- Access token in Zustand store (persisted to localStorage)
- Axios interceptor handles 401 errors with token refresh queue
- Client-side route protection in
ClientLayout.tsx
Key files:
- Auth store:
src/entities/user/model/auth.store.ts - Axios instance:
src/shared/lib/axiosInstance.ts
Zustand stores (with persist middleware):
auth.store.ts— Authentication stateonboarding.store.ts— Multi-step onboarding flowtagData.store.ts— Food preference tagsquestionAnswer.store.ts— Recommendation questionnaireuserInfoSetup.store.ts— User profile setup
- Centralized Axios instance with interceptors
- Base URL from
NEXT_PUBLIC_API_URL - Response format:
{ resultType, error, success } - React Query for caching/mutations
fetchJSON유틸: resultType 래핑 자동 처리, 404 →[]반환
// v4 정확한 문법
z.enum(["a", "b"], { message: "에러 메시지" });
// 틀린 문법 (v3)
z.enum(["a", "b"], { errorMap: () => ({ message: "..." }) });- 브랜치:
develop→mainPR 방식 - 커밋: pre-commit hook이 자동 검증 후 커밋
- 커밋 메시지:
type: 설명(한국어/영어 혼용 가능)
1. eslint --fix (lint + auto-fix)
2. prettier --write (format)
3. tsc --noEmit (type check)
세 단계 모두 통과해야 커밋 가능. git commit --no-verify 사용 금지.
| 도구 | 역할 | 레벨 |
|---|---|---|
| ESLint (check-file) | 파일명 컨벤션 | warn |
| ESLint (FSD rules) | 레이어 import 위반 | error |
| ESLint (eqeqeq) | == 사용 |
error |
| ESLint (no-console) | console.log | warn |
| ESLint (prettier) | 포맷 불일치 | error |
| tsc --noEmit | 타입 에러 | error |
NEXT_PUBLIC_API_URL=<backend-api-url>
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=<google-maps-api-key>
GOOGLE_MAP_SERVER_API_KEY=<google-map-server-api-key>
NEXT_PUBLIC_EMBED_API_URL=<embed-api-url>
NEXT_PUBLIC_SENTRY_DSN=<sentry-dsn>
SENTRY_AUTH_TOKEN=<sentry-auth-token>
SENTRY_ORG=omechu
SENTRY_PROJECT=omechu-fe
NEXT_PUBLIC_SITE_URL=https://omechu.log8.kr- AWS S3 for uploads
- Next.js
<Image>withremotePatternsconfigured - Korean filenames: NFC normalization 필요