Next.js 16 App Router와 MDX로 만든 개인 개발 블로그입니다. 글은 Git 서브모듈로 따로 관리하고, 모든 페이지를 빌드 시점에 정적으로 만들어 둡니다.
| 영역 | 사용 기술 |
|---|---|
| 프레임워크 | Next.js 16 App Router, Turbopack 빌드, React Compiler |
| UI | React 19, Tailwind CSS 4, next-themes, lucide-react |
| 언어 | TypeScript 6 strict |
| 콘텐츠 | MDX, Shiki 코드 하이라이팅 |
| 검증 | Zod (frontmatter 스키마 검증) |
| 테스트 | Vitest, React Testing Library, MSW, Playwright |
| 런타임 API | Upstash Redis (조회수) |
| 품질 도구 | ESLint 9, Prettier 3, Lefthook 2 |
Next.js 라우팅은 루트 app/이 맡고 라우트 파일은 src/의 구현을 재노출만 합니다. src/는 FSD(Feature-Sliced Design) 여섯 레이어로 나뉩니다. 위에서부터 app과 pages, widgets, features, entities, shared 순이고 위 레이어가 아래 레이어만 참조합니다. 슬라이스 밖과는 public API(index.ts)로만 연결합니다.
자세한 규칙과 디렉토리 구성은 CONTRIBUTING.md에 있습니다.
정적 생성 우선. 콘텐츠 페이지를 빌드 시점에 모두 만들어 둡니다. 런타임 CMS와 서버 검색, 클라이언트 캐시는 넣지 않았습니다. 런타임에 도는 것은 조회수를 세는 /api/views와 공유 카드를 그리는 /og 둘입니다.
조회수는 실패해도 페이지를 막지 않습니다. 저장소가 없거나 장애가 나면 0을 보여주고 기록은 건너뜁니다. 조회수 API 호출 자체가 실패하면 숫자 대신 대시를 띄웁니다.
글은 별도 저장소에 둡니다. contents/를 서브모듈로 분리해 소스 코드와 글의 커밋 이력이 섞이지 않습니다.
주소 규칙은 영역마다 다릅니다. 포스트 주소는 도구와 CDN 호환을 위해 영문만 씁니다. 태그와 시리즈는 한글을 허용하되 공백을 하이픈으로 바꿉니다.
테스트는 남길 근거가 있는 것만 둡니다. Testing Trophy를 따르되 층별 비율 목표는 두지 않습니다. 기준은 개수가 아니라 그 테스트가 빨간불을 냈을 때 무엇이 깨졌는지 이름으로 말할 수 있는가입니다.
| 문서 | 내용 |
|---|---|
| product/PRD.md | 무엇을 왜 만드는지, 범위와 목표 |
| product/SPEC.md | 기능이 정확히 어떻게 동작하는지 |
| product/ROADMAP.md | 아직 하지 않은 것과 뺀 것 |
| design/DESIGN.md | 디자인 방향, 톤과 색 |
| design/DESIGN-SPEC.md | 화면별 배치와 상태 |
| operations/RUNBOOK.md | 배포 절차와 장애 대응 |
| operations/SEO.md | 코드 밖에서 하는 검색 유입 작업 |
| CHANGELOG.md | 버전별 변경 내역 |
Vercel에 자동으로 배포됩니다. main에 머지하면 프로덕션에 올라가고, develop으로 PR을 열면 Preview가 만들어집니다.
배포 빌드는 pnpm build:vercel로 돌고 결과물은 .next/에 나옵니다. 로컬에서 같은 조건으로 확인하려면 pnpm build:strict를 씁니다. 그냥 pnpm build는 frontmatter를 어긴 글을 건너뛰기만 합니다. 조회수를 쓰려면 UPSTASH_REDIS_REST_URL과 UPSTASH_REDIS_REST_TOKEN을 환경 변수로 넣습니다. 넣지 않아도 빌드와 렌더는 정상 동작합니다.
환경 설정과 명령어, 코드 규약, Git 워크플로우는 CONTRIBUTING.md에 정리했습니다.
MIT License. 자세한 내용은 LICENSE를 참조하세요.