Visual Summary 명세와 코드를 함께 읽는 수업
VISUAL SUMMARY · LEARNING NOTES

내용이 디자인에
들어가기까지

tier와 fallback, projection, I.I.S. 경계와 구조 대응을 하나의 예제로 연결한다. 마지막에는 실제 코드 흐름을 따라 읽고 50문항으로 이해를 확인한다.

입문자를 위한 설명코드 기준 2026.09.20정답과 해설 50개외부 연결 없이 학습 가능
사용자 내용형태가 바뀌어도 남겨야 하는 사실실험 · 4월 · 결과를 기록한다
Timeline의 내용 구조label: 실험 period: 4월 items: [결과를 기록한다]

날짜를 period에 둔다. 아래 선택을 바꾸면 fallback 도착지에서 P3이 하는 일을 볼 수 있다.

강의 6강의 고정 예시다. 실제 검색 엔진을 실행하는 화면은 아니다.

Visual Summary: 내용이 디자인에 들어가기까지

이 문서의 역할 Visual Summary를 처음 배우는 사람을 위한 강의자료다. 고등학생 수준의 독해를 전제로 용어, 명세, comp-frame 코드를 연결한다. 오너 미지정 · 상태 DRAFT · 최종 갱신 2026-09-20 SSoT는 여기가 아니다. 규칙은 스키마, capability, 기계 판독 표를 따른다. 같이 쓰는 자료 퀴즈 50문항과 해설 · 학습 페이지

0. 먼저 질문에 답한다

질문 답
tier는 YAML의 타입으로 찾는 것인가? tier 1이 그 역할이다. tier 전체는 검색할 카테고리를 우선순위로 묶은 것이다. 현재 코드는 tier 2에 정해진 fallback 카테고리, tier 3에 마지막 대안을 둔다
alternatives도 tier에 포함되는가? 명세에는 사용자 내용에 따른 대안으로 존재한다. 현재 comp-frame 검색은 무시한다. 나중에 어느 tier로 넣을지는 현재 구현으로 단정하지 않는다
fallback은 검색 결과가 없으면 비슷한 종류를 찾는 것인가? 대체로 맞다. 다만 비슷해 보이는 종류를 임의로 고르지 않고 정해진 순서를 따른다. 성립 조건 미충족이나 대치 가능한 후보 부족도 진행 이유가 된다
projection은 무엇인가? 다른 표현으로 바뀌어도 내용을 담을 수 있게 데이터 모양이나 대응을 바꾸는 작업이다. 카테고리를 바꿀 때 하는 structure projection과 표현 속성을 맞추는 property projection을 구분한다
IIS boundary와 property는 어떻게 연결되는가? 경계가 먼저 '어느 묶음이 무슨 종류인가'를 정한다. 그 경계를 대상으로 I.I.S. (Semantic Component Property)가 표현 속성과 구조 대응을 구한다
IIS property가 없으면 YAML이 안 들어가는가? 무엇이 없는지에 따라 다르다. 표현 속성 properties가 없는 것과 글상자 대응표 structure가 없는 것은 다르다. 현행 코드에는 대응표가 없을 때 규칙으로 Frame을 만드는 보완 경로도 있다

출처: 코드 C1, C3, C5, C7, 명세 5절과 6.1절.

읽는 순서

  1. 1~3강에서 YAML과 디자인의 차이를 이해한다.
  2. 4~6강에서 tier, fallback, projection을 구분한다.
  3. 7~9강에서 I.I.S.와 내용 대치를 연결한다.
  4. 10~12강에서 실제 코드와 예외를 확인한다.
  5. 퀴즈를 풀고 틀린 문항의 복습 절로 돌아온다.

예제의 학교 행사와 노드 번호는 설명용 가상 데이터다. 실제 검색 결과나 추출 정확도를 뜻하지 않는다.

1. Visual Summary는 무엇을 만드는가

사용자가 다음과 같이 입력했다고 하자.

과학 발표회는 3월에 주제를 정하고, 4월에 실험하며, 5월에 결과를 발표한다.

이 글에는 내용과 표현 선택이 함께 필요하다. 내용은 '주제 선정, 실험, 발표'와 각 시기다. 표현은 시간의 흐름이 보이는 Timeline으로 할 수 있다.

Visual Summary는 이 내용을 검색과 대치가 읽을 수 있는 구조로 적은 결과물이다. 완성된 이미지나 디자인 파일은 아니다. 글상자의 좌표와 글꼴을 전부 생성하는 문서도 아니다.

사용자 글
   ↓ 내용과 표현 선택을 구조화한다
Visual Summary YAML
   ↓ 내용을 받을 수 있는 디자인 후보를 찾는다
후보 디자인 + 어느 글을 어디에 넣을지 정한 대응
   ↓ 텍스트와 데이터를 대치하고 렌더링한다
화면에 보이는 결과

여기서 검색 성공과 대치 성공은 다르다. 'Timeline'이라는 이름이 맞아도 글상자가 두 개뿐이거나 글이 너무 길면 원하는 결과를 만들지 못한다. 그래서 실제 검색 코드는 후보에 내용을 맞춰 본 결과까지 검사한다. S1, C3, C6.

2. YAML을 한 줄씩 읽는다

version: "0.5.6"
title: 과학 발표회 준비
components:
  - type: TimelineSchedule.Timeline
    params:
      orientation: horizontal
    structure:
      entities:
        - label: 주제 선정
          period: 3월
          items: [조별로 주제를 정한다]
        - label: 실험
          period: 4월
          items: [결과를 기록한다]
        - label: 발표
          period: 5월
          items: [포스터를 발표한다]

YAML은 사람이 읽기 쉬운 데이터 표기법이다. 들여쓰기는 포함 관계를, -는 목록의 항목을 나타낸다. components는 여러 컴포넌트 블록의 목록이고, 이 예제에는 하나만 있다.

필드 읽는 말 예제에서의 뜻
version 어떤 규약으로 썼나 예제는 0.5.6 표기를 쓴다
title 이 문서의 제목은 무엇인가 과학 발표회 준비
type 어떤 종류로 표현하고 싶은가 Timeline
params 그 종류의 표현 방식을 어떻게 할까 가로 방향
structure 실제로 넣을 내용은 무엇인가 세 항목과 날짜, 설명
entities 항목들이 무엇인가 주제 선정, 실험, 발표
label 항목 이름 실험
items 항목의 상세 내용 결과를 기록한다
period 시기 4월

'4월'은 내용이고, '가로 방향'은 표현 선택이다. 날짜를 빼면 전달할 사실이 줄어든다. 가로 방향을 세로 방향으로 바꿔도 세 사건과 날짜는 그대로 적을 수 있다. 출처: 스키마 2절, Timeline capability.

type은 필수이지만 최종 결과를 강제하지 않는다

명세는 생성기가 type을 반드시 적도록 한다. 다만 검색에서 성립하지 않거나 쓸 후보가 없으면 fallback으로 바꿀 수 있다. '반드시 적는다'와 '반드시 그대로 쓴다'는 다른 규칙이다.

Hierarchy.Pyramid처럼 대분류와 중분류를 합친 값을 적는다. 명세상 Hierarchy만 적는 것은 올바른 type이 아니다. 코드 내부의 옛 호환 처리가 더 넓은 입력을 받아도 명세가 허용한다는 뜻은 아니다. 스키마 6절.

글 목록 외에 무엇을 적을 수 있나

구조 무엇을 표현하나 작은 예
entities 항목별 내용 주제 선정, 실험, 발표
entity.children 같은 내용 구조 안의 하위 항목 과학부 아래 물리팀과 화학팀
entity.components 항목 안에 별도 종류의 컴포넌트 팀별 카드 안에 각 팀의 Steps
dataset.shape: measure 단일 수치와 단위, 목표 등 참여율 80%
dataset.shape: matrix 열 정의와 행으로 된 데이터 반별 참여 인원 표
relations 항목 사이의 추가 관계 두 집합의 교집합 설명
columns entities로 표를 표현할 때의 열 정보 항목, 담당자, 기한

measure와 matrix는 수치 데이터의 서로 다른 형태다. '80%' 하나를 표현하는 것과 '1반 20명, 2반 25명, 3반 18명'을 비교하는 것은 다르다. relations는 항목 사이의 관계이며, 단순한 순서는 보통 배열 순서로 나타낸다. 모든 관계를 params에 적는 것이 아니다.

decoration은 내용과 별개로 꾸밈 선호를 나타낸다. ornament와 background에 prefer, indifferent, avoid를 쓸 수 있다. 배경을 피하고 싶다는 뜻과 기존 디자인에 실제 배경이 있다는 사실도 구분한다. 스키마 2절과 2.7절.

카테고리와 그림의 겉모양도 구분한다

같은 Steps라도 가로로 놓거나 세로로 놓을 수 있다. RLSC의 HStack과 VStack은 물리 배치이고, 의미 카테고리와 같지 않다. 또한 차트처럼 보이는 도형 묶음과 실제 데이터가 연결된 차트 요소는 다르다. 기존 그림의 글을 바꿀 수 있어도 수치에 맞춰 막대 길이를 바꿀 수 있다는 뜻은 아니다.

그래서 스키마 어휘에 있는 카테고리와 생성기에 허용한 카테고리는 다를 수 있다. 예를 들어 capability의 generatable 제약은 현재 대치로 지원할 수 있는 범위를 고려한다. 전체 어휘를 외우기보다 해당 카테고리의 성립 조건과 생성 허용 여부를 확인한다. capability 10절.

3. 사용자 내용과 기존 디자인은 서로 다른 데이터다

검색에는 두 쪽의 정보가 필요하다.

사용자 내용 쪽 기존 디자인 쪽
넣고 싶은 글과 숫자 실제 글상자와 표, 도형
type: 원하는 종류 categories: 추출한 종류
params: 원하는 표현 속성 properties: 디자인에서 확인한 표현 속성
structure: 글과 수치 자체 I.I.S.의 structure: 어느 노드가 어떤 필드인지 나타내는 구조 대응
예: label: 실험 예: label: text-21

같은 structure라는 이름이라도 오른쪽은 내용이 아니라 내용을 넣을 위치에 대한 정보다.

text-21은 기존 디자인의 글상자를 구별하는 식별자다. 실제 시스템에서는 RLSC 노드 ID와 디자인의 sheet 요소 ID를 구분하기도 한다. RLSC는 기존 디자인의 배치와 역할을 트리로 나타낸 데이터다. 화면에서 사각형으로 보이는 글상자 하나를 프로그램이 정확히 지정하려면 이 식별자가 필요하다.

현재 comp-frame은 두 쪽을 공통 형식인 Frame으로 바꾼다. 질의 Frame은 슬롯에 넣을 글을, 컴포넌트 Frame은 슬롯의 노드 위치와 글 용량을 담는다. '슬롯'은 여기서 특정 종류의 내용을 받을 자리다. 용어 SSoT, C2, C5.

4. tier는 검색할 카테고리의 우선순위 묶음이다

현행 buildTiersFor를 기준으로 보면 다음과 같다.

군 어떤 카테고리를 넣나 주의할 점
tier 1 YAML의 type 중 성립하는 것 현재 alternatives는 넣지 않는다
tier 2 원래 type의 fallback 행에 적힌 카테고리 중 변환 후 성립하는 것 원래 행의 순서를 유지한다
tier 3 내용 모양으로 고른 마지막 대안 하나 앞 군에 있으면 중복으로 추가하지 않는다

마지막 대안은 List, LongFormTextLayout, HighlightedMessage 중 내용에 맞는 하나다. 보통 항목이 둘 이상이면 List, 하나이고 여러 문장이면 LongFormTextLayout, 하나이고 한 문장이면 HighlightedMessage를 고르는 규칙이다. dataset만 있는 입력 등의 세부 처리는 terminalCategory와 성립 검사를 함께 봐야 한다.

학교 행사 예제

원래 type은 Timeline이고 항목은 세 개다. capability에 있는 Timeline의 fallback 행은 다음과 같다.

Timeline → Steps → Staircase → List

각 후보가 성립한다면 다음 검색 군이 만들어진다.

tier 1: Timeline
tier 2: Steps, Staircase, List
tier 3: 없음 (마지막 대안 List를 tier 2에서 이미 포함했다)

따라서 tier 2가 fallback 한 번, tier 3가 fallback 두 번이라는 뜻은 아니다. tier 2 안에도 여러 카테고리가 있다. 어떤 입력에서는 tier 1 자체가 없을 수 있다. C1, capability 3.2절.

alternatives의 현재 위치

명세 6.1절의 alternatives는 생성기가 이번 내용을 보고 고른 대안이다. capability fallback은 카테고리별로 미리 정해 놓은 순서다. 명세는 alternatives를 정적 fallback보다 먼저 참고하도록 설명한다.

반면 현재 comp-frame은 alternatives를 파싱할 수 있지만 검색 군을 만들 때 읽지 않는다. 해당 코드에는 2026-09-10 사용하지 않기로 한 지시가 기록돼 있다. 문서에 필드가 있다는 것, 코드가 읽는다는 것, 나중에 넣을 예정이라는 것은 별개의 주장이다. 이번 자료는 미래 구현 순서를 확정하지 않는다. 스키마 6.1절, C1.

5. fallback은 왜 필요한가

fallback은 원하는 표현을 사용할 수 없을 때 정해진 다른 표현을 시도하는 과정이다. 이유를 세 가지로 나누면 이해하기 쉽다.

상황 예 어디에서 판단하나
내용이 그 종류의 성립 조건을 못 채운다 항목 하나로 Steps를 요청한다. Steps는 둘 이상이다 capability 성립 검사
조건은 맞지만 검색 풀에 쓸 디자인이 없다 세 단계 요청은 맞지만 대응하는 후보 공급이 없다 후보 검색
후보는 있지만 실제 대치가 부적합하다 세 항목은 맞지만 글이 넘치거나 내용이 손실된다 align, 용량 검사, 속성 정책

현행 코드는 실제 검사를 통과한 후보 수로 다음 tier까지 갈지 판단한다. DB에서 100개를 받았어도 전부 탈락하면 충분한 후보를 확보한 것이 아니다. 반대로 tier 1 후보가 몇 개 있더라도 중단 기준에 모자라면 tier 2를 찾아볼 수 있다.

기본값은 min_candidates: 500, tier_targets: [50, 50]이다. 이 값은 명세의 의미 규칙이 아니라 현재 코드의 검색 설정이다. 예를 들어 tier 1 처리를 마친 시점에 통과 후보가 20개라면 기본 하강 기준 50개에 못 미친다. 다음 tier를 시도할 수 있다. 정확히 50개만 조회한다는 뜻은 아니다. 한 tier 안의 조회가 끝나거나 다른 중단 조건에 걸린 뒤 하강 여부를 판단한다. C3, C8.

'유사 카테고리'는 느낌으로 고르지 않는다

원래 type의 fallback 행을 왼쪽부터 소비한다. Timeline에서 Steps를 시도했다고 해서 Steps 행으로 갈아타는 식으로 계속 확장하지 않는다. '얼핏 비슷함'만으로 표를 게이지로 바꾸지도 않는다. 예를 들어 measure와 matrix 사이에는 임의 변환 규칙을 두지 않기로 했다. 여러 수치를 하나로 줄이면 정보를 잃기 때문이다. C1, capability 3~4절.

6. projection은 표현이 바뀔 때 내용을 어디로 옮길지 정한다

Timeline을 Steps로 바꾼다고 하자. type 이름만 바꾸면 끝날까?

Timeline은 period를 읽지만 Steps는 날짜 전용 필드를 읽는다고 선언하지 않았다. 그래서 아래처럼 이름만 바꾸면 날짜가 사용되지 않을 수 있다.

# 바꾸기 전, Timeline의 내용
label: 실험
period: 4월
items: [결과를 기록한다]

structure projection은 날짜를 읽을 수 있는 자리로 옮긴다.

# Steps로 가는 projection 뒤의 내용, 설명용 예시
label: 실험
items:
  - 결과를 기록한다
  - "period: 4월"

날짜 전용 배치는 포기했지만 '4월'이라는 사실은 남았다. fallback은 'Steps도 시도하자'라는 선택이고, projection은 'Steps가 받을 수 있게 날짜를 items에 옮기자'라는 변환이다. capability 3.4절, C4.

structure projection 규칙

규칙 쉬운 설명 보존하는 것 또는 포기하는 것
P1 항목과 그 순서를 기본적으로 유지한다 기본 내용 구조
P2 도착 카테고리가 읽지 않는 params를 제거한다 사용할 수 없는 표현 선택을 포기한다
P3 읽지 못하는 내용 필드를 items의 글로 옮긴다 날짜, 작성자, 하위 내용 등의 사실
P4 표 데이터를 행별 항목과 설명으로 바꾼다 행과 열의 내용을 텍스트로 남긴다
P5 단일 수치 데이터를 entity의 수치와 단위로 옮긴다 값과 단위
P6 항목과 설명을 표의 행과 열로 바꾼다 항목별 내용
P7 그릴 수 없는 관계의 설명을 관련 항목으로 옮긴다 관계가 가진 텍스트 정보

규칙들은 적용 조건에 따라 함께 작동한다. P1이 있다고 해서 'children은 어떤 도착지에서도 절대 변환하지 않는다'로 읽으면 안 된다. 읽지 못하는 필드에 대한 P3 등이 함께 적용된다.

P8은 폐기됐다. 카테고리의 개수 상한을 넘는 항목을 마지막 항목에 억지로 접어 그 카테고리를 성립시키는 규칙이다. 현재 명세는 그 카테고리를 탈락시키고 다른 후보를 시도한다. 후단 align에서 상세 글을 한 슬롯에 합치는 것까지 전부 금지했다는 뜻은 아니다. capability 3.4절, C6.

property projection은 무엇이 다른가

structure projection이 내용 형태를 바꾸는 동안, property projection은 요청한 표현 속성과 후보의 표현 속성을 맞추는 문제를 다룬다.

예를 들어 요청한 Staircase의 direction이 ascending이고 후보가 descending이면, 그 종류에서 정의한 순서 의미에 맞춰 채움 순서를 뒤집는 방안을 검토할 수 있다. 반대로 가로 배치를 세로 배치로 만드는 것은 순서만 뒤집어서 해결되지 않는다. 모든 속성 차이에 같은 처리를 할 수 없다.

현행 코드의 속성 판정부에는 다음 정책이 있다.

정책 판정부의 동작
exclude 명시적인 속성 충돌이 있으면 후보를 제외한다
reorder 정의된 반대 순서 쌍이면 reverse 표시를 만들고, 나머지 충돌은 결함으로 센다
ignore 후보를 유지하면서 충돌을 결함으로 센다

판단과 실제 대치 실행은 별도 단계다. 읽은 로컬 경로에서는 costOf가 reverse를 계산하고 rankCandidates가 결과에 넣지만, align은 그 전에 실행되며 nodeTextMapFromResult는 binding에 이미 들어 있는 글을 사용한다. 이 경로에서 reverse를 실제로 적용하는 연결을 확인하지 못했다. 따라서 '순서 수정이 화면까지 완료된다'고 가르치지 않는다. 원격 서버의 실제 동작과 배포 상태는 이번 확인 범위 밖이다. C6, C7, C9.

같은 단어의 다른 사용도 있다

projectForBoundaryExtraction의 projection은 LLM이 경계를 판단할 때 필요한 RLSC 정보만 남기는 입력 정리다. fallback 변환과 다르다. 함수 이름에 projection이 있으면 입력, 출력, 호출 위치부터 확인한다. C7.

7. I.I.S. boundary가 알아내는 것

기존 디자인에 다음 글상자가 있다고 하자.

group-A
 ├─ text-11: 준비
 ├─ text-12: 계획을 세운다
 ├─ text-21: 실행
 ├─ text-22: 행동한다
 ├─ text-31: 점검
 └─ text-32: 결과를 본다

I.I.S. (Semantic Component Boundary)는 이 중 어느 노드 묶음이 하나의 의미 컴포넌트이며 어떤 카테고리에 해당하는지 판정한다. 예를 들어 'group-A가 Steps 경계'라는 결과를 낼 수 있다.

경계는 화면의 사각형 좌표만 뜻하지 않는다. 이 문맥에서는 RLSC의 어느 노드 또는 묶음까지 한 컴포넌트로 볼지에 대한 정보다. 하나의 디자인 안에 부분 경계가 여러 개 있을 수도 있다.

하지만 이것만으로는 text-11이 항목 이름인지 설명인지 확정되지 않는다. 경계가 정해졌다고 내부 모든 글상자의 의미 역할까지 정해진 것은 아니다. 용어 SSoT, C7.

8. I.I.S. (Semantic Component Property)가 더 알아내는 것

이 국면의 이름 'Property'는 넓은 뜻이다. 결과는 두 종류다.

산출물 질문 내용
properties 이 디자인은 어떤 표현 속성을 갖는가? 예: marker가 number인가
structure 각 항목의 label과 items는 어느 노드인가? 예: 첫 항목 label은 text-11, items는 text-12

학습용으로 단순화한 구조 대응은 다음과 같다. 실제 DB 직렬화 전체를 그대로 복사한 예시는 아니다.

node_id: group-A
categories: [ProcessFlow_Steps]
properties:
  marker: number
structure:
  entities:
    - id: group-step-1
      fields:
        label: text-11
        items: [text-12]
    - id: group-step-2
      fields:
        label: text-21
        items: [text-22]
    - id: group-step-3
      fields:
        label: text-31
        items: [text-32]

이제 새 내용의 첫 label '주제 선정'을 text-11에, 첫 설명을 text-12에 넣을 수 있다.

Boundary: group-A는 Steps다
       ↓ 그 경계의 내부를 해석한다
Property 국면: 표현 속성 + 노드별 구조 대응을 구한다
       ↓ 대응표와 용량을 정리한다
Component Frame: label 자리, items 자리, 각 노드 ID와 용량
       ↓ 새 내용과 맞춘다
binding: text-11 ← 주제 선정

이 연결에서 내용 대치에 직접 필요한 것은 구조 대응과 실제 슬롯이다. properties.marker만으로는 '실험'을 어느 글상자에 넣어야 하는지 알 수 없다. C2, C5.

9. 'property가 없으면 안 들어간다'를 다섯 경우로 나눈다

9.1 카테고리가 그 내용 필드를 읽는다고 선언하지 않았다

예를 들어 Steps는 period 전용 필드를 읽지 않는다. 이는 I.I.S. 추출이 빠졌다는 말과 다르다. 카테고리의 규칙 문제다.

처음부터 Steps를 생성한다면 날짜가 필요한 경우 '3월에 주제를 정한다'처럼 items에 적는다. Timeline에서 Steps로 fallback한다면 P3이 period를 items로 옮긴다. 처음부터 Steps로 쓰는 경로에는 fallback projection이 자동으로 구제해 준다는 보장이 없다. 생성기가 읽지 않는 필드에 내용을 넣지 않아야 하는 이유다. capability 1절 B-33.

9.2 표현 속성 properties만 없다

구조 대응이 있고 properties만 비었다면 글상자 위치는 알 수 있다. 코드에서 속성 누락은 missingOnComponent로 구분하고, 값이 다르다는 명시적 충돌로 취급하지 않는다. properties가 비었다는 사실만으로 YAML 내용 대치 전체를 막지 않는다. C7.

9.3 IIS 구조 대응이 없다

명시적인 노드별 역할 정보가 없어서 어디에 넣을지 판단하기 어렵다. 하지만 현재 적재 코드는 IIS Frame을 만들 수 없으면 buildRuleFrame으로 보완한다. RLSC의 반복 단위와 역할로 슬롯을 만들 수 있기 때문이다. 모든 의미를 정확히 복원한다는 보장은 없고, Frame의 출처를 남긴다. C5.

9.4 일부 글상자만 슬롯에 잡히지 않았다

그 글상자는 사용자 글과의 대응이 정해지지 않은 자리다. 글이 보인다는 이유만으로 임의의 사용자 문장을 넣으면 제목, 날짜, 설명의 역할이 뒤섞일 수 있다.

로컬 대치 경로는 다음을 구분한다.

상태 처리
binding 해당 노드에 사용자 글을 넣는다
blank Frame 슬롯은 있지만 채울 글이 없다. 보존 예외가 아니면 임시 문구를 넣는다
preserve 원래 내용을 유지한다
inside_unbound_text 경계 안인데 Frame 슬롯에 안 잡힌 글이다. 일반적으로 임시 문구 대상이며 원문 보존 예외가 있다
outside_text 선택된 경계 밖 글이다. 일반적으로 원문을 유지한다

경계 밖에 제목을 넣을 수 있는 별도 후보가 있고 경계 안에서 제목을 채우지 못했다면, 문서 제목을 바깥 노드 하나에 넣는 예외가 있다. 그러므로 'IIS가 안 찍은 글은 전부 원문 그대로'나 '전부 빈칸'도 정확한 설명이 아니다. C9.

9.5 새 내용에 맞는 전용 슬롯이 없다

새 내용의 날짜 전용 슬롯은 없지만 items 슬롯은 있을 수 있다. align은 필드를 items 등 받을 수 있는 슬롯으로 옮기는 처리를 한다. 반면 정말 담을 곳이 없어 contentLoss > 0이 되면 현재 랭커는 그 후보를 제외한다. '매핑을 못 했으니 그 글만 조용히 버린다'가 의도된 성공 경로는 아니다. C6.

핵심 이유는 전달할 내용의 역할과 넣을 위치를 정확히 연결해야 하기 때문이다. property라는 단어 때문에 막는 것이 아니라, 프로그램이 어떤 내용을 어느 노드로 보낼 수 있는지 판단할 근거가 필요하다.

10. comp-frame 코드를 실제 순서로 읽는다

10.1 미리 준비하는 색인 경로

기존 디자인 + RLSC + I.I.S. 결과 + 글 용량
  → buildIisFrame
  → 실패하거나 구조 대응이 없으면 buildRuleFrame
  → Frame과 검색용 요약 필드, 잔여 텍스트 메타데이터를 저장

buildIisFrame은 node_id로 경계를 찾고, 카테고리에서 허용한 필드를 대상으로 노드 참조를 슬롯으로 바꾼다. properties는 Frame의 params 쪽에 담긴다. 일부 누락된 반복 단위를 규칙으로 보완하는 처리도 있다. 이것은 'YAML을 받아 그때마다 IIS를 새로 수행한다'는 흐름과 다르다. C5.

10.2 요청마다 실행하는 검색 경로

단계 주요 코드 쉬운 설명
1. 입력 해석 parseVisualSummaryInput.ts YAML 또는 JSON을 읽고 규약 형태를 검사한다
2. 문서 분리 documentSearch.ts, nestedSearch.ts 최상위 블록을 독립 처리하고 중첩이면 N1~N5 순서를 적용한다
3. 검색 군 준비 buildTiersFor type, fallback 행, 마지막 대안을 성립 검사한다
4. 질의 Frame 생성 buildQueryFrames, summary-projection.ts 도착 카테고리에 맞는 내용과 슬롯 형태를 만든다
5. 후보 조회 searchFrames, filter.ts 카테고리, 모양, dataset, 용량 등으로 후보를 가져온다
6. 대응 계산 align 새 내용과 실제 슬롯을 맞추고 binding, 빈 슬롯, 보존 등을 나눈다
7. 적합성 검사와 순위 rankCandidates, costOf 용량, 내용 손실, 속성 충돌 등을 확인하고 후보를 정렬한다
8. 대치 지도 nodeTextMapFromResult 디자인 요소 ID마다 넣을 글을 정한다
9. 표시 FrameThumbnail 등 실제 디자인에 대치를 적용해 결과를 보여 준다

검색에서 align을 미리 돌리는 이유는 카테고리 이름만 맞고 글은 못 넣는 후보를 성공으로 세지 않기 위해서다. C1~C10.

10.3 코드에서 구분해야 할 숫자 셋

이름 의미 예
tier 1~3 검색할 카테고리의 우선순위 군 type, fallback, 마지막 대안
stage 0~2 같은 카테고리 안에서 Frame 모양 조건을 넓히는 단계 shape bits 차이 0, 1, 2
N1~N5 중첩된 컴포넌트 구조를 어떤 순서로 변환할지 구조 유지, 부모 벗기기, 평탄화

shape bits는 'label이 있나, items가 있나' 같은 특징의 존재 여부를 압축한 검색용 값이다. stage 0은 모든 글과 개수, 시각 배치가 완벽히 동일하다는 보장이 아니다. 후속 검사를 따로 한다.

stage를 내려도 안 풀리는 비트가 있다. 소프트닝 대상은 앞쪽 17개뿐이라, dataset(measure·matrix)과 extraGrids 비트는 하드 필터로 끝까지 남는다. 표·차트 데이터를 요구하는 질의가 stage 2까지 내려가도 데이터가 없는 후보는 절대 올라오지 않는다는 뜻이다.

stage 2는 '2비트 이하 차이'가 아니라 정확히 2비트 차이이고, stage 1의 패턴을 빼고 만든다. 안 빼면 같은 후보를 stage 1과 2에서 두 번 센다. C3, C10.

10.4 결과는 무조건 tier 1부터 나오나

현재 기본값은 rank_mode: tier_pool과 clean_first: above_tier다. 코드가 계산한 unauthored_count가 0인 결과를 tier보다 먼저 고려한다. 그래서 이미 조회된 후보끼리 비교할 때, 잔여가 없는 tier 2 결과가 잔여가 있는 tier 1 결과보다 먼저 나올 수 있다.

기본 비교 순서는 대략 다음과 같다.

unauthored_count가 0인가
→ tier
→ fallback 행에서의 순서
→ 꾸밈 선호 일치
→ 감점 cost
→ 동률 해소 규칙

여기서 '온전함'은 이 코드가 센 잔여 기준이지 사람이 보기에 무조건 아름답고 의미가 정확하다는 증명은 아니다. 또한 정렬은 이미 조회한 후보를 대상으로 한다. tier 1에서 검색이 멈췄다면 아직 조회하지 않은 tier 2 디자인을 정렬이 새로 찾지는 않는다.

같은 Frame의 형제 중에서는 온전한 쪽을 대표로 세운다. 지문이 같은 후보 묶음에서 먼저 만난 것이 아니라 잔여가 0인 것을 고르고, 대표가 바뀌면 다시 정렬한다. 이 처리가 없으면 자리는 값싼 형제가 벌어 놓고 화면에는 오염된 형제가 뜬다.

왜 잔여를 tier보다 먼저 보나 — 실측 하나

2026-09 평가에서 잰 값이 근거다. 대치 후에도 사용자 글이 아닌 자리가 남는 것이 judge 판정에는 영향을 주지 않는데(오즈비 1.012, p = 0.878) 사용자 선택에는 영향을 준다(오즈비 0.688, p = 0.022 — 고를 확률이 약 31% 낮다). 컴포넌트가 글에 안 맞아서 생긴 착시인지 의심해 보정해도 0.663으로 남았다.

그래서 남은 자리를 줄이는 이유는 judge 점수가 아니라 카드 채택률이다. 이 수치는 규약이 아니라 도입 판단용 실측이고, 새 경로 수치는 자리째 비는 버그가 있는 상태에서 잰 것이라 고치면 달라질 수 있다. 평가 자료, C6, C8.

10.5 결과 하나가 페이지 하나라는 전제가 깨졌다

Content Threading은 내용이 한 장에 안 들어갈 때 여러 장에 이어서 담는 기능이다. 그래서 검색 결과 하나가 페이지 여러 장을 소유할 수 있다.

알아 둘 것 내용
적용 범위 지금은 두 카테고리뿐이다 — Enumeration.List와 QnACard.QnACard
결과의 모양 결과에 threading.pages와 unit_count가 붙는다
위쪽 슬롯 설명 binding·blank 목록은 그 결과의 첫 페이지만 설명한다
함께 꺼지는 것 threading 결과는 경계 밖 제목 구제와 폰트 축소를 둘 다 끈다

'결과 = 페이지 하나'로 외우면 두 번째 장부터의 글이 어디서 왔는지 설명할 수 없다. C11.

10.6 코드가 네 군데에 나뉘어 있다

이름이 비슷해서 헷갈리기 쉽다. packages/comp-frame이라는 디렉터리는 없다.

자리 무엇이 사는가
src/comp-frame/ 질의 측 알고리즘 코어. tier 생성, 필터, 검색, align, 점수, 결과 지도
packages/comp-frame-shared/ 공유 기반 — Frame 스키마, shape 비트, 벤더 코드, forked/
packages/comp-frame-index/ 적재 배치. 검색 테이블을 만드는 쪽
packages/comp-frame-client/ 배포용 패키지. 원격 검색과 대치 페이로드

forked/는 comp-sign·comp-match를 직접 import 하지 않기 위해 필요한 조각을 복사해 둔 자리다. 줄 단위 문자열 검사를 하는 테스트가 네 군데에서 이 금지를 지킨다. 다만 문자열 검사라 만능은 아니고, 벤더 eregion/ 디렉터리는 검사에서 빠진다. C12.

11. 여러 블록과 중첩: L1, L2, L3

레벨 허용하는 구조 예
L1 단일 컴포넌트 Timeline 하나
L2 최상위 컴포넌트 여러 개 Timeline 옆에 List
L3 entity 안에 다른 컴포넌트도 중첩 비교표의 한 항목 안에 Steps

이는 생성기에 허용한 복잡도 범위다. L3를 허용해도 모든 생성물이 꼭 중첩이어야 하는 것은 아니다. entity.children은 항목의 계층이고 entity.components는 별도 컴포넌트의 중첩이므로 구분한다.

세 레벨은 누적이다. L1 ⊂ L2 ⊂ L3이고, 산출물 한 건의 모양이 아니라 생성기에 허용된 범위의 이름이다. 가르는 축은 하나다 — 컴포넌트 경계를 넘는 계층 entity.components[]를 쓰느냐. entity.children[]은 어느 레벨에서나 쓴다.

생성기가 둘이고 레벨이 다르다. 실서비스 Summary 생성기는 L2까지, PoC 생성기는 이미 L3다. 그래서 'L3는 자리만 열려 있다'는 설명은 생성 쪽에는 더 이상 맞지 않는다. 비어 있는 것은 오케스트레이션(I-11)과 중첩 후보 대치 로직(D-7) 두 칸이다. 스키마 8.8절.

최상위 components가 둘인 L2는 이미 경계가 나뉜 상태다. 각각 검색한다. L3 중첩에서 원하는 전체 디자인을 못 찾으면 다음을 시도한다.

순서 동작
N1 중첩 구조와 카테고리를 그대로 찾는다
N2 구조를 유지하면서 노드의 카테고리를 fallback한다
N3 가장 바깥 부모를 벗기고 자식을 위로 올려 다시 찾는다
N4 필요한 만큼 부모 벗기기를 반복한다
N5 중첩 내용을 글로 평탄화한다

각 단계에는 이름이 붙어 있다 — N1 ExactMatch: Structure&Semantic, N2 ExactStructure-SemanticFallback, N3 Root-Peeling, N4 Re-Peeling, N5 Content Flattening. 아래로 갈수록 잃는 것이 는다. N2는 노드마다의 카테고리 표현을, N3·N4는 벗긴 부모의 표현과 형제 인접성을, N5는 자식 전부의 카테고리 표현을 잃는다.

반복 상한은 두지 않는다. N3·N4를 되풀이해 최상위 컴포넌트가 늘어도 배치 엔진이 페이지를 나눈다.

이 사다리를 실제로 쓰는 것은 L3뿐이다. L2는 최상위 원소마다 검색이 독립으로 돌 뿐 이 절을 쓰지 않는다.

N1은 지금 실제로는 언제나 비어 있다. D-7이 이종중첩 템플릿을 검색 풀에서 뺐기 때문에 후보가 0이다. 그래서 이종 질의는 실질적으로 N3부터 시작한다. 되돌릴지는 D-8로 열려 있다. 사다리에 N1이 적혀 있다는 것과 N1이 후보를 낸다는 것은 다른 주장이다.

N2에서 조합은 강등 칸수 합이 작은 것부터 시도한다. 동률이면 더 깊은 노드부터 강등한다. 부모 표현을 바꾸는 영향이 더 클 수 있기 때문이다. 형제는 큰 연속 묶음부터 찾고 순서를 유지한다.

Hierarchy 부모는 N3를 건너뛰고 N5로 간다. 부모를 벗기면 회사, 부서, 직원의 소속 관계가 사라질 수 있기 때문이다. 평탄화는 들여쓴 글로 그 관계를 남긴다. 현재 iui에는 nestedSearch.ts가 있으므로 과거 위키의 '오케스트레이션 미구현'을 그대로 현재 설명으로 쓰면 안 된다. capability 3.6절, C10.

12. 한 사례를 처음부터 끝까지 설명한다

상황

사용자는 2강의 Timeline YAML을 냈다. 설명을 단순하게 하려고, Timeline과 Steps에는 대치 가능한 후보가 없고 Staircase도 없으며 List에서 쓸 후보를 찾았다고 가정한다. 실제 DB 조회 결과가 아니다.

순서 일어난 일 지켜야 할 점
1 type Timeline과 세 항목을 읽는다 type과 내용은 별개다
2 tier 1 Timeline, tier 2 Steps, Staircase, List를 준비한다 각 도착지는 성립 검사를 통과해야 한다
3 후보 카테고리별 projection을 만든다 이름만 바꾸지 않는다
4 Timeline을 조회하고 대치 적합성을 확인한다 조회 건수와 통과 후보 수는 다르다
5 다음 카테고리를 정해진 순서로 시도한다 Steps 행으로 갈아타지 않는다
6 List용 내용에서 날짜를 items로 보존한다 세 사건, 세 날짜가 모두 남아야 한다
7 List 후보의 Frame 슬롯에 새 내용을 맞춘다 실제 노드 ID가 필요하다
8 글 용량과 내용 손실 등을 확인한다 같은 List라도 전부 통과하지 않는다
9 binding에 따라 디자인의 글을 바꾼다 슬롯 밖 글은 별도 정책으로 처리한다

완료 후에는 세 가지를 따로 확인한다. 내용이 보존됐는가, 표현이 요청과 얼마나 달라졌는가, 실제 글상자에 잘 들어갔는가. 첫 번째가 맞다고 두 번째와 세 번째도 맞는 것은 아니다.

13. 명세와 현재 코드가 다른 곳

주제 명세 또는 과거 문서 이번에 확인한 로컬 구현
alternatives 정적 fallback보다 먼저 참고한다 검색 군 생성에서 무시한다
property 처리 과거 모듈 지도에는 T-8 미구현이라고 적혀 있다 비교, 정책, reverse 계산이 존재한다. 로컬 대치까지 reverse를 적용하는 연결은 확인되지 않았다
중첩 검색 과거 위키에는 조립 코드 미구현이라는 기록이 있다 nestedSearch.ts에 N1~N5가 있다
IIS 구조 대응 없음 'property가 없으면 대치 불가'로 요약하기 쉽다 규칙 기반 Frame 보완 경로가 있다
마지막 fallback 규약은 성립 가능한 마지막 표현을 둔다 실제 검색은 공급, 필터, 용량 때문에 결과가 비어 있을 수 있다
화면 위치 옛 자료는 CompFrame.tsx를 안내한다 /comp-frame은 VisualSummary.tsx를 사용하고 local/remote backend를 고른다. CompFrame.tsx는 삭제됐고 CompFrame.module.css만 남아 헷갈리기 쉽다
규칙 수 일부 옛 제목은 P1~P8이라고 적혀 있다 현행 capability 본문에서 P8은 폐기됐다. 살아 있는 규칙은 일곱이다. 3.4절 제목이 아직 "여덟 확정 + 둘 제안"으로 남아 있어 제목만 읽으면 열로 센다
N1 중첩 사다리 첫 칸으로 적혀 있다 후보가 0이라 실제로는 언제나 비어 있다. 이종중첩 템플릿이 검색 풀에서 빠져 있다. 이종 질의는 실질 N3부터다
L3 생성 '자리만 열려 있다'로 읽기 쉽다 PoC 생성기는 이미 L3를 낸다. 실서비스 생성기가 L2까지다. 비어 있는 것은 오케스트레이션과 중첩 후보 대치 쪽이다
alternatives를 읽는 코드 '코드에 없다'로 줄여 말하기 쉽다 벤더 fallback 코드에는 읽는 자리가 남아 있다. 다만 검색 경로가 그 함수를 부르지 않는다. 화면 툴팁도 아직 'type/alternatives'라고 적어 두었다
결과와 페이지 결과 하나가 페이지 하나 Content Threading은 결과 하나가 여러 장을 소유한다. 적용은 Enumeration.List·QnACard.QnACard 둘뿐이다
감점 항목 수 옛 자료는 16개라고 적기도 한다 현재 20개다. 그중 일곱은 2026-09-15 전수 실측으로 조정됐고 나머지 열셋은 초기값 그대로다
uuid 없는 결과 '검색이 거부한다'로 읽기 쉽다 거부는 배포 패키지 경계 한 자리다. 로컬 검색은 uuid: null인 결과를 그대로 돌려준다. 진짜 방어선은 적재와 DB 제약이다
검색 테이블 옛 자료는 comp_frames_v2 현재는 comp_frames다

마지막 fallback의 '성립 가능'을 'DB에 무조건 적합한 실제 디자인이 존재'로 해석하지 않는다. 또한 이 자료는 로컬 코드 독해 결과다. 운영 DB의 모든 행이 최신 IIS와 Frame으로 재적재됐는지, 원격 서버가 같은 버전인지, 실제 배포가 완료됐는지는 검증하지 않았다.

14. 코드와 문서 출처

이 판은 2026-09-20에 세 저장소의 origin/main을 읽어 고쳤다.

저장소 기준 커밋 비고
mordor-wiki (규약) fad1e30 로컬 체크아웃도 같은 커밋으로 맞췄다
miricanvas-iui (코드) d1d2ad0eb git show origin/main: 으로 읽었다
aippt-prisonbreak 202c9b21c 규약은 이 저장소의 docs 서브모듈로도 들어온다

주의할 것이 하나 있다. 아래 코드 링크는 상대 경로라 여러분 컴퓨터의 작업 트리를 연다. 이 문서를 쓴 시점의 iui 체크아웃은 작업 브랜치 jhhong-adoptive-fix에 있었고 미커밋 UI 변경이 있었다. 본문 설명은 origin/main 기준이고 링크가 여는 파일은 그것과 다를 수 있다. 링크와 설명이 어긋나면 git show origin/main:<경로>로 확인한다.

이전 판(2026-09-20 이전)은 wiki bbabab9, iui db0b7ef13 기준이었다. 질의 측 알고리즘 코어(align·query-frame·filter·search·nestedSearch·summary-*)는 그 사이 한 줄도 바뀌지 않았다. 달라진 것은 그 바깥 껍데기와 규약 바깥의 현황이다.

명세

  • S1. schema-v0.5.md: 2절 필드, 5절 fallback, 6.1절 alternatives, 8.8절 레벨.
  • S2. capability-table.md: 1절 읽기와 생성 의무, 2절 성립 조건, 3.2절 fallback 행, 3.4절 projection, 3.6절 중첩.
  • S3. 용어와 두 가지 property: params/properties, 내용 구조/구조 대응의 구분. 구현 현황과 일부 버전 표기는 과거 기록이므로 코드와 구분해서 읽는다.
  • S4. 내용 대치 contract: 용어와 설계 배경. 현재 구현 여부는 아래 소스를 우선 확인한다.

C1. 검색 군과 질의 Frame

query-frame.ts: buildTiersFor, buildQueryFrames. alternatives 무시, 각 도착지 성립 검사, 자기 fallback 행 사용, tier 3 중복 제거.

C2. Frame 공통 형식

frame.ts: Frame, Unit, Slot, NodeRef. 질의에는 글, 후보에는 노드와 용량을 담는다.

C3. 후보 조회와 중단

search.ts: searchFrames, admittedCount, tier 하강. filter.ts: buildCandidateFilter, stage별 shape 조건.

C4. 내용 projection

summary-projection.ts: projectStructure. 벤더 projection.ts: P1~P7 기반 변환. summary-content.ts: 내용 보존과 마지막 카테고리 선택.

C5. IIS 결과에서 실제 Frame으로

component-frame.ts: buildIisFrame, buildRuleFrame. build-comp-frames.ts: IIS Frame 실패 시 규칙 기반 Frame을 호출하는 분기.

C6. 대응과 점수

align.ts: 슬롯 대응, 필드 이동, 보존, 내용 손실. rank.ts: rankCandidates, compareResults. cost.ts: 속성 정책과 감점. capacityGate.ts: 최종 대치 텍스트의 용량 검사.

C7. IIS 용어와 속성 비교

iisSemanticComponent.ts: 경계 입력 정리와 구조 대응 검사. propertyMatch.ts: compareProperties, planForPolicy, ORDER_REVERSIBLE_PARAMS, missingOnComponent.

C8. 기본 옵션

types.ts: DEFAULT_OPTIONS, CleanFirst, RankMode. 값은 운영 설정에 따라 덮어쓸 수 있다.

C9. 실제 노드에 넣을 글

resultMaps.ts: nodeTextMapFromResult, outsideTitleTarget, preservedTextSheetIds. FrameThumbnail.tsx: 대치 결과를 미리보기에 전달하는 UI 경로.

C11. Content Threading

src/visual-summary/ 아래 네 파일이다 — contentThreading.ts(contentThreadingCategory로 적용 카테고리 둘을 정하고 planContentThreading·searchWithContentThreading), threadingTemplate.ts, threadingCapacity.ts, threadingPresentation.ts.

여기만 링크를 걸지 않았다. 이 네 파일은 origin/main에는 있지만 이 문서를 쓴 시점의 로컬 작업 트리에는 아직 없다(브랜치가 뒤처져 있다). 죽은 링크를 만들지 않으려고 이름만 적는다. git show origin/main:src/visual-summary/contentThreading.ts로 읽는다.

C12. 네 자리로 나뉜 코드와 fork 경계

src/comp-frame/AGENTS.md 질의 코어의 자리. comp-frame-shared/src/forked/: comp-sign에서 복사해 온 조각들. no-legacy-imports.test.ts: 문자열 검사로 직접 import를 막는다. 같은 이름의 검사가 src/visual-summary, packages/comp-frame-shared, packages/comp-frame-client에도 있다.

C13. 평가 자료 (규약이 아니다)

evaluation/placeholder-effect.md: 남은 자리가 judge 판정과 사용자 채택에 주는 효과. 요약본. 도입 판단용 실측이고 규약이 아니다. 카드 채택률(노출 카드 한 장이 분모)과 하위검색 채택률(하위검색 한 번이 분모)은 다른 지표이므로 섞어 읽지 않는다.

C10. 문서 검색과 화면 진입점

nestedSearch.ts, documentSearch.ts, 입력 파서, 라우트, localBackend.ts, remoteBackend.ts. local은 TS 검색 코어를 호출하고 remote는 서버 검색을 요청한다.