💡 Key Takeaways
- When Nested Arrays Cost a Fintech $1.2M in Reporting Errors
- Building Pipelines That Process 2 Billion Events Monthly
- How Shopify's Webhook Nearly Destroyed a Client's Inventory System
- Comparing JSON-to-CSV Libraries: What Actually Breaks in Production
# 데이터를 망칠 12가지 JSON에서 CSV로의 엣지 케이스
💡 주요 내용
- 중첩 배열이 핀테크 업체에 보고서 오류로 120만 달러의 손해를 끼쳤을 때
- 매월 20억 개 이벤트를 처리하는 파이프라인 구축하기
- Shopify의 웹후크가 고객의 재고 시스템을 거의 파괴한 과정
- JSON에서 CSV 라이브러리 비교: 실제 운영에서 실패하는 것들
중첩 배열이 핀테크 업체에 보고서 오류로 120만 달러의 손해를 끼쳤을 때
지난 3월, 핀테크 고객의 CFO로부터 오후 11시에 당황한 슬랙 메시지를 받았습니다. 그들의 분기 이사회 보고서는 수익 수치가 내부 대시보드와 일치하지 않는다고 보여주었습니다. 불일치? 120만 달러. 6주 동안 그들의 자동 JSON에서 CSV로의 변환이 중첩 거래 배열을 조용히 망가뜨리고 있었고, 아무도 이를 알아차리지 못했습니다. 이사회 자료는 이미 투자자들의 손에 있었습니다.
근본 원인은 기만적으로 단순했습니다: 그들의 지불 처리업체 웹후크가 거래 데이터를 중첩 JSON 배열로 전달했습니다. 하나의 거래는 여러 항목을 포함할 수 있으며, 각 항목은 고유한 세금 계산을 가지고 있습니다. 그들의 ETL 파이프라인이 이를 CSV로 평면화할 때, 각 자식 항목에 대해 부모 레코드를 중복 생성했지만 거래 총액을 중복 제거하지 않았습니다. 모든 다중 항목 거래가 여러 번 계산되었습니다.
이것은 주니어 엔지니어의 실수가 아니었습니다. 이는 50,000개 이상의 GitHub 별점을 보유한 인기 오픈 소스 라이브러리를 사용하는 고위 팀이었습니다. 이 라이브러리는 단순한 JSON 구조에 대해서는 완벽하게 작동했습니다. 그러나 실제 세계의 SaaS 데이터는 결코 단순하지 않습니다. 그것은 중첩되어 있고, 일관성이 없으며, 운영에서만 드러나는 엣지 케이스로 가득 차 있습니다. 보통은 최악의 순간에 말이죠.
저는 8년 동안 초기 단계 스타트업에서부터 매월 수십억 개의 이벤트를 처리하는 상장 회사에 이르기까지 SaaS 회사를 위한 ETL 파이프라인을 구축해 왔습니다. 저는 새벽 3시에 데이터 손상을 디버깅하고, 몇 달 동안 조용히 실패한 파이프라인을 다시 구축하며, JSON에서 CSV로의 변환이 대부분의 데이터 무결성 문제의 숨겨진 장소라는 것을 배웠습니다. 데이터베이스에서가 아닙니다. API에서도 아닙니다. 모든 사람이 "그냥 작동한다"고 생각하는 그 사소해 보이는 변환 단계에서 입니다.
매월 20억 개 이벤트를 처리하는 파이프라인 구축하기
저는 고부하 SaaS 플랫폼을 위한 데이터 엔지니어링 배경을 가지고 있습니다. 저는 하루에 5억 개 이상의 이벤트를 처리하는 마케팅 자동화 도구를 위한 수집 파이프라인, 1억 명 이상의 사용자로부터 클릭스트림 데이터를 처리하는 분석 플랫폼, 그리고 단일 손상된 기록이 규제 위반을 초래할 수 있는 금융 시스템을 구축했습니다.
제가 반복적으로 보는 패턴: 팀은 데이터베이스 규모를 확장하고 API를 최적화하는 데 집중하지만, 데이터 변환을 후순위로 취급합니다. 그들은 50ms를 절약하는 Postgres 쿼리 최적화에 몇 주를 할애한 다음, 0.01%의 레코드를 조용히 손상시키는 순진한 JSON에서 CSV로 전환기를 사용합니다. 그 0.01%는 시간이 지남에 따라 누적되어, 이사회에 당신의 메트릭이 현실과 일치하지 않는 이유를 설명하게 만듭니다.
JSON에서 CSV로의 변환은 대부분의 데이터 파이프라인의 중대한 교차점에 위치합니다. 이곳에서 구조화된 계층 데이터가 분석 도구, 스프레드시트 및 레거시 시스템을 위해 표 형식으로 평면화됩니다. 이 변환은 본질적으로 손실이 발생합니다. CSV는 중첩 구조를 표현할 수 없지만, 대부분의 구현은 이 손실을 잘 처리하지 못합니다. 그들은 데이터 평면화를 위한 방법에 대해 암묵적인 결정을 내리며, 그 결정을 문서화하거나 결과를 검증하지 않습니다.
도구들은 도움이 되지 않습니다. 대부분의 JSON에서 CSV로의 라이브러리는 단순한 사용 사례를 위해 설계되었습니다: 일관된 스키마를 가진 평면 객체. 현실 세계의 API 응답에 선택적 필드, 중첩 배열, 다형적 타입 및 일관성이 없는 구조를 제공할 때, 그들은 무너집니다. 그리고 그들은 조용히 무너집니다. 오류도 경고도 없으며, 누군가 재무 보고서를 실행할 때까지 괜찮아 보이는 미세하게 손상된 데이터로 남게 됩니다.
저는 JSON에서 CSV로의 변환을 데이터베이스 마이그레이션 또는 API 계약과 동일한 엄격함을 요구하는 중요한 시스템 구성 요소로 취급하게 되었습니다. 이는 포괄적인 테스트, 엣지 케이스의 명시적 처리 및 모든 단계에서의 검증을 의미합니다. 이게 실제로 어떻게 보이는지 살펴보겠습니다.
Shopify의 웹후크가 고객의 재고 시스템을 거의 파괴한 과정
3년 전, 저는 여러 플랫폼에서 데이터를 집계하는 전자상거래 분석 회사와 작업했습니다. 그들은 Shopify, Stripe 및 기타 서비스에서 웹후크를 수집하고, CSV로 변환하고, 데이터 웨어하우스에 로드하여 상인에게 보고서를 생성하는 한눈에 보기 간단해 보이는 파이프라인을 가지고 있었습니다.
어느 월요일 아침, 그들의 지원 팀은 티켓으로 넘쳐났습니다. 상인들은 불가능한 재고 수치를 보고했습니다—음수 재고 수치, 품절된 제품이 "재고 있음"으로 표시되고, 주문 수량이 실제 판매와 일치하지 않았습니다. 분석 플랫폼의 데이터는 완전히 잘못되었으며, 아무도 알아차리지 못한 채로 3일이 지났습니다.
범인은 Shopify의 변형 구조였습니다. Shopify의 API에서, 제품은 여러 변형(사이즈, 색상 등)을 가질 수 있으며, 각 변형은 고유한 재고 수치를 가지고 있습니다. JSON 구조는 다음과 같습니다:
```json
{
"product_id": "12345",
🛠 저희 도구를 확인해 보세요
"title": "티셔츠",
"variants": [
{"id": "v1", "size": "S", "inventory": 10},
{"id": "v2", "size": "M", "inventory": 15},
{"id": "v3", "size": "L", "inventory": 8}
]
}
```
그들의 CSV 변환기는 변형마다 하나의 행을 생성하여 이를 평면화했습니다. 이는 합리적으로 보입니다. 하지만 엣지 케이스는 이렇습니다: 변형이 품절되고 Shopify가 변형 배열에서 이를 제거했을 때, 변환기는 해당 행을 생성하지 않았습니다. 다운스트림 시스템은 "누락된 행"을 "변화 없음"으로 해석하는 대신 "현재 재고가 0"으로 해석했습니다. 제품은 실제로 품절되었지만 재고가 있는 것으로 표시되었습니다.
수정하려면 명시적 처리 가 필요했습니다: 변형을 평면화할 때, 그들은 모든 알려진 변형 ID의 참조 목록을 유지하고 JSON에서 사라진 변형에 대해 재고가 0인 행을 명시적으로 작성해야 했습니다. 이는 "단순한" 변환을 과거 데이터를 추적하는 상태 저장 작업으로 변경했습니다.
가장 위험한 데이터 버그는 그럴싸 보이는 출력을 생성하는 것입니다. 변환이 중단되거나 명백히 잘못된 데이터를 생산했다면 그들은 즉시 이를 발견했을 것입니다. 대신, 이는 완전히 정상으로 보이는 CSV 파일을 생성했습니다—단지 중요한 정보가 누락된 상태였습니다.
이 패턴은 제가 구축한 모든 SaaS 통합에서 반복됩니다. 엣지 케이스는 이국적인 시나리오가 아닙니다—API가 데이터를 표현하는 방법에서의 정상적인 변형입니다. 하지만 대부분의 변환 도구는 이를 이국적으로 다루며, 이로 인해 조용히 실패합니다.
JSON에서 CSV 라이브러리 비교: 실제 운영에서 실패하는 것들
저는 Node.js, Python 및 Go에서 모든 주요 JSON에서 CSV로의 라이브러리를 테스트했습니다. 현실 세계의 SaaS 데이터를 입력할 때 어떤 것이 손상되는지 다음과 같습니다:
| 라이브러리 | 중첩 배열 | 누락된 필드 | 유형 불일치 | Null 처리 | 운영 준비 완료 |
|---|---|---|---|---|---|
| json2csv (Node) | 부모 중복 | 빈 문자열 | 문자열로 강제 변환 | 빈 문자열 | ⚠️ 구성 필요 |
| pandas (Python) | 실패하거나 잘림 | NaN | 유형 보존 | NaN 또는 빈 | ⚠️ 정규화 필요 |
| csvkit (Python) | 문자열로 변환 | 빈 문자열 | 문자열로 강제 변환 | 빈 문자열 | ❌ 과도한 손실 발생 |
| encoding/csv (Go) | 수동 처리 필요 | 수동 처리 필요 | 수동 처리 필요 | 수동 처리 필요 | ✅ 완전한 제어 가능 |
| 커스텀 솔루션 | 명시적 전략 | 명시적 전략 | 명시적 전략 | 명시적 전략 | ✅ 추천됨 |
"운영 준비 완료" 열은 제가 이 라이브러리를 재무 데이터와 함께 신뢰할 수 있는지 여부를 반영합니다. 대부분의 라이브러리는 단순한 경우에 대해서는 잘 작동하지만, 엣지 케이스에 대해 암묵적인 결정을 내리며, 이는 귀하의 요구 사항과 일치하지 않을 수 있습니다.
핵심 통찰: 중첩된 JSON을 CSV로 평면화하는 "정확한" 방법은 없습니다. 그것은 귀하의 사용 사례에 따라 다릅니다. 데이터를 아카이브용으로 보존하고 있습니까? 분석을 위해 데이터를 준비하고 있습니까? 최종 사용자를 위한 보고서를 생성하고 있습니까? 각 사용 사례는 중첩 구조, 누락된 필드 및 유형 불일치에 대한 다른 처리를 요구합니다.
핀테크 고객을 위해 제가...