[DATA-012][실무] 원본 표를 대시보드로 바꾸는 흐름 만들기
페이지 정보
작성자 기술팀장 작성일 26-09-06 17:36 조회 58 댓글 0본문
[이번 수업]
CSV 원천 데이터를 검사하고 채널별로 집계해 HTML 대시보드를 만듭니다. 외부 서비스 없이 검증→변환→표시→재검증 흐름을 완성합니다.
[선수지식]
DATA-002의 CSV, DATA-003의 결측치, DATA-004의 집계, DATA-007의 시각화, DATA-011의 품질을 알면 좋습니다.
[학습목표]
1. 원천 데이터의 스키마와 품질 규칙을 정한다.
2. 같은 입력에서 같은 집계 결과를 재현한다.
3. 지표 정의·출처·갱신 시각을 대시보드와 함께 관리한다.
[핵심개념]
원천 데이터는 가공 전 기록입니다. 스키마는 열 이름·자료형·허용 범위의 약속입니다. 표시 전에 필수 열과 0 이상인 주문·매출을 검사해야 잘못된 숫자의 확산을 막습니다.
파이프라인은 데이터를 단계별로 전달하는 흐름입니다. ETL은 추출·변환·적재이며 여기서는 읽기, 검증·집계, HTML 저장을 수행합니다. 같은 입력은 같은 결과를 내야 합니다. 데이터 계보는 결과와 원본·처리 규칙을 잇는 기록입니다.
지표는 계산식까지 정의합니다. 총 주문은 orders의 합, 총 매출은 revenue의 합입니다. 실제로는 취소 포함 여부·통화·시간대도 적습니다. 표에는 제목과 머리글을 넣고 색만으로 구분하지 않습니다.
[따라하기]
sales.csv를 UTF-8로 저장하세요. 개인정보가 없는 가짜 데이터입니다.
```csv
date,channel,orders,revenue
2026-09-01,web,3,36000
2026-09-01,app,2,28000
2026-09-02,web,4,46000
2026-09-02,app,1,12000
```
같은 폴더에 build_dashboard.py를 저장합니다.
```python
from collections import defaultdict
from html import escape
from pathlib import Path
import csv
with open("sales.csv", newline="", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
if reader.fieldnames != ["date", "channel", "orders", "revenue"]:
raise ValueError("열 이름이 다릅니다")
rows = list(reader)
totals = defaultdict(lambda: [0, 0])
for line, row in enumerate(rows, 2):
orders, revenue = int(row["orders"]), int(row["revenue"])
if min(orders, revenue) < 0 or not row["channel"].strip():
raise ValueError(f"{line}행 오류")
totals[row["channel"]][0] += orders
totals[row["channel"]][1] += revenue
body = "".join(f"<tr><th>{escape(k)}</th><td>{v[0]}</td><td>{v[1]:,}</td></tr>" for k, v in sorted(totals.items()))
orders = sum(v[0] for v in totals.values())
revenue = sum(v[1] for v in totals.values())
page = f"""<!doctype html><html lang='ko'><meta charset='utf-8'><title>판매 현황</title>
<h1>판매 현황</h1><p>총 주문 {orders}건 · 총 매출 {revenue:,}원</p>
<table><caption>채널별 집계</caption><tr><th>채널</th><th>주문</th><th>매출(원)</th></tr>{body}</table></html>"""
Path("dashboard.html").write_text(page, encoding="utf-8")
print(f"완료: {len(rows)}행, 주문 {orders}건, 매출 {revenue:,}원")
```
macOS·Linux는 `python3 build_dashboard.py`, Windows는 `py build_dashboard.py`를 실행합니다. 예상 결과는 `완료: 4행, 주문 10건, 매출 122,000원`입니다. dashboard.html에는 총계와 app 3건·40,000원, web 7건·82,000원이 표시됩니다.
원본 파일명·행 수·처리 시각·규칙 버전·합계를 기록하고 원본은 덮어쓰지 않습니다. 자동화에서는 검사가 통과한 뒤에만 기존 결과를 교체합니다.
[흔한 실수]
빈 값을 0으로 바꾸거나 실패 행을 조용히 버리지 마세요. 정의와 시간대가 다른 자료를 합치지 말고 합계뿐 아니라 행 수와 채널별 값도 검증합니다.
[보안 주의]
실제 이름·연락처는 넣지 않습니다. 필요한 열만 수집하고 접근권한과 보관 기간을 정하세요. HTML 문자열은 이스케이프하며 본인 소유 로컬 파일만 씁니다.
[직접 해볼 과제]
store 채널 2행과 기대 합계를 추가하세요. revenue가 -1인 시험 파일이 실패하는지 확인한 뒤 날짜별 집계와 마지막 데이터 날짜도 표시합니다.
[확인문제]
1. 화면을 만들기 전에 스키마와 값 범위를 검사해야 하는 이유는 무엇인가요?
2. 지표 이름뿐 아니라 계산식과 포함 범위를 기록해야 하는 이유는 무엇인가요?
3. 같은 원본으로 파이프라인을 다시 실행했을 때 결과가 같아야 하는 이유는 무엇인가요?
[다음 학습]
SWE-012에서 테스트가 부족한 기존 프로그램을 안전하게 개선하는 종합 프로젝트를 진행합니다.
[공식 참고 자료]
https://www.w3.org/TR/tabular-data-model/
https://www.w3.org/TR/dwbp/
https://www.w3.org/TR/WCAG22/
https://docs.python.org/3/library/csv.html
CSV 원천 데이터를 검사하고 채널별로 집계해 HTML 대시보드를 만듭니다. 외부 서비스 없이 검증→변환→표시→재검증 흐름을 완성합니다.
[선수지식]
DATA-002의 CSV, DATA-003의 결측치, DATA-004의 집계, DATA-007의 시각화, DATA-011의 품질을 알면 좋습니다.
[학습목표]
1. 원천 데이터의 스키마와 품질 규칙을 정한다.
2. 같은 입력에서 같은 집계 결과를 재현한다.
3. 지표 정의·출처·갱신 시각을 대시보드와 함께 관리한다.
[핵심개념]
원천 데이터는 가공 전 기록입니다. 스키마는 열 이름·자료형·허용 범위의 약속입니다. 표시 전에 필수 열과 0 이상인 주문·매출을 검사해야 잘못된 숫자의 확산을 막습니다.
파이프라인은 데이터를 단계별로 전달하는 흐름입니다. ETL은 추출·변환·적재이며 여기서는 읽기, 검증·집계, HTML 저장을 수행합니다. 같은 입력은 같은 결과를 내야 합니다. 데이터 계보는 결과와 원본·처리 규칙을 잇는 기록입니다.
지표는 계산식까지 정의합니다. 총 주문은 orders의 합, 총 매출은 revenue의 합입니다. 실제로는 취소 포함 여부·통화·시간대도 적습니다. 표에는 제목과 머리글을 넣고 색만으로 구분하지 않습니다.
[따라하기]
sales.csv를 UTF-8로 저장하세요. 개인정보가 없는 가짜 데이터입니다.
```csv
date,channel,orders,revenue
2026-09-01,web,3,36000
2026-09-01,app,2,28000
2026-09-02,web,4,46000
2026-09-02,app,1,12000
```
같은 폴더에 build_dashboard.py를 저장합니다.
```python
from collections import defaultdict
from html import escape
from pathlib import Path
import csv
with open("sales.csv", newline="", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
if reader.fieldnames != ["date", "channel", "orders", "revenue"]:
raise ValueError("열 이름이 다릅니다")
rows = list(reader)
totals = defaultdict(lambda: [0, 0])
for line, row in enumerate(rows, 2):
orders, revenue = int(row["orders"]), int(row["revenue"])
if min(orders, revenue) < 0 or not row["channel"].strip():
raise ValueError(f"{line}행 오류")
totals[row["channel"]][0] += orders
totals[row["channel"]][1] += revenue
body = "".join(f"<tr><th>{escape(k)}</th><td>{v[0]}</td><td>{v[1]:,}</td></tr>" for k, v in sorted(totals.items()))
orders = sum(v[0] for v in totals.values())
revenue = sum(v[1] for v in totals.values())
page = f"""<!doctype html><html lang='ko'><meta charset='utf-8'><title>판매 현황</title>
<h1>판매 현황</h1><p>총 주문 {orders}건 · 총 매출 {revenue:,}원</p>
<table><caption>채널별 집계</caption><tr><th>채널</th><th>주문</th><th>매출(원)</th></tr>{body}</table></html>"""
Path("dashboard.html").write_text(page, encoding="utf-8")
print(f"완료: {len(rows)}행, 주문 {orders}건, 매출 {revenue:,}원")
```
macOS·Linux는 `python3 build_dashboard.py`, Windows는 `py build_dashboard.py`를 실행합니다. 예상 결과는 `완료: 4행, 주문 10건, 매출 122,000원`입니다. dashboard.html에는 총계와 app 3건·40,000원, web 7건·82,000원이 표시됩니다.
원본 파일명·행 수·처리 시각·규칙 버전·합계를 기록하고 원본은 덮어쓰지 않습니다. 자동화에서는 검사가 통과한 뒤에만 기존 결과를 교체합니다.
[흔한 실수]
빈 값을 0으로 바꾸거나 실패 행을 조용히 버리지 마세요. 정의와 시간대가 다른 자료를 합치지 말고 합계뿐 아니라 행 수와 채널별 값도 검증합니다.
[보안 주의]
실제 이름·연락처는 넣지 않습니다. 필요한 열만 수집하고 접근권한과 보관 기간을 정하세요. HTML 문자열은 이스케이프하며 본인 소유 로컬 파일만 씁니다.
[직접 해볼 과제]
store 채널 2행과 기대 합계를 추가하세요. revenue가 -1인 시험 파일이 실패하는지 확인한 뒤 날짜별 집계와 마지막 데이터 날짜도 표시합니다.
[확인문제]
1. 화면을 만들기 전에 스키마와 값 범위를 검사해야 하는 이유는 무엇인가요?
2. 지표 이름뿐 아니라 계산식과 포함 범위를 기록해야 하는 이유는 무엇인가요?
3. 같은 원본으로 파이프라인을 다시 실행했을 때 결과가 같아야 하는 이유는 무엇인가요?
[다음 학습]
SWE-012에서 테스트가 부족한 기존 프로그램을 안전하게 개선하는 종합 프로젝트를 진행합니다.
[공식 참고 자료]
https://www.w3.org/TR/tabular-data-model/
https://www.w3.org/TR/dwbp/
https://www.w3.org/TR/WCAG22/
https://docs.python.org/3/library/csv.html
댓글목록 0
등록된 댓글이 없습니다.
