When the source of truth is a Google Sheet
Author: Danish Mohammed (WMG Lab, Warner Music Group) | Source: tech.wmg.com | Published: 2026-09-04
한 줄 요약
비즈니스가 좋아하고 실제로 잘 작동하는 스프레드시트를 뺏지 않으면서 그 위에 거버넌스된 쿼리 가능한 데이터셋을 짓는 방법 — 보여준 스키마가 아니라 drift를 위해 설계하고, 방어를 인제스션 레이어에 두고, 테이블이 log인지 mirror인지 의도적으로 결정하는 것.
핵심 주장/내용
- 맥락 — Forward Deployed Engineering: WMG의 FDE 팀은 비즈니스 팀에 embed되어 무언가를 설계하기 전에 일을 shadow하고 수동 부분을 덜어낸다. 보통 문제에 붙는 첫 엔지니어라서, 일을 빠르게 돌아가게 만들되 파일럿이 비즈니스가 의존하는 시스템이 될 때도 버티도록 만드는 것이 임무다
- 문제: Content Protection 팀이 위조·침해 머치 리스팅을 RIAA에 신고한다. WMG가 RIAA에 신고하는 모든 리스팅은 누군가 공유 Google Sheet에 타이핑하는 행 하나로 삶을 시작한다 — 월별 탭 하나씩(Jan 2026, Feb 2026), 그리고 팀이 이것저것 정리하는 scratch 탭
- takedown을 제출하는 사람들에게는 좋은 인터페이스다. 빠르고, 모두가 이미 접근권이 있고, 제출을 기록하는 데 데이터 도구를 배울 필요가 없다
- 하지만 시트를 채우는 사람이 숫자를 필요로 하는 사람이 아니다. 매달 Content Protection은 몇 개를 신고했고 몇 개가 내려갔는지 보고하는데, 두 숫자 모두 리스팅이 발견된 곳과 영향받는 레이블로 쪼개져 리더십이 집행이 어디서 효과가 있는지 볼 수 있게 한다. 전부 누군가 손으로 채운 두 날짜 — 신고일과 제거일 — 에서 나온다
- 이 숫자는 화면에서 읽어 슬라이드 덱에 다시 타이핑되곤 했다. 시트는 매달 탭이 늘고 사람들이 컬럼을 추가·개명하면서 헤딩이 움직여서, 그 위에 자동화한 것은 누군가 이름을 바꾸는 첫 순간 깨진다
- 시트를 뺏고 싶지 않았다. 비즈니스가 좋아하고 잘 작동한다. 질문: 열두 명이 손으로 편집하는 스프레드시트 위에 어떻게 거버넌스된 쿼리 가능한 데이터셋을 짓는가?
- 가장 까다로운 부분은 탭이나 헤더가 아니라 — 행이 쓰인 뒤에 바뀐다는 것: 1월에 제출이 기록된다. 3월에 누군가 1월 탭을 열어 리스팅이 드디어 내려갔기 때문에 “date item removed” 컬럼을 채운다. 한 행이, 몇 주 뒤에, 제자리에서 편집된다
- 이것이 명백한 두 적재 전략을 모두 깨뜨린다. 매 실행마다 append하면 1월 제출이 매달 다시 카운트되고, overwrite하면 현재 상태는 얻지만 지난주 시트가 뭐라고 했는지 기록을 잃는다
- overwrite를 선택했다. 매 실행이 시트 전체를 읽고 테이블을 시트에 맞춰 재구축해 리포팅 테이블이 시트의 1:1 미러가 된다. 새 행이 나타나고, 편집이 stale 중복 버전 없이 제자리에 반영되고, 삭제된 행이 사라진다. 시트가 source of truth이므로 우리 테이블이 그런 척하기를 그만뒀다
- 혼란을 예상하기 — 인제스션 레이어의 세 가지 방어. 각각이 시트가 경고 없이 바뀌는 특정 방식 때문에 존재한다
- 파이프라인이 무엇이 데이터인지 결정한다 — 시트의 탭을 나열해 월 이름이 붙은 것만 유지한다.
Jan 2026은 데이터, scratch 탭은 무시. 팀이 우리에게 먼저 말하지 않고 작업 영역을 추가할 수 있다. 구현: Google Sheets API v4, Databricks secret scope에 보관된 OAuth2 refresh token으로 인증, 탭 이름에 대한 월 이름 regex - 컬럼 이름이 움직일 것이라고 가정한다 — 헤더를 안정적인
snake_case로 정규화해 “Date Reported”가 이번 달에 어떻게 대문자화되었든date_reported가 된다. 인식하지 못하는 것은 드롭하거나 적재를 깨뜨리게 두는 대신 단일 JSON 컬럼에 보관한다. 그러면 다음 달의 새 비즈니스 컬럼은 incident가 아니라 우리가 의도적으로 타입 필드로 승격시키는 대상이 된다 - 편집과 새 제출을 구분해야 한다 — 모든 행에 fingerprint를 부여한다. 행이 어디서 왔는지(spreadsheet ID, 탭 이름, 행 번호)와 무엇을 담고 있는지에 대한 SHA-256 해시. 같은 내용의 같은 행은 항상 같은 fingerprint를 내므로 쓰기 전에 중복을 제거할 수 있고, 셀 하나가 바뀌면 fingerprint가 함께 바뀌므로 1월 행이 추가가 아니라 편집되었음을 안다
- 파이프라인이 무엇이 데이터인지 결정한다 — 시트의 탭을 나열해 월 이름이 붙은 것만 유지한다.
- 두 단계 테이블 구축: raw 텍스트가 먼저 bronze 테이블에 착지하는데 모든 필드가
STRING으로, 시트에서 나온 그대로라서 이상한 값이 적재를 실패시킬 수 없다. 타이핑은 그 위의 silver 테이블에서 일어난다 — 날짜 컬럼을DATE로 캐스팅(누군가 3/14/2026을 타이핑했든 Google이 그날을 serial number로 저장했든), count를BIGINT로. 세 번째 테이블이 현재 어떤 행이 적재되어 있는지 기록한다- 각 쓰기가 단일 Delta Lake 트랜잭션의 full overwrite라서 세 테이블이 항상 서로 그리고 시트와 일치한다
- 미러링의 비용과 두 가드레일: 시트를 미러링한다는 것은 시트의 실수 삭제가 다음 실행에서 테이블의 실수 삭제가 된다는 뜻이다. 알고 그 트레이드오프를 받아들이되 두 가드레일을 뒀다
- 최소 한 행이라도 읽혔을 때만 테이블을 overwrite한다 — 그래서 비어 있거나 실패한 API 응답이 데이터를 날릴 수 없다. 실제로 우리를 겁먹게 한 실패 모드가 그것이었다
- 각 overwrite가 단일 원자적 Delta 트랜잭션이라 실행 중에 쿼리하는 사람이 반쯤 적재된 테이블을 결코 보지 않는다
- 나머지는 Delta time travel — 행이 사라졌는데 그러면 안 됐다면 이전 버전이 여전히 있다. 비즈니스가 나중에 point-in-time 복구가 아니라 진짜 감사 추적을 원하면 업그레이드 경로는 sync 아래의 soft-delete 레이어(
is_active,removed_at플래그)와 현재 상태를 view로 노출하는 것. 미러링이 요청받은 것이었으므로 만들지 않았다
- 하나의 shape, 여러 소스: 이미 공유 클라우드 드라이브에 대해 돌리던 인제스션 파이프라인과 똑같이 보이도록 만들었다 — 같은 bronze·silver·tracking 테이블, 같은 명명 규칙, 같은 secret 처리 패턴. 다른 소스, 같은 shape
- 이것은 정돈 그 자체를 위한 것이 아니다. 팀이 지원하는 리포팅은 각자 자기 API·파일 포맷·오너를 가진 여섯 개 남짓 소스에서 온다. 모두가 맞춤 아키텍처를 얻으면 파이프라인 여섯 개를 유지해야 한다. 하나의 shape를 공유하면 하나의 패턴과 하나의 운영 플레이북을 유지하고, on-call인 사람이 한 번도 본 적 없는 파이프라인을 읽을 수 있다
주요 수치 / 사실
- 올해 7개 월 탭이 필터를 통과, 총 약 1,600행 — 한산한 달은 20행 미만, 바쁜 달은 900행 가까이. 그 정도 크기의 시트는 한 패스에 읽히므로 노트북이 하루 한 번 스케줄된 Databricks 잡으로 매번 모든 탭을 다시 읽는다
- 핵심 원칙 — “Design for drift”: 소스가 사람들이 일하는 스프레드시트라면 보여준 스키마를 위해 설계하지 마라. 탭이 나타나고, 헤더가 움직이고, 행이 쓰인 지 몇 달 뒤 편집될 것이라고 가정하라. 방어를 인제스션 레이어에 두어라 — 탭을 패턴으로 필터하고, 모르는 컬럼을 드롭하지 말고 보관하고, 행을 해시해 편집과 삽입을 구분하고, 당신의 테이블이 log인지 mirror인지 의도적으로 결정하라
- 결과: 덱은 은퇴했고, 리더십은 이 테이블 위에 만든 대시보드를 읽는다. 비즈니스는 이제 takedown 활동을 나머지 데이터와 나란히 쿼리할 수 있다
관련 위키
- Schema Evolution
- Data Pipeline Fundamentals
- Medallion Architecture
- Idempotency
- Catalog-Managed Tables
- Data Quality and Validation
- Data Governance
- Change Data Capture
Source: 원문 보기