CodeGym /행동 /JAVA 25 SELF /JSON 검증: JSON Schema, 검증 오류

JSON 검증: JSON Schema, 검증 오류

JAVA 25 SELF
레벨 46, 레슨 4
사용 가능

1. 왜 JSON 검증이 필요한가

상상해 봅시다: 여러분은 클래스 User를 작성했고, 항상 다음과 같은 JSON이 들어올 것이라고 기대합니다:

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

그런데 갑자기 이런 것이 들어옵니다:

{
  "id": "사십이",
  "name": 123,
  "email": null,
  "admin": true
}

혹은 심지어 이런 경우도 있습니다:

{
  "username": "Alice"
}

최선의 경우 Jackson이나 Gson이 역직렬화를 시도하는 순간 예외를 던집니다. 최악의 경우 — 아무 말 없이 필드에 기본값을 넣어 버려 비즈니스 코드가 잘못 동작하기 시작합니다. 이것이 여러분 서비스의 설정이라면 — 팀 전체가 한동안 찾아 헤매야 할 곤란한 버그로 이어질 수 있습니다.

JSON 검증 — JSON의 구조, 타입, 값이 정해진 규칙(스키마)에 맞는지 확인하는 과정입니다. 마치 데이터에 대한 출입국 심사와 같습니다: 통과하지 못하면 탑승 불가!

2. JSON Schema: 무엇이며 어떻게 생겼나

JSON 세계에는 데이터 구조를 설명하는 공식 표준 — JSON Schema가 있습니다. 프로그램 요구사항에 JSON이 부합하는지 확인할 수 있는 “체크리스트”와 같습니다.

JSON Schema도 JSON이지만, type, properties, required 같은 특수 키를 사용합니다.

가장 간단한 스키마 예시

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string" },
    "email": { "type": "string", "format": "email" }
  },
  "required": ["id", "name"]
}

여기서 무엇을 정의하나요:

  • 객체를 기대합니다 (type: "object").
  • 객체에는 "id", "name", "email" 필드가 있을 수 있으며(properties에서 정의).
  • "id" — 반드시 정수(type: "integer").
  • "name" — 반드시 문자열(type: "string").
  • "email" — 이메일 형식의 문자열(키 format 값은 "email").
  • required는 필수 필드 목록을 지정합니다: "id"와 "name".

JSON에 "id"나 "name"이 없거나 타입이 다르면 — 검증을 통과하지 못합니다.

JSON Schema로 할 수 있는 일(요약)

  • 타입 지정(type: "string", "integer", "array", "object", "boolean", "null").
  • 중첩 객체와 배열 설명(properties, items).
  • 필수/옵션 필드(required).
  • 문자열 길이, 숫자 범위 검사(minLength, maximum 등).
  • 형식 검사(format: "email", "date", "uri" 등).
  • 열거형(enum: 허용되는 값 목록).
  • 문자열에 대한 정규식(pattern).
  • 복합 조건: anyOf, oneOf, allOf(고급 케이스).

3. Java에서의 JSON 검증: 라이브러리 개요

Java 표준 라이브러리에는 스키마 기반 JSON 검증이 포함되어 있지 않습니다. 하지만 인기 있는 서드파티 라이브러리가 있습니다. 대표적인 것들은 다음과 같습니다:

  • everit-org/json-schema — 간단하고 무료이며 널리 사용됩니다.
  • networknt/json-schema-validator — 빠르고 최신 표준을 지원합니다.
  • Jackson-module-jsonSchema — Jackson용 확장(완전한 검증은 지원하지 않음).
  • Justify, Java JSON Tools — 그 밖에도 있으나 비교적 드뭅니다.

이 강의에서는 everit-org/json-schema를 살펴보겠습니다 — 초보자도 쓰기 쉽고, 문서가 잘 되어 있으며, 복잡한 설정이 필요 없습니다.

everit-org/json-schema 설치

pom.xml(Maven)에 의존성을 추가하세요:

<dependency>
  <groupId>org.everit.json</groupId>
  <artifactId>org.everit.json.schema</artifactId>
  <version>1.14.2</version>
</dependency>

또는 Gradle:

implementation 'org.everit.json:org.everit.json.schema:1.14.2'

4. 예제: 스키마 기반 JSON 검증(단계별)

실제로 JSON을 검증해 봅시다. 이를 위해 스키마, JSON, 그리고 약간의 코드가 필요합니다.

스키마 예시 (user-schema.json):

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string", "minLength": 2, "maxLength": 30 },
    "email": { "type": "string", "format": "email" }
  },
  "required": ["id", "name"]
}

유효한 JSON 예시 (user.json):

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com"
}

유효하지 않은 JSON 예시:

{
  "id": "하나",
  "name": "",
  "email": "not-an-email"
}

검증 코드

import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONTokener;
import org.everit.json.schema.ValidationException;

import java.nio.file.Files;
import java.nio.file.Paths;

public class JsonValidationExample {
    public static void main(String[] args) throws Exception {
        // 파일에서 스키마 로드
        String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
        JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
        Schema schema = SchemaLoader.load(rawSchema);

        // 검증할 JSON 로드
        String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
        JSONObject json = new JSONObject(new JSONTokener(jsonString));

        // 검증
        try {
            schema.validate(json); // 모든 것이 정상이라면 아무 일도 일어나지 않습니다
            System.out.println("JSON이 유효합니다!");
        } catch (ValidationException e) {
            System.out.println("JSON이 유효하지 않습니다!");
            for (String msg : e.getAllMessages()) {
                System.out.println("오류: " + msg);
            }
        }
    }
}

코드에 대한 설명:

  • Schema 클래스와 로더 SchemaLoader.load(...)를 사용합니다.
  • 메서드 schema.validate(json)는 스키마에 맞지 않으면 예외를 던집니다.
  • catch 블록에서 getAllMessages()로 모든 오류를 얻을 수 있습니다.

애플리케이션에 어떻게 통합할까요?

보통 스키마는 리소스(예: resources 폴더)에 보관합니다. Java 객체로 역직렬화하기 전에 JSON을 검증합니다. 문제가 없다면 역직렬화하여 이후 로직을 진행합니다.

5. 검증 오류 처리

JSON이 검증을 통과하지 못하면 라이브러리는 ValidationException을 던집니다. 메시지에는 어떤 부분이 스키마와 맞지 않는지에 대한 오류 목록이 포함됩니다.

오류 출력 예시

위의 유효하지 않은 JSON에 대해 출력은 대략 다음과 같습니다:

JSON이 유효하지 않습니다!
오류: #: required key [id] not found
오류: #/name: expected minLength: 2, actual: 0
오류: #/email: String [not-an-email] is invalid against requested format [email]
오류: #/id: expected type: Integer, found: String

오류를 해석하는 방법:

  • required key [id] not found — 필수 필드가 없습니다.
  • expected minLength: 2, actual: 0 — 문자열이 너무 짧습니다.
  • String [...] is invalid against requested format [email] — 잘못된 이메일 형식입니다.
  • expected type: Integer, found: String — 타입이 일치하지 않습니다.

중요! 메시지는 영어일 수 있지만 충분히 이해하기 쉽습니다.

사용자에게 오류를 어떻게 보여줄까요?

오류를 목록으로 모아 REST API 또는 GUI에서 사용자에게 반환할 수 있습니다. 그러면 입력 데이터에 무엇이 잘못되었는지 빠르게 파악할 수 있습니다.

6. 실습: 객체 배열 검증

종종 단일 객체가 아니라 배열을 검증해야 합니다:

[
  { "id": 1, "name": "Alice", "email": "alice@example.com" },
  { "id": 2, "name": "Bob" },
  { "id": "이게 뭐지?", "name": 123, "email": "not-an-email" }
]

스키마:

{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id":    { "type": "integer" },
      "name":  { "type": "string", "minLength": 2, "maxLength": 30 },
      "email": { "type": "string", "format": "email" }
    },
    "required": ["id", "name"]
  }
}

검증 방식은 동일하며, JSON으로 배열을 전달합니다. 오류에는 문제가 있는 요소의 인덱스가 포함됩니다(예: [#/2/id]).

7. JSON 검증에서 자주 발생하는 실수

오류 №1: 데이터 타입 불일치. 종종 숫자 대신 문자열이 들어옵니다("id": "123")인데, 스키마는 integer를 기대합니다. 검증은 통과하지 못합니다. 데이터 출처를 통제할 수 없다면 — 스키마를 수정하거나 미리 데이터를 변환하세요.

오류 №2: 필수 필드 누락. 스키마에 필수로 지정되어 있다면("required": ["id","name"]), JSON에 해당 필드가 없을 경우 오류가 발생합니다. 프런트엔드가 필드를 보내지 않았거나 API가 변경된 경우 등 예상치 못하게 발생할 수 있습니다.

오류 №3: JSON의 불필요한 필드. 기본적으로 JSON Schema는 추가 필드를 허용합니다. 엄격한 스키마를 원한다면 "additionalProperties": false를 추가하세요. 그렇지 않으면 JSON에 임의의 불필요한 필드가 들어올 수 있습니다.

오류 №4: 스키마 버전 또는 문법 오류. 스키마 버전에 없는 키를 사용하거나 오타가 있으면 검증기가 스키마를 로드하지 못합니다. https://www.jsonschemavalidator.net/ 같은 서비스로 스키마를 확인하세요.

오류 №5: 미흡한 오류 처리. 첫 번째 예외만 잡고 사용자에게 상세 정보를 보여주지 않으면 JSON의 어떤 부분이 문제인지 파악하기 어렵습니다. 모든 오류를 출력하려면 getAllMessages()를 사용하세요.

오류 №6: 지나치게 엄격한 형식. "format": "email"이나 "date" 검사는 다소 “피상적”일 수 있습니다. 더 엄격한 검증이 필요하다면 코드에서 추가 검사를 사용하세요.

1
과제
JAVA 25 SELF, 레벨 46, 레슨 4
잠금
새 사용자 프로필 검사 🧐
새 사용자 프로필 검사 🧐
1
과제
JAVA 25 SELF, 레벨 46, 레슨 4
잠금
온라인 클럽 입구의 수문장: 이메일 검증 🚫
온라인 클럽 입구의 수문장: 이메일 검증 🚫
1
설문조사/퀴즈
JSON 직렬화, 레벨 46, 레슨 4
사용 불가능
JSON 직렬화
JSON 직렬화
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION