# task-1350.1 완료 보고서: 배너 에디터 흰 화면 버그 수정

## SCQA

**S**: 대시보드 배너 에디터 탭은 18개 배너(M1 6, M2 6, M3 6)를 편집할 수 있어야 하며, API(`GET /api/banner-editor/list`)는 정상 작동 중이었다.

**C**: 배너 에디터 탭 클릭 시 흰색 바탕만 표시. 원인 3가지: (1) `index.html`에 `BannerEditorView.js` script 태그 누락으로 컴포넌트 undefined → React 크래시, (2) `utils.js`에서 `useMemo` 미해체로 BannerEditorView 내부 ReferenceError, (3) M1/M3 12개 배너에 HTML 파일 부재로 편집 불가(`has_html: false`).

**Q**: 배너 에디터 탭이 정상 렌더링되고, 전체 18개 배너를 편집할 수 있는가?

**A**: 3개 파일 수정 + 12개 HTML 파일 생성으로 해결. 수정 후 Playwright 테스트로 검증: 배너 목록 정상 렌더링(18개 전체 "편집 가능"), M1/M2 에디터 진입 성공, 콘솔 에러 0건(관련 에러 기준).

## 수정 내역

### 1. 프론트엔드 수정 (이리스, Sonnet)

**`dashboard/index.html:99`** — BannerEditorView.js script 태그 추가 (SkillView.js 뒤, App.js 앞)

**`dashboard/components/utils.js:3`** — `useMemo` React hook 해체 추가

**`dashboard/components/BannerEditorView.js`** — M1(`.banner`), M3(`.container`) 루트 클래스 지원:
- `:185` `getEditableElements()` — `.canvas > *, .banner > *, .container > *`
- `:201` `refreshLayers()` — `.canvas > *, .banner > *, .container > *`
- `:783` `handleLayerDragOver()` — `.canvas || .banner || .container`

### 2. M1/M3 HTML 파일 생성 (불칸, Sonnet)

**`dashboard/extract_banner_html.py`** — 유틸리티 스크립트 작성 및 실행. gen 스크립트에서 HTML 템플릿 추출, bg_url을 빈 문자열로 치환, 원래 루트 클래스 유지.

### 3. CSS-HTML 클래스 불일치 수정 (팀장 직접)

불칸이 생성한 HTML에서 `class="canvas"`로 변경되어 CSS(`.banner`/`.container`)와 불일치 발생. 원래 클래스명으로 복원:
- M1 HTML: `class="banner"` 복원 (CSS `.banner {}` 와 일치)
- M3 HTML: `class="container"` 복원 (CSS `.container {}` 와 일치)

## 발견 이슈 및 해결

### 자체 해결 (4건)
1. **script 태그 누락** — `index.html`에 BannerEditorView.js 추가 (`index.html:99`)
2. **useMemo 미해체** — `utils.js:3`에 `useMemo` 추가
3. **루트 클래스 미지원** — BannerEditorView.js 3곳에 `.banner`, `.container` 셀렉터 추가
4. **CSS-HTML 클래스 불일치** — 생성된 M1/M3 HTML에서 원래 클래스명 복원 (sed 일괄 치환)

## 테스트 결과

Playwright 자동화 테스트 (headless Chromium):
- 배너 목록: "배너 에디터" 제목 표시 ✓, "18개 배너" 카운트 ✓, M1/M2/M3 그룹 표시 ✓
- M1 에디터 진입: "← 목록" 버튼 ✓, "속성 편집" 패널 ✓, "레이어" 패널 ✓, 배너 텍스트 렌더링 ✓
- M2 에디터 진입: 배경 이미지 + 텍스트 오버레이 정상 ✓
- 콘솔 에러: 관련 JS 에러 0건 (404 2건은 M1 HTML 빈 bg_url — 예상 동작)

## 산출물 파일

### 수정된 파일
- `dashboard/index.html`
- `dashboard/components/utils.js`
- `dashboard/components/BannerEditorView.js`

### 생성된 파일
- `dashboard/extract_banner_html.py`
- `output/google-ads/banners/m1/m1-1-1080x1080.html`
- `output/google-ads/banners/m1/m1-1-1200x628.html`
- `output/google-ads/banners/m1/m1-2-1080x1080.html`
- `output/google-ads/banners/m1/m1-2-1200x628.html`
- `output/google-ads/banners/m1/m1-3-1080x1080.html`
- `output/google-ads/banners/m1/m1-3-1200x628.html`
- `output/google-ads/banners/m3/m3-1-1080x1080.html`
- `output/google-ads/banners/m3/m3-1-1200x628.html`
- `output/google-ads/banners/m3/m3-2-1080x1080.html`
- `output/google-ads/banners/m3/m3-2-1200x628.html`
- `output/google-ads/banners/m3/m3-3-1080x1080.html`
- `output/google-ads/banners/m3/m3-3-1200x628.html`

## QC 자동 검증 결과

```
overall: PASS (7 PASS, 5 SKIP)
TRUST: T✓ R✓ U✓ S✓ T✓

file_check: PASS (extract_banner_html.py 12229 bytes, report 4172 bytes)
data_integrity: PASS
pyright_check: PASS (0 errors, 0 warnings)
style_check: PASS (black OK, isort OK)
critical_gap: PASS
spec_compliance: PASS
duplicate_check: PASS (max 유사도 8.6%)
test_runner: SKIP (관련 테스트 0개 — 정당한 SKIP)
tdd_check: SKIP
schema_contract: SKIP
scope_check: SKIP
api_health: SKIP (비서버 작업)
```

## 모델 사용 기록
- 이리스 / 프론트엔드 수정 (index.html, utils.js, BannerEditorView.js) / sonnet
- 불칸 / M1/M3 HTML 생성 유틸리티 작성 및 실행 / sonnet
- 헤르메스(팀장) / 분석, CSS 클래스 복원, 테스트, 보고서 / opus
