본문으로 건너뛰기

HTML 문서 — 데이터 확인 산출물의 구성

문서 목록으로 · 언어별 가이드

변환된 데이터를 사람이 확인하는 산출물의 페이지 구성 · 페이지 이동 · 통계 규격입니다.


배경

이 타깃은 어떤 프로그램도 읽지 않습니다. 존재 이유는 빌드에 들어간 데이터를 눈으로 확인하는 것이고, 그 목적에 대해 현재 산출물은 세 가지가 부족합니다.

측정 대상실측
한 프로젝트의 목록 페이지36.9 MB — 테이블 71개 · 행 109,218개가 한 페이지
그중 최대 테이블LevelCurve 95,490행 — 이 테이블 하나가 전체의 87%
상세 페이지에서 다른 페이지로 가는 링크0개 — 브라우저 뒤로가기 외에 이동 수단이 없습니다
전체 규모를 나타내는 수치테이블 제목의 N Record(s) 1개

여기에 결함 2건이 함께 있습니다.

  • html-foot.sbn의 주석에 {{ user }}가 있어 }}주석 블록을 조기 종료합니다. 그 뒤 6줄이 모든 페이지 본문에 출력되고, 타임스탬프는 그 잔재 뒤에 붙습니다. 골든 6트리(core · core-client · core-server · excel-typed · foreign-field · layout-edge)가 이 상태를 기록하고 있습니다.
  • html-enum.sbn은 공통 푸터를 include하지 않아 enum 페이지에 </body></html>가 없습니다.

골든 비교는 이 종류를 검출하지 못합니다. 두 건 다 처음부터 그 상태였으므로 산출물이 변하지 않았고, 골든이 판정하는 것은 「변했는가」입니다.

결정 요약

항목결정근거
페이지 경계테이블당 페이지 하나한 테이블이 10만 행인 것이 이상한 데이터가 아닙니다. 전체를 한 페이지에 두면 최대 테이블 하나 때문에 나머지 66개를 못 봅니다
tables.html테이블 목록 페이지로. 데이터는 테이블당 페이지로같은 이름이 「전부 한 페이지」에서 「목록」으로 바뀝니다. 목록이 내는 것(무엇이 있고 얼마나 큰가)과 데이터가 내는 것은 다릅니다
사이드바모든 페이지에 전체 엔티티 목록을 담습니다페이지 하나만 전달받아도 이동이 되어야 합니다. 항목당 약 60바이트이므로 엔티티 500개에서 페이지당 약 30 KB이고, 같은 페이지의 데이터 표보다 작습니다
행 상한MaxRowsPerTable 기본 1,000행, 0은 무제한상한이 없으면 최대 테이블의 페이지가 34 MB입니다. 절단은 페이지에 명시하므로 「전량을 봤다」로 읽히지 않습니다
다크 테마지원합니다<font color=blue>를 제거하면 밝은 테마로 고정한 이유가 없어집니다
enum 미리보기셀에서 호버 카드로 제시합니다이동해야 확인되는 값은 확인 비용이 이동 비용입니다. 정의는 페이지마다 한 번 싣고 셀은 이름만 듭니다 — 셀마다 title에 담으면 10만 행 테이블에서 enum 전체가 10만 번 반복됩니다
테마시스템 · 밝게 · 어둡게 3단, localStorage에 저장「시스템을 따른다」는 상태의 부재가 아니라 하나의 상태입니다. 둘만 두면 되돌아갈 자리가 없습니다
아이콘인라인 SVG 심볼, 이모지 아님이모지는 폰트의 해석이라 플랫폼마다 다른 그림이 됩니다. 심볼은 페이지마다 하나를 정의하고 자리마다 use로 참조합니다 — 사이드바가 테이블 500개를 담아도 경로는 한 벌입니다
목록 페이지테이블 · 컬럼 · enum · 상수 세트마다 하나개요에 목록을 두면 프로젝트가 커질수록 카운터 아래가 이름의 벽이 됩니다. 정렬·필터가 필요한 목록은 표 페이지의 도구를 그대로 쓰는 편이 낫습니다
시트 형태중첩 · 다중 중첩 · 옵셔널 필드 · 옵셔널 원소 전부 지원아래 §6
페이지의 문구한국어이 산출물을 읽는 사람이 한국어로 일합니다. 시트에서 온 이름과 설명은 그대로 두고, 도구가 쓰는 문구만 옮깁니다 — 타입 이름(int32)과 측 표기(c·s·cs)는 recipe의 어휘이므로 번역하지 않습니다
스크롤 영역페이지당 하나표와 페이지가 함께 스크롤되면, 페이지가 내려간 만큼 표의 고정 헤더가 상단 바 뒤로 들어갑니다. 창이 틀이고 넘치는 영역 하나만 스크롤합니다
참조 값그 행으로 가는 링크키가 적혀 있는데 그 행을 찾는 방법이 없었습니다. 대상이 여럿인 컬럼은 후보를 나열하고 읽는 사람이 고릅니다
참조 그래프references.html목록은 한 테이블씩 냅니다. 「무엇이 뿌리이고 무엇이 매달려 있는가」는 그림이 냅니다
외부 요청계속 0개폐쇄망 전제는 그대로입니다. 스타일 · 파비콘 · 스크립트 전부 인라인입니다

1. 페이지 구성

<Path>/
index.html 개요 — 통계 · 컬럼 분포 · 최대 테이블 · 원본 워크북
tables.html 테이블 목록 — 행 수 · 컬럼 수 · 시트
fields.html 전체 컬럼 색인 — 이름 · 소속 테이블 · 타입 · 측 · 필수
enums.html enum 목록 — 레이블 수 · 사용 컬럼 수 · 시트
structs.html 구조체 목록 — 공통 멤버 수 · 변종 수 · 사용 그룹 수
constantsets.html 상수 세트 목록과 각 세트의 상수
references.html 참조 그래프 — 테이블 사이의 참조와 그 층
tables/<kebab>.html 테이블 하나 — 스키마 · 데이터 · 피참조 목록
enums/<kebab>.html enum 하나 — 레이블 · 사용처
structs/<kebab>.html 구조체 하나 — 공통 멤버 · 변종마다의 멤버 · 사용 그룹

목록은 개요에서 분리했습니다. 처음에는 개요가 종류마다 목록을 들었는데, 그러면 프로젝트가 커질수록 카운터 아래가 이름의 벽이 됩니다. 목록마다 페이지를 두면 필터와 정렬을 그대로 얻고, 개요는 카운터와 분포만 남습니다 — 카드가 각 목록으로 들어가는 입구입니다.

상수 세트만 목록과 내용이 한 페이지에 있는 것은 분량이 다르기 때문입니다. 세트는 수십 개 상수를 가지고, 테이블은 수만 행을 가집니다.

fields.html이 내는 것은 「어느 테이블에 어떤 컬럼이 있는가」이고, 이름으로 정렬하면 같은 컬럼 이름이 붙어 나오므로 타입이나 측이 어긋난 곳이 드러납니다.

링크 규칙의 단일화

엔티티로 가는 상대 경로를 생성기 한 곳(HtmlLinks)에서 만듭니다. 분할 전 참조 컬럼의 타입 칸은 #table_X를 냈고, 그것은 같은 페이지 앵커 전제였으므로 페이지를 나누면 전부 어긋납니다. 같은 종류의 결함이 enum 링크에서 이미 한 번 발생하였고, 그때의 조치가 「한 곳으로 모으는 것」이었습니다.

테이블 링크에는 fragment를 붙이지 않습니다. 페이지가 곧 그 테이블이므로 앵커는 제목을 지나 헤더 행으로 내려보내고, 그 자리는 고정 상단 바가 덮습니다. 컬럼처럼 페이지 안의 한 자리를 가리켜야 하는 링크는 fragment를 쓰고, 모든 id에 scroll-margin-top을 두어 상단 바 아래로 숨지 않게 합니다.

2. 페이지 이동

요소무엇
상단 바index · Tables · Fields · Enums · ConstantSets 링크와 브레드크럼
사이드바현재 종류의 형제 목록 전체. 현재 항목을 표시하고, 목록을 그 항목에 맞춰 스크롤합니다 — 페이지마다 문서가 새로 열리므로 목록은 맨 위에서 시작하고, 테이블이 수백 개인 모델에서 현재 항목은 화면 밖 수백 행 아래에 표시됩니다. 가운데에 두는 것은 다음에 갈 곳이 그 위아래이기 때문입니다. 컨테이너의 scrollTop으로 옮깁니다 — scrollIntoView는 스크롤 가능한 조상을 모두 움직이므로 페이지까지 함께 밀려납니다
검색 상자사이드바 목록과 데이터 표의 행을 즉시 필터합니다. 인라인 스크립트이고 외부 요청이 없습니다
테마 버튼시스템 → 밝게 → 어둡게 순환. 선택은 localStorage에 남고, 페이지가 그려지기 전에 head의 스크립트가 적용합니다 — 그 뒤에 적용하면 매 페이지에서 다른 테마가 한 번 번쩍입니다
아이콘테이블 · 컬럼 · enum · 상수 세트 · 워크북 · 시트마다 하나. 상단 바 · 목록 · 사이드바 · 제목 줄에 같은 심볼을 쓰고, 목록의 이름 앞에도 붙습니다 — 이름만으로는 그것이 테이블인지 enum인지 알 수 없는 자리가 여러 곳입니다
이동하는 카드개요의 카드 중 페이지로 가는 것에는 가 붙습니다. 절반은 숫자이고 절반은 문이었는데 생김새가 같아서, 어느 쪽인지 눌러 보고 알았습니다
선언 위치워크북과 시트를 함께 냅니다 — 목록의 「선언 위치」 열과 테이블·enum·상수 세트 페이지의 머리줄입니다. 시트 이름만으로는 찾을 수 없습니다: 한 프로젝트의 시트가 워크북 10여 개에 흩어져 있고 시트 이름에는 어느 쪽인지 적혀 있지 않으므로, 읽는 사람이 파일을 하나씩 열어 보게 됩니다. 워크북은 파일 이름으로 적고 경로는 툴팁에 둡니다 — 경로는 모든 행에서 같고, 같은 글자로 채워진 열은 아무도 읽지 않습니다
링크의 조각행 앵커는 키를 담으므로 URL 인코딩합니다. 키는 문자열일 수 있고 문자열 키에는 공백·따옴표·#이 들어갈 수 있습니다 — 속성이나 URL이 그 자리에서 끝납니다. 앵커 쪽 id는 사람이 읽는 형태로 둡니다. 브라우저가 조각을 디코드한 뒤 id와 맞추기 때문입니다
목록의 기본 정렬이름 오름차순, 대소문자를 가리지 않습니다. 순수 서수 정렬은 대문자를 모두 소문자 앞에 두므로 찾는 이름이 예상한 자리에 없습니다. 헤더에 정렬 표시가 붙습니다
앵커테이블 · 컬럼 · 행 · enum 레이블마다 id. 다른 페이지가 정확한 자리를 가리킵니다 — 컬럼 색인은 그 컬럼의 헤더로, 데이터 칸은 그 레이블의 행으로

3. 통계

index.html이 제시하는 수치입니다. 전부 모델에서 계산하므로 별도 수집 단계가 없습니다.

지표출처
테이블 · enum · 상수 세트 개수Model.Tables · Enums · ConstantSets
총 행 수 · 총 컬럼 수Table.Data.Count · Table.Fields.Count의 합
원본 워크북 수Table.Location.Filename의 중복 제거
타입별 컬럼 분포Field.ElementType별 개수
배열 · 옵셔널 · 참조 컬럼 수Field.IsArray · IsRequired · IsRef
text · asset 컬럼 수Field.Role
측별 컬럼 분포Field.TargetSide
최대 테이블 상위 목록행 수 내림차순

분포는 CSS 폭 막대로 표시합니다. 차트 라이브러리를 싣지 않는 것은 외부 요청 0개 규칙 때문이고, 막대 하나에 필요한 것은 백분율 하나입니다.

수치는 게이트가 검사합니다. 페이지에 적힌 테이블 개수와 행 수 합이 모델과 일치하는지 확인합니다 — 통계가 틀리면 없는 것보다 나쁩니다.

4. 역참조

지금 링크는 나가는 방향만 있습니다. 「이 enum을 쓰는 컬럼은 어디인가」와 「이 테이블을 가리키는 컬럼은 어디인가」는 데이터를 확인할 때 실제로 필요한 질문입니다.

페이지추가되는 것
enums/<x>.html이 enum을 타입으로 쓰는 컬럼 목록 (테이블 · 컬럼)
tables/<x>.html이 테이블을 참조하는 컬럼 목록. 참조는 테이블 하나를 지목하므로 대상은 컬럼마다 하나입니다
목록의 단위엔트리입니다. 접힌 참조 배열은 페이지에서 컬럼 하나이고 워크북에서 N개이므로, 시트의 컬럼을 그대로 적으면 AdvSpcEffTerms0·AdvSpcEffTerms1처럼 그 테이블 페이지에 없는 이름으로 답하게 됩니다. 링크는 엔트리의 첫 컬럼으로 갑니다 — 헤딩의 앵커가 거기 붙어 있습니다

참조 행 미리보기

무엇규격
담는 것그 페이지가 이름을 부르는 행만, 대상 테이블의 앞 컬럼 다섯 개까지, 값은 48자에서 자릅니다
담는 자리숨긴 요소의 JSON. script 태그가 아닌 것은 시트 값에 URL처럼 읽히는 것이 있을 수 있고, 「어떤 페이지도 네트워크에 요청하지 않는다」는 검사가 script 태그를 보기 때문입니다
만드는 때페이지마다 대상 테이블을 훑는 것이 아니라, 모든 페이지가 필요로 하는 키를 먼저 모아 대상 테이블을 한 번만 훑습니다
크기 실측테이블 1개 페이지 2,594 KB에 미리보기 187 KB (7%)
행 상한을 넘는 행미리보기에는 나옵니다. 상한은 페이지 크기의 문제이고 미리보기는 값 몇 개입니다

enum 미리보기

역참조와 같은 이유로 필요한 것이 하나 더 있습니다. enum 값은 이동해야 확인됩니다 — 셀에 있는 것은 레이블 하나이고, 그 enum이 무슨 값을 가지는지는 enum 페이지에만 적혀 있습니다.

무엇규격
표시enum 값 칸에 커서를 올리거나 포커스를 주면 그 enum 전체가 카드로 나타나고, 해당 레이블이 표시됩니다
정의의 위치페이지마다 한 번. 셀은 data-enum·data-label만 듭니다
링크그대로 유지합니다. 카드는 이동을 대체하는 것이 아니라 이동하지 않아도 되게 하는 것입니다
스크립트가 없는 환경값과 링크는 그대로 보입니다. 카드만 나타나지 않습니다
카드 안으로 진입가능합니다. 셀이나 카드를 떠나면 짧은 타이머가 돌고 카드에 들어오면 취소됩니다 — 긴 enum은 카드 안에서 스크롤해야 하므로, 셀을 떠나는 순간 닫히면 스크롤할 방법이 없습니다
기존 title 툴팁제거했습니다
패널의 두 용도enum 전체와 긴 값 전체가 같은 패널을 씁니다. 브라우저 툴팁은 커서 자리에 나타나므로 비교 중인 위아래 행을 가립니다
카드 폭min(560px, 92vw), 폭은 max-content

게이트는 「셀이 가리키는 enum을 그 페이지가 싣고 있는가」를 검사합니다 — 안 실린 카드는 열리지 않는 카드와 구별되지 않습니다.

참조 그래프

무엇규격
레이아웃왼쪽에서 오른쪽으로 층. 한 테이블은 자기를 가리키는 것들보다 한 층 오른쪽에 놓이므로 모든 화살표가 앞을 향합니다. 쿠커가 참조 순환을 거부하므로 그 순서는 언제나 존재하고, 자기 참조만 예외로 고리로 그립니다
층 안의 순서들어오는 노드들의 평균 높이, 같으면 이름순. 교차를 줄이는 통상의 한 패스이고, 물리 시뮬레이션과 달리 매번 같은 그림이 나옵니다
규모별 형태간선 80개까지는 전체를 그리고, 그 위에서는 그리지 않습니다. 323개를 창에 맞추면 글자가 2px인 회색 얼룩이 됩니다 — 그림처럼 보이면서 아무 질문에도 답하지 않습니다. 대신 층의 크기와 한 테이블의 참조를 냅니다
열었을 때전체를 그리지 않는 페이지는 가장 연결이 많은 테이블의 이웃으로 열립니다. 빈 화면은 기다리는 페이지가 아니라 실패한 페이지로 읽힙니다
층의 크기0층부터 5층까지 각 층의 테이블 수, 그 뒤는 한 줄로 접습니다(「6층 이상」). 28층까지 한 줄씩 적는 것은 형태의 서술이 아니라 목록입니다
고르는 방법행마다 그래프 아이콘 버튼이 있습니다. 행 전체도 그림을 바꾸지만, 한 자리는 링크이고 나머지는 그림을 바꾸는 행은 어느 쪽도 말해주지 않아 아무도 누르지 않았습니다. 누를 것이 물건이어야 누릅니다
이웃으로 걷기이웃 노드를 누르면 그 테이블의 참조로 옮겨갑니다 — 참조 체인을 전체 그림으로 돌아가지 않고 따라갈 수 있습니다. 처음부터 되던 것이고, 화살표를 붙여 눌러도 된다는 것을 보이게 했습니다
목록의 자리그림 왼쪽에 같은 줄입니다. 그림을 바꾸는 것이 그 목록이므로, 아래에 있으면 고르러 내려가고 결과를 보러 올라와야 했습니다 — 긴 그림에서는 둘이 한 화면에 있을 수 없었습니다. 줄의 높이를 정해 두고 두 칸이 그것을 나눠 씁니다. 목록의 높이가 기준이 되면 줄이 모델의 테이블 전부만큼 길어집니다
보는 방법끌어서 이동하고 휠로 확대합니다. 화면은 고정 크기이고 그림이 변환된 그룹 안에 있으므로, 이동은 요소 하나의 속성 하나입니다 — 레이아웃 크기만 한 캔버스를 매번 다시 그리는 것이 아닙니다
한 테이블만 보기테이블을 누르면 그 테이블의 이웃만 그립니다 — 왼쪽에 가리키는 테이블, 가운데에 그 테이블, 오른쪽에 가리켜지는 테이블. 이웃을 다시 누르면 그쪽으로 걸어갑니다. 「전체 그래프」로 돌아옵니다
그 그림에서의 자기 참조고리입니다. 전체 그래프가 하는 것과 같습니다
고리의 자리오른쪽 열이 없으면 그만큼(44px)을 폭에 더합니다
참조가 자기 자신뿐인 테이블고리 위에 그 사실을 글로 적습니다
호버그 테이블의 간선만 남고 나머지가 흐려집니다. 간선 전체를 담은 그룹의 불투명도 하나를 바꾸고 해당 간선만 위 그룹에 복사합니다 — 수백 개를 다시 스타일링하는 것이 곧 캔버스 전체의 재도색입니다
규모에 따른 장식간선 80개까지는 화살촉과 간선 툴팁을 그리고, 그 위에서는 층을 여러 열로 나눠 그림을 정사각형에 가깝게 만듭니다
연결 정도 표테이블마다 가리키는 수·가리켜지는 수. 어디서부터 볼지 정하는 자리이고, 행을 누르면 그 테이블이 그려집니다
그래프에 없는 테이블아래에 목록으로 적습니다 — 참조가 하나도 없는 모델이 이상한 것이 아닙니다

5. 데이터 표

표의 컬럼은 시트의 컬럼이 아니라 테이블의 엔트리

레코드 배열은 시트에서 원소마다 멤버마다 한 컬럼으로 적힙니다 — statBonus[0]["Id"], statBonus[0]["Value"], statBonus[1]["Id"]. 그대로 그리면 컬럼 33개가 늘어서고, 어느 숫자가 어느 멤버인지는 헤더를 세어야 알 수 있습니다. 접힌 엔트리 하나가 컬럼 하나입니다.

형태컬럼
스칼라하나
배열(구분자 또는 접힌 컬럼)하나[1, 2, 3] + 원소 수
레코드하나(0, 1077, 421)
레코드 배열하나[(0, 1077, 421), (0, 1122, 196)] + 원소 수

셀은 튜플이고 툴팁에 이름이 적힙니다. 멤버 이름은 마크업에 있고 셀에서만 숨깁니다 — 그래서 툴팁이 그 이름을 읽고, 페이지가 값을 두 번 담지 않으며, 필터도 멤버 이름으로 걸립니다. 툴팁은 들여쓴 형태입니다:

[
{
Type: 0,
Id: 1077 → StatOperator,
Value: 421
}
]
무엇규격
필수 여부타입에 ? 붙습니다. 별도 행은 레코드 하나에 멤버가 여럿이면 답할 수 없어 「전부 optional」로 뭉쳤습니다 — 시트가 ?를 타입에 쓰므로 페이지도 그렇게 씁니다
레코드의 타입멤버마다 이름·타입·?·대상: {Type: double?, Id: double? ⇢ StatOperator | WorldPassiveEffect, Value: double?}[]
필수 여부의 표기컬럼 색인의 필수 열은 논리값과 같은 표기입니다 — 노란 과 붉은 . 한 문서에서 예·아니오를 두 가지로 적으면 읽는 사람이 문서를 먼저 익혀야 합니다
「컬럼」이 세는 것엔트리입니다. 개요의 카드·테이블 목록·테이블 페이지·컬럼 색인이 모두 그렇습니다. 시트의 칸을 세면 접힌 배열 하나가 컬럼 여러 개가 되어, 카드의 숫자가 그 아래 모든 페이지와 어긋납니다. 워크북의 크기도 필요한 값이므로 「시트 컬럼」이라는 이름으로 함께 냅니다 — 개요의 한 줄, 테이블 페이지의 괄호, 색인의 머리줄에 있습니다
타입·역할·측 분포시트 컬럼을 셉니다. 접힌 배열의 원소마다 타입과 측이 있으므로 묶어 세면 그 표가 내는 것이 없어집니다. 그래서 개요에 어느 쪽을 세는지 적습니다
기본 인덱스타입 뒤에 🔑. 타입 색은 다른 타입과 같습니다 — 표시는 이모지 하나로 충분합니다
무엇규격
행 상한MaxRowsPerTable 기본 1,000. 절단 시 「전체 N행 중 처음 M행」을 표 위에 명시합니다
정렬컬럼 헤더 클릭. 상한 이하의 표에서만 활성화합니다
필터행 단위 문자열 일치. 일치 행 수를 표시합니다
빈 값빈 문자열은 "", 값 없음은 로 구분합니다. 분할 전에는 둘 다 빈 칸이었습니다
논리값참은 노란 , 거짓은 붉은 . 거짓이 빈 칸이었을 때는 거짓·값 없음·빈 문자열 셋이 같은 그림이었고, 그 셋을 가르는 것이 이 페이지의 목적입니다
참조 미리보기참조 셀에 커서를 올리면 그 행의 앞 컬럼 다섯 개가 패널로 나옵니다. 링크는 그대로이고, 미리보기는 「이 키가 무엇인가」만 냅니다
배열[1, 2, 3]처럼 대괄호로 감싸고 원소 개수를 옆에 붙입니다. 길면 잘리고, 커서를 올리면 전체 목록이 패널로 나옵니다 — 값은 페이지에 그대로 있으므로 복사도 전체입니다
필수 여부타입 행 아래에 한 줄. 값이 없는 칸을 보는 사람이 「이 컬럼이 빈 값을 허용하는가」를 그 자리에서 확인합니다. 표기는 required·optional로, 타입 행과 측 행처럼 recipe의 어휘입니다
긴 값46자 폭에서 자르고 를 붙입니다. 표시만 자르므로 값은 페이지에 그대로 있고 필터도 복사도 전체를 봅니다. 커서를 올리면 전체가 패널로 나오고, 누르면 그 자리에서 펼쳐집니다
참조 컬럼의 타입키의 타입 다음에 화살표입니다 — int? → AdvSpcEffTermsGroup[]. 화살표만 있으면 셀이 숫자인지 이름인지 알 수 없고, 그것이 그 아래 값들을 읽는 데 필요한 정보입니다. 행 전체를 가리키는 참조는 모델이 타입 이름을 대상 이름으로 지어 두므로(ForeignRecord·AdvSpcEffTerms.Record) 그것을 그대로 쓰면 대상을 두 번 적게 됩니다. 셀에 들어 있는 것은 대상의 기본 인덱스이므로, 대상 시트가 적은 그 컬럼의 타입 이름을 씁니다 — 페이지가 타입 이름을 지어내지 않습니다
깨진 참조의 수참조 컬럼의 타입 뒤에 ⚠ N이 붙습니다 — 그 컬럼의 키 중 대상에 그 행이 없는 것의 개수이고, 툴팁에 어느 테이블에 몇 개인지 적힙니다. 값마다 붙는 ?다른 기호를 쓰는 것이 요점입니다: ?는 스키마가 빈 칸을 허용한다는 뜻이고 이것은 데이터가 틀렸다는 뜻이므로, 같은 글자로 적으면 이 페이지가 존재하는 이유인 그 구분이 사라집니다. 세는 단위는 이고 행 상한과 무관하게 테이블의 모든 행을 셉니다 — 페이지가 짧다고 깨끗한 것은 아닙니다. 대상을 여럿 지목한 컬럼은 「어느 쪽에도 없는 키」 하나로 셉니다. 그리고 이 수는 이 빌드에 대한 것입니다: 레시피가 대상 테이블을 제외했다면 그 키들도 여기 들어옵니다
문자열 키다른 문자열과 같이 ""로 감쌉니다. 링크 안에서는 따옴표만 쓰고 문자열 색은 쓰지 않습니다 — 여기서 색은 「따라갈 수 있다」는 뜻입니다. 링크가 되지 않는 경로(대상이 이 빌드에 없는 경우가 흔합니다)에서도 값의 표기는 같습니다
참조 값그 키를 실제로 가진 테이블의 행으로 링크됩니다. 대상이 여럿이면 옆에 어느 쪽인지 적고, 툴팁에는 선언된 대상 전부가 적힙니다 — 가진 쪽은 그 행의 앞 컬럼들을, 없는 쪽은 없다는 사실을. 어느 쪽에도 없으면 ?를 붙이고 어느 테이블들에 없는지 적습니다. 행 상한으로 그 페이지에 행이 없으면 페이지까지만 가리킵니다
시트가 선언만 한 대상참조로 승격되지 않은 컬럼(레코드 멤버가 여러 대상을 지목하면 코어가 보류합니다)도 같습니다. 그 선언이 그 컬럼에 대해 페이지가 말할 수 있는 가장 쓸모 있는 것이고, 그때까지는 double 하나만 나오고 있었습니다
값 없음Cell.HasValue를 봅니다. 부재는 null이 아니라 「타입의 빈 값 + 없음 표시」로 저장되므로, 값만 읽으면 아무도 입력하지 않은 0이 입력한 0과 같아집니다 — 이 페이지가 막으려고 존재하는 혼동입니다. 표기는 <none>이고 배열 원소마다도 같습니다
시트가 적은 제약최소·최대·허용값 개수·레코드 안 필수를 헤더 툴팁에 냅니다. 모델에 있는데 페이지에 하나도 없었습니다
고정헤더와 첫 컬럼을 고정합니다. 표 컨테이너에 높이(100vh - 190px)를 주어 표가 컨테이너 안에서 스크롤되게 한 뒤입니다 — 높이가 없으면 컨테이너가 스크롤되지 않으므로 sticky가 페이지와 함께 밀려 올라가고, 값만 남은 화면에서 컬럼 이름을 보려고 되돌아 올려야 했습니다
스크롤 영역표가 하나뿐인 페이지는 그 표가 남은 높이를 받고, 페이지 자체는 스크롤하지 않습니다. 표가 여럿이거나 카운터가 있는 페이지는 페이지가 스크롤하고 표는 자기 높이대로 놓입니다 — 스크롤이 둘이면 고정 헤더가 붙을 자리를 잃습니다
고정하는 단위thead 하나입니다. 행마다 따로 고정하고 오프셋(23px·46px·69px)을 페이지가 계산하던 방식은, 배율이 정수가 아닌 화면(1.25배·1.5배)에서 행의 실제 높이와 오프셋이 서로 다르게 반올림되어 행들이 소수점만큼 겹쳤습니다 — 두 선이 겹친 자리는 두껍고 한 선이 다른 선에 덮인 자리는 사라지며, 스크롤에 따라 그 자리가 변했습니다. 띠 전체를 고정하면 행의 경계가 한 박스 안쪽이 되므로 계산 자체가 없어집니다
헤더의 선한 가지 방법으로만 그립니다. 셀의 테두리와 inset 그림자를 함께 쓰면 둘 다 그려진 자리만 두꺼워집니다. 색은 --head-line입니다 — 본문의 --line-soft는 헤더 배경과 같은 색이고, --line은 헤더 안쪽 선으로 쓰면 무겁습니다. 띠의 아래 경계만 --line입니다
빈 열·빈 행없으면 그리지 않습니다

6. 표현할 수 있는 시트 형태

타깃마다 「이 형태를 아는가」를 선언하고, 모르는 타깃은 그 자리에서 거부합니다 — 그래야 잘못된 산출물 대신 이름이 적힌 메시지가 나옵니다. html은 네 가지 모두에 아무것도 선언하지 않고 있었습니다. 그래서 레코드 그룹이나 옵셔널 컬럼을 쓰는 워크북은 문서가 한 장도 생성되지 않았고, 그것이 바로 데이터를 들여다볼 필요가 가장 큰 시트입니다.

형태이 타깃이 하는 것
레코드 그룹 (Group.Member)접힌 엔트리 하나를 컬럼 하나로 그립니다(§5). 시트의 컬럼을 그대로 늘어놓으면 어느 숫자가 어느 멤버인지 헤더를 세어야 합니다
다중 중첩 (Star1.Position.X)같습니다. 깊이는 컬럼 이름의 문제이지 페이지의 문제가 아닙니다
옵셔널 필드값이 없는 칸은 null, 빈 문자열은 ""로 그립니다. 기울임체는 null 씁니다 — 빈 문자열과 빈 배열은 값이므로 정체로 두어야 부재와 구별됩니다
옵셔널 원소같은 표기를 배열 원소에 적용하고, 빈 배열은 []입니다

구조를 다루지 않는 타깃이라 구현이 필요한 것은 없었습니다 — 선언과 그룹 이름을 읽는 자리 하나뿐입니다(레코드 그룹의 Fields는 설계상 비어 있으므로 멤버의 이름 경로에서 읽습니다).

6.1. 접힌 타입은 접기 전의 표기로

이 타깃이 내는 것은 다른 타깃들의 것과 다릅니다. 나머지는 「이 값을 어떻게 싣는가」를 내고, 이것은 「시트가 무엇이라고 적었는가」를 냅니다. 코어는 여러 타입을 파싱이 끝나는 지점에서 더 단순한 것으로 접습니다 — 그것이 와이어와 언어별 생성기를 그 기능에서 떼어놓는 장치이고, 접힌 결과는 참인 서술입니다. 다만 이 타깃이 낼 것은 아닙니다.

시트코어가 접는 결과이 타깃근거
vec3f · quat · color32컴포넌트마다 컬럼 하나타입은 vec3f, 셀은 (1.5, -2.5, 0)Field.CompositeOrigin이 원래 타입을 들고 있고 읽는 곳이 없었습니다. 멤버 이름을 붙인 (X: 1.5, Y: -2.5, Z: 0)은 저자가 적은 값의 3배 폭이고, 그 컬럼을 읽는 사람이 이미 아는 것입니다
color · color32같음색 견본과 튜플색을 실제로 보여줄 수 있는 산출물이 이것뿐입니다. (R: 51, G: 153, B: 204, A: 255)로 색을 확인하는 사람은 없습니다. 견본의 색은 인라인 스타일입니다 — 워크북이 가진 색마다 규칙을 둘 수는 없습니다
bitsetbigint0x1F, 툴팁에 비트 수와 10진수타입을 비트셋으로 만든 이유가 표기인데, 옆의 bigint 컬럼과 값이 한 글자도 다르지 않았습니다. 「모든 비트」가 -1로 나왔습니다. 16진수인 것은 패턴을 니블로 읽기 때문이고, 16자리로 채우지 않는 것은 0x000000000000001F이 한눈에 비교되지 않기 때문입니다
set<T>배열 컬럼 하나set<T>, 셀은 {"new", "sale"}Field.Container가 선언에서 남은 유일한 표시입니다. 배열로 그리면 저자가 무엇을 적었는지 말할 방법이 없습니다
map<K,V>길이가 같은 배열 둘map<K,V>, 셀은 {10: 100, 11: 120}짝짓기가 맵의 전부입니다. (Key: [10, 11], Value: [100, 120])은 읽는 사람이 두 목록을 세어 짝지어야 하고, 선언은 그러지 말라고 적은 것입니다
int?[]int[]와 같은 컬럼int?[]ElementsRequired를 읽지 않아 원소에 null이 있는 컬럼의 타입이 「모든 원소가 값」이라고 적혀 있었습니다. ?가 괄호 안쪽인 것은 시트가 그 자리에 쓰기 때문입니다

6.2. 구조체

무엇규격근거
페이지structs.html과 타입마다 structs/<kebab>.html추상 타입은 테이블과 enum 옆의 엔티티입니다 — 한 번 선언되고 여러 그룹이 그것일 수 있습니다. enum이 가진 것을 그대로 갖습니다
타입 페이지의 구성공통 멤버와 변종마다의 멤버를 나눠서 냅니다둘이 내는 것이 다릅니다. 공통 멤버는 변종을 가리지 않고 읽을 수 있는 것이고, 변종의 멤버는 가려야 하는 것입니다. 하나의 표로 합치면 그 구분이 없어지고, 선언에 적힌 것이 그 구분입니다
변종의 번호이름 옆에 냅니다파일이 싣는 것이 그것이고, 대부분의 언어는 보여주지 않습니다. 저장된 데이터를 확인하는 사람에게는 필요합니다
공유 열같은 멤버를 선언하는 다른 변종멤버 하나는 컬럼 하나이고 변종마다의 필드 하나입니다. 두 변종 아래에 같은 이름이 보이는 것은 중복이 아니고, 그것을 말해 주는 자리가 없었습니다
$type변종의 이름, 그 선언으로 가는 링크파일이 싣는 것은 번호이고 시트에 적힌 것은 이름입니다. 페이지는 번호를 냈습니다 — 변종을 열거한 합집합 위에서, 이 행이 그중 무엇인지 적지 않고
어느 변종도 아닌 번호붉게 냅니다생성된 리더가 모르는 판별자는 로드되지 않는 데이터입니다
타입 칸의 $typestruct.<이름>으로 그 구조체를 가리킵니다int로는 Type이라는 이름의 평범한 멤버와 구별되지 않았고, 옆의 옵셔널 멤버 전부를 설명하는 하나가 어디에도 없었습니다
변종 멤버의 타입어느 변종이 선언하는지를 옆에 붙입니다합집합만으로는 답할 수 없습니다. Damage: int?가 「때때로 없다」로 읽히는데 사실은 「이 행이 Strike일 때 정확히 있다」입니다
그 행의 변종에 없는 멤버. 기울임체를 쓰지 않습니다「값이 없다」와 「이 행이 가진 타입의 일부가 아니다」는 다른 사실이고, 그 구분이 이 페이지가 존재하는 이유입니다. 기울임체는 이 페이지에서 부재를 뜻하므로 여기 쓰지 않습니다
판별자 컬럼의 이름$type — 시트의 표기모델은 Type으로 정규화합니다. 헤딩이 시트처럼 읽혀야 하는 것은 그것이 누군가 편집하러 갈 컬럼이기 때문입니다

6.3. 컬럼 헤딩과 행 앵커

무엇규격근거
헤딩컬럼 이름의 첫 단계까지. 대괄호와 점 양쪽을 자릅니다시트는 부분마다 한 칸을 적으므로 그 뒤는 엔트리가 아니라 부분의 이름입니다. 대괄호만 자르고 있어서 점 표기로 쓴 모든 그룹이 첫 멤버의 이름을 달고 있었습니다 — vec3fPos.X, mapBag.Tags, 구조체가 Effect.$type
번호가 붙은 단계모델의 이름을 씁니다Slot1은 원소이고 Slot이 그 전부를 덮습니다
행 앵커선언된 키의 모든 컬럼을 이어 만듭니다첫 컬럼에서 만들고 있었습니다. 키를 지정한 시트는 키가 아닌 컬럼에 앵커가 붙었고, 여러 컬럼으로 키를 이룬 시트는 같은 id가 여러 번 나왔습니다 — row_Grid.0이 한 페이지에 3번 있었고 그리로 오는 링크는 먼저 나온 행에 닿았습니다
앵커의 자리키 컬럼 하나id는 한 자리에 있어야 하고, 한 행을 가리키는 링크가 세 칸을 가리킬 수는 없습니다
🔑기본 키의 모든 컬럼에. 툴팁에 종류가 적힙니다Field.Indexing은 기본 인덱스와 * 유일 인덱스 둘 다에 참이므로, 한 문구로 적으면 두 번째가 첫 번째로 읽힙니다. 여러 컬럼으로 이룬 키는 아무 표시도 없었습니다

6.4. 상수 세트의 배열

무엇규격근거
배열 상수원소로 냅니다 — ["bronze", "silver", "gold"]스칼라 enum 하나만 분기가 있었고 나머지는 object.ToString()에 닿았습니다. 배열에 그것은 CLR 타입의 이름이므로 string[] 상수가 System.String[]으로, Grade[]System.Int32[]으로 나왔습니다
enum 배열원소마다 레이블·링크·미리보기 카드같은 이유로 링크와 카드도 함께 잃고 있었습니다. 카드 수집 필터가 Enum을 정확히 요구했고 배열은 EnumArray입니다
골든과의 관계골든이 이것을 정답으로 기록하고 있었습니다4건이 이 페이지의 첫 커밋부터 있었습니다. 골든이 판정하는 것은 「변했는가」입니다

6.5. 읽히는 표기

무엇규격근거
논리값참은 초록 , 거짓은 붉은 . 한 단계 크고 한 단계 굵게--good은 이 페이지가 플래그에 쓰는 호박색입니다 — 밝은 테마에서 그것은 흰 바탕의 노란 글리프이고, 그 컬럼이 페이지에서 가장 눈에 안 들어오는 것이었습니다. 표시는 글자가 아니라 표이므로 크기와 굵기를 함께 쓸 수 있습니다. --true가 자기 토큰인 것은 --num도 초록이고 둘이 테마마다 따로 정해지기 때문입니다
상수 세트의 논리값같은 표기상수 페이지가 true라고 적고 테이블 페이지가 라고 적으면 읽는 사람이 문서를 먼저 익혀야 합니다. 컬럼 색인이 그것을 그리는 이유와 같습니다
설명의 강조**굵게** · *기울임* · `코드`. 먼저 이스케이프하고 그다음에 태그설명 칸에 강조를 적는 사람이 있고 페이지가 별표를 그대로 내고 있었습니다. 셀이 가진 것은 아무것도 마크업으로 닿지 않습니다 — 모든 꺾쇠와 앰퍼샌드가 태그 하나 붙기 전에 인코딩되고, 붙는 태그는 이 도구의 것입니다. 시트가 셀에 마크업을 적어 페이지에 마크업을 넣을 수는 없습니다
그 셋뿐블록·목록·링크 없음설명은 셀 하나의 한 문장입니다. 링크는 이 페이지가 애초에 아무것도 가져오지 않는 url입니다
툴팁이스케이프만어트리뷰트는 마크업을 글자 그대로 냅니다
고정된 칸z-index를 가집니다위치만 잡힌 요소는 문서 순서로 그려지므로 오른쪽 칸들이 그 위에 그려집니다. 가로로 밀면 둘째 컬럼의 값이 키 위에 그려져 두 글자가 겹쳤습니다. 칸은 그동안 자리를 지키고 있었으므로 밀기 전에는 아무것도 이상해 보이지 않았습니다
그림 안의 테이블 누르기붙잡기를 끌기가 시작될 때로눌린 포인터를 붙잡으면 뒤따르는 이벤트와 브라우저가 만드는 클릭이 붙잡은 요소에게 갑니다. 그래서 테이블 위에서 누르고 떼면 그림이 그것을 받았고 테이블의 처리기는 돌지 않았습니다 — 페이지가 읽는 사람에게 하라고 적어 둔 그 하나가 아무 일도 하지 않았습니다

6.6. 구조체 페이지의 구성

무엇규격근거
공통 멤버패널. 왼쪽에 강조선모든 변종이 가지는 것이므로 변종들보다 위에 있고, 그 관계가 보여야 합니다
변종아래로 내려가는 레일에 붙은 카드. 카드마다 자기 테두리, 레일에서 카드로 가는 짧은 선h2 하나와 h3 여럿이었습니다. 변종이 41개인 타입에서 그것은 같은 굵기의 제목이 늘어선 벽이고, 어느 표가 어디서 끝나는지가 선이 아니라 짐작이었습니다. 선언에 적힌 것은 관계인데 페이지가 관계를 그리지 않았습니다
이름 칩변종이 6개를 넘으면 위에 한 줄전체를 볼 수 없는 페이지는 아무도 이동하지 않습니다
/// 설명구조체와 변종 둘 다 냅니다모델이 들고 있지 않아서 낼 수 없었습니다. 멤버의 설명은 그것을 담은 컬럼을 타고 오지만 타입 자신의 설명은 어느 컬럼도 아닌 것을 서술하므로, 선언을 읽고 버리면 영구히 없어집니다. PolymorphicType.Comment·PolymorphicVariant.Comment
채움하지 않습니다표 하나짜리 페이지가 아니고 여러 카드입니다. 채움 사슬의 min-height: 90px가 멤버 한 개짜리 카드마다 빈 띠를 남겼습니다 — 이 문서가 다른 자리에서 렌더 실패로 읽힌다고 적은 것입니다
「변종에 없음」 표기, 점선 밑줄. 그리고 표 위에 그 둘을 적은 줄기울인 null과 대시는 듣지 않은 사람에게 너무 조금 다릅니다. 구조체 그룹의 컬럼은 변종 전부의 합집합이므로 그런 표의 셀 대부분이 이 표기이고, 툴팁만으로는 그 대부분이 설명되지 않습니다

7. 마크업과 색

  • <font color=...> · align · valign · <thead> 직속 <th>를 제거하고 CSS 클래스와 <tr>로 대체합니다. 값의 종류(숫자 · 참조 키 · enum · 논리값)는 클래스로 나타냅니다.
  • 색은 CSS 변수로 세 벌 정의합니다 — 밝은 것을 루트에, 어두운 것을 prefers-color-scheme 아래에(테마를 고르지 않은 독자), 어두운 것을 [data-theme="dark"] 아래에 한 번 더(고른 독자). 마지막 것이 있어야 버튼이 운영체제를 양방향으로 덮을 수 있습니다.
  • 스크롤바는 얇은 것으로 지정합니다. 플랫폼 기본 스크롤바는 창의 일부가 아니라 페이지의 일부처럼 보입니다.
  • 스타일 · 스크립트 · 파비콘은 인라인을 유지합니다. 페이지 하나가 파일 하나입니다.

8. 게이트

기존 HtmlTargetTests에 추가합니다. 기존 4개 (내부 링크 해소 · 외부 요청 없음 · 빈 이름 없음 · 빈 목록 없음)는 그대로 둡니다.

검사무엇을 방지하는가
템플릿 주석 잔재 없음이번 결함의 재발. ##~}} · {{~## · Scriban 잔재 문자열
모든 페이지가 </html>로 종료enum 페이지의 미완결
모든 페이지에 홈 링크 존재갈 곳 없는 페이지의 재발
통계 수치와 페이지들의 일치틀린 통계
절단 시 그 사실의 명시무성 절단
셀이 가리키는 enum을 그 페이지가 싣고 있음열리지 않는 미리보기 카드
참조 링크가 실재하는 행이나 페이지를 가리킴상한 때문에 없는 앵커를 가리키는 링크
한 페이지가 같은 id를 두 번 쓰지 않음엉뚱한 행에 닿고 그것을 알릴 방법이 없는 링크. 위의 링크 검사는 이것을 보지 못합니다 — 페이지의 id를 집합에 담아 조회하므로, 같은 id 3개는 집합의 원소 1개입니다
값의 자리에 런타임 타입 이름이 없음System.String[]. 배열만이 아니라 System. 전부를 봅니다 — 다음 것은 여기서 예측하지 못한 타입일 것이기 때문입니다
컬럼 헤딩이 자기 그룹의 부분을 가리키지 않음Pos.X · Bag.Tags. 헤딩에 .이나 [가 남아 있으면 실패합니다
행 앵커가 키의 모든 컬럼을 담음한 페이지의 앵커는 전부 같은 폭입니다. 폭이 섞이면 앵커가 키가 아닌 것에서 만들어졌다는 뜻입니다
셀이 대는 변종을 구조체 페이지가 선언하고 있음선언 없는 변종 이름. 미리보기 카드 검사와 같은 결함의 다른 자리입니다
시트가 선언한 타입 표기가 페이지에 나옴§6.1의 접기 전 표기 전부. 마크업이 아니라 태그를 걷어낸 글에서 찾습니다 — 타입 표기는 여러 요소로 조립되므로 마크업에서 찾으면 마크업을 찾는 것이 됩니다
그림을 숨길 수 있음hidden은 HTML 어트리뷰트이고 브라우저의 기본 스타일시트가 그것을 SVG에 적용하지 않습니다. 마크업은 옳았고 선언 하나가 없었으므로 다른 검사가 볼 자리가 없습니다 — 그 선언을 이름으로 확인합니다
고정된 칸에 z-index가 있음같은 종류입니다. 규칙 하나가 없고 나머지는 옳았습니다
설명의 강조가 렌더됨<b>가 나오고, 태그를 걷어낸 글에 **가 남지 않습니다. 픽스처가 하나를 적으므로 만나지 않아 통과하는 일이 없습니다

8.1. 게이트가 도는 시나리오

위의 검사들은 core 하나에서 돌고 있었습니다. core는 컴포지트도, 컨테이너도, 구조체도, 비트 패턴도, 여러 컬럼으로 이룬 키도 선언하지 않습니다 — 그래서 그중 무엇을 틀리게 내든, 아예 내지 않든 아무것도 알아채지 못했습니다.

시나리오무엇을 지킵니까
compositevec3f · vec2i · quat · color32 · color
polymorphism구조체 페이지 · $type 칸 · 변종 멤버
bitset16진수 표기
composite-key행 앵커와 🔑
nullable-elementsint?[]
containersset<> · map<>
packed스키마 파일이 선언한 그룹

레시피마다 html 타깃을 더했으므로 골든이 이 7개를 함께 봅니다. 늘어나는 것은 4.4 MB이고, 그것이 이 기능들에 대한 유일한 방어선입니다 — 위의 결함은 전부 페이지가 처음 쓰인 날부터 골든에 정답으로 기록되어 있었습니다.

9. 단계

단계내용골든
1결함 2건 · 폐기 마크업 정리재기록
2페이지 분할 · 공통 셸 · 링크 단일화재기록
3통계 · 컬럼 색인 · 역참조재기록
4표 도구 · 행 상한재기록
5doc/languages/html.md 갱신 · 샘플 재생성

1단계에서 4단계까지 한 번에 진행하고 골든은 마지막에 1회 재기록합니다. 단계마다 재기록하면 같은 검증을 4번 하게 되고, 전체 스위트 1회가 8분에서 9분입니다.

절차는 아키텍처와 개발의 순서를 따릅니다 — 골든 재기록 → 전 언어 비교본 재생성 → 샘플 재생성 → 기록 없이 재검증.