JSON Schema 유효성 검사: 완벽 가이드
JSON Schema는 JSON 문서에 주석을 달고 유효성을 검사하기 위한 어휘입니다. 데이터가 어떤 형태를 가져야 하는지, 어떤 타입이 허용되는지, 어떤 필드가 필수인지를 정의하는 계약을 제공합니다. API, 설정 파일, 또는 구조화된 데이터를 다루는 경우 JSON Schema는 필수 도구입니다.
JSON Schema를 사용하는 이유
유효성 검사 없이 JSON 데이터는 와일드카드입니다. 나이 필드가 문자열로, 이메일이 누락되거나, 날짜가 잘못된 포맷으로 올 수 있습니다. JSON Schema는 잘못된 데이터가 시스템을 통해 전파되기 전에 경계에서 이러한 문제를 잡습니다.
핵심 이점:
- API 계약 강제: 요청 및 응답 본문을 자동으로 유효성 검사
- 문서화: 스키마가 데이터 구조의 살아있는 문서 역할
- 코드 생성: 도구가 JSON Schema에서 타입, 폼, 데이터베이스 스키마를 생성
- 테스팅: 스키마에 대한 테스트 픽스처 및 목 데이터 유효성 검사
기본 스키마 구조
모든 JSON Schema는 몇 가지 표준 속성으로 시작합니다:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/user.schema.json",
"title": "User",
"description": "A registered user in the system",
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 }
},
"required": ["id", "name", "email"]
}
팀과 공유하기 전에 JSON 포맷터로 스키마를 미화하여 가독성을 높이세요.
타입 제약
JSON Schema는 6가지 기본 타입을 지원합니다:
| 타입 | 설명 | 예시 |
|---|---|---|
string | 텍스트 데이터 | "hello" |
number | 모든 숫자 값 | 3.14 |
integer | 정수만 | 42 |
boolean | 참 또는 거짓 | true |
array | 순서 있는 목록 | [1, 2, 3] |
object | 키-값 쌍 | {"key": "value"} |
null | Null 값 | null |
문자열 제약
{
"type": "string",
"minLength": 1,
"maxLength": 255,
"pattern": "^[A-Za-z0-9]+$",
"format": "email"
}
내장 포맷: email, uri, date, date-time, ipv4, ipv6, uuid, hostname.
숫자 제약
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMinimum": 0,
"multipleOf": 0.01
}
배열 유효성 검사
배열은 항목, 길이, 고유성에 대해 검증할 수 있습니다:
{
"type": "array",
"items": { "type": "string", "format": "email" },
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
}
튜플 유효성 검사 (위치별로 특정 타입이 있는 고정 길이 배열):
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "type": "boolean" }
],
"items": false
}
객체 유효성 검사
객체는 속성 유효성 검사, 필수 필드, 추가 속성 제어를 지원합니다:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"role": { "enum": ["admin", "editor", "viewer"] }
},
"required": ["name", "role"],
"additionalProperties": false
}
additionalProperties를 false로 설정하면 스키마에 정의되지 않은 속성을 거부하는 엄격 모드입니다. 이는 필드 이름의 오타를 잡으려는 API에 특히 유용합니다.
조합 키워드
JSON Schema는 논리 연산자를 사용한 스키마 결합을 지원합니다:
allOf (AND)
데이터가 모든 서브스키마에 대해 유효해야 합니다:
{
"allOf": [
{ "$ref": "#/$defs/baseUser" },
{ "properties": { "role": { "const": "admin" } } }
]
}
oneOf (XOR)
데이터가 서브스키마 중 정확히 하나에 대해 유효해야 합니다:
{
"oneOf": [
{ "properties": { "type": { "const": "email" }, "email": { "type": "string" } } },
{ "properties": { "type": { "const": "phone" }, "phone": { "type": "string" } } }
]
}
anyOf (OR)
데이터가 서브스키마 중 하나 이상에 대해 유효해야 합니다.
조건부 유효성 검사
if/then/else 키워드는 조건부 유효성 검사를 가능하게 합니다:
{
"type": "object",
"properties": {
"country": { "type": "string" },
"postalCode": { "type": "string" }
},
"if": {
"properties": { "country": { "const": "US" } }
},
"then": {
"properties": { "postalCode": { "pattern": "^[0-9]{5}(-[0-9]{4})?$" } }
},
"else": {
"properties": { "postalCode": { "pattern": "^[A-Z0-9 -]+$" } }
}
}
참조와 재사용
재사용 가능한 정의를 참조하기 위해 $ref를 사용합니다:
{
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string" }
},
"required": ["street", "city", "country"]
}
},
"type": "object",
"properties": {
"billingAddress": { "$ref": "#/$defs/address" },
"shippingAddress": { "$ref": "#/$defs/address" }
}
}
실제 유효성 검사
JavaScript (Ajv)
const Ajv = require('ajv');
const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) console.log(validate.errors);
Python (jsonschema)
from jsonschema import validate, ValidationError
try:
validate(instance=data, schema=schema)
except ValidationError as e:
print(f"Validation error: {e.message}")
개발 환경을 설정하지 않고 빠르게 유효성을 검사하려면 JSON 유효성 검사기에 JSON과 스키마를 붙여넣으세요.
실제 예시: API 응답 스키마
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": { "enum": ["success", "error"] },
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"createdAt": { "type": "string", "format": "date-time" }
},
"required": ["id", "name"]
}
},
"pagination": {
"type": "object",
"properties": {
"page": { "type": "integer", "minimum": 1 },
"perPage": { "type": "integer", "minimum": 1, "maximum": 100 },
"total": { "type": "integer", "minimum": 0 }
}
}
},
"required": ["status", "data"]
}
자주 묻는 질문
JSON Schema draft-07과 2020-12의 차이점은 무엇인가요?
Draft 2020-12는 여러 개선을 도입했습니다: prefixItems가 items의 튜플 형태를 대체, $dynamicRef가 더 유연한 참조를 가능하게 하며, 어휘 지원이 커스텀 확장을 허용합니다. 새 프로젝트에는 2020-12를 사용하세요. 이전 도구와의 호환성이 필요하면 draft-07도 여전히 널리 지원됩니다.
JSON Schema로 문자열 필드 내의 중첩 JSON을 유효성 검사할 수 있나요?
직접적으로는 불가능합니다. JSON Schema는 파싱된 JSON의 구조를 유효성 검사합니다. 필드에 JSON 문자열이 포함되어 있으면 먼저 파싱하고 별도로 유효성을 검사해야 합니다. 일부 유효성 검사기는 이를 처리할 수 있는 커스텀 포맷 유효성 검사기를 제공하지만 표준의 일부는 아닙니다.
관련 리소스
- JSON 포맷터 — JSON 스키마 포맷 및 미화
- JSON 유효성 검사 방법 — 일반적인 JSON 유효성 검사 오류와 수정
- JSON API 디자인 패턴 — JSON으로 더 나은 REST API 구축