` 적용 - 섹션 식별: `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 구조 스냅샷 강화: `
` 내부 섹션/폼 포함 검증 - Exporter CSS 토큰 스냅샷: skip-link/focus outline/반응형/모션 감소 토큰 존재 검증 - Features/Testimonials `aria-labelledby`/헤딩 id 일치 검증 추가 - Exporter 헤딩 레벨 일관성 전수 테스트(hero=h1, others=h2, FAQ=h3) 추가 - a11y 색/폰트 최소 기준 스모크(본문 컬러/폰트 패밀리/모바일 h1 폰트 크기) 추가 - CTA 섹션에 `aria-labelledby` + `

` 추가(헤딩 레벨 일관성) - 전체 테스트 그린(유닛/컴포넌트/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/반응형 토큰 통합 검증 추가 - 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 커버리지 체크리스트 - [x] Hero: `Hero Heading` 입력 `aria-describedby` → `ins-hero-heading-help` - [x] CTA: `CTA Text`/`CTA Href` 입력 `aria-describedby` → `ins-cta-text-help`/`ins-cta-href-help` - [x] FAQ: `FAQ Q n`/`FAQ A n` 입력 `aria-describedby` → `ins-faq-help` - [x] Features: `Feature n` 입력 `aria-describedby` → `ins-features-help` - [x] Testimonials: `Author n`/`Quote n` 입력 `aria-describedby` → `ins-testimonials-help` - [x] 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 실행 ### CI 최종 점검 - main/master 브랜치 푸시/PR 시 워크플로 자동 실행 확인 - 새로 추가된 테스트(헤딩 레벨 전수, 색/폰트 스모크) CI 통합 확인 - 작업 브랜치 푸시 완료: `chore/ci-verify-a11y-headings` (원격: origin) - PR 생성 및 Actions 결과 기록 예정(base: main) - CI verification note: 러너 온라인 이후, 사소 변경으로 CI 트리거 검증 예정 - CI rerun: 워크플로 재실행용 ping 커밋 ### 다음 스프린트 후보 과제 - Exporter 회귀 스냅샷 보강(폼 토큰/검증 토큰 범위 확대) - 인스펙터 신규 섹션 추가 시 a11y 베이스라인 재사용 가이드 마련 - 버튼 네이밍 일관성 전수 스캔 스모크 범위 확대 - node 20 + npm ci - Vitest 실행 → Playwright 브라우저 설치 → E2E 실행 ## 마일스톤 1. 스캐폴딩 및 테스트 환경 구성 2. 스키마 정의(Zod) 및 HTML Exporter TDD 3. 폼 엔진 TDD(필드/검증/스팸 방지) 4. 에셋 매니저 TDD(업로드/메타/해싱/참조 재작성) 5. Google Sheets 연동(Apps Script 템플릿) TDD 6. 빌더 UI(DnD/미리보기/i18n) TDD 7. ZIP 내보내기(JSZip) 및 로컬 에셋 포함 TDD 8. a11y/SEO/OG/분석 스니펫 TDD + E2E ## 다음 단계(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:`로 조정, 전면 그린 ## 의사결정 기록(ADR) - [YYYY-MM-DD] Next.js(App Router) 채택: 생태계/배포/서버리스/SEO 편의성. - [YYYY-MM-DD] Sheets 기본 방안: Apps Script 웹앱 템플릿(사용자 편의/트래픽 분산). - [YYYY-MM-DD] 로컬 이미지 업로드 허용: 서버 저장 없이 ZIP에 `assets/`로 패키징(브라우저 JSZip 우선).