diff --git a/.gitignore b/.gitignore index 97766e1..de28342 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,6 @@ blob-report/ # Plans (exclude from Git) 메인플랜.md 최초플랜.md +MAIN_PLAN.md /src/generated/prisma diff --git a/MAIN_PLAN.md b/MAIN_PLAN.md deleted file mode 100644 index 95ca2ff..0000000 --- a/MAIN_PLAN.md +++ /dev/null @@ -1,289 +0,0 @@ -# 메인 플랜 (Page Builder) - -## 현재 완료된 단계 -- **builder-1 ~ builder-8**: 기본 에디터, 텍스트/버튼/섹션 블록, DnD, Undo/Redo, 블록 삭제/복제 등 핵심 UX 구현 -- **builder-9-templates-advanced** (현재 브랜치): - - Hero / Features / CTA / FAQ / Pricing / Testimonials / Blog / Team / Footer 템플릿 섹션 - - 프리뷰 페이지 및 모바일 뷰포트 E2E - - CI 환경에서 Prisma Client generate + API 테스트용 DATABASE_URL 설정 - - API 테스트에서 Prisma를 메모리 기반 목으로 교체하여 DB 없이 테스트 통과 - -## 다음 단계 개요 -1. **builder-10-block-types** - - 더 다양한 블록 타입 추가 (예: 이미지, 아이콘이 있는 카드, 구분선, 리스트 등) - - Zustand store에 타입/props 정의 및 액션 추가 - - 에디터 UI에 새 블록 버튼 및 속성 패널 연동 - - 새 블록 타입에 대한 유닛 테스트 + E2E 테스트 추가 - -2. **builder-11-editor-ux-advanced** - - 드래그앤드롭 UX 고도화 (섹션/컬럼 이동 UX 개선, 드롭 힌트 강화) - - 키보드 숏컷 확장 및 포커스 관리 개선 - - 인라인 편집 UX 다듬기 - -3. **## builder-12-theme-output 상세 플랜 - - Tailwind 기반 디자인 시스템 정리 (색상/타이포/간격 스케일) - - Public/Preview 렌더링 품질 개선 (반응형, 여백, 타이포그래피) - - 샘플 테마 프리셋 추가 - -### 3) 텍스트 스타일 시스템 정리 (에디터 쪽 선행 작업) - -- 에디터 텍스트 블록 속성 구조와 렌더링 규칙 정리 - - `TextBlockProps` 에서 정렬/폰트 크기/줄 간격/자간/색상/최대 너비/장식 등 타이포 관련 속성 확장 - - `TextPropertiesPanel` 에서 위 속성들을 슬라이더/프리셋/컬러 피커로 제어하도록 구현 - - `SortableEditorBlock` 에서 텍스트 블록 렌더링 시 - - `align` → `pb-text-left/center/right` - - `fontSizeMode`/`fontSizeScale`/`fontSizeCustom` → `pb-text-*` 또는 `style.fontSize` - - `lineHeightMode`/`lineHeightScale`/`lineHeightCustom` → `pb-leading-*` 또는 `style.lineHeight` - - `letterSpacingCustom` → `style.letterSpacing` - - `colorPalette`/`colorCustom` → `pb-text-color-*` 또는 `style.color` - - `maxWidthMode`/`maxWidthScale`/`maxWidthCustom` → `pb-text-maxw-*` 또는 `style.maxWidth` - - `underline`/`strike`/`italic` → `pb-underline`/`pb-line-through`/`pb-italic` - - 텍스트 편집 모드가 아닐 때, 실제 텍스트 요소(`
`)에도 위 클래스와 `textStyleOverrides` 를 직접 적용해 시각적 일관성 확보 -- 테스트 정렬 - - `tests/e2e/editor.spec.ts` 에서 텍스트/구분선/리스트 블록 관련 정렬/크기 기대값을 Tailwind `text-*` 가 아니라 `pb-text-*` 유틸리티 네이밍에 맞게 조정 - - JSON 내보내기/불러오기 시나리오에서도 `pb-text-left/right/sm/lg` 클래스가 복원되는지 검증 - -4. **builder-13-forms** - - 폼 전송 구조 설계 및 구현 (internal API + Webhook/Google Sheets) - - 폼 전송 설정(헤더/토큰/추가 파라미터) 및 다중 폼 컨트롤러 구조 설계 - - 폼 요소 블록(input/select/radio/checkbox) 추가 및 자유 레이아웃 배치 지원 - ---- - -## builder-10-block-types 상세 플랜 - -### 1) 대상 블록 타입 정의 -- **이미지 블록(image)** - - props: `src`, `alt`, `align`, `width`, `borderRadius` 등 -- **구분선(divider)** - - props: `style` (solid/dashed), `thickness`, `color`, `marginY` -- **리스트(list)** - - props: `items` (문자열 배열), `ordered` (true/false), `align` -- (필요시) **카드(card)** - - props: `title`, `description`, `imageSrc`, `align` - -※ 실제 구현 범위는 TDD를 진행하면서 우선순위 높은 것부터 차례대로 확장한다. - -### 2) TDD 순서 -1. **유닛 테스트부터 작성 (실패 상태로 시작)** - - `tests/unit/editorStore.spec.ts` - - `addImageBlock`, `addDividerBlock`, `addListBlock` 등 새 액션 테스트 추가 - - 블록이 올바른 기본 props 와 함께 `blocks` 배열에 추가되는지 확인 - - 섹션/컬럼 내부에 배치되는 경우 위치 정보(`sectionId`, `columnId`) 테스트 -2. **스토어 구현 (editorStore.ts)** - - `Block` 타입에 새 `type` 과 `props` 구조 추가 - - `EditorState`에 새 액션 시그니처 추가 - - `createEditorState` 내부에 실제 로직 구현 (createId 활용, 기존 텍스트/버튼/섹션 패턴 재사용) -3. **에디터 UI 연동 (editor/page.tsx)** - - 사이드바에 새 블록 버튼 추가 (이미지, 구분선, 리스트 등) - - 캔버스 렌더링 분기에 새 블록 타입별 렌더링 로직 추가 - - 속성 패널에서 최소한의 필수 props 편집 가능하도록 구현 -4. **E2E 테스트 추가 (Playwright)** - - `tests/e2e/editor.spec.ts` - - 새 블록 타입 버튼 클릭 → 캔버스에 올바르게 렌더링되는지 확인 - - 속성 패널에서 값 수정 시 캔버스 반영 확인 (예: 이미지 src, 리스트 아이템 수정) -5. **리팩터링 & 안정화** - - 중복되는 렌더링/props 처리 로직 정리 - - 필요시 추가 유닛 테스트/E2E로 회귀 방지 - ---- - -## builder-11-editor-ux-advanced 상세 플랜 - -### 1) DnD UX 고도화 -- **목표** - - 블록/섹션 드래그앤드롭이 시각적으로 명확하고, 의도한 위치로 안정적으로 이동하도록 개선 - - 2컬럼 섹션 컬럼 이동, 블록 순서 변경 관련 E2E를 CI에서도 신뢰할 수 있게 만들기 - -- **작업 항목 (TDD)** - 1. `tests/e2e/editor.spec.ts` - - `"블록 드래그앤드롭으로 순서를 변경할 수 있어야 한다"` 시나리오를 현재 UX에 맞게 단순/안정화 - - `"2컬럼 섹션에서 블록을 다른 컬럼 영역으로 드래그하면 컬럼이 변경되어야 한다"` 시나리오를 실제 드롭 영역 구조에 맞춰 조정 - 2. `EditorPage`/`SortableEditorBlock` - - 드롭 대상 컬럼/섹션의 hit-area를 넓히거나, 불필요한 overlay가 pointer events를 가로채지 않게 레이아웃 조정 - - DnD Kit 설정(sensors, collision detection 등) 필요 시 보정 - 3. 필요 시 `editorStore` 유닛 테스트에서 - - `reorderBlocks`, `moveBlock` 조합 케이스(섹션/컬럼 간 이동)를 명시적으로 테스트해 회귀 방지 - -### 2) 키보드/포커스 UX 개선 -- **목표** - - ArrowUp/ArrowDown 으로 선택 블록이 예측 가능하게 이동 - - Cmd/Ctrl+Z / Cmd/Ctrl+Shift+Z / Cmd/Ctrl+D 와 같은 단축키가 OS에 관계없이 일관되게 동작 - -- **작업 항목 (TDD)** - 1. `tests/e2e/editor.spec.ts` - - `"ArrowUp/ArrowDown 키로 선택된 블록을 위/아래로 이동하며 순차적으로 선택할 수 있어야 한다"` 테스트를 CI에서도 통과하도록 재정의 - - 초기 선택 상태, 포커스 위치, 기대되는 `data-selected`/`aria-selected` 값을 명확히 기술 - 2. `EditorPage`의 전역 keydown 핸들러 - - ArrowUp/Down 선택 이동 로직을 단순/명확하게 정리 (현재 선택이 없을 때 첫 블록 선택 등) - - OS별 Cmd/Ctrl 판별 로직(`navigator.platform`)과 E2E에서 보내는 키 조합을 일치시켜 유지 - -### 3) 인라인 편집 UX 정리 -- **목표** - - 텍스트 블록 더블클릭 → 인라인 편집 진입 - - Enter → 커밋, Esc → 취소, 포커스 아웃 시 안전하게 커밋 - -- **작업 항목 (TDD)** - 1. `tests/e2e/editor.spec.ts` - - `"텍스트 블록을 더블클릭해서 내용을 수정할 수 있어야 한다"` 시나리오를 - - 더블클릭 → 편집 모드 진입 - - Enter 커밋 / Esc 취소 흐름까지 포함하도록 보완 - 2. `EditorPage`의 인라인 편집 상태(`editingBlockId`, `editingText`) - - 더블클릭/포커스 아웃/Enter/Esc 이벤트에 대한 상태 전이와 `updateBlock` 호출 타이밍을 명확히 정의 - -이 모든 작업도 `builder-10`과 동일하게 **실패하는 테스트 추가 → 최소 구현 → 리팩터링 → CI/E2E 확인** 순서로 진행한다. - ---- - -## builder-13-forms 상세 플랜 - -### 1) 폼 전송 구조 (v1: 단일 폼 + 필드 배열) -- **목표** - - SPA 스타일로 페이지 리로드 없이 폼을 전송하고, CORS 문제 없이 외부 API/Google Sheets 로 연동 - -- **현재 구현 상태 요약** - - `FormBlockProps` 에 전송 설정 추가 - - `submitTarget: "internal" | "webhook"` - - `destinationUrl`, `method`, `headers`, `extraParams` - - `successMessage`, `errorMessage` - - 퍼블릭 렌더러: 폼 블록을 `
` 으로 렌더링하고 `/api/forms/submit` 으로 `FormData` 전송 - - `/api/forms/submit` 라우트: - - `__config` hidden 필드로 넘어온 `FormBlockProps` 를 파싱 - - `submitTarget === "internal"` 일 때 서버 내부 처리 (향후 DB 저장/메일 발송용) - - `submitTarget === "webhook"` 일 때 `destinationUrl` 로 서버사이드 POST (x-www-form-urlencoded) - - `headers`/`extraParams` 를 함께 전송하여 토큰/고정 파라미터 전달 - - 폼 필드 정의: - - `FormFieldConfig` (`id`, `name`, `label`, `type`, `required`) - - `FormBlockProps.fields?: FormFieldConfig[]` 로 필드 배열 관리 - - 에디터 우측 패널에서 필드 추가/삭제/순서/라벨/name/type/필수 여부 편집 UI 제공 - -- **TDD 순서 (v1)** - 1. `tests/unit/editorStore.spec.ts` - - `FormBlockProps`/`FormFieldConfig` 초기값, `addFormBlock` 동작 테스트 - 2. `editorStore.ts` - - `FormFieldConfig`, `FormBlockProps` 정의 및 `addFormBlock` 구현 - 3. `PublicPageRenderer.tsx` - - `fields` 기반 폼 렌더링 및 `/api/forms/submit` 호출 로직 구현 - 4. `src/app/api/forms/submit/route.ts` - - internal/webhook 분기 및 Webhook/Google Sheets 전송 로직 구현 - 5. `tests/e2e/editor.spec.ts`/`tests/e2e/preview.spec.ts` - - 폼 추가 → 필드 수정 → 프리뷰에서 폼 전송 → 성공/에러 메시지 확인 시나리오 추가 - -### 2) 폼 요소 블록 + 폼 컨트롤러 구조 (v2: 자유 레이아웃) -- **목표** - - 폼 요소(input/select/radio/checkbox)를 텍스트 블록처럼 섹션/컬럼 안에 자유롭게 배치 - - 폼 블록은 전송 설정 + 어떤 요소(id)들을 묶어서 어디로 보낼지 제어하는 컨트롤러 역할만 수행 - - 페이지 내에 여러 폼 블록이 있어도 각기 다른 필드/submit 버튼/전송 설정을 사용 가능 - -- **설계 개요** - - 새 블록 타입 추가: - - `formInput`, `formSelect`, `formRadio`, `formCheckbox` 등 - - 각 props 예시: - - `FormInputBlockProps`: `label`, `placeholder`, `formFieldName?`, `formFieldType`, `required?` - - `FormSelectBlockProps`: `label`, `formFieldName?`, `options[]`, `required?` - - `FormRadioBlockProps`: `groupLabel`, `formFieldName?`, `options[]`, `required?` - - `FormCheckboxBlockProps`: `label`, `formFieldName?`, `required?` - - `FormBlockProps` 확장: - - `fieldIds: string[]` (이 폼이 담당하는 폼 요소 블록들의 id) - - `submitButtonId?: string` (이 버튼이 눌리면 이 폼을 전송) - - 좌측 사이드바에 "폼 요소" 섹션 추가: - - 텍스트 입력/셀렉트/라디오 그룹/체크박스 블록 추가 버튼 - - `editorStore` 에 `addFormInputBlock`/`addFormSelectBlock` 등 액션 추가 - - 런타임 동작: - - 버튼 클릭 → 해당 버튼 id 를 `submitButtonId` 로 가진 FormBlock 검색 - - FormBlock.fieldIds 에 포함된 블록들 중 `formFieldName` 이 설정된 요소만 수집 - - 현재 값(value)을 모아 `FormData` 또는 JSON 으로 `/api/forms/submit` 에 전송 - -- **TDD 순서 (v2)** - 1. `tests/unit/editorStore.spec.ts` - - 새 폼 요소 블록 타입 추가 액션(addFormInputBlock 등) 테스트 - - FormBlock 의 `fieldIds`/`submitButtonId` 기본값 및 업데이트 동작 테스트 - 2. `editorStore.ts` - - `BlockType` 에 폼 요소 타입 추가, 각 props/interface 정의 - - `addFormInputBlock` 등 구현 (섹션/컬럼 내 자유 배치) - 3. `src/app/editor/forms/elements/*.tsx` - - `InputElement`/`SelectElement`/`RadioGroupElement`/`CheckboxElement` 컴포넌트 구현 및 에디터/퍼블릭 렌더링 연계 - 4. `src/app/editor/page.tsx` - - 좌측 사이드바에 "폼 요소" 섹션 추가 및 액션 연결 - - 블록 렌더링 분기에 새 폼 요소 블록 타입 처리 추가 - - 우측 폼 패널에 FormBlock 의 `fieldIds`/`submitButtonId` 매핑 UI 추가 - 5. `tests/e2e/editor.spec.ts`/`tests/e2e/preview.spec.ts` - - 섹션/컬럼 안에 폼 요소들을 자유 배치 → FormBlock 에 id 매핑 → 버튼 클릭 시 해당 요소 값만 전송되는지 검증 - -폼 기능 역시 다른 단계와 동일하게 **실패하는 테스트 → 최소 구현 → 리팩터링** 순서를 유지하며, Webhook/Google Sheets 연동 시 CORS 문제를 피하기 위해 항상 `/api/forms/submit` 경유 구조를 사용한다. - -### 3) 진행 현황 (2025-11-20) -- **v2 TDD 1단계(editorStore)** - - `tests/unit/editorStore.spec.ts` - - `formInput` 블록 추가 액션(`addFormInputBlock`)에 대한 유닛 테스트 작성 - - FormBlock 컨트롤러 기본 속성(`fieldIds`, `submitButtonId`) 초기값 유닛 테스트 작성 - - `src/features/editor/state/editorStore.ts` - - `BlockType`에 `"formInput"` 타입 추가 - - `FormInputBlockProps` 정의 및 공통 `Block.props` 유니온에 포함 - - `FormBlockProps`에 `fieldIds?: string[]`, `submitButtonId?: string | null` 추가 - - `addFormBlock`에서 `fieldIds: []`, `submitButtonId: null` 기본값 설정 - - `addFormInputBlock` 액션 구현 (루트에 기본 label/formFieldName 으로 추가, 선택 상태 업데이트) - - 결과: `editorStore.spec.ts` 전체 25개 테스트 모두 통과 - -- **v2 TDD 2단계(editorStore + Editor UI)** - - `tests/unit/editorStore.spec.ts` - - `formSelect`/`formRadio`/`formCheckbox` 블록 추가 액션 및 FormBlock `fieldIds`/`submitButtonId` 업데이트 테스트 추가 - - `src/features/editor/state/editorStore.ts` - - `BlockType`에 `"formSelect"`, `"formRadio"`, `"formCheckbox"` 추가 - - `FormSelectBlockProps`/`FormRadioBlockProps`/`FormCheckboxBlockProps` 정의 및 `Block.props`/`updateBlock`에 반영 - - `addFormSelectBlock`/`addFormRadioBlock`/`addFormCheckboxBlock` 구현 (루트에 기본 label/options 로 추가) - - `src/app/editor/page.tsx` - - 좌측 사이드바에 "폼 요소" 섹션 추가 - - 우측 속성 패널에서 FormBlock 선택 시 "폼 컨트롤러" 섹션 추가 - - 체크박스/셀렉트 변경 시 `updateBlock` 으로 FormBlock 의 `fieldIds`/`submitButtonId` 업데이트 - - `tests/e2e/editor.spec.ts` - - "폼 요소 사이드바에서 폼 입력/셀렉트/라디오/체크박스 블록을 추가할 수 있어야 한다" 시나리오 추가 - - "폼 블록을 선택하면 우측 속성 패널에서 폼 필드/버튼 매핑 UI가 보여야 한다" 시나리오 추가 - - 결과: 관련 유닛/E2E 테스트 모두 통과 - -- **v2 TDD 3단계(Preview/Public 렌더러 폼 제출 v2)** - - `tests/e2e/preview.spec.ts` - - "폼 컨트롤러에 매핑한 폼 요소와 버튼이 프리뷰에서 함께 동작해야 한다" 시나리오 추가 - - 에디터에서 `formInput`/`button`/`form` 블록을 추가 - - FormBlock 의 `fieldIds`/`submitButtonId` 매핑 설정 후 `/preview` 이동 - - 프리뷰에서 입력값을 채우고 "버튼" 클릭 시 성공 메시지("성공적으로 전송되었습니다.")가 표시되는지 검증 - - `src/features/editor/components/PublicPageRenderer.tsx` - - FormBlock 렌더링 시 v2 컨트롤러 우선 사용 - - `fieldIds` 가 설정된 경우 해당 id 를 가진 `formInput`/`formSelect`/`formCheckbox`/`formRadio` 블록들만 수집 - - 각 폼 요소 블록의 `formFieldName`/`label`/`options` 를 사용해 ``/`