Files
C.E.L_Slide_test2/docs/history/IMPROVEMENT-PHASE-B.md
kyeongmin c42e01f060 문서 정리: Phase 히스토리 md를 docs/history/로 이동 + 오래된 테스트/에셋 정리
- 루트의 IMPROVEMENT-PHASE-*.md, PHASE-*.md 등 45개 → docs/history/로 이동
- docs/block-tests/ 오래된 블록 테스트 HTML 삭제 (figma_to_html_agent로 대체)
- docs/figma-analysis/, docs/figma-assets/, docs/figma-screenshots/ 정리
- docs/test-*.html 등 초기 테스트 파일 정리
- 참고 페이지/ 스크린샷 정리

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 10:56:23 +09:00

15 KiB

Phase B: 누락 기능 구현 — 실행 상세

누락된 기능 구현. 실작업 5개 (B-6, B-7은 해결됨). 원칙: 하드코딩 금지. 모든 판단은 AI 사고. 회귀 금지.


실행 순서

[독립] B-1 (details 템플릿), B-4+B-5 (이미지/표 판단), B-8 (fallback 교체)
  → B-2 (인쇄 JS, B-1 후)
  → B-3 (catalog 등록, B-1 후)

B-1: details-block 템플릿 제작 완료

현재 상태

  • BLOCK_SLOTS에 정의 있음 (design_director.py:47~49): required: ["summary_text", "detail_content"], optional: ["label"]
  • _apply_defaults에 기본값 있음 (content_editor.py:248): {"summary_text": "(상세 내용)", "detail_content": ""}
  • HTML 템플릿 파일이 templates/blocks/ 어디에도 없음 → 렌더링 불가

API 선택

  • AI 호출 없음. HTML/CSS 템플릿만.

작업

templates/blocks/emphasis/details-block.html 신규 제작

구조:

<details class="block-details">
    <summary class="dt-summary">
        {% if label %}<span class="dt-label">{{ label }}</span>{% endif %}
        {{ summary_text }}
    </summary>
    <div class="dt-content">{{ detail_content }}</div>
</details>

하드코딩 점검 — CSS 규칙

  • 색상: var(--color-*) 만 사용. #직접값 금지
  • 폰트: var(--font-*) 사용
  • 여백: var(--spacing-*) 사용
  • 테두리: var(--border-width), var(--accent-border), var(--radius) 사용
  • 기존 emphasis 블록의 직접값(#1e3a5f 등)을 따라하지 않는다

충돌/회귀

  • 충돌: 없음. 신규 파일 추가만
  • 회귀: 없음. BLOCK_SLOTS/_apply_defaults와 정합
  • 단발성: 아님. <details>/<summary>는 HTML 표준 — 브라우저 내장, 의존성 없음

수정 파일

  • 신규: templates/blocks/emphasis/details-block.html

구현 결과

  • templates/blocks/emphasis/details-block.html 신규 제작 완료
  • HTML 구조: <details class="block-details"><summary class="dt-summary"> (label + summary_text) → <div class="dt-content"> (detail_content)
  • CSS: #직접값 0개 — 전부 디자인 토큰으로 구현
    • 배경: var(--color-bg-subtle), 테두리: var(--color-border), 액센트: var(--color-accent)
    • 폰트: var(--font-body), var(--font-caption), 여백: var(--spacing-inner), var(--spacing-block)
  • summary 마커: 기본 브라우저 마커 숨기고(list-style: none, ::-webkit-details-marker { display: none }) 커스텀 ▶/▼ 표시
  • 좌측 파란 액센트 라인: border-left: var(--accent-border) solid var(--color-accent) — quote-left-border와 톤 통일

B-2: 인쇄 시 details 자동 펼침 JS 완료

현재 상태

  • slide-base.html의 @media print에 page-break만 있음
  • details 자동 펼침 JS 없음
  • CLAUDE.md: "인쇄 시 JavaScript 6줄로 자동 펼침"

작업

slide-base.html </body> 직전에 삽입:

<script>
window.onbeforeprint = function() {
    document.querySelectorAll('details').forEach(function(d) { d.open = true; });
};
window.onafterprint = function() {
    document.querySelectorAll('details').forEach(function(d) { d.open = false; });
};
</script>

하드코딩 점검

  • 없음. DOM API만 사용. 고정값 없음.

충돌/회귀

  • 충돌: 없음. {% endfor %} 이후, Jinja2 루프 밖에 삽입
  • 회귀: 없음. 기존 HTML에 <details> 없으면 querySelectorAll이 빈 NodeList → 무동작
  • 다운로드 HTML: renderer.py의 CSS 인라인 삽입은 <link><style> 교체만. <script>는 그대로 포함됨

수정 파일

  • templates/slide-base.html

의존성

  • B-1 완료 후 의미 있음 (details 태그가 있어야 JS가 동작)

구현 결과

  • slide-base.html </body> 직전에 <script> 블록 삽입
  • window.onbeforeprint: 모든 <details> 요소에 open = true 설정 (인쇄 시 펼침)
  • window.onafterprint: 모든 <details> 요소에 open = false 복원 (인쇄 후 접힘)
  • <details> 태그가 없으면 querySelectorAll 빈 NodeList → 무동작 (기존 슬라이드에 영향 없음)
  • 다운로드 HTML에도 JS 그대로 포함됨 (renderer의 CSS 인라인 처리는 <link><style> 교체만)

B-3: catalog에 details-block 등록 완료

현재 상태

  • catalog.yaml에 미등록 → Sonnet(팀장)이 이 블록을 선택할 수 없음

작업

catalog.yaml blocks 배열에 추가:

- id: details-block
  name: 자세히보기 (접기/펼치기)
  template: blocks/emphasis/details-block.html
  height_cost: "~60px (compact, 접힌 상태 기준. 펼치면 내용에 따라 가변)"
  visual: "접힌 요약 1줄 + 클릭하면 상세 내용 펼침. HTML 네이티브 <details> 사용."
  when: >
    너무 구체적/세부적인 내용을 접어서 보여줄 때.
    본문 흐름을 끊지 않으면서 상세 데이터를 제공할 때.
    비교표, 상세 스펙 등 detail_target 꼭지.
  not_for: >
    본문 핵심 내용 (접으면 안 됨).
    결론이나 강조 메시지 (항상 보여야 함).
    일반 텍스트 (topic-header 또는 card 사용).
  slots:
    required: [summary_text, detail_content]
    optional: [label]
  character_limits:
    summary_text: 60
    detail_content: 500
    label: 10

하드코딩 점검

  • height_cost: 접힌 상태의 사실적 높이. AI가 zone 예산 계산에 사용하는 참고값
  • character_limits: AI 참고용 가이드. 강제 아님 (CLAUDE.md: "의미 우선")

충돌/회귀

  • 충돌: 없음. catalog에 항목 추가만
  • _load_catalog_map(): id+template만 추출하므로 정상 로드
  • 높이 참고표 주석(14행): compact 목록에 details-block 추가 필요

수정 파일

  • templates/catalog.yaml

의존성

  • B-1 (템플릿이 존재해야 catalog에 등록 의미 있음)

구현 결과

  • catalog.yaml emphasis 섹션 마지막(divider-text 뒤, media 섹션 전)에 삽입
  • id: details-block, template: blocks/emphasis/details-block.html
  • height_cost: compact (접힌 상태 기준)
  • when: "너무 구체적/세부적인 내용을 접어서 보여줄 때. detail_target 꼭지."
  • not_for: "본문 핵심 내용 (접으면 안 됨). 결론 → conclusion-accent-bar."
  • slots: required: [summary_text, detail_content], optional: [label]
  • _load_catalog_map() 정상 로드 확인 (총 46개 블록)

B-4 + B-5: 1단계 이미지/표 상세 판단 필드 완료

현재 상태

  • KEI_PROMPT 출력 형식(kei_client.py:44~53행)에 content_type: "text|image|table|mixed" 한 줄만
  • 이미지 개수/소속/핵심여부/텍스트포함, 표 행/열 규모 등 상세 필드 없음
  • CLAUDE.md: "몇 개인지, 어떤 꼭지 소속인지, 핵심/보조인지", "행/열 규모, 전체 표시 가능 여부"

API 선택

  • Kei API (1차). KEI_PROMPT가 Kei API로 전달됨 (kei_client.py:96행)
  • Sonnet 직접이 아님

작업 — 3곳 동기화 필수

1) KEI_PROMPT (kei_client.py:20~56행)

프롬프트 본문에 이미지/표 판단 규칙 보강 + 출력 형식에 필드 추가:

현재 출력 형식:

{"title": "...", "total_pages": 1, "info_structure": "...",
 "topics": [{"id": 1, ..., "content_type": "text|image|table|mixed", "detail_target": false, "page": 1}]}

변경:

{"title": "...", "total_pages": 1, "info_structure": "...",
 "topics": [{"id": 1, ..., "content_type": "text|image|table|mixed", "detail_target": false, "page": 1}],
 "images": [{"topic_id": 1, "role": "key|supporting", "has_text": false, "description": "이미지 설명"}],
 "tables": [{"topic_id": 2, "rows": 5, "cols": 3, "fits_single_page": true, "description": "표 설명"}]}

2) fallback system_prompt (kei_client.py:168~184행) — 동기화

현재 문제:

  • role 필드 누락 → sidebar-right 프리셋 절대 선택 안 됨
  • info_structure 필드 누락
  • images[], tables[] 없음

→ KEI_PROMPT와 동일한 출력 스키마로 전면 동기화. 이것은 단발성 패치가 아니라, "같은 역할(1단계 실장)의 두 경로가 동일한 출력 구조를 사용해야 한다"는 구조적 원칙.

3) manual_classify (kei_client.py:225~243행) — 기본값 추가

return {
    ...
    "images": [],
    "tables": [],
}

하드코딩 점검

  • images[]/tables[] 필드: AI가 판단하여 채움. 스키마 정의일 뿐 고정값 아님
  • "key|supporting", "true|false": AI가 선택하는 enum. 하드코딩 아님

하류 영향 분석 (에이전트 검증 완료)

모듈 images[]/tables[] 추가 영향
pipeline.py .get("topics"), .get("total_pages")만 접근. 무시됨
design_director.py select_preset() topics의 role/emphasis만 사용. 무시됨
design_director.py create_layout_concept() user_prompt에 analysis 텍스트로 포함 → 이점 (Sonnet이 참고)
content_editor.py fill_content() analysis 미참조 (인자로만 받음). 완전 무관
pipeline.py _adjust_design() select_preset()만 호출. 무시됨

충돌/회귀

  • 충돌: 없음. 기존 필드 변경 없이 필드 추가만
  • 회귀: 없음. images[]/tables[]가 없어도 하류 .get() 패턴으로 안전
  • 기존 결함 수리 포함: fallback에 role/info_structure 누락 문제도 함께 해결

수정 파일

  • src/kei_client.py — KEI_PROMPT, fallback system_prompt, manual_classify (3곳)

구현 결과 — 3곳 동기화

1) KEI_PROMPT (kei_client.py:3840행 프롬프트 본문, 4656행 출력 형식)

  • 프롬프트 본문: "이미지/표가 있으면 그것도 판단해줘" → 구체화
    • "이미지가 있으면: 몇 개인지, 어떤 꼭지 소속인지, 핵심인지 보조인지, 텍스트 포함 여부 판단"
    • "표가 있으면: 행/열 규모, 1페이지 전체 표시 가능 여부 판단"
    • "이미지/표 판단 결과를 images[], tables[] 배열에 기록"
  • 출력 형식: topics[] 뒤에 images[], tables[] 배열 추가
    • images: [{"topic_id": 1, "role": "key|supporting", "has_text": false, "description": "..."}]
    • tables: [{"topic_id": 2, "rows": 5, "cols": 3, "fits_single_page": true, "description": "..."}]

2) fallback system_prompt (kei_client.py:172~195행)

  • 기존 누락 필드 전부 추가: role: "flow|reference", info_structure, images: [], tables: []
  • 꼭지 추출 규칙에 "참조 정보는 role: 'reference'" 추가
  • 출력 스키마가 KEI_PROMPT와 동일한 구조로 동기화
  • 기존 결함 수리: fallback에서도 sidebar-right 프리셋이 선택 가능해짐 (role 필드 존재)

3) manual_classify (kei_client.py:238~258행)

  • info_structure: "" 추가
  • topics[0]에 role: "flow" 추가
  • 최상위에 images: [], tables: [] 추가

B-6, B-7: 해결됨 (작업 불필요)

  • B-6: quote-left-border → 등록 안 함 확정 (구 블록 제거 방향)
  • B-7: comparison-2col → 등록 안 함 확정 (구 블록 제거 방향)

B-8: fallback_layout에서 card-grid → topic-header 교체 완료

현재 상태

  • _fallback_layout() (design_director.py:438행): "type": "card-grid"
  • card-grid는 BLOCK_SLOTS에서 제거됨 (주석 24행), _apply_defaults에서도 제거됨, catalog에도 없음
  • 현재 fallback 경로가 이미 깨져있음 (정합성 분석으로 확인)

작업

# 변경 전
"type": "card-grid",
...
"char_guide": {"title": 20, "description": 100},

# 변경 후
"type": "topic-header",
...
# char_guide 제거 — 편집자가 자체 판단 (하드코딩 방지)

topic-header 정합성 (에이전트 검증 완료)

체인 존재 여부
BLOCK_SLOTS (design_director.py:77~80) required: ["title", "description"]
_apply_defaults (content_editor.py:257) {"title": "(소제목)", "description": ""}
catalog.yaml id: topic-header, template: blocks/headers/topic-left-right.html
템플릿 파일 templates/blocks/headers/topic-left-right.html 존재

하드코딩 점검

  • 기존 char_guide: {"title": 20, "description": 100}제거
  • 편집자(3단계)가 가이드 없이 자체 판단 (CLAUDE.md: "글자 수 가이드는 참고. 의미 우선")
  • 하드코딩 0개

충돌/회귀

  • 충돌: 없음. fallback 블록 타입 변경만
  • 회귀: 아님. 깨진 fallback을 수리하는 변경 (card-grid는 이미 체인에서 제거됨)

수정 파일

  • src/design_director.py_fallback_layout() (438행)

구현 결과

  • _fallback_layout() 436~442행:
    • "type": "card-grid""type": "topic-header" 변경
    • "char_guide": {"title": 20, "description": 100} 완전 제거 (하드코딩 제거)
    • "size": "medium" 유지 (AI 판단 참고용)
  • topic-header 정합성 검증 통과:
    • BLOCK_SLOTS , _apply_defaults , catalog , 템플릿
  • 깨진 fallback 수리 완료: card-grid는 BLOCK_SLOTS/defaults/catalog 모두에서 제거된 블록이었음 → topic-header로 교체하여 전체 체인 정합

수정 파일 총괄

파일 항목 변경 성격
신규 templates/blocks/emphasis/details-block.html B-1 HTML/CSS 템플릿 제작
templates/slide-base.html B-2 <script> 6줄 추가
templates/catalog.yaml B-3 details-block 항목 + 높이 참고표 업데이트
src/kei_client.py B-4, B-5 KEI_PROMPT + fallback + manual_classify (3곳 동기화)
src/design_director.py B-8 _fallback_layout() 블록 타입 교체 + char_guide 제거

검증 체크리스트

  • B-1: details-block.html이 <details>/<summary> 사용. CSS에 var(--*) 만 사용. #직접값 없음
  • B-1: BLOCK_SLOTS 슬롯명(summary_text, detail_content, label)과 템플릿 변수명 일치
  • B-2: 인쇄 시 details 자동 펼침. 화면에서는 접힌 상태 유지
  • B-2: 다운로드 HTML에 <script> 포함
  • B-3: catalog에 details-block 등록. _load_catalog_map()에서 정상 로드
  • B-4: KEI_PROMPT에 images[] 스키마 추가. Kei API로 전달
  • B-5: KEI_PROMPT에 tables[] 스키마 추가. Kei API로 전달
  • B-4/B-5: fallback system_prompt에 role, info_structure, images[], tables[] 동기화
  • B-4/B-5: manual_classify에 images:[], tables:[] 빈 배열 추가
  • B-8: _fallback_layout()에서 "topic-header" 사용. char_guide 없음
  • B-8: fallback 경로에서 topic-header 렌더링 정상 동작

수정 이력

날짜 내용
2026-03-25 초안. 하드코딩 제거 반영 (B-8 char_guide 제거, B-1 CSS 토큰 강제). fallback 동기화 추가.
2026-03-25 Phase B 전체 구현 완료. 검증 통과.

구현 완료 확인

항목 검증 결과
B-1 details-block.html 존재. #직접값 0개 — CSS 변수만 사용. 슬롯명(summary_text, detail_content, label) BLOCK_SLOTS와 정합
B-2 slide-base.html에 onbeforeprint/onafterprint JS 포함. <details> 없으면 무동작(안전)
B-3 catalog에 details-block 등록. _load_catalog_map() 정상 로드 (총 46개 블록)
B-4 KEI_PROMPT에 images[] 스키마 + 판단 규칙 추가
B-5 KEI_PROMPT에 tables[] 스키마 + 판단 규칙 추가
B-4/5 동기화 fallback system_prompt에 role, info_structure, images[], tables[] 동기화 완료. manual_classify에도 동기화
B-8 fallback 블록 topic-header. char_guide 없음(편집자 자체 판단). BLOCK_SLOTS/defaults/catalog/템플릿 전부 정합