From 642b8859361fea6d80f126e062ee2888aa93af66 Mon Sep 17 00:00:00 2001 From: Jongchan Date: Thu, 17 Jul 2025 19:36:20 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EA=B3=84=EC=B8=B5=EC=A0=81=20CLAUD?= =?UTF-8?q?E.md=20=EA=B5=AC=EC=A1=B0=20=EB=B0=8F=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=95=84=ED=82=A4=ED=85=8D=EC=B2=98=20=EA=B0=9C?= =?UTF-8?q?=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .context/commands.md | 173 ++++++++ .context/current-status.md | 13 + .context/development-guidelines.md | 157 +++++++ .context/git-workflow.md | 89 ++++ .context/project-overview.md | 115 +++++ .context/styling-guide.md | 85 ++++ CLAUDE.md | 404 +++--------------- src/app/CLAUDE.md | 55 +++ src/app/[category]/CLAUDE.md | 56 +++ src/app/[category]/[slug]/page.tsx | 6 +- .../[category]/_components/CategoryBadge.tsx | 2 +- .../[category]/_components/CategoryList.tsx | 2 +- .../[category]/_components/PostContent.tsx | 2 +- src/app/[category]/_components/PostHeader.tsx | 2 +- .../[category]/_components/PostNavigation.tsx | 2 +- .../_components/RelatedPostItem.tsx | 2 +- .../[category]/_components/RelatedPosts.tsx | 2 +- src/app/[category]/page.tsx | 7 +- src/app/_components/CLAUDE.md | 65 +++ src/app/_components/Header.tsx | 8 +- src/app/_lib/CLAUDE.md | 61 +++ src/app/page.tsx | 129 +++++- src/contents/CLAUDE.md | 47 ++ src/domain/CLAUDE.md | 43 ++ src/domain/blog/CLAUDE.md | 52 +++ src/{entities => domain}/blog/index.ts | 0 .../blog/logic/categories.ts | 0 src/{entities => domain}/blog/logic/posts.ts | 0 src/{entities => domain}/blog/logic/tags.ts | 0 src/{entities => domain}/blog/types.ts | 0 30 files changed, 1219 insertions(+), 360 deletions(-) create mode 100644 .context/commands.md create mode 100644 .context/current-status.md create mode 100644 .context/development-guidelines.md create mode 100644 .context/git-workflow.md create mode 100644 .context/project-overview.md create mode 100644 .context/styling-guide.md create mode 100644 src/app/CLAUDE.md create mode 100644 src/app/[category]/CLAUDE.md create mode 100644 src/app/_components/CLAUDE.md create mode 100644 src/app/_lib/CLAUDE.md create mode 100644 src/contents/CLAUDE.md create mode 100644 src/domain/CLAUDE.md create mode 100644 src/domain/blog/CLAUDE.md rename src/{entities => domain}/blog/index.ts (100%) rename src/{entities => domain}/blog/logic/categories.ts (100%) rename src/{entities => domain}/blog/logic/posts.ts (100%) rename src/{entities => domain}/blog/logic/tags.ts (100%) rename src/{entities => domain}/blog/types.ts (100%) diff --git a/.context/commands.md b/.context/commands.md new file mode 100644 index 0000000..ab2d093 --- /dev/null +++ b/.context/commands.md @@ -0,0 +1,173 @@ +# λͺ…λ Ήμ–΄ 레퍼런슀 + +ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•˜λŠ” μ£Όμš” λͺ…령어듀을 μ •λ¦¬ν•©λ‹ˆλ‹€. + +## 개발 λͺ…λ Ήμ–΄ + +### νŒ¨ν‚€μ§€ 관리 +```bash +# μ˜μ‘΄μ„± μ„€μΉ˜ +pnpm install + +# νŒ¨ν‚€μ§€ μΆ”κ°€/제거 +pnpm add +pnpm add -D # 개발 μ˜μ‘΄μ„± +pnpm remove +``` + +### μ£Όμ˜μ‚¬ν•­ +```bash +# 🚫 κΈˆμ§€λœ λͺ…λ Ήμ–΄λ“€ (CLAUDE.md μ§€μΉ¨) +pnpm dev # κ°œλ°œμ„œλ²„ μ‹€ν–‰ κΈˆμ§€ +pnpm build # λΉŒλ“œ κΈˆμ§€ +pnpm start # μ„œλ²„ μ‹€ν–‰ κΈˆμ§€ +``` + +## ν…ŒμŠ€νŠΈ (Vitest) + +### κΈ°λ³Έ ν…ŒμŠ€νŠΈ +```bash +# ν…ŒμŠ€νŠΈ μ‹€ν–‰ +pnpm test + +# ν…ŒμŠ€νŠΈ UI μ‹€ν–‰ +pnpm test:ui + +# ν…ŒμŠ€νŠΈ κ°μ‹œ λͺ¨λ“œ +pnpm test:watch + +# ν…ŒμŠ€νŠΈ 컀버리지 +pnpm test:coverage +``` + +### νŠΉμ • ν…ŒμŠ€νŠΈ +```bash +# νŒŒμΌλ³„ ν…ŒμŠ€νŠΈ +pnpm test -- posts.test.ts + +# νŒ¨ν„΄ ν…ŒμŠ€νŠΈ +pnpm test -- --grep "νƒœκ·Έ κ·Έλž˜ν”„" +``` + +## μ½”λ“œ ν’ˆμ§ˆ (Biome) + +### μ½”λ“œ 검사 및 ν¬λ§·νŒ… +```bash +# μ½”λ“œ 검사 +pnpm biome:check + +# μžλ™ μˆ˜μ • +pnpm biome:fix + +# μŠ€ν…Œμ΄μ§€λœ 파일만 μˆ˜μ • +pnpm biome:staged +``` + +### TypeScript νƒ€μž… 검사 +```bash +# νƒ€μž… 검사 +pnpm type + +# νƒ€μž… 검사 (κ°μ‹œ λͺ¨λ“œ) +pnpm type -- --watch +``` + +## 디버깅 및 뢄석 + +### ν”„λ‘œμ νŠΈ μƒνƒœ 확인 +```bash +# μ˜μ‘΄μ„± 트리 +pnpm list + +# λ³΄μ•ˆ 취약점 검사 +pnpm audit + +# λ””μŠ€ν¬ μ‚¬μš©λŸ‰ +du -sh node_modules/ out/ +``` + +### 파일 검색 +```bash +# 파일 μ°ΎκΈ° +find src/ -name "*.tsx" -type f + +# λ‚΄μš© 검색 +grep -r "getRelatedPostsByTags" src/ +``` + +## Git μ›Œν¬ν”Œλ‘œμš° + +### 브랜치 μž‘μ—… +```bash +# κΈ°λŠ₯ 브랜치 생성 +git checkout -b feature/new-feature + +# 변경사항 확인 +git status +git diff + +# 컀밋 +git add . +git commit -m "feat: μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€" +``` + +## 문제 ν•΄κ²° + +### μΊμ‹œ 정리 +```bash +# pnpm μΊμ‹œ 정리 +pnpm store prune + +# TypeScript μΊμ‹œ 정리 +rm -rf node_modules/.cache +``` + +### μ˜μ‘΄μ„± μž¬μ„€μΉ˜ +```bash +# node_modules 정리 ν›„ μž¬μ„€μΉ˜ +rm -rf node_modules pnpm-lock.yaml +pnpm install +``` + +### 포트 좩돌 ν•΄κ²° +```bash +# 포트 μ‚¬μš© 확인 +lsof -i :3000 + +# ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œ +kill -9 +``` + +## λΉŒλ“œ κ²°κ³Ό 확인 + +### 정적 μ‚¬μ΄νŠΈ 뢄석 +```bash +# λΉŒλ“œ κ²°κ³Ό 확인 (out/ 디렉토리) +ls -la out/ + +# 파일 크기 확인 +find out/ -name "*.js" -exec ls -lh {} \; | sort -k5 -h +``` + +## μœ μš©ν•œ 슀크립트 + +### 개발 ν™˜κ²½ μ„€μ • +```bash +#!/bin/bash +# 개발 ν™˜κ²½ μ΄ˆκΈ°ν™” +pnpm install +pnpm type +pnpm biome:check +pnpm test +echo "βœ… 개발 ν™˜κ²½ μ€€λΉ„ μ™„λ£Œ" +``` + +### μ½”λ“œ ν’ˆμ§ˆ 검사 +```bash +#!/bin/bash +# μ½”λ“œ ν’ˆμ§ˆ 전체 검사 +pnpm biome:check || exit 1 +pnpm type || exit 1 +pnpm test || exit 1 +echo "βœ… μ½”λ“œ ν’ˆμ§ˆ 검사 톡과" +``` \ No newline at end of file diff --git a/.context/current-status.md b/.context/current-status.md new file mode 100644 index 0000000..9d11ae2 --- /dev/null +++ b/.context/current-status.md @@ -0,0 +1,13 @@ +# current-status.md + +졜근 μ™„λ£Œ μž‘μ—…κ³Ό λ‹€μŒ μž‘μ—… μš”μ†Œλ₯Ό μ •λ¦¬ν•œ λ¬Έμ„œμž…λ‹ˆλ‹€. + +## 졜근 μ™„λ£Œ μž‘μ—… +- βœ… **CLAUDE.md λ¬Έμ„œ 체계화**: @import ꡬ문으둜 λͺ¨λ“ˆν™”λœ λ¬Έμ„œ ꡬ쑰 μ™„μ„± +- βœ… **development-guidelines.md μ΅œμ ν™”**: μ‹€μ œ ν”„λ‘œμ νŠΈμ˜ 도메인 쀑심 섀계, λͺ¨λ“ˆ 레벨 캐싱, νƒœκ·Έ κ·Έλž˜ν”„ μ‹œμŠ€ν…œ νŒ¨ν„΄ μ€‘μ‹¬μœΌλ‘œ μž¬μž‘μ„± +- βœ… **styling-guide.md μ΅œμ ν™”**: μ‹€μ œ μ»΄ν¬λ„ŒνŠΈ νŒ¨ν„΄(CategoryBadge, TagBadge, CVA μ‹œμŠ€ν…œ) λ°˜μ˜ν•œ μ‹€μš©μ  κ°€μ΄λ“œ +- βœ… **git-workflow.md μ΅œμ ν™”**: μ‹€μ œ ν”„λ‘œμ νŠΈμ˜ 브랜치 νŒ¨ν„΄κ³Ό 컀밋 μ»¨λ²€μ…˜ 반영 +- βœ… **commands.md μ΅œμ ν™”**: μ‹€μ œ package.json 슀크립트 기반, κ°œλ°œμ„œλ²„/λΉŒλ“œ κΈˆμ§€ μ§€μΉ¨ ν¬ν•¨ν•œ μ‹€μš©μ  λͺ…λ Ήμ–΄ κ°€μ΄λ“œ + +## λ‹€μŒ μž‘μ—… μš”μ†Œ +- μΆ”ν›„ μ •μ˜ diff --git a/.context/development-guidelines.md b/.context/development-guidelines.md new file mode 100644 index 0000000..c99f9f9 --- /dev/null +++ b/.context/development-guidelines.md @@ -0,0 +1,157 @@ +# 개발 κ°€μ΄λ“œλΌμΈ + +이 λΈ”λ‘œκ·Έ ν”„λ‘œμ νŠΈμ˜ 핡심 νŒ¨ν„΄κ³Ό κ΅¬ν˜„ 원칙을 μ •μ˜ν•©λ‹ˆλ‹€. + +## μ•„ν‚€ν…μ²˜ νŒ¨ν„΄ + +### 도메인 쀑심 섀계 +- **도메인 둜직**: `src/domain/blog/` 에 톡합 +- **UI μ»΄ν¬λ„ŒνŠΈ**: `src/app/` ν•˜μœ„μ— κ΅¬ν˜„ +- **νƒ€μž… μ •μ˜**: `src/domain/blog/types.ts` 쀑앙 집쀑 + +```typescript +// 도메인 API μ‚¬μš© μ˜ˆμ‹œ +import { getCategoryById, getPostsByCategory, getRelatedPostsByTags } from '@/domain/blog' + +const category = getCategoryById('dev') +const posts = getPostsByCategory('dev') +const related = getRelatedPostsByTags(currentSlug, 3) +``` + +### λͺ¨λ“ˆ 레벨 캐싱 +λΉŒλ“œ νƒ€μž„μ— λͺ¨λ“  데이터λ₯Ό 사전 λ‘œλ“œν•˜μ—¬ SSG μ΅œμ ν™” + +```typescript +// src/domain/blog/index.ts +export const allPosts = await getAllPosts() +export const allCategories = await getAllCategories() +export const allTags = extractTagsFromPosts(allPosts) +export const tagGraph = createTagGraph(allPosts, allTags) +export const tagClusters = createTagClusters(tagGraph) +``` + +### νƒœκ·Έ κ·Έλž˜ν”„ μ‹œμŠ€ν…œ +graphology 라이브러리둜 νƒœκ·Έ κ°„ 관계 뢄석 + +```typescript +// νƒœκ·Έ 관계 뢄석 +const relationships = getTagRelationships() +const clusters = getTagClusters() +const relatedPosts = getRelatedPostsByTags(currentSlug, 3) +``` + +## μ½˜ν…μΈ  ꡬ쑰 + +### MDX 파일 ꡬ쑰 +``` +src/contents/ +β”œβ”€β”€ dev/ # 개발 μΉ΄ν…Œκ³ λ¦¬ +β”‚ β”œβ”€β”€ category.json +β”‚ └── *.mdx +└── life/ # 일상 μΉ΄ν…Œκ³ λ¦¬ + β”œβ”€β”€ category.json + └── *.mdx +``` + +### 포슀트 frontmatter +```yaml +--- +title: '포슀트 제λͺ©' +date: 2025-01-17 +tags: ['tag1', 'tag2'] +description: '포슀트 μ„€λͺ…' +category: 'dev' # λ˜λŠ” 'life' +--- +``` + +### μΉ΄ν…Œκ³ λ¦¬ 메타데이터 +```json +{ + "name": "개발", + "description": "개발 κ΄€λ ¨ 포슀트", + "color": "blue", + "icon": "πŸ’»" +} +``` + +## μ»΄ν¬λ„ŒνŠΈ νŒ¨ν„΄ + +### νƒ€μž… μ •μ˜ +```typescript +interface PostHeaderProps { + title: string + date: string + tags: string[] + category?: CategoryId + readingTime?: number + author?: string + className?: string +} +``` + +### μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„ +```typescript +// μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ κΈ°λ³Έ, μƒν˜Έμž‘μš© ν•„μš”μ‹œλ§Œ ν΄λΌμ΄μ–ΈνŠΈ +export function PostHeader({ title, date, tags, category }: PostHeaderProps) { + return ( +
+
+ {category && } +

{title}

+
+ +
+ ) +} +``` + +## ν’ˆμ§ˆ κΈ°μ€€ + +### TypeScript 엄격 λͺ¨λ“œ +- `any` νƒ€μž… μ‚¬μš© κΈˆμ§€ +- λͺ¨λ“  Props μΈν„°νŽ˜μ΄μŠ€ λͺ…μ‹œμ  μ •μ˜ +- 도메인 νƒ€μž… μž¬μ‚¬μš© (`CategoryId`, `Post`, `Tag`) + +### ν…ŒμŠ€νŠΈ νŒ¨ν„΄ +```typescript +// λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 ν…ŒμŠ€νŠΈ +describe('νƒœκ·Έ κ·Έλž˜ν”„ μ‹œμŠ€ν…œ', () => { + it('κ΄€λ ¨ 포슀트λ₯Ό νƒœκ·Έ μœ μ‚¬λ„λ‘œ μ°ΎλŠ”λ‹€', () => { + const related = getRelatedPostsByTags('test-slug', 3) + expect(related).toHaveLength(3) + }) +}) +``` + +### μ„±λŠ₯ μ΅œμ ν™” +- λͺ¨λ“  데이터 λΉŒλ“œ νƒ€μž„ 사전 계산 +- `generateStaticParams()` μ‚¬μš©ν•œ 정적 경둜 생성 +- μ»΄ν¬λ„ŒνŠΈ κ°„ props μ΅œμ†Œν™” + +## 디렉토리 ꡬ쑰 + +``` +src/ +β”œβ”€β”€ app/ # Next.js App Router +β”‚ β”œβ”€β”€ [category]/ # μΉ΄ν…Œκ³ λ¦¬ νŽ˜μ΄μ§€ +β”‚ β”‚ β”œβ”€β”€ [slug]/ # 포슀트 상세 +β”‚ β”‚ └── _components/ # νŽ˜μ΄μ§€λ³„ μ»΄ν¬λ„ŒνŠΈ +β”‚ └── _components/ # μ „μ—­ μ»΄ν¬λ„ŒνŠΈ +β”œβ”€β”€ domain/blog/ # 도메인 둜직 +β”‚ β”œβ”€β”€ index.ts # 곡개 API + 캐싱 +β”‚ β”œβ”€β”€ types.ts # νƒ€μž… μ •μ˜ +β”‚ └── logic/ # λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 +β”‚ β”œβ”€β”€ posts.ts +β”‚ β”œβ”€β”€ categories.ts +β”‚ └── tags.ts +└── contents/ # MDX μ½˜ν…μΈ  + β”œβ”€β”€ dev/ + └── life/ +``` + +## κ΅¬ν˜„ 원칙 + +1. **도메인 API μ‚¬μš©**: 직접 파일 μ‹œμŠ€ν…œ μ ‘κ·Ό κΈˆμ§€ +2. **νƒ€μž… μ•ˆμ „μ„±**: λͺ¨λ“  데이터 흐름 νƒ€μž… 보μž₯ +3. **λͺ¨λ“ˆ 레벨 캐싱**: λΉŒλ“œ νƒ€μž„ 데이터 사전 λ‘œλ“œ +4. **μ»΄ν¬λ„ŒνŠΈ λ‹¨μˆœν™”**: 단일 μ±…μž„ 원칙 \ No newline at end of file diff --git a/.context/git-workflow.md b/.context/git-workflow.md new file mode 100644 index 0000000..39b772d --- /dev/null +++ b/.context/git-workflow.md @@ -0,0 +1,89 @@ +# Git μ›Œν¬ν”Œλ‘œμš° + +브랜치 μ „λž΅, 컀밋 μ»¨λ²€μ…˜, PR ν”„λ‘œμ„ΈμŠ€λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. + +## 브랜치 μ „λž΅ + +### κΈ°λ³Έ 브랜치 +- **main**: 배포용 μ•ˆμ •ν™” 브랜치 +- **feature/**: κΈ°λŠ₯ 개발용 브랜치 +- **fix/**: 버그 μˆ˜μ •μš© 브랜치 +- **docs/**: λ¬Έμ„œ μž‘μ—…μš© 브랜치 + +### 브랜치 넀이밍 +```bash +# κΈ°λŠ₯ 개발 (μ‹€μ œ ν”„λ‘œμ νŠΈ νŒ¨ν„΄) +feat/entities +feat/post-navigation +feat/entity-optimization-and-category-system + +# 버그 μˆ˜μ • +fix/resolve-navigation-issue +fix/correct-typo-in-header + +# λ¬Έμ„œ/μ„€μ • μž‘μ—… +docs/claude-pr-workflow +config/github-templates +config/tailwind-setup +``` + +## 컀밋 μ»¨λ²€μ…˜ + +### κΈ°λ³Έ ν˜•μ‹ +``` +type: subject + +body (optional) + +footer (optional) +``` + +### 컀밋 νƒ€μž… +- **feat**: μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€ +- **fix**: 버그 μˆ˜μ • +- **docs**: λ¬Έμ„œ μˆ˜μ • +- **style**: μ½”λ“œ ν¬λ§·νŒ…, μ„Έλ―Έμ½œλ‘  λˆ„λ½ λ“± +- **refactor**: μ½”λ“œ λ¦¬νŒ©ν† λ§ +- **test**: ν…ŒμŠ€νŠΈ μΆ”κ°€ λ˜λŠ” μˆ˜μ • +- **chore**: λΉŒλ“œ ν”„λ‘œμ„ΈμŠ€ λ˜λŠ” 보쑰 도ꡬ λ³€κ²½ + +### 컀밋 λ©”μ‹œμ§€ μ˜ˆμ‹œ (μ‹€μ œ ν”„λ‘œμ νŠΈ νŒ¨ν„΄) +```bash +# κΈ°λŠ₯ μΆ”κ°€ +feat: 톡합 blog entity ꡬ쑰 및 SSG μ΅œμ ν™” (#10) + +# κΈ°λŠ₯ μΆ”κ°€ (상세) +feat: 포슀트 λ„€λΉ„κ²Œμ΄μ…˜ 및 κ΄€λ ¨ 포슀트 μ‹œμŠ€ν…œ κ΅¬ν˜„ (#9) + +도메인 둜직 기반 포슀트 λ„€λΉ„κ²Œμ΄μ…˜ κ΅¬ν˜„ +- 이전/λ‹€μŒ 포슀트 μ°ΎκΈ° +- νƒœκ·Έ 기반 κ΄€λ ¨ 포슀트 μΆ”μ²œ +- λͺ¨λ“ˆ 레벨 캐싱 μ΅œμ ν™” + +# 버그 μˆ˜μ • +fix: .nojekyll 파일 μΆ”κ°€ + +# λ¬Έμ„œ μˆ˜μ • +docs: 리넀이밍 및 frontmatter μΆ”κ°€ + +# CI/CD μž‘μ—… +ci: GitHub Actions 배포 μ›Œν¬ν”Œλ‘œμš°μ— enablement μΆ”κ°€ +``` + +## PR ν”„λ‘œμ„ΈμŠ€ + +### PR 생성 μ „ 체크리슀트 +- [ ] `pnpm test` 톡과 확인 +- [ ] `pnpm biome:check` 톡과 확인 +- [ ] `pnpm type` 톡과 확인 +- [ ] 컀밋 λ©”μ‹œμ§€ 정리 μ™„λ£Œ +- [ ] κ΄€λ ¨ 이슈 μ—°κ²° + +### PR ν…œν”Œλ¦Ώ +```markdown +## 변경사항 μš”μ•½ + + +## λ³€κ²½ 이유 + +``` \ No newline at end of file diff --git a/.context/project-overview.md b/.context/project-overview.md new file mode 100644 index 0000000..70e43e5 --- /dev/null +++ b/.context/project-overview.md @@ -0,0 +1,115 @@ +# ν”„λ‘œμ νŠΈ κ°œμš” + +Next.js 15 기반의 정적 λΈ”λ‘œκ·Έ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ, MDXλ₯Ό μ‚¬μš©ν•œ μ½˜ν…μΈ  관리와 도메인 주도 섀계λ₯Ό λ”°λ¦…λ‹ˆλ‹€. + +## 기술 μŠ€νƒ + +- **ν”„λ ˆμž„μ›Œν¬**: Next.js 15 (App Router) +- **μ½˜ν…μΈ **: MDX with rehype-pretty-code +- **μŠ€νƒ€μΌλ§**: Tailwind CSS 4.0 + shadcn/ui +- **μ–Έμ–΄**: TypeScript (엄격 λͺ¨λ“œ) +- **μ½”λ“œ ν’ˆμ§ˆ**: Biome (formatting/linting) +- **ν…ŒμŠ€νŒ…**: Vitest +- **νŒ¨ν‚€μ§€ λ§€λ‹ˆμ €**: pnpm +- **κ·Έλž˜ν”„**: graphology (νƒœκ·Έ 관계 뢄석) + +## μ•„ν‚€ν…μ²˜ + +### 디렉토리 ꡬ쑰 + +``` +src/ +β”œβ”€β”€ app/ # Next.js App Router 루트 +β”‚ β”œβ”€β”€ [category]/ # μΉ΄ν…Œκ³ λ¦¬λ³„ 포슀트 λͺ©λ‘ νŽ˜μ΄μ§€ +β”‚ β”‚ β”œβ”€β”€ [slug]/ # κ°œλ³„ 포슀트 상세 νŽ˜μ΄μ§€ +β”‚ β”‚ β”‚ └── page.tsx +β”‚ β”‚ β”œβ”€β”€ _components/ # μΉ΄ν…Œκ³ λ¦¬/포슀트 κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ +β”‚ β”‚ └── page.tsx +β”‚ β”œβ”€β”€ _components/ # μ „μ—­ 곡톡 μ»΄ν¬λ„ŒνŠΈ +β”‚ β”œβ”€β”€ _fonts/ # 폰트 μ„€μ • +β”‚ β”œβ”€β”€ _lib/ # 곡톡 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ +β”‚ β”œβ”€β”€ about/ # About νŽ˜μ΄μ§€ +β”‚ β”œβ”€β”€ layout.tsx # 루트 λ ˆμ΄μ•„μ›ƒ +β”‚ └── page.tsx # 메인 νŽ˜μ΄μ§€ +β”œβ”€β”€ contents/ # MDX λΈ”λ‘œκ·Έ 포슀트 원본 파일 +β”‚ β”œβ”€β”€ dev/ # 개발 κ΄€λ ¨ 포슀트 +β”‚ β”‚ β”œβ”€β”€ category.json # μΉ΄ν…Œκ³ λ¦¬ 메타데이터 +β”‚ β”‚ └── *.mdx +β”‚ └── life/ # 일상 κ΄€λ ¨ 포슀트 +β”‚ β”œβ”€β”€ category.json +β”‚ └── *.mdx +β”œβ”€β”€ entities/ # 도메인 μ—”ν‹°ν‹° (톡합) +β”‚ └── blog/ # λΈ”λ‘œκ·Έ 도메인 톡합 ꡬ쑰 +β”‚ β”œβ”€β”€ index.ts # 곡개 API + λͺ¨λ“ˆ 레벨 캐싱 +β”‚ β”œβ”€β”€ types.ts # λͺ¨λ“  νƒ€μž… μ •μ˜ +β”‚ └── logic/ # λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 뢄리 +β”‚ β”œβ”€β”€ posts.ts # 포슀트 κ΄€λ ¨ 둜직 +β”‚ β”œβ”€β”€ categories.ts # μΉ΄ν…Œκ³ λ¦¬ κ΄€λ ¨ 둜직 +β”‚ └── tags.ts # νƒœκ·Έ κ΄€λ ¨ 둜직 +β”œβ”€β”€ mdx-components.tsx # MDX λ Œλ”λ§ μ‹œ μ‚¬μš©ν•  μ»€μŠ€ν…€ μ»΄ν¬λ„ŒνŠΈ +└── test/ # ν…ŒμŠ€νŠΈ κ΄€λ ¨ μ„€μ • +``` + +### 데이터 흐름 + +1. **λΉŒλ“œ νƒ€μž„**: MDX 파일 β†’ gray-matter νŒŒμ‹± β†’ λͺ¨λ“ˆ 레벨 캐싱 +2. **μ—”ν‹°ν‹° λ ˆμ΄μ–΄**: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 처리 (νƒœκ·Έ κ·Έλž˜ν”„, κ΄€λ ¨ 포슀트 λ“±) +3. **App Router**: μ„œλ²„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ μΊμ‹œλœ 데이터 ν™œμš©ν•˜μ—¬ λ Œλ”λ§ + +### μ„±λŠ₯ μ΅œμ ν™” + +#### SSG μ΅œμ ν™” +- **λͺ¨λ“ˆ 레벨 캐싱**: λΉŒλ“œ νƒ€μž„μ— λͺ¨λ“  포슀트, μΉ΄ν…Œκ³ λ¦¬, νƒœκ·Έ 데이터 ν•œ 번만 λ‘œλ“œ +- **사전 계산**: νƒœκ·Έ κ·Έλž˜ν”„, ν΄λŸ¬μŠ€ν„°, κ΄€λ ¨ 포슀트 관계 λΉŒλ“œ νƒ€μž„μ— 계산 +- **정적 생성**: `generateStaticParams()`둜 λͺ¨λ“  경둜 사전 생성 + +```typescript +// λͺ¨λ“ˆ 레벨 캐싱 μ˜ˆμ‹œ +export const allPosts = await getAllPosts() +export const allCategories = await getAllCategories() +export const tagGraph = createTagGraph(allPosts, allTags) +export const tagClusters = createTagClusters(tagGraph) +``` + +## 넀이밍 μ»¨λ²€μ…˜ + +- **λΌμš°νŠΈκ°€ μ•„λ‹Œ 폴더**: μ–Έλ”μŠ€μ½”μ–΄ 접두사 (`_components`, `_lib`) +- **μ»΄ν¬λ„ŒνŠΈ μŠ€μ½”ν”„**: + - μ „μ—­: `src/app/_components/` + - νŽ˜μ΄μ§€λ³„: `src/app/[route]/_components/` +- **파일λͺ…**: kebab-case λ˜λŠ” PascalCase (μ»΄ν¬λ„ŒνŠΈ) +- **λ³€μˆ˜/ν•¨μˆ˜**: camelCase +- **νƒ€μž…/μΈν„°νŽ˜μ΄μŠ€**: PascalCase +- **μƒμˆ˜**: UPPER_SNAKE_CASE + +## μ½˜ν…μΈ  관리 + +### MDX ꡬ쑰 +```yaml +--- +title: '포슀트 제λͺ©' +date: 2025-01-17 +tags: ['tag1', 'tag2'] +description: '포슀트 μ„€λͺ…' +--- + +# 포슀트 λ‚΄μš© +``` + +### μΉ΄ν…Œκ³ λ¦¬ 메타데이터 (category.json) +```json +{ + "name": "Development", + "description": "개발 κ΄€λ ¨ 포슀트", + "color": "blue", + "icon": "πŸ’»" +} +``` + +## λΌμš°νŒ… ꡬ쑰 + +- `/` - 메인 νŽ˜μ΄μ§€ (Hero, μΉ΄ν…Œκ³ λ¦¬, μ΅œμ‹  포슀트, νƒœκ·Έ) +- `/{category}` - μΉ΄ν…Œκ³ λ¦¬λ³„ 포슀트 λͺ©λ‘ (dev, life) +- `/{category}/{slug}` - κ°œλ³„ 포슀트 상세 νŽ˜μ΄μ§€ +- `/tags` - νƒœκ·Έ λͺ©λ‘ 및 필터링 +- `/about` - About νŽ˜μ΄μ§€ \ No newline at end of file diff --git a/.context/styling-guide.md b/.context/styling-guide.md new file mode 100644 index 0000000..92f8184 --- /dev/null +++ b/.context/styling-guide.md @@ -0,0 +1,85 @@ +# μŠ€νƒ€μΌλ§ κ°€μ΄λ“œ + +Tailwind CSS, shadcn/ui, λ””μžμΈ μ‹œμŠ€ν…œ κ°€μ΄λ“œλΌμΈμ„ μ •μ˜ν•©λ‹ˆλ‹€. + +## κΈ°λ³Έ 원칙 + +- ꡬ쑰λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 것 μ΄μƒμ˜ μƒμ„Έν•œ UI μŠ€νƒ€μΌλ§μ€ μœ μ €μ˜ μš”μ²­μ΄ μ—†μœΌλ©΄ μ§„ν–‰ν•˜μ§€ μ•ŠμŒ + +### μŠ€νƒ€μΌλ§ μš°μ„ μˆœμœ„ +1. **κΈ°λŠ₯ ꡬ쑰 μš°μ„ **: μŠ€νƒ€μΌλ§λ³΄λ‹€ 둜직과 ꡬ쑰 집쀑 +2. **일관성**: 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μΌκ΄€λœ λ””μžμΈ μ–Έμ–΄ +3. **μ ‘κ·Όμ„±**: λͺ¨λ“  μ‚¬μš©μžκ°€ μ ‘κ·Ό κ°€λŠ₯ν•œ λ””μžμΈ +4. **λ°˜μ‘ν˜•**: λͺ¨λ°”일 퍼슀트 μ ‘κ·Ό + +## shadcn/ui μ»΄ν¬λ„ŒνŠΈ μ‚¬μš© + +### μ„€μΉ˜ 및 μ‚¬μš© +```bash +npx shadcn-ui@latest add button +npx shadcn-ui@latest add card +npx shadcn-ui@latest add input +``` + +### μ‹€μ œ ν”„λ‘œμ νŠΈ νŒ¨ν„΄ + +#### 색상 클래슀 객체 νŒ¨ν„΄ (CategoryBadge) +```typescript +const colorClasses = { + blue: 'bg-blue-100 text-blue-800 border-blue-200 hover:bg-blue-200', + green: 'bg-green-100 text-green-800 border-green-200 hover:bg-green-200', + gray: 'bg-gray-100 text-gray-800 border-gray-200 hover:bg-gray-200', +} + +const className = cn( + 'inline-flex items-center gap-1 px-2 py-1 text-sm font-medium rounded-md border transition-colors', + colorClasses[category.color as keyof typeof colorClasses] || colorClasses.gray +) +``` + +#### ν•΄μ‹œ 기반 색상 ν• λ‹Ή (TagBadge) +```typescript +// getTagColor.ts - νƒœκ·Έλ³„ 고유 색상 생성 +const TAG_COLORS = [ + 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300', + 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300', + // ... 더 λ§Žμ€ 색상 +] as const + +export function getTagColor(tag: string): string { + const hash = /* ν•΄μ‹œ ν•¨μˆ˜ κ΅¬ν˜„ */ + const index = Math.abs(hash) % TAG_COLORS.length + return TAG_COLORS[index] +} +``` + +#### CVA 기반 λ³€ν˜• μ‹œμŠ€ν…œ (Badge) +```typescript +const badgeVariants = cva( + 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium', + { + variants: { + variant: { + default: 'border-transparent bg-primary text-primary-foreground', + secondary: 'border-transparent bg-secondary text-secondary-foreground', + outline: 'text-foreground hover:bg-accent hover:text-accent-foreground', + }, + }, + defaultVariants: { variant: 'default' }, + } +) +``` + +### μ„±λŠ₯ 고렀사항 +- **클래슀 동적 생성**: `cn()` μœ ν‹Έλ¦¬ν‹° μ‚¬μš© +- **쑰건뢀 μŠ€νƒ€μΌ**: μ‚Όν•­ μ—°μ‚°μžλ³΄λ‹€ `cn()` ν™œμš© +- **μž¬μ‚¬μš©μ„±**: 곡톡 μŠ€νƒ€μΌ μ»΄ν¬λ„ŒνŠΈν™” + +```typescript +// μ˜¬λ°”λ₯Έ μ˜ˆμ‹œ +const buttonStyles = cn( + "px-4 py-2 rounded-md font-medium transition-colors", + isActive && "bg-blue-600 text-white", + isDisabled && "opacity-50 cursor-not-allowed" +) +``` \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index ae9bfad..eb0b71e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,364 +1,86 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +Next.js 15 기반 정적 λΈ”λ‘œκ·Έμ˜ μ•„ν‚€ν…μ²˜ μ§€μΉ¨κ³Ό μž‘μ—… κ°€μ΄λ“œμž…λ‹ˆλ‹€. -## ν”„λ‘œμ νŠΈ κ°œμš” +## πŸ”§ 핡심 원칙 -Next.js 15 기반의 정적 λΈ”λ‘œκ·Έ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ, MDXλ₯Ό μ‚¬μš©ν•œ μ½˜ν…μΈ  관리와 도메인 주도 섀계λ₯Ό λ”°λ¦…λ‹ˆλ‹€. +### ⚠️ κ°€μž₯ μ€‘μš”ν•œ μ§€μΉ¨ +- **μŠ€μ½”ν”„λ³„ CLAUDE.md μœ μ§€**: μž‘μ—…ν•  λ•Œλ§ˆλ‹€ ν•΄λ‹Ή λ””λ ‰ν† λ¦¬μ˜ CLAUDE.mdλ₯Ό μ—…λ°μ΄νŠΈ +- **계측적 μ»¨ν…μŠ€νŠΈ 상속**: ν•˜μœ„ CLAUDE.mdλŠ” μƒμœ„ λ¬Έμ„œλ“€μ„ importν•˜μ—¬ 전체 λ§₯락 확보 +- **μ»¨ν…μŠ€νŠΈ μ§€μ—­ν™”**: 각 λ””λ ‰ν† λ¦¬λ³„λ‘œ ꡬ체적이고 μ •ν™•ν•œ μž‘μ—… μ»¨ν…μŠ€νŠΈ μœ μ§€ +- **μ‹€μ‹œκ°„ μ—…λ°μ΄νŠΈ**: μ½”λ“œ λ³€κ²½κ³Ό λ™μ‹œμ— ν•΄λ‹Ή μ˜μ—­μ˜ CLAUDE.md μ¦‰μ‹œ 반영 -## 기술 μŠ€νƒ +### μ œμ•½μ‚¬ν•­ +- κ°œλ°œμ„œλ²„/λΉŒλ“œ μ‹€ν–‰ κΈˆμ§€ +- μ½”λ“œμ— μ‹€μ œ λ ˆν„°λ§ λŒ€μ‹  placeholder μ‚¬μš© +- μŠ€μ½”ν”„λ³„ CLAUDE.mdλŠ” 50쀄 μ΄λ‚΄λ‘œ κ°„κ²° μž‘μ„± -- **ν”„λ ˆμž„μ›Œν¬**: Next.js 15 (App Router) -- **μ½˜ν…μΈ **: MDX with rehype-pretty-code -- **μŠ€νƒ€μΌλ§**: Tailwind CSS + shadcn/ui -- **μ–Έμ–΄**: TypeScript -- **μ½”λ“œ ν’ˆμ§ˆ**: Biome (formatting/linting) -- **ν…ŒμŠ€νŒ…**: Vitest -- **νŒ¨ν‚€μ§€ λ§€λ‹ˆμ €**: pnpm -- **κ·Έλž˜ν”„**: graphology +## πŸ—οΈ μ•„ν‚€ν…μ²˜ 원칙 -## 디렉토리 ꡬ쑰 +### 도메인 주도 섀계 (DDD) +- **도메인 둜직**: `src/domain/` 에 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 쀑앙집쀑 +- **UI 뢄리**: `src/app/` 에 ν”„λ ˆμ  ν…Œμ΄μ…˜ λ ˆμ΄μ–΄ 뢄리 +- **데이터 흐름**: 도메인 β†’ UI 단방ν–₯ μ˜μ‘΄μ„± -``` -src/ -β”œβ”€β”€ app/ # Next.js App Router 루트 -β”‚ β”œβ”€β”€ [category]/ # μΉ΄ν…Œκ³ λ¦¬λ³„ 포슀트 λͺ©λ‘ νŽ˜μ΄μ§€ -β”‚ β”‚ β”œβ”€β”€ [slug]/ # κ°œλ³„ 포슀트 상세 νŽ˜μ΄μ§€ -β”‚ β”‚ β”‚ └── page.tsx -β”‚ β”‚ β”œβ”€β”€ _components/ # μΉ΄ν…Œκ³ λ¦¬/포슀트 κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ -β”‚ β”‚ └── page.tsx -β”‚ β”œβ”€β”€ _components/ # μ „μ—­ 곡톡 μ»΄ν¬λ„ŒνŠΈ -β”‚ β”œβ”€β”€ _fonts/ # 폰트 μ„€μ • -β”‚ β”œβ”€β”€ _lib/ # 곡톡 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ -β”‚ β”œβ”€β”€ about/ # About νŽ˜μ΄μ§€ -β”‚ β”œβ”€β”€ layout.tsx # 루트 λ ˆμ΄μ•„μ›ƒ -β”‚ └── page.tsx # 메인 νŽ˜μ΄μ§€ -β”œβ”€β”€ contents/ # MDX λΈ”λ‘œκ·Έ 포슀트 원본 파일 -β”œβ”€β”€ entities/ # 도메인 μ—”ν‹°ν‹° (λΉ„μ¦ˆλ‹ˆμŠ€ 둜직) -β”‚ β”œβ”€β”€ categories/ # μΉ΄ν…Œκ³ λ¦¬ 도메인 둜직 -β”‚ β”œβ”€β”€ posts/ # 포슀트 도메인 둜직 -β”‚ └── tags/ # νƒœκ·Έ 도메인 둜직 -β”œβ”€β”€ mdx-components.tsx # MDX λ Œλ”λ§ μ‹œ μ‚¬μš©ν•  μ»€μŠ€ν…€ μ»΄ν¬λ„ŒνŠΈ -└── test/ # ν…ŒμŠ€νŠΈ κ΄€λ ¨ μ„€μ • -``` - -## 넀이밍 μ»¨λ²€μ…˜ - -- **λΌμš°νŠΈκ°€ μ•„λ‹Œ 폴더**: μ–Έλ”μŠ€μ½”μ–΄ 접두사 μ‚¬μš© (`_components`, `_hooks`) -- **μ»΄ν¬λ„ŒνŠΈ μŠ€μ½”ν”„**: - - μ „μ—­: `src/app/_components/` - - νŽ˜μ΄μ§€λ³„: `src/app/[route]/_components/` -- **파일λͺ…**: kebab-case λ˜λŠ” PascalCase (μ»΄ν¬λ„ŒνŠΈ) - -## 도메인 μ•„ν‚€ν…μ²˜ - -### μ—”ν‹°ν‹° ꡬ쑰 -- `src/entities/posts/`: 포슀트 κ΄€λ ¨ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 - - `index.ts`: 곡개 API - - `types.ts`: νƒ€μž… μ •μ˜ - - `logic.ts`: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 - - `*.test.ts`: ν…ŒμŠ€νŠΈ 파일 - -### 데이터 흐름 -1. MDX 파일 (`src/contents/`) β†’ gray-matter νŒŒμ‹± -2. μ—”ν‹°ν‹° λ ˆμ΄μ–΄μ—μ„œ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 처리 -3. App Router νŽ˜μ΄μ§€μ—μ„œ λ Œλ”λ§ - -## μŠ€νƒ€μΌλ§ κ°€μ΄λ“œλΌμΈ - -### Tailwind CSS -- **컬러 νŒ”λ ˆνŠΈ**: `stone` 계열 μ‚¬μš© (`text-stone-900`, `border-stone-200`) -- **λ°˜μ‘ν˜•**: λͺ¨λ°”일 퍼슀트 μ ‘κ·Ό -- **닀크λͺ¨λ“œ**: CSS λ³€μˆ˜ 기반 ν…Œλ§ˆ 지원 - -### shadcn/ui μ»΄ν¬λ„ŒνŠΈ -- **μ„€μΉ˜**: `npx shadcn@latest add ` -- **μœ„μΉ˜**: `src/app/_components/ui/` -- **μŠ€νƒ€μΌ**: "new-york" μŠ€νƒ€μΌ, stone 베이슀 컬러 -- **μ•„μ΄μ½˜**: Lucide React μ‚¬μš© - -## μ½˜ν…μΈ  관리 - -### μ½˜ν…μΈ  μœ„μΉ˜ -`src/contents/*.mdx` - -### MDX ꡬ쑰 -```yaml ---- -title: '포슀트 제λͺ©' -slug: 'post-slug' -date: 2025-01-01 -tags: ['tag1', 'tag2'] ---- - -# 포슀트 λ‚΄μš© -``` +### λͺ¨λ“ˆ 레벨 캐싱 +- **λΉŒλ“œ νƒ€μž„ μ΅œμ ν™”**: λͺ¨λ“  데이터 사전 λ‘œλ“œ 및 캐싱 +- **SSG μ΅œμ ν™”**: 정적 μ‚¬μ΄νŠΈ 생성을 μœ„ν•œ μ„±λŠ₯ μ΅œμ ν™” +- **관계 사전 계산**: νƒœκ·Έ κ·Έλž˜ν”„, ν΄λŸ¬μŠ€ν„°, κ΄€λ ¨ 포슀트 λΉŒλ“œ νƒ€μž„ 계산 -## μž‘μ—… μ§„ν–‰ 원칙 +### νƒ€μž… μ•ˆμ „μ„± +- **TypeScript 엄격 λͺ¨λ“œ**: `any` νƒ€μž… κΈˆμ§€ +- **λͺ…μ‹œμ  νƒ€μž…**: λͺ¨λ“  μΈν„°νŽ˜μ΄μŠ€μ™€ ν•¨μˆ˜ μ‹œκ·Έλ‹ˆμ²˜ λͺ…μ‹œ +- **도메인 νƒ€μž… 쀑심**: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직의 νƒ€μž… μš°μ„  섀계 -- μž‘μ—…μ˜ λ‹¨μœ„λ₯Ό κ°€λŠ₯ν•˜λ©΄ μž‘κ²Œ μ„€μ • -- μ§€μ‹œκ°€ λͺ¨ν˜Έν•˜λ‹€κ³  λŠκ»΄μ§€λ©΄ 질문 ν›„ μ§„ν–‰ +## πŸ“ 전체 ꡬ쑰 λ§΅ -## κ΅¬ν˜„ 원칙 - -### 점진적 개발 -- ν•œ λ²ˆμ— ν•˜λ‚˜μ˜ νƒœμŠ€ν¬λ§Œ μ§‘μ€‘ν•˜μ—¬ κ΅¬ν˜„ -- νƒœμŠ€ν¬ μ™„λ£Œ ν›„ μ‚¬μš©μž κ²€ν†  λŒ€κΈ°, μžλ™μœΌλ‘œ λ‹€μŒ νƒœμŠ€ν¬ μ§„ν–‰ν•˜μ§€ μ•ŠμŒ -- 각 λ‹¨κ³„μ—μ„œ 이전 λ‹¨κ³„μ˜ 결과물을 기반으둜 ꡬ좕 - -### μ½”λ“œ ν’ˆμ§ˆ -- TypeScript 엄격 λͺ¨λ“œ μ€€μˆ˜, any νƒ€μž… μ‚¬μš© κΈˆμ§€ -- μ»΄ν¬λ„ŒνŠΈλŠ” 단일 μ±…μž„ 원칙 적용 -- Props μΈν„°νŽ˜μ΄μŠ€ λͺ…μ‹œμ  μ •μ˜ -- μ μ ˆν•œ κΈ°λ³Έκ°’ μ„€μ • - -### ν…ŒμŠ€νŠΈ μš°μ„  -- λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 κ΅¬ν˜„ μ‹œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ ν•¨κ»˜ μž‘μ„± -- AAA νŒ¨ν„΄ (Arrange, Act, Assert) μ€€μˆ˜ -- μ˜λ―ΈμžˆλŠ” ν…ŒμŠ€νŠΈλͺ… μ‚¬μš© - -## κ΅¬ν˜„ νŒ¨ν„΄ - -### μ»΄ν¬λ„ŒνŠΈ 섀계 -- **단일 μ±…μž„ 원칙**: ν•˜λ‚˜μ˜ μ»΄ν¬λ„ŒνŠΈλŠ” ν•˜λ‚˜μ˜ μ—­ν• λ§Œ -- **Props μΈν„°νŽ˜μ΄μŠ€**: λͺ¨λ“  props에 λŒ€ν•œ λͺ…μ‹œμ  νƒ€μž… μ •μ˜ -- **κΈ°λ³Έκ°’ μ„€μ •**: 선택적 props에 λŒ€ν•œ μ μ ˆν•œ κΈ°λ³Έκ°’ -- **μ»€μŠ€ν…€ν›…**: λ‘œμ§μ€ μ»€μŠ€ν…€ν›…μœΌλ‘œ 뢄리함 -- **μ»΄ν¬λ„ŒνŠΈ 뢄리**: 50쀄 μ΄μƒμ˜ μ»΄ν¬λ„ŒνŠΈλŠ” 뢄리 κ³ λ € -- **ν…ŒμŠ€νŠΈ κΈˆμ§€**: μ»΄ν¬λ„ŒνŠΈ μžμ²΄λŠ” ν…ŒμŠ€νŠΈν•˜μ§€ μ•Šμ•„μ•Όν•¨ - -```typescript -// 1. μΈν„°νŽ˜μ΄μŠ€ μ •μ˜ -interface ComponentProps { - required: string - optional?: boolean - children?: React.ReactNode -} - -// 2. μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„ -export function Component({ - required, - optional = false, - children -}: ComponentProps) { - // κ΅¬ν˜„ -} ``` - -### λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 κ΅¬ν˜„ - -- **λͺ¨λ“ˆν™”**: ESM λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ ν™œμš©ν•΄ 적절히 μΈν„°νŽ˜μ΄μŠ€ λ…ΈμΆœ -- **ν…ŒμŠ€νŠΈ**: λΉ„μ¦ˆλ‹ˆμŠ€ 둜직의 κ²½μš°μ—” ν…ŒμŠ€νŠΈλ₯Ό 해야함 - -```typescript -// 1. νƒ€μž… μ •μ˜ -export type DataType = { - id: string - value: string -} - -// 2. 둜직 ν•¨μˆ˜ κ΅¬ν˜„ -export function processData(data: DataType[]): DataType[] { - // κ΅¬ν˜„ -} - -// 3. ν…ŒμŠ€νŠΈ μž‘μ„± -describe('processData', () => { - it('should process data correctly', () => { - // ν…ŒμŠ€νŠΈ κ΅¬ν˜„ - }) -}) +/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ domain/ # 🧠 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 +β”‚ β”‚ └── blog/ # λΈ”λ‘œκ·Έ 도메인 (SSG 캐싱, νƒœκ·Έ κ·Έλž˜ν”„) +β”‚ β”œβ”€β”€ app/ # 🎨 UI λ ˆμ΄μ–΄ (Next.js App Router) +β”‚ β”‚ β”œβ”€β”€ [category]/ # 동적 λΌμš°νŒ… (μΉ΄ν…Œκ³ λ¦¬λ³„ νŽ˜μ΄μ§€) +β”‚ β”‚ β”œβ”€β”€ _components/ # μ „μ—­ 곡톡 μ»΄ν¬λ„ŒνŠΈ +β”‚ β”‚ └── _lib/ # UI μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ +β”‚ └── contents/ # πŸ“ MDX μ½˜ν…μΈ  +β”œβ”€β”€ .context/ # πŸ“š ν”„λ‘œμ νŠΈ λ¬Έμ„œ +└── CLAUDE.md # πŸ—ΊοΈ λ§ˆμŠ€ν„° μ•„ν‚€ν…μ²˜ (이 λ¬Έμ„œ) ``` -### Next.js μ•„ν‚€ν…μ²˜ μ€€μˆ˜ -- App Router의 이점과 μ„œλ²„ μ»΄ν¬λ„ŒνŠΈλ₯Ό 적극 ν™œμš© - -### κΈ°λŠ₯ ꡬ쑰 μš°μ„  -- μŠ€νƒ€μΌλ§λ³΄λ‹€ κΈ°λŠ₯적 ꡬ쑰와 λ‘œμ§μ— 집쀑 -- μ»΄ν¬λ„ŒνŠΈμ˜ μ—­ν• κ³Ό μ±…μž„μ„ λͺ…ν™•νžˆ μ •μ˜ -- 데이터 흐름과 μƒνƒœ 관리 ꡬ쑰 μš°μ„  섀계 -- UIλŠ” 기본적인 λ ˆμ΄μ•„μ›ƒλ§Œ κ΅¬ν˜„ν•˜κ³  μ„ΈλΆ€ μŠ€νƒ€μΌλ§μ€ ν›„μˆœμœ„ +## πŸ”— μŠ€μ½”ν”„λ³„ CLAUDE.md 링크 -### μ•„ν‚€ν…μ²˜ 쀑심 μ ‘κ·Ό -- 도메인 둜직과 UI 둜직의 λͺ…ν™•ν•œ 뢄리 -- μ»΄ν¬λ„ŒνŠΈ κ°„μ˜ μ˜μ‘΄μ„±κ³Ό 데이터 전달 ꡬ쑰 섀계 -- μž¬μ‚¬μš© κ°€λŠ₯ν•œ 둜직의 좔상화 -- ν™•μž₯ κ°€λŠ₯ν•œ ꡬ쑰둜 섀계 +### 1뎁슀 - μ£Όμš” μ˜μ—­ +- `src/domain/CLAUDE.md` - 도메인 둜직 전체 λ§₯락 +- `src/app/CLAUDE.md` - App Router ꡬ쑰와 νŒ¨ν„΄ +- `src/contents/CLAUDE.md` - MDX μ½˜ν…μΈ  관리 κ·œμΉ™ -### μ΅œμ†Œ μŠ€νƒ€μΌλ§ -- ꡬ쑰화에 ν•„μš”ν•œ μ΅œμ†Œν•œμ˜ μŠ€νƒ€μΌλ§ κ°€λŠ₯ -- ν•„μš”μ‹œ shadcn/ui의 μ»΄ν¬λ„ŒνŠΈλ₯Ό 이용 +### 2뎁슀 - μ„ΈλΆ€ κ΅¬ν˜„ +- `src/domain/blog/CLAUDE.md` - λΈ”λ‘œκ·Έ 도메인 세뢀사항 +- `src/app/[category]/CLAUDE.md` - 동적 λΌμš°νŒ… ꡬ쑰 +- `src/app/_components/CLAUDE.md` - μ „μ—­ μ»΄ν¬λ„ŒνŠΈ μ—­ν•  +- `src/app/_lib/CLAUDE.md` - μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ λͺ©μ  -```typescript -// ꡬ쑰에 μ§‘μ€‘ν•œ μ»΄ν¬λ„ŒνŠΈ μ˜ˆμ‹œ -
{/* κΈ°λ³Έ λ ˆμ΄μ•„μ›ƒλ§Œ */} -
- {/* κΈ°λŠ₯적 ꡬ쑰 μš°μ„  */} -
-
-
- {/* ... */} -
-
-
-``` - -### UI κ΅¬ν˜„ -- **ν”Œλ ˆμ΄μŠ€ν™€λ” μ‚¬μš©**: μ‹€μ œ μ½˜ν…μΈ  λŒ€μ‹  `[Page Title]`, `[Description]` λ“± μ‚¬μš© -- **μ‚¬μš©μž 승인**: UI ꡬ쑰 κ΅¬ν˜„ μ „ λͺ…μ‹œμ  μš”κ΅¬μ‚¬ν•­ 확인 -- **점진적 κ΅¬ν˜„**: ν•œ λ²ˆμ— λͺ¨λ“  κΈ°λŠ₯ κ΅¬ν˜„ν•˜μ§€ μ•ŠκΈ° +## πŸ“– μΆ”κ°€ ν”„λ‘œμ νŠΈ λ¬Έμ„œ -## μ ‘κ·Όμ„± 고렀사항 +### 개발 κ°€μ΄λ“œ +- @.context/development-guidelines.md - κ΅¬ν˜„ νŒ¨ν„΄, ν’ˆμ§ˆ κΈ°μ€€ +- @.context/styling-guide.md - Tailwind CSS, shadcn/ui κ°€μ΄λ“œ +- @.context/git-workflow.md - 브랜치 μ „λž΅, 컀밋 μ»¨λ²€μ…˜ -### ν•„μˆ˜ μš”μ†Œ -- μ‹œλ§¨ν‹± HTML νƒœκ·Έ μ‚¬μš© -- μ μ ˆν•œ ARIA λ ˆμ΄λΈ” -- ν‚€λ³΄λ“œ λ„€λΉ„κ²Œμ΄μ…˜ 지원 -- μΆ©λΆ„ν•œ 색상 λŒ€λΉ„ - -### κ΅¬ν˜„ μ˜ˆμ‹œ -```typescript -