본문으로 건너뛰기

이름 표기 규약

상태: 1·2단계 구현 완료 (2026-08-20) · 3단계 설계 · 근거 조사 2026-08-20 (이름 검증 경로 전수 · 전 언어 파일 이름 실측) · 관련: 생성 코드의 이름 체계

네 가지를 추가합니다.

  1. 시트 이름의 표기 규약을 레시피가 선언하고, 코어가 강제합니다Naming 섹션 (§3).
  2. 선언이 없어도 동작하는 방어선을 둡니다 — 표기 충돌 검사와 연속 밑줄 검사 (§4).
  3. 생성 코드의 멤버 표기를 타깃별로 재정의합니다MemberCase (§5).
  4. 파일 이름의 표기를 재정의합니다 — 코드 파일은 타깃별 FileCase, 데이터 파일은 레시피 전역 DataFileCase (§6).

기본값은 전부 현행입니다. 아무것도 선언하지 않은 레시피의 산출물은 바이트 단위로 동일하고, 새로 나타나는 것은 §4의 보고 2종뿐입니다.


1. 문제 — 시트 표기 편차의 코드 전파

같은 필드를 시트마다 maxHitPoints · maxhitpoints · maxhitPoints로 적는 사례가 실제로 있습니다. 정규화(ToPascalCase)는 단어 경계를 입력의 대소문자에서 얻으므로, 세 표기는 서로 다른 이름으로 정규화됩니다.

시트 표기정규화 결과
maxHitPointsMaxHitPoints
maxhitpointsMaxhitpoints
maxhitPointsMaxhitPoints

같은 개념이 세 갈래의 프로퍼티·접근자로 생성되고, 소비 코드는 테이블마다 다른 이름을 참조하게 됩니다. 표기가 하나 더 늘 때마다 생성 코드에 유사 중복이 하나 더 쌓입니다.

규모는 가설이 아닙니다. 이전 대규모 코퍼스의 배포 데이터(테이블 531개 · 컬럼 6,439개)를 2026-08에 실측한 결과가 있습니다 — 대소문자만 다른 표기가 공존하는 이름이 102종, 그중 한 이름은 3가지 표기로 40개 테이블에 흩어져 있었습니다. 그 비용은 표기에 그치지 않았습니다. 소비 코드 310곳이 각각 「이 테이블은 어느 표기인가」를 기억해야 하였고, 두 표기를 모두 읽는 방어 코드가 들어갔으며, 확인된 이용자 영향 결함 4건 중 3건이 표기 혼재에서 나왔습니다. 동적 언어에서 잘못된 표기의 읽기는 오류가 아니라 값 없음이므로, 증상은 감지되지 않는 기능 누락으로 나타났습니다.

이름 규칙이 모호하면 그 부담은 사라지는 것이 아니라 런타임으로 떠넘겨지고, Lua처럼 타입 선언이 없는 언어에서 가장 불안정해집니다. 존재하지 않는 필드의 읽기가 오류가 아니라 값 없음이기 때문입니다. 위 실측의 결함 하나가 그 구조를 그대로 보여줍니다 — 컬럼은 type인데 한 호출부가 Type으로 읽었습니다.

local isMaterial = item.type == ITEM_TYPE.PLAN or item.Type == ITEM_TYPE.MATERIAL
-- ^^^^ 정상 ^^^^ 항상 nil, 뒤 비교는 항상 거짓

예외도 로그도 없이 한 분류의 아이템이 판매 목록에서 통째로 빠졌고, 컴파일 오류가 없으므로 발견 경로는 이용자 제보뿐입니다. 표기가 테이블마다 다르면 이 실수는 「언젠가 나는」 것이 아니라 호출부 수에 비례해 나는 것이 됩니다.

시트가 표기를 보장하지 않으면 소비 코드가 그 보장을 런타임 검사로 대신하게 됩니다. 실측에는 이미 그런 방어 코드가 들어가 있습니다.

-- 어느 표기인지 확신할 수 없어 두 표기를 모두 읽는다
return row and (row.maxHitPoints or row.MaxHitPoints)

이 한 줄이 들어가는 순간 정본이 무엇인지는 코드가 더 이상 답하지 않고, 표기가 정리된 뒤에도 이 줄은 제거되지 않습니다 — 지워도 되는지 아무도 확신할 수 없기 때문입니다.

정적 타입 언어도 온전히 안전하지는 않습니다. 타입 선언 자체를 시트 표기에서 옮겨 적으므로, 표기를 틀리게 옮기면 선언과 코드가 함께 틀려 타입 검사가 통과합니다.

interface ShopRow { buffId: number; } // 시트 컬럼은 BuffId
if (shop.buffId) { ... } // 컴파일 통과, 값은 항상 undefined

이름 검사를 변환 시점으로 옮기는 것은 이 떠넘김을 끊는 것입니다 — 런타임의 모든 호출부가 각자 방어하는 대신, 시트 앞의 한 곳에서 표기를 보장합니다.

현재 코어가 이름에 대해 강제하는 것은 두 가지뿐입니다.

검사위치
문자 제약 (IsValidIdentifier)CookingContext.RequiresIdentifier — 레이아웃 파서에서 11곳 호출
정확 일치 중복필드·엔티티·enum 라벨·상수 4종

표기 규약 검사 — 「필드는 camelCase로 적는다」 — 는 코어에 없습니다. 현재의 유일한 경로는 사용자가 validation/rules/에 C# 규칙을 직접 작성하는 것인데(doc/validation.mdNaming.cs 예시), 프로젝트마다 같은 규칙을 다시 작성해야 합니다.

1.1 사례와 대응

이 스펙의 나머지는 이 표의 전개입니다 — 시트에서 실제로 나타나는 사례마다 대응을 하나씩 둡니다.

사례무엇이 일어나는가대응
maxHitPoints · maxhitpoints · maxhitPoints표기마다 다르게 정규화되어 생성 코드가 갈라집니다표기 충돌 검사 (§4.1) — 선언 없이 항상 수행. 생성물이 갈라지므로 기본 warn
my_flag · myFlag생성물은 같으나 시트 표기가 어긋납니다. 방치되면 갈라지는 표기로 이어집니다같은 검사가 한 단계 낮은 무게로 보고 (§4.1) — 기본 info
a_b · a__b · a___b밑줄 개수의 차이는 어느 생성물에도 반영되지 않습니다연속 밑줄 검사 (§4.2) — 선언 없이 항상 수행
Maxhitpoints · max_hitcamel 규약에서선언한 규약 밖의 표기입니다왕복 검사 (§3) — OnViolation 기본 error
다수가 어긋나고 소수가 규약에 맞는 계열다수를 따르는 개명이 잘못된 방향입니다권장 표기의 선택 (§4.1) — 규약이 다수보다 우선
기존 모델의 위반 수백 건전량 오류는 도입이 성립하지 않고, 전량 경고는 새 위반이 묻힙니다Exempt 목록 (§3.3) — 신규 유입만 차단하고 계열 단위로 개명
maxhitpoints 단독 — 경계가 소실된 이름의 첫 출현어느 검사로도 검출되지 않습니다한계로 명시 (§9) — snake 계열 규약이 구조적으로 회피
applypointId · applyPointId — 둘 다 camel을 만족규약으로는 어느 쪽이 맞는지 판별할 수 없습니다한계로 명시 (§9) — 이 경우에만 다수 표기를 제시
보고를 받고도 무엇을 적어야 할지 모르는 것표기 규칙을 사람이 다시 해석해 손으로 적용해야 합니다진단이 고쳐 쓸 이름으로 끝납니다 (§3.2) — 세 검사 전부

2. 설계 원칙

  • 정본은 시트입니다. 도구는 어긋난 표기를 고쳐 쓰지 않고 위치와 함께 보고합니다. maxhitpoints에서 소실된 단어 경계는 복원의 대상이 아니라 시트 수정의 대상입니다.
  • 규약의 내용은 레시피가 정하고, 코어는 검사 장치만 제공합니다. 특정 팀의 표기 규칙이 코어에 들어가지 않습니다.
  • 판정과 변환은 같은 코드를 씁니다. 규약 판정은 왕복 동일성(§3)이므로, 두문자어 처리 같은 세부에서 판정기와 정규화기가 어긋날 수 없습니다.

이 문서의 나머지

무엇어디
시트 표기의 선언과 강제Naming 으로 규약을 선언하는 것과, 선언이 없을 때의 방어선
생성 표기 — 멤버와 파일 이름생성 코드의 멤버 표기와 파일 이름 표기를 다시 정하는 법
단계 · 미채택 · 한계구현 단계와 그 파급, 미채택한 것, 지금의 한계, 게이트