[Datamates] 데이터 품질 진단 페이지 설계
규칙과 결과에서 진단으로
dbt 테스트를 품질 규칙으로 관리하는 화면은 «어떤 규칙이 몇 건 위반했는가»까지 답한다. 그 뒤 세 걸음 — 원인이 어디인지, 무엇이 영향을 받는지, 무엇부터 손대야 하는지 — 는 사람이 계보를 되짚어 판단해 왔다. 이 문서는 그 세 걸음을 화면 안으로 옮기는 설계다.
본문의 이름은 예시 값
모델명 stg_events·fct_events, 스키마명, 파일 경로는 설명을 위한 예시 값이다. 실제 프로젝트의 식별자는 다르다.
1.1 현재 화면이 이미 하는 일
- 규칙 = dbt 테스트 그대로 — 메타DB에 규칙을 따로 두지 않는 구조. 화면에는 있는데 실행되지 않는 규칙이 생기지 않음
- 규칙 편집의 파일 왕복 — 추가·심각도 변경·사용 중지·삭제가
schema.yml에 반영되고dbt parse로 재확인 - 집계 지표 — 품질 점수, 실패·주의 건수, 미검사 건수, 모델별 통과 수
- 위반 행 조회 —
store_failures가 켜진 규칙에 한해 실제 행 표시 - 검사 이력 — 실행 결과 적재 테이블 기반 통과율·마지막 실패 시각 조회 가능
1.2 진단으로 넘어가야 하는 이유
| 한계 | 지금 화면 | 사용자가 대신 하는 일 |
|---|---|---|
| 원인 미구분 | 상류 한 곳의 실패가 하류 다섯 곳을 함께 깨뜨려도 여섯 줄 나란히 나열 | 계보를 머릿속으로 되짚어 시작점 추정 |
| 영향 미표시 | 실패 모델의 하류 모델·파이프라인·대시보드가 화면 밖 | 모델 화면·파이프라인 화면·대시보드를 각각 열어 확인 |
| 추이 지표 불일치 | 추이 그래프가 오케스트레이터의 DAG 성공률 집계 — 테스트 통과율과 다른 값 | «어제보다 나아졌나» 판단 불가 |
| 미검사·통과 혼동 | 점수 산식은 미검사를 제외하나 목록에서는 구분 약함 | 실행 전 규칙을 «괜찮음»으로 오독 |
1.3 진단의 정의
질문 네 개를 한 화면에서 순서대로 답하는 구조.
- 증상 — 어떤 규칙이 몇 건 위반, 언제 확인
- 원인 — 계보상 어디에서 시작, 이 이슈가 원인인지 파생인지
- 영향 — 하류 모델·파이프라인·조회 전용 입력·대시보드 범위
- 조치 — 무엇부터, 어디에서 — 모델 수정·규칙 조정·재검증·재실행
1.4 설계 원칙
- dbt가 아는 것은 재계산 금지 — 규칙 정의·의존·결과·건수는 산출물 그대로 사용
- 모르는 것은 «확인 불가» — 계보 미해석·이력 부재를 빈 값이나 0으로 채우지 않음
- 규칙 저장소 이중화 금지 — 진단 화면도 규칙은 파일을 거쳐 수정. 진단용 사본 없음
- 진단 1건 = 조치 1건 — 파생 이슈는 원인 아래로 접어 큐 길이를 조치 수에 맞춤
- 계산은 서버, 화면은 표현 — 원인·영향·우선순위 산출은 응답에 포함
진단 신호와 결과 판정
품질 검증은 한 가지 검사 방식이 아니다. 성격이 다른 여섯 신호로 나뉘고, 신호마다 답하는 질문과 선언 위치가 다르다. 진단은 이 여섯을 하나의 이슈 목록으로 합치는 작업이다.
2.1 신호 6종
| 신호 | 답하는 질문 | 산출물 | 진단에서의 쓰임 |
|---|---|---|---|
데이터 테스트not_null unique relationships accepted_values |
컬럼 값이 규칙을 벗어났는가 | run_results.json · 결과 적재 테이블 |
이슈 본체. 컬럼이 지정되므로 컬럼 계보와 결합 가능 |
| 싱귤러 테스트 건수 정합 · 정정 반영 |
모델 사이의 건수·정정 결과가 어긋났는가 | 동일 | 모델 단위 정합 이슈. 컬럼이 없어 모델 단위 원인 후보로 취급 |
| 유닛 테스트 고정 입력·기대 출력 |
변환 로직이 회귀했는가 | dbt build 결과 |
데이터 문제와 로직 문제의 구분 근거. 로직 회귀는 배지로 별도 표기 |
| 실행 결과 모델 상태 · 반영 행 수 · 소요 |
적재 자체가 실패·급변했는가 | run_results.json |
원인 판정 입력. 적재 실패는 테스트 실패보다 상위 원인 |
원천 최신성dbt source freshness |
원천이 예상 시각까지 도착했는가 | sources.json |
전제 미충족 수집 시각 컬럼 지정이 선행 조건 |
| 이상 감지 건수 · 최신성 · 분포 |
규칙은 통과하나 분포가 어긋났는가 | 패키지 메트릭 테이블 | 미도입 규칙으로 잡히지 않는 건수 급감 대응 |
2.2 결과 판정 구분
검사 결과는 통과·실패 두 값이 아니다. 실패로 판정하지 않는 위반, 상류 실패로 돌지 못한 검사, 아직 돌지 않은 검사를 구분해야 실패 건수가 실제 원인 수와 맞는다.
| 결과 | 의미 | 하위 모델 | 통과율 집계 |
|---|---|---|---|
| pass | 규칙 충족 | 진행 | 분자·분모 모두 포함 |
| warn | 위반이나 실패로 판정하지 않음 — 규칙별 심각도를 주의로 지정한 경우 | 진행 | 분모만 포함 |
| fail | 위반 행 존재 | 건너뜀 | 분모만 포함 |
| error | 검사 질의 자체가 실행되지 못함 | 건너뜀 | 분모만 포함 |
| skipped | 상류 실패로 미실행 — 그 모델의 문제 아님 | 해당 없음 | 제외 |
| 결과 없음 | 아직 한 번도 돌지 않음 | 해당 없음 | 제외 — 미검사 건수로 별도 표기 |
skipped를 실패에 합산 금지. 상류 실패로 돌지 못한 검사까지 실패로 세면 진단 대상이 실제 원인 수보다 부풀어 우선순위가 뒤집힌다.
2.3 진단 전제 조건
- 위반 행 보관 — 규칙별 지정. 전체에 일괄 적용하면 테스트 수만큼 결과 테이블이 생겨 실행이 느려짐. 이슈마다 «행 조회 가능» 배지와 켜기 안내 노출
- 원천 최신성 — 수집 시각 컬럼 지정 전에는 지연 도착 판정 불가
- 이력 테이블 — 시각 컬럼이 문자열로 적재되므로 비교·정렬 전 타임스탬프 변환 필수. 변환 누락 시 문자열 비교로 조용히 틀린 이력
- 재검증 실행 — 모델을 다시 만들지 않는
dbt test단독 실행. 적재와 겹치면 카탈로그 커밋 충돌
정보 출처 — 진단이 새로 만드는 사실은 셋
원칙은 하나다. dbt가 아는 것은 다시 계산하지 않는다. 진단이 새로 만드는 사실은 원인 추정·영향 범위·이력 판정 세 개뿐이고, 나머지는 전달이다.
그림 3.1 출처 다섯 곳이 서버의 진단 계산으로 모이고, 결과가 화면 세 영역으로 갈라진다. 이력 조회만 조회 엔진을 우회해 저장소를 직접 읽는다(점선) — 엔진 경유 시 집계 하나에 십수 초가 걸려 화면에서 쓸 수 없다.
도식 설명: 다섯 출처가 서버의 진단 계산으로 모이고, 계산 결과가 화면의 세 영역으로 나뉘는 구조
3.1 화면 항목별 출처
| 화면 항목 | 출처 | 계산 주체 |
|---|---|---|
| 규칙 정의 · 심각도 · 사용 여부 | manifest | dbt |
| 최근 결과 · 위반 건수 | run_results.json |
dbt |
| 위반 행 | 실패 행 보관 테이블 | dbt |
| 통과율 추이 · 최초 발견 · 연속 실패 | 결과 적재 테이블 | 적재 패키지 |
| 실행 상태 · 소요 · 재시도 | 오케스트레이터 + run_results.json |
오케스트레이터 |
| 컬럼 계보 · 변환식 | SQL 파서 분석 | 플랫폼 |
| 적재 담당 파이프라인 · 조회 전용 입력 | 메타스토어 + 계보 | 플랫폼 |
| 영향 대시보드 | 시각화 도구 데이터셋 매핑 | 플랫폼 |
| 원인 · 영향 · 이력 판정 · 우선순위 | 위 항목의 결합 | 진단(신규) |
3.2 조회 경로를 나누는 이유
- 이력 집계 — 조회 엔진을 거치면 집계 하나에 십수 초. 저장소를 직접 읽어 같은 조회를 17초에서 0.04초로 단축
- 컬럼 계보 — 컴파일을 쓰지 않음. 로컬 엔진 기동에 15초가 들어 화면에서 사용 불가. 대신 의미가 확정된 템플릿만 치환하고 해석 불가 구간은 «확인 불가»로 표기
- 대시보드 영향 — 외부 API 왕복이라 상세 진입 후 지연 로딩. 큐 목록에서는 계산 제외
화면 구성
기존 3탭(대시보드·규칙·위반 내역)에 «진단» 탭을 추가해 첫 탭으로 승격. 대시보드 탭은 진단 헤더로 흡수하고, 규칙·위반 내역은 유지한다.
그림 4.1 좌측 큐는 «조치 단위»로, 파생 이슈는 원인 아래 접힌다(점선). 우측 상세의 다섯 블록 순서가 §1.3의 질문 순서와 일치한다. 하단 근거 탭은 상세의 주장을 뒷받침하는 원자료 전용이다.
도식 설명: 상단 지표 네 칸, 좌측 이슈 큐, 우측 진단 상세 다섯 블록, 하단 근거 탭으로 구성된 화면 배치
4.1 진단 헤더
| 지표 | 산식 | 누름 동작 |
|---|---|---|
| 진단 점수 | 통과 규칙 ÷ 결과 있는 활성 규칙 × 100 — 미검사 제외 | 통과율 추이 그래프 펼침 |
| 실패 | fail·error 활성 규칙 수 |
큐를 실패로 필터 |
| 주의 | warn 활성 규칙 수 |
큐를 주의로 필터 |
| 미검사 | 활성 규칙 − 결과 있는 규칙 | 큐를 미검사로 필터 + 재검증 버튼 강조 |
- 추이 그래프는 테스트 통과율 기준. DAG 성공률 기반 지표는 사용 중지
- 이력이 없는 날짜는 항목 생성 금지 — 0으로 채우면 «그날 전부 실패»로 읽힘
4.2 이슈 큐와 진단 상세
- 기본 정렬 — 우선순위 점수 내림차순, 동점 시 최근 발견 순
- 그룹 단위 — 원인 이슈 1행 + 파생 이슈 접힘. 파생 수를 원인 행에 표기
- 행 구성 — 심각도 색 띠 · 규칙 이름 · 대상 모델 · 판정 배지(원인/파생) · 이력 배지(신규/지속/재발) · 위반 건수 · 우선순위 점수
- 상세 5블록 — 증상 · 원인(계보 경로와 확신도) · 영향(4계층) · 이력(연속 횟수와 통과율) · 조치(모델 열기·규칙 설정·재검증·파이프라인 열기)
- 근거 4탭 — 위반 행 · 검사 정의(파일 원문과 컴파일된 SQL) · 검사 이력 · 계보
4.3 표시 규약
| 상황 | 화면 문구 | 금지 |
|---|---|---|
| 검사 이력 없음 | «아직 검사하지 않음 — 파이프라인 1회 실행 후 결과 채움» | 0점 · 통과 표기 |
| 위반 행 미저장 | «실패 행 미저장 — 보관 옵션을 켜고 재검증» | 빈 표 (위반 없음으로 오독) |
| 이력 테이블 부재 | «검사 이력 적재 전 — 최근 결과만 표시» | 추이 0으로 채우기 |
| 오케스트레이터 무응답 | 헤더에 «실행 상태 확인 불가» 배지, 이슈·이력은 정상 표시 | 화면 전체 오류 |
| 계보 미해석 | 원인 블록에 «계보 확인 불가 — 미해석 구간 포함» | 추측 원인 지목 |
| 상류 실패로 미실행 | «건너뜀» 별도 표기 + 원인 이슈로 이동 | 실패 합산 |
원인 추정과 영향 범위
이슈 키는 dbt 테스트 이름을 그대로 쓴다. 모델·컬럼·검사 유형이 이미 결합된 값이라 별도 키가 필요 없다. 이슈가 되는 것은 활성 규칙 중 실패·주의뿐이고, 미검사와 건너뜀은 이슈가 아니라 상태로 헤더에 집계한다.
그림 5.1 같은 실패 여섯 건의 두 표현. 차이는 계보 결합 하나 — 조치 대상이 6에서 2로 줄고, 파생은 원인 해소 시 함께 사라진다.
도식 설명: 같은 실패 여섯 건을 평면 나열한 현재 화면과, 계보 최상류를 원인으로 지목하고 나머지를 파생으로 접은 진단 화면의 차이
5.1 판정 순서
- 적재 실패 우선 — 같은 모델에 적재 실패가 있으면 그 모델의 테스트 이슈는 모두 파생
- 계보 상류 탐색 — 대상 컬럼의 상류 경로에서 같은 컬럼 계열의 실패 검색. 존재 시 파생, 부재 시 원인
- 모델 단위 대체 — 컬럼 없는 이슈(싱귤러·유닛)는 모델 의존 상류로 같은 판정 적용
- 확신도 부여 — 컬럼 계보 직결 높음 / 모델 의존만 연결 보통 / 미해석 구간 포함 확인 불가
«확인 불가»는 파생이 아니라 원인으로 취급
계보를 모르는 이슈를 파생으로 접으면 원인 목록에서 사라져 조치가 누락된다. 모른다는 표기와 함께 큐에 남긴다. 계보가 이어지지 않는 실패도 서로 독립 원인으로 본다.
5.2 영향 범위 4계층
| 계층 | 산출 | 표시 |
|---|---|---|
| 모델 | 대상 모델의 전체 하류. 컬럼 경로가 있으면 컬럼까지 | 모델 이름 목록 + 계보 열기 |
| 파이프라인 | 하류 모델을 적재하는 파이프라인 — 적재 소유권 기준 1:1 | 파이프라인 이름 + 다음 예약 시각 |
| 연쇄 실행 | 조회 전용 입력·데이터 이벤트로 이어진 후속 파이프라인 | «미해소 시 N개 파이프라인이 오염 데이터로 실행» 경고 |
| 대시보드 | 하류 모델을 데이터셋으로 쓰는 대시보드 | 대시보드 제목 + 열기 |
- 영향 계산은 하류 방향만 — 상류는 원인 추정이 담당
- 주의 이슈는 파이프라인이 멈추지 않으므로 «오염 데이터 전파 가능»으로 구분 표기
조치 우선순위와 이력 판정
6.1 우선순위 점수
P = S × (1 + log2(1 + 하류 모델 수)) × (1 + 위반 비율) × R
S 심각도 계수 실패 3 · 주의 1
R 이력 계수 신규 1.0 · 지속 1.3 · 재발 1.5
위반 비율 위반 행 ÷ 대상 행 — 산출 불가 시 0 (점수 부풀림 방지)
| 이슈 | S | 하류 | 위반 비율 | R | P | 순서 |
|---|---|---|---|---|---|---|
범위 · stg_events.amount |
3 | 3 | 0.11 | 1.3 | 13.0 | 1 |
중복 · dim_event_types.event_type |
3 | 2 | 0.00 | 1.0 | 7.8 | 2 |
필수값 · fct_events.event_category |
1 | 1 | 0.02 | 1.0 | 2.0 | 3 |
- 파생 이슈는 점수 계산 대상에서 제외 — 원인 행의 하류 폭에 이미 반영
- 점수는 정렬 근거일 뿐이므로 절대값 강조 금지. 행 우측에 소수 첫째 자리까지만 표기
6.2 이력 판정
그림 6.1 판정은 검사 이력의 연속 결과만으로 결정된다. 재발은 «해소 후 재실패»로만 성립하므로, 계속 깨져 있는 이슈와 구분된다.
도식 설명: 미검사에서 신규, 지속, 해소, 재발로 이어지는 이슈 이력 상태 전이
| 판정 | 기준 | 이력 계수 |
|---|---|---|
| 신규 | 직전 검사 통과 또는 이력 없음 | 1.0 |
| 지속 | 2회 이상 연속 실패 — 최초 발견일 함께 표기 | 1.3 |
| 재발 | 해소 후 재실패 — 같은 규칙의 통과 구간이 사이에 존재 | 1.5 |
| 해소 | 직전 실패 후 통과 | 큐 제외 · 이력 탭 보존 |
인터페이스와 구현 단계
7.1 엔드포인트
| 구분 | 경로 | 용도 |
|---|---|---|
| 신규 | GET /quality/diagnosis |
헤더 지표 + 이슈 요약 + 출처 상태 |
| 신규 | GET /quality/issues |
이슈 큐 — 원인·파생 묶음, 우선순위 정렬 |
| 신규 | GET /quality/issues/{id} |
상세 5블록 통합 응답 |
| 신규 | GET /quality/issues/{id}/impact |
영향 4계층 — 지연 로딩 |
| 신규 | GET /quality/issues/{id}/history |
검사 이력 · 통과율 · 연속 횟수 |
| 재사용 | GET /quality/rules 외 CRUD |
규칙 관리 — 변경 없음 |
| 재사용 | POST /quality/runs |
재검증 — dbt test --select |
| 폐기 | GET /quality/trend |
DAG 성공률 — 품질 추이로 오독 유발 |
7.2 이슈 응답
{
"id": "dbt_utils_accepted_range_stg_events_amount__10000__-1000",
"modelId": "model.analytics.stg_events",
"col": "amount",
"sev": "error", "status": "err", "cnt": 41,
"ratio": 0.11, // 위반 행 ÷ 대상 행. 산출 불가 시 null
"cause": {
"role": "cause", // cause | derived
"confidence": "high", // high | medium | unknown
"reason": "컬럼 계보 최상류 실패 — 상류에 실패 이슈 없음",
"path": ["source.analytics.raw.events", "model.analytics.stg_events"],
"derived": [{"id": "not_null_fct_events_amount"}]
},
"impact": {"models": 3, "pipelines": 2, "chained": 1, "dashboards": 1},
"history": {"verdict": "persisting", "streak": 3, "passRate14d": 71.4},
"priority": 13.0,
"evidence": {"rowsAvailable": false, "lineage": "ok"},
"sources": {"airflow": "ok", "history": "ok", "lineage": "ok"}
}
sources— 출처별 가용 상태. 화면 축소 표시의 근거- 부분 실패는
200+sources로 전달. 출처 하나가 죽어 화면 전체가 오류로 떨어지는 상황 차단
7.3 캐시와 목표
- 실행 스냅샷 · 진단 계산 3초 — 같은 주기로 무효화해 어긋난 두 숫자 방지
- 이력 집계 60초, 대시보드 매핑 5분. 재검증 완료 시 즉시 무효화
- 목표 — 큐 첫 화면 0.5초(이슈 50건, 대시보드 제외), 상세 진입 1초
7.4 구현 단계
| 단계 | 범위 | 완료 기준 |
|---|---|---|
| 1 — 기존 데이터 | 진단 탭 · 헤더 4지표 · 이슈 큐 · 상세 3블록 · 근거 4탭 · 표시 규약 | 고정값 그래프 제거, 미검사와 통과가 큐에서 구분 |
| 2 — 계보 결합 | 원인 추정 · 파생 접힘 · 영향 4계층 · 우선순위 점수 | 상류 1건 실패 시 큐 길이가 조치 수와 일치 |
| 3 — 신규 설정 | 원천 최신성 신호 · 이상 감지 · 이슈 유형 확장 | 지연 도착과 값 위반이 서로 다른 이슈로 분리 |
7.5 착수 전 확정 사항
| 질문 | 기본 제안 |
|---|---|
| 진단 점수 산식 | 규칙 수 기준 유지 — 카탈로그 품질 상태와 같은 값 보장 |
| 위반 행 저장 정책 | 규칙별 지정 유지 + 화면 안내 — 전역 활성은 빌드 지연 |
| 이상 감지 도입 | 3단계 도입 — 값 규칙으로 잡히지 않는 건수 급감 대응 |
| 우선순위 점수 노출 | 숫자 표기 — 정렬 근거 확인 가능, 등급은 정보 손실 |
정리
- 품질 신호는 여섯 종류이고 답하는 질문이 다르다. 어떤 신호를 쓰고 있는지 구분하지 않으면 검사하지 않은 범위가 검증된 것으로 읽힌다.
- 검사 결과는 통과·실패 두 값이 아니다.
warn·skipped·미검사를 분리해야 실패 건수가 실제 원인 수와 맞는다. - 실행 결과 파일은 매 실행 덮어쓰므로 이력 판정에 쓸 수 없다. 결과를 테이블로 적재해 두고 그쪽을 읽는다.
- dbt가 답하지 않는 것은 원인 지목·영향 범위·이력 판정·우선순위 넷이다. 산출물을 결합해 서버에서 계산하고, 화면은 그리기만 한다.
- 원인과 파생을 구분하면 실패 여섯 건이 조치 두 건으로 줄어든다. 파생은 원인 해소 후 재검증에서 함께 확인된다.
- 계보를 해석하지 못한 실패는 파생으로 접지 않고 «확인 불가» 원인으로 남긴다. 틀린 계보로 원인을 지목하면 조치 대상이 잘못 좁혀진다.
- 출처 하나가 죽어도 화면 전체가 오류로 떨어지지 않게 출처별 가용 상태를 응답에 담아 축소 표시한다.
- 1단계는 기존 데이터만으로 완결된다. 새 설정이 필요한 신호(원천 최신성·이상 감지)는 마지막 단계로 미룬다.
Leave a comment