27 KiB
메인플랜: 랜딩 페이지 빌더
목표
- 비개발자도 사용 가능한 단일 페이지 랜딩 페이지 빌더.
- 결과물: 정적 ZIP(HTML/CSS/JS) 다운로드. 이미지: 외부 URL 또는 로컬 업로드(서버 저장 없음, ZIP에 패키징). 폰트는 외부 CDN 허용.
- 폼: 다양한 필드 타입(파일 업로드 제외), 커스텀 name, action URL 설정. action 미지정 시 Google Sheets 저장.
- 다국어 지원(i18n), 접근성 준수, SEO/OG 메타, 분석 스니펫 삽입.
확정 요구사항
- 단일 페이지 기준.
- 이미지: 외부 URL 또는 로컬 업로드 허용(서버 저장 없음, ZIP 패키징). CDN 허용(예: Google Fonts 등).
- 폼 필드 타입: 텍스트/이메일/전화/셀렉트/라디오/체크박스/텍스트에어리어/날짜 등(파일 업로드는 추후).
- 검증 규칙 필요. 스팸 방지: 기본(honeypot, 제출지연 타임스탬프).
- 제출 경로: 1) 사용자가 action URL 지정 → 2) 미지정 시 Google Sheets.
- 기본: Google Apps Script 웹앱 템플릿 가이드 제공(사용자 배포 URL 사용)。
- 대안: 서비스계정 + Google Sheets API(선택).
- 다국어 지원 필요. 접근성 준수. SEO/OG 포함. 분석 스니펫 삽입 기능 필요.
- 스택: React 기반. Next.js(App Router) + TS + Tailwind + shadcn/ui 권장.
아키텍처 개요
- 빌더 앱: Next.js(App Router, TS), Tailwind, shadcn/ui, dnd-kit, Zustand.
- 도메인 스키마: Page → Section[] → Block[] → Form(fields, validation, actionUrl) → Theme.
- 에셋 매니저: 사용자가 업로드한 이미지를 메모리/브라우저에서 관리, 파일명 해싱 후
assets/경로로 참조 재작성. - HTML Exporter: JSON 스키마 → 정적 HTML/CSS/JS 생성. 로컬 업로드 이미지는
assets/로 참조, 외부 URL은 그대로 유지. - 제출 경로: action 사용 또는 Google Sheets(기본 Apps Script 템플릿 제공).
- i18n: next-intl. a11y: Testing Library + axe-core 스모크. SEO/OG 메타 생성.
- 내보내기: ZIP(브라우저 JSZip 우선). 필요 시 서버(API Route) 대안 제공. 로컬 업로드 이미지는 ZIP 내
assets/폴더에 파일로 포함.
UI/UX 리디자인 계획(합의안)
- 정보 구조(3패널)
- 좌: 컴포넌트 패널(Hero/FAQ/CTA + Form 프리셋)
- 중: 캔버스(dnd-kit, 섹션/필드 배치·정렬·삭제)
- 우: 인스펙터(선택 요소 속성/검증/스타일 편집)
- 상단 툴바: 언어, 테마, 프리셋, Export ZIP
- 프리뷰: 실시간 렌더, 반응형 뷰포트 토글
- 계약 고정: 스키마(섹션/폼/검증), 상태 인터페이스, Exporter 입·출력
단계별 TDD 진행
- 레이아웃 스캐폴딩(shadcn/ui) + 접근성 라벨링 테스트
- dnd 캔버스 1차: 섹션 드래그 정렬/삭제 테스트 → 최소 구현
- 인스펙터 1차: 선택 섹션/필드 기본 속성 편집 테스트 → 연동
- 컴포넌트 패널: 카드 추가(섹션/필드 프리셋) 테스트 → 드래그 추가
- 프리뷰 강화: 실시간 반영/뷰포트 토글 스모크 테스트
현재 상태(MVP)
- 섹션 추가/삭제, 폼 필드 구성(옵션/필수/패턴/최대길이), i18n, ZIP Export, 로컬 에셋 포함 동작 확인
- Next.js 16:
middleware→proxy마이그레이션 완료 - dnd 상호작용 테스트 및 구현 안정화(정렬/삭제 버튼 폴백 포함)
- 섹션 프리셋 확장: Features/Testimonials 카드 추가 및 프리뷰/Exporter 스텁 반영 완료
- SEO/Analytics/OG 인스펙터 추가 및 프리뷰 반영
- Exporter: og:image 메타 태그 반영 및 테스트 추가
- a11y: jest-axe 스모크 테스트 통과(FormBuilderPanel), dnd-kit 라이브 리전/안내 존재 검증
- 라벨-id 매핑 강화(중앙 입력/인스펙터/FAQ/CTA/Features/Testimonials)
- 프리뷰 뷰포트 토글(Desktop/Mobile) + localStorage 영속화
- Features/Testimonials 인스펙터 편집 UI(리스트 추가/삭제/편집) + 테스트
- Exporter 타입 보강(any 제거) 및 CSS 보강(Features/Testimonials)
- E2E(Playwright):
- 기본 플로우(섹션 추가→편집→프리뷰→Export 클릭)
- dnd 키보드 폴백(Up/Down) 재정렬
- SEO/Analytics/OG 프리뷰 반영 검증
- Export ZIP 메타(SEO/OG/Analytics) 검증(테스트 훅 사용)
- 프리뷰 뷰포트 토글(aria-pressed/키보드 접근) 검증
a11y/반응형 정책(Exporter)
- 문서 내비게이션: skip link,
<main id="main" role="main">적용 - 섹션 식별:
aria-labelledby+ 섹션 헤딩 id(features/testimonials/faq) - 이미지 대체텍스트: hero img
alt= heading 파생 - 포커스 표시: 포커스 가능한 요소 outline 표시
- 반응형: 모바일 컨테이너 패딩 축소, grid 자동 칼럼,
prefers-reduced-motion준수
최근 보강 내역(접근성/테스트)
- 인스펙터 입력에
aria-describedby연결(SEO/Analytics/OG/Hero/CTA) - SEO/Analytics/OG/Hero/CTA/FAQ 헬프텍스트 추가
- Exporter 구조 스냅샷 강화:
<main>내부 섹션/폼 포함 검증 - Exporter CSS 토큰 스냅샷: skip-link/focus outline/반응형/모션 감소 토큰 존재 검증
- Features/Testimonials
aria-labelledby/헤딩 id 일치 검증 추가 - Exporter 헤딩 레벨 일관성 전수 테스트(hero=h1, others=h2, FAQ=h3) 추가
- Exporter 색상/타이포그래피 토큰 스모크 테스트 추가
- Exporter(Form): helptext ↔ aria-describedby 연결 TDD 완료
- 각 필드
id=field-<name>와<label for>연결 help가 있을 경우id=field-<name>-help요소 생성 및 컨트롤에aria-describedby부여- 라디오 그룹은
fieldset에aria-describedby적용, 옵션별id/htmlFor연결
- 각 필드
- next-intl 기본 timeZone 설정:
Asia/Seoul로 ENVIRONMENT_FALLBACK 경고 제거 - a11y 색/폰트 최소 기준 스모크(본문 컬러/폰트 패밀리/모바일 h1 폰트 크기) 추가
- CTA 섹션에
aria-labelledby+<h2 id="cta-heading">추가(헤딩 레벨 일관성) - 전체 테스트 그린(유닛/컴포넌트/E2E)
- Inspector a11y 스모크: SEO/Analytics/OG 입력과 헬프텍스트의
aria-describedby연결 검증 - Exporter CSS 스모크: 본문 텍스트 컬러/포커스 outline/모바일 h1 폰트 크기/MQ 토큰 존재 검증
- FAQ 인스펙터 Q/A 입력
aria-describedby(ins-faq-help) 연결 보강 - Inspector 키보드 UX 스모크: Features/Testimonials 항목 추가( Space/Enter ) 검증 추가
- Inspector 키보드 UX 스모크: Features/Testimonials 항목 삭제(Remove) Enter/Space 검증 추가
- 인스펙터 Remove 버튼 키보드 처리(onKeyDown/onKeyUp) 반영
- Exporter 회귀 스냅샷: heading/ARIA/반응형 토큰 통합 검증 추가
- Preview 토글 ARIA 회귀 스냅샷(Desktop/Mobile: aria-pressed/controls/describedby)
- Exporter CSS 토큰 회귀 확대(:root --primary, body font, skip-link base/focus, focus outline, reduced-motion)
- 컴포넌트 i18n 회귀 스냅샷(en/ko 주요 라벨/버튼 키 존재)
- 섹션 a11y 자동 검증 템플릿(testUtils/sectionA11y) 도입 및 템플릿 테스트 추가
- 이미지 최적화 옵션 1차(TDD): hero 이미지에
fetchpriority/srcset/sizes조건부 렌더링 - 테스트:
lib/exporter/image.responsive.options.test.ts(속성 존재 검증) - 구현:
lib/exporter/html.ts(ExportOptions에 옵션 추가 및<img>렌더 반영) - 브랜치:
feat/image-optimization(로컬 커밋, 푸쉬 보류) - 이미지 최적화 옵션 2차(TDD):
srcset만 제공 시 기본sizes자동 적용(imageAutoSizes) - 기본값:
(max-width: 640px) 100vw, 600px - 테스트:
image.responsive.options.test.ts(auto on/off 케이스) - 구현:
ExportOptions.imageAutoSizes추가 및renderHero적용 - 신규 섹션 추가: Benefits
- 스키마 확장(
benefits): props.items:string[] - Exporter 렌더 추가:
<section class="benefits" aria-labelledby>,<h2 id="benefits-heading">, 리스트 렌더 - 테스트: sections.benefits.test.ts (h2/aria-labelledby/리스트 아이템 검증)
- 섹션 회귀 스냅샷(benefits 포함) 보강: sections.regression.benefits.test.ts 추가(ARIA/헤딩/리스트 구조 전수 검증)
- 섹션 회귀 스냅샷(CTA/Testimonials): sections.regression.cta-testimonials.test.ts 추가(figure/blockquote/figcaption, CTA 링크 클래스/URL 검증)
- 섹션 회귀 스냅샷(Features/FAQ): sections.regression.features-faq.test.ts 추가(리스트/클래스/아이템 수 검증)
- Exporter 이스케이프 회귀: sections.escape.regression.test.ts 추가(특수문자 <, >, &, ") 안전 이스케이프 검증)
- 인스펙터 a11y 자동 체크 헬퍼 도입: components/testUtils/inspectorA11y.ts + 템플릿 테스트 2종(InspectorA11y.template/sections)
- i18n 회귀 확장: Sidebar Add 버튼군(en/ko) 라벨 스모크(i18n.sidebar.actions.test.tsx)
- E2E 스모크: 프리뷰/Export 마커(heading/ARIA/반응형 CSS) 존재 검증 추가
- SEO description 기본값 설정으로 Export 스키마 안정화
- 프리뷰 토글 버튼에
aria-controls="preview-container"추가로 제어 영역 명시 - 프리뷰 토글에 sr-only 설명 추가 및 두 토글 버튼
aria-describedby연결 - SectionCanvas 제어 버튼
aria-label스모크(Select/Up/Down/Remove) 추가 - CSS focus-visible 토큰 스모크(3px solid + HEX 컬러) 추가
- 인스펙터 Add 버튼 title 보조 힌트 추가(Features/Testimonials/FAQ) 및 일관성 테스트
- SectionCanvas 버튼 title/aria-label 일관성 스모크
- 전역 버튼 네이밍 일관성 보강: Inspector Add/Remove 및 SectionCanvas Select/Up/Down/Remove의 title/aria-label 일치 확인 및 스모크 완료
- a11y 커버리지 최종 점검: 전 섹션 인스펙터 입력 aria-describedby 연결 확인(체크리스트 충족)
다음 작업(계획)
- Exporter 헤딩 레벨 일관성 테스트: hero는 h1, 나머지 섹션은 h2, FAQ 항목은 h3 검증
- a11y 색/폰트 최소 기준 스모크: 대비/폰트크기 관련 CSS 토큰/규칙 존재 여부 스냅샷
Inspector a11y 커버리지 체크리스트
- Hero:
Hero Heading입력aria-describedby→ins-hero-heading-help - CTA:
CTA Text/CTA Href입력aria-describedby→ins-cta-text-help/ins-cta-href-help - FAQ:
FAQ Q n/FAQ A n입력aria-describedby→ins-faq-help - Features:
Feature n입력aria-describedby→ins-features-help - Testimonials:
Author n/Quote n입력aria-describedby→ins-testimonials-help - Preview 토글: sr-only 설명(
preview-toggle-help)과 버튼aria-describedby연결,aria-controls="preview-container"
CI/테스트 실행
- 유닛/컴포넌트:
npm test(Vitest) - E2E:
npm run test:e2e(Playwright) - 로컬 개발 서버는 Playwright 설정에서 자동 기동(webServer:
npm run dev) - GitHub Actions 워크플로:
.github/workflows/ci.yml- Node 20 +
npm ci - Vitest 실행 → Playwright 브라우저 설치(
npx playwright install --with-deps) → E2E 실행
- Node 20 +
CI 최종 점검
- main/master 브랜치 푸시/PR 시 워크플로 자동 실행 확인
- 새로 추가된 테스트(헤딩 레벨 전수, 색/폰트 스모크) CI 통합 확인
- 작업 브랜치 푸시 완료:
chore/ci-verify-a11y-headings(원격: origin) - PR 생성 및 Actions 결과 기록 예정(base: main)
- CI verification note: 러너 온라인 이후, 사소 변경으로 CI 트리거 검증 예정
- CI rerun: 워크플로 재실행용 ping 커밋
PR/CI 결과(2025-11-14)
- PR: feat/ci-verify-run → main 머지 완료
- CI: Vitest + Playwright 전부 그린
- E2E 추가: Exported page skip-link focus 표시 회귀 테스트 포함
- 워크플로: self-hosted runner [macos, host], Playwright 보고서 업로드 아티팩트 활성화
- 제안: 다음 릴리스 태그(v0.2.0)로 회귀 커버리지/Benefits 섹션 추가 반영
결과: CI 통과 (self-hosted Gitea Actions)
- PR: #2
- 러너: self-hosted, labels: [macos, host]
- 테스트: Unit/Component + E2E 총 7/7 green
- 수정 요약(E2E 포커스):
- 초기 포커스 선점 방지:
body클릭으로 포커스 부여 - 스킵링크 등 선행 포커서블 회피: Desktop 버튼에 명시적
focus() - 키보드 시나리오: Tab → Mobile 포커스 확인 → Space로 토글 → Shift+Tab → Enter
- 초기 포커스 선점 방지:
추가 CI 결과(2025-11-14)
- E2E a11y 스모크 추가: 포커스 아웃라인(3px solid, 브랜드 컬러), 스킵링크 탭 순서(main 포커스 이동)
- Exporter JS에 스킵링크 클릭 시
#main에tabindex="-1"부여 후focus()처리 → CI 그린 통과
머지 상태
- 머지 방식: Squash and merge
- 브랜치 정리:
feat/ci-verify-run삭제 완료
다음 스프린트 후보 과제
- Exporter 회귀 스냅샷 보강(폼 토큰/검증 토큰 범위 확대)
- 인스펙터 신규 섹션 추가 시 a11y 베이스라인 재사용 가이드 마련
- 버튼 네이밍 일관성 전수 스캔 스모크 범위 확대
- node 20 + npm ci
- Vitest 실행 → Playwright 브라우저 설치 → E2E 실행
마일스톤
- 스캐폴딩 및 테스트 환경 구성
- 스키마 정의(Zod) 및 HTML Exporter TDD
- 컴포넌트/상태관리(Zustand) 및 인스펙터 기본 기능
- 접근성/키보드 네비게이션 강화 및 테스트 안정화
- CI/CD(Self-hosted Gitea) 연동 및 그린 검증
릴리스
v0.1.0
- CI 검증 완료(Self-hosted Gitea runner, labels: [macos, host])
- E2E 안정화: 초기 포커스 이슈 제거(Desktop 명시 focus), 스킵링크 고려
- Exporter(Form) a11y: helptext ↔ aria-describedby, id/htmlFor 연결, 라디오 fieldset 설명 연결
- i18n: en/ko 중첩 구조, 누락 키 보강(builder.action., builder.form.)
- next-intl: 기본 timeZone=Asia/Seoul 설정으로 ENVIRONMENT_FALLBACK 경고 제거
- 태그: v0.1.0
접근성 베이스라인 가이드(신규 섹션/인스펙터 적용 규칙)
- 레이블 연결: 모든 입력은
id=ins-...또는id=field-...부여,<label for>로 연결 - 헬프텍스트 연결: 헬프 요소
id=...-help생성, 입력에aria-describedby로 연결 - 라디오 그룹: 개별
<input>대신<fieldset>에aria-describedby적용, 옵션은id/htmlFor로 연결 - 버튼 네이밍: 보이는 텍스트 또는
aria-label로 안정적 접근 가능한 이름 제공 - 키보드 활성화: Enter/Space 활성(onClick와 동일 동작),
focus-visible스타일 유지 - 스킵링크:
<a class="skip-link">제공, 포커스 시 화면 내 표시 - 헤딩 레벨: hero=h1, 1뎁스 섹션=h2, FAQ 항목=h3 일관성 유지
- 미리보기 토글: 토글 버튼에
aria-pressed,aria-controls,aria-describedby연결 - i18n 키: 중첩 객체 구조, 신규 키 추가 시 en/ko 동시 보강
- 테스트 원칙: TDD로 유닛/컴포넌트/E2E 및 스냅샷 추가. 포커스/ARIA/토큰 스모크 포함
적용 가이드(신규 섹션 추가 시)
- 1) 섹션 구현:
<section class="<name>" aria-labelledby="<name>-heading">와<h2 id="<name>-heading">구성 - 2) 인스펙터 입력:
id=ins-...,<label for>, 헬프텍스트...-help+aria-describedby연결 - 3) Exporter 반영: 섹션 렌더 함수에 위 구조/속성 부여
- 4) 테스트 추가:
- 공통 템플릿 사용:
lib/exporter/testUtils/sectionA11y.ts→assertSectionA11y(html) - 샘플:
lib/exporter/sectionA11y.template.test.ts참고하여 새 섹션 추가해 검증 - 필요 시 개별 스냅샷/스모크: 헤딩/ARIA/키보드/토큰 추가 검증
- 공통 템플릿 사용:
다음 단계(E2E 초안)
- 시나리오: 섹션 추가(Hero/FAQ/Features/Testimonials/CTA) → 인스펙터 편집 → Export ZIP 생성 → HTML 메타/섹션/폼 렌더 확인
- 환경: Playwright
- 체크리스트:
- 섹션 추가/정렬/삭제 동작
- 인스펙터 편집 반영(미리보기/Export HTML)
- i18n 라벨 안정성(data-testid 사용)
- a11y 스모크 무결성 유지
TDD 전략
- 각 기능 단위로: 실패 테스트 작성 → 최소 구현 → 리팩터 → 커버리지 확인.
- 단위: Vitest + Testing Library + jsdom. E2E: Playwright.
- 접근성: axe-core 스모크 테스트.
작업 보드(요약)
- 스키마/도메인(Zod)
- HTML Exporter
- 에셋 매니저
- 폼 엔진
- Sheets 연동
- 빌더 UI
- 내보내기 ZIP
- i18n
- a11y/SEO/OG/Analytics
오류/버그 기록
-
[2025-11-13 22:55] BuilderClientPage 렌더 중 ref 접근 경고 / useRef.current를 렌더 중 참조하여 ESLint 경고 발생 / FormBuilderPanel 통합 시
useRef(createFormStore())사용 /useMemo(() => createFormStore(), [])로 스토어 인스턴스 고정하고useSyncExternalStore에 직접 주입하여 해결 / 렌더 중 ref 접근 금지 원칙 재확인 -
[2025-11-14 09:35] Vitest 로그에서 i18n 누락 메시지 경고 확인(en:
builder.action.addSelect/addRadio,builder.form.actionUrl/honeypot/minSeconds) →messages/en.json에 키 존재 여부 점검 및 누락 보완 후 재실행, 모든 테스트 그린 유지 -
[2025-11-14 09:45] 프리뷰 텍스트 매칭 불안정으로 테스트 실패 →
data-testid기반 안정화(preview-seo-title등) 및 텍스트 매칭 지점Title:→Locale:로 조정, 전면 그린 -
[2025-11-14 20:30] E2E 실패: 스킵링크 활성화 후 main 포커스 미이동 → Exporter JS에 스킵링크 클릭 핸들러 추가(
tabindex=-1+focus()) → 로컬/CI 모두 그린 -
[2025-11-14 23:45] Gitea 라벨 중복 생성/페이로드 오류 → 워크플로 수정
- auto-label.yml/auto-pr.yml에서 라벨 생성 로직 제거(기존 레이블만 사용)
- 라벨 지정 시 JSON 페이로드를
{"labels":[id]}형태로 고정 - 다중 동일 라벨 존재 시 가장 작은 ID를 선택하도록 정렬 적용
- ci.yml 자동 머지 가드: PR 라벨 조회 후
automerge존재시에만 머지
CI 파이프라인 자동화 결과(automerge)
- 브랜치 푸시 → auto-pr로 PR 생성 → auto-label로
automerge부여(기존 레이블만) → ci.yml에서 테스트 그린 시 자동 머지 - 안전장치:
automerge라벨 없으면 머지 스킵, PR 닫힘/드래프트 시 머지 스킵 - 진단 로그: API status/라벨 JSON 일부/머지 응답 코드 출력으로 디버그 용이성 확보
충돌 해결 기록
- [2025-11-14 23:50]
.gitea/workflows/auto-pr.ymlmain 병합 충돌 해결- 충돌 마커 제거, 메시지 통일: "기존 'automerge' 라벨만, 생성 금지"
- PR 번호 해석 재시도 루프 유지 및 라벨 ID 해석 로직 안정화
ZIP 검증 TDD(자산/메타)
- 통합/메타 테스트 추가:
zip.validation.integrated.test.ts,zip.validation.meta.test.ts- exportPage + buildZip 통합: 자산 경로 재작성 및 포함, 하위 폴더 포함 검증
ZIP 검증 TDD Phase4(루트 파일/manifest)
- 테스트:
zip.validation.phase4.test.ts- ZIP 루트에
favicon.ico/site.webmanifest/robots.txt포함 검증 site.webmanifestJSON 파싱 유효성 검증(잘못된 JSON 시 에러)
- ZIP 루트에
- 구현:
lib/exporter/zip.tsBuildZipInput.extras추가(루트 파일 수집)- manifest JSON 파싱 검증 로직 추가
- 브랜치/PR:
feat/manifest-robots-auto→ CI 트리거 및 자동 머지 대상(automerge 라벨 조건)
SEO/Perf 개선(TDD): theme-color, hero width/height
- 테스트:
lib/exporter/seoPerf.themeColor.heroDims.test.ts<meta name="theme-color" content="#0ea5e9" />헤드 포함 검증(page.theme.primaryColor 사용)- hero 이미지에
width/height속성 존재 검증(CLS 저감)
- 구현:
lib/exporter/html.tsbuildHead에 theme-color 메타 추가renderHero이미지 태그에 width/height 기본값 추가(1200x675)
- 브랜치/PR:
feat/seo-perf-themecolor-hero-dims→ CI 트리거 및 자동 머지 대상
Export 옵션 기반(TDD): extras 오버라이드(robots/manifest)
- 테스트:
lib/exporter/extras.overrides.test.ts- robots.txt 내용 오버라이드(Disallow 경로) 반영 검증
- manifest 필드(name/short_name/start_url/display/theme_color) 오버라이드 검증
- 구현:
lib/exporter/extras.tsbuildExtrasForPage(page, overrides?)시그니처로 확장overrides.robotsText/overrides.manifest병합 적용, 기본값 유지
- 브랜치/PR:
feat/export-options-overrides→ CI 트리거 및 자동 머지 대상
ZIP 검증 TDD Phase5(extras 내용 검증)
- 테스트:
lib/exporter/zip.validation.phase5.test.tsrobots.txt텍스트 베이스라인 확인(User-agent: *)site.webmanifestJSON 파싱 및 필수 키(start_url,display) 확인
- 구현: 기존
buildExtrasForPage/buildZip경로 사용(추가 구현 불필요) - 브랜치/PR:
feat/zip-validation-phase5→ CI 트리거 및 자동 머지 대상
Export 옵션 UI 폴리시(TDD): display 드롭다운 + theme_color HEX 검증
- 테스트:
components/ExportOptions.polish.ui.test.tsx- Manifest
display입력이 드롭다운(select)으로 제공되고 옵션 값(standalone/minimal-ui/browser) 중에서만 선택됨을 검증 theme_color는#RGB/#RRGGBB형식의 HEX만 허용하며, 잘못된 값은 오버라이드에 반영되지 않음을 검증
- Manifest
- 구현:
components/BuilderClientPage.tsxdisplay입력을<select>로 변경하고 옵션 추가theme_color는 정규식^#([0-9a-fA-F]{3}){1,2}$통과 시에만manifestOverrideBase.theme_color에 반영
- 결과: 전체 테스트 그린(유닛/컴포넌트/E2E)
- 브랜치/PR 제안:
feat/export-ui-polish→ CI 트리거 및 자동 머지 대상(automerge 라벨 조건)
폰트 최적화(TDD)
- 테스트:
lib/exporter/font.optimization.test.ts- head에
<link rel="preconnect" href="https://fonts.googleapis.com">,<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>존재 검증 - CSS에
system-ui폴백 포함 검증
- head에
- 구현:
lib/exporter/html.ts- head에 googleapis/gstatic preconnect 추가
- CSS body
font-family에 system-ui 폴백 유지
- 브랜치/PR:
feat/font-optimization→ CI 트리거 및 자동 머지 대상(automerge 라벨 조건)
폼 제출: Google Sheets 템플릿 Export 포함(TDD)
- 테스트:
lib/exporter/sheets.template.test.ts- actionUrl 미지정 시 ZIP extras에
sheets/Code.gs,sheets/README.txt포함 검증 - actionUrl 지정 시 extras에 포함되지 않음 검증
- actionUrl 미지정 시 ZIP extras에
- 구현:
lib/exporter/extras.tsbuildExtrasForPage(page)로 extras 생성- Apps Script Code.gs/README.txt 기본 템플릿 제공
- 브랜치/PR:
feat/form-sheets-template→ CI 트리거 및 자동 머지 대상(automerge 라벨 조건)
site.webmanifest/robots.txt 자동 생성(TDD)
- 테스트:
lib/exporter/manifest.robots.auto.test.ts- extras에
site.webmanifest/robots.txt자동 포함 검증 - manifest 파생 키 검사(name/short_name/start_url/display/theme_color)
- extras에
- 구현:
lib/exporter/extras.tsbuildExtrasForPage(page)에서 robots.txt/manifest 자동 생성 및 포함
- 브랜치/PR:
feat/manifest-robots-auto→ CI 트리거 및 자동 머지 대상(automerge 라벨 조건)
의사결정 기록(ADR)
-
[YYYY-MM-DD] Next.js(App Router) 채택: 생태계/배포/서버리스/SEO 편의성.
-
[YYYY-MM-DD] Sheets 기본 방안: Apps Script 웹앱 템플릿(사용자 편의/트래픽 분산).
-
[YYYY-MM-DD] 로컬 이미지 업로드 허용: 서버 저장 없이 ZIP에
assets/로 패키징(브라우저 JSZip 우선). -
[ci] automerge 재검증용 ping (2025-11-14 21:36:38)
-
[ci] auto-pr 라벨 자동부여 재검증 ping (2025-11-14 21:45:14)
-
[ci] auto-label 재트리거 ping (2025-11-14 21:56:22)
-
[ci] automerge 라벨 수동부여 후 재트리거 ping (2025-11-14 22:01:41)
-
[ci] full auto pipeline 재검증 ping (2025-11-14 22:09:02)
-
[ci] auto-label payload fix 재검증 ping (2025-11-14 22:15:14)
-
[ci] auto pipeline 최종 재검증 최종 ping (2025-11-14 22:26:59)
Export ZIP 무결성 강화(Option2) 진행 현황
목표
- Export ZIP 무결성 강화: 자산 무결성 지표와 번들 검증 체계 구축
- CI/CD(Gitea)에서 PR 자동화, 라벨링, 조건부 머지 적용
- 모든 개발은 TDD로 진행하고, 기능 단위 커밋/대브랜치 단위 푸쉬
브랜치 전략
- 대범위 기능 묶음 브랜치:
feat/export-bundle-integrity - 관련 기능 완료까지 로컬 커밋만 진행, 완료 시점에 일괄 푸쉬
진행 현황
- SHA-256(base64) 무결성 구현 및
assets.integrity.index.json생성 assets.index.json그룹 정렬/경로 전략(grouped/flat) 커버리지 확장referencedAt추적(HTML 내rewriteUrl('local:...')) 매트릭스 테스트verifyIntegrity유틸 구현 및 TDD(정상/변조/누락/번들해시 불일치) 통과assets.integrity.index.json에size(byte) 추가,verifyIntegrity에서 크기 불일치 검증verifyIntegrity결과에summary(total/missing/integrityMismatches/sizeMismatches/bundleHashMismatch) 추가- Export Options(폰트 프리커넥트/프리로드, 이미지 로딩/디코딩, robots 메타) UI/문서화
- 브랜치 푸쉬 완료:
feat/export-bundle-integrity - 이미지 최적화 옵션(1차):
fetchpriority/srcset/sizes추가 및 테스트 그린 - 이미지 최적화 옵션(2차):
imageAutoSizes(srcset만 있을 때 기본 sizes 자동 적용) - 이미지 최적화 옵션(3차):
imageSizesPreset(mobile-first/desktop-fixed) + 우선순위(imageSizes > preset > auto > none) - 브랜치/PR:
feat/image-optimization푸쉬 완료 - verifyIntegrity CLI 래퍼(TDD) 추가:
lib/exporter/verify.cli.ts+ 테스트 - 폴더 기반 직렬화 유틸(TDD) 추가:
lib/exporter/verify.serialize.ts+ 테스트 - CI 통합(권장 플로우):
.gitea/workflows/ci.yml에./artifacts/export존재 시 검증 스텝 자동 실행 - 스크립트:
scripts/make-verify-json.js,scripts/verify.js+npm run verify:make-json/verify:run - 브랜치/PR:
feat/verify-integrity-cli푸쉬 완료
하드닝
- ZIP 경로 가드:
../, 절대경로(/), 역슬래시(\) 차단 (테스트 포함)
TDD 계획 및 상태
- 테스트 선작성 → 최소 구현 → 리팩터/문서화
- 현재 테스트: 110 files / 173 tests 모두 통과
- 무결성 인덱스/검증에 대한 정상/오류 케이스 커버 완료
오류/디버깅 로그 요약
- 그룹 경로(other) 정규식 기대값 수정으로 테스트 안정화
- SHA-256 재계산 불일치 회피: 테스트에서 중복 해시 구현 제거, 산출물 기준 비교
- Node/브라우저 호환 btoa/atob 제거, 안전한 변환 로직 적용
다음 작업
- PR 생성/머지 순서 관리:
feat/image-optimization,feat/verify-integrity-cli→ main - CI 검증 리포트 형식 보강: 요약 필드(total/missing/...)를 워크플로 로그에 하이라이트
- PR 라벨 자동화 및 조건부 머지(Gitea Actions/웹훅) 유지
- 변환 파이프라인(AVIF/WebP) 설계 초안(TDD 스텁)
- README/개발자 가이드에
./artifacts/export폴더 정책 명시(로컬/CI 공통)
산출물 경로 정책(CI)
- 기본 폴더:
./artifacts/export/(ZIP 해제 또는 직접 생성) - 검증 입력:
npm run verify:make-json→./artifacts/extras.json/./artifacts/assets.json - 검증 실행:
npm run verify:run(종료코드 0/1/2)
CI/CD 계획(초안)
- PR 오픈 시: 라벨 자동 지정, 테스트/빌드, 무결성 검증 스텝 실행
- main 머지 조건: 테스트/빌드 통과 + 무결성 검증 통과 + 최소 라벨 셋 충족
커밋/푸쉬 원칙
- 기능 단위 커밋은 한국어 메시지
- 대범위 브랜치 완료 전에 푸쉬 금지(현 단계는 완료되어 푸쉬 수행)