본문으로 건너뛰기

기존 파이프라인에서 옮겨오기

지금 쓰는 변환을 끄지 않고, 시트를 고치지 않고 시작하는 순서입니다.

문서 목록으로


엑셀을 읽어 게임 데이터로 만드는 스크립트는 이미 있을 것입니다. 그것을 하루에 걷어내는 것이 아니라, 테이블 하나에서 시작해 두 파이프라인이 같은 값을 내는 것을 확인하고 범위를 넓히는 것이 이 문서의 순서입니다.

옮기는 것과 그대로 두는 것

그대로 둡니다바뀝니다
워크북, 시트 탭, 컬럼 배치시트를 읽어 산출물을 만드는 코드
데이터를 적는 사람의 작업 방식생성된 타입과 리더를 쓰는 소비 코드
버전 관리와 리뷰 절차산출물의 형식 — 텍스트에서 바이너리

데이터를 적는 사람이 새로 배우는 것은 없습니다. 1·2단계는 시트를 한 셀도 고치지 않고 끝나고, 시트에 표기를 더하는 것은 3단계부터입니다.

네 단계

단계끝나면 아는 것기존 파이프라인
1 — 테이블 하나를 JSON으로 대조두 도구가 셀을 같게 읽는가그대로 돕니다
2 — 검증만 먼저 붙이기지금 시트에 무엇이 어긋나 있는가그대로 돕니다
3 — 참조와 분리를 시트에 적기참조가 실제로 이어지는가옮긴 워크북부터 떠납니다
4 — 코드와 바이너리로 전환기존 변환을 지울 수 있는가끕니다

앞의 두 단계 동안 기존 변환은 계속 돕니다. Tabbit은 자기 출력 폴더에만 쓰고, 실패한 실행은 아무것도 남기지 않습니다.

3단계부터는 워크북 하나가 단위입니다. 옮긴 워크북은 머리 모양이 달라져 기존 스크립트가 더는 읽지 못하므로, 그 워크북의 테이블을 쓰는 소비 코드도 함께 넘어갑니다. 나머지 워크북은 그대로 두고 갑니다.

1단계 — 테이블 하나를 JSON으로 대조

recipe 하나

tabbit --new-recipe compare.jsonc --template ci

만들어진 파일에서 소스와 타깃만 남깁니다.

{
"Sources": {
"Xlsx": [
{
"Path": "./sheets",
"Layout": "sheet-per-table", // 이 도구의 규칙으로 쓰이지 않은 시트
"IncludeSheets": [ "ItemTable" ],
"OnBlankCell": "empty", // 아래 「엄격한 기본값」 참고
"OnFormulaError": "empty"
}
]
},

"Targets": [
{ "Type": "json", "Path": "./out/compare", "Indented": true }
]
}

Layout은 소스 항목마다 지정하므로 한 recipe에서 섞어 읽어도 됩니다. sheet-per-table은 선언 셀 없이 시트 탭 하나가 곧 테이블 하나이고 머리 3줄이 헤더인 형태를 그대로 읽습니다 — 설명·이름·타입 순서입니다. sprout 샘플의 워크북 17개가 그 형태이고, 그 산출물이 이 단계의 결과가 무엇인지 보여줍니다.

IncludeSheets에 이름을 적은 시트만 읽으므로, 같은 워크북의 나머지 시트는 대상이 아닙니다.

대조에서 갈리는 자리

테이블 하나가 파일 하나이고, 행이 객체인 배열입니다.

[
{ "id": 1605000, "titleKey": "ACH_TITLE_0014", "targetCount": 5 },
{ "id": 1605001, "titleKey": "ACH_TITLE_0058", "targetCount": 8752 }
]

지금 쓰는 파이프라인의 출력과 나란히 놓고 봅니다. 값이 같은지보다 먼저 보아야 하는 것은 같지 않은 자리의 이유이고, 대개 아래 넷 중 하나입니다.

어긋나는 자리무엇을 정해야 하나
빈 칸기존 도구가 0으로 읽던 자리를 이 도구는 거부합니다. 엄격한 기본값
-·null·없음「값 없음」의 표기가 무엇이었는지. 이 도구는 - 하나이고 타입이 ?로 끝날 때만 허용합니다
숫자 정밀도bigintJSON에 문자열로 나갑니다. float는 왕복 가능한 최단 십진수입니다
이름 표기Naming을 선언하지 않으면 시트의 표기 그대로입니다. 아래

이 단계가 끝나면 아는 것은 「두 도구가 이 시트를 같게 읽는다」 하나입니다. 그것을 모른 채로 다음 단계를 진행하면, 뒤에서 나오는 모든 차이가 이 단계의 것인지 아닌지 가릴 수 없습니다.

시트 수를 늘리려면 IncludeSheets를 비웁니다. 폴더 아래 전부가 됩니다.

2단계 — 검증만 먼저 붙이기

tabbit --recipe compare.jsonc --validate-only

산출물을 하나도 쓰지 않습니다. 그래서 기존 파이프라인이 도는 저장소의 PR 검사에 그대로 더할 수 있고, 실패하면 종료 코드 1과 함께 어느 워크북 어느 시트 어느 셀인지 출력합니다 (종료 코드 · 빌드 리포트).

이 단계에서 얻는 것은 지금 쓰는 도구가 보고하지 않던 것들입니다.

검사무엇을 잡나
타입컬럼 타입과 맞지 않는 셀
인덱스겹치는 키. OnDuplicateIndex
컬럼 제약범위·허용값·필수 여부를 시트에서 읽어
검증 규칙시트로 표현할 수 없는 규칙을 C# 파일로. 실행 전·테이블별·전역·런타임 네 단계

참조 검증은 아직 아닙니다. 그것은 다음 단계입니다.

3단계 — 참조와 분리를 시트에 적기

sheet-per-table 레이아웃에는 참조를 적을 자리도, 컬럼을 한쪽 사이드로 표시할 자리도 없습니다. 그 두 형태는 이 도구의 규칙으로 쓰인 시트에서만 나오므로, 여기서 처음으로 시트에 표기를 더합니다.

옮긴 워크북은 기존 스크립트를 떠납니다. 선언 셀이 들어가면서 머리 모양이 달라지기 때문입니다. 그래서 이 단계는 한 번에 전부가 아니라 워크북 하나씩이고, 레이아웃을 소스 항목마다 지정할 수 있는 것이 그것을 가능하게 합니다.

"Xlsx": [
{ "Path": "./sheets/converted", "Layout": "tabbit" }, // 옮긴 것
{ "Path": "./sheets", "Layout": "sheet-per-table" } // 아직인 것
]

한쪽 워크북의 테이블이 다른 쪽에서 선언한 enum을 타입으로 써도 됩니다. 그래서 전부 옮기기 전에도 모델은 하나입니다.

시트에 더하는 것은 선언 셀과 타입 칸의 표기뿐입니다.

더하는 표기얻는 것
foreign없는 키를 빌드가 거부하고, 코드에서 가리키는 행이 바로 옵니다. 참조
c · s클라이언트가 받는 파일에서 그 컬럼이 아예 빠집니다. 사이드
?빈 칸이 「없음」이 되는 컬럼. 빈 칸과 없음
[]여러 행이 한 행의 배열이 되는 것. 멀티 로우

전부 한 번에 할 일이 아닙니다. 참조가 가장 먼저인 이유는 그것만이 지금 데이터에 이미 들어 있는 결함을 찾아내기 때문입니다 — 나머지 셋은 표현을 바꾸는 것이고, 이것은 검사입니다.

4단계 — 코드와 바이너리로 전환

타깃에 언어와 바이너리를 더합니다. JSON은 대조가 끝날 때까지 함께 둡니다.

"Targets": [
{ "Type": "json", "Path": "./out/compare" },
{ "Type": "binary", "Path": "./game/data" },
{ "Type": "csharp", "Path": "./game/Generated", "AccessorName": "GameData" },
{ "Type": "go", "Path": "./server/gamedata", "AccessorName": "GameData",
"PackageName": "gamedata", "WriteGoMod": false, "TargetSide": "s" }
]

서버와 클라이언트의 언어가 달라도 같은 시트에서 같은 타입이 나옵니다. 자기 언어의 적용 방법은 언어별 가이드에 있습니다.

소비 코드를 옮기는 순서는 읽는 쪽부터입니다.

  1. 생성된 리더로 데이터를 한 번 읽고, 기존 로더가 읽은 것과 값을 비교하는 검사를 붙입니다
  2. 통과하면 소비 코드를 생성된 타입으로 바꿉니다
  3. 기존 로더와 변환 스크립트를 지웁니다

배포 판정을 CI에

전환이 끝나면 남는 질문은 「이번 변경으로 무엇을 배포해야 하는가」입니다.

셀 수정은 데이터 패치로 나가지만, 상수는 빌드 말고 어디에도 실리지 않고, enum은 이름이 코드에 숫자가 데이터에 나뉘어 있습니다. 히스토리가 스냅샷마다 그것을 판정합니다.

To ship this range: data + code
- enum Grade: 1 label(s) added
- column Item.SellPrice added
! enum Grade: labels added while data changed in the same conversion. ...

--detailed-exit-code를 주면 「데이터가 그대로여서 할 일이 없었다」를 종료 코드 2로 구분하므로, 배포 단계를 건너뛰는 조건으로 쓸 수 있습니다 (종료 코드).

기존 시트가 엄격한 기본값에 걸릴 때

기본값이 엄격한 이유는 그 셀들이 대개 적다 만 셀이기 때문입니다. 다만 옮겨오는 동안에는 「우리가 아직 관리하지 못하는 워크북」이 있고, 그 자리를 위한 완화가 소스 항목마다 있습니다.

설정기본값완화하면
OnBlankCellerror숫자·날짜 컬럼의 빈 칸을 타입의 빈 값으로 읽고 컬럼당 한 번 경고합니다
OnFormulaErrorerror#REF! 같은 셀을 빈 값으로 읽습니다. 모델이 값으로 든 셀만 대상입니다
OnDuplicateIndexerror겹치는 인덱스를 허용합니다
TrimTrailingArrayElementsfalseSlot1·Slot2·Slot3에서 셋째를 비운 행이 길이 2를 냅니다

완화한 것은 몇 건을 그렇게 읽었는지가 실행 기록에 남습니다. 그 숫자가 0이 되면 설정을 지우는 것이 이 완화의 사용법입니다.

이름 표기가 제각각일 때

Naming 섹션을 선언하면 표기를 벗어난 이름을 전부 보고합니다. 그 목록을 Exempt로 옮기고 OnViolationerror로 두면, 그 시점부터 새 이름만 규약을 지킵니다.

"Naming": {
"Field": "camel",
"Entity": "pascal",
"OnViolation": "error",
"Exempt": [ "Art_Path", "HP_Max" ] // 아직 못 고친 기존 이름
}

기존 이름은 계열 단위로 개명하며 목록에서 지웁니다. 목록은 줄어드는 방향으로만 관리하고, 규약의 나머지는 Naming 설정에 있습니다.

시트 규칙이 두 레이아웃 어느 쪽도 아닐 때

sheet-per-table은 특정한 형태 하나입니다 — 머리 3줄이 설명·이름·타입이고, 시트 탭 하나가 테이블 하나입니다. 지금 시트가 그것과 다르면 레이아웃을 하나 더 만듭니다.

[TabbitLayout("id")]를 단 클래스 하나이고, 레지스트리가 어트리뷰트 스캔으로 찾으므로 코어에 등록 코드를 더할 필요가 없습니다 (아키텍처). 레이아웃 전용 설정이 필요하면 LayoutOptions의 자유 키/값 맵을 읽습니다 — 문서.

그 파일 하나를 지웠을 때 코어에 흔적이 남지 않는 것이 이 구조의 조건이고, 그래서 검증이 끝난 뒤 레이아웃을 버리는 비용이 파일 하나입니다.

되돌리기

1·2단계까지는 생성 폴더와 recipe를 지우면 끝입니다. 시트를 고치지 않았고, 새로 설치한 라이브러리가 없습니다. 캐시(.tabbit/)는 커밋 대상이 아닙니다.

3단계부터는 시트가 함께 바뀌어 있습니다. 옮긴 워크북을 되돌리는 것은 그 커밋을 되돌리는 것이고, 워크북 하나씩 옮기는 것이 되돌릴 단위를 작게 두는 방법입니다.

다음에 읽을 것

무엇어디
이 도구의 규칙으로 시트를 적는 방법시트 작성
생성된 코드를 프로젝트에 붙이는 방법언어별 가이드
형식별 크기와 로드 비용, 그리고 재현 절차벤치마크
빌드가 실패했을 때트러블슈팅