Karrot 데이터 매핑: Column-Level Lineage 구축 방법

발행: (2025년 12월 5일 오후 12:25 GMT+9)
9 min read

Source: 당근마켓 Tech Blog

문제: 데이터 흐름을 볼 수 없을 때

당근마켓에서는 다양한 팀이 BigQuery에서 파생 테이블을 생성합니다. 데이터 엔지니어, 분석가, 제품 매니저 등 많은 사람들이 데이터를 필요한 형태로 변환합니다.

하지만 우리는 큰 문제에 직면했습니다. 다음을 이해하기가 매우 어려웠습니다.

  • 어떤 테이블이 다른 테이블에 영향을 미치는가?
  • 이 테이블이 궁극적으로 의존하고 있는 상위 소스는 무엇인가?

데이터 전체 흐름이 보이지 않다 보니 작업을 이어가거나 문제를 해결하는 과정이 고통스러워졌습니다.

사례 1: 연쇄적인 실패의 악몽

하나의 테이블 파이프라인이 실패했지만, 그 테이블이 여러 다른 테이블에서 참조되고 있어 연쇄적인 실패가 발생했습니다. 영향을 받은 테이블을 빠르게 파악할 방법이 없었기 때문에 우리는 쿼리를 하나씩 수작업으로 조사했습니다. 폭발 반경을 이해하고 문제를 해결하는 데 몇 시간이 걸렸습니다.

사례 2: MySQL 스키마 변경의 파급 효과

MySQL에서 단일 컬럼을 삭제하려고 할 때, “이 컬럼을 삭제하면 어떤 테이블과 컬럼이 깨질까?” 라는 질문에 신뢰할 수 있는 답을 찾을 방법이 없었습니다. 영향을 판단하는 데 많은 시간이 소요되었습니다.

이러한 경험을 통해 우리는 데이터 흐름을 신뢰성 있게 추적하고 관리할 수 있는 시스템이 필요하다는 것을 깨달았습니다. 그 시스템이 바로 데이터 라인리지였습니다.

왜 컬럼‑레벨 라인리지를 선택했는가?

라인리지를 구축하기로 결정한 뒤, 다음 질문이 나왔습니다: “얼마나 세밀하게 추적해야 할까?” 테이블‑레벨 라인리지만 추적할 것인가, 아니면 컬럼‑레벨까지 갈 것인가?

테이블‑레벨 라인리지의 한계

테이블‑레벨 라인리지는 비교적 쉽게 구축할 수 있습니다. BigQuery는 참조된 테이블과 대상 테이블을 포함하는 JOBS 뷰를 제공하므로 테이블 간 의존성을 추론할 수 있습니다. 하지만 일상 업무에는 충분하지 않습니다.

  • 하위 테이블에서 어떤 특정 컬럼이 영향을 받는지 알려주지 않는다.
  • 단일 컬럼을 삭제하거나 변경했을 때의 영향을 답할 수 없다.
  • PII와 같은 민감한 필드의 전파를 숨긴다.

즉, 테이블‑레벨 라인지만으로는 너무 거친(코스) 수준입니다.

컬럼‑레벨 라인리지의 가치

컬럼 수준에서 라인리지를 추적하면 다음을 가능하게 합니다.

  1. 세밀한 영향 분석 – 예를 들어 users.email을 수정하면 즉시 user_stats.email이나 marketing.user_email과 같은 하위 컬럼이 영향을 받는 것을 보여준다.
  2. 데이터 보안 및 PII 추적 – PII 컬럼이 어디서 시작해 웨어하우스 전반에 어떻게 흐르는지 따라갈 수 있다.
  3. 빠른 근본 원인 분석 – 하위 데이터 품질 이슈를 신속히 원본 컬럼까지 추적한다.

남은 질문은: *어떻게 하면 이렇게 상세한 컬럼‑레벨 라인리지를 신뢰성 있게 추출할 수 있을까?*였습니다.

라인리지 추출 방법 평가

우리는 두 가지 기준에 초점을 맞춰 여러 접근 방식을 검토했습니다.

  • 컬럼 수준에서 정확하게 추출할 수 있는가.
  • 당근마켓 모든 팀의 쿼리를 스타일 차이와 관계없이 파싱할 수 있는가.

1) BigQuery INFORMATION_SCHEMA 기반 접근

제한 사항:

  1. 컬럼‑레벨 추적 불가 – BigQuery 내장 메타데이터는 세밀한 컬럼 의존성을 제공하지 않는다.
  2. 뷰‑레벨 라인리지 부재 – 뷰는 참조 객체로 기록되지 않으며, 기본 테이블만 나타나기 때문에 뷰 위에 구축된 파이프라인을 이해하기 어렵다.

따라서 BigQuery 메타데이터만으로는 필요한 상세 정보를 얻을 수 없었습니다.

2) OpenLineage와 같은 오픈소스 프레임워크 사용

당근마켓 팀은 Airflow, 크론 잡, 노트북, 내부 배치 시스템 등 다양한 메커니즘으로 쿼리를 실행합니다. OpenLineage를 도입하려면:

  • 모든 실행 환경에 클라이언트 라이브러리 또는 플러그인을 설치해야 한다.
  • 각 작업을 계측하여 라인리지 이벤트를 전송하도록 해야 한다.

중앙 데이터 팀 입장에서 이는 다음과 같은 문제를 야기합니다.

  • 여러 팀에 새로운 의존성을 추가하도록 요구한다.
  • 각 팀의 비즈니스 로직 및 배포 패턴에 영향을 줄 위험이 있다.
  • 새로운 파이프라인이 올바르게 계측되는지 지속적으로 검증해야 한다.

OpenLineage는 강력하지만, 통합 노력과 컬럼‑레벨 파싱 필요성 때문에 우리 상황에 최적은 아니었습니다.

우리의 선택: SQL 파싱

당근마켓의 주요 데이터 웨어하우스는 BigQuery이며, 거의 모든 DW 관련 작업이 여기서 실행됩니다. 모든 실행자를 계측하는 대신 BigQuery에 저장된 쿼리 로그를 파싱하기로 했습니다.

즉, BigQuery 작업 메타데이터에서 모든 쿼리를 추출하고 SQL 자체에서 라인리지를 뽑아내는 방식입니다. 이 접근법이 우리 환경에 가장 간단하고 견고합니다.

무엇을 파싱하나요?

당근마켓의 대부분 데이터 작업은 BigQuery SQL로 이루어집니다. BigQuery는 실행된 모든 쿼리를 INFORMATION_SCHEMA.JOBS에 저장하며, 여기에는 다음과 같은 필드가 포함됩니다.

  • query – 원시 SQL 텍스트.
  • creation_time, end_time, user_email 등.

우리는 query 필드를 SQL 파서의 입력으로 사용하여 소스 테이블, 대상 테이블, 그리고 컬럼‑레벨 매핑을 식별합니다.

-- Example placeholder for parsing logic
SELECT
  src_table,
  src_column,
  dest_table,
  dest_column
FROM
  parsed_lineage
WHERE
  job_id = @job_id;

(구현 세부 사항 및 추가 코드 스니펫은 여기서 이어지며, 코드 블록은 원본 그대로 유지됩니다.)

Back to Blog

관련 글

더 보기 »