3. 시트의 구조
3.1 마커 열과 선언 셀
엔티티는 선언 셀로 시작합니다. :table · :enum · :const로 시작하는 셀이 어디에
있든 그 자리가 엔티티의 시작이고, 그 셀의 열이 그 엔티티의 마커 열입니다.
:table Item(side=s) ← 선언 셀. 이름과 괄호 메타
- 선언 셀의 오른쪽 첫 셀이 엔티티의 설명입니다. 비우면 설명 없음입니다.
:table·:enum·:const셋 다 같은 규칙입니다. - 괄호 메타는 생략할 수 있습니다. 아는 키는 §3.4의 표가 정본이고, 모르는 키는 아는
키의 목록과 함께 오류입니다(
RequireKnownOptions와 같은 정책). - 이름은 기존 규칙 그대로 Pascal 표기로 정규화되고, 종류와 무관하게 유일해야 합니다.
마커 열은 엔티티의 세로 범위 전체에서 예약됩니다. 담을 수 있는 것은 다음뿐이고, :로
시작하는 미지의 값은 오류입니다 — Luban이 미지의 헤더 태그를 로그로만 남기는 것과 갈리는
자리입니다. : 없는 낱말은 행 태그이고, 그쪽은 이름을 선언하지 않으므로 검사하지
않습니다(태그 §4).
| 마커 열의 셀 | 뜻 |
|---|---|
:table … · :enum … · :const … | 엔티티 선언 (이 엔티티의 종료이기도 합니다) |
:field · :type · :desc · :target · :variant | 헤더 행 키(§3.3) |
| 빈 칸 | 데이터 행 |
# (또는 //) | 그 행을 변환에서 제외. 데이터 칸을 편집하지 않고 제외 · 복원합니다 |
| 그 밖 | 그 행의 태그 — key 또는 key=value, 쉼표로 여러 개. 레시피가 부를 때만 그 행이 빠집니다(태그). :enum과 :const에서는 담기기만 하고 행은 빠지지 않습니다 — 그 행들은 생성 코드이기 때문입니다 |
3.2 경계
| 축 | 범위 |
|---|---|
| 가로 | 마커 열의 오른쪽부터, 다음 마커 열 직전까지. 없으면 시트의 마지막 컬럼까지 |
| 세로 | 선언 셀의 다음 행부터, 완전히 빈 행 또는 다음 선언 셀 직전까지 |
- 「완전히 빈 행」의 판정 범위는 그 엔티티의 컬럼 범위이고, 마커 열과 메모 컬럼을 포함합니다. 데이터 아래에 메모를 적으려면 한 줄 띄우면 됩니다.
- 데이터 중간에 시각적 여백이 필요하면 마커 열에
#를 적거나 메모 컬럼에 구분선 문자를 적습니다 — 그 행은 빈 행이 아니므로 엔티티가 계속됩니다. - 기존 레이아웃의 「엔티티 간 침범 금지」 규칙은 필요 없습니다 — 옆 엔티티는 자기 마커
열에서 시작합니다. 엔티티 옆에 자유 기재가 필요하면 메모 컬럼(
#, §3.3)으로 자리를 만들거나, 빈 행 하나로 닫힌 아래쪽에 적습니다. - 셀 병합은 읽지 않습니다. 병합 범위의 좌상단 셀만 값을 갖고 나머지는 빈 칸의 일반 규칙(§3.7)으로 읽힙니다. 판정은 경고이고 대상은 엔티티 범위 안의 병합입니다 — 메모 컬럼과 엔티티 밖은 자유입니다.
경고를 내려면 병합 정보가 먼저 와야 합니다. 지금은 병합 여부가 로우 모델에 오지 않습니다 —
RawCell에 그 자리가 없고, 스트리밍 리더(Sylvan.Data.Excel)는 행과 값만 줍니다. 병합 사각형은 xlsx의<mergeCells>· xlsb의 병합 레코드 · 구글 API의merges를 따로 읽어야 나오고, 그것은 정의된 이름과 같은 등급의 임포터 작업입니다. 그래서 이 레이아웃의 일이 아니라 별건이고, §16의 순서에 넣지 않았습니다.그때까지 병합 사고가 조용히 지나가지는 않습니다 — 병합에 의존하던 헤더는 좌상단 밖이 빈 칸이 되므로 「
:field가 빈 컬럼에 데이터가 있음」(§3.3)과 「:type셀이 빔」 (§3.7)이 그 셀을 가리켜 걸립니다. 없는 것은 「병합입니다」라고 이름을 대는 것이고, 그것이 경고가 더할 값입니다.
3.3 헤더 행
| 행 키 | 역할 | 생략 |
|---|---|---|
:field | 컬럼의 정체 — 이름 · 경로 · @N · * · # | 불가 |
:type | 타입 표현과 괄호 메타(§4) | 테이블은 불가. enum · const는 행 자체가 없습니다(§8) |
:desc | 컬럼 설명. 생성 코드의 doc comment가 됩니다 | 가능 |
:target | 대상의 쉼표 목록 — c · s · c,s. 행 생략과 개별 빈 칸 모두 양쪽입니다 | 가능 |
:variant | 필드 변형의 이름(§3.6). 빈 칸은 기본 변형입니다 | 가능 |
- 행의 순서는 자유이고, 데이터 행보다 위에 있어야 합니다.
:field를 데이터 바로 위에 두는 것을 권장 관례로 합니다 — 엑셀의 정렬 · 필터 머리글이 데이터와 인접합니다. :field셀에#하나만 적은 컬럼은 메모 컬럼입니다 — 시트 작성자가 자유롭게 적는 공간이라는 표시이고, 모델에 흔적을 남기지 않습니다.VLOOKUP표시, 계산용 임시 열, 한국어 라벨이 여기에 해당합니다.:field셀이 빈 컬럼은 자리가 없는 컬럼입니다. 그 컬럼의 데이터 칸에 값이 있으면 오류이고, 메시지가 「필드라면 이름을, 메모 공간이라면#를」 안내합니다 — 이름 없는 컬럼의 데이터가 조용히 버려지는 경로를 없애는 자리입니다(§11의 7).#이름@N은 제외된 필드이고 와이어 태그를 예약합니다(Tombstone). 메모 컬럼(#단독)과 뜻이 다릅니다 — 행 키 레이아웃 §2.3의 구분 그대로입니다.- 기본 인덱스는 지정이 없으면 첫 필드 컬럼입니다. 선언 셀의
key메타로 다른 컬럼을 지정하거나 복합 키를 선언할 수 있습니다(§3.5). 어느 쪽이든 기존 규칙 전부가 성분마다 적용됩니다 — 유일 · 옵셔널 불가 · 양쪽 대상 필수 · 될 수 있는 타입.
3.4 선언 셀의 메타 키
| 키 | 값 | 뜻 |
|---|---|---|
side | c · s · c,s(기본) | 엔티티의 target-side. 기존 마커의 :side 자리 |
key | 필드 이름, 또는 따옴표 쉼표 목록 | 기본 인덱스의 지정. 생략하면 첫 필드 컬럼입니다(§3.5) |
단일 행 테이블(one) 등은 예약 후보로만 두고 §13에 적었습니다.
target의 표기는 쉼표 목록입니다 — Luban의 ##group c,s와 같은 형태이고, 대상이 셋
이상으로 일반화되는 날(기능 대조 §5.3)에도 표기가 그대로
확장됩니다. 붙여 쓴 cs는 c,s와 같은 뜻으로 받습니다 — 기존 시트 · recipe · CLI가 쓰는
표기라서입니다. 괄호 메타 안에서는 쉼표가 항목 구분자이므로, 쉼표가 든 값은 DSL의 규칙
그대로 따옴표로 감쌉니다(side="c,s") — 다만 양쪽이 기본값이라 실제로 적을 일이
드뭅니다.
3.5 키의 지정 — key 메타
SQL의 키 체계를 따릅니다. 첫 키가 PRIMARY KEY, 나머지가 UNIQUE 키에 대응하고,
어느 것이든 단일 · 복합이 됩니다. 쉼표가 한 키 안의 성분을, 세미콜론이 키와 키를
가릅니다 — allowed=a;b;c와 같은 관례이고 새 기호가 없습니다. 쉼표 · 세미콜론이 들어가는
값은 따옴표로 감쌉니다(§3.4).
| 표기 | 뜻 | SQL 대응 |
|---|---|---|
| (생략) | 첫 필드 컬럼이 기본 인덱스 — 지금 규칙 그대로 | PRIMARY KEY |
key=code | 그 이름의 컬럼이 기본 인덱스. 첫 컬럼 강제가 풀리고, 첫 컬럼은 평범한 필드가 됩니다 | PRIMARY KEY |
key="stage,slot" | 복합 기본 인덱스. 조합이 유일해야 하고, 조회가 다인자가 됩니다 — FindByKey(stage, slot) | 복합 PRIMARY KEY |
key="stage,slot; slot,code" | 키 여러 개. 첫 키가 기본 인덱스, 나머지는 보조 키입니다 — 각자 유일하고 각자 조회를 얻으며, 개수 제한이 없습니다 | PRIMARY KEY + UNIQUE … |
- 첫 키가 기본 인덱스입니다 — 행을 가리키는 것 · 참조 대상 · 멀티 로우의 레코드 경계(§6.1)가 전부 첫 키의 것입니다.
*는 단일 보조 키의 컬럼 자리 표기입니다 — SQL이 컬럼 자리의UNIQUE와 테이블 자리의UNIQUE (…)를 둘 다 두는 것에 대응합니다. 컬럼 옆에서 보이는 것이*의 가치이고, 목록에 적는 것과 뜻이 같습니다. 같은 키를 양쪽에 이중 선언하면 오류입니다.- 성분의 규칙 —
key에 적힌 이름은 실재하는 최상위 스칼라 필드여야 하고(경로 ·[]·[n]불가), 성분마다 기본 인덱스의 기존 규칙(인덱스 가능 타입 · 옵셔널 불가 · 양쪽 대상)이 적 용됩니다. 한 키 안에서 같은 성분을 두 번 적거나, 성분 구성이 같은 키를 두 번 선언하면 오류입니다. 성분 하나가 여러 키에 들어가는 것은 됩니다 — 위 표의slot이 그 예입니다. - 복합 키의 성분에
*를 따로 붙이는 것도 됩니다 — 「조합도 유일하고 이 성분 혼자서도 유일하다」는 서로 다른 두 선언입니다. - 성분이
foreign인 것은 됩니다. 연결 테이블 — 한 행이 두 카탈로그의 짝을 뜻하는 표 — 이 복합 키가 가장 많이 나오는 자리이고, 그 두 컬럼이 다 참조입니다. 이때 조회의 매개변수는 대상의 키이지 대상의 행이 아닙니다 — 부르는 쪽이 들고 있는 것은 어디서 읽어 온 id이고, 사전이 키로 삼는 것도 그 id들이 만드는 텍스트입니다. 참조가 내는 두 이름 중 컬럼 자신의 이름이 키이고 파생된 이름이 행이므로(참조가 내는 이름 §4·§5), 둘을 바꿔 쓰면 부를 수 없는 조회가 나옵니다. - 기본 인덱스가 복합인 테이블은
foreign의 대상이 될 수 없습니다 — 참조는 키 값 하나를 담는 구조이므로(설계) 거부로 시작하고, 요구가 실측되면 그때 형태를 정합니다. 보조 키는 참조와 무관합니다. - 와이어는 움직이지 않습니다 — 성분 컬럼이 그대로 실립니다. 움직이는 것은 생성 코드의
조회 표면(키마다 하나, 복합이면 다인자)이고, 모든 언어에 파급되므로 별도
단계입니다(§16의 8). 그때까지 복합 키는 문법 · 모델 · 유일성 검사까지 받고, 조회
생성만 이름 과 함께 거부합니다. 단일
key=지정은 조회 형태가 지금과 같으므로 1단계에 포함됩니다.
3.6 필드 변형 — :variant 행
한 필드의 값 컬럼을 여러 벌 적고, 빌드가 하나를 고르는 표기입니다. 지역별 가격처럼 컬럼 하나만 갈리고 나머지가 공유되는 데이터가 대상입니다.
- 같은 필드 이름을 컬럼 여러 개에 그대로 적고,
:variant행이 그것들을 구분합니다.:variant가 빈 컬럼이 기본 변형입니다. 같은 이름 · 같은 변형 이름의 컬럼 둘은 중복 오류입니다(빈 칸 둘도 마찬가지입니다). - 선택은 recipe가 합니다 —
"Variants": { "Item.Price": "kr" }형태의테이블.필드 → 변형 이름맵이고, CLI는--variant Item.Price=kr입니다. 지정이 없으면 기본 변형이고, 기본 변형이 없는 필드(모든 컬럼에 변형 이름이 있는 필드)는 선택이 필수입니다. 없는 변형을 지정하면 있는 목록과 함께 오류입니다. - 산출물은 변형을 모릅니다. 선택된 컬럼 하나가 그 필드가 되고, 나머지 컬럼은 그
빌드에 없습니다 — 모델 · 와이어 · 생성 코드 전부 필드 하나이므로 형식 · 생성 코드
무변경입니다.
text수집도 선택된 컬럼만 합니다. - 헤더 기재의 정본은 기본 변형 컬럼(없으면 첫 변형 컬럼)입니다.
:type·:desc·:target·@N은 거기에 적고, 다른 변형 컬럼에 반복 기재하면 일치를 검사합니다 — §4.3의 그룹 규칙과 같은 형태입니다. - 키 컬럼(§3.5의 성분 포함)에는 변형을 둘 수 없습니다. 행을 가리키는 값이 빌드마다 달라지면 참조 · 히스토리 · 배포 판정이 전부 「같은 행」을 말할 수 없게 됩니다.
- 그룹 멤버(
pos.x) ·[]·[n]컬럼의 변형은 1차에서 거부합니다 — 컬럼 집합이 변형마다 복제되는 형태라, 요구가 실측되면 그때 정합니다.
행 세트와의 구분
행 세트(그 문서의 이름은 「행 벌」입니다)와는 둘 다 「여러 벌 중 하나를 고른다」이지만 고르는 단위가 다릅니다. 겹치는 기능이 아니므로 어느 쪽도 다른 쪽을 대신하지 않습니다.
:variant | 행 세트 | |
|---|---|---|
| 여러 벌인 것 | 한 필드의 값 컬럼 | 행의 집합(레코드들) |
| 공유되는 것 | 같은 행의 나머지 필드 전부 | 테이블의 스키마 |
| 맞는 요구 | 지역별 가격 · 난이도별 수치 하나 | 지역별 상품 구성 · 환경별 데이터 추가 · 교체 |
3.7 빈 칸의 뜻 — 자리 별 총정리
시트를 작성하는 사람이 가장 자주 묻는 질문이 「여기 비워도 되나」입니다. 빈 칸의 뜻이 자리마다 다르므로 한 표로 정리합니다 — 이 표가 시트 작성 문서에 그대로 들어가야 합니다. 데이터 칸의 정본 규칙은 빈 칸과 없음이고, 이 절은 그 규칙을 이 레이아웃의 자리에 배치한 것입니다.
세 줄 요약.
- 빈 칸은 「값 없음」이 아닙니다. 값 없음은
-이고,?컬럼에서만 적을 수 있습니다. - 비워도 되는 곳 — 문자열 · bool · 배열의 데이터 칸(각각
""·false· 빈 배열이 됩니다), 선택 헤더 행의 칸, 정본이 다른 칸에 있는 헤더 칸. - 비우면 안 되는 곳 — 숫자 계열의 데이터 칸(기본 정책), 기본 인덱스, 연장 행의 스칼라 칸, 이름 없는 컬럼의 데이터 칸.
헤더 영역
| 자리 | 빈 칸의 뜻 |
|---|---|
| 선언 셀의 오른쪽(설명) | 설명 없음. 허용 |
:field 셀 | 자리가 없는 컬럼. 그 아래 데이터가 있으면 오류(§3.3) — 자유 기재 공간이 필 요하면 #를 적습니다 |
:type 셀 — 보통의 필드 컬럼 | 오류. 타입은 생략할 수 없습니다 |
:type 셀 — struct 그룹의 2번째 이후 멤버 · [n]의 원소 1 이후 · 기본이 아닌 변형 컬럼 | 비우는 것이 정본입니다(§4.3). 적으면 일치 검사 |
:desc 셀 | 설명 없음. 허용 |
:target 셀 | 양쪽(c,s). 허용 |
:variant 셀 | 기본 변형. 허용 |
:desc · :target · :variant 행 자체의 생략 | 전부 위의 기본값. 허용 |
메모 컬럼(#)의 모든 칸 | 자유 공간 — 헤더 행의 칸도 포함합니다 |
데이터 영역
| 자리 | 빈 칸의 뜻 |
|---|---|
| 마커 열 | 보통의 데이터 행 |
| 기본 인덱스 칸 | 오류 — 그 행을 가리킬 것이 없습니다. 예외는 멀티 로우 표 하나이고, 거기서는 연장 행의 신호입니다(§6.1). 복합 키의 성분 일부만 비면 오류 |
string · bool · T[] 칸 | 값입니다 — "" · false · 빈 배열. 값 없음이 아닙니다 |
숫자 · bitset · datetime · timespan · uuid · enum 칸 | 오류(기본). 소스의 OnBlankCell: "empty"가 타입의 빈 값으로 완화하고, 그렇게 읽은 것은 컬럼당 한 번 경고됩니다 |
참조(foreign) 칸 | 오류. OnBlankCell로도 통과하지 않습니다 — 키를 적거나, 옵셔널 참조면 -를 적습니다 |
옵셔널(?) 칸 | 빈 칸의 뜻은 위와 똑같습니다. ?가 바꾸는 것은 「-를 적을 수 있다」뿐입니다 — string?의 빈 칸은 없음이 아니라 ""입니다 |
[n] 원소 칸 | TrimTrailingArrayElements가 켜져 있으면 값 없는 뒤쪽 원소(-)는 배열 밖입니다. 가운데의 빈 원소는 기본 거부입니다(AllowArrayGaps) |
[] 그룹의 범위가 전부 빈 행 | 그 행에 그 그룹의 원소가 없습니다(§6.1 규칙 4) |
연장 행의 [] 아닌 칸 | 비어 있어야 합니다. 값이 있으면 그 셀을 가리켜 오류입니다(§6.1 규칙 3) |
enum의 label · value, const의 name · type · value | 오류 — 필수 칸입니다 |
enum의 alias · desc, const의 desc | 없음. 허용 |
| 완전히 빈 행 | 엔티티의 종료(§3.2) |
| 엔티티 밖(경계 너머)의 셀 | 레이아웃이 읽지 않습니다 — 자유 |