--- 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`, ``, `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//` 하위에 `.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]] ``` > ⏳ **`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줄) - **화살표 라벨**: ` ` — 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 작성 표준:** - **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 ` 로 생성 후 `/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 ` → `/branch-spec <근거 URL...>` → `/depth ` 순으로 각 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 ` → `/branch-spec <근거>` → `/depth ` 로 전개하면 여기에 등재한다. 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//` - 명명: `architecture-{viewpoint}-{YYYY-MM-DD}.drawio.svg` - viewpoint 예: `overview`, `deployment`, `data-flow`, `security`, `network` - 갱신 시: 새 날짜로 파일 추가 + frontmatter `diagrams:` 에 모든 활성 다이어그램 나열 - 폐기 시: 파일 삭제하지 말고 `diagrams//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-`) ## 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` 등재 후 반영