- src: phase_z2 composition/mapper/pipeline/placement_planner/retry, ai_fallback(prompts/schema/validate), mdx_text_atoms 신규 - Front: PipelineTracePanel 신규, FramePanel/SlideCanvas/Home/designAgentApi 등 갱신 + 테스트 4종 추가 - templates/phase_z2: catalog(component_expansion_registry, node_slot_mapping 신규), frames, families, slide_base 갱신 - tests/matching: phase2~26 매칭 실험 스크립트·리포트·온톨로지 전체 (미커밋 진행분) - tests: b4_v4 evidence, task5~28.5 시리즈, regression(imp95 baseline) 등 신규 테스트 대량 추가 - docs/reference: MDX 구조 인벤토리, MDX→Frame 구조 계약 문서 - scripts: mdx 계약/parity/coverage/viewport 체크, gitea comment, run sync 유틸 - .gitignore: tmp*.json, chromedriver, .orchestrator, *.pkl, Front_test* 등 임시/스냅샷 제외 미완성 작업의 보존용 스냅샷 커밋 (2026-07-02) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
942 lines
32 KiB
HTML
942 lines
32 KiB
HTML
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>슬라이드 변환 프로세스 정리</title>
|
|
<style>
|
|
:root {
|
|
--ink: #17211b;
|
|
--muted: #66736b;
|
|
--line: rgba(35, 50, 40, 0.14);
|
|
--paper: #fbf7ec;
|
|
--paper-strong: #fffaf0;
|
|
--green: #1f5c45;
|
|
--green-soft: #dbeadd;
|
|
--clay: #9d5d3b;
|
|
--clay-soft: #f1dfcf;
|
|
--gold: #c99b2f;
|
|
--gold-soft: #f6edce;
|
|
--blue: #315c8a;
|
|
--blue-soft: #dce8f4;
|
|
--danger: #8f3f32;
|
|
--shadow: 0 24px 80px rgba(38, 45, 38, 0.14);
|
|
--radius-lg: 28px;
|
|
--radius-md: 18px;
|
|
--radius-sm: 12px;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
min-height: 100vh;
|
|
color: var(--ink);
|
|
font-family: "Pretendard", "Noto Sans KR", "Malgun Gothic", sans-serif;
|
|
background:
|
|
radial-gradient(circle at 10% 6%, rgba(201, 155, 47, 0.24), transparent 27rem),
|
|
radial-gradient(circle at 86% 14%, rgba(49, 92, 138, 0.18), transparent 24rem),
|
|
linear-gradient(135deg, #f8efd9 0%, #f4f0e6 44%, #e8efe5 100%);
|
|
}
|
|
|
|
body::before {
|
|
content: "";
|
|
position: fixed;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
opacity: 0.22;
|
|
background-image:
|
|
linear-gradient(rgba(23, 33, 27, 0.04) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(23, 33, 27, 0.04) 1px, transparent 1px);
|
|
background-size: 34px 34px;
|
|
mask-image: linear-gradient(to bottom, #000, transparent 88%);
|
|
}
|
|
|
|
a {
|
|
color: inherit;
|
|
}
|
|
|
|
.shell {
|
|
position: relative;
|
|
width: min(1760px, calc(100vw - 36px));
|
|
min-height: calc(100vh - 36px);
|
|
margin: 18px auto;
|
|
display: grid;
|
|
grid-template-columns: 300px minmax(520px, 1fr) 420px;
|
|
gap: 18px;
|
|
}
|
|
|
|
.panel {
|
|
background: rgba(255, 250, 240, 0.78);
|
|
border: 1px solid rgba(54, 69, 56, 0.16);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
backdrop-filter: blur(18px);
|
|
}
|
|
|
|
.sidebar {
|
|
padding: 22px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 18px;
|
|
}
|
|
|
|
.brand {
|
|
padding: 18px 18px 20px;
|
|
border-radius: 22px;
|
|
background: linear-gradient(145deg, rgba(31, 92, 69, 0.96), rgba(35, 62, 52, 0.96));
|
|
color: #fff8e8;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.brand::after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 170px;
|
|
height: 170px;
|
|
border-radius: 50%;
|
|
right: -74px;
|
|
top: -74px;
|
|
background: rgba(255, 255, 255, 0.12);
|
|
}
|
|
|
|
.eyebrow {
|
|
margin: 0 0 10px;
|
|
color: rgba(255, 248, 232, 0.72);
|
|
font-size: 12px;
|
|
letter-spacing: 0.18em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
h1 {
|
|
margin: 0;
|
|
font-size: 29px;
|
|
line-height: 1.14;
|
|
letter-spacing: -0.05em;
|
|
}
|
|
|
|
.brand p {
|
|
margin: 14px 0 0;
|
|
color: rgba(255, 248, 232, 0.78);
|
|
line-height: 1.58;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.stage-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.stage-button {
|
|
width: 100%;
|
|
border: 1px solid var(--line);
|
|
border-radius: 20px;
|
|
padding: 16px;
|
|
text-align: left;
|
|
background: rgba(255, 255, 255, 0.42);
|
|
color: var(--ink);
|
|
cursor: pointer;
|
|
transition: transform 180ms ease, background 180ms ease, border 180ms ease, box-shadow 180ms ease;
|
|
}
|
|
|
|
.stage-button:hover {
|
|
transform: translateY(-2px);
|
|
background: rgba(255, 255, 255, 0.72);
|
|
}
|
|
|
|
.stage-button.active {
|
|
background: #fffdf7;
|
|
border-color: rgba(31, 92, 69, 0.36);
|
|
box-shadow: 0 16px 34px rgba(31, 92, 69, 0.14);
|
|
}
|
|
|
|
.stage-no {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 62px;
|
|
height: 27px;
|
|
margin-bottom: 12px;
|
|
border-radius: 999px;
|
|
color: #fffdf7;
|
|
background: var(--green);
|
|
font-size: 12px;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.stage-button:nth-child(2) .stage-no {
|
|
background: var(--clay);
|
|
}
|
|
|
|
.stage-button:nth-child(3) .stage-no {
|
|
background: var(--blue);
|
|
}
|
|
|
|
.stage-title {
|
|
display: block;
|
|
font-size: 18px;
|
|
font-weight: 850;
|
|
letter-spacing: -0.04em;
|
|
}
|
|
|
|
.stage-desc {
|
|
display: block;
|
|
margin-top: 8px;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
line-height: 1.52;
|
|
}
|
|
|
|
.note {
|
|
margin-top: auto;
|
|
padding: 16px;
|
|
border-radius: 18px;
|
|
background: rgba(23, 33, 27, 0.06);
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
line-height: 1.55;
|
|
}
|
|
|
|
.main {
|
|
padding: 26px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero {
|
|
min-height: 214px;
|
|
padding: 30px;
|
|
border-radius: var(--radius-lg);
|
|
color: #fffaf0;
|
|
background:
|
|
linear-gradient(120deg, rgba(23, 33, 27, 0.92), rgba(31, 92, 69, 0.88)),
|
|
radial-gradient(circle at right top, rgba(201, 155, 47, 0.55), transparent 28rem);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: auto -50px -110px auto;
|
|
width: 360px;
|
|
height: 360px;
|
|
border-radius: 50%;
|
|
border: 44px solid rgba(255, 250, 240, 0.08);
|
|
}
|
|
|
|
.hero-meta {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 12px;
|
|
border-radius: 999px;
|
|
background: rgba(255, 250, 240, 0.12);
|
|
border: 1px solid rgba(255, 250, 240, 0.18);
|
|
color: rgba(255, 250, 240, 0.88);
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.hero h2 {
|
|
position: relative;
|
|
z-index: 1;
|
|
margin: 24px 0 12px;
|
|
font-size: clamp(34px, 4vw, 58px);
|
|
line-height: 0.98;
|
|
letter-spacing: -0.075em;
|
|
}
|
|
|
|
.hero p {
|
|
position: relative;
|
|
z-index: 1;
|
|
width: min(760px, 100%);
|
|
margin: 0;
|
|
color: rgba(255, 250, 240, 0.76);
|
|
font-size: 17px;
|
|
line-height: 1.7;
|
|
word-break: keep-all;
|
|
}
|
|
|
|
.summary-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
gap: 12px;
|
|
margin-top: 18px;
|
|
}
|
|
|
|
.summary-card {
|
|
border: 1px solid var(--line);
|
|
border-radius: 20px;
|
|
padding: 18px;
|
|
background: rgba(255, 255, 255, 0.52);
|
|
}
|
|
|
|
.summary-card strong {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-size: 13px;
|
|
color: var(--green);
|
|
}
|
|
|
|
.summary-card p {
|
|
margin: 0;
|
|
color: var(--ink);
|
|
font-size: 14px;
|
|
line-height: 1.62;
|
|
word-break: keep-all;
|
|
}
|
|
|
|
.section-title {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 14px;
|
|
margin: 28px 0 14px;
|
|
}
|
|
|
|
.section-title h3 {
|
|
margin: 0;
|
|
font-size: 21px;
|
|
letter-spacing: -0.045em;
|
|
}
|
|
|
|
.section-title span {
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
}
|
|
|
|
.method-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 14px;
|
|
}
|
|
|
|
.method-card {
|
|
border: 1px solid var(--line);
|
|
border-radius: 22px;
|
|
padding: 19px;
|
|
background: rgba(255, 253, 247, 0.74);
|
|
cursor: pointer;
|
|
min-height: 190px;
|
|
transition: transform 180ms ease, box-shadow 180ms ease, border 180ms ease;
|
|
}
|
|
|
|
.method-card:hover {
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 18px 40px rgba(23, 33, 27, 0.11);
|
|
border-color: rgba(31, 92, 69, 0.32);
|
|
}
|
|
|
|
.method-card.active {
|
|
border-color: rgba(31, 92, 69, 0.54);
|
|
box-shadow: inset 0 0 0 2px rgba(31, 92, 69, 0.08), 0 18px 42px rgba(31, 92, 69, 0.12);
|
|
}
|
|
|
|
.method-kicker {
|
|
display: inline-flex;
|
|
padding: 6px 10px;
|
|
margin-bottom: 14px;
|
|
border-radius: 999px;
|
|
background: var(--green-soft);
|
|
color: var(--green);
|
|
font-size: 12px;
|
|
font-weight: 850;
|
|
}
|
|
|
|
.method-card:nth-child(2) .method-kicker {
|
|
background: var(--clay-soft);
|
|
color: var(--clay);
|
|
}
|
|
|
|
.method-card:nth-child(3) .method-kicker {
|
|
background: var(--blue-soft);
|
|
color: var(--blue);
|
|
}
|
|
|
|
.method-card h4 {
|
|
margin: 0 0 10px;
|
|
font-size: 19px;
|
|
letter-spacing: -0.04em;
|
|
}
|
|
|
|
.method-card p {
|
|
margin: 0;
|
|
color: var(--muted);
|
|
line-height: 1.58;
|
|
font-size: 14px;
|
|
word-break: keep-all;
|
|
}
|
|
|
|
.evidence {
|
|
display: grid;
|
|
grid-template-columns: 1.15fr 0.85fr;
|
|
gap: 14px;
|
|
margin-top: 14px;
|
|
}
|
|
|
|
.evidence-box {
|
|
border: 1px solid var(--line);
|
|
border-radius: 22px;
|
|
padding: 20px;
|
|
background: rgba(255, 255, 255, 0.48);
|
|
}
|
|
|
|
.evidence-box h4 {
|
|
margin: 0 0 12px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.bullets {
|
|
margin: 0;
|
|
padding-left: 18px;
|
|
color: var(--muted);
|
|
line-height: 1.72;
|
|
font-size: 14px;
|
|
word-break: keep-all;
|
|
}
|
|
|
|
.metric-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto;
|
|
gap: 14px;
|
|
padding: 10px 0;
|
|
border-bottom: 1px dashed rgba(23, 33, 27, 0.14);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.metric-row:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
.metric-row span {
|
|
color: var(--muted);
|
|
}
|
|
|
|
.metric-row strong {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.detail {
|
|
padding: 22px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.detail-head {
|
|
padding: 20px;
|
|
border-radius: 22px;
|
|
background: #17211b;
|
|
color: #fffaf0;
|
|
}
|
|
|
|
.detail-head p {
|
|
margin: 0 0 8px;
|
|
color: rgba(255, 250, 240, 0.62);
|
|
font-size: 12px;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.detail-head h3 {
|
|
margin: 0;
|
|
font-size: 24px;
|
|
line-height: 1.22;
|
|
letter-spacing: -0.055em;
|
|
}
|
|
|
|
.detail-body {
|
|
overflow: auto;
|
|
padding-right: 4px;
|
|
}
|
|
|
|
.detail-block {
|
|
padding: 18px;
|
|
margin-bottom: 12px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 20px;
|
|
background: rgba(255, 255, 255, 0.54);
|
|
}
|
|
|
|
.detail-block h4 {
|
|
margin: 0 0 10px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.detail-block p {
|
|
margin: 0;
|
|
color: var(--muted);
|
|
line-height: 1.68;
|
|
font-size: 14px;
|
|
word-break: keep-all;
|
|
}
|
|
|
|
.detail-list {
|
|
margin: 0;
|
|
padding-left: 18px;
|
|
color: var(--muted);
|
|
line-height: 1.72;
|
|
font-size: 14px;
|
|
word-break: keep-all;
|
|
}
|
|
|
|
.links {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.report-link {
|
|
display: inline-flex;
|
|
padding: 9px 12px;
|
|
border-radius: 999px;
|
|
border: 1px solid rgba(31, 92, 69, 0.2);
|
|
background: rgba(255, 255, 255, 0.56);
|
|
color: var(--green);
|
|
font-size: 13px;
|
|
font-weight: 800;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.status {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 7px 10px;
|
|
border-radius: 999px;
|
|
background: var(--gold-soft);
|
|
color: #765715;
|
|
font-size: 12px;
|
|
font-weight: 850;
|
|
}
|
|
|
|
.footer-line {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
margin-top: 14px;
|
|
}
|
|
|
|
@media (max-width: 1260px) {
|
|
.shell {
|
|
grid-template-columns: 260px 1fr;
|
|
}
|
|
|
|
.detail {
|
|
grid-column: 1 / -1;
|
|
min-height: 420px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 860px) {
|
|
.shell {
|
|
width: min(100vw - 20px, 760px);
|
|
grid-template-columns: 1fr;
|
|
margin: 10px auto;
|
|
}
|
|
|
|
.summary-grid,
|
|
.method-grid,
|
|
.evidence {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.brand h1,
|
|
h1 {
|
|
font-size: 25px;
|
|
}
|
|
|
|
.hero {
|
|
padding: 24px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="shell">
|
|
<aside class="sidebar panel">
|
|
<div class="brand">
|
|
<p class="eyebrow">Process Review</p>
|
|
<h1>슬라이드 변환 프로세스 정리</h1>
|
|
<p>MDX 내용을 어떤 Figma 프레임으로 변환할지 찾기 위해 거친 세 단계의 실험과 판단 근거를 한 화면에서 검토합니다.</p>
|
|
</div>
|
|
|
|
<nav class="stage-list" aria-label="검토 단계">
|
|
<button class="stage-button active" type="button" data-stage="stage1">
|
|
<span class="stage-no">1단계</span>
|
|
<span class="stage-title">기계적 매칭</span>
|
|
<span class="stage-desc">단어, 구조, 의미 유사도를 각각 계산해 프레임 후보를 비교한 초기 실험입니다.</span>
|
|
</button>
|
|
<button class="stage-button" type="button" data-stage="stage2">
|
|
<span class="stage-no">2단계</span>
|
|
<span class="stage-title">AI 분석 기반 매칭</span>
|
|
<span class="stage-desc">AI가 Figma 프레임을 해석하고 키워드, 의미, 구조 정보를 보강해 매칭한 단계입니다.</span>
|
|
</button>
|
|
<button class="stage-button" type="button" data-stage="stage3">
|
|
<span class="stage-no">3단계</span>
|
|
<span class="stage-title">사전 정의 기반 매칭</span>
|
|
<span class="stage-desc">키워드집과 프레임별 대표 키워드를 먼저 정리한 뒤, MDX를 점수로 매칭한 단계입니다.</span>
|
|
</button>
|
|
</nav>
|
|
|
|
<div class="note">
|
|
사용 방식: 왼쪽에서 단계를 선택하고, 가운데 카드에서 세부 항목을 누르면 오른쪽 창에 실행 방식, 결과, 한계가 표시됩니다.
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="main panel">
|
|
<section class="hero" id="hero">
|
|
<div class="hero-meta" id="heroMeta"></div>
|
|
<h2 id="stageTitle"></h2>
|
|
<p id="stageLead"></p>
|
|
</section>
|
|
|
|
<section class="summary-grid" id="summaryGrid"></section>
|
|
|
|
<div class="section-title">
|
|
<h3>세부 검토 항목</h3>
|
|
<span>카드를 누르면 오른쪽에 상세가 열립니다</span>
|
|
</div>
|
|
<section class="method-grid" id="methodGrid"></section>
|
|
|
|
<section class="evidence">
|
|
<div class="evidence-box">
|
|
<h4>핵심 판단</h4>
|
|
<ul class="bullets" id="judgementList"></ul>
|
|
</div>
|
|
<div class="evidence-box">
|
|
<h4>대표 수치</h4>
|
|
<div id="metricList"></div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<aside class="detail panel" aria-live="polite">
|
|
<div class="detail-head">
|
|
<p id="detailKicker"></p>
|
|
<h3 id="detailTitle"></h3>
|
|
</div>
|
|
<div class="detail-body" id="detailBody"></div>
|
|
</aside>
|
|
</div>
|
|
|
|
<script>
|
|
const data = {
|
|
stage1: {
|
|
meta: ["초기 실험", "단어 기반", "구조 기반", "의미 기반"],
|
|
title: "1단계. 기계적 매칭",
|
|
lead: "MDX 구간과 Figma 프레임을 수치적으로 비교한 첫 단계입니다. 단어가 얼마나 겹치는지, 구조가 닮았는지, 의미 임베딩이 가까운지를 각각 계산해 Top 후보를 뽑았습니다.",
|
|
summaries: [
|
|
["했던 방식", "TF-IDF, BM25, 문자 n-gram, Kiwi+BM25, 구조 유사도, AI 메타데이터 유사도 등을 비교했습니다."],
|
|
["실행 방법", "MDX 01~03의 섹션과 Figma 32개 프레임 텍스트를 대상으로 각 방법의 Top 1~3 후보를 산출했습니다."],
|
|
["실행 결과", "명확한 일부 구간은 잘 맞았지만, 구조 단독 또는 표면 단어 단독 방식은 후보가 흔들렸습니다."],
|
|
["한계", "왜 그 프레임이어야 하는지 설명이 약하고, 단어가 적거나 추상적인 MDX에서는 점수 신뢰도가 낮았습니다."]
|
|
],
|
|
methods: [
|
|
{
|
|
kicker: "단어 기반",
|
|
title: "TF-IDF / BM25 / Kiwi+BM25",
|
|
short: "MDX와 프레임 텍스트의 단어 겹침을 점수화했습니다.",
|
|
detailTitle: "단어 기반 매칭 상세",
|
|
blocks: [
|
|
["적용 방식", "MDX 섹션을 문서로 보고, Figma 프레임의 텍스트와 단어 빈도 및 희소도를 비교했습니다. 한국어 형태소 분석을 붙인 Kiwi+BM25 방식도 함께 비교했습니다."],
|
|
["실행 결과", "BIM, DX, Process, Product처럼 명확한 용어가 있는 구간은 상위 후보가 잘 잡혔습니다. 반면 단어가 일반적이거나 짧은 구간은 다른 프레임과 쉽게 섞였습니다."],
|
|
["한계", "단어가 같다고 같은 의도는 아니고, 단어가 다르다고 다른 의도도 아닙니다. 그래서 표면 단어 기반만으로는 슬라이드 선택 근거가 부족했습니다."]
|
|
]
|
|
},
|
|
{
|
|
kicker: "구조 기반",
|
|
title: "레이아웃/구성 유사도",
|
|
short: "제목, 카드, 비교, 단계, 관계 구조가 비슷한지 비교했습니다.",
|
|
detailTitle: "구조 기반 매칭 상세",
|
|
blocks: [
|
|
["적용 방식", "프레임의 구성 패턴을 비교형, 카드형, 단계형, 관계형처럼 분류하고 MDX 내용의 구조와 맞춰 보았습니다."],
|
|
["실행 결과", "구조가 강한 슬라이드는 후보군을 좁히는 데 도움이 됐습니다. 하지만 내용 주제가 다른데 구조만 비슷한 프레임도 함께 올라왔습니다."],
|
|
["한계", "구조는 좋은 보조 신호지만, 단독으로는 주제 적합성을 보장하지 못했습니다. 같은 3열 카드라도 담아야 할 의미가 다르면 다른 프레임이어야 합니다."]
|
|
]
|
|
},
|
|
{
|
|
kicker: "의미 기반",
|
|
title: "임베딩/메타데이터 유사도",
|
|
short: "문장 의미와 요약 정보를 벡터 또는 메타 정보로 비교했습니다.",
|
|
detailTitle: "의미 기반 매칭 상세",
|
|
blocks: [
|
|
["적용 방식", "MDX 섹션 요약과 프레임 설명을 의미적으로 비교했습니다. 단어가 완전히 같지 않아도 주제가 가까우면 후보로 올라오도록 설계했습니다."],
|
|
["실행 결과", "추상적 주제에서는 단어 기반보다 보완 효과가 있었습니다. 그러나 점수만 보면 판단 근거가 불투명해지는 문제가 있었습니다."],
|
|
["한계", "의미 유사도는 그럴듯한 후보를 잘 찾지만, 사람이 검수할 수 있는 명확한 근거를 남기기 어렵습니다. 설명 가능한 규칙이 추가로 필요했습니다."]
|
|
]
|
|
}
|
|
],
|
|
judgements: [
|
|
"기계적 매칭은 후보 탐색에는 유용했지만, 최종 프레임 선택 근거로는 부족했습니다.",
|
|
"단어, 구조, 의미 중 하나만 쓰면 각각 다른 종류의 오답이 발생했습니다.",
|
|
"다음 단계에서는 AI가 프레임의 의도와 사용 맥락을 더 풍부하게 해석하도록 실험했습니다."
|
|
],
|
|
metrics: [
|
|
["비교 대상", "MDX 7개 섹션 / Figma 32개 프레임"],
|
|
["비교 방식", "8개 매칭 방식"],
|
|
["대표 결과", "일부 명확 구간 Top 1 적중"],
|
|
["주요 문제", "설명력 및 재현성 부족"]
|
|
],
|
|
links: [
|
|
["초기 비교 결과", "RESULT.md"],
|
|
["Phase 26 비교", "MATRIX_PHASE26.html"]
|
|
]
|
|
},
|
|
stage2: {
|
|
meta: ["중간 실험", "AI 프레임 해석", "키워드 보강", "구조 보강"],
|
|
title: "2단계. AI 분석 기반 매칭",
|
|
lead: "AI가 Figma 프레임을 직접 분석해 키워드, 의미, 구조, 사용 가능성을 보강한 단계입니다. 사람의 사전 정의 없이도 프레임의 의도를 넓게 읽어내는지 확인했습니다.",
|
|
summaries: [
|
|
["했던 방식", "AI가 프레임별 핵심 키워드, 내용 요약, 구조 의도, 적용 가능성을 해석하도록 했습니다."],
|
|
["실행 방법", "키워드 점수에 의미 요약, 구조 정보, 템플릿 적합성, 적용 방식 판단을 단계적으로 더했습니다."],
|
|
["실행 결과", "검증 대상 4개에서는 기대 프레임이 1순위로 올라오는 결과가 나왔습니다."],
|
|
["한계", "결과가 좋아 보여도 AI가 어떤 기준을 만들었는지, 그 기준이 재현 가능한지 검증하기 어려웠습니다."]
|
|
],
|
|
methods: [
|
|
{
|
|
kicker: "키워드 추가",
|
|
title: "AI가 뽑은 프레임 키워드",
|
|
short: "프레임을 보고 핵심어와 후보 anchor를 만들었습니다.",
|
|
detailTitle: "AI 키워드 보강 상세",
|
|
blocks: [
|
|
["적용 방식", "Figma 프레임의 텍스트와 구성 맥락을 보고 AI가 주요 키워드와 대표 표현을 추출했습니다."],
|
|
["실행 결과", "사람이 놓칠 수 있는 의미 연결을 일부 보완했습니다. 단순 단어 매칭보다 프레임 의도를 더 잘 설명하는 경우가 있었습니다."],
|
|
["한계", "AI가 만든 키워드가 원문에 있는 것인지, 해석으로 추가된 것인지 경계가 흐려졌습니다. 사용자는 이 지점을 가장 강하게 의심했고, 그 의심은 타당했습니다."]
|
|
]
|
|
},
|
|
{
|
|
kicker: "의미 추가",
|
|
title: "내용 요약과 의미 유사도",
|
|
short: "프레임의 설명 가능성을 높이기 위해 내용 요약을 매칭 신호로 넣었습니다.",
|
|
detailTitle: "의미 보강 상세",
|
|
blocks: [
|
|
["적용 방식", "프레임별 내용 요약과 MDX 섹션 요약을 비교해 키워드만으로 부족한 추상적 유사성을 보완했습니다."],
|
|
["실행 결과", "4개 검증 대상에서는 기대 프레임이 모두 1순위가 되는 결과가 나왔습니다."],
|
|
["한계", "정답을 아는 4개만 보면 좋아 보일 수 있습니다. 홀드아웃 검증이 없으면 과적합인지 일반화 가능한지 판단하기 어렵습니다."]
|
|
]
|
|
},
|
|
{
|
|
kicker: "구조 추가",
|
|
title: "템플릿 적합성 판단",
|
|
short: "프레임 구조와 MDX 내용이 실제 슬라이드로 적용 가능한지 함께 봤습니다.",
|
|
detailTitle: "구조 및 적용 가능성 상세",
|
|
blocks: [
|
|
["적용 방식", "프레임의 카드 수, 관계 구조, 슬롯, 사용 가능성, 수정 필요 여부를 함께 판단했습니다."],
|
|
["실행 결과", "단순히 비슷한 프레임을 찾는 것을 넘어, 실제 변환에 쓸 수 있는지 판단하는 방향으로 확장됐습니다."],
|
|
["한계", "이 단계도 여전히 AI 판단 비중이 큽니다. 따라서 사람이 검수 가능한 사전 범위와 명시적 규칙이 필요해졌습니다."]
|
|
]
|
|
}
|
|
],
|
|
judgements: [
|
|
"AI 분석 기반 매칭은 성능을 끌어올렸지만, 근거의 통제 가능성이 약했습니다.",
|
|
"좋은 후보를 찾는 능력과 검증 가능한 기준을 갖는 것은 별개의 문제였습니다.",
|
|
"그래서 3단계에서는 먼저 키워드집과 프레임별 적용 범위를 정리하는 방식으로 전환했습니다."
|
|
],
|
|
metrics: [
|
|
["검증 대상", "대표 MDX 4개 구간"],
|
|
["결과", "기대 프레임 4/4 Top 1"],
|
|
["대표 보강", "키워드 + 의미 + 구조 + 적용성"],
|
|
["주요 문제", "AI 기준의 불투명성"]
|
|
],
|
|
links: [
|
|
["Phase 26 결과", "MATRIX_PHASE26.html"],
|
|
["템플릿 적합성", "TEMPLATE_FIT_V1.md"]
|
|
]
|
|
},
|
|
stage3: {
|
|
meta: ["현재 정리", "키워드집", "프레임별 적용", "MDX Top 3"],
|
|
title: "3단계. 사전 정의 기반 매칭",
|
|
lead: "먼저 전체 문서에서 키워드집을 만들고, 그 키워드집을 각 Figma 프레임에 적용한 뒤, MDX 구간과 프레임을 점수로 비교한 단계입니다. 지금 가장 설명 가능성이 높은 방식입니다.",
|
|
summaries: [
|
|
["했던 방식", "전체 문서에서 키워드를 추출하고, 프레임별 단독 대표 키워드, 대표 키워드 묶음, 연관 키워드를 정리했습니다."],
|
|
["실행 방법", "MDX 키워드와 프레임별 키워드를 비교해 단독 대표, 묶음, 연관 점수를 합산했습니다."],
|
|
["실행 결과", "검증 대상 4개 MDX에서 기대 프레임이 모두 1순위로 매칭됐고, Top 3 후보와 점수를 함께 확인할 수 있게 됐습니다."],
|
|
["남은 보완", "영어 기능어 잡음 제거와 홀드아웃 검증이 남아 있습니다."]
|
|
],
|
|
methods: [
|
|
{
|
|
kicker: "키워드집",
|
|
title: "전체 문서 기반 키워드 정리",
|
|
short: "어떤 문서에서 몇 개의 문장과 단어를 모았는지 먼저 투명하게 기록했습니다.",
|
|
detailTitle: "키워드집 생성 상세",
|
|
blocks: [
|
|
["입력 범위", "Figma 프레임 32개, BEPS 마스터 텍스트 1개, MDX 검증 구간 4개를 입력으로 사용했습니다."],
|
|
["추출 결과", "고유 문장 2,125개와 형태소 분석 결과 13,913개를 거쳐 최종 고유 키워드 1,743개를 만들었습니다."],
|
|
["정리 방식", "키워드를 특정 프레임에만 등장, 일부 프레임에 등장, 여러 프레임에 넓게 등장으로 나누고, DX/BIM/S/W 같은 중요 표기는 별도 보조 표시로 관리했습니다."]
|
|
]
|
|
},
|
|
{
|
|
kicker: "프레임 적용",
|
|
title: "프레임별 3층 키워드",
|
|
short: "각 프레임에 키워드집을 적용해 대표 키워드와 묶음을 만들었습니다.",
|
|
detailTitle: "프레임별 키워드 적용 상세",
|
|
blocks: [
|
|
["단독 대표 키워드", "특정 프레임을 강하게 가리키는 희소 키워드입니다. 단독으로 발견돼도 프레임 후보를 좁히는 데 도움을 줍니다."],
|
|
["대표 키워드 묶음", "사람 + 자연 + 기술처럼 함께 등장할 때 의미가 강해지는 조합입니다. 단어 하나보다 묶음 일치율이 더 중요한 신호가 됩니다."],
|
|
["연관 키워드", "프레임의 주변 맥락을 보조하는 키워드입니다. 단독 결정력은 약하지만 전체 점수를 안정화합니다."]
|
|
]
|
|
},
|
|
{
|
|
kicker: "MDX 매칭",
|
|
title: "점수 기반 Top 1~3 후보",
|
|
short: "MDX 구간별로 가장 적합한 프레임 1~3순위를 점수와 함께 보여줍니다.",
|
|
detailTitle: "MDX 매칭 상세",
|
|
blocks: [
|
|
["점수식", "종합 매칭 점수는 단독 대표 키워드 0.3, 대표 키워드 묶음 0.5, 연관 키워드 0.2를 합산해 계산했습니다."],
|
|
["실행 결과", "MDX 01-2는 Frame 18, MDX 02-2.2는 Frame 14, MDX 03-1은 Frame 13, MDX 03-2는 Frame 29가 1순위로 매칭됐습니다."],
|
|
["남은 검증", "현재는 알려진 4개 정답 구간 중심입니다. 다른 MDX 섹션으로 홀드아웃 검증을 해야 점수식의 일반화 가능성을 말할 수 있습니다."]
|
|
]
|
|
}
|
|
],
|
|
judgements: [
|
|
"3단계는 AI가 임의로 만든 체계가 아니라, 원문 기반 키워드와 규칙 기반 점수를 남기는 방식입니다.",
|
|
"다만 완전 자동 객관 체계는 아니며, 수동 phrase rule과 user dictionary가 포함된 hybrid 방식입니다.",
|
|
"현재 가장 설득력 있는 방향이지만, 영어 잡음 제거와 홀드아웃 검증 후 적용 가능성을 확정하는 것이 안전합니다."
|
|
],
|
|
metrics: [
|
|
["입력 문서", "Figma 32개 + BEPS 1개 + MDX 4개"],
|
|
["고유 문장", "2,125개"],
|
|
["최종 키워드", "1,743개"],
|
|
["검증 결과", "4/4 기대 프레임 Top 1"]
|
|
],
|
|
links: [
|
|
["키워드집 보고서", "KEYWORD_INVENTORY.html"],
|
|
["프레임별 적용", "FRAME_KEYWORD_REVIEW.html"],
|
|
["MDX 매칭 결과", "MDX_MATCHING_REPORT.html"]
|
|
]
|
|
}
|
|
};
|
|
|
|
let currentStage = "stage1";
|
|
let currentMethod = 0;
|
|
|
|
const stageButtons = [...document.querySelectorAll(".stage-button")];
|
|
const heroMeta = document.getElementById("heroMeta");
|
|
const stageTitle = document.getElementById("stageTitle");
|
|
const stageLead = document.getElementById("stageLead");
|
|
const summaryGrid = document.getElementById("summaryGrid");
|
|
const methodGrid = document.getElementById("methodGrid");
|
|
const judgementList = document.getElementById("judgementList");
|
|
const metricList = document.getElementById("metricList");
|
|
const detailKicker = document.getElementById("detailKicker");
|
|
const detailTitle = document.getElementById("detailTitle");
|
|
const detailBody = document.getElementById("detailBody");
|
|
|
|
function renderStage(stageKey, methodIndex = 0) {
|
|
currentStage = stageKey;
|
|
currentMethod = methodIndex;
|
|
const stage = data[stageKey];
|
|
|
|
stageButtons.forEach((button) => {
|
|
button.classList.toggle("active", button.dataset.stage === stageKey);
|
|
});
|
|
|
|
heroMeta.innerHTML = stage.meta.map((item) => `<span class="chip">${item}</span>`).join("");
|
|
stageTitle.textContent = stage.title;
|
|
stageLead.textContent = stage.lead;
|
|
|
|
summaryGrid.innerHTML = stage.summaries.map(([label, text]) => `
|
|
<article class="summary-card">
|
|
<strong>${label}</strong>
|
|
<p>${text}</p>
|
|
</article>
|
|
`).join("");
|
|
|
|
methodGrid.innerHTML = stage.methods.map((method, index) => `
|
|
<article class="method-card ${index === methodIndex ? "active" : ""}" role="button" tabindex="0" data-method="${index}">
|
|
<span class="method-kicker">${method.kicker}</span>
|
|
<h4>${method.title}</h4>
|
|
<p>${method.short}</p>
|
|
</article>
|
|
`).join("");
|
|
|
|
judgementList.innerHTML = stage.judgements.map((item) => `<li>${item}</li>`).join("");
|
|
metricList.innerHTML = stage.metrics.map(([label, value]) => `
|
|
<div class="metric-row">
|
|
<span>${label}</span>
|
|
<strong>${value}</strong>
|
|
</div>
|
|
`).join("");
|
|
|
|
renderDetail(methodIndex);
|
|
bindMethodCards();
|
|
}
|
|
|
|
function renderDetail(methodIndex) {
|
|
const stage = data[currentStage];
|
|
const method = stage.methods[methodIndex];
|
|
detailKicker.textContent = `${stage.title} / ${method.kicker}`;
|
|
detailTitle.textContent = method.detailTitle;
|
|
|
|
const blockHtml = method.blocks.map(([title, text]) => `
|
|
<section class="detail-block">
|
|
<h4>${title}</h4>
|
|
<p>${text}</p>
|
|
</section>
|
|
`).join("");
|
|
|
|
const linkHtml = stage.links.length ? `
|
|
<section class="detail-block">
|
|
<h4>관련 산출물</h4>
|
|
<div class="links">
|
|
${stage.links.map(([label, href]) => `<a class="report-link" href="${href}">${label}</a>`).join("")}
|
|
</div>
|
|
</section>
|
|
` : "";
|
|
|
|
const cautionHtml = `
|
|
<section class="detail-block">
|
|
<h4>검토 상태</h4>
|
|
<p><span class="status">검토용 시안</span></p>
|
|
<div class="footer-line">
|
|
<span class="report-link">기존 리포트는 수정하지 않음</span>
|
|
<span class="report-link">내용 보강 가능</span>
|
|
</div>
|
|
</section>
|
|
`;
|
|
|
|
detailBody.innerHTML = blockHtml + linkHtml + cautionHtml;
|
|
}
|
|
|
|
function bindMethodCards() {
|
|
[...document.querySelectorAll(".method-card")].forEach((card) => {
|
|
const activate = () => {
|
|
currentMethod = Number(card.dataset.method);
|
|
[...document.querySelectorAll(".method-card")].forEach((item) => {
|
|
item.classList.toggle("active", item === card);
|
|
});
|
|
renderDetail(currentMethod);
|
|
};
|
|
|
|
card.addEventListener("click", activate);
|
|
card.addEventListener("keydown", (event) => {
|
|
if (event.key === "Enter" || event.key === " ") {
|
|
event.preventDefault();
|
|
activate();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
stageButtons.forEach((button) => {
|
|
button.addEventListener("click", () => renderStage(button.dataset.stage));
|
|
});
|
|
|
|
renderStage(currentStage);
|
|
</script>
|
|
</body>
|
|
</html>
|