Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 88 additions & 36 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,90 @@
# Repository Guidelines

## Always-Follow Rules
- Keep changes minimal and scoped to the task. Do not edit unrelated files.
- Preserve the current MDX pipeline. MDX must stay on the custom webpack rule with `@mdx-js/loader` in `next.config.mjs`; do not switch to Next.js built-in MDX unless the whole content pipeline is intentionally migrated.
- Keep visualization-heavy UI in `src/components/visualization/`. Do not move that code to other folders without a clear architectural reason.
- Never use `any`. Use concrete types or `unknown` with narrowing.
- Never use arbitrary Tailwind values such as `p-[13px]`. Use standard utilities, shared tokens, and existing style patterns.
- Do not replace feature-first structure with flat shared folders. Keep code in `src/features/`, `src/shared/`, and `src/domains/` by responsibility.
- Preserve blog content structure as `posts/**/index.mdx` with nearby `meta.json`, including nested series directories when present.

## Project Structure
- `src/app/` — Next.js App Router pages, layouts, handlers
- `src/core/` — app-level providers and configuration composition
- `src/domains/` — cross-feature contracts and schemas
- `src/features/` — feature modules such as blog, home, resume, and search
- `src/shared/` — reusable UI, layout, analytics, SEO, and providers
- `src/components/visualization/` — animation and visualization-heavy components
- `src/styles/` — design tokens and global styles
- `posts/` — blog content, series entries, and metadata managed as nested `index.mdx` + `meta.json`
- `tests/` — Playwright end-to-end coverage
- `internal/` — scripts and tool configuration

## Development Commands
- `npm run dev` — run the local dev server with webpack
- `npm run build` — create the production build
- `npm run lint` — run ESLint on source files
- `npm run lint:css:syntax` — check CSS syntax rules
- `npm run test:unit` — run Vitest unit tests
- `npm run test:components` — run component-focused Vitest tests
- `npm run test:e2e` — run Playwright scenarios
- `npm run test:ci` — run the main CI-equivalent validation set

## Style & Testing
Use TypeScript with 2-space indentation, semicolons, single quotes, trailing commas (`es5`), and 80-column width; Prettier enforces this. Name components in `PascalCase`, hooks in `camelCase` with a `use` prefix, and tests as `*.test.ts` or `*.test.tsx`. Add targeted Vitest or Playwright coverage when changing logic, UI behavior, parsers, or app actions. Before opening a PR, run `npm run build` and the most relevant test command for the change.

## Commits & PRs
Use commit messages like `type(scope): concise description`, for example `fix(home): preview 배포 타입 오류 수정`. Common types include `feat`, `fix`, `refactor`, `test`, and `chore`. Use branch names like `codex/<task>`. Follow `.github/pull_request_template.md`, link related issues or PRs, and include screenshots when UI changes are visible. Confirm mobile/desktop and dark/light behavior when layout or navigation changes.
## 항상 지킬 규칙

- 변경은 작업 범위에 맞게 최소화한다. 관련 없는 파일은 수정하지 않는다.
- 현재 MDX 파이프라인을 보존한다. MDX는 `next.config.mjs`의 `@mdx-js/loader`
기반 커스텀 webpack rule에 남겨둔다. 전체 콘텐츠 파이프라인을 의도적으로
마이그레이션하지 않는 한 Next.js 내장 MDX로 바꾸지 않는다.
- 시각화 중심 UI는 `src/components/visualization/`에 둔다. 명확한
아키텍처 이유 없이 다른 폴더로 옮기지 않는다.
- `any`를 사용하지 않는다. 구체 타입을 쓰거나 `unknown`과 narrowing을
사용한다.
- `p-[13px]` 같은 arbitrary Tailwind value를 사용하지 않는다. 표준 utility,
shared token, 기존 스타일 패턴을 사용한다.
- feature-first 구조를 flat shared folder 구조로 바꾸지 않는다.
`src/features/`, `src/shared/`, `src/domains/`를 책임별로 유지한다.
- 블로그 콘텐츠 구조는 `posts/**/index.mdx`와 주변 `meta.json`으로 유지한다.
중첩된 series 디렉터리도 보존한다.
- 변경이 아래 ADR 작성 조건에 걸리면 반드시 ADR을 작성하거나 갱신한다.

## ADR 작성 조건

아래 조건 중 하나라도 해당하면 `docs/adr/`에 ADR을 작성하거나 갱신한다.

- 선택지가 2개 이상이고 트레이드오프가 존재한 경우.
- 반복적으로 따라야 할 규칙이나 경계를 정의한 경우.
- 테스트 전략이나 검증 방식이 결정의 핵심이었던 경우.

ADR 작성 기준:

- `docs/adr/` 아래에 번호가 붙은 파일을 만들고 `docs/adr/README.md`를
갱신한다.
- 배경, 결정, 결과, 검토한 대안, 가능하면 관련 커밋 히스토리를 기록한다.
- 과거를 지우기 위해 기존 ADR을 고쳐 쓰지 않는다. 이전 결정을 대체하는 새
ADR을 작성한다.
- ADR은 AI 협업 노트와 분리한다. ADR은 누가 초안을 작성했는지와 무관하게
엔지니어링 결정을 기록하는 문서다.

## 프로젝트 구조

- `src/app/`: Next.js App Router 페이지, 레이아웃, 핸들러
- `src/core/`: 앱 수준 provider와 설정 조합
- `src/domains/`: feature 사이에서 공유되는 계약과 스키마
- `src/features/`: blog, home, resume, search 같은 feature 모듈
- `src/shared/`: 재사용 가능한 UI, layout, analytics, SEO, provider
- `src/components/visualization/`: animation과 시각화 중심 컴포넌트
- `src/styles/`: design token과 global style
- `posts/`: 중첩 가능한 `index.mdx`와 `meta.json` 기반 블로그 콘텐츠
- `tests/`: Playwright E2E 테스트
- `internal/`: script와 tool configuration

## 개발 명령

- `npm run dev`: webpack 기반 로컬 개발 서버 실행
- `npm run build`: production build 생성
- `npm run lint`: source file ESLint 실행
- `npm run lint:css:syntax`: CSS syntax rule 검사
- `npm run verify:docs`: ADR과 핵심 문서 하네스 검사
- `npm run test:unit`: Vitest unit test 실행
- `npm run test:components`: component 중심 Vitest test 실행
- `npm run test:e2e`: Playwright scenario 실행
- `npm run test:ci`: 주요 CI-equivalent validation set 실행

## 스타일과 테스트

TypeScript를 사용하고 2-space indentation, semicolon, single quote, trailing
comma(`es5`), 80-column width를 따른다. Prettier가 이를 강제한다. Component는
`PascalCase`, hook은 `use` prefix가 붙은 `camelCase`, test는 `*.test.ts`
또는 `*.test.tsx`로 작성한다. Logic, UI behavior, parser, app action을 바꿀
때는 대상 Vitest 또는 Playwright coverage를 추가한다. PR 전에는
`npm run build`와 변경에 가장 관련 있는 test command를 실행한다.

## AI 작업 운영

- AI가 제안했더라도 이 저장소의 구조, ADR, 테스트 규칙을 우선한다.
- 사용자나 다른 작업자가 만든 변경을 명시 요청 없이 되돌리지 않는다.
- 작업 전 관련 문서와 현재 구현을 먼저 확인하고, 추측으로 구조를 바꾸지
않는다.
- 작업 후 변경 내용, 이유, 검증 방법을 짧게 요약한다.
- 반복되는 AI 작업 절차는 새 중복 가이드보다 `AGENTS.md`, ADR, 실행 계획,
또는 기존 guide 중 가장 직접적인 문서에 흡수한다.

## 커밋과 PR

커밋 메시지는 `type(scope): concise description` 형식을 사용한다. 예:
`fix(home): preview 배포 타입 오류 수정`. 주로 쓰는 type은 `feat`, `fix`,
`refactor`, `test`, `chore`다. Branch name은 `codex/<task>` 형태를 사용한다.
`.github/pull_request_template.md`를 따르고 관련 issue나 PR을 연결한다. UI 변경이
보이면 screenshot을 포함한다. Layout이나 navigation이 바뀌면 mobile/desktop,
dark/light 동작을 확인한다.
9 changes: 6 additions & 3 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ARCHITECTURE

Last updated: 2026-02-27
Last updated: 2026-05-06

## System Summary

Expand All @@ -9,7 +9,7 @@ Last updated: 2026-02-27
- File-based MDX content under `posts/**`.
- Static generation for blog routes.
- Server-side view counting via Supabase RPC.
- Client-side analytics via GA4 trackers.
- Client-side analytics via Umami trackers.
- Token-driven UI styling (Tailwind + CSS variables).

## Runtime Topology
Expand Down Expand Up @@ -80,7 +80,7 @@ Last updated: 2026-02-27

### Analytics and SEO

- GA4 event helpers in `src/shared/analytics/lib/analytics.ts`.
- Umami event helpers in `src/shared/analytics/lib/analytics.ts`.
- Trackers in `src/shared/analytics/components/*`.
- Structured data via `JsonLd` component in layout and post page.

Expand Down Expand Up @@ -127,7 +127,10 @@ Last updated: 2026-02-27

## Decisions to Preserve

복원한 아키텍처 결정의 상세 기록은 `docs/adr/README.md`에 둔다.

1. Keep folder-based content (`posts/**`) with `meta.json + index.mdx`.
2. Keep Zod schema validation in content ingestion path.
3. Keep token-first styling and avoid one-off visual constants where possible.
4. Keep route-level separation for feed, OG, and view-count concerns.
5. Keep Umami analytics separate from Supabase-backed public view counts.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ becomes part of the maintained workflow.
- `docs/QUALITY_SCORE.md`
- `docs/blog-quality-guide.md`
- Delivery/process:
- `docs/adr/README.md`
- `docs/PLANS.md`
- `docs/exec-plans/active/README.md`
- `docs/exec-plans/completed/README.md`
- `docs/exec-plans/tech-debt-tracker.md`
- `docs/guides/agentation-workflow.md`
- `docs/guides/pr-workflow.md`
- `docs/guides/testing-guide.md`
- `docs/guides/ai-collaboration.md`
- `docs/guides/ui-components-guide.md`
- Product/domain:
- `docs/PRODUCT_SENSE.md`
Expand Down
42 changes: 42 additions & 0 deletions docs/adr/0001-use-nextjs-app-router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 0001. Next.js App Router를 사용한다

Date: 2026-01-20
Status: Accepted

## 배경

이 프로젝트는 개인 블로그와 이력서 사이트로 시작했다. 첫 번째로 오래 남은
런타임 결정은 정적 사이트 생성기나 클라이언트 전용 React 앱이 아니라
Next.js 애플리케이션으로 만든다는 선택이었다.

이후 동적 블로그 라우트, 사이트맵과 피드 라우트, 라우트 단위 메타데이터,
조회수 집계를 위한 서버 액션, edge OG 이미지 엔드포인트가 추가됐다. 이
흐름은 별도 백엔드보다 App Router의 기능에 의존한다.

## 결정

애플리케이션 런타임으로 Next.js App Router를 사용한다. 라우트 진입점은
`src/app/**`에 두고, 실제 기능 구현은 feature 모듈로 위임한다.

## 결과

- 정적 블로그 페이지는 `generateStaticParams`를 사용할 수 있다.
- 라우트 핸들러로 `feed.xml`, OG 이미지, 통합 엔드포인트를 제공할 수
있다.
- 조회수 집계 같은 변경 작업은 서버 액션이 소유할 수 있다.
- 라우트 파일에 기능 구현이 과하게 쌓이지 않도록 경계를 유지해야 한다.

## 검토한 대안

- 정적 사이트 생성기: 배포는 단순하지만 라우트 핸들러, 서버 액션, 런타임
통합에는 약하다.
- 클라이언트 전용 React 앱: 렌더링 모델은 단순하지만 글 중심 사이트에
필요한 SEO와 피드 지원에 불리하다.

## 관련 히스토리

- `521eae7` (2026-01-20): 초기 프로젝트 설정.
- `eaed3bf` (2026-01-20): Markdown 기반 블로그 시스템.
- `b49aa5b` (2026-01-23): 사이트맵과 기본 메타데이터.
- `1df701f` (2026-01-27): Next.js 16 업그레이드.
- `2c92fdc` (2026-02-26): 아키텍처 기준 문서 추가.
45 changes: 45 additions & 0 deletions docs/adr/0002-keep-custom-mdx-webpack-pipeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 0002. 커스텀 MDX webpack 파이프라인을 유지한다

Date: 2026-01-27
Status: Accepted

## 배경

블로그는 Markdown 파일에서 MDX로 이동했다. 글 안에서 더 풍부한 콘텐츠,
커스텀 컴포넌트, 코드 하이라이팅, heading, 인터랙티브 위젯을 렌더링하기
위해서였다. 현재 파이프라인은 `next.config.mjs`에서 `@mdx-js/loader`,
`remark-gfm`, `rehype-slug`, `rehype-pretty-code`를 직접 연결한다.

이 파이프라인은 콘텐츠 시스템의 일부다. Next.js 내장 MDX로 교체하면
heading 생성, 코드 렌더링, 컴포넌트 매핑, 글 import 방식이 달라질 수
있다.

## 결정

MDX는 `next.config.mjs`의 커스텀 webpack rule로 유지한다. 커스텀
컴포넌트 매핑은 `src/features/blog/ui/mdx/components.tsx`에 집중한다.

## 결과

- MDX 동작을 하나의 Next.js 설정 파일에서 명시적으로 검토할 수 있다.
- 코드 하이라이팅과 slug 동작이 글 전체에서 안정적으로 유지된다.
- Next.js 빌드는 webpack 경로를 계속 사용해야 한다.
- 향후 MDX를 바꾸려면 패키지 교체가 아니라 콘텐츠 파이프라인 마이그레이션
작업으로 다뤄야 한다.

## 검토한 대안

- Next.js 내장 MDX: 로컬 설정은 줄어들지만 현재 글 렌더링 계약을 바꿀
위험이 있다.
- Plain Markdown: 수집은 단순하지만 React 컴포넌트와 인터랙티브 시각화
지원을 잃는다.

## 관련 히스토리

- `e97330d` (2026-01-27): 피드 콘텐츠를 MDX로 마이그레이션.
- `fc500c6` (2026-01-27): MDX 처리 인프라 추가.
- `90a22b0` (2026-01-27): 피드 페이지를 MDX 렌더링으로 전환.
- `cf1c9cb` (2026-01-28): Next 설정을 `next.config.mjs`로 이동.
- `5be50b5` (2026-02-28): 소스 구조 개편 중 커스텀 MDX 유지.
- `9545057` (2026-03-30): 현재 저장소 규칙과 충돌하던 구조 평탄화 계획
폐기.
40 changes: 40 additions & 0 deletions docs/adr/0003-store-posts-as-folder-mdx-and-meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 0003. 글을 폴더형 MDX와 메타데이터로 저장한다

Date: 2026-02-09
Status: Accepted

## 배경

콘텐츠 모델은 flat Markdown 파일에서 글 단위 폴더 구조로 발전했다. 현재
저장소는 `posts/**` 아래에 글을 저장하고, 유효한 글 폴더는 본문
`index.mdx`와 주변 메타데이터 `meta.json`을 함께 가진다.

중첩된 시리즈 디렉터리도 모델의 일부다. repository 계층은 폴더를 재귀적으로
탐색하고, 라우트, 피드, 사이트맵, 검색에 글을 노출하기 전에 Zod로
메타데이터를 검증한다.

## 결정

`posts/**/index.mdx`와 `posts/**/meta.json`을 표준 콘텐츠 형식으로 사용한다.
시리즈는 단일 목록으로 평탄화하지 않고 중첩 폴더로 표현한다.

## 결과

- 본문과 메타데이터가 가까운 위치에 유지된다.
- 시리즈를 디렉터리 구조로 표현할 수 있다.
- repository 계층은 재귀 탐색과 검증을 보존해야 한다.
- 새 글 생성 도구는 두 파일을 모두 만들어야 한다.

## 검토한 대안

- flat `posts/*.mdx`: 탐색은 단순하지만 시리즈와 주변 자산 관리에 약하다.
- frontmatter-only MDX: 파일 수는 줄지만 메타데이터 검증과 정책 검사가
약해진다.
- 외부 CMS: 편집 UI는 좋아지지만 이 프로젝트에는 운영 비용이 과하다.

## 관련 히스토리

- `1e5355d` (2026-02-09): SEO 친화적 콘텐츠 폴더와 slug 도입.
- `1cd4e06` (2026-02-13): 시리즈와 조회수 통합 확장.
- `5be50b5` (2026-02-28): 표준 콘텐츠 루트를 `content/`에서 `posts/`로 이동.
- `21a4a6a` (2026-04-14): 메타데이터 정책과 글 스캐폴딩 변경.
47 changes: 47 additions & 0 deletions docs/adr/0004-preserve-feature-first-source-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 0004. Feature-first 소스 구조를 유지한다

Date: 2026-02-28
Status: Accepted

## 배경

소스 트리는 여러 차례 구조 변경을 거쳤다. 초기 코드는 route-local 구조와
일반 컴포넌트 폴더에 섞여 있었다. 이후 라우트, feature 모듈, shared
인프라, domain 계약, app-level provider를 분리했다.

나중에 일반적인 Next.js 구조로 평탄화하자는 실행 계획도 있었지만, 저장소
가이드와 충돌했기 때문에 superseded 상태로 정리됐다.

## 결정

다음 feature-first 구조를 유지한다.

- `src/app/**`: App Router 진입점.
- `src/features/**`: 기능 구현.
- `src/domains/**`: feature 사이에서 공유되는 계약과 스키마.
- `src/shared/**`: 재사용 가능한 layout, UI, analytics, SEO, provider,
integration.
- `src/core/**`: 앱 수준 설정과 provider 조합.

## 결과

- flat shared component 디렉터리보다 소유권이 명확하다.
- 라우트 파일은 얇게 유지하고 feature page를 조합할 수 있다.
- shared 코드로 올릴 때는 feature-local 코드보다 높은 재사용 기준이 필요하다.
- 리팩터링 시 import와 테스트를 이 경계에 맞춰 유지해야 한다.

## 검토한 대안

- route-local 구현: 작은 앱에는 유용하지만 blog, resume, search, home
기능이 커지면서 중복이 늘었다.
- flat `components/`, `lib/`, `types/`: 처음에는 단순하지만 feature 소유권과
계약이 흐려진다.
- 일반적인 Next.js 평탄화: 실행 계획 아카이브에서 명시적으로 superseded
처리됐다.

## 관련 히스토리

- `e17fe0f` (2026-01-25): 첫 feature 기반 컴포넌트 재구성.
- `5be50b5` (2026-02-28): FSD 스타일 마이그레이션 정리.
- `2c92fdc` (2026-02-26): 아키텍처 기준 문서 추가.
- `9545057` (2026-03-30): 평탄화 계획을 superseded 상태로 completed에 이동.
Loading
Loading