밥.net 프론트엔드. 쿠키 하나로 사이트를 고르고, UI·SEO·끼니 슬롯은 API 카탈로그에서 받습니다.
API: bap-back · 문서: api.밥.net/docs · 에이전트: AGENTS.md
/— 식단. 쿠키가 없으면 같은 페이지에서 사이트를 고릅니다 (쿠키bap-site-id)/?site={id}—bap-site-id쿠키를 넣고/로 이동 (예:/?site=kdmhs)/?home=1—bap-site-id쿠키를 지우고 사이트 선택 UI로 이동/docs— API Scalar로 리다이렉트 (api.밥.net/docs)
사이트별 App Router 폴더는 없습니다. 백엔드에 프로바이더만 추가하면 홈 사이트 선택·manifest에 자동 반영됩니다.
bun install --frozen-lockfile
NEXT_PUBLIC_API_BASE_URL=http://localhost:3001 bun dev # 기본 :3000로컬 API가 3000이면 NEXT_PUBLIC_API_BASE_URL 생략 가능 (운영 API 기본값).
bun run build
bun start
bun run lint| 변수 | 기본값 | 설명 |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL |
https://api.xn--rh3b.net |
API 베이스 (https://api.밥.net과 동일 호스트) |
코드에서는 src/shared/lib/apiBase.ts의 API_BASE_URL을 사용합니다.
Next.js 16 proxy.ts (middleware 대체):
| 경로 | 동작 |
|---|---|
/ + 쿠키 없음 |
홈에서 사이트 선택 UI (리다이렉트 없음) |
/?site={id} |
bap-site-id 쿠키 설정 후 쿼리 제거·리다이렉트 |
/?home=1 |
bap-site-id 쿠키 삭제, 이번 요청은 사이트 선택 UI |
| 없는 경로 | → / (쿼리 유지) |
/ |
x-site-id 헤더 주입 |
/docs |
→ API_BASE_URL/docs (Scalar) |
/ + 잘못된 site id |
홈에서 사이트 선택 UI |
클라이언트는 쿠키를 직접 읽지 않고 useSite() / SiteProvider를 씁니다. 홈으로 가면 bap-site-id를 지우고 사이트 선택 UI를 보여 줍니다.
GET / (catalog) → layout / 홈 선택 UI / manifest
GET /{basePath}/{date} → TanStack Query (useMealQuery)
GET /docs (API) → Scalar (백엔드가 직접 서빙)
POST /mcp → 에이전트 (프론트가 호출하지 않음)
SSR initialData → 오늘 날짜에만 시드 (날짜 변경 시 다른 날 메뉴로 섞이지 않음)
src/
app/ App Router (pages, OG, manifest)
shared/
components/ MealLayout, MealSection, Glass, …
hooks/ useMealQuery, useMealData, …
lib/ catalog, mealService, apiBase, queryKeys
types/ SitePresentation, PublicDayMenu, …
sites/
config.ts BRAND 상수만
context.tsx SiteProvider
server.ts getSiteId() — proxy 헤더
proxy.ts Edge 라우팅·쿠키
public/
img/ 끼니 배경 SVG
icons/ PWA·favicon
og.png OG/Twitter 카드
기본은 프론트 수정 없음. 학교는 bap-back 프로바이더 + presentation으로만 추가합니다. 카탈로그가 홈 사이트 선택, 엣지 패널, PWA manifest, MCP bap_list_providers에 같이 반영됩니다.
공개 문서: api.밥.net/docs · 백엔드 절차: bap-back README
하지 말 것:
src/sites/{id}/트리,SITES맵,/kdmhs같은 경로 하드코딩- 클라이언트에서
bap-site-id쿠키를 읽어 라우팅 - API 문서를 프론트에 다시 구현 (Scalar은 백엔드
/docs)
필요할 때만:
| 상황 | 프론트에서 할 일 |
|---|---|
| 기존 아침·점심·저녁 에셋 재사용 | 없음. presentation이 /icon/lunch.svg 등을 가리키면 됨 |
| 새 끼니 아이콘·배경 | public/icon, public/img에 파일 추가 후 presentation URL 맞추기 |
| 메뉴 사진 검색 | 백엔드 features.foodSearch: true + handleExtraRoute. 프론트 mealSection이 플래그만 봄 |
presentation 계약은 백엔드 SitePresentation과 동일합니다 (src/shared/types). 카탈로그에 id, basePath, meals[]가 없으면 getCatalog()가 그 사이트를 버립니다.
- Next.js 16, React 19, Tailwind 4
- TanStack Query (식단 fetch·prefetch)
- Jotai (
currentDateAtom만) - Wanted Sans (globals.css CDN)
- Biome (lint/format)
Docker (Bun 빌드, TZ=Asia/Seoul). 빌드 시 NEXT_PUBLIC_API_BASE_URL bake-in.
코딩 규칙은 AGENTS.md 참고.
- 카탈로그 fetch 실패 시 사이트 선택이 빈 그리드 (에러 UI 없음)
- 모든 사이트 OG 이미지 공유 (
public/og.png) - React Query Devtools가 prod에서도 마운트됨