본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 9장: 스키마 레지스트리와 데이터 계약
2026년 2월 26일·데이터·

9장: 스키마 레지스트리와 데이터 계약

스키마 진화의 필요성, Confluent Schema Registry, Avro/Protobuf/JSON Schema 비교, 호환성 규칙, 데이터 계약 개념까지 스키마 관리 전략을 체계적으로 학습합니다.

18분667자9개 섹션
streamingdata-engineering
공유
realtime-pipeline9 / 11
1234567891011
이전8장: CDC(Change Data Capture)다음10장: Exactly-once 보장과 신뢰성

학습 목표

  • **Schema Evolution(스키마 진화)**의 필요성과 위험을 이해합니다.
  • Confluent Schema Registry의 아키텍처와 동작 방식을 파악합니다.
  • Avro, Protobuf, JSON Schema의 특성을 비교합니다.
  • 호환성 규칙(BACKWARD, FORWARD, FULL, NONE)을 학습합니다.
  • Data Contract(데이터 계약) 개념과 스키마 검증 전략을 이해합니다.

스키마 진화의 필요성

실시간 파이프라인에서 프로듀서와 컨슈머는 독립적으로 배포됩니다. 프로듀서가 새 필드를 추가하거나 필드 타입을 변경했는데, 컨슈머가 아직 업데이트되지 않았다면 어떻게 될까요?

스키마 없는 세계의 문제

JSON을 스키마 없이 사용하는 경우를 생각해 보겠습니다.

v1-event.json
json
{"orderId": "O-001", "amount": 50000, "currency": "KRW"}

어느 날 프로듀서 팀이 필드를 추가합니다.

v2-event.json
json
{"orderId": "O-002", "amount": 50000, "currency": "KRW", "discount": 5000}

이 정도는 문제가 없습니다. 그런데 다음과 같은 변경이라면?

v3-breaking.json
json
{"order_id": "O-003", "total": 50000, "curr": "KRW"}

필드 이름이 변경되어 모든 컨슈머가 즉시 깨집니다. 스키마 관리 없이는 이런 파괴적 변경을 사전에 차단할 방법이 없습니다.

스키마 관리가 해결하는 문제

  • 계약 정의: 프로듀서와 컨슈머 간 데이터 형식의 명확한 계약
  • 호환성 보장: 비호환 변경을 배포 전에 자동 차단
  • 문서화: 스키마 자체가 데이터의 구조 문서 역할
  • 직렬화 효율: 타입 정보를 스키마에 분리하여 페이로드 크기 감소

Confluent Schema Registry

Confluent Schema Registry는 Kafka 생태계에서 가장 널리 사용되는 스키마 관리 시스템입니다. 스키마를 중앙에서 저장, 버전 관리, 호환성 검증하는 서비스입니다.

아키텍처

동작 흐름은 다음과 같습니다.

  1. 프로듀서가 스키마를 Schema Registry에 등록합니다 (이미 존재하면 ID만 반환).
  2. 프로듀서가 메시지를 직렬화할 때, 스키마 ID를 메시지 앞에 붙여 Kafka에 전송합니다.
  3. 컨슈머가 메시지를 읽을 때, 스키마 ID로 Schema Registry에서 스키마를 조회합니다.
  4. 조회한 스키마로 메시지를 역직렬화합니다.

스키마 등록과 조회

schema-registry-api.sh
bash
# 스키마 등록
curl -X POST http://schema-registry:8081/subjects/orders-value/versions \
  -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  -d '{
    "schema": "{\"type\":\"record\",\"name\":\"Order\",\"fields\":[{\"name\":\"orderId\",\"type\":\"string\"},{\"name\":\"amount\",\"type\":\"double\"},{\"name\":\"currency\",\"type\":\"string\"}]}"
  }'
 
# 최신 스키마 조회
curl http://schema-registry:8081/subjects/orders-value/versions/latest
 
# 특정 버전 조회
curl http://schema-registry:8081/subjects/orders-value/versions/1
 
# 모든 서브젝트 목록
curl http://schema-registry:8081/subjects
 
# 호환성 검사 (등록 전 미리 테스트)
curl -X POST http://schema-registry:8081/compatibility/subjects/orders-value/versions/latest \
  -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  -d '{"schema": "..."}'
Info

스키마는 Subject(서브젝트) 단위로 관리됩니다. 기본 전략인 TopicNameStrategy에서는 [토픽명]-key와 [토픽명]-value가 서브젝트가 됩니다. 예를 들어 orders 토픽의 값 스키마는 orders-value 서브젝트에 저장됩니다.


직렬화 포맷 비교

Schema Registry는 세 가지 주요 직렬화 포맷을 지원합니다.

Apache Avro

Avro는 Kafka 생태계에서 가장 오랫동안 사용된 직렬화 포맷입니다.

order-avro-schema.avsc
json
{
  "type": "record",
  "name": "Order",
  "namespace": "com.shop.events",
  "fields": [
    {"name": "orderId", "type": "string"},
    {"name": "amount", "type": "double"},
    {"name": "currency", "type": "string", "default": "KRW"},
    {"name": "discount", "type": ["null", "double"], "default": null},
    {"name": "metadata", "type": {
      "type": "map",
      "values": "string"
    }, "default": {}}
  ]
}

Avro의 특성은 다음과 같습니다.

  • 바이너리 포맷: 컴팩트한 직렬화, 네트워크/디스크 효율적
  • 스키마와 데이터 분리: 스키마는 Registry에, 데이터에는 스키마 ID만 포함
  • 동적 타이핑: 코드 생성 없이도 사용 가능 (Generic Records)
  • 풍부한 스키마 진화: 기본값, union 타입을 통한 유연한 진화

Protocol Buffers (Protobuf)

Google이 개발한 직렬화 포맷으로, 강타입과 성능이 강점입니다.

order.proto
protobuf
syntax = "proto3";
package shop.events;
 
message Order {
  string order_id = 1;
  double amount = 2;
  string currency = 3;
  optional double discount = 4;
  map<string, string> metadata = 5;
}

Protobuf의 특성은 다음과 같습니다.

  • 매우 컴팩트: 필드 번호 기반 인코딩으로 Avro보다 작은 크기
  • 코드 생성: .proto 파일로 다양한 언어의 클라이언트 코드 자동 생성
  • 강타입: 컴파일 타임에 타입 오류 검출
  • 필드 번호 기반 진화: 필드 번호를 유지하면 이름 변경 가능

JSON Schema

JSON 데이터의 구조를 정의하는 표준입니다.

order-json-schema.json
json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "orderId": {"type": "string"},
    "amount": {"type": "number"},
    "currency": {"type": "string", "default": "KRW"},
    "discount": {"type": "number"}
  },
  "required": ["orderId", "amount"]
}

JSON Schema의 특성은 다음과 같습니다.

  • 사람이 읽기 쉬움: JSON 텍스트 포맷
  • 낮은 진입 장벽: JSON에 익숙한 개발자에게 친숙
  • 크기 비효율: 텍스트 포맷이므로 바이너리 대비 크기가 큼
  • 스키마 진화 제한: Avro/Protobuf 대비 진화 규칙이 약함

포맷 비교

항목AvroProtobufJSON Schema
크기작음매우 작음큼
속도빠름매우 빠름느림
가독성바이너리바이너리텍스트
코드 생성선택적필수불필요
스키마 진화우수우수보통
Kafka 생태계최적좋음좋음
Tip

Kafka 생태계에서는 Avro가 가장 널리 사용됩니다. gRPC를 함께 사용하거나 다국어 환경이라면 Protobuf를 고려하세요. JSON Schema는 디버깅 편의성이 중요하고 성능 요구가 낮은 경우에 적합합니다.


호환성 규칙

Schema Registry의 핵심 기능은 스키마 변경의 호환성을 자동으로 검증하는 것입니다. 새 스키마를 등록할 때, 이전 버전과의 호환성을 확인하여 비호환 변경을 차단합니다.

BACKWARD 호환성

새 스키마로 이전 데이터를 읽을 수 있음을 보장합니다. 컨슈머가 먼저 업그레이드되는 시나리오에 적합합니다.

허용되는 변경:

  • 기본값이 있는 필드 추가
  • 기존 필드 삭제
backward-example
text
v1: [orderId, amount, currency]
v2: [orderId, amount, currency, discount(default=0)]  -- 기본값 있는 필드 추가: 호환
v2로 v1 데이터를 읽으면 discount에 기본값 0 적용

FORWARD 호환성

이전 스키마로 새 데이터를 읽을 수 있음을 보장합니다. 프로듀서가 먼저 업그레이드되는 시나리오에 적합합니다.

허용되는 변경:

  • 기존 필드 삭제
  • 기본값이 있는 필드 추가
forward-example
text
v1: [orderId, amount, currency]
v2: [orderId, amount]  -- currency 필드 삭제
v1 스키마로 v2 데이터를 읽으면 currency가 없어도 기본값으로 처리

FULL 호환성

BACKWARD + FORWARD. 양방향 호환성을 모두 보장합니다. 가장 안전하지만 변경 범위가 제한됩니다.

허용되는 변경:

  • 기본값이 있는 필드 추가
  • 기본값이 있는 필드 삭제

NONE

호환성 검사를 하지 않습니다. 어떤 변경이든 허용되지만, 프로듀서와 컨슈머 사이의 호환성이 보장되지 않습니다.

호환성 설정

compatibility-config.sh
bash
# 글로벌 호환성 수준 설정
curl -X PUT http://schema-registry:8081/config \
  -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  -d '{"compatibility": "BACKWARD"}'
 
# 서브젝트별 호환성 수준 설정 (글로벌 설정 오버라이드)
curl -X PUT http://schema-registry:8081/config/orders-value \
  -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  -d '{"compatibility": "FULL"}'
Warning

호환성 수준을 NONE으로 설정하면 스키마 레지스트리의 핵심 가치를 무력화합니다. 개발 환경에서 빠른 반복을 위해 일시적으로만 사용하고, 프로덕션에서는 최소 BACKWARD를 유지하세요.


데이터 계약 (Data Contract)

데이터 계약이란

스키마는 데이터의 구조(필드 이름, 타입)만 정의합니다. 그러나 실제 운영에서는 구조 외에도 많은 것이 중요합니다.

  • 이 필드의 값 범위는?
  • 이 토픽의 SLA(서비스 수준 합의)는?
  • 데이터 소유자는 누구인가?
  • 이 스키마를 변경하려면 누구의 승인이 필요한가?

**Data Contract(데이터 계약)**은 프로듀서와 컨슈머 사이의 포괄적인 합의입니다. 스키마를 포함하되, 데이터 품질 규칙, SLA, 소유권, 변경 프로세스까지 아우릅니다.

데이터 계약의 구성 요소

data-contract-example.yaml
yaml
# 데이터 계약 예시
contract:
  name: "orders-stream"
  version: "2.1.0"
  owner: "checkout-team"
  description: "결제 완료된 주문 이벤트 스트림"
 
schema:
  type: "avro"
  subject: "orders-value"
  compatibility: "FULL"
 
quality:
  rules:
    - field: "amount"
      rule: "amount > 0"
      severity: "error"
    - field: "currency"
      rule: "currency IN ('KRW', 'USD', 'EUR', 'JPY')"
      severity: "error"
    - field: "userId"
      rule: "userId IS NOT NULL AND length(userId) > 0"
      severity: "error"
 
sla:
  availability: "99.9%"
  freshness: "< 30 seconds"
  throughput: "> 1000 events/second"
 
governance:
  change_process: "PR to schema repo, review by data-platform team"
  breaking_change_notice: "14 days"
  deprecation_notice: "90 days"

스키마 검증 파이프라인

데이터 계약을 자동으로 검증하는 파이프라인을 구축할 수 있습니다.

  1. 프로듀서 팀이 스키마 변경을 Pull Request로 제출합니다.
  2. CI 파이프라인이 Schema Registry에 대해 호환성을 자동 검증합니다.
  3. 검증 통과 시 리뷰 후 머지하고 Schema Registry에 등록합니다.
  4. 검증 실패 시 PR이 거부되어 비호환 변경이 프로덕션에 도달하지 못합니다.
Info

데이터 계약은 조직 규모가 커질수록 중요해집니다. 소규모 팀에서는 비공식적 소통으로 충분하지만, 수십 개 팀이 수백 개 토픽을 공유하는 환경에서는 명시적 계약 없이는 관리가 불가능합니다.


실전 적용: Avro + Schema Registry

프로듀서와 컨슈머에서 Schema Registry를 실제로 사용하는 코드를 살펴보겠습니다.

프로듀서

AvroProducer.java
java
Properties props = new Properties();
props.put("bootstrap.servers", "kafka:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
props.put("schema.registry.url", "http://schema-registry:8081");
props.put("auto.register.schemas", "false"); // 프로덕션에서는 자동 등록 비활성화
 
KafkaProducer<String, GenericRecord> producer = new KafkaProducer<>(props);
 
// Avro 레코드 생성
Schema schema = schemaRegistry.getLatestSchema("orders-value");
GenericRecord record = new GenericData.Record(schema);
record.put("orderId", "O-001");
record.put("amount", 50000.0);
record.put("currency", "KRW");
 
producer.send(new ProducerRecord<>("orders", "O-001", record));

컨슈머

AvroConsumer.java
java
Properties props = new Properties();
props.put("bootstrap.servers", "kafka:9092");
props.put("group.id", "order-processor");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "io.confluent.kafka.serializers.KafkaAvroDeserializer");
props.put("schema.registry.url", "http://schema-registry:8081");
props.put("specific.avro.reader", "false"); // GenericRecord 사용
 
KafkaConsumer<String, GenericRecord> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("orders"));
 
while (true) {
    ConsumerRecords<String, GenericRecord> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, GenericRecord> record : records) {
        GenericRecord order = record.value();
        String orderId = order.get("orderId").toString();
        double amount = (double) order.get("amount");
        // discount 필드가 없는 v1 메시지도 안전하게 처리
        Object discount = order.get("discount"); // null 가능
    }
}
Warning

프로덕션에서는 auto.register.schemas=false를 설정하여 프로듀서가 임의로 새 스키마를 등록하지 못하도록 해야 합니다. 스키마 등록은 CI/CD 파이프라인을 통해서만 허용하세요.


정리

이번 장에서는 스키마 레지스트리와 데이터 계약을 학습했습니다.

  • 스키마 진화: 프로듀서와 컨슈머의 독립적 배포를 위해 스키마 호환성 관리가 필수입니다.
  • Schema Registry: 스키마를 중앙에서 저장, 버전 관리, 호환성 검증하는 서비스입니다.
  • 직렬화 포맷: Avro(Kafka 표준), Protobuf(성능), JSON Schema(가독성) 중 선택합니다.
  • 호환성 규칙: BACKWARD/FORWARD/FULL/NONE으로 허용 가능한 변경을 제어합니다.
  • 데이터 계약: 스키마를 넘어 품질 규칙, SLA, 거버넌스까지 포괄하는 합의입니다.

다음 장 미리보기

10장에서는 Exactly-once 보장과 신뢰성을 다룹니다. 세 가지 전달 보장 수준의 구현 원리, Kafka 트랜잭션과 Flink 체크포인트의 조합, 멱등성 설계, DLQ, 백프레셔, 장애 복구 전략까지 프로덕션 수준의 신뢰성을 확보하는 방법을 학습합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#streaming#data-engineering

관련 글

데이터

10장: Exactly-once 보장과 신뢰성

At-least-once/At-most-once/Exactly-once 비교, Kafka 트랜잭션과 Flink 체크포인트의 조합, 멱등성 설계, DLQ, 백프레셔, 장애 복구 전략까지 프로덕션 신뢰성을 학습합니다.

2026년 2월 28일·19분
데이터

8장: CDC(Change Data Capture)

CDC의 원리와 WAL 기반 변경 캡처, Debezium 아키텍처, PostgreSQL/MySQL CDC 실습, Flink CDC 3.6, 아웃박스 패턴, 이벤추얼 컨시스턴시까지 데이터 통합의 핵심을 학습합니다.

2026년 2월 24일·18분
데이터

11장: 프로덕션 모니터링과 운영

브로커/프로듀서/컨슈머 핵심 메트릭, Prometheus/Grafana 대시보드, 알림 설계, 용량 계획, 비용 최적화, 실전 아키텍처까지 실시간 파이프라인의 프로덕션 운영을 학습합니다.

2026년 3월 2일·23분
이전 글8장: CDC(Change Data Capture)
다음 글10장: Exactly-once 보장과 신뢰성

댓글

목차

약 18분 남음
  • 학습 목표
  • 스키마 진화의 필요성
    • 스키마 없는 세계의 문제
    • 스키마 관리가 해결하는 문제
  • Confluent Schema Registry
    • 아키텍처
    • 스키마 등록과 조회
  • 직렬화 포맷 비교
    • Apache Avro
    • Protocol Buffers (Protobuf)
    • JSON Schema
    • 포맷 비교
  • 호환성 규칙
    • BACKWARD 호환성
    • FORWARD 호환성
    • FULL 호환성
    • NONE
    • 호환성 설정
  • 데이터 계약 (Data Contract)
    • 데이터 계약이란
    • 데이터 계약의 구성 요소
    • 스키마 검증 파이프라인
  • 실전 적용: Avro + Schema Registry
    • 프로듀서
    • 컨슈머
  • 정리
  • 다음 장 미리보기