# 메인플랜: 랜딩 페이지 빌더
## [정렬] WYSIWYG 빌더 목표/요구사항/수용기준 (2025-11-17)
- **목표**
- /[locale]/wysiwyg 에서 비개발자도 랜딩을 시각적으로 제작(Add→편집→Export)할 수 있다.
- Export 산출물은 ZIP(index.html/css/js/assets/*, site.webmanifest, robots.txt, 무결성 파일)을 포함한다.
- **확정 요구사항**
- R1: 텍스트/이미지/버튼 추가 및 속성 편집(X/Y/W/H/Rotate + 타입별 속성)
- R2: 조작 UX(드래그/리사이즈/회전, 8px/15° 스냅, 멀티선택·마퀴, 키보드 너지, 가이드 표시, a11y 라이브 리전)
- R3: 레이어 관리(순서 이동, lock, visible 토글)
- R4: Export ZIP 생성(무결성 파일 포함) 및 verify ok
- R5: 경로 고정(/[locale]/wysiwyg)
- **수용 기준(DoD)**
- A1: /wysiwyg에서 Add Text → 속성 수정 → Export 실행 시 ZIP 생성, HTML/CSS에 수정 반영
- A2: 드래그/리사이즈/회전 스냅 동작 및 가이드 표시 스모크 그린
- A3: 라이브 리전 문구가 선택/가이드/너지에 맞춰 갱신
- A4: 생성된 ZIP에 대해 verifyIntegrity 결과 ok=true
- A5: 레이어 순서 변경이 렌더에 반영
- **TDD 매핑**
- R1 → PropsPanel.test(기존) + WysiwygBuilderPage 추가/수정 반영 스모크(신규)
- R2 → Canvas.*.test(기존) + 빌더 멀티선택/마퀴 스모크(신규)
- R3 → LayersPanel.test(기존) + 빌더 순서 반영(신규)
- R4 → exportFrame/buildZip/무결성(기존) + 빌더 Export 스모크(신규)
- R5 → 라우트 스모크(기존)
- **순차 작업 계획**
1) 수용 테스트 추가(빌더 Add→Edit→Export, 레이어 순서 반영, 멀티선택/마퀴 스모크)
2) /wysiwyg Export에 무결성 파일 포함 연동
3) 최소 UI 토글(가이드/그리드) 노출 및 문서 업데이트
4) 전체 그린 확인 후 브랜치 범위 점검 → 푸쉬
### 가이드라인 고도화 진행(2025-11-16)
- 목표: 객체-객체 스냅 가이드(8px 임계)
- 수직 중심 정렬 가이드/스냅
- 수평 중심 정렬 가이드/스냅
- 좌/우/상/하 가장자리 정렬 가이드/스냅
- TDD
- `components/wysiwyg/Canvas.guidelines.advanced.test.tsx`
- 수직 중심 스냅 가이드(8px) 검증. Moveable 모킹, `act`/`waitFor` 적용
- `components/wysiwyg/Canvas.guidelines.horizontal.test.tsx`
- 수평 중심 스냅 가이드(8px) 검증
- `components/wysiwyg/Canvas.guidelines.edges.test.tsx`
- 좌/우 에지 스냅 가이드(8px) 1차 케이스 검증(T→L 등 에지 페어)
- 구현
- `components/wysiwyg/Canvas.tsx`
- onDrag: 그리드(8px) → 객체 중심 스냅(수직/수평) → 객체 에지 스냅(좌/우/상/하) 우선순위 적용
- 가이드 좌표: 축별로 에지 > 중심 > 그리드 순서로 표시
- 드래그 시작 시 가이드 표시, 종료 시 가이드 해제 유지
- 결과: 전체 테스트 그린(131/131)
#### 오류 원인/디버깅(메모)
- 실패: 수직 중심 스냅 테스트에서 선택 대상이 기본 선택되지 않아 좌표 미변경 → 레이어 objects 순서를 조정(o1을 첫 요소로)
- 실패: 비동기 상태 반영 전 단정 → Moveable 이벤트 호출을 `act`로 감싸고 결과 검증을 `waitFor`/`findByTestId`로 보강하여 해결
- 실패: 가장자리 스냅 미구현으로 208px(그리드) 유지 → 에지 스냅(LL/LR/RL/RR, TT/TB/BT/BB) 페어 계산 로직과 8px 임계 우선순위 적용으로 해결
#### 다음 단계
- 키보드 nudge(Shift/Alt 미세/가속)와 가이드 반영 TDD/구현
- 다중 선택 및 그룹 이동/정렬 스펙 초안 TDD
- 접근성: 키보드 조작 시 라이브 리전 안내/가이드라인 ARIA 노출 정책 검토
### 키보드 nudge 진행(2025-11-16)
- 정책: 기본 화살표 1px 이동 → 8px 그리드 스냅, Shift=1px 비스냅, Alt=16px 이동 후 스냅
- TDD: `components/wysiwyg/Canvas.nudge.test.tsx`에 Shift/Alt 케이스 추가
- 구현: `components/wysiwyg/Canvas.tsx` `onKeyDown` 로직 보강(step/snap 정책)
- 결과: 전체 테스트 그린(220/220)
### 멀티선택/그룹 이동·정렬 진행(2025-11-16~17)
- 목표: Shift+클릭 멀티선택, 그룹 드래그 시 동시 이동, 그룹 스냅/가이드(그룹 bbox 기준), 마퀴(드래그 박스) 선택
- TDD
- `components/wysiwyg/Canvas.multiselect.test.tsx`
- Shift+클릭으로 다중 선택 토글, 선택 아웃라인 표시, 그룹 드래그 시 동시 이동
- `components/wysiwyg/Canvas.multigroup.guides.test.tsx`
- 그룹 bbox 기준 중심/에지 스냅, 혼합 케이스(에지>중심), 다수 후보 중 최근접 우선 검증
- 그리드 스냅 근사치(±8px)로 인한 픽셀 오차는 상대 delta로 검증(안정성 강화)
- `components/wysiwyg/Canvas.marquee.test.tsx`
- 빈 캔버스 드래그로 마퀴(드래그 박스) 선택. 교차하는 객체 자동 선택. 빈 클릭 시 선택 해제
- 구현: `components/wysiwyg/Canvas.tsx`
- 상태: `selectedIds`(배열)로 전환, 마지막 선택을 Moveable `target`으로 사용
- Shift+클릭 토글 선택. 선택된 모든 객체에 아웃라인/핸들 표시
- 그룹 드래그: 마지막 선택에서 제안된 좌표 → 그룹 bbox로 변환해 스냅 후보(중심/에지) 계산 → 스냅된 그룹 delta를 모든 선택 객체에 적용
- 가이드: 그룹 컨텍스트에서 에지>중심>그리드 우선 표시(축별)
- 마퀴: 캔버스 `mousedown→mousemove→mouseup`으로 사각형 추적, 교차 판정으로 선택 세트 갱신. 빈 캔버스 클릭 시 선택 해제
- 테스트 환경 폴리필: `document.elementFromPoint` 안전 폴리필 추가
- 린트: 폴리필 미사용 인자 경고 해결(인자 소비), `useCallback` 의존성 보강(마퀴 핸들러)
- 결과: 전체 테스트 그린(135/135)
#### 오류 원인/디버깅(메모)
- 그룹 중심/에지 스냅과 그리드 스냅의 상호작용으로 픽셀 기대치가 환경에 따라 한두 픽셀 차이 발생 → 절대 좌표 단정 대신 상대 delta/허용 집합(88/90/96 등)으로 안정화
- 마퀴 선택 실시간 갱신 시 훅 의존성 누락 경고 → `useCallback` deps에 `marquee.active/x0/y0` 및 `model.layers` 반영
- jsdom 환경의 `elementFromPoint` 부재로 이벤트 경로 해석 이슈 → 폴리필로 무해화(`() => null`)
#### 다음 단계
- 멀티선택 혼합/최근접 보강 케이스 수평축/복합 시나리오 추가 점검
- 접근성 보강: 선택/스냅/가이드 상태에 대한 라이브 리전 안내 및 ARIA 속성 방안 설계
### WYSIWYG 빌더 Export/이미지/Props 동기화 진행(2025-11-17)
- 목표: /wysiwyg에서 실제 랜딩에 가까운 HTML/CSS Export와 로컬 이미지 업로드, 속성 패널과 Canvas의 일관된 동기화 확보
- TDD
- `lib/wysiwyg/export.test.ts`
- 버튼 오브젝트를 `label`로 내보내는지 검증
- 숨김 레이어(`visible: false`)가 HTML/CSS에 포함되지 않는지 검증
- `exportFrame` 결과 HTML이 ``, ``, ``를 포함하는 전체 문서 구조인지 검증
- `components/wysiwyg/WysiwygBuilder.acceptance.test.tsx`
- 로컬 이미지 업로드 시 Canvas에 ``가 렌더되는지 검증
- PropsPanel에서 Width/Height/Rotate 변경이 Canvas 스타일(width/height/transform)에 반영되는지 검증
- PropsPanel에서 Text/Label 변경이 Canvas 텍스트/버튼 콘텐츠에 반영되는지 검증
- 구현
- `lib/wysiwyg/export.ts`
- frame 내용을 `