Files
llm-wiki/docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/process/pre-review-source.md
T

510 lines
39 KiB
Markdown

---
title: CA Skeleton Frontend Operational Contract
source_type: project-note
status: draft
confidence: unknown
tags: [project-note, ca-skeleton, frontend, clean-architecture]
related_projects: [ca-skeleton-frontend]
last_reviewed: 2026-07-18
diagrams: []
architecture_review:
status_label: active
---
# CA Skeleton Frontend Operational Contract
> Layer: `raw/project-notes/` (primary, hub) → `/ingest` 후 검증된 사실은 `wiki/projects/` 로 추출.
> 본 문서는 **프로젝트의 최상위 hub**. 프로젝트 전체 컨텍스트 / 문제 정의 / 시스템 아키텍처 / 핵심 시퀀스가 여기에 집중. 모든 branch / errors / interviews / lectures / job-postings / blog-topics / sources 가 본 문서로 upward link.
> `status_label`: `active` | `paused` | `completed` | `archived`
## 1. 프로젝트 개요 / Overview
> 외부인이 1분 안에 "이게 뭐 하는 프로젝트인가" 이해할 수 있어야 함.
- **한 줄 요약**: 도메인/비즈니스 로직을 제거한 **Clean Architecture 프론트엔드 skeleton**. 어떤 화면·기능을 붙여도 (1) 같은 방식으로 백엔드 API 를 호출하고, (2) 같은 방식으로 실패를 분류·표시하고, (3) 같은 계층 의존 규칙을 강제하고, (4) 같은 계약 테스트로 깨짐을 감지하는 React SPA 뼈대. 백엔드 [[raw/project-notes/ca-skeleton-operational-contract]] 의 프론트엔드 대응물이며, 개인 학습·포트폴리오·재사용 starter 로 쓴다.
- **기간**: 2026-07-18 ~ in-progress (**기획 단계** — 코드 미착수, `/branch-spec` 로 계약별 상세 설계 예정)
- **현재 상태**: `active`
- **나의 역할 / Role**: 설계자 / 구현자 (개인 프로젝트)
- **저장소 / Repo**:
- 메인: `<미생성 — Vite + React + Tailwind SPA 레포 예정>` · `NO_GROUND_TRUTH`: 아직 코드 레포가 없어 본 hub 의 모든 구현 주장은 `planned` 등급이다(§9). `actually-implemented` 승격은 레포 생성 후 `src/` grep 으로만.
- 부속: —
## 2. 문제 정의 / Problem
> 추상화 금지. 구체 시나리오·수치로.
### 2.1 현재 상태의 문제
- 문제 1: 도메인 없는 React 프로젝트를 새로 시작할 때마다 **API 호출 래퍼·에러 분류·로딩/빈/에러 UI 상태·응답 검증을 매번 다르게 재구현**한다. 결과적으로 화면마다 실패 처리가 제각각이라, "이 화면은 500 을 어떻게 보여주지?"를 화면 수만큼 되묻게 된다.
- 문제 2: **JS(무타입) 환경**에서 백엔드 응답 스키마가 바뀌면 컴파일타임이 아니라 런타임에서야 깨짐이 드러난다. 경계에서 검증 계약이 없으면 `undefined` 접근 에러가 UI 트리 깊숙한 곳에서 터져, 원인이 API 계약 변경임을 추적하기 어렵다.
- 문제 3: "Clean Architecture"를 표방해도 **강제 장치가 없으면** presentation 컴포넌트가 API DTO 를 직접 만지고 의존 방향이 역전된다(백엔드의 ArchUnit 대응물이 프론트엔드엔 부재). 리뷰어의 눈에만 의존하면 시간이 지나며 반드시 새어나간다.
### 2.2 왜 지금 해결해야 하는가
- 트리거 (왜 지금): 백엔드 [[raw/project-notes/ca-skeleton-operational-contract]] 운영 계약을 구축 완료했다. 같은 "도메인 제거 + 운영 계약 우선" 철학을 프론트엔드에 **대칭 적용**할 시점 — 백엔드가 응답 envelope·error category·trace 를 계약화했으므로 프론트엔드도 그 계약의 소비 규약을 고정해야 짝이 맞는다.
- 비용 (해결 안 했을 때 손실): 계약 없이 화면부터 만들면, 나중에 횡단 관심사(에러 분류·로깅·검증·접근성·성능 예산)를 **소급 적용하는 비용이 화면 수에 비례해 폭증**한다. 특히 무타입 JS 라 런타임 깨짐이 QA/운영 단계로 밀린다.
- 기회 (해결 시 가치): 도메인 없는 **재사용 skeleton 1벌** → 이후 모든 프론트엔드 프로젝트의 출발점 + 면접/포트폴리오에서 "계층·계약·강제 장치를 어떻게 설계했는가"를 증거로 말할 수 있는 자산.
### 2.3 성공 기준 / Success criteria
> 측정 가능해야 함. "잘 동작한다" 같은 모호 표현 금지.
> 아래 임계 수치는 **초기 기본값(default)** 이며, 각 계약 branch(`/branch-spec`)에서 측정·조정한다. "잘 동작한다" 류 추상 표현을 쓰지 않는 것이 목적.
- 기준 1 (계약 관통): 도메인 없는 **sample feature slice** 화면이 `api-client → zod 검증 → error boundary → async 4-state → CA 의존 규칙`을 모두 관통하고, **계약 위반 시 실패하는 contract test** 가 CI 에서 green.
- 기준 2 (의존 규칙 강제): CA 계층 의존 규칙 lint(ESLint boundaries / dependency-cruiser) **위반 0건**. `presentation → infrastructure` 직접 의존 등 역방향 import 발생 시 **빌드 실패**.
- 기준 3 (4-state 강제): 모든 async 화면 표면이 `loading / empty / error / success` **4상태를 렌더**. sample slice 에 4상태 각각의 component test(RTL) 존재.
- 기준 4 (성능 예산): 프로덕션 초기 JS 번들 **< 200KB gzip**(default, route-level code splitting 후), web-vitals **LCP < 2.5s · CLS < 0.1 · INP < 200ms** 임계를 CI 게이트가 검사.
- 기준 5 (재사용성): 새 화면을 만들 때 개발자가 API 에러/로딩/검증을 **재구현하지 않고** 계약 훅·컴포넌트(`useApiQuery`, `<AsyncBoundary>`, `apiClient`)를 재사용 — sample slice 복제로 새 화면 골격이 나옴.
## 3. 시스템 아키텍처 / System architecture
> **필수 섹션.** 아키텍처 다이어그램이 없는 project-note 는 hub 역할을 못 함.
### Diagram tool 선택 — 엄격한 분리
| 다이어그램 종류 | 도구 | 이유 |
|---|---|---|
| **시스템 아키텍처 / 컴포넌트 구성도 / 배포 토폴로지 / 데이터 흐름 (정적 구조)** | **draw.io XML (`.drawio` 또는 `.drawio.svg`)** | 자유 배치 / 시각적 그룹화 / 신뢰 경계 / 색상 코딩 / Obsidian draw.io 플러그인 native 편집 |
| **시퀀스 다이어그램** | **Mermaid `sequenceDiagram`** | 텍스트 기반·git diff 친화, 시간축 표현에 최적 |
| **ER 다이어그램 (데이터 모델, 선택)** | **Mermaid `erDiagram`** | 텍스트 기반·관계 카디널리티 표기 직관적 |
| 작은 결정 트리 / 짧은 플로우차트 | Mermaid `flowchart` 도 허용 (작은 규모 한정) | 시퀀스가 아닌 단순 분기 |
**금지**:
- 시스템 아키텍처를 Mermaid `graph TD`/`graph LR` 로 작성 — 시각 표현력 부족, draw.io 사용 의무
- 시퀀스 흐름을 draw.io 로 작성 — 시간축 표현 불편, Mermaid 사용 의무
### Diagram 컨퍼런스급 표준 (필수 정독)
> [[rules/diagram-standards]] 에서 **컨퍼런스급(Toss SLASH / Kakao if(dev) / Naver DEVIEW 수준) 다이어그램 표준 v2 (minimalist-first)** 를 정의한다. 본 template 본문에 별도 기준을 두지 않는다 — 항상 `rules/diagram-standards.md` 를 정독.
>
> **핵심 원칙: "적을수록 좋다" (Less is more)**. 정보를 다이어그램에 몰아넣으면 청중이 어디부터 봐야 할지 모른다.
>
> v2 의 요약 (전체는 rules 정독):
>
> - **요소 수 상한** (HARD): Vertex ≤ 10 / Edge ≤ 8 / Callout ≤ 1 / Boundary group ≤ 3 / Legend 항목 ≤ 6 / 색상 ≤ 4
> - **박스 라벨 ≤ 2줄**, **화살표 라벨 ≤ 5단어**
> - **80% 회색/흑백 + 강조색 ≤ 2** (color salad 금지)
> - **Boundary 는 정보 있을 때만** (장식용 boundary 금지)
> - **Legend 는 표준 컨벤션이면 생략** (점선=외부 / cylinder=DB / 실선=동기 / 점선=비동기 는 legend 불필요)
> - **Callout 1개** (있을 때만) — 비자명한 함정·결정에만
> - **출처 wikilink 는 본문/캡션에**, 다이어그램 안에 박지 말 것
> - **스케일 어노테이션 (QPS/latency)** 은 다이어그램의 질문이 *성능* 일 때만
> - **5초 룰 + 30초 룰** 통과
>
> **8항 self-check checklist** ([[rules/diagram-standards]] §14) 를 모두 ✓ 해야 컨퍼런스 발표 가능 수준. 1개라도 미달 → 분할 또는 단순화.
>
> `wiki-diagram-reviewer` agent 가 위 기준으로 `.drawio` XML 을 grep-카운트 후 0~100 점수 부여, ≥95 PASS.
### 3.1 아키텍처 다이어그램 (draw.io XML)
> 컴포넌트 구성도. **저장 경로**: `raw/diagrams/<project-slug>/` 하위에 `.drawio` 또는 `.drawio.svg` 형식으로 저장. Obsidian draw.io 플러그인으로 더블클릭 편집.
>
> **파일 명명 규약**: `architecture-{viewpoint}-YYYY-MM-DD.drawio.svg`
> 예: `architecture-overview-2026-05-25.drawio.svg`, `architecture-deployment-2026-05-25.drawio.svg`, `architecture-data-flow-2026-05-25.drawio.svg`
>
> **임베드 작성 방법**: 아래 code block 형식을 참고해 실제 파일명을 채워 wikilink 작성. **placeholder 그대로 두지 말 것** — Obsidian이 placeholder를 파일명으로 채택해 root에 orphan 파일을 생성함.
```markdown
실제 사용 예 (drawio 파일 생성 후 placeholder 부분을 실제 값으로 치환):
![[raw/diagrams/my-project/architecture-overview-2026-05-25.drawio.svg]]
```
<!-- 본 템플릿 사용자: 위 code block 안의 line을 일반 wikilink로 옮기되, my-project 와 날짜를 실제 값으로 치환한 뒤에만 사용. -->
> ⏳ **`needs-diagram` (DIAGRAM_PENDING_USER)** — 아키텍처 `.drawio` 는 `/project-spec` 가 자동 생성할 수 없다. 아래 대상 파일을 사용자가 직접 작성한 뒤, 백틱을 풀어 활성 임베드(`![[...]]`)로 바꾼다. 그때까지는 **inline code span** 이라 구조 린터의 `BROKEN_LINK` 를 발생시키지 않는다. 이 항목은 readiness 게이트에서 **Blocking 이 아니라 Should-fix** 로 취급되어 판정이 `Ready-pending-user` 가 된다.
>
> 작성 대상 (권장 viewpoint 2개, 저장 경로 `raw/diagrams/ca-skeleton-frontend/`):
> - `![[raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio.svg]]` — CA 계층(presentation → application → domain, adapters 는 바깥) + 백엔드 API 신뢰 경계 + cross-cutting(error boundary / observability / config). 화살표는 **의존 방향**(안쪽을 향함)을 보여준다.
> - `![[raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio.svg]]` — 정적 호스팅(CDN/오브젝트 스토리지) → 브라우저 SPA → 백엔드 API 토폴로지 (서버 런타임 없음).
>
> 작성 후: `wiki-diagram-reviewer` 로 ≥95 점 확인(게이트가 판정 못 하는 **권고 단계** — `rules/project-readiness-gate.md` R2 ≥95 주). 이후 frontmatter `diagrams:` 배열에 두 파일 등재 + `architecture_review:` 날짜 기입.
**다이어그램 작성 요약 (v2 minimalist, 상세는 [[rules/diagram-standards]] 정독):**
- **컴포넌트 라벨**: 시스템 이름 (Bold 1줄) + 핵심 한 줄 (Stack OR 역할, 둘 중 하나만). 절대 ≥3 줄 금지.
예: `**User Service**` / `Spring Boot 3.4 · :8080` (2줄)
- **화살표 라벨**: `<step?> <verb/protocol> <object>` — 5단어 이내
예: `① GET /`, `proxy_pass :8080`, `Kafka publish user.signed-up`
- **외부 시스템**: 점선 (`#D0D7DE`) + fill `#F6F8FA`. Legend 불필요 (표준 컨벤션)
- **Boundary**: Trust / Network / External — **정보 있을 때만**. 모든 컴포넌트를 boundary 1개 안에 넣지 말 것 (정보 0)
- **색상 ≤ 4** — 80% 회색 + 강조 ≤ 2 (blue / orange 한 family씩) + (선택) warning red callout
- **Legend 생략 가능** — 점선=외부 / cylinder=DB / 실선=동기 같은 표준 컨벤션이면 legend 불필요. 비표준 색·기호 있을 때만 ≤6 항목 legend.
- **데이터 모델 카디널리티** 는 ER 다이어그램 (Mermaid `erDiagram`)에서만. 아키텍처 다이어그램의 화살표에 `1..N` 같은 cardinality 박지 말 것.
### 3.2 컴포넌트 책임 분담
> 다이어그램의 각 컴포넌트가 정확히 무엇을 책임지는지 표로.
> 컴포넌트 = **Clean Architecture 계층**. 의존 방향은 항상 바깥→안(presentation → application → domain). adapters 는 domain 이 정의한 port 를 구현해 바깥에서 주입된다. 이 방향을 `feature-frontend-architecture-enforcement-lint-contract` branch 가 lint 로 강제한다.
| 컴포넌트 (계층) | 역할 | 기술 스택 | 의존하는 외부 |
|---|---|---|---|
| `presentation` (components / pages) | UI 렌더 · 사용자 입력 · **view-model 만** 소비 (raw API DTO 직접 접근 금지) | React 19 + Tailwind | (하위 계층만) |
| `application` (hooks / use-cases) | 화면 로직 · 상태 오케스트레이션 · use-case 훅 | React hooks + TanStack Query | adapters port (인터페이스) |
| `domain` (models / policies) | 도메인 제거 skeleton 이라 최소 — view-model 스키마 · 불변식 · 순수 함수 | 순수 JS + zod schema | **없음** (가장 안쪽, 의존 0) |
| `adapters/infrastructure` (api-client / storage / logger) | HTTP client · 응답 envelope 파싱 · zod 검증 · storage · 로깅 sink | fetch + zod + TanStack Query | Backend API · 브라우저 storage |
| `cross-cutting` (error-boundary / observability / config) | 에러 경계 · 구조화 로깅 · env config · trace 전파 | React ErrorBoundary + logger | Backend(trace) · error sink |
### 3.3 외부 의존성 / External dependencies
| 외부 시스템 | 용도 | 통신 방식 | 장애 시 영향 (degrade / fail / fallback) |
|---|---|---|---|
| Backend API | 데이터 read/write (인증 자체는 위임 — 아래) | REST/JSON, structured envelope (`{success,data,meta}` / `{success:false,error}`) | async 4-state 의 `error` 로 degrade + TanStack Query 가 `retryable` 만 backoff 재시도 |
| 브라우저 런타임 | 실행 환경 (fetch / history / storage) | Web API | 미지원 API 는 feature-detect + fallback, 앱 셸은 유지 |
| (선택) 에러 트래킹 sink | 관측 (`feature-frontend-observability-logging-trace-contract`) | HTTPS, best-effort async | fail 시 콘솔 fallback, 화면 영향 0 |
> **인증 경계 주의**: 토큰 획득/저장/refresh 는 본 skeleton 범위 밖 → [[raw/project-notes/keycloak-patterns-overview]] 로 위임. 본 skeleton 은 "이미 인증된 요청" 을 전제로 API 호출 계약만 다룬다(§7 보안 참조).
### 3.4 배포 다이어그램 / Deployment (선택)
> 운영 환경 토폴로지가 비자명하면 별도 draw.io.
```markdown
실제 사용 예 (placeholder 치환 후 사용):
![[raw/diagrams/my-project/architecture-deployment-2026-05-25.drawio.svg]]
```
## 4. 핵심 시퀀스 / Key sequences
> **필수 섹션.** 최소 1개의 주요 user flow 를 Mermaid sequence diagram 으로. happy path + 주요 error path 함께.
### 4.1 인증된 API 호출 + 응답 검증 + 에러 정규화 (핵심 계약 흐름)
**시나리오**: 이미 인증된 사용자가 화면에 진입해 백엔드 데이터를 읽는다. api-client 가 응답 envelope 을 파싱하고 zod 로 검증한 뒤, 성공/스키마위반/envelope에러를 각각 정규화해 async 4-state 로 표시한다. 이 흐름이 skeleton 의 대다수 계약(api-client · runtime 검증 · error 분류 · async UI state)을 관통한다.
```mermaid
sequenceDiagram
autonumber
actor User
participant UI as Presentation (React)
participant Hook as Application (use-case hook)
participant Q as TanStack Query
participant Client as api-client (adapter)
participant Zod as zod schema
participant API as Backend API
User->>UI: 화면 진입 / 액션
UI->>Hook: useResource()
Hook->>Q: useQuery(key, fetcher)
Q->>Client: GET /api/v1/resource
Client->>API: HTTP GET (traceparent 전파)
alt 200 OK
API-->>Client: {success:true, data, meta}
Client->>Zod: schema.parse(data)
alt 스키마 유효
Zod-->>Client: 검증된 model
Client-->>Q: model
Q-->>Hook: {data, isLoading:false}
Hook-->>UI: view-model
UI-->>User: success 상태 렌더
else 스키마 위반 (SCHEMA_MISMATCH)
Zod-->>Client: ZodError
Client-->>Q: throw ContractError(non-retryable)
Q-->>Hook: {error}
Hook-->>UI: error 상태
UI-->>User: 안전 메시지 (원본/스택 미노출)
end
else 4xx / 5xx (envelope error)
API-->>Client: {success:false, error:{code,category,retryable}}
Client-->>Q: throw ApiError(category)
Q-->>Q: retryable=true 면 exponential backoff 재시도
Q-->>Hook: {error}
Hook-->>UI: error 상태 (category 별 분기)
UI-->>User: error 상태 렌더 (retry 버튼 등)
end
```
**시퀀스 작성 표준 (필수 준수):**
- **`autonumber` 활성화** — 본문에서 "단계 3에서 ..." 처럼 참조 가능
- **`actor` vs `participant`**: 사람은 `actor`, 시스템은 `participant`
- **순서**: User → Frontend → Backend → External (좌→우)
- **화살표 라벨 명세**:
- HTTP: `METHOD /path {body 요약}` (예: `POST /api/v1/login {email, password}`)
- 메시징: `event-name {payload 요약}` (예: `user.signed-up {userId}`)
- 메서드 호출: `method()` (예: `validateCredentials()`)
- **응답**: `-->>` (점선 화살표)
- **alt / opt / loop**: 분기·옵션·반복은 명시적 블록
- **`Note over X,Y`**: 비자명한 동작은 노트로 명시
- **에러 경로 1개 이상 필수**: happy path 만 그리면 미완성
### 4.2 앱 부팅 + 런타임 config 검증 (fail-fast)
**시나리오**: SPA 부팅 시 `VITE_*` env 를 zod 로 검증한다. 필수 값 누락/형식 오류면 화면을 렌더하지 않고 부팅 에러로 즉시 실패시켜 "잘못된 config 로 반쯤 동작하는 상태" 를 차단한다. (Vite 는 `import.meta.env` 로만 env 를 노출하고 `VITE_` 접두 변수는 번들에 박히므로 secret 은 여기 두지 않는다 — [[raw/official-docs/vite-build-tool-official]].)
```mermaid
sequenceDiagram
autonumber
participant Boot as main.jsx (bootstrap)
participant Cfg as config loader (adapter)
participant Env as import.meta.env
participant Zod as env schema (zod)
participant App as React App
Boot->>Cfg: loadConfig()
Cfg->>Env: read VITE_* 변수
Cfg->>Zod: envSchema.parse(raw)
alt env 유효
Zod-->>Cfg: 검증된 config
Cfg-->>Boot: config
Boot->>App: render(App with config)
else 필수 env 누락 / 형식 오류
Zod-->>Cfg: ZodError
Cfg-->>Boot: throw ConfigError (fail-fast)
Boot-->>Boot: 렌더 중단 + 부팅 에러 화면 (secret 미노출)
end
```
## 5. 데이터 모델 / Data model (선택)
> 핵심 엔터티가 5~10개 이상이면 ER 다이어그램으로. 그 미만이면 글로만.
>
> **본 skeleton: 해당 없음 (N/A).** 도메인/비즈니스 로직을 제거했으므로 영속 엔터티가 없다. sample feature slice 의 **최소 view-model 만 zod schema 로** 정의하며(런타임 계약), 관계형 ER 다이어그램은 불요. 도메인 채택 시 각 feature branch 가 자체 view-model schema 를 정의한다.
<!-- ER 다이어그램 없음: 도메인 제거 skeleton (위 N/A 참조). 도메인 채택 시 feature branch 가 view-model zod schema 를 정의. -->
**ER 작성 표준:**
- **PK / FK 표시 필수**
- **관계 카디널리티 기호**:
- `||--||` (1:1)
- `||--o{` (1:N)
- `}o--o{` (M:N)
- `||..o{` (identifying vs non-identifying 표현)
- **관계 라벨**: 동사로 (예: `places`, `contains`, `ordered as`)
- 핵심 엔터티만 (5~10개 이내). 모든 테이블 그리지 말 것.
## 6. 기술 결정 / Tech decisions
> 주요 기술 선택과 이유. 트레이드오프 + 근거 자료 link 필수.
> **범례**: `deferred` = 이번 `/project-spec` 자동조사 6개 한도 밖으로 미룬 결정 — 근거는 해당 branch 의 `/branch-spec` 단계에서 채운다(R3 Blocking 면제). *사용자 결정* 은 근거 자료 없이도 확정된 제약이다.
| 결정 영역 | 선택 | 검토한 대안 | 채택 이유 | 트레이드오프 | 근거 자료 |
|---|---|---|---|---|---|
| 언어 | JavaScript (ESM) | TypeScript · JS+JSDoc | 사용자 지정 제약. 진입 장벽·빌드 단순 | ⚠️ 컴파일타임 타입 계약 **부재** → 경계 계약을 런타임(zod)이 짊. UNSUPPORTED 아니라 사용자 결정 | (사용자 결정) |
| 빌드/번들 | Vite (client-only SPA) | Next.js · CRA · Remix | 서버 런타임 불필요·백엔드 API 와 분리, native ESM dev server + HMR, `import.meta.env` 로 env 노출(secret 은 번들 금지) | SSR/SEO 필요 시 부적합(범위 밖) | `[[raw/official-docs/vite-build-tool-official]]` |
| UI 라이브러리 | React 19 | Vue · Svelte · SolidJS | 컴포넌트 기반 UI 분해, 생태계·채용시장, 기존 SPA 자산 | 러닝커브·리렌더 관리 | `[[raw/official-docs/react-ui-library-official]]` |
| 스타일링 | Tailwind CSS | CSS Modules · styled-components · vanilla-extract | utility-first + theme 토큰으로 magic number 제거, 런타임 CSS-in-JS 비용 0 | 클래스 verbosity·초기 학습 | `[[raw/official-docs/tailwind-css-utility-first-official]]` |
| server-state | TanStack Query | SWR · RTK Query · 수제 | server-state 를 client-state 와 분리, 캐시·동기화·stale 계약을 표준 제공(수제 시 놓침) | 의존성·개념 학습 | `[[raw/official-docs/tanstack-query-server-state-official]]` |
| 런타임 검증 | zod | yup · valibot · 수제 | 무타입 JS 에서 경계 계약을 `.parse()` 런타임으로 강제, `ZodError` 로 실패 신호 | 번들 크기·스키마 유지 | `[[raw/official-docs/zod-runtime-schema-validation-official]]` |
| 라우팅 | React Router (Declarative Mode) | TanStack Router · wouter | client-only SPA nested route 표준(SSR/framework 모드 불요) | 타입-라우팅은 TanStack 우위; guard/loader 는 별도 계약(branch) | `[[raw/official-docs/react-router-official]]` |
| client-state | Context / Zustand (경량) | Redux Toolkit · Jotai | server-state 는 Query 담당 → client-state 경량으로 충분 | 규모 증가 시 재검토 | `deferred``/branch-spec` |
| 테스트 | Vitest + RTL + Playwright | Jest · Cypress | Vite 정합(Vitest), 컴포넌트(RTL)·e2e(Playwright) 계층 분리 | 도구 3종 셋업 | `deferred``/branch-spec` |
| 아키텍처 패턴 | Clean Architecture (계층 + 의존 규칙) | Feature-Sliced Design · Layered · Atomic | 의존 방향 강제 + 도메인 순수성(백엔드 대칭) | 프론트 적용 시 boilerplate | `deferred` — branch 단계 `wiki-decision-researcher` 로 FSD 심층 비교 |
| 계층 강제 도구 | ESLint boundaries / dependency-cruiser | 수동 리뷰 | 의존 규칙 자동 강제(백엔드 ArchUnit 대응) | 규칙 유지보수 | `deferred``/branch-spec` |
## 7. 비기능 요구사항 / Non-functional
> 측정 가능한 비기능 목표. 없으면 명시적으로 "해당 없음".
> 수치는 초기 default. 각 값의 측정·강제는 대응 계약 branch 가 담당(§8.0).
- **성능**: 초기 JS 번들 **< 200KB gzip**(default) · route-level code splitting · web-vitals **LCP < 2.5s / CLS < 0.1 / INP < 200ms** 를 CI 게이트가 검사 → `feature-web-vitals-performance-budget-contract` (선택 포함).
- **가용성**: 정적 자산이므로 프론트 자체 다운타임은 CDN 가용성에 종속. 백엔드 장애 시 async 4-state 의 `error`**부분 degrade**(앱 셸 유지), retryable 요청만 backoff 재시도.
- **확장성**: 정적 자산 CDN 배포, 상태는 클라이언트 → 수평 확장 무관(서버 인스턴스 없음).
- **보안**: 인증 토큰 획득/저장/refresh 는 **범위 밖 → [[raw/project-notes/keycloak-patterns-overview]] 로 위임**. 본 skeleton 은 (1) secret 의 클라이언트 번들 유입 금지(`VITE_*` 에 secret 금지), (2) XSS 기본 방어(`dangerouslySetInnerHTML` 금지·CSP 권고), (3) 응답 원본/스택/토큰의 화면·로그 노출 금지만 계약화.
- **운영 / Observability**: 구조화 클라이언트 로깅 + 백엔드로 `traceparent` 전파 + (선택) 에러 트래킹 sink. 토큰·PII 로그 금지 → `feature-frontend-observability-logging-trace-contract` (선택 포함).
- **접근성 / a11y**: WCAG 2.1 AA 기본선 — 키보드 네비게이션·focus 관리·ARIA·a11y lint → `feature-accessibility-baseline-contract` (선택 포함).
- **재해 복구 / DR**: 정적 자산 재배포로 복구(RTO 낮음), 클라이언트 무상태(RPO 무관).
- **컴플라이언스**: **해당 없음** — 도메인·PII 미수집(skeleton). 도메인 채택 시 재평가.
- **i18n**: **범위 밖 (deferred)** — 이번 skeleton 미포함(사용자 미선택). 후속 프로젝트에서 별도 계약으로.
## 8.0 Branch 분해 / 실행계획 (Branch decomposition)
> **`/project-spec` 가 채우는 핸드오프 섹션.** 이 hub 에서 깊은 조사로 도출된 *자식 branch 의 네이밍과 달성 목표 조건만* 적는다. 각 branch 의 *결정 내용·메커니즘은 여기 적지 않는다* (SSOT 이중화 방지) — 그건 `/branch <slug>` 로 생성 후 `/branch-spec` 가 깊게 채운다.
>
> 작성 규칙:
> - `branch slug` 는 `rules/naming-conventions.md` §2.1 준수 (prefix 4종 `feature-`/`fix-`/`chore-`/`experiment-` 중 하나 + kebab-case, numbered hierarchy 금지).
> - `달성 목표 조건` 은 **측정가능**해야 함 ("잘 된다" 금지). 그 branch 가 "끝났다"고 말할 수 있는 검증 가능한 결과.
> - `우선순위` 는 실행 순서(P1 먼저). 의존이 있으면 `의존` 칸에 선행 branch slug.
| branch slug | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-frontend-clean-architecture-layering-contract` | presentation/application/domain/adapters 4계층 디렉터리 + 계층별 허용 import 규칙 문서화, sample import 예시가 규칙과 일치 | P1 | - |
| `feature-api-client-response-envelope-contract` | fetch 래퍼가 성공/실패 envelope 파싱 + requestId/traceId 전파, 계약 위반 응답 시 정의된 에러 throw (contract test green) | P1 | - |
| `feature-runtime-schema-validation-contract` | 경계(API 응답·form)에서 zod parse 강제, 위반 시 SCHEMA_MISMATCH 로 정규화 (test) | P1 | `feature-api-client-response-envelope-contract` |
| `feature-frontend-error-classification-boundary-contract` | 에러 category(retryable/non-retryable/auth/validation) 분류 + React ErrorBoundary + 원본/스택 미노출 test | P1 | `feature-api-client-response-envelope-contract` |
| `feature-frontend-env-runtime-config-contract` | 부팅 시 VITE_ env 를 zod 검증, 필수값 누락 시 fail-fast, secret 번들 미유입 lint (test) | P1 | - |
| `feature-frontend-architecture-enforcement-lint-contract` | ESLint boundaries/dependency-cruiser 가 역방향 import 시 CI 실패, 위반 0건 리포트 | P1 | `feature-frontend-clean-architecture-layering-contract` |
| `feature-async-ui-state-contract` | 모든 async 표면이 loading/empty/error/success 4상태 렌더, sample 에 4상태 각각 RTL test | P2 | `feature-frontend-error-classification-boundary-contract` |
| `feature-boundary-mapper-viewmodel-contract` | API DTO → view-model 매핑 강제(presentation 이 raw DTO 미접근), 매핑 누락 시 test 실패 | P2 | `feature-runtime-schema-validation-contract` |
| `feature-server-state-caching-contract` | TanStack Query query-key 규약·staleTime·invalidation·retry 정책 + 캐시 무효화 시나리오 test | P2 | `feature-api-client-response-envelope-contract` |
| `feature-client-state-management-contract` | client-state 경계: server data 를 client store 에 중복 저장 금지, 위반 감지 test | P2 | `feature-server-state-caching-contract` |
| `feature-routing-navigation-guard-contract` | route 구조 + guarded route(비인증 redirect)·error/loading route·404 정의, 네비게이션 test | P2 | - |
| `feature-tailwind-design-token-styling-contract` | theme 토큰(spacing/color scale) 정의 + arbitrary value 금지 lint + 컴포넌트 스타일 경계 규칙 | P2 | - |
| `feature-frontend-test-taxonomy-contract` | unit/component(RTL)/integration(msw)/e2e(Playwright) 4계층 정의 + 각 계층 예제 test 1개 | P2 | - |
| `feature-frontend-observability-logging-trace-contract` | 구조화 로거 + traceparent 전파 + 토큰/PII redaction, 금지 필드 로그 유입 시 test 실패 | P2 | `feature-api-client-response-envelope-contract` |
| `feature-sample-feature-slice-contract-fixture` | 도메인 없는 sample slice 가 api-client·zod·error·4-state·CA 계층 모두 관통 + 계약 위반 시 실패 test 세트, 채택 시 제거 가능 표시 | P2 | `feature-async-ui-state-contract` |
| `feature-frontend-build-bundle-supply-chain-contract` | Vite 프로덕션 빌드 + code splitting + lockfile 고정 + 번들 분석, 번들 예산 초과 시 CI 실패 | P3 | - |
| `feature-web-vitals-performance-budget-contract` | LCP/CLS/INP 임계 + 번들 크기 예산을 CI 게이트로 측정, 초과 시 실패 | P3 | `feature-frontend-build-bundle-supply-chain-contract` |
| `feature-accessibility-baseline-contract` | WCAG 2.1 AA 기본선 + 키보드 네비·focus·ARIA + a11y lint(axe) CI 게이트, sample slice 통과 | P3 | `feature-async-ui-state-contract` |
| `feature-frontend-ci-quality-gates-contract` | lint·test·번들예산·a11y 게이트를 CI 로 통합, 하나라도 실패 시 머지 차단 | P3 | `feature-frontend-test-taxonomy-contract` |
| `feature-frontend-api-compatibility-contract` | 백엔드 API additive 변경 관대 + breaking 감지(스키마 버전) 정책, 불일치 시 명시적 실패 | P3 | `feature-runtime-schema-validation-contract` |
> 채운 뒤: `/branch <slug>` → `/branch-spec <slug> <근거 URL...>` → `/depth <slug>` 순으로 각 branch 를 깊게 작성.
## 8. Cluster / 묶음 (이 프로젝트에 묶이는 모든 raw 자료)
> 본 project-note 는 cluster 의 entry point. 모든 branch / errors / interviews / lectures / job-postings / blog-topics / sources 가 여기로 upward link. hub 측에서도 카테고리별 명시.
### 8.1 Branches (project 의 직접 자식 branch — `parent_branch:` 비어있음)
> project-note 가 직접 가리키는 branch 들. 자식 branch 가 있는 branch 는 자기 Cluster 섹션에서 자식들을 참조하므로 여기에는 등재 안 함.
> 아직 branch 미생성. §8.0 분해표의 각 slug 를 `/branch <slug>` → `/branch-spec <slug> <근거>` → `/depth <slug>` 로 전개하면 여기에 등재한다. P1 6개(layering / api-client / runtime-validation / error-boundary / env-config / arch-enforcement-lint)부터 착수 권장.
### 8.2 Sources (프로젝트 전체 차원 foundational 조사)
> 특정 branch 에 묶이지 않는 전체 프로젝트 단위 근거 자료.
- `[[raw/official-docs/vite-build-tool-official]]` — Vite 빌드/dev server + `import.meta.env` env·secret 계약 근거
- `[[raw/official-docs/react-ui-library-official]]` — React 컴포넌트 모델
- `[[raw/official-docs/tailwind-css-utility-first-official]]` — Tailwind utility-first + theme 토큰(magic number 제거)
- `[[raw/official-docs/tanstack-query-server-state-official]]` — server-state vs client-state 구분 + 캐시 계약
- `[[raw/official-docs/zod-runtime-schema-validation-official]]` — 무타입 JS 런타임 경계 검증(`.parse`/`.safeParse`)
- `[[raw/official-docs/react-router-official]]` — Declarative Mode nested routing (SSR/framework 모드 아님)
### 8.3 Errors (branch 외 발생한 환경·운영 이슈)
- (아직 없음 — 구현 착수 후 환경·운영 이슈 발생 시 등재)
### 8.4 Interview prep (프로젝트 전체 차원 면접 질문)
- (아직 없음)
### 8.5 Blog topics / job-posting tie-ins
- (아직 없음)
### 8.6 Derived wiki documents
- (아직 없음 — canonical 승급은 verified 사실 발생 후 `/ingest``wiki/projects/` 에)
## 9. 검증 등급 / Verification status
> 본 project-note 의 각 부분이 어느 등급까지 검증되었는지. CLAUDE.md §15 lifecycle 참조.
| 영역 | 등급 | 근거 |
|---|---|---|
| 아키텍처 다이어그램 | `planned` (`needs-diagram`) | §3.1 사용자 작성 예정, 아직 파일 없음 |
| 시퀀스 다이어그램 | `documented-only` | §4 Mermaid 2개(설계 흐름), 코드 대응물 없음 |
| 기술 결정 | `documented-only` | §6 6개 결정이 official 근거 보유 — 단 *결정*일 뿐 구현 아님 |
| 비기능 요구사항 | `planned` | §7 임계는 목표치, 측정값 없음(코드 미착수) |
### 9.1 실제 구현 내용 (`actually-implemented`)
> 코드에 존재하는 것만. 파일·함수 단위로 구체적으로. 면접에서 "구현했다"고 말해도 되는 부분.
- **없음** — 레포 미생성(`NO_GROUND_TRUTH`). 이 항목은 Vite 레포 생성 + `src/` grep 확인 후에만 채운다.
### 9.2 로컬/dev 검증 (`locally-verified`)
> 로컬 또는 dev 환경에서 동작 확인한 부분. 어떻게 검증했는지(로그/테스트/측정값).
- **없음** (기획 단계).
### 9.3 운영 검증 (`prod-verified`)
> 운영(prod) 환경에서 동작·성능을 확인한 부분. 근거(릴리즈 노트 / 운영 로그 / 모니터링 대시보드 / 인시던트 보고서)를 함께 명시.
- **없음** (기획 단계).
### 9.4 문서/계획만 존재 (`documented-only` / `planned`)
> 설계 문서에만 있고 아직 구현 안 된 것. 면접에서 "구현했다"고 말하면 안 되는 부분.
- **본 hub 전체가 현재 이 등급** — §1~§8 의 모든 계약·아키텍처·branch 분해는 `planned`(설계만). 외부에 "구현했다"고 말하면 안 됨.
- 아키텍처 `.drawio` = `needs-diagram`(§3.1, 사용자 작성 예정).
- 각 계약의 `actually-implemented` 승격은 대응 branch 착수 + 코드 커밋 + contract test green 후 `src/` grep 으로 확정.
## 10. 면접·외부 공개 답변 경계
### 10.1 자신 있게 답할 수 있는 범위
- 이 skeleton 이 **왜** 필요한가 — 도메인 없는 운영 계약을 프론트엔드에 대칭 적용한 설계 의도(§1·§2)와 백엔드 skeleton 과의 대응 관계.
- 각 스택의 **설계 선택과 트레이드오프**(§6) — Vite/React/Tailwind/TanStack Query/zod/React Router 를 왜 골랐는지 + 검토한 대안 + 근거(official 문서).
- CA 계층을 프론트엔드에 적용하고 lint 로 의존 방향을 강제하려는 **설계**(§3, §8.0), 무타입 JS 에서 zod 로 계약을 런타임 강제하는 이유.
### 10.2 적당히 답할 수 있는 범위
- 각 계약의 구체 구현 메커니즘 — 아직 branch 설계 단계이므로 "이렇게 **설계**했다" 수준까지만. "이미 구현했다"로 넘어가면 안 됨.
### 10.3 답하면 안 되는 / "공식 문서 다시 확인" 해야 하는 범위
- "이 skeleton 을 실제로 **구현/운영**했다" — 거짓(코드 미착수). "설계·계획했다"만 사실.
- 성능 수치(200KB, LCP/CLS/INP)를 **측정 결과**처럼 — 전부 목표치.
- 인증 토큰 처리 세부 — 본 skeleton 범위 밖([[raw/project-notes/keycloak-patterns-overview]]).
### 10.4 과장 금지 지점
> 외부에 설명할 때 사실보다 부풀려지기 쉬운 표현. 자기 검열용.
- "프로덕션 검증된 프론트엔드 아키텍처" ❌ → `planned`.
- "번들 200KB 달성 / web-vitals 통과" ❌ → 목표치, 미측정.
- "TanStack Query 로 캐시 최적화 경험" ❌ → 설계 결정일 뿐 구현 경험 아님.
## 11. Architecture Review Checklist (작성 / 갱신 시 self-check)
> 본 project-note 가 hub 역할을 제대로 하려면 모두 ✓ 여야 함.
- [ ] 한 줄 요약 + 현재 상태 + 나의 역할 채워짐 (§1)
- [ ] 측정 가능한 성공 기준 1개 이상 (§2.3)
- [ ] **아키텍처 다이어그램 (`.drawio.svg`) 1개 이상 첨부** (§3.1)
- [ ] 다이어그램이 [[rules/diagram-standards]] v2 minimalist 통과 — `wiki-diagram-reviewer` 로 ≥95 점 (vertex ≤ 10 / edge ≤ 8 / callout ≤ 1 / 박스 라벨 ≤ 2줄 / 화살표 라벨 ≤ 5단어 / 80% 회색 + 강조 ≤ 2 / boundary 정보 있을 때만 / 5초 + 30초 룰)
- [ ] 외부 시스템이 점선 + 회색 fill 로 시각적 구분 (legend 불필요 — 표준 컨벤션)
- [ ] **시퀀스 다이어그램 1개 이상 (Mermaid)** — happy path + error path 함께 (§4)
- [ ] 데이터 모델은 5~10개 이상 엔터티 시에만 ER 그림 (§5)
- [ ] 주요 기술 결정 표에 트레이드오프 + 근거 자료 link 명시 (§6)
- [ ] 비기능 요구사항이 측정 가능한 수치 (§7)
- [ ] **Branch 분해표 채워짐** — 각 자식 branch 가 naming-conventions slug + 측정가능 목표조건 (§8.0)
- [ ] Cluster 섹션의 project 직접 자식 branch 목록 채워짐 (§8.1)
- [ ] 검증 등급이 각 영역별로 매겨짐 (§9)
- [ ] 면접 답변 경계 명시 (§10)
- [ ] 마지막 architecture review 날짜 frontmatter `architecture_review:` 에 기록
## 12. 다이어그램 파일 관리 가이드
> draw.io 파일과 Mermaid 코드 모두 본 project-note 와 함께 라이프사이클 관리.
### 12.1 draw.io (`.drawio.svg`)
- 저장 위치: `raw/diagrams/<project-slug>/`
- 명명: `architecture-{viewpoint}-{YYYY-MM-DD}.drawio.svg`
- viewpoint 예: `overview`, `deployment`, `data-flow`, `security`, `network`
- 갱신 시: 새 날짜로 파일 추가 + frontmatter `diagrams:` 에 모든 활성 다이어그램 나열
- 폐기 시: 파일 삭제하지 말고 `diagrams/<project>/archived/` 하위로 이동 + project-note 에서 link 제거
- Obsidian 임베딩 문법: `![[architecture-overview-2026-05-25.drawio.svg]]`
### 12.2 Mermaid
- 본 문서 본문에 직접. 외부 파일로 분리 안 함.
- 갱신 시: code block 그대로 수정 (git diff 친화적)
- 너무 커지면 (>50줄) 별도 sub-branch 의 branch-note 로 분리하고 본문에서는 요약만
### 12.3 그림 변경 시 의무
- 아키텍처가 변경되면 본 project-note 의 `architecture_review:` frontmatter 날짜 갱신
- 변경 사유는 §6 "기술 결정" 표에 한 줄 추가 (예: "2026-06-01: PostgreSQL → Aurora 변경 — 이유: 가용성 SLO 99.99%")
- 폐기된 결정도 표에서 지우지 말고 status 컬럼 추가로 표시 (`active` / `deprecated` / `superseded-by-<row>`)
## 13. 관련 개념 / Related concepts
> §3~§6 표에 등장하지 않은 보조 개념·자료.
- `[[raw/project-notes/ca-skeleton-operational-contract]]` — 백엔드 대응 skeleton (운영 계약 철학의 원본, 본 프로젝트의 대칭 기준)
- `[[raw/project-notes/keycloak-patterns-overview]]` — 인증 토큰 처리 위임처(본 skeleton 범위 밖 경계)
- `[[rules/diagram-standards]]` — §3 아키텍처 / §4 시퀀스 다이어그램 컨퍼런스급 표준
- `[[rules/naming-conventions]]` — §8.0 branch slug 규칙
## 14. 다음 단계 / Next steps
- [ ] 아키텍처 `.drawio` 2개 작성(§3.1) → `wiki-diagram-reviewer` ≥95 확인 → frontmatter `diagrams:`/`architecture_review:` 기입 (readiness `Ready-pending-user` 해소)
- [ ] Vite + React + Tailwind 레포 생성 (§1 저장소 채우기 — 이후 `actually-implemented` 승격 근거)
- [ ] P1 6개 branch 착수: `/branch feature-frontend-clean-architecture-layering-contract``/branch-spec``/depth` (나머지 P1 동일)
- [ ] `deferred` 결정(client-state / 테스트 / 계층강제 도구 / 아키텍처 패턴)을 branch 단계에서 `wiki-decision-researcher` 로 대안 심층 조사
- [ ] frontmatter `tags:` 확장 검토 — 필요 시 taxonomy 에 L4 `vite`·`tanstack-query`·`zod` 등재 후 반영