스키마를 다듬다 보면 표준 키워드만으로 채울 수 없는 정보가 나옵니다.
userId는 internal이고 email은 confidential이라는 사실을 스키마 어디에 적어야 도구가 그 정보를 가져갈 수 있을까요? SLA 정보도 마찬가지입니다. “이 필드는 5분 이내 업데이트가 보장됩니다” 같은 약속을 남기고 싶은데, title과 description만으로는 형식이 잡히지 않습니다.
4편에서는 어노테이션(annotation) 키워드로 의미와 맥락을 담는 방법을 다뤘습니다. 그러나 이 어노테이션이 모든 의미를 담아 주지는 못합니다. 조직 안에서 통용되는 데이터 분류 등급, 산업 표준이 정해 둔 라이선스 코드, 데이터셋의 출처와 갱신 주기 같은 정보가 그렇습니다.
이런 자리에서 JSON 스키마는 자기 어휘를 만들 수 있는 길을 열어 둡니다. 5편에서는 표준 어휘를 넘어서는 두 가지의 확장 방법을 풀어 보겠습니다.
1. 간이 확장과 정식 어휘, 두 가지 방법
표준 어휘로 부족할 때 JSON 스키마에는 두 가지 확장 방법이 있습니다.
| 방법 | 방식 | 비용 | 효과 | 적합한 상황 |
|---|---|---|---|---|
| 간이 확장 | 비표준 키워드를 스키마에 그대로 적고, 어노테이션처럼 회수 | 작음 (즉시 시작) | 한 팀 안에서 빠르게 통용 | 실험적 활용, 단일 프로젝트 |
| 정식 어휘 | 명세 문서 작성 → 메타스키마로 문법 형식화 → 밸리데이터(validator) 확장 | 큼 (3단계 작업) | 도구 간 일관성, 형식적 검증 | 조직 단위 표준화, 산업 표준화 |
1) 간이 확장: 알려지지 않은 키워드
간이 확장은 표준에 없는 키워드를 스키마에 그대로 적어 두는 방법입니다. 별도의 명세나 메타스키마 없이, 원하는 키워드를 스키마 필드에 추가하면 됩니다. 이게 가능한 이유는 JSON 스키마 밸리데이터(validator, 검증기)가 모르는 키워드를 만났을 때 오류를 내지 않고 그냥 무시하기 때문입니다. 어서션(assertion)으로 취급하지 않고 어노테이션처럼 넘기는 것이 명세가 정한 기본 동작입니다.
비표준 키워드 이름을 지을 때는 접두사 선택이 중요합니다. 흔히 쓰이는 세 가지 접두사가 있고, 각각 쓰임새가 다릅니다.
| 접두사 | 예시 | 배경 | 권장 여부 |
|---|---|---|---|
x- | x-classification | HTTP 헤더·이메일 헤더에서 비표준을 표시하던 관행 | 비권장 (RFC 6648에서 폐기 권고) |
$ | $classification | JSON 스키마 코어 키워드 영역 | 사용 금지 ($schema·$id·$ref·$vocabulary 등 코어 네임스페이스와 충돌) |
| 도메인 접두사 | myorg:classification | 조직·도메인 네임스페이스를 콜론으로 분리 | 권장 (충돌 방지, 출처 명확) |
도메인 접두사가 충돌 방지와 출처 전달을 모두 충족하는 선택입니다.
예시
회원가입 이벤트 스키마에 데이터 분류 등급을 명시하는 경우를 보겠습니다. 표준 키워드에는 없는 정보이지만, 스키마에 그대로 적어 둘 수 있습니다.
{
"$JSON 스키마": "<https://json-JSON> 스키마.org/draft/2020-12/JSON 스키마",
"$id": "<https://example.com/JSON> 스키마s/signup-event",
"type": "object",
"properties": {
"userId": {
"type": "string",
"format": "uuid",
"myorg:classification": "internal"
// 비표준 키워드: 밸리데이터는 이 키워드를 모르므로 무시
},
"email": {
"type": "string",
"format": "email",
"myorg:classification": "confidential"
},
"marketingConsent": {
"type": "boolean",
"myorg:classification": "public"
}
}
}
myorg:classification은 표준 키워드가 아닙니다. 밸리데이터는 이 키워드를 모르므로 검증에 영향을 주지 않습니다. userId가 UUID 형식의 문자열이고 email이 이메일 형식이라면 검증은 그대로 통과합니다. myorg:classification은 스키마 안에 남아 있지만 어서션으로 작동하지 않습니다.
다만 스키마에 적어 둔 것만으로 도구가 알아서 값을 꺼내 쓰지는 않습니다. 3편에서 다뤘던 어노테이션 추출을 통해 검증 결과에서 myorg:classification 값을 회수해야, 데이터 카탈로그가 email 필드를 confidential로 분류하거나 CI 파이프라인이 해당 필드의 로깅을 차단하는 흐름까지 연결됩니다.
2) 간이 확장의 장점과 한계
| 측면 | 장점 | 한계 |
|---|---|---|
| 시작 비용 | 별도 인프라 없이 즉시 시작 가능 | 형식적 정의가 없어 의미가 흩어질 수 있음 |
| 도구 호환성 | 밸리데이터가 무시하므로 검증이 깨지지 않음 | 밸리데이터마다 회수 방식이 달라 도구 간 호환성이 떨어짐 |
| 검증 | 인스턴스 검증에 영향 없음 | 키워드 값이 잘못 적혀도 알아차리지 못함 (형식적 검증 부재) |
| 유지 보수 | 한 팀 안에서 가볍게 갱신 가능 | 같은 키워드를 다른 사람이 다른 의미로 쓸 위험 |
간이 확장을 도입할 때 판단 기준은 그 키워드를 누가 읽느냐입니다. 같은 팀의 내부 스크립트 한두 개가 읽는 수준이라면 간이 확장으로 충분합니다. 다른 팀의 밸리데이터, 외부 파트너의 데이터 카탈로그, CI 파이프라인까지 읽어야 한다면 키워드의 이름·값 형식·필수 여부를 형식적으로 정의해야 합니다.
3) 알려지지 않은 키워드를 어노테이션으로 회수하기
스키마에 적어 둔 비표준 키워드를 도구가 실제로 꺼내 쓰려면 밸리데이터의 어노테이션 출력에 해당 키워드가 포함되어야 합니다. 여기서 밸리데이터마다 처리 방식이 갈립니다. 기본 동작으로 알려지지 않은 키워드를 어노테이션 출력에 포함하는 밸리데이터가 있고, 완전히 무시해서 출력에 담지 않는 밸리데이터가 있습니다. 후자도 옵션을 켜면 수집하는 경우가 있습니다. 이 차이를 모르면 같은 스키마가 도구에 따라 다르게 동작합니다.
| 밸리데이터 | 기본 동작 | 비표준 키워드 어노테이션 수집 |
|---|---|---|
| Hyperjump | 어노테이션 출력에 포함 | 기본 지원 |
| Ajv | 무시 | 별도 플러그인 필요 |
| json-everything (.NET) | 무시 | 옵션 활성화 시 수집 |
Hyperjump처럼 기본 지원하는 밸리데이터를 쓰면, 검증 결과에 myorg:classification: "internal" 같은 값이 함께 딸려 나옵니다. 도구는 그 결과를 받아서 데이터 카탈로그를 갱신하거나, 보안 정책을 평가하거나, 사용자 정의 UI를 그릴 수 있습니다.
회수된 어노테이션이 실무에서 어떤 문제를 푸는지 정리하면 다음과 같습니다.
| 활용 영역 | 회수 시나리오 | 푸는 문제 |
|---|---|---|
| 데이터 카탈로그 | myorg:classification 값을 카탈로그에 자동 반영 | 분류 등급을 위키에 수동 동기화하는 부담 |
| 보안·컴플라이언스 | confidential 등급 필드에 자동 마스킹 적용 | 정책 적용이 사람 판단에 의존하는 문제 |
| SLA 모니터링 | myorg:freshness 값으로 데이터 갱신 주기 점검 | SLA 약속을 형식적으로 추적할 수단이 없는 문제 |
| 내부 도구 | 조직 내부 도구가 어노테이션을 받아 화면에 표시 | 도구마다 스키마를 직접 파싱하는 코드 중복 |
앞 섹션의 장단점 표에서 정리한 것처럼, 간이 확장은 한 팀 안에서는 충분하지만 키워드가 여러 팀·여러 조직으로 퍼지면 이름 충돌, 값 형식 불일치, 오타 검증 부재 같은 문제가 누적됩니다. 이 한계를 넘으려면 키워드의 이름·값 형식·필수 여부를 형식적으로 정의해야 합니다. 그것이 정식 어휘입니다.
2. 정식 어휘 작성: 3단계 작성법
보캐뷸러리는 1편에서 다뤘던 JSON 스키마 구성 요소의 한 층입니다. 같은 목적의 키워드들이 묶여 의미와 문법을 공유하는 단위이고, 2020-12 다이얼렉트는 7가지 공식 보캐뷸러리(Core·Applicator·Unevaluated·Validation·Meta-Data·Format-Annotation·Content)를 가집니다. 자기 어휘를 만든다는 것은 이 묶음을 하나 더 추가한다는 뜻입니다.
간이 확장으로 풀리지 않는 경우에 정식 어휘가 필요합니다. 같은 메타데이터를 여러 팀이 일관되게 써야 할 때, 같은 산업의 회사들이 공유하는 표준 어휘가 필요할 때, 키워드 값의 오타나 형식 오류를 밸리데이터가 잡아내야 할 때가 그렇습니다.
정식 어휘 작성은 명세, 메타스키마, 구현체 확장의 3단계로 구성됩니다.
1. 명세 2. 메타스키마 3. 구현체 확장
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ 키워드 이름 │ │ $vocabulary로 │ │ 밸리데이터에 │
│ 값의 타입·제약 │ ──►│ 어휘 선언 │ ──►│ 키워드 처리 │
│ 검증/어노테이션 │ │ 키워드별 타입· │ │ 코드 추가 │
│ 동작 정의 │ │ 제약 형식화 │ │ │
└────────────────┘ └────────────────┘ └────────────────┘
1단계: 명세
키워드의 이름, 값의 타입, 검증으로 작동할지 어노테이션으로 작동할지를 글로 정의합니다. 조직의 분류 등급 어휘라면 “myorg:classification은 문자열 타입이며 public·internal·confidential·restricted 중 하나의 값을 가진다” 같은 정의가 들어갑니다.
2단계: 메타스키마
1단계의 명세를 JSON 스키마로 형식화한 문서입니다. $vocabulary 키워드로 새 어휘를 선언하고, 각 키워드의 타입·제약을 정의합니다. 이 문서가 있어야 밸리데이터가 새 어휘의 문법을 인식합니다.
3단계: 구현체 확장
밸리데이터에 새 어휘의 처리 코드를 추가하는 단계입니다. 어휘 식별자를 등록하고, 키워드마다 검증·어노테이션 동작을 구현합니다. 가장 무거운 작업이지만, 가벼운 어휘라면 기존 밸리데이터의 플러그인 메커니즘으로 처리할 수 있습니다.
1) 어휘 식별자와 $vocabulary 키워드
간이 확장에서 만든 myorg:classification 키워드에는 값의 형식을 강제할 수단이 없습니다. 누군가 "confidential" 대신 "confidentil"을 적어도, "CONFIDENTIAL"로 대소문자를 바꿔도 밸리데이터는 아무 말 없이 통과시킵니다. 정식 어휘는 이 키워드의 허용 값을 메타스키마에서 정의해서 밸리데이터가 잡아내게 만듭니다.
먼저 사용자 어휘의 키워드 문법을 정의하는 스키마가 필요합니다.
{
// 사용자 어휘의 키워드 문법 정의
"$schema": "<https://json-schema.org/draft/2020-12/schema>",
"$id": "<https://example.com/meta/data-classification/vocab>",
"properties": {
"myorg:classification": {
// 이 키워드의 값은 아래 네 가지 중 하나여야 한다
"enum": ["public", "internal", "confidential", "restricted"]
// "confidentil" 같은 오타는 여기서 걸린다
}
}
}
이 스키마를 밸리데이터가 인식하려면, 다이얼렉트 메타스키마에 등록해야 합니다. 등록에 쓰이는 키워드가 $vocabulary입니다. $vocabulary는 어휘 식별자(vocabulary identifier) URI와 불리언 쌍으로, 이 메타스키마가 어떤 어휘들을 사용하는지 선언합니다.
{
"$schema": "<https://json-schema.org/draft/2020-12/schema>",
"$id": "<https://example.com/meta/data-classification>",
"$vocabulary": {
// 표준 어휘: 필수 (true)
"<https://json-schema.org/draft/2020-12/vocab/core>": true,
"<https://json-schema.org/draft/2020-12/vocab/applicator>": true,
"<https://json-schema.org/draft/2020-12/vocab/validation>": true,
"<https://json-schema.org/draft/2020-12/vocab/meta-data>": true,
// 사용자 어휘: true이므로 이 어휘를 모르는 밸리데이터는
// 이 메타스키마를 따르는 스키마를 처리할 수 없다
"<https://example.com/vocab/data-classification>": true
},
"$dynamicAnchor": "meta",
"allOf": [
{ "$ref": "<https://json-schema.org/draft/2020-12/schema>" },
// 위에서 정의한 키워드 문법 스키마를 참조
{ "$ref": "<https://example.com/meta/data-classification/vocab>" }
]
}
$vocabulary의 불리언 값이 밸리데이터의 동작을 결정합니다.
| 불리언 값 | 의미 | 밸리데이터의 동작 |
|---|---|---|
true | 필수 어휘 | 밸리데이터가 이 어휘를 모르면 스키마 처리를 거부 |
false | 선택 어휘 | 밸리데이터가 이 어휘를 모르면 무시하고 진행 |
이제 회원가입 이벤트 스키마의 $schema를 이 메타스키마로 바꿔 보겠습니다.
{
// 사용자 정의 메타스키마를 $schema로 지정
"$schema": "<https://example.com/meta/data-classification>",
"$id": "<https://example.com/schemas/signup-event>",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"myorg:classification": "confidential" // 허용 값 → 통과
},
"marketingConsent": {
"type": "boolean",
"myorg:classification": "pubic" // 오타 → 밸리데이터가 거부
}
}
}
간이 확장이었다면 "pubic"이 그대로 통과했을 것입니다. 정식 어휘에서는 메타스키마가 enum으로 허용 값을 정의하고 있으므로, 밸리데이터가 스키마 자체의 문법 오류로 잡아냅니다.
어휘 식별자가 URI인 이유는 두 가지입니다. 도메인 단위로 네임스페이스가 나뉘므로 다른 조직의 어휘와 이름이 겹치지 않고, URI를 따라가면 해당 어휘의 명세 문서나 메타스키마 위치를 찾을 수 있습니다. 다만 식별자 URI가 실제 문서를 가리키지 않아도 동작하는 밸리데이터가 많습니다. URI는 식별 용도이지, 반드시 네트워크에서 가져와야 한다는 약속은 아닙니다.
$vocabulary는 JSON 스키마 명세상 다이얼렉트 메타스키마에만 둘 수 있습니다. 일반 스키마에 넣으면 동작하지 않습니다. 2020-12 공식 메타스키마도 같은 구조로, Core·Applicator·Unevaluated·Validation·Meta-Data·Format-Annotation·Content 7가지 어휘를 $vocabulary에 선언하고 allOf로 각 어휘의 문법 스키마를 참조합니다. 사용자 정의 메타스키마는 이 구조를 그대로 따르면 됩니다.
3. 간이 확장과 정식 어휘, 어느 쪽을 선택할 것인가
정식 어휘는 명세·메타스키마·구현체 확장의 3단계를 거치고, 한 번 만들면 가볍게 바꿀 수 없습니다. 간이 확장으로 충분한 곳에서 정식 어휘를 만들면 비용이 효과를 넘고, 정식 어휘가 필요한 곳에서 간이 확장으로 버티면 도구 간 호환성이 무너집니다. 다음 질문으로 판단할 수 있습니다.
| 점검 질문 | 그렇다 | 아니다 |
|---|---|---|
| 같은 키워드를 여러 팀 또는 여러 조직이 일관되게 써야 하는가 | 정식 어휘 검토 | 간이 확장으로 충분 |
| 외부 파트너와 데이터를 교환하는가 | 정식 어휘 검토 | 간이 확장으로 충분 |
| 키워드 값의 오타나 형식 오류를 밸리데이터가 잡아내야 하는가 | 정식 어휘 검토 | 간이 확장으로 충분 |
| 키워드를 인식하는 도구를 만들거나 통합할 계획이 있는가 | 정식 어휘 검토 | 간이 확장으로 충분 |
| 키워드 정의가 자주 바뀔 영역인가 | 간이 확장으로 시작 | 정식 어휘 가능 |
| 한 팀·한 프로젝트 안에서만 쓰는가 | 간이 확장으로 충분 | 정식 어휘 검토 |
위 네 질문 중 하나라도 “그렇다”이고, 아래 두 질문이 모두 “아니다”라면 정식 어휘를 검토할 시점입니다. 반대로 위 네 질문이 모두 “아니다”이거나, 아래 두 질문 중 하나라도 “그렇다”라면 간이 확장으로 시작하는 쪽이 합리적입니다. 간이 확장으로 시작해서 여러 팀이 같은 키워드를 다른 의미로 쓰기 시작하거나, 밸리데이터마다 처리 방식이 달라 문제가 생기면 그때 정식 어휘로 옮겨가면 됩니다.
마무리
5편에서는 표준 키워드로 채울 수 없는 정보를 스키마에 담는 두 가지 방법을 다뤘습니다. 간이 확장은 비표준 키워드를 스키마에 그대로 적어 두는 방법이고, 정식 어휘는 명세·메타스키마·구현체 확장을 거쳐 키워드의 의미와 문법을 형식적으로 정의하는 방법입니다. 한 팀 안에서 빠르게 시작할 때는 간이 확장으로 충분하고, 여러 팀이 같은 키워드를 일관되게 써야 하거나 밸리데이터가 키워드 값의 오류를 잡아내야 할 때 정식 어휘로 넘어가면 됩니다.
6편에서는 만들어진 스키마와 어휘를 운영하는 단계를 다룹니다. 스키마를 어디에 호스팅할지, 여러 스키마를 어떻게 번들로 묶을지, 변경의 영향 범위를 어떻게 추적할지가 주제입니다.
JSON 시리즈

