← 전체 글

JSON Schema란? 예시로 보는 실용 가이드

JSON Schema는 JSON 데이터의 구조와 제약을 기술하는 어휘. 핵심 키워드, 실제 예시, JavaScript·Python·브라우저에서의 검증을 배운다.

JSON Schema는 JSON 문서의 구조, 제약, 타입을 기술하는 어휘입니다. 유효한 JSON이 어떤 모양이어야 하는지를 정확히 정의하고 — 어떤 키가 필수인지, 각 값이 어떤 타입이어야 하는지, 어떤 값이 허용되는지 — 그 규칙에 대해 임의의 JSON 문서를 자동으로 검증할 수 있게 해줍니다. 이 가이드는 JSON Schema가 무엇인지, 어떻게 작성하는지, 그리고 JavaScript, Python, 그리고 브라우저에서 그것을 사용해 JSON을 검증하는 방법을 설명합니다.

JSON Schema란 무엇인가?

JSON Schema 자체가 JSON 문서입니다. 데이터베이스 스키마가 테이블을 기술하는 것과 같은 방식으로 다른 JSON 문서를 기술합니다 — 데이터의 예상되는 모양, 타입, 제약을 선언합니다. JSON Schema는 검증기 — 라이브러리, API, 폼 생성기, IDE — 에게 특정 JSON에 대해 「유효함」이 무엇을 의미하는지 알려줍니다.

스펙은 json-schema.org에서 관리되며 현재 Draft 2020-12입니다. OpenAPI(REST API 문서화 표준), JSON Forms(스키마로부터 UI 생성), IDE 도구(VS Code는 package.jsontsconfig.json의 자동완성에 JSON Schema를 사용), 그리고 수많은 내부 데이터 파이프라인에서 사용됩니다.

최소한의 JSON Schema 예시

다음은 사용자를 나타내는 JSON 문서와 그 예상 구조를 기술하는 스키마입니다:

// 검증 대상 JSON 문서
{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com",
  "age": 30,
  "active": true
}
// JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["id", "name", "email"],
  "properties": {
    "id":     { "type": "integer" },
    "name":   { "type": "string", "minLength": 1 },
    "email":  { "type": "string", "format": "email" },
    "age":    { "type": "integer", "minimum": 0, "maximum": 150 },
    "active": { "type": "boolean" }
  },
  "additionalProperties": false
}

이 스키마가 말하는 것: 문서는 객체여야 하고, id, name, email은 필수이며, age가 존재한다면 0과 150 사이여야 하고, 나열된 다섯 개 외의 키는 허용되지 않습니다.

핵심 키워드

type

값의 JSON 타입을 지정합니다. 유효한 타입은 "string", "number", "integer", "boolean", "array", "object", "null"입니다. 배열로 여러 타입을 허용할 수도 있습니다:

{ "type": ["string", "null"] }  // 값은 문자열이거나 null일 수 있음

properties

객체의 각 키에 대한 스키마를 정의합니다. 각 항목 자체가 스키마입니다:

{
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName":  { "type": "string" },
    "age":       { "type": "integer", "minimum": 0 }
  }
}

required

객체에 반드시 존재해야 하는 키 이름의 배열입니다. properties에는 있지만 required에는 없는 키는 선택입니다 — 없어도 되지만, 있다면 그 스키마와 일치해야 합니다.

items

배열의 모든 요소에 대한 스키마를 정의합니다. 다음 스키마는 문자열 배열을 요구합니다:

{
  "type": "array",
  "items": { "type": "string" }
}

문자열 제약

{
  "type": "string",
  "minLength": 1,
  "maxLength": 100,
  "pattern": "^[a-zA-Z0-9_]+
quot; // 정규식 패턴 }

숫자 제약

{
  "type": "number",
  "minimum": 0,
  "maximum": 1,
  "multipleOf": 0.01   // 0.01의 배수여야 함
}

enum

값을 허용된 고정 집합으로 제한합니다:

{ "enum": ["pending", "active", "suspended", "deleted"] }

additionalProperties

properties에 나열되지 않은 키가 허용되는지 제어합니다. 예상치 못한 키를 거부하려면 false로 설정하세요 — 엄격한 API 계약에 유용합니다:

{ "additionalProperties": false }

실세계의 JSON Schema 예시

다음은 전자상거래 API의 상품 스키마입니다 — OpenAPI 정의나 내부 데이터 계약에서 보게 되는 종류입니다:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/product.json",
  "title": "Product",
  "description": "A product available for purchase",
  "type": "object",
  "required": ["id", "name", "price", "category"],
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique product identifier"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "price": {
      "type": "number",
      "minimum": 0,
      "description": "Price in USD"
    },
    "category": {
      "type": "string",
      "enum": ["electronics", "clothing", "books", "home", "sports"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "uniqueItems": true
    },
    "dimensions": {
      "type": "object",
      "properties": {
        "width":  { "type": "number", "minimum": 0 },
        "height": { "type": "number", "minimum": 0 },
        "depth":  { "type": "number", "minimum": 0 }
      },
      "required": ["width", "height", "depth"]
    },
    "inStock": { "type": "boolean" }
  },
  "additionalProperties": false
}

스키마로 JSON 검증하기

jsonschema 검증기를 고르시겠다고요? 표준 선택은 Ajv(JavaScript), jsonschema(Python), 그리고 jsonschemavalidator.net 같은 온라인 서비스입니다. 반대 방향이 필요하다면 — 예시에서 json schema를 생성하거나, json 문서에서 json schema를 추론하는 — Python에서는 GenSON, 브라우저에서는 transform.tools을 시도해 보세요. 이들은 샘플로부터 출발점 스키마를 추론하는 json schema generator 도구입니다. 이후 required, additionalProperties, 값 제약을 손으로 조여주세요.

JavaScript에서(Ajv)

Ajv는 JavaScript 생태계에서 가장 널리 쓰이는 JSON Schema 검증기입니다. Draft-07과 Draft 2020-12를 지원합니다.

npm install ajv
import Ajv from 'ajv';

const ajv = new Ajv();

const schema = {
  type: 'object',
  required: ['id', 'name', 'email'],
  properties: {
    id:    { type: 'integer' },
    name:  { type: 'string', minLength: 1 },
    email: { type: 'string' },
  },
  additionalProperties: false,
};

const validate = ajv.compile(schema);

const data = { id: 42, name: 'Alice', email: 'alice@example.com' };
const valid = validate(data);

if (!valid) {
  console.error(validate.errors);
} else {
  console.log('Valid!');
}

ajv.compile(schema)는 재사용 가능한 검증 함수를 반환합니다. 한 번 컴파일하고 그 결과를 반복해 호출하는 편이 매번 재컴파일하는 것보다 훨씬 빠릅니다.

JavaScript에서(브라우저, 의존성 없음)

의존성 없이 단순한 타입과 필수 필드 검사를 하려면 손으로 검증할 수도 있지만, 그 이상이라면 Ajv를 사용하세요. 스키마 검증 로직은 손수 작성하기에는 충분히 복잡해 오류가 잦습니다.

Python에서(jsonschema)

pip install jsonschema
import json
import jsonschema
from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "required": ["id", "name", "email"],
    "properties": {
        "id":    {"type": "integer"},
        "name":  {"type": "string", "minLength": 1},
        "email": {"type": "string"},
    },
    "additionalProperties": False,
}

data = {"id": 42, "name": "Alice", "email": "alice@example.com"}

try:
    validate(instance=data, schema=schema)
    print("Valid!")
except ValidationError as e:
    print(f"Invalid: {e.message}")

스키마에 대해 JSON 파일 검증

import json
import jsonschema

with open('schema.json') as f:
    schema = json.load(f)

with open('data.json') as f:
    data = json.load(f)

jsonschema.validate(instance=data, schema=schema)

브라우저에서(fixjson)

코드 없이 JSON 문서가 구조적으로 유효한지 확인하고 싶다면 — fixjson의 JSON 검증기가 즉시 문법을 검사하고 오류를 표시합니다. JSON을 붙여 넣으면 어떤 문법 문제든 각 오류의 정확한 줄과 위치와 함께 인라인으로 나타납니다.

완전한 스키마 검증(타입, 필수 필드, 제약 확인)에는 위의 라이브러리 기반 접근법 중 하나, 또는 jsonschemavalidator.net 같은 전용 스키마 테스트 도구를 사용하세요.

$ref: 스키마 재사용

실제 스키마는 모든 것을 인라인으로 정의하지 않습니다. $ref 키워드는 다른 스키마를 $id로 또는 같은 문서 내의 JSON Pointer 경로로 참조하게 해주어 스키마를 DRY하고 합성 가능하게 유지합니다.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "address": {
      "type": "object",
      "required": ["street", "city", "country"],
      "properties": {
        "street":  { "type": "string" },
        "city":    { "type": "string" },
        "country": { "type": "string", "minLength": 2, "maxLength": 2 }
      }
    }
  },
  "type": "object",
  "properties": {
    "billingAddress":  { "$ref": "#/$defs/address" },
    "shippingAddress": { "$ref": "#/$defs/address" }
  }
}

$defs는 단일 파일 내에서 재사용 가능한 하위 스키마를 정의하는 표준 위치입니다(Draft 2019-09에서 definitions을 대체했습니다).

스키마 결합: allOf, anyOf, oneOf

JSON Schema에는 「전부에 부합해야 함」, 「적어도 하나에 부합해야 함」, 「정확히 하나에 부합해야 함」을 표현하는 세 가지 조합 키워드가 있습니다:

// allOf: 나열된 모든 스키마를 만족해야 함
{
  "allOf": [
    { "type": "object" },
    { "required": ["id"] },
    { "properties": { "id": { "type": "integer" } } }
  ]
}

// anyOf: 적어도 하나의 스키마를 만족해야 함
{
  "anyOf": [
    { "type": "string" },
    { "type": "null" }
  ]
}

// oneOf: 정확히 하나의 스키마를 만족해야 함(서로 배타적이어야 함)
{
  "oneOf": [
    { "properties": { "type": { "const": "circle" } }, "required": ["radius"] },
    { "properties": { "type": { "const": "rectangle" } }, "required": ["width", "height"] }
  ]
}

format 키워드: date-time, email, uri, uuid

원시 타입을 넘어 JSON Schema의 format 키워드는 문자열에 의도된 모양 — ISO 날짜시간, 이메일 주소, URI, UUID, IP 주소 등 — 을 주석으로 답니다. 모양은 다음과 같습니다:

{
  "type": "object",
  "properties": {
    "id":         { "type": "string", "format": "uuid" },
    "created_at": { "type": "string", "format": "date-time" },
    "email":      { "type": "string", "format": "email" },
    "homepage":   { "type": "string", "format": "uri" }
  }
}

미묘하지만 중요한 점: Draft 2020-12(및 2019-09)에서 format 키워드는 기본적으로 주석입니다 — 검증기가 그렇게 구성되지 않는 한 형식을 강제하지 않습니다. Draft-07 이전에는 일부 검증기에서 기본이 단언이었습니다. Ajv에서 엄격하게 하려면:

import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv();
addFormats(ajv);              // date-time, email, uri, uuid 등을 등록

// 2020-12에서는 메타스키마에서 format 단언을 켜거나, Ajv 버전에 따라
// { validateFormats: true }를 명시적으로 전달해야 합니다.

일반적인 format 값: date-time, date, time, duration, email, idn-email, hostname, ipv4, ipv6, uri, uri-reference, uuid, regex, json-pointer.

format은 힌트로 다루세요, 보안 경계가 아닙니다 — format: "email"을 통과한 문자열도 여전히 신뢰할 수 없는 입력입니다. 전달 가능성이나 실제 유효성을 확인하려면 대역 외에서 검증하세요(DNS, 확인 메일 발송, 직접 통제하는 정규식 실행).

흔한 JSON Schema 실수

「properties」와 「required」 혼동

properties에 키를 나열했다고 해서 그 키가 필수가 되지는 않습니다. properties의 키는 「그 키가 나타난다면」 그 스키마를 따라야 함을 정의합니다. 존재를 강제하려면 required에도 함께 나열해야 합니다.

「number」를 의도하면서 「integer」 사용

"type": "integer"3.14를 거부합니다. 필드가 임의의 숫자(소수 포함)일 수 있다면 "type": "number"를 사용하세요.

「additionalProperties」가 기본이 true임을 잊음

기본적으로 JSON Schema는 properties에 나열되지 않은 추가 속성을 허용합니다. 이는 의도된 것 — 스키마는 포스텔의 법칙에 따라 기본적으로 열려 있어야 합니다. 닫힌 객체를 원한다면 명시적으로 "additionalProperties": false로 설정하세요.

$schema를 지정하지 않음

Draft마다 의미가 다릅니다. $schema 키워드를 생략하면 검증기가 어떤 버전을 쓸지 추측하게 됩니다. 항상 포함하세요:

{ "$schema": "https://json-schema.org/draft/2020-12/schema" }

JSON Schema 검증을 보안 검증으로 취급함

JSON Schema는 구조와 타입을 검증할 뿐 — 정제 도구가 아닙니다. "type": "string"을 통과한 문자열에 여전히 SQL 인젝션, XSS 페이로드, 또는 기타 악의적 내용이 포함될 수 있습니다. 스키마 검증은 첫 번째 게이트일 뿐 완전한 보안 대책이 아닙니다.

JSON Schema와 OpenAPI

OpenAPI(이전의 Swagger)는 요청 본문, 응답 본문, 매개변수를 기술하는 데 JSON Schema의 부분집합을 사용합니다. OpenAPI 사양을 작성했다면 JSON Schema를 작성한 것입니다 — 다만 OpenAPI는 확장과 제한이 일부 있는 수정된 방언을 씁니다.

이는 JSON Schema 지식이 곧바로 API 설계로 옮겨감을 뜻합니다. OpenAPI 문서의 components/schemas 섹션은 JSON Schema 모음이며, Stoplight, Redoc, Swagger UI 같은 도구는 이를 대화형 문서로 자동 렌더링합니다.

실무에서 Ajv 사용하기

실제 프로젝트에서 본전을 뽑게 해주는 플래그가 둘 있습니다:

  • strict: true(현대 Ajv에서 기본) — 스키마 컴파일 시점에 알 수 없는 키워드와 알 수 없는 format을 거부하여, 데이터가 조용히 통과되기 전에 "minimun" 같은 오타나 누락된 $schema을 잡습니다.
  • allErrors: true — 첫 오류에서 멈추는 대신 모든 검증 실패를 모읍니다. 사용자 메시지에는 ajv.errorsText()와 잘 어울립니다.

테스트와 픽스처용으로 스키마에서 가짜 JSON을 생성하려면 json-schema-faker를 사용하세요: 스키마를 따라가며 일치하는 값을 생성합니다(타입, 범위, 열거, 그리고 ajv-formats와 함께 쓰면 format까지). 속성 기반 테스트나 스키마로 UI 초깃값 채우기에 유용합니다.

기존 JSON에서 JSON Schema 생성

이미 있는 JSON 데이터에서 출발점 스키마를 생성하고 싶다면, 여러 도구가 과정을 자동화합니다:

  • quicktype.io — 샘플 JSON에서 스키마(및 TypeScript, Go, Python 등의 타입 정의)를 추론
  • generate-schema (npm) —— generate-schema json schema.json data.json
  • Python: gensonpip install genson SchemaBuilder가 하나 이상의 샘플 객체에서 스키마를 추론

생성된 스키마는 출발점이지 완성품이 아닙니다. 샘플 데이터에서 타입을 추론하지만 비즈니스 규칙은 알지 못합니다 — 어떤 문자열 필드가 이메일이라거나, 정수가 양수여야 한다거나, 두 필드가 상호 배타라는 사실을 추론할 수 없습니다. 운영에 투입하기 전에 생성된 스키마를 항상 검토하고 조여 두세요.

자주 묻는 질문

JSON Schema는 어디에 쓰이나요?

JSON 데이터의 구조를 기술하고 검증하는 데 사용됩니다 — 어떤 키가 필수인지, 각 값이 어떤 타입이어야 하는지, 어떤 값이 허용되는지. OpenAPI, package.json/tsconfig.json의 IDE 자동완성, 폼 생성, 데이터 파이프라인 계약 등을 뒷받침합니다.

JSON과 JSON Schema의 차이는?

JSON은 데이터이고, JSON Schema는 유효한 데이터가 어떤 모습이어야 하는지를 기술하는 별개의 JSON 문서입니다. 포맷 자체가 처음이라면 JSON이란 무엇인가?부터 시작하세요.

어떤 JSON Schema 드래프트를 사용해야 하나요?

Draft 2020-12가 현행이고 새 스키마에 권장됩니다. Draft-07은 도구 지원이 넓어 여전히 흔히 쓰입니다. 검증기가 추측하지 않도록 항상 $schema로 버전을 선언하세요.

스키마에 대해 JSON을 어떻게 검증하나요?

JavaScript에서는 Ajv, Python에서는 jsonschema 라이브러리(위 예시), 또는 웹 검증기를 사용하세요. 우선 JSON이 문법적으로 유효한지 확인하려면 JSON 검증 방법을 보거나 검증기에 돌려 보세요.

JSON Schema가 format(email, date-time, uri…)을 검증하나요?

Draft 2020-12 / 2019-09에서 format 키워드는 기본적으로 주석입니다 — 검증기는 그렇게 설정될 때에만 강제합니다. Ajv에서는 ajv-formats을 등록하고 format 단언을 활성화해야 실제로 잘못된 값을 거부합니다.

결론

JSON Schema는 비공식 문서(「이 필드는 숫자여야 한다」)를 언어, 팀, 시스템에 걸쳐 검증기가 일관되게 강제할 수 있는 기계 검사 가능한 계약으로 바꿉니다. 핵심 어휘 — type, properties, required, items, enum — 은 현실 사례의 대부분을 다룹니다. $ref, 조합 키워드, format 같은 고급 기능이 나머지를 담당합니다.

정말 강제해야 할 것만 강제하는 최소 스키마로 시작하세요. 데이터에 대한 이해가 굳어짐에 따라 점진적으로 제약을 추가합니다. 유효한 데이터를 거부하는 스키마는 스키마가 없는 것만 못합니다.

검증하려는 JSON이 스키마 검증기에 도달하기 전에 이미 문법 오류를 가지고 있다면 먼저 그것부터 고치세요 — 스키마 검증기는 문법적으로 올바른 JSON을 입력으로 요구합니다. JSON이 깨끗해지면 검증한 다음 스키마를 적용하세요.