From 64a5a0760bc84f55d01e12b062808292eaf97848 Mon Sep 17 00:00:00 2001 From: thxforall <113906780+thxforall@users.noreply.github.com> Date: Fri, 15 May 2026 19:19:50 +0900 Subject: [PATCH 1/4] =?UTF-8?q?docs:=20ADR=20+=20architecture=20=E2=86=92?= =?UTF-8?q?=20vault=20stub=20=EC=A0=84=ED=99=98=20(refs=20#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 본문이 decoded-docs vault Architecture/ 로 이미 마이그레이션(M2) 완료된 상태. monorepo 원본은 vault redirect stub만 남김 — sync-policy.md 의 "MOVED" 룰 대로 vault 가 SoT, monorepo 원본은 stub. 변경 파일 (6): - docs/adr/ADR-0001-ai-dev-boilerplate.md - docs/adr/ADR-0002-llm-wiki-foundation.md - docs/architecture/README.md - docs/architecture/assets-project.md - docs/architecture/data-pipeline.md - docs/architecture/state-management.md 각 파일은 vault GitHub 링크 + Obsidian path + sync-policy 참조만 보유. 검증: vault 본문은 monorepo 본문에 frontmatter(date/tags/source/migrated_on) 만 추가된 형태로 정보 손실 없음. CLAUDE.md / docs/agent/ 의 architecture·adr 링크는 stub 을 거쳐 vault 로 redirect 되는 구조이므로 깨지지 않음. CLAUDE.md 직링크 교체는 Step 5(라우팅 업데이트)에서 처리. --- docs/adr/ADR-0001-ai-dev-boilerplate.md | 186 +------ docs/adr/ADR-0002-llm-wiki-foundation.md | 69 +-- docs/architecture/README.md | 505 +------------------ docs/architecture/assets-project.md | 172 +------ docs/architecture/data-pipeline.md | 528 +------------------- docs/architecture/state-management.md | 602 +---------------------- 6 files changed, 44 insertions(+), 2018 deletions(-) diff --git a/docs/adr/ADR-0001-ai-dev-boilerplate.md b/docs/adr/ADR-0001-ai-dev-boilerplate.md index 8940d4a7..3d7f831d 100644 --- a/docs/adr/ADR-0001-ai-dev-boilerplate.md +++ b/docs/adr/ADR-0001-ai-dev-boilerplate.md @@ -1,181 +1,9 @@ ---- -title: "ADR-0001: Multi-AI Development Boilerplate" -owner: human -status: approved -updated: 2026-04-17 -tags: [harness, agent] ---- - # ADR-0001: Multi-AI Development Boilerplate -## Status - -Accepted (v1.0) - -## Context - -We use multiple AI tools in our development workflow: - -- **Claude** (via gstack/Superpowers): For refactors, code analysis, and TDD-driven implementation -- **Cursor**: Main coding assistant for feature implementation -- **Gemini**: Documentation generation -- **Codex**: Spec templates and checklists - -Each tool has different strengths and should be used intentionally. Without clear guidelines, developers may: - -- Use the wrong tool for a task -- Waste time figuring out which tool to use -- Create inconsistent workflows -- Duplicate effort across tools - -## Decision - -We will create a dedicated AI playbook structure with: - -1. **Shared principles** (`docs/ai-playbook/01-principles.md`): - - Language conventions (Korean conversation, English code) - - Code quality standards - - Safety guidelines - - Workflow principles - -2. **Tool-specific profiles** (`docs/ai-playbook/*-profile.md`): - - Clear role definition for each tool - - Usage guidelines and examples - - Integration points with other tools - - Do's and don'ts - -3. **Workflow overview** (`docs/ai-playbook/02-workflow-overview.md`): - - How tools work together - - Typical workflows for common tasks - - Integration with existing infrastructure - -4. **Configuration files**: - - `.cursor/rules/`: Cursor-specific rules (JSONC format) - - `.claude/settings.json`: Claude Code harness configuration - - `.codex/config.json`: Codex CLI configuration - -5. **Prompt templates** (`docs/prompts/`): - - Templates for Gemini documentation generation - - Templates for Codex spec generation - -6. **Integration with existing infrastructure**: - - Integrate with `.claude/` harness workflow (gstack, Superpowers, GSD) - - Preserve `.cursor/rules/` for Cursor users - -## Consequences - -### Positive - -- **Clear mental model**: Developers know which tool to use when -- **Faster onboarding**: New team members understand the workflow quickly -- **Consistency**: Standardized approach across the team -- **Better collaboration**: Clear handoffs between tools -- **Reduced confusion**: Less time spent deciding which tool to use - -### Negative - -- **Additional maintenance**: More documentation files to keep updated -- **Learning curve**: Team needs to learn the new structure -- **Potential rigidity**: May feel restrictive if not balanced with flexibility - -### Risks - -- **Documentation drift**: Docs may become outdated if not maintained -- **Over-engineering**: Too much structure can slow down simple tasks -- **Tool version changes**: Tool updates may require doc updates - -### Mitigation - -- **Version tracking**: Each profile includes "Last verified with" date -- **Kill-switch**: If setup time exceeds work time, simplify to core principles -- **Regular review**: Update docs when tools or workflows change -- **Flexibility**: Structure is advisory, not mandatory - -## Alternatives Considered - -### A: Single Source of Truth (Single File) - -**Approach**: One `docs/ai-playbook/ai-roles.md` file with all rules - -**Rejected because**: - -- Less optimized for each tool's format -- Harder to maintain tool-specific guidance -- Doesn't leverage tool-specific configuration formats - -### B: Tool-Specific Profiles + Shared Principles (Selected) - -**Approach**: Separate profiles with shared principles - -**Selected because**: - -- Best balance of consistency and tool optimization -- Easier to maintain tool-specific guidance -- Supports tool-specific configuration formats - -### C: Task Pipeline Approach - -**Approach**: Process-based documentation (e.g., "small-refactor-claude.md") - -**Rejected because**: - -- Harder to maintain when tools change -- Less reusable across different tasks -- Doesn't provide clear tool roles - -## Implementation - -### Phase 1: Structure Creation (v1.0) - -- Create directory structure -- Write core principles and tool profiles -- Create configuration files -- Set up prompt templates - -### Phase 2: Integration (v1.0) - -- Integrate with existing `.specify/` infrastructure -- Update workflow overview with integration points -- Create ADR document - -### Phase 3: Experimentation (v1.1) - -- Track usage in `docs/ai-playbook/usage-log.md` -- Gather feedback from team -- Refine based on actual usage - -### Phase 4: Refinement (v1.1+) - -- Update profiles based on learnings -- Simplify if needed (kill-switch criteria) -- Maintain version information - -## Success Criteria - -- **Time to first commit**: Reduced by 50%+ for new tasks -- **Tool switching frequency**: Reduced confusion about which tool to use -- **Confusion score**: "Which AI to use when?" score improves (1-5 scale) -- **Workflow clarity**: Team feels workflow is clear and helpful - -## Version History - -- **v1.0** (2025-01-27): Initial boilerplate structure -- **v1.1** (TBD): Updates based on Week 1 usage and feedback - -## References - -- `docs/ai-playbook/01-principles.md`: Core principles -- `docs/ai-playbook/02-workflow-overview.md`: Workflow integration -- `.specify/memory/constitution.md`: Project constitution -- `.cursor/rules/`: Cursor configuration - -## Notes - -This ADR documents the decision to create a multi-AI development boilerplate. The structure is designed to be: - -- **Flexible**: Can adapt to tool changes -- **Maintainable**: Clear version tracking and update process -- **Practical**: Based on actual workflow needs -- **Evolvable**: Can be simplified if needed (kill-switch criteria) - -The boilerplate complements, rather than replaces, existing infrastructure like `.specify/` and `.claude/commands/`. +> **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** +> +> 본문: [`Architecture/adr/ADR-0001-ai-dev-boilerplate.md`](https://github.com/decodedcorp/decoded-docs/blob/main/Architecture/adr/ADR-0001-ai-dev-boilerplate.md) +> +> Obsidian path: `Architecture/adr/ADR-0001-ai-dev-boilerplate` +> +> Sync policy: [Guides/sync-policy](https://github.com/decodedcorp/decoded-docs/blob/main/Guides/sync-policy.md) diff --git a/docs/adr/ADR-0002-llm-wiki-foundation.md b/docs/adr/ADR-0002-llm-wiki-foundation.md index 57c24e93..dbc43e8d 100644 --- a/docs/adr/ADR-0002-llm-wiki-foundation.md +++ b/docs/adr/ADR-0002-llm-wiki-foundation.md @@ -1,64 +1,9 @@ ---- -title: "ADR-0002: LLM Wiki Foundation" -owner: human -status: approved -updated: 2026-04-17 -tags: [architecture, harness, agent] -related: - - docs/superpowers/specs/2026-04-17-llm-wiki-foundation-design.md - - docs/wiki/schema/README.md ---- - # ADR-0002: LLM Wiki Foundation -- **Status:** Accepted (2026-04-17) -- **Supersedes:** — -- **Related:** Issue #153, PR #223, Spec `docs/superpowers/specs/2026-04-17-llm-wiki-foundation-design.md` - -## Context - -decoded-monorepo는 현재 4개 이상의 문서 소스가 공존한다: 루트 `CLAUDE.md`, `.cursor/rules/*.mdc`, `docs/agent/`, `docs/ai-playbook/`, `.planning/codebase/`. 각각 부분적으로 중복된 컨벤션·아키텍처·에이전트 규칙을 담고 있으며 정본(SSOT) 규칙이 없다. 이슈 #153은 이 상태를 정비하면서 Karpathy LLM wiki 컨셉(LLM이 markdown 위키를 자동 ingest/lint/update하는 구조)에서 영감을 얻어 에이전트 하네스 프로그래밍을 업그레이드한다. - -## Decision - -`docs/` 하위에 LLM wiki 2계층 구조(`docs/wiki/wiki/` + `docs/wiki/schema/`)를 도입한다. Karpathy의 3계층(source/wiki/schema) 중 `source`는 "source = code"인 이 repo 특성상 생략한다. 기존 `docs/agent/`는 topic summary 허브로 확장하고, 기존 topic 폴더(`docs/architecture/` 등)에 distributed `agent.md`를 두지 않는다. 컨벤션·하네스 규칙은 `docs/wiki/schema/`로 정본화하고 `CLAUDE.md`·`.cursor/rules/`는 pointer로 축소한다. - -## Alternatives Considered - -1. **Do nothing** — 현 상태 유지. 드리프트 지속. -2. **Flatten everything into `docs/agent/`** — Karpathy 컨셉 포기, 성장 경로 상실. -3. **Full Karpathy 3계층(source/wiki/schema)** — `source` 계층 억지 적용. -4. **Consolidation only** (schema 도입 없이 중복만 줄임) — SSOT 구조화 기회 상실. - -## Rationale - -에이전트 작업 지식의 누적을 허용(hybrid D 기반)하면서, OMC critic·architect 리뷰가 지적한 3가지 위험(distributed `agent.md`, `source` 계층 억지, 자동화 지연)을 동시 회피한다. - -## Consequences - -### Positive - -- 컨벤션 조회 시 정본 1곳으로 수렴 (현재 4+). -- LLM·Cursor·Claude가 공통 규약 파일을 참조. -- 에이전트 지식 누적을 받아내는 전용 공간(`wiki/wiki/`) 확보. - -### Negative - -- 새 디렉토리 도입으로 초기 learning curve. -- Phase 1 산출물이 Sub-3 자동화 없이 수동 유지. Sub-3 지연 시 drift 위험. -- 기존 파일 프론트매터 추가·pointer 축소 migration 비용. - -## Reversibility - -Phase 1 후 2~3개월 내 이 구조가 overhead로 드러나면: - -1. 이 ADR을 `Superseded`로 변경하고 사유 기록. -2. `docs/wiki/schema/**` 삭제 또는 `/archive/` 이동. -3. PR-C에서 축소한 `CLAUDE.md`·`.cursor/rules/` 컨벤션 섹션을 `git revert`로 복원. -4. `docs/agent/*-summary.md` 파일은 유지 여부를 파일별로 재평가. -5. `.planning/codebase/`와 기존 `docs/agent/` 인벤토리는 변경 없이 잔존. - -## Implementation - -- Plan: `docs/superpowers/plans/2026-04-17-llm-wiki-foundation.md` -- Follow-ups: Sub-3 (자동화), Sub-4 (CLAUDE.md·.cursor/rules 전면 리팩토링) +> **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** +> +> 본문: [`Architecture/adr/ADR-0002-llm-wiki-foundation.md`](https://github.com/decodedcorp/decoded-docs/blob/main/Architecture/adr/ADR-0002-llm-wiki-foundation.md) +> +> Obsidian path: `Architecture/adr/ADR-0002-llm-wiki-foundation` +> +> Sync policy: [Guides/sync-policy](https://github.com/decodedcorp/decoded-docs/blob/main/Guides/sync-policy.md) diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 8424bc57..7feed864 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -1,500 +1,9 @@ # System Architecture -> Version: 1.0.0 -> Last Updated: 2026-01-14 -> Purpose: 시스템 아키텍처 개요 및 컴포넌트 의존성 - ---- - -## Overview - -Decoded는 K-콘텐츠 패션 발견 플랫폼으로, 모노레포 구조의 Next.js 웹 앱과 Expo 모바일 앱으로 구성됩니다. - ---- - -## 1. System Architecture Diagram - -![System Architecture](../diagrams/system-architecture.excalidraw.png) - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ CLIENT LAYER │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ packages/web │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ Next.js │ │ React │ │ TypeScript │ │ │ -│ │ │ 16.0.7 │ │ 18.3.1 │ │ 5.9.3 │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • App Router │ │ • Components │ │ • Type-safe │ │ │ -│ │ │ • SSR/SSG │ │ • Hooks │ │ • Interfaces │ │ │ -│ │ │ • API Routes │ │ • Context │ │ • Generics │ │ │ -│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ Tailwind │ │ Zustand │ │ React Query │ │ │ -│ │ │ 3.4.18 │ │ 4.5.7 │ │ 5.90.11 │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • Utility │ │ • 전역 상태 │ │ • 서버 상태 │ │ │ -│ │ │ • Design │ │ • Filter │ │ • Caching │ │ │ -│ │ │ System │ │ • Search │ │ • Mutations │ │ │ -│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ GSAP │ │ Motion │ │ Lenis │ │ │ -│ │ │ 3.13.0 │ │ 12.23.12 │ │ 1.3.15 │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • FLIP │ │ • Gestures │ │ • Smooth │ │ │ -│ │ │ • ScrollTrig │ │ • Transitions│ │ Scroll │ │ │ -│ │ │ • Timeline │ │ • Spring │ │ • Virtual │ │ │ -│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ packages/shared │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ Hooks │ │ Stores │ │ Queries │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • useImages │ │ • filter │ │ • images │ │ │ -│ │ │ • useDebounce│ │ • search │ │ • items │ │ │ -│ │ │ │ │ • hierarchic │ │ • adapter │ │ │ -│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ packages/mobile │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ Expo │ │ React Native │ │ Reanimated │ │ │ -│ │ │ SDK 54 │ │ 0.81 │ │ 4 │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • Router 6 │ │ • iOS/Andro │ │ • 60fps │ │ │ -│ │ │ • Notif │ │ • Native UI │ │ • Gestures │ │ │ -│ │ │ • ImagePick │ │ │ │ │ │ │ -│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ - │ - │ HTTPS - ▼ -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ SUPABASE BACKEND │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────┐ ┌──────────────────────┐ ┌────────────────────┐ │ -│ │ PostgreSQL │ │ Storage │ │ Auth │ │ -│ │ │ │ │ │ │ │ -│ │ Tables: │ │ Buckets: │ │ Providers: │ │ -│ │ • image │ │ • uploads │ │ • Kakao │ │ -│ │ • post │ │ • cropped │ │ • Google │ │ -│ │ • item │ │ │ │ • Apple │ │ -│ │ • post_image │ │ │ │ │ │ -│ │ • (future) user │ │ │ │ Sessions: │ │ -│ │ • (future) vote │ │ │ │ • JWT tokens │ │ -│ │ • (future) comment │ │ │ │ • Refresh tokens │ │ -│ │ │ │ │ │ │ │ -│ └──────────────────────┘ └──────────────────────┘ └────────────────────┘ │ -│ │ -│ ┌──────────────────────┐ ┌──────────────────────┐ │ -│ │ Edge Functions │ │ Realtime │ │ -│ │ (Future) │ │ (Future) │ │ -│ │ │ │ │ │ -│ │ • AI Detection │ │ • Live updates │ │ -│ │ • Scraper │ │ • Notifications │ │ -│ │ • Click tracking │ │ │ │ -│ │ │ │ │ │ -│ └──────────────────────┘ └──────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ - │ - │ (Future Integration) - ▼ -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ EXTERNAL SERVICES │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────┐ ┌──────────────────────┐ ┌────────────────────┐ │ -│ │ Vision API │ │ Scraper Engine │ │ Affiliate APIs │ │ -│ │ (TBD) │ │ │ │ │ │ -│ │ │ │ • Product info │ │ • Musinsa │ │ -│ │ • Object detection │ │ • Price extraction │ │ • 29CM │ │ -│ │ • Brand recognition │ │ • Image scraping │ │ • Farfetch │ │ -│ │ • Fashion tagging │ │ │ │ • SSENSE │ │ -│ │ │ │ │ │ │ │ -│ └──────────────────────┘ └──────────────────────┘ └────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 2. Monorepo Structure - -``` -decoded-app/ -├── packages/ -│ ├── web/ # Next.js 웹 앱 -│ │ ├── app/ # App Router pages -│ │ │ ├── page.tsx # Home -│ │ │ ├── layout.tsx # Root layout -│ │ │ ├── images/[id]/ # Image detail -│ │ │ ├── @modal/ # Parallel route (modal) -│ │ │ └── lab/ # Experimental -│ │ │ -│ │ ├── lib/ # App-specific code -│ │ │ ├── components/ # React components -│ │ │ │ ├── detail/ # Detail view components -│ │ │ │ ├── filter/ # Filter components -│ │ │ │ ├── grid/ # Grid components -│ │ │ │ └── ui/ # Base UI components -│ │ │ │ -│ │ │ ├── hooks/ # Custom hooks -│ │ │ ├── stores/ # Zustand stores -│ │ │ ├── supabase/ # Supabase client -│ │ │ └── utils/ # Utilities -│ │ │ -│ │ └── public/ # Static assets -│ │ -│ ├── shared/ # 공유 코드 (web + mobile) -│ │ ├── hooks/ # Shared hooks -│ │ ├── stores/ # Shared stores -│ │ ├── supabase/ # Supabase queries -│ │ │ └── queries/ # Query functions -│ │ ├── types/ # Shared types -│ │ └── data/ # Mock data -│ │ -│ └── mobile/ # Expo 모바일 앱 (초기 구조) -│ ├── app/ # Expo Router -│ └── components/ # Mobile components -│ -├── docs/ # 문서 -│ ├── architecture/ # 아키텍처 문서 -│ ├── database/ # DB 스키마 문서 -│ ├── design-system/ # 디자인 시스템 -│ ├── testing/ # 테스트 문서 -│ ├── performance/ # 성능 가이드 -│ ├── adr/ # 아키텍처 결정 기록 -│ └── ai-playbook/ # AI 도구 가이드 -│ -├── specs/ # 기능 명세 -│ ├── feature-spec/ # 기능별 명세서 -│ └── 001-scroll-animation/ # Feature spec -│ -├── __tests__/ # 테스트 파일 -│ -├── package.json # Yarn workspaces root -├── yarn.lock # Yarn 4 lock file -└── .yarnrc.yml # Yarn 설정 (node-modules linker) -``` - ---- - -## 3. Component Dependency Graph - -![Navigation Flow](../diagrams/navigation-flow.excalidraw.png) - -### 3.1 Home Page Dependencies - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ HOME PAGE DEPENDENCY GRAPH │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ app/page.tsx (SSR) │ -│ │ │ -│ ├──▶ fetchLatestImages() [Server] │ -│ │ │ -│ └──▶ HomeClient.tsx (Client) │ -│ │ │ -│ ├──▶ useInfiniteFilteredImages() │ -│ │ │ │ -│ │ └──▶ fetchUnifiedImages() │ -│ │ │ │ -│ │ └──▶ Supabase │ -│ │ │ -│ ├──▶ Header.tsx │ -│ │ │ │ -│ │ ├──▶ FilterTabs.tsx │ -│ │ │ │ │ -│ │ │ └──▶ filterStore (Zustand) │ -│ │ │ │ -│ │ ├──▶ SearchInput.tsx │ -│ │ │ │ │ -│ │ │ └──▶ searchStore (Zustand) │ -│ │ │ │ -│ │ └──▶ ThemeToggle.tsx │ -│ │ │ │ -│ │ └──▶ next-themes │ -│ │ │ -│ └──▶ ThiingsGrid.tsx │ -│ │ │ -│ ├──▶ CardCell.tsx │ -│ │ │ │ -│ │ └──▶ transitionStore (FLIP) │ -│ │ │ -│ └──▶ useScrollAnimation() │ -│ │ │ -│ └──▶ IntersectionObserver │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 3.2 Detail Page Dependencies - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ DETAIL PAGE DEPENDENCY GRAPH │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Desktop (Modal): │ -│ app/@modal/(.)images/[id]/page.tsx │ -│ │ │ -│ └──▶ ImageDetailModal.tsx │ -│ │ │ -│ ├──▶ transitionStore (FLIP state) │ -│ │ │ -│ └──▶ ImageDetailContent.tsx ◀───┐ │ -│ │ │ -│ Mobile/Direct: │ (공유) │ -│ app/images/[id]/page.tsx │ │ -│ │ │ │ -│ └──▶ ImageDetailPage.tsx ─────────────────┘ │ -│ │ -│ ImageDetailContent.tsx │ -│ │ │ -│ ├──▶ useImageById() │ -│ │ │ │ -│ │ └──▶ fetchImageById() → Supabase │ -│ │ │ -│ ├──▶ HeroSection.tsx │ -│ │ │ │ -│ │ └──▶ GSAP (Ken Burns, Parallax) │ -│ │ │ -│ ├──▶ InteractiveShowcase.tsx │ -│ │ │ │ -│ │ ├──▶ useNormalizedItems() │ -│ │ │ │ -│ │ └──▶ ItemDetailCard.tsx │ -│ │ │ -│ ├──▶ ShopGrid.tsx │ -│ │ │ │ -│ │ └──▶ 수평 캐러셀 │ -│ │ │ -│ └──▶ RelatedImages.tsx │ -│ │ │ -│ └──▶ useRelatedImagesByAccount() │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 4. Module Responsibility Matrix - -| Module | Responsibility | Key Files | Dependencies | -|--------|---------------|-----------|--------------| -| **App Router** | 라우팅, SSR, 레이아웃 | `app/**/*.tsx` | Next.js | -| **Components** | UI 렌더링, 인터랙션 | `lib/components/**` | React, Tailwind | -| **Hooks** | 재사용 로직, 데이터 페칭 | `lib/hooks/**` | React Query | -| **Stores** | 전역 상태 관리 | `lib/stores/**` | Zustand | -| **Queries** | Supabase 데이터 액세스 | `shared/supabase/queries/**` | Supabase | -| **Utils** | 헬퍼 함수 | `lib/utils/**` | - | -| **Types** | 타입 정의 | `shared/types/**` | TypeScript | - ---- - -## 5. Data Flow Architecture - -### 5.1 Read Flow (Query) - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ READ DATA FLOW │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Component │ -│ │ │ -│ │ useInfiniteFilteredImages({ filter, search, limit }) │ -│ ▼ │ -│ React Query │ -│ │ │ -│ │ queryKey: ["images", "infinite", { filter, search, limit }] │ -│ │ │ -│ │ ┌─────────────────────────────────────────────────────────────────┐ │ -│ │ │ Cache Check │ │ -│ │ │ │ │ -│ │ │ staleTime > 0? ──YES──▶ Return cached data │ │ -│ │ │ │ │ │ -│ │ │ NO │ │ -│ │ │ │ │ │ -│ │ │ ▼ │ │ -│ │ │ Background refetch (if not fresh) │ │ -│ │ └─────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ queryFn: fetchUnifiedImages() │ -│ ▼ │ -│ Supabase Client │ -│ │ │ -│ │ SELECT * FROM post_image │ -│ │ JOIN image ON ... │ -│ │ JOIN post ON ... │ -│ │ WHERE account = filter │ -│ │ ORDER BY created_at DESC │ -│ │ LIMIT 50 │ -│ ▼ │ -│ PostgreSQL │ -│ │ │ -│ │ Query execution │ -│ ▼ │ -│ Response │ -│ │ │ -│ │ Transform: normalizeImage() │ -│ ▼ │ -│ Component Update │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 5.2 State Update Flow - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ STATE UPDATE FLOW │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ User Action (Filter Click) │ -│ │ │ -│ ▼ │ -│ FilterTabs.tsx │ -│ │ │ -│ │ onClick={() => setFilter('blackpinkk.style')} │ -│ ▼ │ -│ filterStore (Zustand) │ -│ │ │ -│ │ state.activeFilter = 'blackpinkk.style' │ -│ │ │ -│ │ Subscribers notified │ -│ ▼ │ -│ useInfiniteFilteredImages() │ -│ │ │ -│ │ queryKey changed: ["images", "infinite", { filter: "blackpinkk..." }] │ -│ │ │ -│ │ React Query detects key change │ -│ ▼ │ -│ Automatic Refetch │ -│ │ │ -│ │ fetchUnifiedImages({ filter: 'blackpinkk.style' }) │ -│ ▼ │ -│ ThiingsGrid re-render │ -│ │ │ -│ │ New data displayed │ -│ ▼ │ -│ UI Updated │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 6. Technology Stack Detail - -### 6.1 Frontend Technologies - -| Category | Technology | Version | Purpose | -|----------|------------|---------|---------| -| Framework | Next.js | 16.0.7 | App Router, SSR, API Routes | -| UI Library | React | 18.3.1 | Component rendering | -| Language | TypeScript | 5.9.3 | Type safety | -| Styling | Tailwind CSS | 3.4.18 | Utility-first CSS | -| State (Client) | Zustand | 4.5.7 | Global state | -| State (Server) | React Query | 5.90.11 | Server state, caching | -| Animation | GSAP | 3.13.0 | Complex animations | -| Animation | Motion | 12.23.12 | Declarative animations | -| Scroll | Lenis | 1.3.15 | Smooth scroll | -| Theme | next-themes | 0.4.6 | Dark mode | - -### 6.2 Backend Technologies - -| Category | Technology | Purpose | -|----------|------------|---------| -| Database | Supabase (PostgreSQL) | Primary data store | -| Auth | Supabase Auth | OAuth providers | -| Storage | Supabase Storage | Image uploads | -| Hosting | Vercel (TBD) | Web deployment | - -### 6.3 Development Tools - -| Category | Technology | Version | Purpose | -|----------|------------|---------|---------| -| Package Manager | Yarn | 4.9.2 | Monorepo workspaces | -| Linting | ESLint | 9.39.1 | Code quality | -| Formatting | Prettier | 3.6.2 | Code formatting | -| Testing | Playwright | - | E2E testing | - ---- - -## 7. Security Architecture - -### 7.1 Authentication Flow - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ OAUTH AUTHENTICATION FLOW │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Client Supabase Auth OAuth Provider │ -│ │ │ │ │ -│ │ 1. signInWithOAuth() │ │ │ -│ │ ─────────────────────▶ │ │ │ -│ │ │ │ │ -│ │ 2. Redirect URL │ │ │ -│ │ ◀───────────────────── │ │ │ -│ │ │ │ │ -│ │ 3. Redirect to Provider │ │ -│ │ ───────────────────────────────────────────────────▶│ │ -│ │ │ │ │ -│ │ 4. User authenticates │ │ -│ │ │ │ │ -│ │ 5. Callback with code │ │ -│ │ ◀───────────────────────────────────────────────────│ │ -│ │ │ │ │ -│ │ 6. Exchange code │ │ │ -│ │ ─────────────────────▶ │ ─────────────────────────▶│ │ -│ │ │ │ │ -│ │ │ 7. Tokens │ │ -│ │ │ ◀─────────────────────────│ │ -│ │ │ │ │ -│ │ 8. Session created │ │ │ -│ │ ◀───────────────────── │ │ │ -│ │ │ │ │ -│ │ 9. JWT stored in cookie│ │ │ -│ │ │ │ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 7.2 Security Measures - -| Area | Measure | Implementation | -|------|---------|----------------| -| Authentication | OAuth 2.0 | Supabase Auth (Kakao, Google, Apple) | -| Authorization | RLS | Supabase Row Level Security | -| Data Validation | Server-side | API Route validation | -| XSS Prevention | React | Automatic escaping | -| CSRF | Next.js | SameSite cookies | - ---- - -## Related Documents - -- [data-pipeline.md](./data-pipeline.md) - 데이터 파이프라인 -- [state-management.md](./state-management.md) - 상태 관리 -- [../database/01-schema-usage.md](../database/01-schema-usage.md) - DB 스키마 -- [../specs/feature-spec/README.md](../../specs/feature-spec/README.md) - 기능 명세 +> **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** +> +> 본문: [`Architecture/_README.md`](https://github.com/decodedcorp/decoded-docs/blob/main/Architecture/_README.md) +> +> Obsidian path: `Architecture/_README` +> +> Sync policy: [Guides/sync-policy](https://github.com/decodedcorp/decoded-docs/blob/main/Guides/sync-policy.md) diff --git a/docs/architecture/assets-project.md b/docs/architecture/assets-project.md index 24b93895..5481d5d0 100644 --- a/docs/architecture/assets-project.md +++ b/docs/architecture/assets-project.md @@ -1,163 +1,9 @@ ---- -title: Assets Supabase Project -owner: human -status: approved -updated: 2026-04-25 -tags: [architecture, db, agent] ---- - -# Assets Supabase Project (#333) - -**한 줄 요약**: Pinterest/Instagram 등 외부 플랫폼에서 수집·파싱한 raw_posts 와 파이프라인 중간 상태를 별도 Supabase 프로젝트(`assets`)에 격리. prod 는 admin 이 검증 완료한 데이터만 보유. - -## 왜 분리했나 - -PR #258 의 raw_posts 파이프라인이 prod Supabase 의 `warehouse.raw_post*` 테이블을 직접 사용하면서 두 가지 문제가 누적됐다: - -1. **운영 경계 모호** — 검증된 prod posts 와 unverified raw 데이터가 같은 프로젝트에 섞여 있어 백업/복원, RLS, 권한 분리, 모니터링 모두 한꺼번에 다뤄야 했다. -2. **prod 스키마 오염** — 파이프라인 상태머신, dispatch 로그, parse 결과 등 운영성 메타데이터가 prod 의 schema diff 를 키웠다. - -**원칙**: 두 프로젝트는 cross-ID 참조 없이 완전 독립. prod 는 assets 의 존재를 모르고, assets 도 prod 를 모른다. - -## 구조 - -``` -┌──────────────────────────────────────────────┐ -│ Cloud Supabase: ASSETS (파이프라인 스테이징) │ -│ public.raw_post_sources │ -│ public.raw_posts │ -│ └ status pipeline_status │ -│ └ verified_at / verified_by │ -│ public.pipeline_events │ -│ + RLS: service-role only │ -└──────────────▲──────────────┬─────────────────┘ - │ write │ read - │ │ - ┌───────────┴────┐ ┌───────┴──────────────┐ - │ ai-server │ │ api-server │ ──► R2 (raw bucket, 공유) - │ ARQ pipeline │ │ raw_posts domain │ - │ ↓ │ │ verify endpoint │ - │ status=COMPLETED ──────────► │ - └────────────────┘ └────────┬─────────────┘ - │ on verify ✓ - ▼ INSERT -┌──────────────────────────────────────────────┐ -│ Cloud Supabase: PROD (검증본 — 실서비스) │ -│ public.posts (검증된 raw_post 의 복사본) │ -│ public.users / public.solutions / ... │ -│ public.artists / public.groups / public.brands│ -│ (#335 warehouse → public 이관) │ -└──────────────────────────────────────────────┘ -``` - -## 핵심 설계 결정 - -### 1. 검증(verify) 이 최종 액션 — "승격" 개념 없음 - -admin 이 COMPLETED raw_post 를 검증하면 **한 번의 액션**으로: - -- assets `status = COMPLETED → VERIFIED` (production 환경에서만) -- prod `public.posts` 에 새 row INSERT - -별도의 "promote" 단계나 후처리 없음. `verified_at`/`verified_by` 가 운영적 진실의 기준. - -### 2. 중복 방지는 admin 운영 책임 - -prod `public.posts` 에 **DB UNIQUE 제약을 걸지 않는다**. admin UI 가 같은 raw_post 를 두 번 검증할 가능성을 시각적으로 막고, 실제로 중복이 발생하면 admin 이 수동 정리한다. 이유: - -- 동일한 이미지가 여러 platform/external_id 로 들어올 수 있어 DB 레벨 dedupe 가 false positive 다발 -- VERIFIED 가 cross-project ID(`source_raw_post_id`) 로 prod 에 새는 걸 막아야 함 - -### 3. 5-state 파이프라인 상태머신 - -``` -NOT_STARTED → IN_PROGRESS → COMPLETED ──(admin verify)──► VERIFIED - ↘ ERROR -``` - -| 상태 | 의미 | 누가 전이시키는가 | -|---|---|---| -| `NOT_STARTED` | row 가 막 INSERT 됨 (default) | DB default | -| `IN_PROGRESS` | fetch / parse 진행 중 | ai-server (현재 architecture 에선 거의 사용되지 않음 — 단일 트랜잭션) | -| `COMPLETED` | 자동 처리 완료 (R2 업로드 + 기본 메타) | ai-server `upsert_raw_posts` | -| `VERIFIED` | admin 검증 완료, prod 에 반영됨 | api-server `verify_raw_post` | -| `ERROR` | 어느 단계든 실패 | ai-server `mark_raw_post_error` | - -전환 시 `pipeline_events` 에 감사 row 가 동일 트랜잭션으로 INSERT 된다. - -### 4. APP_ENV 분기로 cloud assets 보호 - -로컬 개발자가 cloud assets(공유) 데이터를 오염시키지 않도록 `APP_ENV=local` 일 때 verify 엔드포인트는 **prod INSERT 만** 수행하고 assets status write 를 스킵한다. production 배포에서만 status=VERIFIED 가 기록된다. - -## verify 시퀀스 - -``` -[Admin] - │ /admin/raw-posts → COMPLETED 탭에서 행 클릭 → "검증" 버튼 - ▼ -[web Next.js] - │ POST /api/admin/raw-posts/items/{id}/verify - ▼ -[api-server proxy] - │ Bearer 위임 - ▼ -[api-server raw_posts::verify_raw_post] - ├─ assets_db.find_by_id(id) ← assets pool - │ status == COMPLETED ? → 아니면 400 - │ - ├─ posts::create_post_from_raw(prod_db, admin_id, raw, dto) ← prod pool - │ INSERT INTO public.posts ... RETURNING * - │ - └─ if APP_ENV == Production: - assets txn: - UPDATE public.raw_posts SET status='VERIFIED' WHERE id=$1 - INSERT INTO public.pipeline_events (raw_post_id, from_status, - to_status, actor) VALUES (...) - commit - (실패 시 prod 는 이미 INSERT 된 상태 — admin 이 시각적 dedupe) -``` - -## 실패 모드 - -| 시나리오 | 영향 | 완화 | -|---|---|---| -| cloud assets 장애 | `/api/v1/raw-posts/*` 503, posts CRUD 정상 | pool 분리. 장애 메시지에서 명시 | -| verify step 2 ✓ + step 3 ✗ | prod 에 새 row, assets 는 여전히 COMPLETED → 다음 클릭에서 중복 INSERT 가능 | admin 시각적 dedupe (설계 결정 #2). loud error log | -| assets URL stale (로컬) | DATABASE_URL fallback + WARN | `APP_ENV=production` 에서는 panic | -| 파이프라인 ERROR 재시도 | `mark_raw_post_error` 가 VERIFIED 는 보존 | `WHERE status <> 'VERIFIED'` 가드 | - -## 마이그레이션 (#335) - -prod 에서 `warehouse` 스키마를 완전 드롭하고 살아남는 엔티티 테이블(artists/groups/brands/group_members/admin_audit_log/instagram_accounts) 을 `public` 스키마로 SET SCHEMA. 자세한 절차는 [`docs/DATABASE-MIGRATIONS.md`](../DATABASE-MIGRATIONS.md) 와 `supabase/migrations/20260425000001_drop_warehouse_and_promote_entities.sql`. - -## 관련 파일 - -- 스키마: `supabase-assets/migrations/20260424120000_initial.sql` (초기) + `20260426130000_drop_r2_columns.sql` (#347 r2_url/r2_key 드롭) -- api-server: `packages/api-server/src/domains/raw_posts/{handlers,service,dto}.rs` -- api-server entity: `packages/api-server/src/entities/{assets_raw_posts,assets_raw_post_sources}.rs` -- api-server config: `packages/api-server/src/config.rs::AppEnv` / `AssetsDatabaseConfig` -- api-server state: `packages/api-server/src/app_state.rs::AppState::assets_db` -- ai-server: `packages/ai-server/src/services/raw_posts/repository.py` -- ai-server pool: `packages/ai-server/src/managers/database/pool.py::DatabaseManager._resolve_dsn` -- web admin: `packages/web/app/admin/raw-posts/page.tsx` -- web hook: `packages/web/lib/api/admin/raw-posts.ts` -- 환경: [`docs/agent/environments.md`](../agent/environments.md) - -## 컬럼 의미 (raw_posts 본체) - -| 컬럼 | 의미 | 케이스별 | -|---|---|---| -| `external_url` | 외부 출처 페이지 URL (Pinterest 핀 페이지 등) | Pinterest/IG: 핀/포스트 URL — 합성: NULL | -| `image_url` | **이미지 위치 URL — 실질적으로 R2 퍼블릭 URL**. ai-server 가 R2 업로드 후 채움 (#347) | 모든 케이스 동일 | -| `caption` | 텍스트 (Pinterest description, IG caption 등) | 합성 케이스: NULL 또는 prompt | -| `author_name` | 저자/소스 명 (Pinterest pinner 등) | — | -| `platform_metadata` | 플랫폼별 자유 메타 (saves, board_id, hashtags 등) | 합성 케이스: 보통 NULL | -| `parse_result` | 비전파싱 결과 (아이템 bbox, 브랜드 후보 등) | 모든 케이스 동일 — Vision 결과 | -| `dispatch_id` | ai-server scheduler 의 1회 dispatch 추적 키 | — | - -> #347 이전: `image_url`(외부 CDN) + `r2_url`(R2 복사본) + `r2_key`(R2 path) 3개 컬럼이었으나 단일화. 외부 CDN URL 은 운영상 거의 미사용이라 드롭, R2 URL 은 `image_url` 한 컬럼으로 통합. - -## 변경 이력 - -- 2026-04-26: r2_url/r2_key 컬럼 드롭, image_url 단일화 (#347) -- 2026-04-25: 초기 작성 — 두 프로젝트 분리, 5-state 상태머신, verify 플로우 (#333) +# Assets Supabase Project + +> **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** +> +> 본문: [`Architecture/assets-project.md`](https://github.com/decodedcorp/decoded-docs/blob/main/Architecture/assets-project.md) +> +> Obsidian path: `Architecture/assets-project` +> +> Sync policy: [Guides/sync-policy](https://github.com/decodedcorp/decoded-docs/blob/main/Guides/sync-policy.md) diff --git a/docs/architecture/data-pipeline.md b/docs/architecture/data-pipeline.md index 5c336210..91cfce2a 100644 --- a/docs/architecture/data-pipeline.md +++ b/docs/architecture/data-pipeline.md @@ -1,523 +1,9 @@ # Data Pipeline -> Version: 1.0.0 -> Last Updated: 2026-01-14 -> Purpose: 데이터 수집, 변환, 캐싱 파이프라인 문서화 - ---- - -## Overview - -이 문서는 Decoded 앱의 데이터 흐름을 설명합니다. 외부 소스에서 데이터 수집, DB 저장, 프론트엔드 표시까지의 전체 파이프라인을 다룹니다. - ---- - -## 1. Data Collection Pipeline - -### 1.1 Ingestion Flow - -![AI Creation Pipeline](../diagrams/ai-creation-pipeline.excalidraw.png) - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ DATA INGESTION PIPELINE │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────┐ │ -│ │ Instagram │ │ -│ │ Posts │ │ -│ └────────┬────────┘ │ -│ │ │ -│ │ Scraper (Backend Service) │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ IMAGE STORAGE │ │ -│ │ │ │ -│ │ S3 / Supabase Storage │ │ -│ │ └── Original images saved │ │ -│ │ └── image_url generated │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ AI DETECTION PIPELINE │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ -│ │ │ Object Detection │ │ Brand Detection │ │ Price Extraction│ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • Fashion items │ │ • Brand names │ │ • Price values │ │ │ -│ │ │ • Bounding boxes│ │ • Logos │ │ • Currency │ │ │ -│ │ │ • Categories │ │ • Confidence │ │ • Links │ │ │ -│ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ -│ │ │ │ │ │ │ -│ │ └────────────────────┼────────────────────┘ │ │ -│ │ │ │ │ -│ │ ▼ │ │ -│ │ Cropped Images Generated │ │ -│ │ └── cropped_image_path │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ SUPABASE DATABASE │ │ -│ │ │ │ -│ │ Tables: │ │ -│ │ • image - 원본 이미지 메타데이터 │ │ -│ │ • post - 소셜 미디어 포스트 정보 │ │ -│ │ • item - 감지된 아이템 정보 │ │ -│ │ • post_image - 포스트-이미지 연결 │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 1.2 Database Schema Relationships - -![Entity Relationship Diagram](../diagrams/entity-relationship.excalidraw.png) - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ ENTITY RELATIONSHIP DIAGRAM │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────┐ ┌──────────────────────┐ │ -│ │ post │ │ image │ │ -│ ├──────────────────────┤ ├──────────────────────┤ │ -│ │ id (PK) │ │ id (PK) │ │ -│ │ account │ ┌───▶│ image_hash │ │ -│ │ article │ │ │ image_url │ │ -│ │ metadata[] │ │ │ with_items │ │ -│ │ ts │ │ │ status │ │ -│ │ created_at │ │ │ created_at │ │ -│ └──────────┬───────────┘ │ └──────────┬───────────┘ │ -│ │ │ │ │ -│ │ 1:N │ │ 1:N │ -│ ▼ │ ▼ │ -│ ┌──────────────────────┐ │ ┌──────────────────────┐ │ -│ │ post_image │────┘ │ item │ │ -│ ├──────────────────────┤ ├──────────────────────┤ │ -│ │ post_id (FK) ────────┼─────────│ id (PK) │ │ -│ │ image_id (FK) ───────┤ │ image_id (FK) ───────┤ │ -│ │ item_locations (JSON)│ │ product_name │ │ -│ │ curated_item_ids │ │ brand │ │ -│ │ created_at │ │ price │ │ -│ └──────────────────────┘ │ center (JSON) │ │ -│ │ bboxes (JSON) │ │ -│ │ citations[] │ │ -│ │ metadata[] │ │ -│ │ status │ │ -│ │ created_at │ │ -│ └──────────────────────┘ │ -│ │ -│ 관계: │ -│ • post 1:N post_image (한 포스트에 여러 이미지) │ -│ • image 1:N post_image (한 이미지가 여러 포스트에) │ -│ • image 1:N item (한 이미지에 여러 아이템) │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 2. Data Transformation Pipeline - -### 2.1 Transformation Functions - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ DATA TRANSFORMATION PIPELINE │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Supabase Query Result │ -│ │ │ -│ │ Raw database rows │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ normalizeImage() │ │ -│ │ File: shared/supabase/queries/images-adapter.ts │ │ -│ │ │ │ -│ │ Input: DbImageRow │ │ -│ │ Output: ImageWithPostId │ │ -│ │ │ │ -│ │ Transformations: │ │ -│ │ • id → id │ │ -│ │ • image_url → imageUrl │ │ -│ │ • with_items → withItems │ │ -│ │ • created_at → createdAt │ │ -│ │ • (join) post.account → postAccount │ │ -│ │ • (join) post_image.created_at → postImageCreatedAt │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ normalizeItem() │ │ -│ │ File: packages/web/lib/hooks/useNormalizedItems.ts │ │ -│ │ │ │ -│ │ Input: DbItemRow + post_image.item_locations │ │ -│ │ Output: UiItem │ │ -│ │ │ │ -│ │ Transformations: │ │ -│ │ • item.center OR item_locations[id] → position │ │ -│ │ • product_name → productName │ │ -│ │ • cropped_image_path → croppedImageUrl │ │ -│ │ • bboxes → boundingBoxes │ │ -│ │ • citations → purchaseUrls │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ React Component │ -│ │ │ -│ │ Normalized data ready for rendering │ -│ ▼ │ -│ UI Display │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 2.2 Type Definitions - -```typescript -// Database Types (from Supabase) -interface DbImageRow { - id: string; - image_hash: string; - image_url: string | null; - with_items: boolean; - status: 'pending' | 'extracted' | 'skipped' | 'extracted_metadata'; - created_at: string; -} - -interface DbItemRow { - id: number; - image_id: string; - product_name: string | null; - brand: string | null; - price: string | null; - center: [number, number] | null; - bboxes: number[][] | null; - citations: string[] | null; - metadata: string[] | null; - status: string | null; - created_at: string; -} - -// Normalized Types (for UI) -interface ImageWithPostId { - id: string; - imageUrl: string; - withItems: boolean; - status: string; - createdAt: string; - postId: string; - postSource: 'post' | 'legacy'; - postAccount: string; - postImageCreatedAt: string; - postCreatedAt: string; -} - -interface UiItem { - id: number; - imageId: string; - productName: string; - brand: string; - price: string; - position: { x: number; y: number }; - boundingBoxes: BoundingBox[]; - purchaseUrls: string[]; - croppedImageUrl: string; -} -``` - ---- - -## 3. Query Functions - -### 3.1 Core Query Functions - -| Function | File | Input | Output | Purpose | -|----------|------|-------|--------|---------| -| `fetchLatestImages` | `images.ts` | limit | ImageRow[] | 최신 이미지 조회 (SSR) | -| `fetchImageById` | `images.ts` | id | ImageDetail | 단일 이미지 + 관계 조회 | -| `fetchUnifiedImages` | `images-adapter.ts` | options | ImagePage | 통합 이미지 조회 (필터/검색) | -| `fetchOrphanImages` | `images-orphan.ts` | options | ImageRow[] | 고아 이미지 조회 | -| `fetchRelatedImagesByAccount` | `images.ts` | account, excludeId | ImageRow[] | 관련 이미지 조회 | - -### 3.2 fetchUnifiedImages 상세 - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ fetchUnifiedImages() FLOW │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Input Parameters: │ -│ { │ -│ filter: 'all' | 'newjeanscloset' | 'blackpinkk.style', │ -│ search: string, │ -│ limit: 50, │ -│ cursor: string | null, │ -│ deduplicateByImageId: boolean │ -│ } │ -│ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ Step 1: Query post_image with filters │ │ -│ │ │ │ -│ │ SELECT pi.*, p.account, p.created_at as post_created_at, │ │ -│ │ i.* FROM post_image pi │ │ -│ │ JOIN image i ON pi.image_id = i.id │ │ -│ │ JOIN post p ON pi.post_id = p.id │ │ -│ │ WHERE p.account = :filter (if not 'all') │ │ -│ │ ORDER BY pi.created_at DESC │ │ -│ │ LIMIT :limit │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ Step 2: Fetch orphan images (if filter is 'all') │ │ -│ │ │ │ -│ │ SELECT * FROM image │ │ -│ │ WHERE id NOT IN (SELECT image_id FROM post_image) │ │ -│ │ ORDER BY created_at DESC │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ Step 3: Merge and deduplicate │ │ -│ │ │ │ -│ │ • Combine post_image results with orphan images │ │ -│ │ • If deduplicateByImageId: remove duplicate image_ids │ │ -│ │ • Sort by created_at │ │ -│ │ │ │ -│ └─────────────────────────────────┬───────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ Output: │ -│ { │ -│ items: ImageWithPostId[], │ -│ nextCursor: string | null, │ -│ hasMore: boolean, │ -│ stats: { fromPostImage: number, fromOrphans: number } │ -│ } │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 4. React Query Caching Strategy - -![Filter Data Flow](../diagrams/filter-data-flow.excalidraw.png) - -### 4.1 Cache Configuration - -```typescript -// lib/react-query/client.ts - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 60 * 1000, // 1분 동안 fresh - gcTime: 5 * 60 * 1000, // 5분 후 garbage collection - retry: 1, // 1회 재시도 - refetchOnWindowFocus: false, // 포커스 시 refetch 안함 - }, - }, -}); -``` - -### 4.2 Query Key Structure - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ QUERY KEY HIERARCHY │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ["images"] │ -│ │ │ -│ ├── ["images", "infinite", { filter, search, limit }] │ -│ │ │ │ -│ │ └── 무한 스크롤 이미지 목록 │ -│ │ • useInfiniteFilteredImages() │ -│ │ • fetchUnifiedImages() │ -│ │ │ -│ ├── ["images", "latest", { limit }] │ -│ │ │ │ -│ │ └── 최신 이미지 (SSR 초기 데이터) │ -│ │ • fetchLatestImages() │ -│ │ │ -│ └── ["image", id] │ -│ │ │ -│ └── 단일 이미지 상세 │ -│ • useImageById() │ -│ • fetchImageById() │ -│ │ -│ ["related", account, excludeId] │ -│ │ │ -│ └── 관련 이미지 │ -│ • useRelatedImagesByAccount() │ -│ • fetchRelatedImagesByAccount() │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 4.3 Cache Invalidation Patterns - -| Trigger | Invalidated Keys | Action | -|---------|------------------|--------| -| Filter 변경 | `["images", "infinite", *]` | 자동 refetch | -| Search 입력 | `["images", "infinite", *]` | debounced refetch | -| 새 이미지 업로드 | `["images"]` | 전체 invalidate | -| 이미지 수정 | `["image", id]` | 해당 키만 invalidate | - ---- - -## 5. Data Flow Hooks - -### 5.1 useInfiniteFilteredImages - -```typescript -// lib/hooks/useImages.ts - -export function useInfiniteFilteredImages(options: { - filter: FilterType; - search: string; - limit?: number; - initialData?: ImagePage; -}) { - const { filter, search, limit = 50, initialData } = options; - - return useInfiniteQuery({ - queryKey: ['images', 'infinite', { filter, search, limit }], - queryFn: ({ pageParam }) => - fetchUnifiedImages({ - filter, - search, - limit, - cursor: pageParam, - deduplicateByImageId: true, - }), - initialPageParam: null, - getNextPageParam: (lastPage) => - lastPage.hasMore ? lastPage.nextCursor : undefined, - initialData: initialData - ? { pages: [initialData], pageParams: [null] } - : undefined, - staleTime: 60 * 1000, - }); -} -``` - -### 5.2 Hook → Store → Query 연결 - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ HOOK-STORE-QUERY CONNECTION │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Component │ -│ │ │ -│ │ const filter = useFilterStore(state => state.activeFilter) │ -│ │ const search = useSearchStore(state => state.debouncedQuery) │ -│ │ │ -│ │ const { data, fetchNextPage } = useInfiniteFilteredImages({ │ -│ │ filter, │ -│ │ search, │ -│ │ }); │ -│ │ │ -│ │ // Store 변경 시 자동으로 queryKey 변경 → refetch │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ filterStore.setFilter('blackpinkk.style') │ │ -│ │ │ │ │ -│ │ │ Zustand state update │ │ -│ │ ▼ │ │ -│ │ Component re-render (filter value changed) │ │ -│ │ │ │ │ -│ │ │ useInfiniteFilteredImages called with new filter │ │ -│ │ ▼ │ │ -│ │ React Query detects queryKey change │ │ -│ │ │ │ │ -│ │ │ ["images", "infinite", { filter: "blackpinkk.style", ... }] │ │ -│ │ ▼ │ │ -│ │ Cache miss → fetchUnifiedImages() │ │ -│ │ │ │ │ -│ │ │ New data fetched │ │ -│ │ ▼ │ │ -│ │ Component re-render with new data │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 6. Error Handling - -### 6.1 Error Types - -| Error Type | HTTP Code | Handling | UI Response | -|------------|-----------|----------|-------------| -| Network Error | - | Retry 1회 | "연결 확인" + 재시도 버튼 | -| Not Found | 404 | No retry | EmptyState 표시 | -| Server Error | 500 | Retry 1회 | "잠시 후 다시" 메시지 | -| Auth Error | 401 | No retry | 로그인 리다이렉트 | - -### 6.2 Error Boundary - -```typescript -// Error handling in useInfiniteFilteredImages -const { data, error, isError } = useInfiniteFilteredImages({...}); - -if (isError) { - // ErrorState component 표시 - return ; -} -``` - ---- - -## 7. Performance Optimizations - -### 7.1 Implemented Optimizations - -| Optimization | Description | File | -|--------------|-------------|------| -| Cursor Pagination | Offset 대신 cursor 사용 | `images-adapter.ts` | -| Deduplication | 중복 이미지 제거 옵션 | `images-adapter.ts` | -| SSR Initial Data | 서버에서 초기 데이터 로드 | `app/page.tsx` | -| Stale Time | 1분간 캐시 유지 | `react-query/client.ts` | -| Keep Previous | 필터 변경 시 이전 데이터 유지 | `useImages.ts` | - -### 7.2 Query Optimization Tips - -```typescript -// 불필요한 refetch 방지 -staleTime: 60 * 1000, -refetchOnWindowFocus: false, - -// 이전 데이터 유지 (깜빡임 방지) -placeholderData: keepPreviousData, - -// 선택적 필드 로드 -select: (data) => data.pages.flatMap(page => page.items), -``` - ---- - -## Related Documents - -- [README.md](./README.md) - 시스템 아키텍처 -- [state-management.md](./state-management.md) - 상태 관리 -- [../database/01-schema-usage.md](../database/01-schema-usage.md) - DB 스키마 -- [../database/03-data-flow.md](../database/03-data-flow.md) - DB 데이터 흐름 +> **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** +> +> 본문: [`Architecture/data-pipeline.md`](https://github.com/decodedcorp/decoded-docs/blob/main/Architecture/data-pipeline.md) +> +> Obsidian path: `Architecture/data-pipeline` +> +> Sync policy: [Guides/sync-policy](https://github.com/decodedcorp/decoded-docs/blob/main/Guides/sync-policy.md) diff --git a/docs/architecture/state-management.md b/docs/architecture/state-management.md index f8d06ada..84232fb0 100644 --- a/docs/architecture/state-management.md +++ b/docs/architecture/state-management.md @@ -1,597 +1,9 @@ # State Management -> Version: 1.0.0 -> Last Updated: 2026-01-14 -> Purpose: Zustand + React Query 상태 관리 패턴 문서화 - ---- - -## Overview - -Decoded 앱은 두 가지 상태 관리 도구를 사용합니다: -- **Zustand**: 클라이언트 전역 상태 (필터, 검색, UI 상태) -- **React Query**: 서버 상태 (데이터 페칭, 캐싱, 동기화) - ---- - -## 1. State Architecture - -### 1.1 State Layer Diagram - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ STATE ARCHITECTURE │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ COMPONENT LAYER │ │ -│ │ │ │ -│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ -│ │ │ Local │ │ Local │ │ Local │ │ │ -│ │ │ State │ │ State │ │ State │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • useState │ │ • useRef │ │ • useReducer│ │ │ -│ │ │ • UI toggle │ │ • DOM refs │ │ • Complex │ │ │ -│ │ │ • Form data │ │ • Timers │ │ local │ │ │ -│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ Props / Context │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ GLOBAL CLIENT STATE │ │ -│ │ (Zustand Stores) │ │ -│ │ │ │ -│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ -│ │ │ filterStore │ │ searchStore │ │transitionStore │ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ • activeFilter │ │ • query │ │ • selectedId │ │ │ -│ │ │ • category │ │ • debouncedQ │ │ • originState │ │ │ -│ │ │ • mediaId │ │ │ │ • rect │ │ │ -│ │ │ • castId │ │ │ │ │ │ │ -│ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ Query Keys │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ SERVER STATE │ │ -│ │ (React Query) │ │ -│ │ │ │ -│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ -│ │ │ Query Cache │ │ │ -│ │ │ │ │ │ -│ │ │ ["images", "infinite", {...}] → ImagePage[] │ │ │ -│ │ │ ["image", id] → ImageDetail │ │ │ -│ │ │ ["related", account, id] → ImageRow[] │ │ │ -│ │ │ │ │ │ -│ │ └─────────────────────────────────────────────────────────────────┘ │ │ -│ │ │ │ -│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ -│ │ │ Mutation Cache │ │ │ -│ │ │ │ │ │ -│ │ │ (Future) vote, comment, upload mutations │ │ │ -│ │ │ │ │ │ -│ │ └─────────────────────────────────────────────────────────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ Supabase Client │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ REMOTE DATA SOURCE │ │ -│ │ (Supabase) │ │ -│ │ │ │ -│ │ PostgreSQL Database │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 2. Zustand Stores - -### 2.1 filterStore - -**File**: `lib/stores/filterStore.ts` / `shared/stores/filterStore.ts` - -```typescript -// 현재 구현 -interface FilterState { - activeFilter: 'all' | 'newjeanscloset' | 'blackpinkk.style'; - setFilter: (filter: FilterType) => void; -} - -const useFilterStore = create((set) => ({ - activeFilter: 'all', - setFilter: (filter) => set({ activeFilter: filter }), -})); - -// 사용 예시 -function FilterTabs() { - const { activeFilter, setFilter } = useFilterStore(); - - return ( -
- - {/* ... */} -
- ); -} -``` - -**Future 확장 (D-02 계층형 필터)**: -```typescript -interface HierarchicalFilterState { - // 현재 선택값 - category: CategoryType | null; - mediaId: string | null; - castId: string | null; - contextType: ContextType | null; - - // Breadcrumb - breadcrumb: FilterBreadcrumb[]; - - // Actions - setCategory: (cat: CategoryType | null) => void; - setMedia: (id: string | null) => void; - setCast: (id: string | null) => void; - setContext: (ctx: ContextType | null) => void; - clearAll: () => void; - navigateToBreadcrumb: (level: number) => void; -} -``` - -### 2.2 searchStore - -**File**: `lib/stores/searchStore.ts` / `shared/stores/searchStore.ts` - -```typescript -interface SearchState { - query: string; - debouncedQuery: string; - setQuery: (query: string) => void; - setDebouncedQuery: (query: string) => void; -} - -const useSearchStore = create((set) => ({ - query: '', - debouncedQuery: '', - setQuery: (query) => set({ query }), - setDebouncedQuery: (debouncedQuery) => set({ debouncedQuery }), -})); - -// SearchInput 컴포넌트에서 사용 -function SearchInput() { - const { query, setQuery, setDebouncedQuery } = useSearchStore(); - - // 250ms debounce - useEffect(() => { - const timer = setTimeout(() => { - setDebouncedQuery(query); - }, 250); - return () => clearTimeout(timer); - }, [query]); - - return ( - setQuery(e.target.value)} - /> - ); -} -``` - -### 2.3 transitionStore - -**File**: `lib/stores/transitionStore.ts` - -```typescript -interface TransitionState { - selectedId: string | null; - originState: Flip.FlipState | null; - rect: DOMRect | null; - - setSelectedId: (id: string | null) => void; - setOriginState: (state: Flip.FlipState | null) => void; - setRect: (rect: DOMRect | null) => void; - clear: () => void; -} - -const useTransitionStore = create((set) => ({ - selectedId: null, - originState: null, - rect: null, - - setSelectedId: (id) => set({ selectedId: id }), - setOriginState: (state) => set({ originState: state }), - setRect: (rect) => set({ rect }), - clear: () => set({ selectedId: null, originState: null, rect: null }), -})); - -// CardCell에서 클릭 시 상태 저장 -function CardCell({ image }) { - const { setSelectedId, setOriginState } = useTransitionStore(); - - const handleClick = (e) => { - const element = e.currentTarget; - const state = Flip.getState(element); - setSelectedId(image.id); - setOriginState(state); - }; - - return
...
; -} - -// Detail Modal에서 애니메이션 실행 -function ImageDetailModal() { - const { originState, clear } = useTransitionStore(); - - useEffect(() => { - if (originState) { - Flip.from(originState, { - duration: 0.5, - ease: 'power2.inOut', - }); - } - return () => clear(); - }, []); -} -``` - ---- - -## 3. React Query Patterns - -### 3.1 Query Client Configuration - -**File**: `lib/react-query/client.ts` - -```typescript -import { QueryClient } from '@tanstack/react-query'; - -export const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 60 * 1000, // 1분 - gcTime: 5 * 60 * 1000, // 5분 - retry: 1, - refetchOnWindowFocus: false, - refetchOnReconnect: true, - }, - mutations: { - retry: 1, - }, - }, -}); -``` - -### 3.2 Query Hooks - -**File**: `lib/hooks/useImages.ts` - -```typescript -// 무한 스크롤 이미지 조회 -export function useInfiniteFilteredImages(options: { - filter: FilterType; - search: string; - limit?: number; - initialData?: ImagePage; -}) { - return useInfiniteQuery({ - queryKey: ['images', 'infinite', { - filter: options.filter, - search: options.search, - limit: options.limit - }], - queryFn: ({ pageParam }) => fetchUnifiedImages({ - filter: options.filter, - search: options.search, - limit: options.limit ?? 50, - cursor: pageParam, - }), - initialPageParam: null, - getNextPageParam: (lastPage) => - lastPage.hasMore ? lastPage.nextCursor : undefined, - initialData: options.initialData - ? { pages: [options.initialData], pageParams: [null] } - : undefined, - staleTime: 60 * 1000, - placeholderData: keepPreviousData, // 필터 변경 시 이전 데이터 유지 - }); -} - -// 단일 이미지 조회 -export function useImageById(id: string) { - return useQuery({ - queryKey: ['image', id], - queryFn: () => fetchImageById(id), - enabled: !!id, - staleTime: 60 * 1000, - }); -} - -// 관련 이미지 조회 -export function useRelatedImagesByAccount( - account: string, - excludeId: string -) { - return useQuery({ - queryKey: ['related', account, excludeId], - queryFn: () => fetchRelatedImagesByAccount(account, excludeId), - enabled: !!account && !!excludeId, - staleTime: 60 * 1000, - }); -} -``` - -### 3.3 Query Key Factory Pattern - -```typescript -// lib/react-query/keys.ts - -export const imageKeys = { - all: ['images'] as const, - lists: () => [...imageKeys.all, 'list'] as const, - list: (filters: ImageFilters) => [...imageKeys.lists(), filters] as const, - infinite: (filters: ImageFilters) => [...imageKeys.all, 'infinite', filters] as const, - details: () => [...imageKeys.all, 'detail'] as const, - detail: (id: string) => [...imageKeys.details(), id] as const, -}; - -export const relatedKeys = { - all: ['related'] as const, - byAccount: (account: string, excludeId: string) => - [...relatedKeys.all, account, excludeId] as const, -}; - -// 사용 -useInfiniteQuery({ - queryKey: imageKeys.infinite({ filter, search, limit }), - // ... -}); -``` - ---- - -## 4. Store ↔ Query Integration - -### 4.1 State Sync Flow - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ STORE ↔ QUERY SYNC FLOW │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ User Action │ -│ (Filter Click) │ -│ │ │ -│ ▼ │ -│ ┌────────────────────────────────────────────────────────────────────────┐ │ -│ │ FilterTabs.tsx │ │ -│ │ │ │ -│ │ onClick={() => setFilter('blackpinkk.style')} │ │ -│ └────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌────────────────────────────────────────────────────────────────────────┐ │ -│ │ filterStore (Zustand) │ │ -│ │ │ │ -│ │ state.activeFilter = 'blackpinkk.style' │ │ -│ │ │ │ -│ │ → All subscribers notified │ │ -│ └────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌────────────────────────────────────────────────────────────────────────┐ │ -│ │ HomeClient.tsx (subscriber) │ │ -│ │ │ │ -│ │ const filter = useFilterStore(s => s.activeFilter); │ │ -│ │ // filter 값이 변경됨 → 컴포넌트 re-render │ │ -│ │ │ │ -│ │ const { data } = useInfiniteFilteredImages({ filter, search }); │ │ -│ │ // queryKey가 변경됨 │ │ -│ └────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌────────────────────────────────────────────────────────────────────────┐ │ -│ │ React Query │ │ -│ │ │ │ -│ │ queryKey: ["images", "infinite", { filter: "blackpinkk.style", ... }] │ │ -│ │ │ │ -│ │ Cache lookup: │ │ -│ │ • Cache miss → fetchUnifiedImages() 호출 │ │ -│ │ • Cache hit + fresh → 캐시 데이터 반환 │ │ -│ │ • Cache hit + stale → 캐시 반환 + background refetch │ │ -│ └────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌────────────────────────────────────────────────────────────────────────┐ │ -│ │ ThiingsGrid re-render │ │ -│ │ │ │ -│ │ 새로운 데이터로 그리드 업데이트 │ │ -│ └────────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### 4.2 Component Usage Pattern - -```typescript -// HomeClient.tsx - -function HomeClient({ initialData }: { initialData: ImagePage }) { - // 1. Zustand stores에서 필터/검색 상태 구독 - const filter = useFilterStore((state) => state.activeFilter); - const search = useSearchStore((state) => state.debouncedQuery); - - // 2. React Query로 데이터 페칭 (store 값이 queryKey에 포함) - const { - data, - fetchNextPage, - hasNextPage, - isFetchingNextPage - } = useInfiniteFilteredImages({ - filter, - search, - limit: 50, - initialData, - }); - - // 3. 데이터 가공 - const images = useMemo(() => - data?.pages.flatMap(page => page.items) ?? [], - [data] - ); - - // 4. 렌더링 - return ( - - ); -} -``` - ---- - -## 5. State Persistence - -### 5.1 Current Persistence - -| Store | Persistence | Location | -|-------|------------|----------| -| filterStore | URL params | `?filter=xxx` | -| searchStore | URL params | `?q=xxx` | -| transitionStore | Memory only | - | -| React Query | Memory (gcTime) | 5분 캐시 | - -### 5.2 Future Persistence (사용자 설정) - -```typescript -// 로그인 사용자 설정 저장 예시 -const useUserPreferences = create( - persist( - (set) => ({ - theme: 'system', - language: 'ko', - setTheme: (theme) => set({ theme }), - setLanguage: (language) => set({ language }), - }), - { - name: 'user-preferences', - storage: createJSONStorage(() => localStorage), - } - ) -); -``` - ---- - -## 6. DevTools - -### 6.1 React Query DevTools - -**File**: `app/providers.tsx` - -```typescript -import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; - -export function AppProviders({ children }: { children: React.ReactNode }) { - return ( - - {children} - - - ); -} -``` - -### 6.2 Zustand DevTools - -```typescript -import { devtools } from 'zustand/middleware'; - -const useFilterStore = create( - devtools( - (set) => ({ - activeFilter: 'all', - setFilter: (filter) => set({ activeFilter: filter }), - }), - { name: 'filterStore' } - ) -); -``` - ---- - -## 7. Best Practices - -### 7.1 When to Use What - -| Use Case | Solution | Example | -|----------|----------|---------| -| UI 상태 (토글, 열림/닫힘) | Local useState | Modal open state | -| 전역 클라이언트 상태 | Zustand | Filter, Search | -| 서버 데이터 | React Query | Images, Items | -| 애니메이션 상태 | Zustand | FLIP transition state | -| 폼 상태 | Local useState + Zustand | Create post form | - -### 7.2 Performance Tips - -```typescript -// 1. Zustand selector로 필요한 값만 구독 -// BAD: 전체 store 구독 -const store = useFilterStore(); - -// GOOD: 필요한 값만 구독 -const filter = useFilterStore((s) => s.activeFilter); - -// 2. React Query select로 데이터 변환 -const { data } = useInfiniteFilteredImages({...}, { - select: (data) => data.pages.flatMap(p => p.items), -}); - -// 3. 불필요한 refetch 방지 -{ - staleTime: 60 * 1000, - refetchOnWindowFocus: false, -} - -// 4. 낙관적 업데이트 (mutations) -useMutation({ - mutationFn: voteItem, - onMutate: async (newVote) => { - await queryClient.cancelQueries(['item', newVote.itemId]); - const previous = queryClient.getQueryData(['item', newVote.itemId]); - queryClient.setQueryData(['item', newVote.itemId], (old) => ({ - ...old, - votes: old.votes + 1, - })); - return { previous }; - }, - onError: (err, newVote, context) => { - queryClient.setQueryData(['item', newVote.itemId], context.previous); - }, -}); -``` - ---- - -## Related Documents - -- [README.md](./README.md) - 시스템 아키텍처 -- [data-pipeline.md](./data-pipeline.md) - 데이터 파이프라인 -- [../../specs/feature-spec/workflows.md](../../specs/feature-spec/workflows.md) - 워크플로우 +> **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** +> +> 본문: [`Architecture/state-management.md`](https://github.com/decodedcorp/decoded-docs/blob/main/Architecture/state-management.md) +> +> Obsidian path: `Architecture/state-management` +> +> Sync policy: [Guides/sync-policy](https://github.com/decodedcorp/decoded-docs/blob/main/Guides/sync-policy.md) From a11aca62146096a1771a61cbb86aefeabada86c1 Mon Sep 17 00:00:00 2001 From: thxforall <113906780+thxforall@users.noreply.github.com> Date: Tue, 19 May 2026 11:52:29 +0900 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20Step=202=20=E2=80=94=20=EB=AF=B8?= =?UTF-8?q?=EB=B6=84=EB=A5=98=20=EB=AC=B8=EC=84=9C=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?(refs=20#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit decoded-docs vault 마이그레이션 Step 2 산출물. docs/ + .planning/codebase/ 전체 파일을 transition-design.md §분류 원칙(5가지 판단 기준)에 따라 🟢 MONOREPO / 🔵 VAULT / ⚪ STUB / 🟡 ARCHIVE 로 분류. 핵심 결정: - wiki/ 전체 MONOREPO 유지 — tools/wiki/lib/config.ts 의 INDEX_PATH + INGEST_TOPICS 하드코딩, CLAUDE.md SSOT import 의존성 (spec과 충돌, Step 5 PR에서 spec 정정 예정) - .planning/codebase/ STUB 패턴 — CLAUDE.md L19/L25/L87-93 8건 직접 링크. Step 1 (ADR/architecture) 와 동일 패턴 적용 - qa-screenshots/ MONOREPO — visual-qa.spec.ts:41 테스트 출력 경로 집계: 🟢 65% / 🔵 28% / ⚪ 11% / 🟡 2%, 🔴 REVIEW 0건 (전부 확정). 다음: 팀 리뷰 → Step 3 (vault 복사 + stub 생성) --- ...2026-05-15-docs-migration-step2-mapping.md | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md diff --git a/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md b/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md new file mode 100644 index 00000000..f1975db8 --- /dev/null +++ b/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md @@ -0,0 +1,305 @@ +# Docs 마이그레이션 Step 2 — 미분류 문서 매핑 + +> **Status:** 팀 리뷰 대기 (Step 2 산출물) +> **Refs:** #518 · [`2026-05-14-obsidian-vault-transition-design.md`](../specs/2026-05-14-obsidian-vault-transition-design.md) · Step 1 PR #542 +> **For agentic workers:** 이 문서는 분류 합의용 작업물. 본 매핑이 확정되면 Step 3(파일 복사+frontmatter), Step 4(병행 운영 1개월), Step 5(monorepo 원본 삭제 PR)로 진행한다. + +## 목적 + +[transition-design.md §분류 원칙](../specs/2026-05-14-obsidian-vault-transition-design.md#분류-원칙)의 5가지 판단 기준을 `docs/` 및 `.planning/codebase/` 전체에 적용해 각 파일의 행선지를 확정한다. Step 1 (ADR + architecture) 은 이미 vault stub으로 전환됨. + +## 분류 기준 (재인용) + +| 판단 기준 | 모노레포 유지 | Vault 이동 | +| --------------------------------- | ------------- | ---------- | +| 특정 코드 파일/API를 참조 | O | X | +| 코드 PR이 문서를 무효화할 수 있음 | O | X | +| 이 리포 개발자만 읽음 | O | X | +| 릴리스와 함께 버전닝 | O | X | +| 여러 프로젝트에서 참조 | X | O | + +## 행선지 카테고리 + +- **🟢 MONOREPO** — 코드 밀착, 이 리포에만 의미 있음 +- **🔵 VAULT** — 운영/팀/크로스프로젝트 지식 (decoded-docs로 이동) +- **⚪ STUB** — 본문은 vault, monorepo는 redirect stub만 유지 (Step 1 패턴, ADR/architecture) +- **🟡 ARCHIVE** — 시점 한정 스냅샷, vault `Logs/historical/` 또는 git history만 남기고 삭제 + +--- + +## Section A — `docs/` 루트 파일 + +| 파일 | 행선지 | 사유 | +| ------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------ | +| `docs/README.md` | 🟢 MONOREPO | 모노레포 문서 인덱스. Step 5에서 vault 링크로 일부 항목 업데이트 | +| `docs/GIT-WORKFLOW.md` | 🟢 MONOREPO | spec 명시 — 이 리포 전용 브랜치 정책 | +| `docs/DATABASE-MIGRATIONS.md` | 🟢 MONOREPO | spec 명시 — 마이그레이션 SOT | +| `docs/LOCAL-DEV.md` | 🟢 MONOREPO | 이 리포 dev 환경 셋업 | +| `docs/BACKEND-ONBOARDING.md` | 🟢 MONOREPO | 이 리포 백엔드 온보딩 | +| `docs/server-setup.md` | 🟢 MONOREPO | 이 리포 인프라 셋업 (Cloudflare/홈서버) | +| `docs/nginx-cloudflare-setup.md` | 🟢 MONOREPO | 이 리포 nginx 설정 | +| `docs/gstack-guide.md` | 🔵 VAULT `Guides/gstack-guide.md` | gstack 사용법은 크로스프로젝트 하네스 지식 | +| `docs/backend-frontend-status.md` | 🟡 ARCHIVE | 시점 한정 상태 스냅샷. 최신 정보는 `.planning/codebase/`와 `docs/agent/` | +| `docs/post-centric-refactoring-summary.md` | 🔵 VAULT `Project/refactoring/post-centric.md` | 완료된 리팩토링 회고 | +| `docs/home-sections-backend-feasibility.md` | 🔵 VAULT `Project/feasibility/home-sections-backend.md` | 완료된 feasibility 분석 | + +## Section B — `docs/adr/` · `docs/architecture/` (Step 1 완료) + +| 파일 | 행선지 | 비고 | +| ------------------------------------------ | ------- | --------------------- | +| `docs/adr/ADR-0001-ai-dev-boilerplate.md` | ⚪ STUB | Step 1 완료 (PR #542) | +| `docs/adr/ADR-0002-llm-wiki-foundation.md` | ⚪ STUB | Step 1 완료 | +| `docs/architecture/README.md` | ⚪ STUB | Step 1 완료 | +| `docs/architecture/assets-project.md` | ⚪ STUB | Step 1 완료 | +| `docs/architecture/data-pipeline.md` | ⚪ STUB | Step 1 완료 | +| `docs/architecture/state-management.md` | ⚪ STUB | Step 1 완료 | + +> ⚠️ spec(§모노레포 유지)은 adr/를 monorepo 유지로 표기했으나 Step 1 합의로 vault stub 전환. transition-design.md 본문은 Step 5 라우팅 PR 시 함께 정정. + +## Section C — `docs/agent/` (코드 인접 인벤토리) + +spec 명시: **전부 🟢 MONOREPO 유지.** (코드 변경 시 함께 갱신되는 라우팅/인벤토리) + +| 파일 | 행선지 | +| ------------------------------------------------------------------- | ------------------------------- | +| `docs/agent/README.md` | 🟢 MONOREPO | +| `docs/agent/architecture-summary.md` | 🟢 MONOREPO | +| `docs/agent/api-summary.md` · `api-v1-routes.md` | 🟢 MONOREPO | +| `docs/agent/database-summary.md` | 🟢 MONOREPO | +| `docs/agent/design-system-summary.md` · `design-system-llm.md` | 🟢 MONOREPO | +| `docs/agent/ai-playbook-summary.md` | 🟢 MONOREPO | +| `docs/agent/monorepo.md` · `environments.md` · `staging.md` | 🟢 MONOREPO | +| `docs/agent/web-routes-and-features.md` · `web-hooks-and-stores.md` | 🟢 MONOREPO | +| `docs/agent/e2e-testing.md` · `verify-flow-qa.md` | 🟢 MONOREPO | +| `docs/agent/version-harness.md` | 🟢 MONOREPO | +| `docs/agent/warehouse-schema.md` | 🟢 MONOREPO (DEPRECATED 표기됨) | +| `docs/agent/wiki-entry.md` | 🟢 MONOREPO | + +## Section D — `docs/api/` (API 스펙) + +spec 미명시. 분류 적용: 코드 PR이 무효화할 수 있고 릴리스 버전닝 → 🟢 **MONOREPO 유지.** + +| 파일 | 행선지 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `docs/api/README.md` · `SPEC-CHANGE-PROCESS.md` · `schemas.md` | 🟢 MONOREPO | +| `docs/api/admin.md` · `badges.md` · `categories.md` · `comments.md` · `earnings.md` · `posts.md` · `rankings.md` · `search.md` · `solutions.md` · `spots.md` · `users.md` · `votes.md` | 🟢 MONOREPO | + +## Section E — `docs/database/` + +spec 명시: 🟢 **MONOREPO 유지.** + +| 파일 | 행선지 | +| --------------------------------------------------------------- | -------------------- | +| `docs/database/01-schema-usage.md` ~ `04-supabase-cli-setup.md` | 🟢 MONOREPO | +| `docs/database/operating-model.md` · `drift-check.md` | 🟢 MONOREPO | +| `docs/database/open_api.json` | 🟢 MONOREPO (생성물) | + +## Section F — `docs/design-system/` + +분류 적용: 디자인 토큰/컴포넌트는 `packages/web/lib/design-system/` 코드와 1:1 → 🟢 **MONOREPO 유지.** + +| 파일 | 행선지 | +| ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- | +| `docs/design-system/README.md` · `tokens.md` · `colors.md` · `typography.md` · `spacing.md` · `icons.md` · `patterns.md` | 🟢 MONOREPO | +| `docs/design-system/components/README.md` · `cards.md` · `headers.md` · `inputs.md` · `typography.md` | 🟢 MONOREPO | +| `docs/design-system/decoded-layout-specs.md` | 🟢 MONOREPO | +| `docs/design-system/decoded.pen` · `untitled.pen` | 🟢 MONOREPO (Pencil 바이너리, 코드 인접) | + +## Section G — `docs/ai-playbook/` + +분류 적용: AI 도구 사용법은 크로스프로젝트 지식 → 🔵 **VAULT 이동.** + +| 파일 | 행선지 | +| ------------------------------------------ | ------------------------------------------------------------------- | +| `docs/ai-playbook/01-principles.md` | 🔵 VAULT `Architecture/ai-playbook/01-principles.md` | +| `docs/ai-playbook/02-workflow-overview.md` | 🔵 VAULT `Architecture/ai-playbook/02-workflow-overview.md` | +| `docs/ai-playbook/claude-profile.md` | 🔵 VAULT `Architecture/ai-playbook/claude-profile.md` | +| `docs/ai-playbook/codex-profile.md` | 🔵 VAULT `Architecture/ai-playbook/codex-profile.md` | +| `docs/ai-playbook/cursor-profile.md` | 🔵 VAULT `Architecture/ai-playbook/cursor-profile.md` | +| `docs/ai-playbook/gemini-profile.md` | 🔵 VAULT `Architecture/ai-playbook/gemini-profile.md` | +| `docs/ai-playbook/usage-log.md` | 🟡 ARCHIVE — 시점 한정 로그. vault `Logs/ai-usage/`로 옮기거나 폐기 | + +> 🟢 monorepo의 `docs/agent/ai-playbook-summary.md`는 라우팅 stub으로 유지. + +## Section H — `docs/wiki/` (확정: spec과 충돌 → 전부 MONOREPO) + +**조사 결과 (2026-05-19):** + +- `tools/wiki/lib/config.ts:10` — `INDEX_PATH = "docs/wiki/wiki/INDEX.md"` **하드코딩** +- `tools/wiki/lib/config.ts:12` — `INGEST_TOPICS = ["harness", "ops", "tasks", "incidents"]` — wiki CLI가 `docs/wiki/wiki/${topic}/`에 새 노트 생성 +- `tools/wiki/ingest.ts:41` — `docs/wiki/wiki/${topicKey}/${slug}.md` 생성 경로 +- `CLAUDE.md:63` — `docs/wiki/schema/conventions.md` SSOT import +- `docs/agent/wiki-entry.md` — wiki/ 진입 가이드, monorepo agent routing의 일부 + +→ wiki/ 전체는 **wiki CLI + CLAUDE.md 코드 의존성**이 명확. spec의 "wiki/harness vault 이동"은 의존성 검토 누락. **전부 🟢 MONOREPO 유지.** + +| 파일 | 행선지 | 사유 | +| --------------------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------- | +| `docs/wiki/wiki/INDEX.md` | 🟢 MONOREPO | `tools/wiki/lib/config.ts:10` 하드코딩 | +| `docs/wiki/wiki/harness/claude-code.md` | 🟢 MONOREPO | wiki CLI `INGEST_TOPICS=["harness",...]` 대상 | +| `docs/wiki/wiki/harness/commit-protocol.md` | 🟢 MONOREPO | 동상 | +| `docs/wiki/wiki/harness/review-flow.md` | 🟢 MONOREPO | 동상 | +| `docs/wiki/wiki/harness/session-discipline.md` | 🟢 MONOREPO | 동상 | +| `docs/wiki/schema/conventions.md` | 🟢 MONOREPO | `CLAUDE.md:63` SSOT import | +| `docs/wiki/schema/README.md` · `frontmatter.md` · `harness.md` · `links.md` · `ownership-matrix.md` · `tags.md` | 🟢 MONOREPO | wiki schema SOT, agent routing 의존 | + +> **spec 정정 항목 (Step 5 PR에서 처리):** `transition-design.md §이동 대상 (1차)` 의 `docs/wiki/wiki/harness/*.md → Architecture/harness/` 행 제거 또는 "monorepo 유지 + vault mirror 검토"로 변경. +> +> **미러링 옵션은 별도 이슈로 분리.** vault 단방향 read-only mirror는 wiki CLI 리팩토링 또는 GitHub Actions sync 필요 — Step 2 범위 밖. + +## Section I — `docs/superpowers/` + +### plans/ (활성 + 완료) + +> ⚠️ **분류 근거 보강 필요:** 아래 "완료" 판정은 파일명 date heuristic. Step 3 진입 시 git log + 해당 이슈 close 상태로 재확정. + +| 파일 | 행선지 | 사유 (heuristic) | +| --------------------------------------------------------- | --------------------------------- | --------------------------- | +| `2026-04-04-editorial-ui-sizing-unification.md` | 🔵 VAULT `Project/plans/2026-04/` | 완료 (≥ 1개월 경과) | +| `2026-04-04-explore-scroll-optimization.md` | 🔵 VAULT | 완료 | +| `2026-04-04-supabase-rls-security-hardening.md` | 🔵 VAULT | 완료 | +| `2026-04-05-explore-search-and-published-filter.md` | 🔵 VAULT | 완료 | +| `2026-04-06-admin-seed-ops-migration.md` | 🔵 VAULT | 완료 | +| `2026-04-09-auth-stabilization.md` | 🔵 VAULT | 완료 | +| `2026-04-09-harness-memory-docs-update.md` | 🔵 VAULT | 완료 | +| `2026-04-09-image-dimensions.md` | 🔵 VAULT | 완료 | +| `2026-04-17-151-admin-real-data-plan.md` | 🔵 VAULT | 완료 | +| `2026-04-17-229-image-proxy-robustness.md` | 🔵 VAULT | 완료 | +| `2026-04-17-llm-wiki-foundation.md` | 🔵 VAULT | 완료 | +| `2026-04-17-sub3-wiki-cli.md` | 🔵 VAULT | 완료 | +| `2026-04-17-upload-modal-refactor.md` | 🔵 VAULT | 완료 | +| `2026-04-23-170-e2e-hardening-infra-p0.md` | 🔵 VAULT | 완료 | +| `2026-04-23-305-source-type-structured-fields-phase-a.md` | 🔵 VAULT | 완료 | +| `2026-04-23-306-upload-ux-polish.md` | 🔵 VAULT | 완료 | +| `2026-04-30-post-actions-modal-ux-plan.md` | 🔵 VAULT | 완료 | +| `2026-05-14-obsidian-vault-infrastructure.md` | 🟢 MONOREPO (활성) | 진행 중. 완료 후 vault 이동 | +| `2026-05-14-personal-vault-claude-volt.md` | 🟢 MONOREPO (활성) | 진행 중 | +| `2026-05-14-tryon-ux-web-funnel.md` | 🟢 MONOREPO (활성) | 진행 중 | +| `2026-05-15-docs-migration-step2-mapping.md` (this file) | 🟢 MONOREPO (활성) | 본 매핑 자체 | + +### plans/archive/ + +| 파일 | 행선지 | +| ------------------------------------------------------ | --------------------------------- | +| `20260406-2026-04-06-admin-seed-ops-migration.md` | 🔵 VAULT `Project/plans/archive/` | +| `20260514-2026-05-14-obsidian-vault-infrastructure.md` | 🔵 VAULT `Project/plans/archive/` | +| `20260514-2026-05-14-personal-vault-claude-volt.md` | 🔵 VAULT `Project/plans/archive/` | + +### specs/ + +전부 🔵 **VAULT `Project/specs/YYYY-MM/`** (완료된 design docs). 활성 spec은 마이그레이션 완료 후 이동. + +| 활성 (monorepo 유지) | 완료 (vault 이동) | +| ----------------------------------------------------- | ---------------------------------- | +| `2026-05-14-obsidian-vault-transition-design.md` | 나머지 25+ specs (2026-04-\* 이전) | +| `2026-05-14-telegram-bot-vault-integration-design.md` | | +| `2026-05-14-personal-vault-claude-volt-design.md` | | +| `2026-05-14-tryon-ux-web-funnel-design.md` | | + +> Step 3 진입 시 완료된 specs 목록을 git log + 이슈 상태로 확정. + +### briefs/ + +| 파일 | 행선지 | +| -------------------------------------------- | -------------------------- | +| `docs/superpowers/briefs/229-image-proxy.md` | 🔵 VAULT `Project/briefs/` | +| `docs/superpowers/briefs/237-rust-audit.md` | 🔵 VAULT `Project/briefs/` | + +## Section J — `docs/prompts/` + +분류 적용: AI 프롬프트 템플릿은 크로스프로젝트 활용 → 🔵 **VAULT 이동.** + +| 파일 | 행선지 | +| --------------------------------------- | ----------------------------------------------------- | +| `docs/prompts/codex/spec-from-issue.md` | 🔵 VAULT `Templates/prompts/codex/spec-from-issue.md` | +| `docs/prompts/gemini/feature-doc.md` | 🔵 VAULT `Templates/prompts/gemini/feature-doc.md` | + +## Section K — `docs/research/` · `docs/incidents/` · `docs/handoffs/` + +| 파일 | 행선지 | +| ---------------------------------------------------------- | ----------------------------- | +| `docs/research/2026-05-14-obsidian-transition-research.md` | 🔵 VAULT `Project/research/` | +| `docs/incidents/2026-05-02-prod-postgrest-stuck.md` | 🔵 VAULT `Project/incidents/` | +| `docs/handoffs/HANDOFF-sns-integration.md` | 🔵 VAULT `Project/handoffs/` | + +## Section L — `docs/testing/` · `docs/performance/` + +| 파일 | 행선지 | 사유 | +| --------------------------- | ----------- | ------------------------------------------------ | +| `docs/testing/scenarios.md` | 🟢 MONOREPO | 이 리포 e2e/qa 시나리오, 코드 변경 시 업데이트 | +| `docs/performance/guide.md` | 🟢 MONOREPO | 이 리포 성능 가이드 (Web Vitals, Next.js 최적화) | + +## Section M — `docs/diagrams/` · `docs/pencil-screen/` · `docs/qa-screenshots/` (확정) + +**조사 결과 (2026-05-19):** + +- `docs/qa-screenshots/` — `packages/web/tests/visual-qa.spec.ts:41` 에서 **테스트 출력 경로로 직접 사용**. README도 `Output: docs/qa-screenshots/`로 명시. +- `docs/pencil-screen/` — `docs/agent/design-system-summary.md`에서 텍스트 참조만. 코드 의존성은 없으나 디자인 시스템 작업 시 즉시 참조. +- `docs/diagrams/` — 코드 구조/ERD/플로우. 코드 변경과 함께 갱신. + +| 디렉토리 | 행선지 | 사유 | +| ------------------------------------ | ----------- | ------------------------------------------------------------------------ | +| `docs/diagrams/*.excalidraw[.png]` | 🟢 MONOREPO | 코드 구조 다이어그램, 코드 변경 시 갱신 | +| `docs/pencil-screen/*.pen` · `*.png` | 🟢 MONOREPO | 디자인 시스템 작업 인접. `docs/agent/design-system-summary.md` 참조 대상 | +| `docs/qa-screenshots/*.png` | 🟢 MONOREPO | `packages/web/tests/visual-qa.spec.ts:41` 테스트 출력 경로 | + +## Section N — `.planning/codebase/` (확정: STUB 패턴) + +**조사 결과 (2026-05-19):** + +`CLAUDE.md` L19/L25/L87-L93 에서 `.planning/codebase/*.md` 전체를 **직접 링크 + Codebase documentation 테이블 SOT**. `docs/agent/api-v1-routes.md:179`에서도 `INTEGRATIONS.md` 직접 참조. + +→ spec은 "vault 이동"이지만, 단순 삭제 시 CLAUDE.md 깨짐. **Step 1 패턴(vault 본문 + monorepo stub) 적용.** + +| 파일 | 행선지 | 비고 | +| ------------------------------------ | ------- | -------------------------------------------------------- | +| `.planning/codebase/README.md` | ⚪ STUB | Step 3에서 vault 복사 + monorepo는 redirect stub | +| `.planning/codebase/STACK.md` | ⚪ STUB | 동상. `CLAUDE.md:19` 직접 링크 | +| `.planning/codebase/ARCHITECTURE.md` | ⚪ STUB | 동상. `CLAUDE.md:88` | +| `.planning/codebase/STRUCTURE.md` | ⚪ STUB | 동상. `CLAUDE.md:89` | +| `.planning/codebase/CONVENTIONS.md` | ⚪ STUB | 동상. `CLAUDE.md:90` | +| `.planning/codebase/TESTING.md` | ⚪ STUB | 동상. `CLAUDE.md:91` | +| `.planning/codebase/INTEGRATIONS.md` | ⚪ STUB | 동상. `CLAUDE.md:92` + `docs/agent/api-v1-routes.md:179` | +| `.planning/codebase/CONCERNS.md` | ⚪ STUB | 동상. `CLAUDE.md:93` | + +> `.planning/` 디렉토리 전체는 GSD 아티팩트. `codebase/`만 vault로 옮기고 나머지(`milestones/`, `phases/` 등)는 monorepo 유지. + +--- + +## 집계 + +| 카테고리 | 파일 수 | 비율 | +| ---------------------------- | ------- | ------ | +| 🟢 MONOREPO | ~80 | 약 65% | +| 🔵 VAULT | ~35 | 약 28% | +| ⚪ STUB (Step 1 완료 + 신규) | 14 | 11% | +| 🟡 ARCHIVE | ~2 | 2% | + +## 다음 단계 (확정 후 진행) + +1. **팀 리뷰** — 본 매핑 합의 +2. **Step 3 (vault 복사 + stub 생성)** — + - VAULT 분류 파일을 vault repo에 복사 + frontmatter 추가 + - STUB 분류 파일(.planning/codebase/\*)은 vault 복사 + monorepo는 redirect stub 전환 + - plans/specs 완료 여부는 git log + 이슈 상태로 재확정 +3. **Step 4 (병행 운영)** — monorepo 원본(VAULT 분류) + vault 양쪽 1개월 유지, drift 모니터링 +4. **Step 5 (monorepo 정리 + spec 정정)** — + - VAULT 분류 원본 삭제 PR + - `transition-design.md §이동 대상` 정정 (wiki/harness 행, adr/ 행) + - CLAUDE.md/docs/agent/ 경로 참조 업데이트 + +## 검증 메모 + +- 2026-05-19: 5/15 Step 1 이후 docs/ 변경 무 (wiki/tags.md 1건 외). 매핑 stale 없음. +- wiki CLI 의존성 검증: `tools/wiki/lib/config.ts` SOT 확인 완료. +- `.planning/codebase/` 의존성 검증: `CLAUDE.md` 직접 링크 8건 확인 완료. + +## 변경 로그 + +- 2026-05-15: 초안 작성 (Step 2 산출물) +- 2026-05-19: advisor 리뷰 반영 + - Section H — wiki/ 전체 MONOREPO 확정 (wiki CLI 의존성 조사 결과) + - Section M — pencil-screen/qa-screenshots MONOREPO 확정 + - Section N — `.planning/codebase/` STUB 패턴 (CLAUDE.md 의존성) + - Section I — plans/specs 완료 판정 재확정 메모 추가 + - 집계 + 다음 단계 업데이트, REVIEW 카테고리 제거 (전부 확정) From a67d7b131c13ad001335cee4a5e19d497056b981 Mon Sep 17 00:00:00 2001 From: thxforall <113906780+thxforall@users.noreply.github.com> Date: Thu, 21 May 2026 12:09:08 +0900 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20Step=202=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=EC=9E=AC=EC=A0=90=EA=B2=80=20=E2=80=94=20Q3=20B=20+=20Q4=20E?= =?UTF-8?q?=20=EB=B0=98=EC=98=81=20(refs=20#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit grill-docs 재점검 (2026-05-21, Q1~Q5) 결과 Step 2 매핑 정정: - Section A: post-centric, home-sections-feasibility → 🟢 MONOREPO 정정 - Section I: plans/specs/briefs 전체 🟢 MONOREPO 잔류 (Q4 E, 5-criteria 4/5 monorepo) - Section K: research/incidents/handoffs 전체 🟢 MONOREPO 잔류 - Section N: `.planning/codebase/*` STUB 패턴 폐기 → 🟢 MONOREPO 잔류 (Q3 B) · 갱신 책임 모호 + outdated 스냅샷이 vault "팀 지식"으로 위장하는 비용 회피 · CLAUDE.md L19/L25/L87-L93 직접 링크 변경 없음 집계 재계산: - 🟢 MONOREPO ~145 / ⚪ STUB 6 (Step 1) / 🔵 VAULT 9 / 🟡 ARCHIVE 2 - vault 이동 대상이 종전 ~35건 → 9건으로 축소 Step 3 scope: - A (이 commit): Step 2 매핑 정정. monorepo 변경 0건. - B (다음): vault `decoded-docs`에 9개 파일 복사 + frontmatter, 별도 PR. `Archive/` 신설안(Q2 C)은 Q4 E 채택으로 자연 폐기. --- ...2026-05-15-docs-migration-step2-mapping.md | 220 +++++++++--------- 1 file changed, 108 insertions(+), 112 deletions(-) diff --git a/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md b/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md index f1975db8..b25ec8e1 100644 --- a/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md +++ b/docs/superpowers/plans/2026-05-15-docs-migration-step2-mapping.md @@ -29,19 +29,19 @@ ## Section A — `docs/` 루트 파일 -| 파일 | 행선지 | 사유 | -| ------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------ | -| `docs/README.md` | 🟢 MONOREPO | 모노레포 문서 인덱스. Step 5에서 vault 링크로 일부 항목 업데이트 | -| `docs/GIT-WORKFLOW.md` | 🟢 MONOREPO | spec 명시 — 이 리포 전용 브랜치 정책 | -| `docs/DATABASE-MIGRATIONS.md` | 🟢 MONOREPO | spec 명시 — 마이그레이션 SOT | -| `docs/LOCAL-DEV.md` | 🟢 MONOREPO | 이 리포 dev 환경 셋업 | -| `docs/BACKEND-ONBOARDING.md` | 🟢 MONOREPO | 이 리포 백엔드 온보딩 | -| `docs/server-setup.md` | 🟢 MONOREPO | 이 리포 인프라 셋업 (Cloudflare/홈서버) | -| `docs/nginx-cloudflare-setup.md` | 🟢 MONOREPO | 이 리포 nginx 설정 | -| `docs/gstack-guide.md` | 🔵 VAULT `Guides/gstack-guide.md` | gstack 사용법은 크로스프로젝트 하네스 지식 | -| `docs/backend-frontend-status.md` | 🟡 ARCHIVE | 시점 한정 상태 스냅샷. 최신 정보는 `.planning/codebase/`와 `docs/agent/` | -| `docs/post-centric-refactoring-summary.md` | 🔵 VAULT `Project/refactoring/post-centric.md` | 완료된 리팩토링 회고 | -| `docs/home-sections-backend-feasibility.md` | 🔵 VAULT `Project/feasibility/home-sections-backend.md` | 완료된 feasibility 분석 | +| 파일 | 행선지 | 사유 | +| ------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------- | +| `docs/README.md` | 🟢 MONOREPO | 모노레포 문서 인덱스. Step 5에서 vault 링크로 일부 항목 업데이트 | +| `docs/GIT-WORKFLOW.md` | 🟢 MONOREPO | spec 명시 — 이 리포 전용 브랜치 정책 | +| `docs/DATABASE-MIGRATIONS.md` | 🟢 MONOREPO | spec 명시 — 마이그레이션 SOT | +| `docs/LOCAL-DEV.md` | 🟢 MONOREPO | 이 리포 dev 환경 셋업 | +| `docs/BACKEND-ONBOARDING.md` | 🟢 MONOREPO | 이 리포 백엔드 온보딩 | +| `docs/server-setup.md` | 🟢 MONOREPO | 이 리포 인프라 셋업 (Cloudflare/홈서버) | +| `docs/nginx-cloudflare-setup.md` | 🟢 MONOREPO | 이 리포 nginx 설정 | +| `docs/gstack-guide.md` | 🔵 VAULT `Guides/gstack-guide.md` | gstack 사용법은 크로스프로젝트 하네스 지식 | +| `docs/backend-frontend-status.md` | 🟡 ARCHIVE | 시점 한정 상태 스냅샷. 최신 정보는 `.planning/codebase/`와 `docs/agent/` | +| `docs/post-centric-refactoring-summary.md` | 🟢 MONOREPO | grill-docs 재점검 (Q4 E): 이 리포 한정 리팩토링 회고, 5-criteria 4/5 monorepo | +| `docs/home-sections-backend-feasibility.md` | 🟢 MONOREPO | grill-docs 재점검 (Q4 E): 이 리포 한정 feasibility, 5-criteria 4/5 monorepo | ## Section B — `docs/adr/` · `docs/architecture/` (Step 1 완료) @@ -147,63 +147,28 @@ spec 명시: 🟢 **MONOREPO 유지.** > > **미러링 옵션은 별도 이슈로 분리.** vault 단방향 read-only mirror는 wiki CLI 리팩토링 또는 GitHub Actions sync 필요 — Step 2 범위 밖. -## Section I — `docs/superpowers/` - -### plans/ (활성 + 완료) - -> ⚠️ **분류 근거 보강 필요:** 아래 "완료" 판정은 파일명 date heuristic. Step 3 진입 시 git log + 해당 이슈 close 상태로 재확정. - -| 파일 | 행선지 | 사유 (heuristic) | -| --------------------------------------------------------- | --------------------------------- | --------------------------- | -| `2026-04-04-editorial-ui-sizing-unification.md` | 🔵 VAULT `Project/plans/2026-04/` | 완료 (≥ 1개월 경과) | -| `2026-04-04-explore-scroll-optimization.md` | 🔵 VAULT | 완료 | -| `2026-04-04-supabase-rls-security-hardening.md` | 🔵 VAULT | 완료 | -| `2026-04-05-explore-search-and-published-filter.md` | 🔵 VAULT | 완료 | -| `2026-04-06-admin-seed-ops-migration.md` | 🔵 VAULT | 완료 | -| `2026-04-09-auth-stabilization.md` | 🔵 VAULT | 완료 | -| `2026-04-09-harness-memory-docs-update.md` | 🔵 VAULT | 완료 | -| `2026-04-09-image-dimensions.md` | 🔵 VAULT | 완료 | -| `2026-04-17-151-admin-real-data-plan.md` | 🔵 VAULT | 완료 | -| `2026-04-17-229-image-proxy-robustness.md` | 🔵 VAULT | 완료 | -| `2026-04-17-llm-wiki-foundation.md` | 🔵 VAULT | 완료 | -| `2026-04-17-sub3-wiki-cli.md` | 🔵 VAULT | 완료 | -| `2026-04-17-upload-modal-refactor.md` | 🔵 VAULT | 완료 | -| `2026-04-23-170-e2e-hardening-infra-p0.md` | 🔵 VAULT | 완료 | -| `2026-04-23-305-source-type-structured-fields-phase-a.md` | 🔵 VAULT | 완료 | -| `2026-04-23-306-upload-ux-polish.md` | 🔵 VAULT | 완료 | -| `2026-04-30-post-actions-modal-ux-plan.md` | 🔵 VAULT | 완료 | -| `2026-05-14-obsidian-vault-infrastructure.md` | 🟢 MONOREPO (활성) | 진행 중. 완료 후 vault 이동 | -| `2026-05-14-personal-vault-claude-volt.md` | 🟢 MONOREPO (활성) | 진행 중 | -| `2026-05-14-tryon-ux-web-funnel.md` | 🟢 MONOREPO (활성) | 진행 중 | -| `2026-05-15-docs-migration-step2-mapping.md` (this file) | 🟢 MONOREPO (활성) | 본 매핑 자체 | - -### plans/archive/ - -| 파일 | 행선지 | -| ------------------------------------------------------ | --------------------------------- | -| `20260406-2026-04-06-admin-seed-ops-migration.md` | 🔵 VAULT `Project/plans/archive/` | -| `20260514-2026-05-14-obsidian-vault-infrastructure.md` | 🔵 VAULT `Project/plans/archive/` | -| `20260514-2026-05-14-personal-vault-claude-volt.md` | 🔵 VAULT `Project/plans/archive/` | - -### specs/ - -전부 🔵 **VAULT `Project/specs/YYYY-MM/`** (완료된 design docs). 활성 spec은 마이그레이션 완료 후 이동. - -| 활성 (monorepo 유지) | 완료 (vault 이동) | -| ----------------------------------------------------- | ---------------------------------- | -| `2026-05-14-obsidian-vault-transition-design.md` | 나머지 25+ specs (2026-04-\* 이전) | -| `2026-05-14-telegram-bot-vault-integration-design.md` | | -| `2026-05-14-personal-vault-claude-volt-design.md` | | -| `2026-05-14-tryon-ux-web-funnel-design.md` | | - -> Step 3 진입 시 완료된 specs 목록을 git log + 이슈 상태로 확정. - -### briefs/ - -| 파일 | 행선지 | -| -------------------------------------------- | -------------------------- | -| `docs/superpowers/briefs/229-image-proxy.md` | 🔵 VAULT `Project/briefs/` | -| `docs/superpowers/briefs/237-rust-audit.md` | 🔵 VAULT `Project/briefs/` | +## Section I — `docs/superpowers/` (확정: 🟢 전부 MONOREPO 잔류) + +> **grill-docs 재점검 결과 (2026-05-21, Q4 E):** plans/specs/briefs 전체를 🟢 MONOREPO 잔류로 정정. 종전 안은 완료된 plans/specs를 `Project/plans/`, `Project/specs/`로 이동하려 했으나, 5-criteria 적용 시 4/5 monorepo 신호: +> +> - 특정 코드 PR 참조 O (대부분 본문에 PR #/issue # 명시) +> - 코드 PR이 plan을 무효화 X (이미 완료 plan은 동결) +> - 이 리포 개발자만 읽음 O +> - 릴리스와 함께 버전닝 △ (한 번만 쓰임) +> - 여러 프로젝트에서 참조 X +> +> * 이미 monorepo에 `docs/superpowers/plans/archive/` 가 운영 중 → vault 분산 시 link 무결성 손실(monorepo PR/이슈 자동 링크 끊김), 검색 분산. + +### plans/ · plans/archive/ · specs/ · briefs/ + +| 카테고리 | 행선지 | +| --------------------------------- | ---------------------------------------------------------------------------------------- | +| `docs/superpowers/plans/` | 🟢 MONOREPO. 완료된 것은 `docs/superpowers/plans/archive/` 로 이동 (이미 운영 중인 패턴) | +| `docs/superpowers/plans/archive/` | 🟢 MONOREPO | +| `docs/superpowers/specs/` | 🟢 MONOREPO | +| `docs/superpowers/briefs/` | 🟢 MONOREPO | + +> 활성/완료 판정 별도 작업은 Step 3 범위 밖. monorepo 안에서 `archive/` 이동만 자체 워크플로우로 진행. ## Section J — `docs/prompts/` @@ -214,13 +179,15 @@ spec 명시: 🟢 **MONOREPO 유지.** | `docs/prompts/codex/spec-from-issue.md` | 🔵 VAULT `Templates/prompts/codex/spec-from-issue.md` | | `docs/prompts/gemini/feature-doc.md` | 🔵 VAULT `Templates/prompts/gemini/feature-doc.md` | -## Section K — `docs/research/` · `docs/incidents/` · `docs/handoffs/` +## Section K — `docs/research/` · `docs/incidents/` · `docs/handoffs/` (확정: 🟢 전부 MONOREPO) + +> **grill-docs 재점검 결과 (2026-05-21, Q4 E):** Section I와 동일 논거. research/incidents/handoffs 모두 monorepo 한정 자료 + PR/이슈 inline 링크 보존을 위해 monorepo 잔류. -| 파일 | 행선지 | -| ---------------------------------------------------------- | ----------------------------- | -| `docs/research/2026-05-14-obsidian-transition-research.md` | 🔵 VAULT `Project/research/` | -| `docs/incidents/2026-05-02-prod-postgrest-stuck.md` | 🔵 VAULT `Project/incidents/` | -| `docs/handoffs/HANDOFF-sns-integration.md` | 🔵 VAULT `Project/handoffs/` | +| 파일 | 행선지 | +| ---------------------------------------------------------- | ----------- | +| `docs/research/2026-05-14-obsidian-transition-research.md` | 🟢 MONOREPO | +| `docs/incidents/2026-05-02-prod-postgrest-stuck.md` | 🟢 MONOREPO | +| `docs/handoffs/HANDOFF-sns-integration.md` | 🟢 MONOREPO | ## Section L — `docs/testing/` · `docs/performance/` @@ -243,56 +210,79 @@ spec 명시: 🟢 **MONOREPO 유지.** | `docs/pencil-screen/*.pen` · `*.png` | 🟢 MONOREPO | 디자인 시스템 작업 인접. `docs/agent/design-system-summary.md` 참조 대상 | | `docs/qa-screenshots/*.png` | 🟢 MONOREPO | `packages/web/tests/visual-qa.spec.ts:41` 테스트 출력 경로 | -## Section N — `.planning/codebase/` (확정: STUB 패턴) +## Section N — `.planning/codebase/` (확정: 🟢 전부 MONOREPO) -**조사 결과 (2026-05-19):** +> **grill-docs 재점검 결과 (2026-05-21, Q3 B):** 종전 안은 STUB 패턴(vault 본문 + monorepo redirect stub)이었으나 폐기. 5-criteria 5/5 monorepo 신호: +> +> | 기준 | `.planning/codebase/*` | +> | ----------------------- | ---------------------------- | +> | 특정 코드 파일/API 참조 | O (전체가 코드 참조) | +> | 코드 PR이 문서를 무효화 | O (의존성·디렉토리 변경마다) | +> | 이 리포 개발자만 읽음 | O | +> | 릴리스와 함께 버전닝 | O | +> | 여러 프로젝트에서 참조 | X | +> +> - STUB 패턴은 갱신 책임 모호(어디서 고치나?) + outdated 스냅샷이 vault "팀 지식"으로 위장하는 신뢰성 비용. +> - ADR과 비대칭은 자연스러움 — ADR = 결정 근거(cross-project) / codebase = 현재 상태(this-repo only). + +| 파일 | 행선지 | +| ------------------------------------ | ----------- | +| `.planning/codebase/README.md` | 🟢 MONOREPO | +| `.planning/codebase/STACK.md` | 🟢 MONOREPO | +| `.planning/codebase/ARCHITECTURE.md` | 🟢 MONOREPO | +| `.planning/codebase/STRUCTURE.md` | 🟢 MONOREPO | +| `.planning/codebase/CONVENTIONS.md` | 🟢 MONOREPO | +| `.planning/codebase/TESTING.md` | 🟢 MONOREPO | +| `.planning/codebase/INTEGRATIONS.md` | 🟢 MONOREPO | +| `.planning/codebase/CONCERNS.md` | 🟢 MONOREPO | + +> 결과적으로 `CLAUDE.md` L19/L25/L87-L93 + `docs/agent/api-v1-routes.md:179` 의 직접 링크는 **변경 없음**. Step 3에서 monorepo 변경 0건. -`CLAUDE.md` L19/L25/L87-L93 에서 `.planning/codebase/*.md` 전체를 **직접 링크 + Codebase documentation 테이블 SOT**. `docs/agent/api-v1-routes.md:179`에서도 `INTEGRATIONS.md` 직접 참조. +--- -→ spec은 "vault 이동"이지만, 단순 삭제 시 CLAUDE.md 깨짐. **Step 1 패턴(vault 본문 + monorepo stub) 적용.** +## 집계 (grill-docs 재점검 후, 2026-05-21) -| 파일 | 행선지 | 비고 | -| ------------------------------------ | ------- | -------------------------------------------------------- | -| `.planning/codebase/README.md` | ⚪ STUB | Step 3에서 vault 복사 + monorepo는 redirect stub | -| `.planning/codebase/STACK.md` | ⚪ STUB | 동상. `CLAUDE.md:19` 직접 링크 | -| `.planning/codebase/ARCHITECTURE.md` | ⚪ STUB | 동상. `CLAUDE.md:88` | -| `.planning/codebase/STRUCTURE.md` | ⚪ STUB | 동상. `CLAUDE.md:89` | -| `.planning/codebase/CONVENTIONS.md` | ⚪ STUB | 동상. `CLAUDE.md:90` | -| `.planning/codebase/TESTING.md` | ⚪ STUB | 동상. `CLAUDE.md:91` | -| `.planning/codebase/INTEGRATIONS.md` | ⚪ STUB | 동상. `CLAUDE.md:92` + `docs/agent/api-v1-routes.md:179` | -| `.planning/codebase/CONCERNS.md` | ⚪ STUB | 동상. `CLAUDE.md:93` | +| 카테고리 | 파일 수 | 비고 | +| ---------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- | +| 🟢 MONOREPO | ~145 | docs/agent/api/database/design-system/wiki + plans/specs/briefs/research/incidents/handoffs + `.planning/codebase/*` | +| ⚪ STUB (Step 1 완료) | 6 | `docs/adr/*` + `docs/architecture/*` (이미 vault stub) | +| 🔵 VAULT (Step 3 대상) | 9 | ai-playbook 6 + prompts 2 + gstack-guide 1 | +| 🟡 ARCHIVE (삭제 예정) | 2 | `ai-playbook/usage-log.md`, `backend-frontend-status.md` | -> `.planning/` 디렉토리 전체는 GSD 아티팩트. `codebase/`만 vault로 옮기고 나머지(`milestones/`, `phases/` 등)는 monorepo 유지. +> grill-docs 재점검 전 종전 집계는 🔵 VAULT ~35건이었으나, plans/specs/briefs/research/incidents/handoffs를 monorepo 잔류로 정정(Q4 E) + `.planning/codebase/*` STUB 패턴 폐기(Q3 B)로 vault 이동 대상이 9건으로 축소됨. ---- +## 다음 단계 (Step 3 — grill-docs 재점검 반영) + +### Step 3 작업 분할 + +| PR/Commit | scope | +| ------------- | ---------------------------------------------------------------------------------------------------------------- | +| A (이 commit) | Step 2 매핑 정정 (본 파일). monorepo 변경 0건. PR #542에 추가 commit | +| B (vault PR) | vault repo `decoded-docs`에 🔵 VAULT 9개 복사 + frontmatter. 별도 브랜치 + PR (활성 워크스페이스 충돌 회피) | -## 집계 +### Step 4 (병행 운영, 1개월) -| 카테고리 | 파일 수 | 비율 | -| ---------------------------- | ------- | ------ | -| 🟢 MONOREPO | ~80 | 약 65% | -| 🔵 VAULT | ~35 | 약 28% | -| ⚪ STUB (Step 1 완료 + 신규) | 14 | 11% | -| 🟡 ARCHIVE | ~2 | 2% | +- 🔵 VAULT 분류 9개 monorepo 원본 + vault 양쪽 유지 → drift 모니터링. +- 🟢 MONOREPO/⚪ STUB는 변경 없음. -## 다음 단계 (확정 후 진행) +### Step 5 (monorepo 정리) -1. **팀 리뷰** — 본 매핑 합의 -2. **Step 3 (vault 복사 + stub 생성)** — - - VAULT 분류 파일을 vault repo에 복사 + frontmatter 추가 - - STUB 분류 파일(.planning/codebase/\*)은 vault 복사 + monorepo는 redirect stub 전환 - - plans/specs 완료 여부는 git log + 이슈 상태로 재확정 -3. **Step 4 (병행 운영)** — monorepo 원본(VAULT 분류) + vault 양쪽 1개월 유지, drift 모니터링 -4. **Step 5 (monorepo 정리 + spec 정정)** — - - VAULT 분류 원본 삭제 PR - - `transition-design.md §이동 대상` 정정 (wiki/harness 행, adr/ 행) - - CLAUDE.md/docs/agent/ 경로 참조 업데이트 +- 🔵 VAULT 9개 monorepo 원본 삭제 PR. +- 🟡 ARCHIVE 2개 (`usage-log.md`, `backend-frontend-status.md`) 삭제. +- `transition-design.md` 본문 정정: + - §이동 대상 (1차) 표에서 `wiki/harness`, `.planning/codebase/`, `adr/` 행 정정 + - §실행 절차 STUB 패턴 언급 정정 (Step 1만 STUB, 나머지 미적용) +- CLAUDE.md/`docs/agent/` 경로 변경 없음 (🟢/⚪ 모두 경로 그대로). ## 검증 메모 - 2026-05-19: 5/15 Step 1 이후 docs/ 변경 무 (wiki/tags.md 1건 외). 매핑 stale 없음. -- wiki CLI 의존성 검증: `tools/wiki/lib/config.ts` SOT 확인 완료. -- `.planning/codebase/` 의존성 검증: `CLAUDE.md` 직접 링크 8건 확인 완료. +- 2026-05-19: wiki CLI 의존성 검증: `tools/wiki/lib/config.ts` SOT 확인 완료. +- 2026-05-19: `.planning/codebase/` 의존성 검증: `CLAUDE.md` 직접 링크 8건 확인 완료. +- 2026-05-21 grill-docs 재점검: + - vault repo (`decoded-docs`) 실측 — Sources/, Expenses/, daily standups, sprint S1 등 활성 워크스페이스로 진화 확인. spec 원안 가정(빈 마이그레이션 타겟)과 어긋남. + - 5-criteria 재적용 → `.planning/codebase/*` 5/5 monorepo, plans/specs 4/5 monorepo → vault 이동 대상에서 제외. + - 인접 spec(`personal-vault-claude-volt`, `telegram-bot-vault-integration`)은 본 마이그레이션과 독립 작업으로 분리 확인. ## 변경 로그 @@ -303,3 +293,9 @@ spec 명시: 🟢 **MONOREPO 유지.** - Section N — `.planning/codebase/` STUB 패턴 (CLAUDE.md 의존성) - Section I — plans/specs 완료 판정 재확정 메모 추가 - 집계 + 다음 단계 업데이트, REVIEW 카테고리 제거 (전부 확정) +- 2026-05-21: grill-docs 재점검 반영 (Q1~Q5) + - Q1: vault 활성 워크스페이스 인식 — Step 3 scope 재설계 + - Q3: Section N `.planning/codebase/*` STUB 패턴 폐기 → 🟢 MONOREPO 잔류 + - Q4: Section I/K plans/specs/briefs/research/incidents/handoffs 전체 🟢 MONOREPO 잔류 (5-criteria 4/5 monorepo) + - Section A: post-centric, home-sections-feasibility → MONOREPO 정정 + - Q5: Step 3 scope 9개 vault 복사로 축소, monorepo 변경 0건. `Archive/` 신설안 자연 폐기 From c25f33da7cede290dbb55e430d202c93102a18e5 Mon Sep 17 00:00:00 2001 From: thxforall <113906780+thxforall@users.noreply.github.com> Date: Thu, 21 May 2026 12:33:34 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix(docs):=20vault=20stub=20frontmatter=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=E2=80=94=20wiki-lint=20=ED=86=B5=EA=B3=BC?= =?UTF-8?q?=20(refs=20#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 1 (PR #542 first commit `64a5a76`) 에서 ADR + architecture 본문을 vault redirect stub으로 전환할 때 frontmatter도 함께 제거되어 wiki-lint MISSING_FRONTMATTER 실패. 6개 stub에 minimal frontmatter 추가: - docs/adr/ADR-0001-ai-dev-boilerplate.md - docs/adr/ADR-0002-llm-wiki-foundation.md - docs/architecture/README.md - docs/architecture/assets-project.md - docs/architecture/data-pipeline.md - docs/architecture/state-management.md 각 stub: owner=llm, status=deprecated (Step 5에서 삭제 예정), tags 에 [obsidian, deprecated] 포함하여 wiki-lint 허용 어휘 준수. ADR 의 title 콜론은 YAML parse 위해 큰따옴표 인용. --- docs/adr/ADR-0001-ai-dev-boilerplate.md | 8 ++++++++ docs/adr/ADR-0002-llm-wiki-foundation.md | 8 ++++++++ docs/architecture/README.md | 8 ++++++++ docs/architecture/assets-project.md | 8 ++++++++ docs/architecture/data-pipeline.md | 8 ++++++++ docs/architecture/state-management.md | 8 ++++++++ 6 files changed, 48 insertions(+) diff --git a/docs/adr/ADR-0001-ai-dev-boilerplate.md b/docs/adr/ADR-0001-ai-dev-boilerplate.md index 3d7f831d..9e52be4c 100644 --- a/docs/adr/ADR-0001-ai-dev-boilerplate.md +++ b/docs/adr/ADR-0001-ai-dev-boilerplate.md @@ -1,3 +1,11 @@ +--- +title: "ADR-0001: Multi-AI Development Boilerplate" +owner: llm +status: deprecated +updated: 2026-05-21 +tags: [architecture, harness, obsidian, deprecated] +--- + # ADR-0001: Multi-AI Development Boilerplate > **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** diff --git a/docs/adr/ADR-0002-llm-wiki-foundation.md b/docs/adr/ADR-0002-llm-wiki-foundation.md index dbc43e8d..440fcb3a 100644 --- a/docs/adr/ADR-0002-llm-wiki-foundation.md +++ b/docs/adr/ADR-0002-llm-wiki-foundation.md @@ -1,3 +1,11 @@ +--- +title: "ADR-0002: LLM Wiki Foundation" +owner: llm +status: deprecated +updated: 2026-05-21 +tags: [architecture, harness, obsidian, deprecated] +--- + # ADR-0002: LLM Wiki Foundation > **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 7feed864..aa1f8b31 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -1,3 +1,11 @@ +--- +title: System Architecture +owner: llm +status: deprecated +updated: 2026-05-21 +tags: [architecture, obsidian, deprecated] +--- + # System Architecture > **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** diff --git a/docs/architecture/assets-project.md b/docs/architecture/assets-project.md index 5481d5d0..6999f903 100644 --- a/docs/architecture/assets-project.md +++ b/docs/architecture/assets-project.md @@ -1,3 +1,11 @@ +--- +title: Assets Supabase Project +owner: llm +status: deprecated +updated: 2026-05-21 +tags: [architecture, db, obsidian, deprecated] +--- + # Assets Supabase Project > **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** diff --git a/docs/architecture/data-pipeline.md b/docs/architecture/data-pipeline.md index 91cfce2a..e759900f 100644 --- a/docs/architecture/data-pipeline.md +++ b/docs/architecture/data-pipeline.md @@ -1,3 +1,11 @@ +--- +title: Data Pipeline +owner: llm +status: deprecated +updated: 2026-05-21 +tags: [architecture, db, obsidian, deprecated] +--- + # Data Pipeline > **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.** diff --git a/docs/architecture/state-management.md b/docs/architecture/state-management.md index 84232fb0..760c837d 100644 --- a/docs/architecture/state-management.md +++ b/docs/architecture/state-management.md @@ -1,3 +1,11 @@ +--- +title: State Management +owner: llm +status: deprecated +updated: 2026-05-21 +tags: [architecture, ui, obsidian, deprecated] +--- + # State Management > **이 문서는 [decoded-docs vault](https://github.com/decodedcorp/decoded-docs)로 이전되었습니다.**