LabHub
배우기 러닝패스 코스

데이터 파이프라인 · 계보와 재현 · 이론

이 숫자는 어디서 왔나 — 계보와 재현

LabHub 에서 이어서 보기

한 줄 요약

산출물마다 어떤 입력(내용 해시)과 어떤 코드 판과 어떤 매개변수로 만들어졌는지를 옆에 적어 두고, 같은 입력으로 다시 돌려 바이트가 같은지로 그 기록을 증명한다.

왜 이게 필요했나

분기 보고서의 매출이 회계 쪽 숫자와 3천만 원 어긋난다. 파이프라인은 그날 정상 종료했고 로그도 깨끗하다. 질문은 하나다 — 이 숫자는 어디서 왔나.

답하려고 보면 손에 쥔 것이 별로 없다. 산출물 파일 하나, 그리고 "그날 돌았다" 는 로그 한 줄. 어떤 드롭 파일이 들어갔는지, 그 파일이 지금도 그때와 같은 내용인지, 코드가 그 뒤에 바뀌지는 않았는지, 임계값 매개변수가 그때 몇이었는지 — 어느 것도 남아 있지 않다. 그래서 대부분의 조사는 "다시 돌려 보자" 로 간다. 그런데 다시 돌린 결과가 그때와 다르면, 무엇이 달라져서 다른지를 또 모른다.

파일 이름과 수정 시각은 근거가 못 된다. 이름은 같은데 상류가 파일을 덮어쓴 경우가 흔하고, 수정 시각은 복사·이동·백업 복원만으로도 바뀐다. 반대로 내용이 바뀌었는데 시각이 그대로인 경우도 있다. 근거가 되는 것은 내용 그 자체의 해시뿐이다.

이 코스와 fde-data 가 갈리는 곳

fde-data 는 고객이 준 파일 한 덩어리를 뜯어 내는 일이다. 그 파일은 한 번 오고, 이상하면 보내는 쪽에 물어보면 된다. 이 코스는 다르다 — 매일 같은 자리에 같은 이름으로 드롭이 떨어지고, 같은 변환이 반복해서 돈다. 그래서 질문이 "이 파일을 어떻게 읽나" 가 아니라 "석 달 전 그 실행은 어떤 파일을 읽었나" 가 된다. 반복이 있는 자리에서만 계보가 문제가 되고, 반복이 있어서 재현이 가능해진다.

매니페스트에 무엇을 적는가

산출물 옆에 같은 이름의 JSON 을 하나 남긴다. 적을 것은 네 가지다.

{  "code_sha256": "9f2c...",  "params": {"min_qty": 1},  "inputs": [{"name": "orders-2026-03-01.csv", "sha256": "4a1e...", "rows": 12}],  "output": {"name": "shops.csv", "sha256": "7b30...", "rows": 4},  "run_id": "a41c9d02f7e3b118",  "created_at": "2026-03-04T02:11:00+00:00"}

이 한 장이 있으면 앞의 질문이 전부 대답 가능해진다. 입력 파일을 지금 다시 해시해 보면 그때와 같은지 알 수 있고, 코드 해시를 비교하면 그 뒤에 바뀌었는지 알 수 있고, 다시 돌려 출력 해시를 대조하면 기록이 맞는지 알 수 있다.

재현을 깨뜨리는 것들

"같은 입력이면 같은 출력" 은 저절로 되지 않는다. 조용히 깨뜨리는 것이 네 가지 있다.

첫째, 현재 시각. 산출물 안에 생성 시각을 적어 넣으면 두 번 돌린 결과는 반드시 다르다. 시각이 필요하면 산출물이 아니라 매니페스트에 적고, 재현 비교에서 제외되는 칸이라고 못 박는다.

둘째, 난수로 뽑은 식별자. 실행마다 uuid4 로 뽑은 run_id 를 매니페스트에 넣으면, 같은 입력으로 돌려도 매니페스트가 달라진다. 식별자는 내용에서 파생하면 된다 — 코드 해시와 매개변수와 출력 해시를 이어 붙여 해시하면, 같은 실행은 같은 이름을 갖고 다른 실행은 다른 이름을 갖는다.

셋째, 순서가 약속되지 않은 것들. os.listdir 의 순서는 정해져 있지 않고, 집합(set)의 순회 순서는 같은 프로그램에서도 실행마다 달라진다(파이썬은 문자열 해시에 실행마다 다른 씨앗을 쓴다). 그래서 파일 목록도 그룹 목록도 정렬해서 쓴다.

넷째, 부동소수점을 더하는 순서. (a + b) + ca + (b + c) 는 부동소수점에서 같은 값이 아니다. 줄의 순서가 흔들리면 합계의 마지막 자리가 흔들리고, 그러면 바이트가 달라진다. 금액처럼 소수 자리가 정해진 값은 정수(센트)로 바꿔 더하면 이 문제가 아예 사라진다.

자료 집합 단위와 칼럼 단위

계보에는 굵기가 있다. 자료 집합 단위는 "이 표는 저 세 파일에서 나왔다" 까지다. 만들기 쉽고, 장애가 났을 때 영향 범위를 훑는 데는 이것으로 충분하다.

칼럼 단위는 "이 표의 amount_cents 칼럼은 입력의 amountorder_idshop 에서 나왔다" 까지 내려간다. 상류가 칼럼 하나를 바꾼다고 알려 왔을 때, 우리 산출물의 어느 칸이 흔들리는지를 답할 수 있는 굵기가 이것이다.

칼럼 단위 계보는 적어 두는 것으로 끝나면 안 된다. 코드가 바뀌면 그 문서는 곧바로 낡는다. 증명하는 방법은 실험이다 — 입력 칼럼 하나를 흔들어 다시 돌리고, 어느 출력 칼럼이 바뀌는지를 본다. 바뀌면 의존이 있는 것이고, 안 바뀌면 없는 것이다. 이렇게 재면 "쓰는 줄 알았는데 안 쓰는 칼럼" 도 같이 드러난다.

현장에서 만나는 모습

실무에서 진짜 중요한 것

다음 실습에서 할 것

매일 떨어지는 주문 드롭을 집계하는 변환기 lineage.py 를 한 단계씩 키운다. 내용 해시를 내는 digest 부터 만들어 이름과 시각이 왜 근거가 못 되는지 확인하고, 집계 산출물과 매니페스트를 같이 내고, 두 번 돌려 바이트가 같은지를 증명하고, 칼럼 단위 계보를 실험으로 확인하고, 매니페스트대로 다시 만들어 대조하는 verify 와 "그 숫자가 어느 입력에서 왔는지" 를 답하는 trace 를 붙인다. 채점기는 매번 다른 상호와 금액으로 자기 드롭을 차려 여러분의 변환기를 실제로 돌리고, 입력 칼럼을 직접 흔들어 여러분이 적어 낸 칼럼 계보가 맞는지 확인한다.