빠른 비교
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["name", "email"],
"additionalProperties": false
}갈리는 기준
properties는 필드 규칙을 설명하지 필수를 강제하지 않는다 — 두 관심사는 다른 keyword로 분리된다
JSON Schema에서 properties에 필드를 선언하는 것은 "그 필드가 있다면 이 규칙을 따른다"는 의미입니다. 필드의 존재를 요구하지 않습니다. 즉 properties에 name을 선언하고 required에 포함하지 않으면, name 필드가 없는 instance도 검증을 통과합니다. "properties에 적었으니 당연히 필수겠지"라는 가정이 실제 동작과 다르기 때문에, required를 명시적으로 선언하지 않으면 예상치 못한 데이터가 검증을 통과하는 문제가 생깁니다.
required는 키 존재를 검증한다 — 값이 null이어도 required는 통과한다
required 배열에 포함된 필드는 instance에 키 자체가 존재해야 검증을 통과합니다. 그러나 키가 있고 값이 null인 경우는 required 검증을 통과합니다. null 허용 여부는 해당 필드의 type 규칙으로 별도 제어해야 합니다. "type": "string"만 선언하면 null은 거부되고, "type": ["string", "null"]로 선언하면 null이 허용됩니다. required와 type은 각각 "있는가"와 "어떤 타입인가"를 독립적으로 검사합니다.
{
"properties": {
"name": { "type": "string" },
"nickname": { "type": ["string", "null"] }
},
"required": ["name"]
}additionalProperties: false는 schema를 닫는다 — 오타 방지에는 유리하지만 확장에는 제약이 생긴다
additionalProperties: false를 선언하면 properties에 없는 필드가 instance에 있을 때 검증이 실패합니다. 이는 오타로 잘못된 필드 이름을 썼을 때 즉시 잡을 수 있고, 소비자가 "이 object에 어떤 필드가 있는지" 완전히 예측할 수 있다는 장점이 있습니다. 반면 나중에 새 필드를 추가하려면 schema를 반드시 업데이트해야 하고, 조합 schema(allOf, anyOf)와 함께 쓸 때 의도치 않게 다른 schema의 필드를 차단하는 부작용이 생길 수 있습니다. JSON Schema 문서는 이 조합을 특히 주의하라고 명시합니다.
열린 schema와 닫힌 schema의 트레이드오프 — 용도에 따라 선택해야 한다
additionalProperties: false인 닫힌 schema는 내부 검증, 설정 파일 파싱, 입력값 엄격 검증에 적합합니다. 반면 외부 API 응답을 검증할 때는 열린 schema(additionalProperties 기본값)가 더 실용적입니다. API가 나중에 새 필드를 추가해도 기존 소비자의 schema 검증이 깨지지 않기 때문입니다. API 응답 schema를 너무 닫아놓으면, 서버가 새 필드를 추가할 때마다 클라이언트 schema도 함께 업데이트해야 하는 과도한 결합이 생깁니다.
즉 입력 검증은 보통 더 닫혀야 하고, 외부 응답 검증은 더 열려 있어야 합니다. 둘을 같은 강도로 닫아 두면 한쪽에서는 오타를 놓치고, 다른 쪽에서는 확장에 과민하게 반응합니다.
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["name", "email"],
"additionalProperties": false
}선택 기준
| 상황 | 적합한 선택 |
|---|---|
| 필드 존재 여부 검증 | required 배열 |
| null 허용 여부 제어 | type: ["string", "null"] |
| 알 수 없는 필드 차단 (내부 검증) | additionalProperties: false |
| 외부 API 응답 검증 (확장 대비) | additionalProperties 기본값 유지 (열린 schema) |
| 필수 + nullable 필드 | required와 type: [..., "null"]를 함께 선언 |
주의할 점
properties만 선언하고 필수 검사가 동작하지 않는다고 느끼면 required를 추가해야 합니다.
additionalProperties: false는 조합 schema(allOf, anyOf)와 함께 쓸 때 의도치 않게
다른 schema의 필드를 차단하는 부작용이 생길 수 있으므로 주의가 필요합니다. 외부 API 응답
schema에는 닫힌 schema보다 열린 schema가 더 실용적입니다.
{
"type": "object",
"properties": {
"name": { "type": "string" }
},
"additionalProperties": false
}이 schema는 name이 없어도 통과합니다. object를 "닫았다"와 "필수 필드를 강제했다"는 다른 문제이므로, 둘을 같은 것으로 보면 검증이 생각보다 느슨해집니다.
반대로 외부 API 응답에 무조건 additionalProperties: false를 걸어 두면 서버가 harmless한 새 필드를 추가했을 때도 소비자 검증이 깨질 수 있습니다. 입력과 응답의 목표를 나눠서 생각하는 편이 안전합니다.
참고 링크
1 sources