테마 선택

📚 지식 노트

AI·하드웨어·주변기기·게임·컴퓨터 시스템·이론 컴퓨터 과학·수학·개발과 운영·보안·응용 분야에 대해 알아 두면 좋은 내용을 정리한 노트다. 위에서 분야를 고르고 아래에서 노트를 고른다.

데이터 엔지니어링

흩어진 원천 데이터를 모아 믿고 쓸 수 있는 표로 만드는 일을 다룬다. ETL과 ELT, 웨어하우스와 레이크하우스, Parquet와 테이블 포맷의 속, DAG 오케스트레이션, 차원 모델링, 데이터 품질과 계약, 배치·스트리밍·CDC를 거쳐, 그 표를 읽는 쪽에 필요한 통계 기초까지 숫자로 계산해 가며 정리한다.

목차 (10)
  1. ETL과 ELT
  2. 웨어하우스, 레이크, 레이크하우스
  3. 열 지향 파일: Parquet
  4. 열린 테이블 포맷: Iceberg, Delta Lake, Hudi
  5. 오케스트레이션: DAG와 재시도
  6. 데이터 모델링: 스타 스키마와 SCD
  7. 데이터 품질과 데이터 계약
  8. 배치, 스트리밍, CDC
  9. 분석가를 위한 통계 기초
  10. 핵심 자료

스트림 처리의 시간 의미와 정확히 한 번 처리는 시스템 설계 › 배치와 스트림 처리에, 로그·지표를 모으는 설계 사례는 시스템 설계 › 데이터 사례에, 열 지향 저장과 분석 질의 엔진은 데이터베이스 › 분석과 검색에 있다. 이 노트는 그 부품을 엮어 분석용 데이터를 만들고 지키는 쪽을 다룬다.

ETL과 ELT

데이터 파이프라인의 뼈대는 세 단계다. 원천(서비스 DB, 외부 API, 로그)에서 추출(Extract)하고, 쓸 모양으로 변환(Transform)하고, 분석 저장소에 적재(Load)한다. 순서를 어떻게 두느냐로 두 방식이 갈린다.

ETL은 원천에서 추출해 변환 서버에서 정제한 뒤 웨어하우스에 싣고, ELT는 원본을 웨어하우스에 먼저 싣고 그 안에서 SQL로 원본 표를 모델 표로 바꾼다ETL원천 DB·API추출변환 서버정제·조인·집계적재웨어하우스정리된 표만 남음ELT원천 DB·API추출·적재웨어하우스 · 레이크하우스원본(raw)SQL모델(mart)ELT는 원본이 남아 변환을 고친 뒤 다시 만들 수 있다. 대신 계산 비용이 웨어하우스로 간다.
ETL은 변환을 마친 결과만 싣고, ELT는 원본을 먼저 싣고 저장소 안에서 SQL로 변환한다.
  • ETL: 저장소가 비싸고 계산이 약하던 시절의 방식이다. 별도 서버에서 정제·조인한 뒤 결과만 싣는다. 개인 정보처럼 저장소에 들어가면 안 되는 값을 미리 걸러 낼 때는 지금도 쓴다.
  • ELT: 클라우드 웨어하우스가 저장과 계산을 따로 늘릴 수 있게 되면서 기본이 됐다. 원본(raw)을 그대로 싣고, 변환은 저장소 안에서 SQL로 한다. dbt 같은 도구가 이 SQL 변환을 버전 관리·테스트·의존 순서와 함께 다룬다.
  • 층 나누기: 흔히 원본 → 정제(staging) → 모델(mart) 순으로 층을 둔다. 원본 층은 손대지 않고 남겨 두기 때문에, 변환에 버그가 있으면 고친 뒤 처음부터 다시 만들 수 있다. 레이크하우스에서는 같은 생각을 메달리온 구조라 부르며 브론즈(원본)·실버(검증)·골드(보강·집계) 층으로 이름 붙인다(Azure Databricks 문서). 층을 지날수록 구조와 품질이 나아진다는 뜻이다.

웨어하우스, 레이크, 레이크하우스

데이터 웨어하우스는 분석 질의에 맞춘 데이터베이스다. 들어올 때 스키마를 강제하고(schema-on-write), 저장 형식은 제품 안에 감춰져 있다. 데이터 레이크는 객체 저장소에 파일을 그대로 쌓는 곳이다. 싸고 무엇이든 넣을 수 있지만, 트랜잭션이 없어 쓰는 도중의 파일을 읽거나 같은 데이터를 두 번 넣는 일이 생기고, 관리하지 않으면 아무도 믿지 못하는 ‘데이터 늪’이 된다.

웨어하우스·레이크·레이크하우스 비교: 데이터 웨어하우스는 저장과 계산이 한 제품, 제품 고유의 열 지향 형식, 쓸 때 스키마(on-write), BI 보고서·SQL 집계; 데이터 레이크는 객체 저장소(S3 등), 열린 파일(Parquet·JSON), 읽을 때 스키마(on-read), 원본 보관·ML 학습; 레이크하우스는 객체 저장소 + 테이블 포맷, Parquet + 메타데이터, 트랜잭션·스키마 진화, 한 사본을 SQL·ML이 공유저장형식스키마주 용도데이터 웨어하우스저장과 계산이 한 제품제품 고유의 열 지향 형식쓸 때 스키마(on-write)BI 보고서·SQL 집계데이터 레이크객체 저장소(S3 등)열린 파일(Parquet·JSON)읽을 때 스키마(on-read)원본 보관·ML 학습레이크하우스객체 저장소 + 테이블 포맷Parquet + 메타데이터트랜잭션·스키마 진화한 사본을 SQL·ML이 공유
세 저장 방식. 레이크하우스는 레이크의 열린 파일 위에 트랜잭션 메타데이터 층을 얹어 웨어하우스의 성질을 되찾는다.

Armbrust 등(CIDR 2021)이 이름 붙인 레이크하우스는 둘을 합친다. 데이터는 객체 저장소의 Parquet 파일로 두고, 그 위의 테이블 포맷이 “지금 이 표는 어떤 파일들인가”를 원자적으로 관리한다. 파일 형식이 열려 있으니 Spark·Trino·DuckDB·웨어하우스 엔진이 같은 사본을 함께 읽고, ML 학습도 복사 없이 같은 파일을 쓴다.

열 지향 파일: Parquet

분석 질의는 행은 많이, 열은 몇 개만 읽는다. 그래서 같은 열의 값을 붙여 저장하는 열 지향 형식이 유리하다. Parquet는 Google Dremel의 중첩 열 저장 방식을 바탕으로 한 열린 형식이다. 파일은 행을 묶은 행 그룹(흔히 수십~수백 MB)으로 나뉘고, 행 그룹 안에서 열마다 열 청크가 이어지며, 열 청크는 다시 페이지로 나뉜다. 파일 끝의 푸터에 스키마와 행 그룹·열마다의 min/max 같은 통계가 있다.

Parquet 파일은 행 그룹마다 열 청크가 이어지고 끝의 푸터에 스키마와 통계가 있다. date가 15에서 18 사이인 행을 찾을 때 행 그룹 1만 읽고, 그 안에서도 date와 amount 열만 읽는다SELECT sum(amount) WHERE date BETWEEN 15 AND 18행 그룹 0datecountryamountdate 1–10통계만 보고 건너뜀행 그룹 1datecountryamountdate 11–20읽음행 그룹 2datecountryamountdate 21–30통계만 보고 건너뜀푸터: 스키마 · 행 그룹·열별 min/max · 위치가장 먼저 읽는다열 청크 안은 다시 페이지(보통 수십 KB~1MB)로 나뉘고, 페이지마다 인코딩과 압축이 걸린다.
읽기는 푸터부터 한다. 행 그룹의 min/max가 조건과 겹치지 않으면 통째로 건너뛰고(술어 푸시다운), 읽는 행 그룹에서도 질의에 나온 열만 읽는다(프로젝션).

그림의 질의는 행 그룹 세 개 중 1개, 열 세 개 중 두 개만 읽는다. 이 건너뛰기는 데이터가 조건 열로 정렬·군집돼 있을 때 효과가 크다. 날짜가 뒤섞여 있으면 모든 행 그룹의 min/max가 넓어져 하나도 건너뛰지 못한다.

열 하나에는 비슷한 값이 몰려 있어 인코딩이 잘 먹힌다. 국가 코드 열 1,000,000행(값 다섯 종류)으로 계산해 보자.

  • PLAIN: 문자열마다 길이 4바이트 + 글자 2바이트라 6.0MB다.
  • 사전 인코딩: 서로 다른 값 다섯 개를 사전 페이지에 한 번만 적고, 각 행에는 사전 번호만 둔다. 다섯 개를 구별하는 데 3비트면 되니 번호를 비트 단위로 빽빽하게 채우면(bit-packing) 약 375KB다. 사전이 너무 커지면 작성기는 PLAIN으로 되돌아간다.
  • RLE: 같은 번호가 이어지면 “번호 × 반복 횟수”로 적는다. 국가별로 정렬해 두면 구간이 다섯 개뿐이라 번호 부분이 50바이트 남짓으로 준다.
국가 코드 열 100만 행의 크기: PLAIN 6.0MB, 사전 + 비트 패킹 375KB, 사전 + RLE 50BPLAIN값마다 길이 4B + 글자 2B6.0MB사전 + 비트 패킹번호 하나에 3비트375KB사전 + RLE정렬해 같은 값이 이어짐50BKR·US·JP·DE·FR 다섯 값, 압축 코덱 전 크기. 막대는 로그 눈금이다(한 칸이 10배).
같은 열을 세 방식으로 저장한 크기(Snappy·ZSTD 같은 압축 코덱을 걸기 전). 정렬 하나로 사전 인코딩 결과가 다시 수천 배 줄어든다.

인코딩 뒤에는 페이지 단위로 Snappy·ZSTD 같은 범용 압축을 한 번 더 건다. 숫자 열에는 이웃한 값의 차이만 적는 DELTA_BINARY_PACKED가 쓰인다. 같은 데이터라도 정렬 순서와 행 그룹 크기에 따라 파일 크기와 질의 속도가 몇 배씩 달라지는 이유가 여기 있다. 작은 파일이 수만 개 쌓이면 푸터를 여는 비용이 커지므로, 주기적으로 파일을 합치는 컴팩션도 필요하다.

열린 테이블 포맷: Iceberg, Delta Lake, Hudi

객체 저장소에는 “파일 여러 개를 한꺼번에 바꾸기”가 없다. 테이블 포맷은 데이터 파일을 고쳐 쓰지 않고, 표를 이루는 파일 목록을 담은 메타데이터를 새로 쓴 뒤 “현재 버전” 포인터 하나만 원자적으로 바꿔 이 문제를 푼다. 커밋 하나가 스냅숏 하나다. 독자는 시작할 때 본 스냅숏만 읽으므로 쓰는 도중의 파일을 보지 않는다.

스냅숏 세 개가 차례로 커밋된다. 시각 25로 시간 여행하면 스냅숏 2를 읽고, 현재는 스냅숏 3다. 현재만 남기고 만료하면 a.parquet을 지울 수 있다스냅숏 1t=10 appenda.parquetAS OF 25스냅숏 2t=20 appenda.parquetb.parquet현재 포인터스냅숏 3t=30 overwriteb.parquetc.parquet데이터 파일은 고쳐 쓰지 않는다. 커밋은 새 파일을 더하고 파일 목록만 바꾼다.스냅숏 3만 남기고 만료하면 a.parquet은 아무도 가리키지 않아 지울 수 있다.
스냅숏은 그 시점의 파일 목록이다. 덮어쓰기도 새 파일을 쓰고 목록을 바꿀 뿐이라, 옛 스냅숏을 읽는 시간 여행이 가능하다. 오래된 스냅숏을 만료해야 옛 파일을 지울 수 있다.
  • 시간 여행: 옛 스냅숏을 지정해 읽는다. Iceberg와 Delta Lake 모두 Spark SQL에서 TIMESTAMP AS OF·VERSION AS OF를 지원한다. 어제 보고서를 그때 데이터로 다시 돌리거나, 잘못된 적재를 이전 스냅숏으로 되돌릴 때 쓴다.
  • 만료와 정리: 스냅숏을 무한히 두면 옛 파일도 무한히 남는다. 보존 기간을 정해 스냅숏을 만료하고, 어떤 스냅숏도 가리키지 않는 파일을 지운다(Iceberg의 expire_snapshots, Delta의 VACUUM). 지운 시점 이전으로는 시간 여행이 안 된다.
  • 스키마·파티션 진화: 열을 이름이 아니라 ID로 추적해 열 이름을 바꾸거나 순서를 바꿔도 옛 파일을 다시 쓰지 않는다. Iceberg는 파티션 규칙도 바꿀 수 있고, 질의는 파티션 열을 몰라도 된다(숨은 파티셔닝).

세 포맷은 출발점이 다르다. Iceberg는 Netflix에서 시작해 메타데이터 파일 → 매니페스트 목록 → 매니페스트의 나무 구조로 큰 표의 계획을 빠르게 하며, 명세 v3에서 삭제 벡터와 반정형 variant 타입이 더해졌다. Delta Lake는 Databricks에서 시작해 _delta_log 폴더에 커밋마다 JSON 로그를 쌓고 주기적으로 체크포인트를 남긴다. Hudi는 Uber에서 잦은 갱신(upsert)과 증분 읽기를 위해 시작했고, 쓸 때 다시 쓰는 copy-on-write와 변경 로그를 따로 쌓았다가 읽을 때 합치는 merge-on-read를 고를 수 있다.

오케스트레이션: DAG와 재시도

파이프라인은 “주문 추출과 사용자 추출이 끝나면 조인하고, 그다음 적재하고, 그다음 보고서를 만든다” 같은 의존 관계로 이뤄진다. 이를 방향 있고 순환 없는 그래프, DAG로 적고 스케줄러가 순서대로 돌린다. Apache Airflow가 대표적이며 2025년에 3.0이 나왔다.

DAG 단계 그림: extract_orders와 extract_users가 transform으로, transform이 load와 report로 이어진다. extract_users가 한 번 실패하고 재시도로 성공한 뒤 나머지가 차례로 돈다extract_orders대기extract_users대기transform대기load대기report대기extract_orders실행 중 · 시도 1extract_users실행 중 · 시도 1transform대기load대기report대기extract_orders성공 · 시도 1extract_users재시도 대기 · 시도 1transform대기load대기report대기extract_orders성공 · 시도 1extract_users실행 중 · 시도 2transform대기load대기report대기extract_orders성공 · 시도 1extract_users성공 · 시도 2transform대기load대기report대기extract_orders성공 · 시도 1extract_users성공 · 시도 2transform실행 중 · 시도 1load대기report대기extract_orders성공 · 시도 1extract_users성공 · 시도 2transform성공 · 시도 1load대기report대기extract_orders성공 · 시도 1extract_users성공 · 시도 2transform성공 · 시도 1load실행 중 · 시도 1report대기extract_orders성공 · 시도 1extract_users성공 · 시도 2transform성공 · 시도 1load성공 · 시도 1report대기extract_orders성공 · 시도 1extract_users성공 · 시도 2transform성공 · 시도 1load성공 · 시도 1report실행 중 · 시도 1extract_orders성공 · 시도 1extract_users성공 · 시도 2transform성공 · 시도 1load성공 · 시도 1report성공 · 시도 1extract_orders성공 · 시도 1extract_users성공 · 시도 2transform성공 · 시도 1load성공 · 시도 1report성공 · 시도 1틱 하나 = 태스크 한 번 실행 시간. 실제 스케줄러는 retry_delay만큼 기다린 뒤 다시 큐에 넣는다.
1 / 12

DAG 실행이 만들어졌다. 아직 아무 태스크도 돌지 않았다.

DAG 실행 한 번. 태스크는 위쪽(upstream)이 모두 성공해야 돈다. extract_users는 첫 시도에서 실패하도록 두었고, retries=2라 재시도 대기(up_for_retry)를 거쳐 다시 돈다. 서로 기다리지 않는 두 추출 태스크는 같은 틱에 함께 돈다.
  • 태스크 상태: Airflow의 태스크 인스턴스는 실행 중(running)·성공(success)·재시도 대기(up_for_retry)·실패(failed)·위쪽 실패(upstream_failed) 같은 상태를 지난다. 네트워크 끊김 같은 일시적 실패는 retries와 retry_delay로 흡수한다.
  • 멱등성: 재시도는 같은 태스크를 다시 돌린다는 뜻이다. “어제 데이터를 덧붙인다”는 두 번 돌면 중복이 생기고, “어제 파티션을 통째로 덮어쓴다”는 몇 번 돌아도 결과가 같다. 태스크는 덮어쓰기나 MERGE처럼 다시 돌려도 안전하게 짠다.
  • 논리 날짜와 백필: 각 실행은 “어느 기간의 데이터를 처리하는가”를 나타내는 논리 날짜를 받는다. 지난 석 달을 새 로직으로 다시 만들 때(백필) 같은 DAG를 날짜만 바꿔 돌릴 수 있으려면, 태스크가 현재 시각이 아니라 이 날짜를 기준으로 동작해야 한다.
  • 자산 중심: Dagster는 “어떤 작업을 돌리나”보다 “어떤 표(자산)가 무엇에서 만들어지나”를 먼저 적게 한다. 자산 그래프에서 실행 순서가 나오고, 어떤 표가 오래됐는지를 바로 볼 수 있다.

데이터 모델링: 스타 스키마와 SCD

분석용 표는 서비스 DB처럼 정규화하지 않는다. Kimball의 차원 모델링은 일어난 일을 숫자로 적는 사실 표와, 그 일을 설명하는 차원 표로 나눈다. 사실 표 가운데 차원 표가 둘러싼 모양이라 스타 스키마라 부른다.

스타 스키마: 가운데 fact_sales 표가 dim_date, dim_product, dim_store, dim_customer 네 차원 표를 키로 가리킨다fact_salesdate_keyproduct_keystore_keycustomer_keyquantityamountdim_datedate_key연·월·요일·휴일dim_productproduct_key이름·분류·브랜드dim_storestore_key지역·매장 형태dim_customercustomer_key등급·도시(SCD)사실 표는 길고 좁고(키 + 숫자), 차원 표는 짧고 넓다. 흐린 열이 차원 키다.
스타 스키마. 질의는 사실 표를 필요한 차원과 한 단계만 조인한 뒤 측정값을 더한다. 차원을 다시 정규화해 쪼개면 눈송이(snowflake) 스키마가 된다.
  • 그레인부터: 사실 표 한 행이 무엇인지(주문 한 건? 주문의 상품 한 줄? 하루 매장 합계?)를 가장 먼저 정한다. 그레인이 섞이면 합계가 두 번 세어진다.
  • 적합 차원: 날짜·고객 같은 차원을 여러 사실 표가 같은 정의로 함께 쓰면, 매출과 반품을 같은 축으로 나란히 볼 수 있다.
  • 넓은 표 하나: 열 지향 엔진은 조인보다 넓은 표 스캔이 싼 경우가 많아, 차원을 미리 붙인 큰 표(One Big Table)를 따로 만들기도 한다. 다만 원천 모델은 스타 스키마로 두고 그 결과로 만든다.

차원 값은 천천히 바뀐다. 고객이 이사하면 지난 매출은 어느 도시로 셀까? SCD(slowly changing dimension) 유형이 그 답이다. 1형은 값을 덮어써 역사를 버리고, 2형은 행을 새로 더해 역사를 남긴다.

SCD 2형 변경 뒤 고객 차원 표: 키 1 C7 부산 2024-01-01부터 2026-03-01까지, 키 2 C9 대전 2025-05-10부터 현재까지, 키 3 C7 서울 2026-03-01부터 현재까지변경: C7의 도시 부산 → 서울 (2026-03-01)keycustomer_idcityvalid_fromvalid_tois_current1C7부산2024-01-012026-03-01false2C9대전2025-05-10—true3C7서울2026-03-01—true옛 주문은 그때의 key 1(부산)을, 새 주문은 key 3(서울)을 가리킨다.
SCD 2형. 고객 ID(자연 키)는 그대로지만 행마다 새 대리 키(key)를 받고, 유효 기간과 현재 여부 열로 어느 시점의 값인지 가린다.

데이터 품질과 데이터 계약

파이프라인 오류는 대개 조용하다. 작업은 성공했는데 표가 비어 있거나, 원천 팀이 열 이름을 바꿔 값이 모두 null이 되거나, 단위가 원에서 센트로 바뀐다. 그래서 데이터 자체를 검사한다. 흔히 보는 축은 신선도(마지막 갱신이 언제인가), 양(행 수가 평소 범위인가), 스키마(열과 타입이 그대로인가), 값(null·중복·허용 값·범위·참조 무결성)이다.

주문 배치 8행에 계약 규칙을 돌린 결과: order_id 필수 위반 1행, status ∈ {paid, refunded, pending} 위반 1행, amount ≥ 0 위반 1행orders 계약 v2order_id: string, 필수status: paid|refunded|pendingamount: number, ≥ 0소유자: 주문팀 · 매일 06시들어온 배치 8행 검사order_id 필수위반 1행status ∈ {paid, refunded, pending}위반 1행amount ≥ 0위반 1행규칙 3개 위반 → 적재 보류, 격리 표로계약은 만드는 쪽과 쓰는 쪽이 함께 정한 약속이다. 검사는 파이프라인 입구에서 자동으로 돈다.
들어온 배치에 계약의 규칙을 돌린 결과. 위반이 하나라도 있으면 본 표에 싣지 않고 격리 표로 보낸 뒤 알린다. 대문자 PAID처럼 사람 눈에는 같아 보이는 값도 걸린다.
  • 도구: dbt 데이터 테스트는 unique, not_null, accepted_values, relationships를 SQL 모델 옆에 적는다. Great Expectations는 기대값 묶음을 코드로 정의해 검증 보고서를 낸다.
  • 쓰고 검사하고 공개(write-audit-publish): 새 데이터를 숨은 곳(스테이징 표, 테이블 포맷의 브랜치)에 먼저 쓰고, 검사가 통과하면 그때 공개한다. 잘못된 데이터가 대시보드에 잠깐이라도 보이는 일을 막는다.
  • 데이터 계약: 품질 검사가 쓰는 쪽에서 문제를 잡는다면, 계약은 만드는 쪽과 미리 약속한다. 스키마·의미·허용 값·갱신 주기·소유자를 문서로 적고, 원천 팀이 바꾸려면 버전을 올리고 알린다. Open Data Contract Standard는 이 문서를 YAML로 적는 공개 형식이다.
  • 리니지: 대시보드 숫자가 이상할 때 가장 먼저 묻는 것은 "이 표는 어느 원천에서 어떤 작업을 거쳐 왔나"다. 표와 작업 사이의 이 계보를 데이터 리니지라 한다. 거꾸로 따라가면 원인을 찾고, 앞으로 따라가면 원천 열 하나를 바꿀 때 영향받는 표와 대시보드를 미리 안다. 오케스트레이터·엔진이 실행할 때마다 입력과 출력을 이벤트로 내보내게 하는 열린 명세가 OpenLineage다.

배치, 스트리밍, CDC

처리 주기는 데이터가 얼마나 늦게 보여도 되는지로 정한다. 일정 간격으로 도는 배치에서 이벤트가 아무 때나 들어온다고 보면, 평균 지연은 주기의 절반 + 실행 시간, 최악은 주기 + 실행 시간이다. 매일 한 번 한 시간짜리 배치면 평균 13시간, 최악 25시간이고, 15분마다 3분짜리 마이크로 배치면 평균 10.5분, 최악 18분이다.

처리 주기별 데이터 지연: 매일 배치 평균 13시간, 최악 25시간, 매시간 배치 평균 40분, 최악 70분, 15분 마이크로 배치 평균 10.5분, 최악 18분, 스트리밍 평균 3초, 최악 3초평균 지연최악 지연(주기 + 실행 시간)매일 배치13시간 / 25시간매시간 배치40분 / 70분15분 마이크로 배치10.5분 / 18분스트리밍3초 / 3초막대는 로그 눈금이다. 이벤트가 주기 아무 때나 도착한다고 보면 평균은 주기의 절반 + 실행 시간이다.
처리 주기별 지연. 주기를 줄일수록 신선해지지만 실행 횟수와 작은 파일이 늘고, 스트리밍은 상태 관리와 늦게 온 이벤트 처리를 떠안는다.

보고서 대부분은 매일·매시간 배치로 충분하다. 사기 탐지나 실시간 재고처럼 초 단위가 필요할 때만 스트리밍을 고른다. 윈도, 워터마크, 정확히 한 번 처리 같은 스트림 처리의 속은 배치와 스트림 처리 노트에서 다룬다.

원천 DB에서 데이터를 가져오는 가장 쉬운 방법은 “updated_at이 지난번 이후인 행”을 주기적으로 질의하는 것이지만, 삭제된 행과 두 질의 사이의 중간 변경을 놓치고 원천 DB에 부하를 준다. CDC(change data capture)는 DB가 복제를 위해 이미 쓰는 트랜잭션 로그(PostgreSQL WAL, MySQL binlog)를 읽어 모든 변경을 순서대로 이벤트로 만든다. Debezium이 널리 쓰는 오픈 소스 커넥터다.

CDC 흐름: 원천 DB → CDC 커넥터 → 이벤트 토픽 → 레이크하우스 표. 이벤트는 생성·변경·삭제를 순서대로 담는다원천 DBWAL · binlogCDC 커넥터Debezium 등이벤트 토픽Kafka 등레이크하우스 표MERGE로 반영토픽에 쌓이는 변경 이벤트(순서대로)cid=7 생성 {status: paid}uid=7 변경 paid → refundeddid=9 삭제쿼리로 긁어 오는 배치와 달리삭제와 중간 변경도 놓치지 않고,원천 DB에 부하를 거의 주지 않는다.
CDC 흐름. 변경 이벤트에는 생성(c)·변경(u)·삭제(d)와 변경 전후 값이 담기고, 싱크는 기본 키로 MERGE해 레이크하우스 표를 원천과 맞춘다.

분석가를 위한 통계 기초

잘 만든 표도 잘못 읽으면 소용없다. 먼저 평균과 중앙값이다. 값 열 개 2, 3, 3, 4, 4, 4, 5, 5, 6, 40의 평균은 7.6이지만 중앙값은 4이다. 이상값 하나가 평균을 끌어올렸다. 응답 시간·매출·체류 시간처럼 꼬리가 긴 분포에서는 평균보다 중앙값과 백분위수가 ‘보통’을 더 잘 말한다.

값 2, 3, 3, 4, 4, 4, 5, 5, 6, 40의 점 그림. 평균은 7.6, 중앙값은 4, 표준편차는 11.4이다010203040중앙값 4평균 7.6이상값표본 분산 130.9, 표준편차 11.4. 이상값 하나가 표준편차까지 크게 부풀린다.
평균은 모든 값의 무게 중심이라 이상값 하나에 크게 움직이고, 중앙값은 순서의 가운데라 거의 움직이지 않는다.
  • 분산과 표준편차: 평균에서 얼마나 퍼졌나를 잰다. 표본 분산은 편차 제곱의 합을 n이 아니라 n − 1로 나눈다(표본 평균을 이미 데이터로 맞춘 만큼 자유도가 하나 준다). 위 값들의 표본 표준편차는 11.4로, 이상값을 빼면 1.2이다.
  • 표본: 전체를 다 볼 수 없으면 일부를 뽑아 추정한다. 무작위로 뽑아야 하고, “앱을 연 사람만”처럼 뽑는 방식이 결과와 얽히면 표본이 아무리 커도 치우친다(선택 편향). 집단 비율이 크게 다르면 층마다 따로 뽑는 층화 추출이 오차를 줄인다.
  • 신뢰 구간: 표본 평균은 뽑을 때마다 흔들린다. 그 흔들림의 크기가 표준 오차 s / √n이고, 95% 신뢰 구간은 평균 ± 1.96 × 표준 오차다. 400명의 평균 체류가 52초, 표준편차가 30초라면 구간은 52 ± 2.9초다. 오차를 절반으로 줄이려면 표본이 네 배 필요하다(1,600명이면 ± 1.5초). 비율도 같다. 1만 명 중 500명이 전환했다면 5.0% ± 0.43%다.

A/B 테스트는 사용자를 무작위로 둘로 나눠 한쪽에만 변경을 보여 주고 차이를 잰다. 관찰된 차이가 우연으로 생길 만한 크기인지를 두 비율 z 검정으로 따진다. 두 그룹의 합동 비율로 표준 오차를 구하고, 차이를 그것으로 나눈 z에서 p값(차이가 없는데도 이만큼 이상 벌어질 확률)을 얻는다.

전환율 A 5.0%, B 5.7%의 95% 신뢰 구간. 그룹당 10000명이면 p값 0.028, 그룹당 1000명이면 p값 0.4873.0%4.0%5.0%6.0%7.0%8.0%그룹당 10,000명p = 0.028유의함(5% 기준)AB그룹당 1,000명p = 0.487차이를 말할 수 없음AB점은 관측 전환율, 막대는 95% 신뢰 구간. 같은 0.7%p 차이도 표본이 작으면 구간이 겹쳐 버린다.
같은 전환율 차이(5.0% 대 5.7%)를 표본 크기만 바꿔 잰 결과. 그룹당 1만 명이면 구간이 좁아 차이가 드러나고, 1천 명이면 구간이 크게 겹쳐 아무것도 말할 수 없다.
  • 계산: 그룹당 1만 명에서 5.0% 대 5.7%(상대 상승 14%)이면 z = 2.20, p = 0.028로 5% 기준에서 유의하다. 같은 비율을 1천 명씩으로 재면 z = 0.70, p = 0.49라 차이가 있다고 말할 수 없다.
  • 표본 크기는 미리: 기준 전환율 5%에서 10% 상대 상승(5.5%)을 유의 수준 5%, 검정력 80%로 잡아내려면 그룹당 약 31,231명이 필요하다. 작은 효과일수록 필요한 표본은 효과 크기의 제곱에 반비례해 늘어난다.
  • 중간에 엿보지 않기: 매일 p값을 보다가 0.05 아래로 내려간 날 멈추면, 실제 효과가 없어도 ‘유의’를 얻을 확률이 5%를 훨씬 넘는다. 정한 표본에 도달할 때까지 기다리거나, 순차 검정처럼 중간 확인을 전제한 방법을 쓴다.
  • 그 밖의 함정: 지표를 스무 개 보면 하나쯤은 우연히 유의하다(다중 비교). 두 그룹 인원이 설계한 비율(50:50)에서 크게 어긋나면 배정 자체가 고장 난 것이니 결과를 믿지 않는다(표본 비율 불일치). 이런 함정은 Kohavi 등의 책에 사례와 함께 정리돼 있다.
  • 상관과 인과, 심슨의 역설: 무작위 배정이 없는 관찰 데이터에서 두 지표가 함께 움직여도 원인이라고 말할 수 없다. 둘 다를 움직이는 제3의 요인이 있을 수 있기 때문이다. 극단적인 경우가 심슨의 역설이다. 집단마다 나눠 보면 모두 한 방향인 차이가, 합쳐 보면 반대 방향으로 뒤집힌다. 집단마다 크기와 기준값이 다르기 때문이다. 잘 알려진 예가 1973년 가을 UC 버클리 대학원 입학 자료로, 전체로는 남성 합격률이 뚜렷이 높았지만, 학과별로 보정하면 오히려 여성 쪽으로 작은 차이가 나타났다. 여성이 합격률이 낮은 경쟁 학과에 더 많이 지원했기 때문이다. 합계를 보기 전에 집단별로 한 번 나눠 본다. A/B 테스트가 무작위 배정을 고집하는 이유도 이 제3의 요인을 양쪽에 고르게 나누려는 것이다.

핵심 자료

요지는 직접 정리한 한 줄이다.

자료종류한 줄 요지
Apache Parquet 파일 형식 문서공식 문서행 그룹·열 청크·페이지와 푸터 메타데이터, 인코딩 목록을 정의한다.
Dremel (Melnik 등)논문중첩 레코드를 열로 쪼개 저장하는 방법을 보였다. Parquet의 바탕이다.
Lakehouse (Armbrust 등, CIDR 2021)논문열린 파일 위에 트랜잭션 메타데이터 층을 얹어 웨어하우스와 레이크를 합치자고 제안했다.
Apache Iceberg 테이블 명세명세스냅숏·매니페스트·메타데이터 파일의 구조와 버전별(v1~v3) 기능을 정한다.
Airflow 핵심 개념: DAG·태스크공식 문서DAG 정의, 태스크 인스턴스 상태, 재시도와 의존 규칙을 설명한다.
Kimball 차원 모델링 기법해설그레인, 사실·차원 표, 적합 차원, SCD 유형을 짧은 항목으로 정리했다.
Designing Data-Intensive Applications (Kleppmann)책저장 엔진, 인코딩, 배치·스트림 처리를 한 흐름으로 설명한다.
OpenIntro Statistics무료 교재표본, 신뢰 구간, 비율 검정을 예제로 풀어 주는 공개 통계 교과서다.
Trustworthy Online Controlled Experiments (Kohavi 등)책온라인 A/B 테스트를 설계·분석할 때의 함정(중간 확인, 표본 비율 불일치)을 다룬다.