diff --git a/figma_to_html_agent/DISCUSSION-SUMMARY-20260411.md b/figma_to_html_agent/DISCUSSION-SUMMARY-20260411.md new file mode 100644 index 0000000..2f5d9c0 --- /dev/null +++ b/figma_to_html_agent/DISCUSSION-SUMMARY-20260411.md @@ -0,0 +1,303 @@ +# Design Agent 구조 재정리를 위한 논의 요약 (2026-04-11) + +> 이 문서는 figma_to_html_agent 세션에서 논의된 핵심 사항을 정리한 것. +> design_agent 총괄 AI에게 전달하여 전체 파이프라인 재구성에 참고하기 위한 브리핑. + +--- + +## 1. Figma → HTML 변환 구조 + +### 1.1 전체 흐름 + +``` +피그마 프레임 (디자인 원본) + ↓ MCP 도구로 데이터 수집 (구조, 스타일, 스크린샷) +실측 기록부 (flat.md) + ↓ 그라데이션 수학 변환 + CSS 작성 +블록 HTML (1:1 정적 변환물) + ↓ Selenium 자동 검증 +블록 라이브러리 등록 (catalog.yaml) +``` + +### 1.2 산출물 3종 세트 + +| 파일 | 역할 | 비유 | +|------|------|------| +| **flat.md** | 원본 피그마의 모든 요소 위치·크기·각도·폰트·색상 기록 | 현장 실측 기록부 | +| **{블록}.html** | HTML+CSS+이미지 참조. 실제 렌더되는 파일 | 표준 설계도면 | +| **catalog.yaml** | 블록 용도·조건·슬롯 메타 정보 | 건축 자재 카탈로그 | + +### 1.3 AI vs 코드 역할 분담 + +| 구간 | 주체 | 오차 | +|------|------|------| +| 데이터 수집 (좌표, 색, 폰트) | **코드** (Figma MCP) | 0 | +| 그라데이션 좌표→각도 변환 | **코드** (gradient_math.py) | 0 | +| HTML 렌더링 | **코드** (Selenium) | 0 | +| 구조 해석, HTML 설계, 검증 판단 | **AI** | 존재 | + +**핵심:** AI는 블록 템플릿을 **1회 설계**하는 데만 관여. 이후 런타임 사용은 **100% 코드** (Jinja2 + CSS). + +--- + +## 2. 블록 선택 방식 — 결론: TF-IDF 직접 매칭 + +### 2.1 검토한 방식들 + +| 방식 | 적합성 | 이유 | +|------|--------|------| +| **FAISS (임베딩 벡터)** | ❌ 불필요 | 35개 블록에 오버스펙. 같은 도메인 텍스트는 벡터가 비슷해서 구분 어려움 | +| **RAG (LLM 판단)** | ❌ 불필요 | 입력이 이미 구조화(MDX)되어 있고 블록 수가 적어 AI 판단 불필요 | +| **BM25 + Kiwi** | ⚠️ 가능 | catalog 설명 품질에 의존. 설명이 잘 되어있으면 작동 | +| **구조 태그 매칭 (코드)** | ✅ 가능 | MDX 파싱 → item_count, relation_type → catalog 태그 비교 | +| **TF-IDF 직접 매칭** | ✅ **최적** | 디자인 안의 실제 텍스트와 MDX 텍스트를 직접 비교 | + +### 2.2 TF-IDF 직접 매칭이 최적인 이유 + +**핵심 전제:** MDX 텍스트와 Figma 디자인 안의 텍스트가 **같은 원본 문서에서 나온 것**이라 핵심 키워드가 자연스럽게 겹침. + +``` +[1회성 사전 준비] +각 Figma 디자인(35개)에서 텍스트 추출 → 블록별 텍스트 저장 + +[매칭 실행] +MDX 텍스트 → TF-IDF로 35개 블록 텍스트와 유사도 계산 → 최고 점수 디자인 선택 → 텍스트 교체 +``` + +**장점:** +- AI 비용 0원 (100% 코드) +- 결정론적 (같은 입력 → 같은 결과) +- 35개 전수 비교에 0.01초 +- 디버깅 용이 ("이 단어들이 겹쳐서 선택됨" 즉시 설명 가능) +- catalog.yaml 설명 품질에 의존 안 함 (디자인 안의 실제 텍스트로 매칭) +- FAISS, RAG, 임베딩 모델 등 복잡한 인프라 불필요 + +**실증:** BM25 테스트에서 콘텐츠A(기술/사람/자연)→3열블록, 콘텐츠B(Process/Product)→2열블록 정확 매칭 확인. + +### 2.3 이 방식이 성립하는 조건 + +1. 블록 수가 30~35개 수준 (소규모) +2. 각 디자인의 텍스트가 서로 충분히 다름 (같은 문서 내 다른 섹션) +3. MDX 콘텐츠가 Figma 디자인의 원본 텍스트와 동일 도메인 +4. 매칭 후 "텍스트만 업데이트"하는 구조 (디자인 구조 자체는 변경 안 함) + +**한계 시점:** 블록이 수백 개가 되거나, 완전히 새로운 도메인 콘텐츠가 들어오면 구조 분석(AI) 또는 임베딩 검색 추가 필요. + +--- + +## 3. 전체 파이프라인 재정리 제안 + +### 3.1 현재 파이프라인 (복잡) + +``` +MDX → Kei 실장(AI) 구조분석 → catalog 태그 매칭(코드) → 블록 선택 + → Kei 편집자(AI) 텍스트 편집 → Jinja2 렌더(코드) → 슬라이드 +``` + +### 3.2 단순화된 파이프라인 (제안) + +``` +MDX → TF-IDF 매칭(코드) → 디자인 선택 → 텍스트 교체(코드) → 슬라이드 +``` + +**AI가 필요한 시점:** +- 매칭 결과가 애매할 때 (점수 차이가 미미한 후보 2~3개 중 선택) +- 텍스트가 디자인 공간에 안 맞을 때 (요약/편집 필요) +- 새로운 패턴의 디자인이 필요할 때 (블록 신규 제작) + +**AI가 불필요한 시점:** +- 디자인 선택 (TF-IDF 매칭) +- 텍스트 삽입 (Jinja2 치환) +- 렌더링 (CSS + 브라우저) + +### 3.3 블록 선택에서 AI 개입 최소화 구조 + +``` +MDX 텍스트 + ↓ +[코드] TF-IDF 매칭 → 후보 1~3개 + 점수 + ↓ +점수 차이 크면 (예: 1위가 2위보다 2배 이상) → 코드가 자동 확정 +점수 차이 작으면 → AI가 후보 중 최종 선택 (최소 개입) + ↓ +[코드] 텍스트 교체 + 렌더 +``` + +--- + +## 4. Figma MCP 관련 발견 사항 + +### 4.1 MCP 2종류 + +| MCP | 제공자 | 장단점 | +|-----|-------|--------| +| Framelink Figma MCP | 커뮤니티 (REST API) | rotation/blend mode 미제공 | +| Figma Desktop Dev Mode MCP | Figma 공식 (로컬 SSE) | rotation/blend/정확한 CSS 모두 제공 | + +**결론:** Figma Desktop Dev Mode MCP (`127.0.0.1:3845/sse`)가 정답. Framelink MCP는 rotation 등 핵심 정보 부재로 한계. + +### 4.2 Figma Dev Mode HTML 직접 활용 + +Figma Dev Mode에서 프레임 전체를 HTML 코드로 export 가능: +- transform: rotate() 포함 +- background-blend-mode 포함 +- 모든 좌표/크기/색상 포함 +- 이 HTML을 기반으로 scale 조정 후 직접 사용 가능 + +### 4.3 MCP 한계 (확인됨) + +- rotation 속성 미제공 (Framelink MCP) +- dimensions가 bounding box (회전 포함된 크기) — 원본 크기와 다를 수 있음 +- gradient 각도: Figma 좌표계 vs CSS 좌표계 차이 → 수학 변환 필요 + +--- + +## 5. CSS 구현 원칙 + +### 5.1 왜 SVG가 아닌 CSS인가 + +| 이유 | 설명 | +|------|------| +| **한글 텍스트** | CSS는 자동 줄바꿈·어절 처리. SVG ``는 수작업 줄바꿈 | +| **레이아웃** | CSS flex/grid/aspect-ratio. SVG는 레이아웃 시스템 없음 | +| **혼합 콘텐츠** | 도형+이미지+텍스트 섞기. HTML이 자연스러움 | +| **템플릿화** | Jinja2 변수 치환이 CSS에서 자연스러움 | +| **예외** | 곡선 아크, 복잡한 필터 → SVG/PNG 유지 | + +### 5.2 그라데이션 수학 변환 + +- SVG: 좌표 기반 (x1, y1, x2, y2) +- CSS: 각도 기반 (Xdeg) +- 변환: `atan2(y2-y1, x2-x1)` → `scripts/gradient_math.py` +- 회전+그라데이션 합성: 래퍼 구조(`transform: rotate()`)로 분리해서 수학 합성 회피 + +### 5.3 회전 처리 + +- 도형 자체를 회전하지 않고, **래퍼(wrapper) 컨테이너를 회전** +- border-radius와 gradient가 같이 돌아감 +- Figma Dev Mode가 자동으로 이 래퍼 구조를 생성해줌 + +--- + +## 6. 핵심 결론 + +1. **블록 선택은 TF-IDF 직접 매칭으로 충분** — RAG/FAISS 불필요 +2. **AI는 "블록 템플릿 1회 제작"에만 관여** — 런타임은 100% 코드 +3. **Figma Desktop Dev Mode MCP가 정답** — Framelink MCP는 한계 +4. **CSS 우선, SVG는 곡선만** — 한글 텍스트 처리와 템플릿화 때문 +5. **복잡한 구조(RAG/임베딩/구조분석)보다 단순한 구조(TF-IDF+코드)가 현재 규모에 최적** + +--- + +## 7. 산출물 구조 확정 (프레임당 4종) + +### 7.1 프레임별 산출물 + +``` +figma_to_html_agent/ +├── CLAUDE.md, PROCESS.md, RULES.md ... ← 프로세스 문서 +├── scripts/gradient_math.py ← 수학 변환 +├── blocks_index.md ← 변환 완료 인덱스 +│ +├── block-tests/ ← 프레임별 산출물 +│ ├── {slug}.html ← ① HTML 블록 +│ ├── {slug}_flat.md ← ② 노드별 실측 기록 +│ └── {slug}_texts.md ← ③ 텍스트만 모은 목록 (TF-IDF용, 신규) +│ +└── texts_index/ ← TF-IDF 매칭 인덱스 (신규) + └── (35개 프레임 텍스트 인덱스) +``` + +- ①②는 기존 프로세스 +- ③은 신규: 프레임 안의 **모든 텍스트를 빠짐없이** 추출해서 별도 목록으로 저장 +- texts_index/는 ③들을 모아 TF-IDF 인덱스로 구축 + +### 7.2 ③ texts.md가 필요한 이유 + +현재 flat.md의 문제: +- 텍스트가 노드 계층 설명 안에 **산발적으로 흩어져** 있음 +- 일부 노드는 텍스트 내용이 **생략**됨 (예: `17:1941 ~ 17:1952 (12개)`) +- TF-IDF 매칭용으로 쓰려면 프레임별 **텍스트만 모은 깨끗한 목록** 필요 + +### 7.3 산출물의 사용처 + +``` +① {slug}.html → templates/에 프로모션 (블록 렌더링용) +② {slug}_flat.md → yaml 메타 정보 + HTML 제작 근거 +③ {slug}_texts.md → TF-IDF 인덱스 (블록 자동 선택용) +``` + +--- + +## 8. TF-IDF 선택 근거 (왜 다른 유사도 방법이 아닌가) + +### 8.1 매칭 신호 = "단어 겹침" + +MDX와 Figma 텍스트는 같은 원본 문서에서 나온 것이라 **같은 단어가 직접 겹침**. +의미 추론이 아니라 단어 일치가 매칭 근거. + +### 8.2 IDF의 역할 + +``` +"건설" → 35개 프레임 전부에 등장 → IDF 낮음 → 구분 못 함 +"3D모델" → 3개 프레임에만 → IDF 높음 → 구분 가능 +"Copy&Paste" → 1개 프레임에만 → IDF 매우 높음 → 확정적 +``` + +"모든 프레임에 나오는 흔한 단어는 무시, 특정 프레임에만 나오는 희귀 단어는 강조" + +### 8.3 다른 방법이 안 맞는 이유 + +| 방법 | 문제 | +|------|------| +| FAISS/임베딩 | 같은 도메인 단어를 **뭉개서** 프레임 구분 못 함. "병렬"≈"교차"로 취급 | +| RAG | 35개에 AI 판단은 오버스펙. 비용 발생 | +| Jaccard | 단어 희귀성 무시. "건설"이든 "Copy&Paste"든 동일 1점 | +| Word2Vec/BERT | 의미 유사도는 불필요 (단어가 직접 겹치므로). 한국어 도메인 모델 품질 불확실 | +| BM25 | 가능하지만 35개 수준에서 TF-IDF와 차이 없음. TF-IDF가 더 단순 | + +--- + +## 9. 정리 완료 현황 (2026-04-16) + +### 9.1 templates/ (완료) + +``` +templates/ +├── blocks/ +│ └── slide-base.html ← 유일하게 남긴 것 +└── catalog.yaml ← 초기화 +``` + +기존 104개 파일 삭제 완료. + +### 9.2 figma_to_html_agent/ (완료) + +``` +figma_to_html_agent/ +├── CLAUDE.md, PROCESS.md, MATH.md, RULES.md, PROCESS-CONTROL.md, README.md +├── DISCUSSION-SUMMARY-20260411.md +├── INSIGHT-GRADIENT.md +├── blocks_index.md +└── scripts/gradient_math.py +``` + +구 파일(FIGMA-EXTRACTION.md, block_index.faiss, figma_beps_full.json, block-tests/, figma-analysis/ 등) 전부 삭제 완료. + +### 9.3 다음 단계 + +깨끗한 상태에서 35개 프레임을 1개씩 재추출: +1. Figma Desktop MCP 또는 Dev Mode HTML로 데이터 수집 +2. 프레임당 3종 산출물 생성 (html + flat.md + texts.md) +3. texts_index/ 에 TF-IDF 인덱스 누적 +4. templates/에 프로모션 (사용자 승인 후) + +--- + +## 10. design_agent 총괄에게 전달할 질문/제안 + +1. **Phase Q 블록 선택 단계**를 TF-IDF 직접 매칭으로 단순화할 수 있는가? +2. **Kei 실장의 "구조 분석" 역할**이 TF-IDF 매칭으로 대체 가능한 범위는? +3. **catalog.yaml의 content_structure/when/not_for 필드**를 유지하되, 1차 매칭은 TF-IDF로 하고 AI는 fallback으로만 쓰는 구조가 합리적인가? +4. **Figma Desktop Dev Mode MCP**를 PROCESS.md STEP 1~3에 공식 반영할 것인가? +5. **산출물 3종 → 4종 (texts.md 추가)** 으로 PROCESS.md 업데이트할 것인가? diff --git a/figma_to_html_agent/HARNESS.md b/figma_to_html_agent/HARNESS.md new file mode 100644 index 0000000..2b01ec9 --- /dev/null +++ b/figma_to_html_agent/HARNESS.md @@ -0,0 +1,294 @@ +# 변환 하네스 (Conversion Harness) + +> **이 체크리스트의 모든 항목을 순서대로 통과해야 한다. 건너뛰기 금지.** +> 각 항목에서 "확인"이 안 되면 다음으로 넘어가지 않는다. +> **2층 구조: 1층(행동 통제) → 2층(구현)**. 1층을 통과하지 않으면 2층 진입 금지. + +--- + +# ═══ 1층: 행동 통제 하네스 ═══ + +## A. 사용자 요청 해석 + +- [ ] 사용자가 실제로 말한 문제를 **한 문장으로** 다시 적었는가? +- [ ] 내가 하려는 수정이 **그 문제에 직접 대응**하는가? +- [ ] 사용자가 말하지 않은 구조 변경을 하려면 **사전 합의**가 필요한가? +- [ ] "이것도 같이 바꾸면 좋겠다"는 생각이 들면 → **멈추고 물어본다** + +## B. 블록 분류 (patch / rewrite / hold) + +작업 시작 전 반드시 분류하고 근거를 3줄로 적는다. + +``` +이 블록은 무엇인가? +├── patch — 구조는 살아 있고 특정 부분만 수정하면 됨 +│ 근거: ___ +├── rewrite — 구조 자체가 틀어져서 처음부터 다시 해야 함 +│ 근거: ___ +└── hold — 지금 건드리지 않고 보류 + 근거: ___ +``` + +- [ ] 분류 결정함 → `blocks/{id}/classification.md`에 기록 +- [ ] 근거 3줄 적음 +- [ ] **patch가 아닌 블록에 임의 구조 전환 금지** +- [ ] **rewrite 블록은 사용자 확인 후에만 착수** + +## C. 변경 범위 제한 + +- [ ] **이번 수정의 단일 목표**를 한 문장으로 적었는가? +- [ ] 이번 수정에서 바꾸는 축은 **1개**인가? + - 예: spacing만 / bullet model만 / gradient CSS 변환만 +- [ ] 다른 축은 건드리지 않았는가? +- [ ] 현재 정상 동작하는 요소를 **변경 금지 목록**으로 적었는가? + +``` +변경 대상: ___ +변경하지 않는 것: ___ +``` + +## D. 변경 전 기록 + +- [ ] 수정 전 현재 값/구조 기록 (되돌릴 수 있도록) +- [ ] 변경 전 렌더링 스크린샷 baseline 저장 + +## E. 중단 조건 + +아래 상황 발생 시 **즉시 멈추고 사용자에게 보고**: + +- [ ] 인코딩 깨짐 발견 +- [ ] MCP 좌표와 렌더 결과가 크게 다름 (30px+ 이상 차이) +- [ ] 패치하려던 블록이 구조 붕괴 상태 +- [ ] 한 군데 고치니 다른 곳이 깨짐 (cascade failure) +- [ ] 사용자 요청과 다른 방향으로 가고 있음을 인지 + +→ patch 중단 +→ rewrite required로 분류 전환 +→ `blocks/{id}/STATUS.md` 남기고 다음 블록으로 + +## F. 완료 판정 + +patch 또는 rewrite가 끝났다고 판단하기 전에: + +- [ ] 사용자가 지적한 문제가 **실제로 사라졌는가** (렌더링 확인) +- [ ] 새로 생긴 **부작용이 없는가** +- [ ] baseline 대비 **더 나빠진 요소가 없는가** +- [ ] 아래 4개 산출물을 남겼는가: + - before screenshot + - after screenshot + - 변경 요약 3줄 + - classification 결과 (patch/rewrite/hold) + +위 전부 통과해야 "완료". 하나라도 안 되면 미완료. + +--- + +# ═══ 2층: 구현 하네스 ═══ + +> **1층 A~F를 모두 통과한 후에만 아래 진행** + +## 0. 시작 전 — 규칙 파일 읽기 + 행동 원칙 확인 + +- [ ] RULES.md 읽음 (R1~R19 전부) +- [ ] PROCESS-CONTROL.md 읽음 (규칙 1~8) +- [ ] PROCESS.md 읽음 (STEP 0~10) +- [ ] HARNESS.md 읽음 (이 파일) +- [ ] blocks_index.md 읽음 (기존 패턴 확인) + +### 행동 원칙 (PROCESS-CONTROL에서, 매 작업 전 상기) +- [ ] **소스는 Figma 데이터다** — PNG를 보고 판단하지 않음 (규칙1) +- [ ] **이미지 해석 금지** — 멀티모달로 gradient 방향 추측 안 함 (규칙2) +- [ ] **작동하는 것은 건드리지 않는다** — A만 문제면 A만 수정 (규칙3) +- [ ] **한 번에 하나만 바꾼다** — gradient 각도와 border-radius 동시 변경 금지 (규칙4) +- [ ] **사용자가 말한 것만 한다** — 자의적 해석 금지 (규칙5) +- [ ] **찍어맞추기 금지** — 값을 바꾸기 전에 WHY를 먼저 설명 (규칙6) +- [ ] **쉬운 전면 재작성 금지** — 80점에서 2개 고칠 때 구조를 갈아엎지 않음 (규칙7) +- [ ] **MCP 데이터 전수 반영** — "핵심만", "단순화" 금지 (규칙8) + +--- + +## 1. MCP 데이터 수집 + +- [ ] get_metadata 호출 → 모든 노드 ID, 위치, 크기 확보 +- [ ] get_design_context 호출 → gradient, font, style, rotation 확보 +- [ ] 에셋 URL 목록 추출 + +--- + +## 2. 에셋 분류 — R9 CSS vs 이미지 판정 (에셋마다 반드시 수행) + +각 에셋에 대해: + +``` +이 에셋이 뭔가? +├── 단순 사각형 + gradient → CSS linear-gradient + border-radius [R9] +├── 단순 원 + gradient → CSS border-radius:50% + linear-gradient [R9] +├── 단순 직선/점선 → CSS border [R9] +├── 단색 사각형 → CSS background-color [R9] +├── plus-darker blend → multiply로 교체 [R10] +└── 복잡 path / 사진 / 아이콘 / 곡선 → 이미지 유지 +``` + +- [ ] 모든 에셋 분류 완료 +- [ ] CSS 대체 가능한 에셋은 다운로드하지 않음 +- [ ] 이미지 유지 에셋만 다운로드 + +--- + +## 3. gradient 변환 — R9 CSS 대체 에셋 + +CSS로 대체하기로 한 에셋마다: + +- [ ] SVG 파일을 열어 linearGradient 데이터 추출 +- [ ] gradient_math.py로 CSS linear-gradient 변환 (인라인 복사 금지) +- [ ] viewBox padding이 있으면 R12 remap 적용 +- [ ] 변환 결과 기록 + +--- + +## 4. 구조 결정 — R17 레이아웃 타입 + +``` +이 블록의 레이아웃은? +├── 순차 행 (비교표, 이슈 목록, 불릿 리스트) +│ → flex column (행 간) + flex row (행 내부) [R17] +│ → 텍스트 늘면 행이 밀림 +│ +├── 표 (행×열 구조) +│ → CSS grid [R17] +│ → border가 곧 그리드 라인 +│ → 셀 높이 auto +│ +├── 2D 다이어그램 (원, 노드, 겹침) +│ → absolute + 명시 height + zoom [R19] +│ → 겹침이 있으므로 flex 불가 +│ +└── 좌우 분할 (좌:이미지 / 우:목록) + → display:flex (좌:flex-none / 우:flex column) +``` + +- [ ] 레이아웃 타입 결정 +- [ ] zoom 적용 (transform:scale 금지) [R19] + +--- + +## 5. HTML 작성 — 요소별 체크 + +### 5-A. DOM 순서 +- [ ] MCP metadata 순서 = HTML DOM 순서 (나중 요소가 위에 렌더) +- [ ] z-index 별도 지정 필요 없음 (DOM 순서로 해결) + +### 5-B. 텍스트 요소 +- [ ] 본문 텍스트에 position:absolute 사용 안 함 [R17] +- [ ] overflow:hidden으로 텍스트 숨기기 안 함 [R17] +- [ ] 고정 height로 텍스트 제한 안 함 [R17] +- [ ] word-break: keep-all 적용 [R14] +- [ ] descender 보정 필요한가? [R1] + - line-height / font-size < 1.448 (Noto Sans KR) → padding-bottom 계산 +- [ ] gradient 텍스트인가? → background-clip: text [R4] +- [ ] 다중 fills인가? → 첫 번째 불투명 fill만 사용 [R5] +- [ ] 중복 노드인가? (동일 좌표 + 동일 내용) → 1개만 렌더 [R6] +- [ ] 세로 텍스트인가? (width < fontSize × 0.8) → `
` 줄바꿈, writing-mode 사용 금지 [R3] +- [ ] vertical center 정렬이 필요한가? → flex justify-center [R15] +- [ ] 불필요한 `
` 삽입 안 함 (자연 wrap에 맡김) + +### 5-C. 불릿/마커 리스트 [R13] +- [ ] `