合格を言えない検証器は検証器ではない
一言でいうと
検証ツールの価値は、何を検出できるかではなく、正常なファイルに対して正常と言えるかで決まります。
なぜ必要なのか
クレンジングスクリプトは、一度書いたら捨てます。検証ツールは毎週動きます。この違いが設計を変えます。
顧客企業にデータ連携を組み込んだあとは、翌月から毎週、同じ形式のファイルが届きます。そのうちのある週には、上流システムが変わって、列が1つ増えたり、日付の形式が変わったり、エンコーディングが変わったりします。その変化を人が目で発見するのは、たいてい集計の数字がおかしいという連絡が入ったあとです。
検証ツールは、その時間差をなくすためのものです。
どう動くのか
使える検証ツールは、4種類のルールを持ちます。
スキーマルール: 列の数と名前、必須の値の有無、型です。「フィールドが5つで、customerは空であってはならず、amountは整数でなければならない」。
重複ルール: キーが一意かどうかです。ここで注意するのは、前のモジュールで見た落とし穴です。キーに欠損がありうるなら、欠損の行は別に数えます。
範囲ルール: 値が常識的な区間にあるかどうかです。amountが0、または負、または9,900万ウォンの行は、形式上は有効な整数ですが、業務上はほぼ確実に間違ったデータです。範囲ルールのない検証ツールは、型さえ合えばすべて通してしまい、小数点の位置が1つずれた値をそのまま流してしまいます。
参照ルール: 他のデータとの整合性です。注文の顧客番号が、顧客テーブルに実際にあるかどうかです。
そして、この4つより重要な性質が1つあります。正常なファイルに対して、必ず合格と言わなければなりません。
現場での姿
検証ツールが失敗する方法は2つあり、どちらもよくあります。
過検知。ルールが厳しすぎて、正常なファイルも失敗します。すると、人は毎週「どうせまたあれだろう」と言って無視し始め、2か月後に本当の問題が来たときも同じように無視します。その時点では、検証ツールがあるほうが、ないよりも悪いです。あると信じさせておきながら、実際には何も防げないからです。
過少検知。ルールが緩くて、何も検出できません。たいてい「とりあえず落ちないように」作った検証ツールが、こうなります。
そこで、検証ツールを作ったら、必ず2つの方向で試験します。正常なファイルに実行して合格するか、壊れたファイルに実行して失敗するか。どちらか一方しか確認していない検証ツールは、半分しか作っていません。
最後に出力形式です。検証ツールは終了コードで伝えなければなりません。合格なら0、失敗なら0以外の値です。そうしてこそ、CIやバッチパイプラインにそのまま組み込めます。人が読むメッセージはその上に載せるもので、機械が読む信号が先です。
検証ツールが備えるべきもの
「間違っている」としか言わない検証ツールは、半分しかできていません。人が直せるようにするには、4つが必要です。
| 要素 | ないと |
|---|---|
| どの行・どのフィールドか | 100万行のうちどこかを探すだけで1日かかります |
| 何を期待したか | 何に直せばよいかわかりません |
| 実際の値は何か | 原本をもう一度開いて見る必要があります |
| 何件か | 1件だけの例外なのか構造的な問題なのかわかりません |
❌ 검증 실패: 잘못된 데이터가 있습니다
✅ 행 4213, 컬럼 order_date: '2026-13-45' 가 날짜 형식이 아닙니다 (기대: YYYY-MM-DD)
같은 오류 1,842건 — 원본의 8행 이후 전부. 인코딩이나 구분자를 먼저 확인하세요.
最後の文が核心です。エラーが特定の地点以降に集中しているなら、個々の値の問題ではなくパースの問題です。検証ツールがそのパターンに気づいて知らせてくれれば、調査の方向がすぐ定まります。
どこで防ぐのか
同じ検査を複数の層に置くのは無駄ではありません。それぞれが別のものを防ぎます。
1. 수집 경계 형식·필수 필드·인코딩 → 나쁜 데이터가 들어오지 못하게
2. 변환 중 비즈니스 규칙·참조 무결성 → 조용히 잘못된 결과를 만들지 못하게
3. 적재 후 행 수·합계·분포 대조 → 옮기다 잃어버린 것을 잡게
3つ目をよく忘れます。原本が12,043行なのに、ロード後に12,041行なら、2行がどこかで消えたということです。行数と金額の合計を照合する1行が、こうしたものを検出します。
-- 적재 후 대조
select
(select count(*) from staging_orders) as src,
(select count(*) from orders where load_id = :id) as dst,
(select sum(amount) from staging_orders) as src_amt,
(select sum(amount) from orders where load_id = :id) as dst_amt;
悪い行をどう扱うのか
すべて失敗させると、良いデータまで使えません。1つも防がなければ、汚染されます。実務の答えは隔離です。
읽은 행 12,043
├ 통과 11,998 → 적재
└ 격리 45 → quarantine 표 + 사유
隔離された行は理由とともに残し、隔離件数にしきい値を設けます。普段0.1%だったものが5%に跳ね上がったなら、元のデータ側で何かが変わったということなので、それ自体がアラートです。静かに捨てると、このシグナルが消えます。
次のラボですること
意図的にそれぞれ違う方法で壊したファイル3つと、正常なファイル1つを用意し、それぞれ何行がルールに違反しているかを数え、最後に、合格と失敗の両方を判定できる検証スクリプトを作ります。