8 분 소요

JSON 데이터를 주고 받을 때 이 데이터를 전송 받는 측에서는 해당 데이터가 적합한 데이터 형식을 띄고 있는지 검증해야한다. 이를 위해 적법한 JSON 데이터 형식을 기술한 문서를 JSON 스키마라고 한다. JSON 문서에서 기본으로 제공하는 구조 외에 더 많은 구조가 필요할 시에도 스키마를 이용하여 객체의 특징을 명시적으로 정의하면 된다. 그러면 전송 받은 JSON 데이터가 스키마에 부합하도록 작성되었는지 확인할 수 있어 해당 구조의 JSON 데이터만 받아들이게 할 수 있다.

JSON 스키마 검증 (validation)

JSON 스키마는 데이터 검증에 있어 세 가지 검증 과정을 거친다.

  1. 데이터의 타입은 정확한가
  2. 필수로 포함되어야 하는 데이터가 존재하는가
  3. 데이터가 원하는 범위 안에 있는가

위의 검증 기준을 모두 키워드를 이용하여 직접 명시할 수 있다.

검증 키워드 (validation keywords)

JSON 스키마에선 검증 기준을 명시하기 위해 여러 가지 키워드를 사용할 수 있는데, 이를 통해 데이터가 여러 조건을 충족시키도록 조건들을 설정해줄 수 있다.

검증 키워드에는 다음이 있다.

검증 키워드 설명
type 유효한 데이터의 자료형을 명시
properties 유효한 데이터의 key-value 쌍을 명시
required 명시한 배열의 모든 요소를 프로퍼티로 가져야만 유효
minimum 최솟값 이상의 숫자만 유효함
maximum 최대값 이하의 숫자만 유효
multipleOf 명시한 숫자의 배수만 유효
maxLength 명시한 최대 길이 이하의 문자열만 유효
minLength 명시한 최소 길이 이상의 문자열만 유효
pattern 명시한 정규 표현식에 해당하는 문자열만 유효

스키마에 대한 정보를 나타내는 메타 데이터 키워드는 title, description, default가 있다.

다음은 검증 키워드 및 메타 데이터 키워드를 이용한 JSON 스키마 예제이다.

{
	"title": "회원정보 스키마",
	"description": "해당 스키마는 회원정보에 관한 데이터 검증을 목적으로 작성된 스키마",
	"type": "object",
	"properties": {
		"name": {"type": "string"},
		"age": {"type": "integer"},
		"job": {"type": "string"},
		"otherProfile": {
			"type": "object",
			"properties": {
				"hobby": {"type": "string"},
				"phoneNumber": {"type": "string"}
			}
		}
	}
}

검증

숫자 검증

정수 검증

{
	"type": "integer"
}

위와 같이 type 키워드의 값을 integer라고 명시하면 해당 데이터가 정수인지를 검증한다. 위 예제의 경우 0, -22, 13 등의 정수는 검증을 통과하나, 3.14와 같은 실수나 “111”과 같은 문자열은 검증에 통과하지 못한다.

숫자 검증

{
	"type": "number"
}

type 키워드의 값을 number로 명시하면 해당 데이터가 숫자인지를 검증한다. 따라서 정수, 실수 모두 검증을 통과한다. 그 외에 숫자가 아닌 자료형 데이터들은 해당 검증을 통과하지 못한다.

배수 검증

데이터로 받아온 숫자가 multipleOf 키워드의 값으로 명시된 숫자의 배수인지를 검증할 수 있다.

다음은 해당 데이터가 정수이면서 5의 배수임을 검증하는 예제이다.

{
	"type": "integer",
	"multipleOf": 5
}

위 예제에서 5의 배수인 정수는 통과하나 23처럼 5의 배수가 아닌 숫자 또는 문자열 등의 전혀 다른 자료형을 가진 데이터들은 해당 검증을 통과하지 못한다.

범위 검증

전송된 데이터 숫자가 유효한 범위 안에 있는지를 검증할 수 있다. 이 때 쓰이는 키워드는 다음과 같다.

  • minimum : 숫자가 가질 수 있는 최솟값 명시
  • maximum : 숫자가 가질 수 있는 최대값 명시
  • exclusiveMinimum : minimum 키워드 값으로 명시된 숫자까지 유효 범위에 포함할 지 명시. true는 해당 숫자도 포함 (이상, ≥와 동일), false는 해당 숫자는 미포함(초과, >와 동일). 기본 값은 false로 설정되어 있음.
  • exclusiveMaximum : maximum 키워드 값으로 명시된 숫자까지 유효 범위에 포함할 지 명시. true는 해당 숫자도 포함 (이하, ≤와 동일), false는 해당 숫자는 미포함 (미만, <와 동일). 기본 값은 false로 설정되어 있음.

예를 들어, 해당 데이터가 숫자이면서 3보다 크거나 같고 6보단 작은 수 (3 ≤ x < 6)인지를 검증하고자 할 땐 다음처럼 작성하면 되겠다.

{
	"type": "number",
	"minimum": 3,
	"maximum": 6,
	"exclusiveMaximum": true
}

문자열 검증

{
	"type": "string"
}

위와 같이 type 키워드의 값을 string으로 명시하면 해당 데이터가 유니코드 문자열인지를 검증한다. 따라서 “제이슨”, “hi”, “3.14” 등의 문자열은 검증을 통과하고 문자열이 아닌 다른 자료형의 데이터들은 해당 검증을 통과하지 못하는 것이다.

문자열 길이 검증

다음의 키워드를 이용하면 데이터의 문자열의 길이가 유효한지 검증할 수 있다.

  • minLength: 해당 키워드 값보다 문자열이 더 긴지를 확인
    • 예) “minLength”: 3 ⇒ 해당 문자열의 길이는 3보다 커야 함
  • maxLength: 해당 키워드 값보다 문자열이 더 짧은 지를 확인
    • 예) “maxLength”: 7 ⇒ 해당 문자열의 길이는 7보다 작아야 함

이 때 두 키워드 모두 값은 0을 포함한 양수만 사용 가능하다.

다음은 데이터로 전송된 문자열의 길이가 3보다 크고 7보다 작은지를 검증하는 예제이다.

{
	"type": "string",
	"minLength": 3,
	"maxLength": 7
}

정규 표현식 검증

pattern 키워드를 이용하여 전송된 문자열이 pattern 키워드의 값으로 명시된 정규 표현식과 일치하는 지를 검증할 수 있다.

정규 표현식에 대한 내용은 파이썬 문서의 정규 표현식정규 표현식 심화 를 참고.

다음은 전송된 문자열이 1개 이상의 영문 소문자를 가지는지를 검증한다.

{
	"type": "string",
	"pattern": "[a-z]+"
}

객체 검증

{
	"type": "object"
}

type 키워드 값을 object로 명시하면 해당 데이터가 객체인지를 검증한다. 위 예제에서는 프로퍼티를 갖는 객체는 검증을 통과하나 그 외 다른 자료형의 데이터는 검증을 통과하지 못한다.

프로퍼티 검증

properties 키워드를 이용하여 해당 객체가 가지는 프로퍼티가 유효한지를 검사할 수 있다. 해당 객체가 가지는 각각의 프로퍼티의 key를 가지는지, 그리고 그 key에 해당하는 value가 특정 타입의 데이터인지를 검증할 수 있다.

{
	"type": "object",
	"properties": {
		"name": {"type": "string"},
		"age": {"type": "integer"},
		"job": {"type": "string"}
	}
}

다음 예제의 JSON 객체는 위 검증을 통과한다.

{
	"name": "제이슨",
	"age": 24,
	"job": "공무원"
}

그러나 다음의 JSON 객체는 위의 프로퍼티 검증을 거치지 못한다.

{
	"name": "제이슨",
	"age": "24",
	"job": "공무원"
}

“age” 프로퍼티의 값이 정수가 아닌 문자열로 쓰였기 때문이다.

필수 프로퍼티 검증

required 키워드를 이용하여 객체가 필수로 가져야 하는 프로퍼티를 명시할 수 있다. 필수 프로퍼티가 하나 이상이면 각 필수 프로퍼티들의 key 이름을 배열로 나열하면 된다.

{
	"type": "object",
	"properties": {
		"name": {"type": "string"},
		"age": {"type": "integer"},
		"job": {"type": "string"}
	},
	"required": ["name", "age"]
}

위 예제는 전송받은 데이터 자료형이 객체이면서 name, age 프로퍼티를 가졌는지를 검증하고 있다.

프로퍼티 개수 검증

전송된 객체가 가질 수 있는 프로퍼티 개수의 최소값과 최대값을 명시할 수 있다. minProperties와 maxProperties 키워드를 이용하면 된다.

다음 예제는 전송받은 데이터 자료형이 객체이면서 프로퍼티를 1개 또는 2개만 가졌는지를 검증한다.

{
	"type": "object",
	"minProperties": 1,
	"maxProperties": 2
}

배열 검증

{
	"type": "array"
}

type 키워드 값으로 array로 명시하면 전송받은 데이터가 배열인지를 검사해준다.

배열 요소 검증

배열에 저장된 각 요소에 대한 검증을 할 수도 있다. 이는 items 키워드를 통해 할 수 있다.

다음은 전송받은 데이터가 배열이면서, 각 배열 요소가 모두 정수인지를 검사하는 예제이다.

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

배열 요소가 모두 정수이거나 요소 자체가 없는 빈 배열은 위 검증을 통과한다.

배열의 각 요소에 서로 다른 검증 조건을 붙이고 싶다면 “items”의 값으로 각 요소들에 적용할 스키마들의 배열 형태로 나타내면 된다. 즉, 다음의 구조를 띤다.

{
	"type": "array",
	"items": [
		{
			"type": "string",
			"maxLength": 10
		},
		{
			"type": "string"
		},
		{
			"type": "string"
		}
	]
}

items의 값으로 대괄호 []를 이용하여 배열을 나타내었고, 그 안에 배열의 요소로 들어갈 각각의 요소에 중괄호 ({}) 안에 각기 다른 스키마를 설정하였다. 예로, 해당 배열의 첫 요소는 자료형이 문자열이어야 하며, 문자열의 최대값은 10으로 설정하였다. 그 외 2, 3번째 요소에는 자료형이 문자열이기만 하면 된다고 설정하였다.

또 한, 위 예제의 3개의 유효한 배열 요소 외에 추가로 다른 배열 요소를 가지고 있는 배열도 검증을 통과한다. ⇒ 4번째 요소부터는 어떻게 적용된다는 건지 이해가 안 감. 4번째 이후 요소들에는 3번째 요소 스키마를 적용시킨다는 것인지, 1, 2, 3번째 스키마를 번갈아가며 적용시킨다는 건지 모르겠음.

그러나 additionalItems 키워드 값을 false로 명시하면 추가로 다른 배열 요소를 가지는 배열은 검증을 통과하지 못하게 할 수 있다.

{
	"type": "array",
	"items": [
		{
			"type": "string",
			"maxLength": 10
		},
		{
			"type": "string"
		},
		{
			"type": "string"
		}
	],
    "additionalItems": false
}

배열 길이 검증

다음의 키워드를 통해 해당 배열이 가질 수 있는 길이의 최대, 최소값을 명시할 수 있다.

  • minItems
  • maxItems

다음은 전송 받은 데이터가 배열이면서 동시에 배열 요소가 4개부터 12개 사이인지를 검증하는 예제이다.

{
	"type": "array",
	"minItems": 4,
	"maxItems": 12
}

중복 값 검증

전송 받은 배열 요소가 서로 중복되는 것을 허용할지 여부를 명시할 수 있다.

uniqueItems 키워드 값이 true이면 배열 내 중복 값을 허용하지 않게 한다.

{
	"type": "array",
	"uniqueItems": true
}

만약 배열이 [1, 2, 3, 4, 5]라면 배열 내 중복되는 값이 없기에 위 검증을 통과할 수 있다. 그러나 배열이 [1, 1, 2, 3, 4]라면 1이라는 요소가 서로 중복되므로 위 검증을 통과하지 못한다.

불리언 검증

{
	"type": "boolean"
}

전송 받은 데이터의 자료형이 불리언인지를 검증한다. true 또는 false인 경우에만 해당 검증을 통과할 수 있다.

JSON에서는 true, false 대신 숫자 1, 0을 대신 사용할 수 없으므로 1, 0은 위 검증을 통과하지 못한다.

null 검증

{
	"type": "null"
}

null 이외에 모든 값은 검증을 통과하지 못한다.

열거형 데이터 검증

전송 받은 데이터가 enum 키워드 값으로 명시된 배열에 속한 값인지를 검증할 수 있다. 이 때 중복 값을 명시할 수는 없다.

{
	"type": "string",
	"enum": ["크로아상", "쿠키", "케이크"]
}

위 스키마에서는 “크로아상”, “쿠키”, “케이크” 이외의 모든 값은 검증을 통과할 수 없다.

스키마 결합

다음의 키워드들을 이용하여 여러 JSON 스키마를 결합하여 사용할 수 있다.

  • allOf
  • anyOf
  • oneOf

스키마 결합을 이용하면 조금 더 복잡한 조건의 스키마를 형성하여 전송 받은 데이터가 더 복잡한 조건을 통과하게끔 할 수 있다.

allOf

allOf 키워드 값으로 배열이 들어가며, 배열 안에 여러 스키마를 넣을 수 있다. allOf 키워드는 전송받은 데이터가 배열 안에 들어있는 모든 스키마를 통과해야만 검증을 통과할 수 있다.

다음은 문자열의 길이가 5이상 10이하인지를 검증하는 예제이다.

{
	"allOf": [
		{"minLength": 5},
		{"maxLength": 10}
	]
}

위 예제에서는 문자열 길이가 최소 5인지를 검증하는 스키마와 최대 10 이하인지를 검증하는 스키마가 있다. 이 때 이 모든 스키마의 검증을 통과하는 데이터만 검증을 통과하게 된다. 즉, 위 예제에서는 “hobby”, “precious”와 같은 길이가 5 이상 10 이하인 문자열만이 검증을 통과한다.

마치 논리 연산의 and와도 같다.

(A and B ⇒ A조건과 B조건을 모두 만족하는가. A, B 두 명제 모두 참이어야만 참이다.)

anyOf

앞서 살펴본 allOf가 and와 같다면, anyOf는 or와 같은 기능을 수행한다. 즉, 배열로 묶인 여러 스키마 중 하나라도 통과해야만 해당 데이터는 검증을 통과할 수 있는 것이다.

다음은 전송 받은 데이터가 객체이거나 배열인지를 검사하는 예제이다.

{
    "anyOf": [
		{"type": "object"},
		{"type": "array"}
	]
}

위 예제에서는 전송 받은 데이터가 객체인지를 검증하는 스키마와 배열인지를 검증하는 스키마가 배열로 묶여있다. anyOf가 쓰였으므로 두 스키마 중 하나의 검증만 통과해도 해당 데이터는 검증을 통과할 수 있다. 그래서 위 예제의 경우 객체와 배열인 데이터만 통과할 수 있다.

(논리 연산 or에 비유하면, A or B일 때 A, B 둘 중 하나만 참이여도 해당 명제는 참이 되는 것과 같다)

oneOf

oneOf 키워드는 배열 내에 나열된 여러 스키마 중 하나만을 충족해야만 해당 검증을 통과할 수 있다. 그래서 둘 이상의 스키마를 충족한다면 해당 검증을 통과할 수 없다.

다음은 해당 데이터가 숫자이고, 동시에 3의 배수이거나 4의 배수인지를 검증하는 예제이다.

{
	"oneOf": [
		{"type": "number", "multipleOf": 3},
		{"type": "number", "multipleOf": 4}
	]
}

위 예제에서는 해당 데이터가 숫자이고 3의 배수인 스키마와 숫자이고 4의 배수인 스키마가 배열되어 있다. oneOf 키워드이므로 둘 중 하나의 스키마만 만족시키는 데이터만 통과될 수 있다. 따라서, 3, 6, 9와 같은 3의 배수와 4, 8, 16과 같은 배수는 검증을 통과하지만, 12, 24, 36처럼 3, 4의 공배수는 검증을 통과하지 못한다.

추측) 만약 3, 4의 공배수도 허용하고자 한다면 oneOf 대신 anyOf를 쓰면 되지 않을까?

not

해당 키워드를 사용하면 명시된 스키마를 만족시키지 못하는 데이터만을 통과시킬 수 있다.

{
	"not": {
		"type": "string"
	}
}

위 예제에서는 전송 받은 데이터가 문자열이 아닐 때에만 통과한다.


References

[1]

코딩교육 티씨피스쿨

[2]

거의 모든 SW 개발의 필수⋯JSON 데이터 포맷의 이해

This content is licensed under CC BY-NC 4.0

댓글남기기