This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
If the user's prompt starts with “EP:”, then the user wants to enhance the prompt. Read the PROMPT_ENHANCER.md file and follow the guidelines to enhance the user's prompt. Show the user the enhancement and get their permission to run it before taking action on the enhanced prompt.
The enhanced prompts will follow the language of the original prompt (e.g., Korean prompt input will output Korean prompt enhancements, English prompt input will output English prompt enhancements, etc.)
This is a Turborepo monorepo containing multiple frontend applications and shared packages for experimenting with different frontend technologies and design patterns.
- Monorepo structure: Uses Turborepo for build orchestration and pnpm workspaces for dependency management
- Applications: Independent React, Next.js, and TypeScript applications in
apps/directory - Shared packages: Design system components and utilities in
packages/directory - Design system: Built with Panda CSS for styling and component generation
apps/next.js/: Next.js application with App Router, Jest testing, and Turbopack for developmentapps/react/: React SPA using Vite, Vitest for testing, and React Router for navigationapps/typescript/: Pure TypeScript application for experimenting with type design patternsapps/blog/web/: Next.js-based blog with MDX support, Supabase analytics, and React Query for data fetching
@design-system/ui: Component library with button components and shared UI elements@design-system/ui-lib: Generated Panda CSS utilities, patterns, and tokens@package/core: Core utilities including HTTP client and status code definitions@package/config: Shared TypeScript configurations
# Install dependencies
pnpm install
# Start all applications
pnpm dev
# Start specific applications
pnpm react # React app + design system
pnpm next # Next.js app + design system
pnpm typescript # TypeScript app
pnpm blog-web # Blog app + design system# Run all tests
pnpm test
# Run tests for specific app
cd apps/react && pnpm test
cd apps/next.js && pnpm test
cd apps/typescript && pnpm test
# Watch mode for Next.js
cd apps/next.js && pnpm test:watch# Build all applications
pnpm build
# Lint all applications
pnpm lint
# Type checking
pnpm check-types
# Blog-specific commands
pnpm blog-build # Build blog applicationUse workspace: protocol for internal package dependencies:
{
"dependencies": {
"@design-system/ui": "workspace:^"
}
}The project uses pnpm catalog feature for consistent version management across apps:
catalog:react19- React 19.1.0 and related typescatalog:typescript5- TypeScript 5.8.3catalog:- Panda CSS dev dependencies
Reference in package.json as "react": "catalog:react19"
Components follow this pattern:
components/ComponentName/
├── ComponentName.tsx
├── ComponentName.test.tsx
└── index.ts
- React app: Vitest + React Testing Library + MSW for API mocking
- Next.js app: Jest + React Testing Library + next-router-mock
- TypeScript app: Jest with Babel preset for TypeScript
Use data-testid attributes for test element selection and mock external dependencies.
Uses Panda CSS with:
- Generated CSS utilities in
@design-system/ui-lib - Component recipes for consistent styling
- JSX patterns for layout components (Box, Flex, Stack, etc.)
The blog (apps/blog/web/) is a statically generated (SSG) Next.js application with a
Supabase BaaS backend, deployed to GitHub Pages. The domain is https://blog.sangwook.dev.
┌─────────────────────────────────────────────────────────────────────┐
│ Build Time (CI/CD) │
│ │
│ apps/blog/posts/ ──→ sync-posts.mjs (이미지) ──→ public/posts/ │
│ (Markdown/MDX) ──→ generate-sitemap.mjs ──→ sitemap.xml │
│ ──→ generate-rss.mjs ──→ rss.xml │
│ ──→ generate-search-index.ts ──→ search-index │
│ ──→ next build (output: export) ──→ out/ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────┐ ┌──────────────────────────────────────────┐
│ GitHub Pages │ │ Supabase (Cloud) │
│ (Static Host) │ │ ┌─────────────────────────────────────┐ │
│ │ │ │ PostgreSQL │ │
│ - HTML/CSS/JS │ │ │ - post_views (조회수) │ │
│ - Images │◄───►│ │ - view_history (조회 이력) │ │
│ - sitemap.xml │ │ │ - RPC functions (analytics) │ │
│ - rss.xml │ │ ├─────────────────────────────────────┤ │
│ - robots.txt │ │ │ Auth (Google OAuth) │ │
│ │ │ │ - Admin 인증 │ │
│ │ │ └─────────────────────────────────────┘ │
└──────────────────┘ └──────────────────────────────────────────┘
│
▼
┌──────────────────┐ ┌──────────────────────────────────────────┐
│ Google Analytics│ │ GA Proxy (apps/ga-proxy/) │
│ (GA4) │◄────│ - Velog 등 외부 플랫폼 조회수 추적 │
└──────────────────┘ └──────────────────────────────────────────┘
- CI/CD:
.github/workflows/deploy-blog.yml로 자동 배포 - Trigger:
main브랜치에apps/blog/**경로 변경 시 자동 빌드 + 수동 트리거 지원 - 빌드 명령:
pnpm build --filter=@blog/web --no-cache - 출력 디렉토리:
apps/blog/web/out/→ GitHub Pages artifact로 업로드 - 환경변수: GitHub Secrets에서 Supabase URL/Key, Admin Email 주입
- 캐싱: Turborepo 캐시(
rharkor/caching-for-turbo) + pnpm 캐시 활용
- Next.js
output: 'export': 프로덕션 빌드 시 완전한 정적 HTML 생성 (개발 모드에서는 해제) trailingSlash: true: GitHub Pages 호환을 위한 후행 슬래시 설정images.unoptimized: true: 정적 호스팅에서 Next.js Image Optimization 사용 불가하므로 비활성화- MDX 지원:
@next/mdx플러그인으로.mdx파일을 페이지로 처리
역할: 정적 사이트에서 불가능한 동적 기능을 담당
| 기능 | 설명 |
|---|---|
| 조회수 추적 | increment_view_count RPC → post_views 테이블에 저장 |
| 조회 이력 | view_history 테이블에 시간별/일별 조회 기록 |
| Admin 인증 | Google OAuth를 통한 관리자 로그인 |
| Analytics RPC | 대시보드용 집계 함수 (트렌드, 시간별, 요일별 통계) |
- 클라이언트:
@supabase/supabase-js로 브라우저에서 직접 연결 (Anon Key 사용) - 로컬 개발:
supabase start/stop으로 로컬 Supabase 인스턴스 실행 (Docker 기반) - 마이그레이션:
supabase/migrations/디렉토리에 SQL 파일로 스키마 관리 - 프로덕션 URL:
.env.production에 Supabase Cloud 프로젝트 URL/Key 설정
- 콘텐츠 작성:
apps/blog/posts/디렉토리에 Markdown 파일 작성- Frontmatter:
title,date,slug,excerpt,thumbnail,tags,published,status,scheduledDate - 폴더 구조로 시리즈(series) 자동 분류
- Frontmatter:
- 콘텐츠 공개 제어 (
isPostVisible()헬퍼로 판단):published: true(기존 방식, 하위호환)status: 'published'— 공개status: 'draft'— 비공개 (빌드에서 제외)status: 'scheduled'— 주의: 반드시scheduledDate(예:scheduledDate: '2026-03-16T09:00:00+09:00') 값을 함께 명시해야 합니다. 누락 시 항상 비공개 처리됩니다.isPostVisible()로직은posts.ts,generate-sitemap.mjs,generate-rss.mjs에 동일하게 적용
- 빌드 전 처리 (
prebuild):sync-posts.mjs: 포스트 디렉토리의 이미지/미디어 파일을public/posts/에 복사generate-sitemap.mjs: 발행된 글 목록으로sitemap.xml생성generate-rss.mjs: RSS 피드(rss.xml) 생성generate-search-index.ts: 검색용 JSON 인덱스(search-index.json) 생성
- 정적 빌드:
next build→out/디렉토리에 정적 파일 생성 - 배포: GitHub Actions → GitHub Pages
main브랜치 push 시 자동 빌드- 매일 KST 09:00 (UTC 00:00) cron 자동 빌드 — 예약 발행 글 공개용
- 수동 트리거(
workflow_dispatch) 지원
- 조회수 카운팅:
useViewCount훅 → Supabase RPC 호출 (6시간 쿨다운, 쿠키 기반 중복 방지) - 댓글: Giscus (GitHub Discussions 기반)
- Analytics 대시보드:
/admin경로, React Query(useSuspenseQuery) + Recharts 차트 - 페이지 전환 애니메이션:
@ssgoi/react+ Motion 라이브러리 - 데이터 페칭:
@tanstack/react-query로 Supabase 데이터 캐싱/관리
- Sitemap: 빌드 시 자동 생성 (
/sitemap.xml) - RSS: 빌드 시 자동 생성 (
/rss.xml) - robots.txt:
/public/robots.txt - OpenGraph/Twitter Card:
layout.tsx메타데이터에 설정 - Google Analytics:
@next/third-partiesGA4 연동 (G-ZS9ENFSSQ0) - GA Proxy:
apps/ga-proxy/로 Velog 등 외부 플랫폼 조회수 추적 - 검색 인증: Naver 사이트 인증 메타태그 포함
- 검색 인덱스:
search-index.json으로 클라이언트 사이드 검색 지원
- Admin 페이지:
/admin(로그인),/admin/analytics(상세 분석) - 인증 방식: Supabase Auth + Google OAuth
- 접근 제어:
NEXT_PUBLIC_ADMIN_EMAIL환경변수에 등록된 이메일만 Admin 접근 허용
| 파일 | 역할 |
|---|---|
next.config.ts |
SSG output, MDX, trailingSlash 설정 |
panda.config.ts |
Panda CSS 스타일 설정 |
.env.production |
Supabase URL/Key, Giscus 설정 |
.env.local |
로컬 개발용 환경변수 (GA, Supabase local 등) |
supabase/config.toml |
로컬 Supabase 설정 (Auth, DB, Storage 등) |
.github/workflows/deploy-blog.yml |
CI/CD 배포 워크플로우 |
- Node.js >= 20
- pnpm 10.10.0 (specified in packageManager field)