본문으로 건너뛰기

편집기와 이식

「정적 검증」으로 돌아가기


16. 이식할 때 — 원본을 옆에 두세요

기존 검증을 옮기는 것이라면 원본을 규칙 파일의 주석으로 붙여 두세요.

이식은 번역이 아니라 대조이고, 대조는 원본이 옆에 있을 때만 됩니다.

검증 폴더/이 그 예입니다. 규칙마다 원본 Lua가 위에 그대로 있고, 달라진 곳에는 왜 달라졌는지가 붙어 있습니다.

// 판매 가능한 아이템에는 판매가가 있어야 합니다.
//
// local function CheckSellPrice(row)
// -- isNotSell이 false일 때 sellPrice가 없으면 오류
// if row.isNotSell == false and not row.sellPrice then
// ...
//
// 원본의 `not row.sellPrice`는 「값이 없다」와 「0이다」를 구별하지 못합니다.
// `HasSellPrice`가 그 구별입니다 — 옵셔널 컬럼의 존재 여부가 와이어에 담겨 있기 때문입니다.

if (row.HasIsNotSell && !row.IsNotSell && !row.HasSellPrice)
context.Error(row, nameof(row.SellPrice), "판매 가능한 아이템에는 sellPrice가 필요합니다.");

이 주석이 실제로 값을 낸 자리가 셋 있었습니다.

무엇
규칙이 나아진 곳을 눈에 보이게 합니다. 위의 옵셔널 구별이 그렇습니다 — 주석이 없으면 「그냥 그렇게 썼다」로 읽힙니다
원본의 버그를 그대로 드러냅니다. custom이 없을 때 오류를 낸 뒤 그대로 내려가 nil을 키로 쓰던 것이 원본에 있었습니다. 고친 것이지 옮긴 것이 아니라는 사실이 남아 있어야 합니다
옮기지 않은 것을 옮기지 않았다고 적습니다. 상수 38개 중 규칙이 쓰는 넷만 옮겼습니다. 원본이 주석에 있으니 필요할 때 꺼내면 됩니다

17. 편집기 자동 완성

됩니다. 그리고 clone 직후부터 됩니다. 받은 사람이 빌드를 한 번 돌릴 필요가 없습니다.

손으로 할 것은 검증 폴더를 편집기에서 여는 것뿐이고, 그것도 편집기 쪽 사정입니다.

검증 폴더에 커밋되는 것

무엇
Validation.csproj규칙을 액세서에 대고 컴파일하는 프로젝트. Tables.Item.MaxStack이 타이핑 중에 해석되는 이유입니다
lib/Tabbit.Validation.dll · .xmlcontext.이 해석되는 이유. 요약 문서가 함께 있어 툴팁에 설명이 나옵니다
lib/Tabbit.Rules.Data.dll · .xml생성 액세서. Tables.context.Tables.가 해석되는 이유입니다
lib/Newtonsoft.Json.dll계약의 Json()JToken을 내므로 그 타입이 있는 어셈블리도 필요합니다. 패키지 참조가 아니라 파일로 두는 이유는 위와 같습니다 — 받은 사람이 아무것도 복원하지 않아도 성립해야 합니다

액세서는 어셈블리로 들어갑니다.

그 소스는 실행할 때 임시 폴더에서 컴파일되고 지워집니다. 아무도 편집하지 않고 컴파일러만 읽던 파일이라, 테이블마다 한 장씩 프로젝트에 둘 이유가 없었습니다.

context.Tables가 성립하는 방식도 여기 있습니다.

계약 어셈블리는 이 도구와 함께 만들어지고 액세서는 남의 시트에서 실행 중에 만들어지므로, 계약이 액세서의 타입을 선언할 방법이 없습니다.

그래서 계약에는 형 없는 슬롯 하나만 두고, 생성된 어셈블리가 그 위에 확장 속성을 얹습니다. context.Tables는 그 확장이고, 돌려주는 것은 생성된 스냅숏 타입이라 필드가 전부 typed입니다.

슬롯 쪽은 자동 완성에서 숨겨 둡니다. 규칙이 쓸 이름은 그 옆의 것 하나입니다.

확장 속성은 C# 14의 것이고, 이 어셈블리는 호스트에서만 컴파일되고 게임에 실리지 않으므로 유니티 하한과 무관합니다.

빌드가 곧 검증

편집기에서 이 프로젝트를 빌드하면(대개 Ctrl+Shift+B) 두 가지가 한 번에 일어납니다.

  1. 컴파일러가 규칙을 검사합니다 — 없는 컬럼, 없는 enum 라벨은 여기서 걸립니다.
  2. 그 다음 도구가 규칙을 실제 데이터에 대고 돌립니다. 보고가 빌드 출력에 그대로 나오고, 검증이 실패하면 빌드가 실패합니다.

터미널로 옮겨가지 않고 규칙을 고치고 바로 돌려볼 수 있습니다.

알아둘 것내용
tabbitPATH에 있어야 합니다없으면 규칙 컴파일까지만 하고 「PATH에 없다」고 경고합니다. 검증이 통과한 것과 구분됩니다
끄려면dotnet build -p:TabbitValidate=false. 규칙을 쓰는 중이라 데이터 쪽 보고가 뻔할 때 씁니다
경로프로젝트가 적는 것은 전부 상대경로입니다 — 그래서 이 파일이 커밋 가능합니다

셋 다 이 도구가 쓰지만 커밋 대상입니다.

프로젝트가 가리키는 것이 전부 자기 폴더 안에 있으므로 받은 사람의 기계에서 그대로 성립합니다. 전에는 도구가 있던 머신의 절대경로를 가리켜서 그럴 수 없었습니다.

바이트가 달라졌을 때만 다시 씁니다.

아무것도 바뀌지 않은 실행이 저장소에서 변경으로 보이지 않게 하기 위해서입니다. 스키마가 바뀌면 lib/의 액세서에 diff가 나는데, 그것은 규칙이 할 수 있는 말이 실제로 달라졌다는 신호입니다.

빌드 산출물은 .build/ 한 곳으로 갑니다.

bin/obj/가 규칙 폴더 옆에 생기면 열어보는 사람이 가장 먼저 보는 것이 그 둘이 되기 때문입니다. 프로젝트가 스스로 그렇게 지정하므로 무시 규칙이 필요한 것이 아니라 애초에 생기지 않습니다.

끄려면 이렇게 합니다. 아래 각주의 오래된 Visual Studio가 그 경우입니다.

"Validation": {
"Path": "./validation",
"EmitIdeProject": false
}

프로젝트가 검증 폴더의 루트에 있고 점으로 시작하는 폴더 안이 아닌 것도 이유가 있습니다.

편집기는 프로젝트를 찾을 때 그런 폴더를 건너뜁니다. 숨긴 폴더에 둔 프로젝트는 어느 편집기도 찾지 못하는 프로젝트입니다.

편집기에서 여는 폴더

VS Code에서 validation/ 폴더를 열거나 워크스페이스 폴더로 추가하세요.

저장소 루트를 열면 되지 않습니다.

C# Dev Kit은 워크스페이스에서 솔루션이나 프로젝트를 하나 골라 로드하고, 루트에는 Tabbit.slnx가 있어 그것이 우선합니다.

검증 폴더를 열면 그 안의 Validation.csproj가 유일한 후보가 됩니다.

확인무엇이 보이면 되는 것인가
솔루션 탐색기Validation 프로젝트와 그 아래 Dependenciestabbit
Tables. 을 타이핑테이블 목록. 이어서 . 을 치면 그 테이블의 컬럼
context. 을 타이핑Error · Warn · Info · Option · Files · Db · Redis
경로 표시(breadcrumb)ItemRules.cs > ItemRules > Validate() : void — 의미 분석이 되고 있다는 표시

Visual Studio 2022 이하는 대상이 아닙니다. 이 저장소가 쓰는 프레임워크보다 오래된 버전이라 프로젝트를 열지 못합니다 — Microsoft.NET.Sdk를 찾을 수 없다는 오류가 나고, src/Tabbit.csproj 도 같은 이유로 열리지 않습니다.

규칙 파일이 클래스로 감싸여 있는 이유

자동 완성이 이 형태를 정했습니다.

처음 형태는 클래스 없이 최상위 문장을 쓰는 파일이었고, 그것이 자동 완성을 얻지 못한다는 것을 실측으로 확인했습니다.

규칙 파일 셋에 존재하지 않는 심볼을 하나씩 넣고 컴파일러가 그것을 검출하는지 확인했습니다. 검출한다면 의미 분석이 되고 있다는 뜻입니다.

규칙 파일 형태보고된 오류해석
최상위 문장CS8802 4개. CS0103 0개최상위 문장이 컴파일 단위 하나에만 허용되므로 나머지 파일은 본문이 바인딩되지 않습니다
클래스로 감싼 형태 (지금)CS0103 6개 — 세 심볼 전부전부 분석됩니다

#:project로 프로젝트를 가리키는 파일 기반 프로그램 방식도 시도했고 되지 않았습니다.

그것은 C# 문법이 아니라 SDK가 컴파일 전에 걷어내는 지시자여서, 편집기의 Roslyn은 1행의 구문 오류로 보고 그 파일의 컴파일을 중단합니다.

남는 것은 평범한 클래스와 평범한 using 두 줄이고, 그것이 지금의 형태입니다.

잃은 것은 「파일을 열면 바로 검사문」이라는 성질이고, 얻은 것은 컬럼 378개를 기억하지 않아도 되는 것입니다. 그 교환은 할 만합니다.

준비 없이도 검출되는 오타

위의 둘을 하지 않아도 컴파일러가 검출합니다. 편집기가 아니라 실행에서 나옵니다.

[rules/tables/ItemRules.cs] 'Grade'에는 'Legend'에 대한 정의가 포함되어 있지 않습니다.
at .../validation/rules/tables/ItemRules.cs(17,33)

없는 컬럼, 없는 enum 값, 타입이 맞지 않는 비교가 데이터를 한 줄도 읽기 전에 파일과 줄 번호로 보고됩니다.

자동 완성은 타이핑 중에 나오는 편의이고, 잘못을 검출하는 것은 어느 쪽이든 됩니다.

18. 검증 자체를 검증하기

규칙이 늘면 그것들이 옳은지 누가 보는가가 남습니다.

잘못된 검증은 통과시키거나, 더 나쁘게는 옳은 데이터를 차단합니다.

이 저장소가 쓰는 방법은 픽스처 워크북과 기대 진단을 짝지어 게이트로 두는 것입니다. test/fixtures/validation/에 그 예가 있고, ValidationPipelineTestsValidationRuntimeTests가 심각도별 동작과 산출물이 실제로 없는 것을 확인합니다.

규칙별 억제 목록은 두지 않습니다. 표시 없는 통과를 recipe에 적어두는 장치이고, 억제가 필요한 규칙은 규칙이 틀린 것입니다.