Skip to content

Commit 2ce73e7

Browse files
authored
docs(adr): document decision rules (#101)
* docs(adr): document decision rules * docs(harness): add documentation verification
1 parent 56154c2 commit 2ce73e7

18 files changed

Lines changed: 720 additions & 82 deletions

AGENTS.md

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,90 @@
11
# Repository Guidelines
22

3-
## Always-Follow Rules
4-
- Keep changes minimal and scoped to the task. Do not edit unrelated files.
5-
- 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.
6-
- Keep visualization-heavy UI in `src/components/visualization/`. Do not move that code to other folders without a clear architectural reason.
7-
- Never use `any`. Use concrete types or `unknown` with narrowing.
8-
- Never use arbitrary Tailwind values such as `p-[13px]`. Use standard utilities, shared tokens, and existing style patterns.
9-
- Do not replace feature-first structure with flat shared folders. Keep code in `src/features/`, `src/shared/`, and `src/domains/` by responsibility.
10-
- Preserve blog content structure as `posts/**/index.mdx` with nearby `meta.json`, including nested series directories when present.
11-
12-
## Project Structure
13-
- `src/app/` — Next.js App Router pages, layouts, handlers
14-
- `src/core/` — app-level providers and configuration composition
15-
- `src/domains/` — cross-feature contracts and schemas
16-
- `src/features/` — feature modules such as blog, home, resume, and search
17-
- `src/shared/` — reusable UI, layout, analytics, SEO, and providers
18-
- `src/components/visualization/` — animation and visualization-heavy components
19-
- `src/styles/` — design tokens and global styles
20-
- `posts/` — blog content, series entries, and metadata managed as nested `index.mdx` + `meta.json`
21-
- `tests/` — Playwright end-to-end coverage
22-
- `internal/` — scripts and tool configuration
23-
24-
## Development Commands
25-
- `npm run dev` — run the local dev server with webpack
26-
- `npm run build` — create the production build
27-
- `npm run lint` — run ESLint on source files
28-
- `npm run lint:css:syntax` — check CSS syntax rules
29-
- `npm run test:unit` — run Vitest unit tests
30-
- `npm run test:components` — run component-focused Vitest tests
31-
- `npm run test:e2e` — run Playwright scenarios
32-
- `npm run test:ci` — run the main CI-equivalent validation set
33-
34-
## Style & Testing
35-
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.
36-
37-
## Commits & PRs
38-
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.
3+
## 항상 지킬 규칙
4+
5+
- 변경은 작업 범위에 맞게 최소화한다. 관련 없는 파일은 수정하지 않는다.
6+
- 현재 MDX 파이프라인을 보존한다. MDX는 `next.config.mjs``@mdx-js/loader`
7+
기반 커스텀 webpack rule에 남겨둔다. 전체 콘텐츠 파이프라인을 의도적으로
8+
마이그레이션하지 않는 한 Next.js 내장 MDX로 바꾸지 않는다.
9+
- 시각화 중심 UI는 `src/components/visualization/`에 둔다. 명확한
10+
아키텍처 이유 없이 다른 폴더로 옮기지 않는다.
11+
- `any`를 사용하지 않는다. 구체 타입을 쓰거나 `unknown`과 narrowing을
12+
사용한다.
13+
- `p-[13px]` 같은 arbitrary Tailwind value를 사용하지 않는다. 표준 utility,
14+
shared token, 기존 스타일 패턴을 사용한다.
15+
- feature-first 구조를 flat shared folder 구조로 바꾸지 않는다.
16+
`src/features/`, `src/shared/`, `src/domains/`를 책임별로 유지한다.
17+
- 블로그 콘텐츠 구조는 `posts/**/index.mdx`와 주변 `meta.json`으로 유지한다.
18+
중첩된 series 디렉터리도 보존한다.
19+
- 변경이 아래 ADR 작성 조건에 걸리면 반드시 ADR을 작성하거나 갱신한다.
20+
21+
## ADR 작성 조건
22+
23+
아래 조건 중 하나라도 해당하면 `docs/adr/`에 ADR을 작성하거나 갱신한다.
24+
25+
- 선택지가 2개 이상이고 트레이드오프가 존재한 경우.
26+
- 반복적으로 따라야 할 규칙이나 경계를 정의한 경우.
27+
- 테스트 전략이나 검증 방식이 결정의 핵심이었던 경우.
28+
29+
ADR 작성 기준:
30+
31+
- `docs/adr/` 아래에 번호가 붙은 파일을 만들고 `docs/adr/README.md`
32+
갱신한다.
33+
- 배경, 결정, 결과, 검토한 대안, 가능하면 관련 커밋 히스토리를 기록한다.
34+
- 과거를 지우기 위해 기존 ADR을 고쳐 쓰지 않는다. 이전 결정을 대체하는 새
35+
ADR을 작성한다.
36+
- ADR은 AI 협업 노트와 분리한다. ADR은 누가 초안을 작성했는지와 무관하게
37+
엔지니어링 결정을 기록하는 문서다.
38+
39+
## 프로젝트 구조
40+
41+
- `src/app/`: Next.js App Router 페이지, 레이아웃, 핸들러
42+
- `src/core/`: 앱 수준 provider와 설정 조합
43+
- `src/domains/`: feature 사이에서 공유되는 계약과 스키마
44+
- `src/features/`: blog, home, resume, search 같은 feature 모듈
45+
- `src/shared/`: 재사용 가능한 UI, layout, analytics, SEO, provider
46+
- `src/components/visualization/`: animation과 시각화 중심 컴포넌트
47+
- `src/styles/`: design token과 global style
48+
- `posts/`: 중첩 가능한 `index.mdx``meta.json` 기반 블로그 콘텐츠
49+
- `tests/`: Playwright E2E 테스트
50+
- `internal/`: script와 tool configuration
51+
52+
## 개발 명령
53+
54+
- `npm run dev`: webpack 기반 로컬 개발 서버 실행
55+
- `npm run build`: production build 생성
56+
- `npm run lint`: source file ESLint 실행
57+
- `npm run lint:css:syntax`: CSS syntax rule 검사
58+
- `npm run verify:docs`: ADR과 핵심 문서 하네스 검사
59+
- `npm run test:unit`: Vitest unit test 실행
60+
- `npm run test:components`: component 중심 Vitest test 실행
61+
- `npm run test:e2e`: Playwright scenario 실행
62+
- `npm run test:ci`: 주요 CI-equivalent validation set 실행
63+
64+
## 스타일과 테스트
65+
66+
TypeScript를 사용하고 2-space indentation, semicolon, single quote, trailing
67+
comma(`es5`), 80-column width를 따른다. Prettier가 이를 강제한다. Component는
68+
`PascalCase`, hook은 `use` prefix가 붙은 `camelCase`, test는 `*.test.ts`
69+
또는 `*.test.tsx`로 작성한다. Logic, UI behavior, parser, app action을 바꿀
70+
때는 대상 Vitest 또는 Playwright coverage를 추가한다. PR 전에는
71+
`npm run build`와 변경에 가장 관련 있는 test command를 실행한다.
72+
73+
## AI 작업 운영
74+
75+
- AI가 제안했더라도 이 저장소의 구조, ADR, 테스트 규칙을 우선한다.
76+
- 사용자나 다른 작업자가 만든 변경을 명시 요청 없이 되돌리지 않는다.
77+
- 작업 전 관련 문서와 현재 구현을 먼저 확인하고, 추측으로 구조를 바꾸지
78+
않는다.
79+
- 작업 후 변경 내용, 이유, 검증 방법을 짧게 요약한다.
80+
- 반복되는 AI 작업 절차는 새 중복 가이드보다 `AGENTS.md`, ADR, 실행 계획,
81+
또는 기존 guide 중 가장 직접적인 문서에 흡수한다.
82+
83+
## 커밋과 PR
84+
85+
커밋 메시지는 `type(scope): concise description` 형식을 사용한다. 예:
86+
`fix(home): preview 배포 타입 오류 수정`. 주로 쓰는 type은 `feat`, `fix`,
87+
`refactor`, `test`, `chore`다. Branch name은 `codex/<task>` 형태를 사용한다.
88+
`.github/pull_request_template.md`를 따르고 관련 issue나 PR을 연결한다. UI 변경이
89+
보이면 screenshot을 포함한다. Layout이나 navigation이 바뀌면 mobile/desktop,
90+
dark/light 동작을 확인한다.

ARCHITECTURE.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ARCHITECTURE
22

3-
Last updated: 2026-02-27
3+
Last updated: 2026-05-06
44

55
## System Summary
66

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

1515
## Runtime Topology
@@ -80,7 +80,7 @@ Last updated: 2026-02-27
8080

8181
### Analytics and SEO
8282

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

@@ -127,7 +127,10 @@ Last updated: 2026-02-27
127127

128128
## Decisions to Preserve
129129

130+
복원한 아키텍처 결정의 상세 기록은 `docs/adr/README.md`에 둔다.
131+
130132
1. Keep folder-based content (`posts/**`) with `meta.json + index.mdx`.
131133
2. Keep Zod schema validation in content ingestion path.
132134
3. Keep token-first styling and avoid one-off visual constants where possible.
133135
4. Keep route-level separation for feed, OG, and view-count concerns.
136+
5. Keep Umami analytics separate from Supabase-backed public view counts.

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ becomes part of the maintained workflow.
2121
- `docs/QUALITY_SCORE.md`
2222
- `docs/blog-quality-guide.md`
2323
- Delivery/process:
24+
- `docs/adr/README.md`
2425
- `docs/PLANS.md`
2526
- `docs/exec-plans/active/README.md`
2627
- `docs/exec-plans/completed/README.md`
2728
- `docs/exec-plans/tech-debt-tracker.md`
2829
- `docs/guides/agentation-workflow.md`
2930
- `docs/guides/pr-workflow.md`
3031
- `docs/guides/testing-guide.md`
31-
- `docs/guides/ai-collaboration.md`
3232
- `docs/guides/ui-components-guide.md`
3333
- Product/domain:
3434
- `docs/PRODUCT_SENSE.md`
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# 0001. Next.js App Router를 사용한다
2+
3+
Date: 2026-01-20
4+
Status: Accepted
5+
6+
## 배경
7+
8+
이 프로젝트는 개인 블로그와 이력서 사이트로 시작했다. 첫 번째로 오래 남은
9+
런타임 결정은 정적 사이트 생성기나 클라이언트 전용 React 앱이 아니라
10+
Next.js 애플리케이션으로 만든다는 선택이었다.
11+
12+
이후 동적 블로그 라우트, 사이트맵과 피드 라우트, 라우트 단위 메타데이터,
13+
조회수 집계를 위한 서버 액션, edge OG 이미지 엔드포인트가 추가됐다. 이
14+
흐름은 별도 백엔드보다 App Router의 기능에 의존한다.
15+
16+
## 결정
17+
18+
애플리케이션 런타임으로 Next.js App Router를 사용한다. 라우트 진입점은
19+
`src/app/**`에 두고, 실제 기능 구현은 feature 모듈로 위임한다.
20+
21+
## 결과
22+
23+
- 정적 블로그 페이지는 `generateStaticParams`를 사용할 수 있다.
24+
- 라우트 핸들러로 `feed.xml`, OG 이미지, 통합 엔드포인트를 제공할 수
25+
있다.
26+
- 조회수 집계 같은 변경 작업은 서버 액션이 소유할 수 있다.
27+
- 라우트 파일에 기능 구현이 과하게 쌓이지 않도록 경계를 유지해야 한다.
28+
29+
## 검토한 대안
30+
31+
- 정적 사이트 생성기: 배포는 단순하지만 라우트 핸들러, 서버 액션, 런타임
32+
통합에는 약하다.
33+
- 클라이언트 전용 React 앱: 렌더링 모델은 단순하지만 글 중심 사이트에
34+
필요한 SEO와 피드 지원에 불리하다.
35+
36+
## 관련 히스토리
37+
38+
- `521eae7` (2026-01-20): 초기 프로젝트 설정.
39+
- `eaed3bf` (2026-01-20): Markdown 기반 블로그 시스템.
40+
- `b49aa5b` (2026-01-23): 사이트맵과 기본 메타데이터.
41+
- `1df701f` (2026-01-27): Next.js 16 업그레이드.
42+
- `2c92fdc` (2026-02-26): 아키텍처 기준 문서 추가.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 0002. 커스텀 MDX webpack 파이프라인을 유지한다
2+
3+
Date: 2026-01-27
4+
Status: Accepted
5+
6+
## 배경
7+
8+
블로그는 Markdown 파일에서 MDX로 이동했다. 글 안에서 더 풍부한 콘텐츠,
9+
커스텀 컴포넌트, 코드 하이라이팅, heading, 인터랙티브 위젯을 렌더링하기
10+
위해서였다. 현재 파이프라인은 `next.config.mjs`에서 `@mdx-js/loader`,
11+
`remark-gfm`, `rehype-slug`, `rehype-pretty-code`를 직접 연결한다.
12+
13+
이 파이프라인은 콘텐츠 시스템의 일부다. Next.js 내장 MDX로 교체하면
14+
heading 생성, 코드 렌더링, 컴포넌트 매핑, 글 import 방식이 달라질 수
15+
있다.
16+
17+
## 결정
18+
19+
MDX는 `next.config.mjs`의 커스텀 webpack rule로 유지한다. 커스텀
20+
컴포넌트 매핑은 `src/features/blog/ui/mdx/components.tsx`에 집중한다.
21+
22+
## 결과
23+
24+
- MDX 동작을 하나의 Next.js 설정 파일에서 명시적으로 검토할 수 있다.
25+
- 코드 하이라이팅과 slug 동작이 글 전체에서 안정적으로 유지된다.
26+
- Next.js 빌드는 webpack 경로를 계속 사용해야 한다.
27+
- 향후 MDX를 바꾸려면 패키지 교체가 아니라 콘텐츠 파이프라인 마이그레이션
28+
작업으로 다뤄야 한다.
29+
30+
## 검토한 대안
31+
32+
- Next.js 내장 MDX: 로컬 설정은 줄어들지만 현재 글 렌더링 계약을 바꿀
33+
위험이 있다.
34+
- Plain Markdown: 수집은 단순하지만 React 컴포넌트와 인터랙티브 시각화
35+
지원을 잃는다.
36+
37+
## 관련 히스토리
38+
39+
- `e97330d` (2026-01-27): 피드 콘텐츠를 MDX로 마이그레이션.
40+
- `fc500c6` (2026-01-27): MDX 처리 인프라 추가.
41+
- `90a22b0` (2026-01-27): 피드 페이지를 MDX 렌더링으로 전환.
42+
- `cf1c9cb` (2026-01-28): Next 설정을 `next.config.mjs`로 이동.
43+
- `5be50b5` (2026-02-28): 소스 구조 개편 중 커스텀 MDX 유지.
44+
- `9545057` (2026-03-30): 현재 저장소 규칙과 충돌하던 구조 평탄화 계획
45+
폐기.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# 0003. 글을 폴더형 MDX와 메타데이터로 저장한다
2+
3+
Date: 2026-02-09
4+
Status: Accepted
5+
6+
## 배경
7+
8+
콘텐츠 모델은 flat Markdown 파일에서 글 단위 폴더 구조로 발전했다. 현재
9+
저장소는 `posts/**` 아래에 글을 저장하고, 유효한 글 폴더는 본문
10+
`index.mdx`와 주변 메타데이터 `meta.json`을 함께 가진다.
11+
12+
중첩된 시리즈 디렉터리도 모델의 일부다. repository 계층은 폴더를 재귀적으로
13+
탐색하고, 라우트, 피드, 사이트맵, 검색에 글을 노출하기 전에 Zod로
14+
메타데이터를 검증한다.
15+
16+
## 결정
17+
18+
`posts/**/index.mdx``posts/**/meta.json`을 표준 콘텐츠 형식으로 사용한다.
19+
시리즈는 단일 목록으로 평탄화하지 않고 중첩 폴더로 표현한다.
20+
21+
## 결과
22+
23+
- 본문과 메타데이터가 가까운 위치에 유지된다.
24+
- 시리즈를 디렉터리 구조로 표현할 수 있다.
25+
- repository 계층은 재귀 탐색과 검증을 보존해야 한다.
26+
- 새 글 생성 도구는 두 파일을 모두 만들어야 한다.
27+
28+
## 검토한 대안
29+
30+
- flat `posts/*.mdx`: 탐색은 단순하지만 시리즈와 주변 자산 관리에 약하다.
31+
- frontmatter-only MDX: 파일 수는 줄지만 메타데이터 검증과 정책 검사가
32+
약해진다.
33+
- 외부 CMS: 편집 UI는 좋아지지만 이 프로젝트에는 운영 비용이 과하다.
34+
35+
## 관련 히스토리
36+
37+
- `1e5355d` (2026-02-09): SEO 친화적 콘텐츠 폴더와 slug 도입.
38+
- `1cd4e06` (2026-02-13): 시리즈와 조회수 통합 확장.
39+
- `5be50b5` (2026-02-28): 표준 콘텐츠 루트를 `content/`에서 `posts/`로 이동.
40+
- `21a4a6a` (2026-04-14): 메타데이터 정책과 글 스캐폴딩 변경.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# 0004. Feature-first 소스 구조를 유지한다
2+
3+
Date: 2026-02-28
4+
Status: Accepted
5+
6+
## 배경
7+
8+
소스 트리는 여러 차례 구조 변경을 거쳤다. 초기 코드는 route-local 구조와
9+
일반 컴포넌트 폴더에 섞여 있었다. 이후 라우트, feature 모듈, shared
10+
인프라, domain 계약, app-level provider를 분리했다.
11+
12+
나중에 일반적인 Next.js 구조로 평탄화하자는 실행 계획도 있었지만, 저장소
13+
가이드와 충돌했기 때문에 superseded 상태로 정리됐다.
14+
15+
## 결정
16+
17+
다음 feature-first 구조를 유지한다.
18+
19+
- `src/app/**`: App Router 진입점.
20+
- `src/features/**`: 기능 구현.
21+
- `src/domains/**`: feature 사이에서 공유되는 계약과 스키마.
22+
- `src/shared/**`: 재사용 가능한 layout, UI, analytics, SEO, provider,
23+
integration.
24+
- `src/core/**`: 앱 수준 설정과 provider 조합.
25+
26+
## 결과
27+
28+
- flat shared component 디렉터리보다 소유권이 명확하다.
29+
- 라우트 파일은 얇게 유지하고 feature page를 조합할 수 있다.
30+
- shared 코드로 올릴 때는 feature-local 코드보다 높은 재사용 기준이 필요하다.
31+
- 리팩터링 시 import와 테스트를 이 경계에 맞춰 유지해야 한다.
32+
33+
## 검토한 대안
34+
35+
- route-local 구현: 작은 앱에는 유용하지만 blog, resume, search, home
36+
기능이 커지면서 중복이 늘었다.
37+
- flat `components/`, `lib/`, `types/`: 처음에는 단순하지만 feature 소유권과
38+
계약이 흐려진다.
39+
- 일반적인 Next.js 평탄화: 실행 계획 아카이브에서 명시적으로 superseded
40+
처리됐다.
41+
42+
## 관련 히스토리
43+
44+
- `e17fe0f` (2026-01-25): 첫 feature 기반 컴포넌트 재구성.
45+
- `5be50b5` (2026-02-28): FSD 스타일 마이그레이션 정리.
46+
- `2c92fdc` (2026-02-26): 아키텍처 기준 문서 추가.
47+
- `9545057` (2026-03-30): 평탄화 계획을 superseded 상태로 completed에 이동.

0 commit comments

Comments
 (0)