API 연결이나 테이블 설계와 같은 업무 회의를 들어가면, 종종 마주치는 질문들이 있습니다.
- “이 필드는 뭐예요?”
- “이 값은 nullable인가요?”
- “이 배열에 빈 값이 들어와도 괜찮나요?”
분명히 위키나 노션에 정의가 적혀 있다고 누군가 말하지만, 막상 열어 보면 코드와 다른 데가 한두 군데가 아닙니다. 프로젝트 중 데이터 필드가 한 줄 늘어났을 때 그 변경 사항을 누가 적어 둘지도 불분명합니다.
이런 상황이 반복되는 이유는, 조직이 데이터를 말로 설명하기 때문입니다. 말로 한 설명은 검증되지 않고, 업데이트되지 않고, 시스템과 연결되지 않습니다. 위키 문서는 그대로 있지만, 코드 안의 데이터는 매주 조금씩 모양이 바뀝니다. 이 둘 사이의 간격이 누적되면 결국 합의가 무너집니다.
이 문제 의식을 6편에 걸쳐 따라갑니다. 도입 결정 → 첫 스키마 작성 → 검증 → 의미 부여 → 어휘 확장 → 운영 순서로, JSON 스키마를 조직에 들이는 실무 단계를 짚어 갑니다.
1편에서는 JSON이 데이터 교환의 기본이 된 이유에서 출발해, JSON 스키마가 단순 검증 도구를 넘어 무엇을 풀어주는지, 깊이 이해하기 위해 잡아 둘 어휘는 무엇인지, 도입을 결정할 때 기준이 되는 신호는 무엇인지를 다룹니다.
1. JSON이 데이터 교환의 표준이 된 이유
JSON 공식 사이트는 JSON을 자바스크립트 객체 표기법(JavaScript Object Notation)에서 출발한 경량 데이터 교환 형식으로 정의합니다.
JSON 스키마 도입을 결정하기 전에, JSON이 왜 데이터 교환의 기본이 됐는지부터 짚어 두는 게 순서입니다. 이 토대를 잡아야 JSON 스키마가 풀려는 문제가 어떤 모양인지 보입니다.
1) XML의 단점
<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>김서연</name>
<age>29</age>
<email>seoyeon@example.com</email>
<roles>
<role>admin</role>
<role>editor</role>
</roles>
</user>
JSON 이전에는 XML(eXtensible Markup Language)이 데이터 교환의 주축이었습니다. XML은 마크업 기반으로 풍부한 표현력을 제공했지만, 무겁고 장황했습니다. 같은 데이터를 표현해도 태그가 두 번씩 등장하니 문서 크기가 커지고, 사람이 한눈에 읽기에는 노이즈가 많았습니다. DTD(Document Type Definition)나 XSD(XML Schema Definition) 같은 스키마 시스템도 학습 비용이 컸습니다.
2010년대에 들어 모바일과 단일 페이지 웹 애플리케이션이 빠르게 자리 잡으면서, 가벼운 데이터 교환 형식의 수요가 커졌습니다. REST API의 보급과 맞물려 JSON은 모바일·웹 API 영역에서 주류 형식이 되었고, XML은 특정 도메인(SOAP, 금융 메시징, 일부 정부 시스템 등)에 남는 흐름으로 정리되었습니다. 두 형식의 차이를 정리하면 다음과 같습니다.
{
"name": "김서연",
"age": 29,
"email": "seoyeon@example.com",
"roles": ["admin", "editor"]
}
| 비교 축 | XML | JSON |
|---|---|---|
| 표기 기반 | 마크업(태그) | 객체·배열(키-값) |
| 무게 | 무거움 (여닫는 태그 반복) | 가벼움 (구조가 짧음) |
| 사람이 읽기 | 노이즈가 많음 | 직관적 |
| 스키마 시스템 | DTD, XSD (별도 문법) | JSON 스키마 (JSON 문법 그대로) |
| 활용 자리 | SOAP, 문서 교환, 금융 메시징 | REST API, 모바일·웹, 설정 파일 |
| 진화사상의 위치 | 2000년대 표준 | 2010년대 이후 주류 표준 |
JSON은 사람이 읽고 쓰기 쉽고, 기계가 분석하고 생성하기 쉬운 형식이라는 두 가지 강점을 동시에 가집니다. 2017년에 JSON RFC 8259로 인터넷 표준이 정리되면서 데이터 교환 형식의 주류 표준이 굳어졌습니다.
2) JSON 문법
JSON RFC 8259가 정의하는 데이터 타입은 6가지입니다. 이 6가지가 JSON으로 표현 가능한 모든 데이터의 토대입니다.
| 데이터 타입 | 설명 | 표기 | 예시 |
|---|---|---|---|
| 불리언(Boolean) | 참 또는 거짓을 나타내는 논리값 | true 또는 false (소문자, 따옴표 없음) | true |
| 숫자(Number) | 정수와 실수를 모두 포함하는 수치값. 별도의 int/float 구분 없이 하나의 타입으로 처리 | 정수·실수, 십진·지수 표기 | 5, -47.32, -5.3E4 |
| 문자열(String) | 텍스트 데이터. 큰따옴표만 허용되며, 작은따옴표는 사용 불가 | 큰따옴표로 감싼 유니코드 문자 시퀀스 | "Hello World" |
| 배열(Array) | 순서가 있는 값의 목록. 서로 다른 타입을 섞어 담을 수 있음 | 대괄호 안의 값 시퀀스 | [1, "two", true] |
| 객체(Object) | 순서가 없는 키-값 쌍의 집합. 키는 반드시 문자열이어야 함 | 중괄호 안의 키-값 쌍, 키는 문자열 | {"name": "Juan", "age": 31} |
| 널(Null) | 값이 없음을 명시적으로 나타내는 타입. 키 자체를 생략하는 것과는 의미가 다름 | 상수 null | null |
객체의 키는 반드시 문자열이어야 하지만, 값 자리에는 6가지 타입 중 아무거나 올 수 있습니다. 값이 다시 객체나 배열이면 자연스럽게 중첩 구조가 됩니다.
{
"name": "김서연", // 값이 문자열
"age": 29, // 값이 숫자
"active": true, // 값이 불리언
"scores": [95, 88, 72], // 값이 배열
"address": { // 값이 객체
"city": "서울",
"zip": "04524"
}
}
"user"의 값이 객체이고, 그 안의 "scores"의 값이 배열입니다. JSON에서 복잡한 데이터를 표현할 수 있는 이유가 이 중첩 구조에 있습니다.
JSON은 데이터의 값을 표현하지만, 그 데이터의 구조·의미·맥락은 표현하지 않습니다. “이 객체에는 어떤 키가 들어와야 하는가”, “이 숫자에 음수가 와도 되는가”, “이 문자열은 이메일 형식이어야 하는가” 같은 질문에 JSON 문법은 답하지 않습니다. 이 빈 곳이 JSON 스키마가 등장한 배경입니다.
2. JSON 스키마의 정의와 네 가지 의의
JSON 스키마 2020-12 명세는 JSON 스키마를 “JSON 문서를 기술하기 위한 미디어 타입”으로 정의합니다. 풀어 쓰면, JSON 스키마는 JSON 데이터의 구조와 의미를 선언적으로 정의하기 위한 언어이고, 그 언어가 JSON 문법으로 쓰여 있습니다. 즉 JSON 스키마 문서는 동시에 JSON 문서이기도 합니다.
JSON 스키마 도입을 결정한다는 것은 검증 도구 하나를 추가하는 일이 아닙니다. JSON 스키마는 다음 4가지를 동시에 풀어냅니다.
| 풀어주는 영역 | 답하는 질문 | 산출물 | 도입 효과 |
|---|---|---|---|
| 검증(Validation) | 데이터가 약속된 형식을 따르는가 | 자동 검증기 통과 여부 | 잘못된 데이터를 시스템 입구에서 차단 |
| 문서화(Documentation) | 사람과 기계가 같은 정의를 보는가 | 항상 갱신되는 사양 문서 | 위키와 코드의 정의가 흩어지지 않음 |
| 계약(Contract) | API·이벤트의 송수신 양측이 같은 형식에 합의하는가 | 양측이 공유하는 스키마 파일 | 변경의 영향 범위가 형식적으로 추적됨 |
| 자동화(Automation) | 데이터 정의에서 다른 산출물을 만들 수 있는가 | 폼·코드·테스트 데이터 생성 | 정의 한 번에서 다운스트림 작업 다수가 파생 |
JSON과 JSON 스키마의 관계를 한 그림으로 정리하면 다음과 같습니다.
┌─────────────────────────────────────────────────┐
│ 메타-스키마 (= JSON 스키마의 JSON 스키마) │
│ │
│ "$schema": │
│ "<https://json-schema.org/draft/2020-12/>..." │
└─────────────────────────────────────────────────┘
│
│ 검증·기술
▼
┌─────────────────────────────────────────────────┐
│ JSON 스키마 (= JSON 문서) │
│ │
│ { "type": "object", │
│ "properties": { │
│ "name": { "type": "string" }, │
│ "age": { "type": "integer", "minimum": 0 }│
│ }, │
│ "required": ["name", "age"] } │
│ │
└─────────────────────────────────────────────────┘
│
│ 검증·기술
▼
┌─────────────────────────────────────────────────┐
│ JSON 인스턴스 │
│ │
│ { "name": "Juan", "age": 31 } │
│ │
└─────────────────────────────────────────────────┘
JSON 스키마는 JSON 인스턴스를 검증·기술하고, 그 JSON 스키마를 다시 또 다른 JSON 스키마, 즉 메타-스키마가 검증·기술합니다. 이 자기 기술적인 구조 덕분에 JSON 스키마는 새 키워드를 추가하거나 표준을 확장할 때도 같은 형식 안에서 일관성을 유지합니다.
그림 아래 설명을 조금 보강하면 될 것 같습니다. 메타-스키마는 “JSON 스키마 문서 자체가 올바른 JSON 스키마인가”를 검증하는 스키마입니다. type, properties, required 같은 키워드를 어떤 규칙으로 써야 하는지를 정의하는 것이 메타-스키마의 역할입니다. 이 자기 기술적인 구조 덕분에 JSON 스키마는 새 키워드를 추가하거나 표준을 확장할 때도 같은 형식 안에서 일관성을 유지합니다.
JSON 스키마 도입은 “우리 조직의 데이터 합의를 어떤 형식으로 둘 것인가”를 정하는 일입니다. 위키 문서나 미팅 결과로 합의를 흘려 두는 대신, 기계가 검증할 수 있고 사람이 읽을 수 있는 형식적 합의로 옮기는 결정입니다.
스키마와 인스턴스 스키마는 “데이터가 어떤 모양이어야 하는가”를 정의하는 규칙이고, 인스턴스는 그 규칙에 따라 실제로 만들어진 데이터입니다. 건축에 비유하면 스키마는 설계도, 인스턴스는 그 설계도대로 지은 건물입니다. JSON 스키마에서는 둘 다 JSON 형식이기 때문에, 규칙을 쓰는 문법을 따로 배울 필요 없이 JSON만 알면 됩니다. 프로그래밍의 클래스와 헷갈릴 수 있는데, 역할이 다릅니다. 클래스는 데이터 구조와 함께 메서드도 정의할 수 있고,
new User()처럼 객체를 직접 생성합니다. 스키마는 데이터의 구조와 제약 조건만 기술하고, 데이터를 만들지 않습니다. 이미 만들어진 데이터가 규칙에 맞는지 검증할 뿐입니다. 클래스가 “설계도 + 공장”이라면, 스키마는 “검수 기준서”에 가깝습니다.
3. JSON 스키마의 빌딩 블록: 키워드, 보캐뷸러리, 메타-스키마, 다이얼렉트
JSON 스키마를 깊이 이해하려면 4가지 어휘를 잡아 둬야 합니다. 키워드(Keyword), 보캐뷸러리(Vocabulary), 메타-스키마(Meta-Schema), 다이얼렉트(Dialect)입니다. 이 어휘들은 시리즈 후반에서 계속 회수되므로, A편에서 한 번 단단히 잡고 가는 게 시간을 벌어 줍니다.
이 4가지의 위계를 그림으로 그리면 다음과 같습니다.
┌──────────────────────────────────────────────────────────────┐
│ 다이얼렉트 (Dialect) │
│ 예: 2020-12 │
│ ─ 이 다이얼렉트가 가져오는 보캐뷸러리들의 묶음을 정의 │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 메타-스키마 (Meta-Schema) │ │
│ │ 예: <https://json-schema.org/draft/2020-12/schema> │ │
│ │ ─ 이 다이얼렉트의 보캐뷸러리들을 $vocabulary로 선언 │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ 보캐뷸러리 (Vocabulary) │ │ │
│ │ │ 예: Core, Validation, Applicator, ... │ │ │
│ │ │ ─ 같은 목적의 키워드 묶음 │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
│ │ │ │ 키워드 (Keyword) │ │ │ │
│ │ │ │ 예: type, properties, required, $schema │ │ │ │
│ │ │ │ ─ 의미를 갖는 최소 토큰 │ │ │ │
│ │ │ └────────────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
1) 다이얼렉트(Dialect)
다이얼렉트는 메타-스키마가 가져오는 보캐뷸러리 묶음 전체를 가리키는 이름입니다. JSON 스키마는 명세 개정마다 새 다이얼렉트를 발행해 왔습니다.
Draft 4 → Draft 6 → Draft 7 → 2019-09 → 2020-12
다이얼렉트가 다르면 같은 키워드라도 다르게 해석될 수 있습니다. 예를 들어 unevaluatedProperties는 2019-09에서 처음 도입되어 그 이전 다이얼렉트에는 존재하지 않고, 2020-12에서는 items와 additionalItems의 의미를 정리하면서 prefixItems를 새로 도입했습니다.
따라서 프로젝트를 시작할 때 어느 다이얼렉트를 쓸지는 초기에 정해야 할 결정입니다. JSON 스키마 명세 페이지 기준으로 글을 쓰는 시점의 최신 다이얼렉트는 2020-12입니다. 2026년 5월 기준으로 새 프로젝트는 2020-12를 기본으로 삼는 것이 일반적이며, 이 시리즈의 예시도 2020-12 기준으로 작성합니다.
2) 메타-스키마(Meta-Schema)
메타-스키마는 “이 다이얼렉트에서 쓸 수 있는 보캐뷸러리는 이것들이다”를 선언하는 JSON 스키마입니다. 스키마를 기술하는 스키마, 즉 한 단계 위의 스키마입니다. JSON 스키마의 모든 공식 릴리스는 대응하는 메타-스키마를 함께 발행합니다.
메타-스키마는 자기 자신의 $schema를 자기 자신으로 가리킵니다. “스키마를 검증하는 스키마”를 또 다른 스키마가 검증해야 한다면 끝없이 올라가야 하는데, 자기 자신을 가리키는 것으로 그 사슬을 끊습니다.
실무에서 메타-스키마를 직접 만들 일은 거의 없습니다. 내가 작성한 스키마가 어떤 메타-스키마를 따르는지를 $schema 키워드로 선언하면 됩니다.
"$schema": "<https://json-schema.org/draft/2020-12/schema>"
이 선언이 있어야 검증기가 어느 다이얼렉트의 규칙으로 스키마를 해석할지 판단할 수 있습니다.
3) 보캐뷸러리(Vocabulary)
키워드가 수십 개로 늘어나면, 전부 한 뭉텅이로 두기엔 관리가 어렵습니다. 그래서 같은 목적의 키워드끼리 묶어 놓은 것이 보캐뷸러리입니다. 프로그래밍 언어의 표준 라이브러리가 문자열 처리, 파일 입출력, 네트워크 같은 모듈로 나뉘어 있는 것과 비슷합니다.
JSON 스키마 2020-12 다이얼렉트는 7가지 공식 보캐뷸러리를 제공합니다.
// 스키마 내 보캐뷸러리 예시
{
"$schema": "<https://json-schema.org/draft/2020-12/schema>", // Core
"$id": "<https://example.com/user.schema.json>", // Core
"type": "object", // Validation
"required": ["name", "email"], // Validation
"properties": { // Applicator
"name": {
"type": "string", // Validation
"title": "사용자 이름", // Meta-Data
"minLength": 1 // Validation
},
"email": {
"type": "string", // Validation
"format": "email", // Format-Annotation
"description": "로그인에 사용되는 이메일 주소" // Meta-Data
}
}
}
즉, 보캐뷸러리는 스키마를 쓰다가 막힐 때 “내가 찾는 키워드가 어느 묶음에 있는지” 방향을 잡아주는 지도입니다. 검증이 안 먹히면 Validation을, 참조가 풀리지 않으면 Core를, 문서에 설명이 비어 보이면 Meta-Data를 먼저 들여다보면 됩니다.
(1) 코어 (Core)
스키마 자체를 식별하고 다른 스키마를 참조하는 토대입니다. 거의 모든 스키마에 필수로 들어갑니다.
| 키워드 | 설명 | 예시 |
|---|---|---|
$schema | 이 스키마가 따르는 다이얼렉트 선언 | "<https://json-schema.org/draft/2020-12/schema>" |
$id | 스키마의 고유 식별자(URI) | "<https://example.com/user.schema.json>" |
$ref | 다른 스키마를 참조 | "#/$defs/address", "./common.schema.json" |
$vocabulary | 메타-스키마에서 보캐뷸러리 목록 선언 | 직접 작성할 일은 거의 없음 |
(2) 검증 (Validation)
타입·범위·필수 여부 같은 실제 검증 규칙입니다. Core와 함께 거의 모든 스키마에 필수로 들어갑니다.
| 키워드 | 설명 | 예시 |
|---|---|---|
type | 허용되는 데이터 타입 지정 | "string", "integer", "object", "array", "boolean", "null" |
required | 반드시 있어야 하는 키 목록 | ["name", "email"] |
minLength / maxLength | 문자열의 최소·최대 길이 | 1, 255 |
minimum / maximum | 숫자의 최솟값·최댓값 | 0, 150 |
enum | 허용되는 값의 목록 | ["admin", "editor", "viewer"] |
(3) 적용자 (Applicator)
조건 분기, 배열·객체 내부에 서브스키마를 적용합니다. 객체나 배열을 다루면 거의 필수입니다.
검증(Validation) 키워드는 인스턴스 값이 특정 조건을 만족하는지 직접 검사합니다. 예를 들어 “type”: “string”, “minimum”: 0, “required” 같은 키워드는 값이나 객체 상태를 보고 통과/실패를 결정합니다.
적용자(Applicator) 키워드는 다른 스키마를 특정 위치나 방식으로 적용합니다. 예를 들어 “properties”, “items”, “allOf” 등은 하위 스키마를 객체·배열·조합 규칙 등에 적용합니다. 적용된 하위 스키마 중 하나라도 실패하면 전체 평가도 실패할 수 있습니다.
| 키워드 | 설명 | 예시 |
|---|---|---|
properties | 객체의 각 키별 스키마 지정 | { "name": { "type": "string" } } |
items | 배열 요소의 스키마 지정 | { "type": "integer" } |
allOf | 나열된 스키마를 모두 만족해야 통과 | [{ "required": ["name"] }, { "required": ["email"] }] |
oneOf | 나열된 스키마 중 정확히 하나만 만족해야 통과 | [{ "type": "string" }, { "type": "integer" }] |
(4) 메타 데이터 (Meta-Data)
스키마 자체에 제목·설명·예시 같은 문서 정보를 첨부합니다. 없어도 검증은 되지만, 실무에서는 거의 항상 넣습니다.
| 키워드 | 설명 | 예시 |
|---|---|---|
title | 스키마의 제목 | "사용자 이름" |
description | 스키마의 설명 | "로그인에 사용되는 이메일 주소" |
examples | 유효한 값의 예시 목록 | ["seoyeon@example.com", "juan@example.com"] |
default | 값이 없을 때의 기본값 | "viewer", 0, false |
(5) 포맷 (Format-Annotation / Format-Assertion)
이메일·날짜·URI 같은 표준 포맷의 표기와 검증입니다. 문자열 포맷 제약이 필요할 때 사용합니다.
| 키워드 | 설명 | 자주 쓰는 값 |
|---|---|---|
format | 문자열의 의미적 포맷 지정 | "email", "date", "date-time", "uri", "uuid", "ipv4" |
(6) 미평가 (Unevaluated)
스키마에서 명시적으로 다루지 않은 속성·항목의 처리입니다. 스키마 상속·조합이 복잡해질 때 사용합니다.
| 키워드 | 설명 | 예시 |
|---|---|---|
unevaluatedProperties | 어떤 서브스키마에서도 평가되지 않은 객체 속성의 처리 | false (미평가 속성 금지), { "type": "string" } |
unevaluatedItems | 어떤 서브스키마에서도 평가되지 않은 배열 항목의 처리 | false (미평가 항목 금지) |
{
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" }
},
"unevaluatedProperties": false
}
이 스키마는 name과 age만 정의했습니다. unevaluatedProperties: false가 있으면, 정의되지 않은 필드가 들어오면 실패합니다.
// 통과
{ "name": "김서연", "age": 29 }
// 실패: "email"은 스키마에서 정의하지 않은 필드
{ "name": "김서연", "age": 29, "email": "seoyeon@example.com" }
unevaluatedProperties: false 대신 { "type": "string" }을 넣으면 “정의되지 않은 필드가 와도 되는데, 값은 문자열이어야 한다”는 뜻이 됩니다.
배열도 마찬가지입니다. unevaluatedItems: false는 스키마에서 정의하지 않은 위치의 배열 요소를 금지합니다.
(7) 콘텐츠 (Content)
문자열 안에 Base64 인코딩 등 비-JSON 데이터가 담긴 경우의 처리입니다. 파일 데이터를 JSON 안에 담을 때만 드물게 사용합니다.
| 키워드 | 설명 | 예시 |
|---|---|---|
contentEncoding | 문자열의 인코딩 방식 | "base64" |
contentMediaType | 디코딩된 데이터의 미디어 타입 | "image/png", "application/pdf" |
4) 키워드(Keyword)
키워드는 JSON 스키마 안에서 의미를 갖는 토큰(더 이상 쪼갤 수 없는 의미의 최소 단위)입니다. type, properties, required, $schema, format, minLength 같은 이름들이 키워드입니다. JSON 스키마가 하는 일의 단위가 키워드라고 봐도 무방합니다.
키워드의 역할은 두 가지로 나뉩니다. 하나는 어설션**(Assertion, 단언)**으로, 인스턴스가 어떤 조건을 만족해야 한다고 선언합니다. type: "string"은 “이 값은 문자열이어야 한다”는 단언입니다. 다른 하나는 **어노테이션(Annotation)**으로, 검증에는 영향을 주지 않지만 그 데이터에 관한 정보를 사람과 기계에 전달합니다. title이나 description은 검증을 막지 않지만, 문서화·UI 생성 같은 다운스트림 작업(downstream, 데이터를 받아서 그다음에 처리하는 모든 작업)에 활용됩니다.
4. 처음 보는 JSON 스키마 풀어 읽기
이 절차를 예시로 적용해 보겠습니다. 다음과 같은 주문 테이블이 있다고 가정합니다.
| order_id | customer_id | status | total_amount | ordered_at | note |
|---|---|---|---|---|---|
| a1b2c3d4-5678-90ab-cdef-1234567890ab | f0e1d2c3-4567-89ab-cdef-0987654321ba | paid | 35000 | 2026-05-20 14:30:00 | 문 앞에 놓아주세요 |
| b2c3d4e5-6789-01bc-defg-2345678901bc | a1b2c3d4-5678-90ab-cdef-1234567890ab | pending | 12500 | 2026-05-20 15:10:00 | |
| c3d4e5f6-7890-12cd-efgh-3456789012cd | d4e5f6g7-8901-23de-fghi-4567890123de | shipped | 89000 | 2026-05-19 09:45:00 | 경비실에 맡겨주세요 |
| 컬럼명 | 타입 | 제약 조건 |
|---|---|---|
| order_id | UUID (고유 식별자) | 기본 키(각 행을 구분하는 값), 비어 있을 수 없음 |
| customer_id | UUID (고유 식별자) | 비어 있을 수 없음, 고객 테이블의 행을 가리키는 참조 값 |
| status | VARCHAR (가변 길이 문자열) | 비어 있을 수 없음, ‘pending’ / ‘paid’ / ‘shipped’ / ‘cancelled’ 중 하나 |
| total_amount | INTEGER (정수) | 비어 있을 수 없음, 0 이상 |
| ordered_at | TIMESTAMP (날짜와 시간) | 비어 있을 수 없음 |
| note | TEXT (긴 문자열) | 비어 있어도 됨 |
이 테이블의 한 행을 JSON으로 표현하면 이렇습니다.
{
"order_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"customer_id": "f0e1d2c3-4567-89ab-cdef-0987654321ba",
"status": "paid",
"total_amount": 35000,
"ordered_at": "2026-05-20T14:30:00Z",
"note": "문 앞에 놓아주세요"
}
이 JSON이 올바른 주문 데이터인지 검증하는 스키마는 다음과 같습니다.
{
// 1단계: 2020-12 다이얼렉트 사용
"$schema": "<https://json-schema.org/draft/2020-12/schema>",
// Core: 이 스키마의 고유 식별자
"$id": "<https://example.com/schemas/order>",
// Meta-Data: 사람과 도구가 읽는 제목·설명
"title": "Order",
"description": "주문 한 건을 기술하는 스키마",
// Validation: 이 스키마가 검증하는 값은 객체여야 함
"type": "object",
// Validation: 아래 5개 키는 반드시 있어야 함
"required": [
"order_id", "customer_id", "status",
"total_amount", "ordered_at"
],
// Applicator: 객체 내부 각 키에 스키마를 연결
"properties": {
// Validation + Format: 문자열이면서 UUID 형식
"order_id": {
"type": "string",
"format": "uuid"
},
// Validation + Format: 문자열이면서 UUID 형식
"customer_id": {
"type": "string",
"format": "uuid"
},
// Validation: 문자열이면서 4개 값 중 하나
"status": {
"type": "string",
"enum": ["pending", "paid", "shipped", "cancelled"]
},
// Validation: 정수이면서 0 이상
"total_amount": {
"type": "integer",
"minimum": 0
},
// Validation + Format: 문자열이면서 날짜시간 형식
"ordered_at": {
"type": "string",
"format": "date-time"
},
// Validation: 문자열 (required에 없으므로 없어도 됨)
"note": {
"type": "string"
}
}
// 4단계: $ref가 없으므로 외부 스키마 의존 없음
}
5. 데이터 정의를 JSON 스키마로 관리하면 뭐가 달라지는가
데이터 구조를 정의하는 건 어떤 방식이든 해야 하는 일입니다. 코드 안에 타입으로 정의할 수도 있고, 위키에 문서로 적어둘 수도 있고, 슬랙 메시지로 공유할 수도 있습니다. 여기서 고민할 건 “정의를 할지 말지”가 아니라, “그 정의를 JSON 스키마라는 표준 형식으로 둘지, 다른 방식으로 둘지”입니다.
1) 다른 방식과 뭐가 다른가
| 정의 방식 | 사람이 읽을 수 있는가 | 기계가 검증할 수 있는가 | 정의와 문서가 한 곳인가 |
|---|---|---|---|
| 슬랙·노션에 정리 | ○ | ✕ | ✕ (코드와 따로 놈) |
| 코드 안 타입 정의 (TypeScript 등) | △ (코드를 읽어야 함) | △ (컴파일 시점만) | △ (코드에 묶여 있음) |
| JSON 스키마 | ○ | ○ (런타임 검증 가능) | ○ (스키마 = 정의 = 문서) |
JSON 스키마의 핵심 차이는 하나의 파일이 정의·문서·검증 규칙을 동시에 한다는 점입니다. 위키에 적어둔 정의는 코드가 바뀌면 따로 업데이트해야 하고, 코드 안 타입 정의는 런타임에 들어오는 데이터를 검증하지 못합니다.
2) JSON 스키마로 관리하면 좋은 상황
| 우리 상황이 이렇다면 | JSON 스키마가 풀어주는 것 |
|---|---|
| 두 팀·시스템 사이에 데이터를 주고받는다 | 양쪽이 공유하는 형식적 합의가 생긴다 |
| 같은 데이터 정의가 위키·코드·문서에 흩어져 있다 | 스키마 하나가 정의의 단일 출처가 된다 |
| API 변경할 때마다 “그거 언제 바뀌었어?”가 반복된다 | 스키마 diff로 변경 범위를 추적할 수 있다 |
| 새로 합류한 사람이 데이터 구조 파악에 오래 걸린다 | 스키마 자체가 문서 역할을 한다 |
| 데이터 정의에서 폼·코드·테스트 데이터를 자동 생성하고 싶다 | 스키마 기반 자동화 도구를 바로 연결할 수 있다 |
| 외부 파트너에게 데이터 형식을 공개해야 한다 | 업계 표준 형식으로 전달할 수 있다 |
반대로 단일 시스템 내부에서만 쓰는 데이터이고, 정의가 코드 한 곳에만 있고, 팀 규모가 작다면 JSON 스키마까지 도입하지 않아도 충분합니다.
3) 들여야 하는 것과 얻을 수 있는 것
| 들어가는 것 | 돌아오는 것 |
|---|---|
| 키워드·보캐뷸러리·다이얼렉트 학습 시간 | 한 번 익히면 어떤 스키마든 같은 방식으로 읽고 쓸 수 있다 |
| 스키마 작성에 드는 초기 작업 | 코드와 문서가 한 곳으로 정리된다 |
| 검증기·문서 생성기 등 도구 세팅 | 자동화로 반복 작업이 줄어든다 |
| 스키마 버전 관리·호스팅 운영 | 변경 이력이 형식적으로 남는다 |
| 팀 간 형식 합의에 드는 시간 | 합의가 기계가 검증할 수 있는 형태로 남는다 |
학습 시간
키워드·보캐뷸러리·다이얼렉트 같은 어휘를 처음 잡는 데 시간이 듭니다. 다만 이 어휘 체계는 JSON 스키마 전체에서 동일하게 반복되기 때문에, 한 번 익혀두면 어떤 스키마를 만나든 같은 방식으로 읽고 쓸 수 있습니다.
초기 작성 비용
기존에 위키나 슬랙에 흩어져 있던 데이터 정의를 스키마 파일로 옮기는 작업이 필요합니다. 이 작업을 거치고 나면 코드와 문서가 한 파일에 정리되어, 이후에 “정의가 어디 있더라?”를 찾아 헤맬 일이 없어집니다.
도구 세팅
검증기, 문서 생성기, 코드 생성기 같은 도구를 프로젝트에 붙여야 합니다. 세팅이 끝나면 스키마 하나에서 API 문서, 타입 코드, 테스트 데이터가 자동으로 나오기 때문에 반복 작업이 크게 줄어듭니다.
버전 관리·운영
스키마 파일도 코드처럼 버전 관리가 필요합니다. Git으로 관리하면 “이 필드가 언제, 왜 바뀌었는지”를 커밋 이력으로 추적할 수 있습니다.
팀 간 합의 비용
데이터 형식을 팀 간에 맞추는 데 시간이 듭니다. 하지만 그 합의가 스키마 파일로 남으면, 나중에 “우리 이거 어떻게 하기로 했더라?”를 슬랙 검색 대신 스키마 파일 하나로 확인할 수 있습니다.
처음부터 스키마를 직접 짜지 않아도 됩니다. 이미 공개된 스키마를 가져다 써보면 도입 효과를 빠르게 체감할 수 있습니다.
- JSON Schema Store는 GitHub Actions, tsconfig, package.json 같은 설정 파일의 표준 스키마를 수백 개 모아둔 카탈로그입니다. IDE에서 이 스키마를 연결하면 자동완성과 검증을 바로 받을 수 있습니다.
- Postman 같은 API 도구는 JSON 스키마를 응답 검증·계약 점검에 기본으로 쓰고 있어서, API를 다루는 팀이라면 자연스럽게 접할 수 있습니다.
마무리: 도입을 결정했다면
이 글에서 잡은 것들을 정리합니다.
- JSON의 6가지 타입과 키-값 쌍의 중첩 구조
- JSON이 표현하지 못하는 것: 구조·의미·맥락
- JSON 스키마의 4가지 역할: 검증, 문서화, 계약, 자동화
- 빌딩 블록 4가지: 키워드, 보캐뷸러리, 메타-스키마, 다이얼렉트
- DB 테이블의 제약 조건이 JSON 스키마 키워드로 어떻게 대응되는지
- 데이터 정의를 JSON 스키마로 관리했을 때 뭐가 달라지는지
JSON 스키마 도입은 검증 도구를 추가하는 일이 아닙니다. “우리 조직의 데이터 합의를 어떤 형식으로 둘 것인가”를 정하는 일입니다. 위키 문서나 슬랙 메시지로 흘려두던 합의를, 사람이 읽을 수 있고 기계가 검증할 수 있는 형식으로 옮기는 결정입니다.
도입을 결정했다면, 다음 질문은 “첫 스키마를 어떻게 작성할 것인가”입니다. 빈 파일 앞에서 첫 줄을 무엇으로 시작할지, 어디까지를 한 스키마로 묶을지, 무엇을 참조로 분리할지. B편에서 이 질문들을 하나씩 풀어갑니다.
JSON 시리즈

