Schemas — Skip Validation and You Pay Twice Later
한국어 원문으로 표시합니다.
한 줄 요약
외부에서 들어오는 데이터는 전부 문자열이라고 가정하고 시작해, 명시적으로 검증하고 변환한 뒤에만 타입 있는 표에 넣어야 한다.
왜 이게 필요했나
실무의 원본 데이터는 예외 없이 지저분하다. 같은 날짜가 네 가지 형식으로 들어오고, 금액에는 통화 기호와 천 단위 쉼표가 붙어 있으며, 상태값은 대소문자와 앞뒤 공백이 제각각이고, 필수 항목이 비어 있다.
이것을 그대로 타입 있는 표에 넣으려 하면 적재가 실패한다. 그러면 흔히 두 가지 잘못된 대응이 나온다. 실패한 행을 그냥 건너뛰거나(아무도 모르게 데이터가 사라진다), 모든 컬럼을 텍스트로 만들어 버리는 것이다(문제를 하류로 미룬다).
어떻게 동작하나
표준적인 구조는 세 층이다.
- 원본(staging) — 모든 컬럼이 문자열. 있는 그대로 보관한다. 절대 고치지 않는다.
- 정제(clean) — 검증을 통과한 행만 타입을 갖춘 채로 들어간다.
- 거부(reject) — 통과하지 못한 행과 그 이유가 들어간다.
이 구조의 핵심은 보존 법칙이다. 정제 건수와 거부 건수의 합이 원본 건수와 정확히 같아야 한다. 어느 쪽에도 없는 행이 있다면 그것은 조용히 사라진 데이터이고, 파이프라인에서 가장 위험한 사고다.
거부 사유를 남기는 것도 타협할 수 없다. 사유 없이 버린 행은 나중에 아무도 복구하지 못한다. "amount 없음", "email 없음" 처럼 짧아도 좋으니 반드시 함께 적는다.
변환 규칙도 명시적이어야 한다. 날짜 형식이 여러 개라면 각 형식을 정규식으로 판별해 서로 다른 파싱 규칙을 적용한다. 자동 추론에 맡기면 03/04/2025 가 3월 4일인지 4월 3일인지에 따라 조용히 틀린다.
금액에서 빈 문자열을 0 으로 바꾸는 것도 흔한 실수다. 값이 없는 것과 0 은 다르다. 결제 금액이 비어 있다면 그것은 0원 결제가 아니라 정보 누락이며, 0 으로 채우는 순간 그 사실이 지워진다.
현장에서 만나는 모습
스키마 계약을 코드로 남기는 것이 도움이 된다. 정제 표의 컬럼과 타입 목록을 별도 표에 기록해 두면, 나중에 누군가 컬럼 타입을 바꿨을 때 하류 파이프라인이 즉시 감지할 수 있다. 스키마 변경은 원래 조용히 일어나서 며칠 뒤에 이상한 숫자로 드러나는 부류의 사고다.
포맷 선택도 짚어 둘 만하다. CSV 는 어디서나 읽히지만 타입 정보가 없고 구분자 이스케이프가 취약하다. JSON 은 중첩을 표현하지만 부피가 크다. Parquet 같은 컬럼 지향 포맷은 타입과 통계를 함께 담고 압축률이 좋아 분석 워크로드에 유리하다. 원본 보관은 CSV 나 JSON 으로, 분석용 재적재는 컬럼 포맷으로 나누는 구성이 흔하다.
스키마 변경을 안전하게 하는 규칙
파이프라인의 스키마는 생산자와 소비자 사이의 계약 입니다. 한쪽만 바꾸면 반대쪽이 깨지므로, 어느 방향으로 호환되는지를 먼저 정합니다.
| 호환 방향 | 뜻 | 허용되는 변경 |
|---|---|---|
| 하위 호환(backward) | 새 소비자가 옛 데이터를 읽는다 | 필드 삭제, 기본값 있는 필드 추가 |
| 상위 호환(forward) | 옛 소비자가 새 데이터를 읽는다 | 필드 추가, 선택 필드 삭제 |
| 완전 호환(full) | 양쪽 다 | 기본값 있는 선택 필드 추가·삭제만 |
스트리밍에서는 하위 호환 을 기본으로 씁니다. 소비자를 먼저 올리고 생산자를 나중에 올리면 되기 때문입니다. 순서를 반대로 하면 새 데이터를 옛 소비자가 만나 깨집니다.
필수 필드를 추가하는 것은 언제나 깨는 변경입니다. 기본값을 주고 선택으로 넣은 뒤, 모든 생산자가 채우게 되면 그때 필수로 올립니다. 두 단계로 나누는 것이 정석입니다.
파일 형식이 성능을 정한다
| 형식 | 구조 | 잘 맞는 곳 | 주의 |
|---|---|---|---|
| CSV | 행 | 사람이 보는 소량 | 타입이 없다. 인코딩·구분자 지옥 |
| JSON Lines | 행 | 스키마가 유동적인 적재 | 크고 느리다 |
| Avro | 행 | 스트리밍, 이벤트 | 스키마 레지스트리와 함께 |
| Parquet | 열 | 분석 질의 | 쓰기가 무겁다. 작은 파일에 부적합 |
열 지향(Parquet)이 분석에서 빠른 이유는 필요한 열만 읽기 때문 입니다. 컬럼 50개 중 3개만 쓰는 질의가 흔한데, 행 지향은 50개를 다 읽습니다. 여기에 열 단위 압축이 잘 들어 크기도 작아집니다.
다만 Parquet 은 작은 파일이 많으면 오히려 느립니다. 파일마다 메타데이터를 읽어야 하고, 그것이 실제 데이터보다 클 수 있습니다. 128MB~1GB 를 목표로 묶습니다.
파티션은 조회 패턴을 따라간다
s3://lake/events/dt=2026-09-06/hour=14/part-0001.parquet
└─ 날짜로 자르는 질의가 대부분이면 이렇게
파티션 키를 잘못 고르면 모든 질의가 전체를 훑습니다. 반대로 너무 잘게 나누면 작은 파일 문제가 생깁니다. 하루에 파일 몇 개가 나오는지 계산해 보고 정합니다.
날짜를 dt=2026-09-06 처럼 문자열 하나로 두는 것이 year=/month=/day= 로
쪼개는 것보다 대개 편합니다. 범위 질의를 쓰기 쉽고 디렉터리 깊이도 얕습니다.
다음 실습에서 할 것
문자열로만 이루어진 staging.orders_raw 를 프로파일링해 결함을 세고, 날짜와 금액과 상태를 정규화한 뒤, 정제 표와 거부 표로 나눠 적재하고 보존 법칙이 성립하는지 확인한다.