출처와 사용 조건 기록
90분 안팎
학습 목표
제공된 교통·날씨 표본의 출처 URL·사용 조건·추출일·수집 기간을 source.json에 기록합니다.
개념
출처는 재현의 시작점입니다
같은 파일 이름이라도 언제 어디서 받은 파일인지 다르면 수치가 달라집니다. 담당자가 바뀐 뒤 “이 CSV가 어디에서 왔나요?”에 답하지 못하면 계산을 다시 실행해도 결과를 신뢰하기 어렵습니다. 출처 기록은 제공자의 이름을 장식처럼 쓰는 일이 아니라 입력의 정체와 사용 범위를 남기는 일입니다. 이 레슨에서는 교통과 날씨 각각의 기록을 source.json에 작성하고 원본 바이트의 SHA-256을 함께 보관합니다.
이번 ZIP은 작성팀이 직접 만든 교육용 합성 표본입니다. source_kind는 synthetic, publisher는 devinkr 부트캠프 작성팀으로 씁니다. source_url은 이 표본을 제공하는 /bootcamp/data/labs/data-data-contract/starter.zip 경로입니다. 실제 기관에서 내려받았다고 적지 않습니다. 실제 관측 자료가 없는 상황에서 기관명과 라이선스를 추측해 채우는 것은 출처 기록을 완성하는 방법이 아닙니다.
직접 출처와 탐색 참고를 분리합니다
source_url은 실제로 사용한 입력의 제공 위치이고 reference_url은 이후 실제 자료를 찾을 때 탐색할 위치입니다. 합성 교통 표본의 참고 위치는 서울 열린데이터광장, 합성 날씨 표본의 참고 위치는 기상자료개방포털로 둡니다. 이 포털 링크는 이번 숫자의 직접 출처나 재배포 허가의 증거가 아닙니다. 실습은 네트워크 연결이나 계정 없이 ZIP의 파일로 진행합니다.
실제 자료를 선택할 때에는 포털 첫 화면보다 데이터 상세 페이지와 받은 파일의 경로를 우선 기록합니다. 상세 페이지의 자료 이름, 제공기관, 제공 주기, 갱신 시점, 항목 설명을 확인합니다. 기록하는 URL은 API 키나 세션 토큰이 들어 있지 않은 공개 주소로 둡니다. 링크만 있으면 파일이 갱신되어 예전 결과를 재현하기 어려우므로 받은 원본과 추출일을 함께 보관합니다.
사용 조건은 “공개니까 자유”로 쓰지 않습니다. 실제 자료는 해당 자료에 표시된 조건과 이용 안내를 읽고 출처 표시, 변경 표시, 재배포 가능 범위 등 확인한 내용을 남깁니다. 확인하지 못한 항목은 확인 대기로 표시하고 외부 공개 여부를 담당자와 정합니다. 이번 표본에는 교육용 합성값이며 실제 관측이나 정책의 근거로 사용하지 않는다는 조건을 기록합니다. 실제 기관 자료의 조건을 이 문장으로 대체하지 않습니다.
시간 기록 세 가지를 구분합니다
extracted_on은 파일을 가져오거나 표본 패키지를 작성한 날짜입니다. 이번 표본은 2026-10-09로 고정합니다. period_start와 period_end는 표 안에 있는 관측 날짜의 최소와 최대이며 각각 2026-09-01과 2026-09-03입니다. 10월에 받은 파일이 9월의 관측을 담을 수 있으므로 추출일을 관측 기간으로 적으면 안 됩니다. 날짜는 해석이 명확한 YYYY-MM-DD 형식을 씁니다.
기간의 양 끝은 포함하고, 시작일이 종료일보다 늦으면 거부합니다. 종료일이 추출일 뒤에 있는 것도 이 실습의 과거 일별 관측 계약에서는 거부합니다. 예보 데이터라면 다른 규칙이 필요하지만 이번 표본은 예보가 아닙니다. 규칙은 자료의 성격에 맞춰 정하며 검사기가 다른 종류의 데이터까지 정확하게 해석한다고 가정하지 않습니다.
표에 적힌 최소·최대 날짜만으로 기간 안의 모든 날짜가 빠짐없이 있다는 사실이 증명되지는 않습니다. 9월 1일과 3일 두 행만 있어도 최소·최대는 같습니다. 지역별 날짜 목록과 누락 여부를 함께 검토해야 합니다. 이번 ZIP의 날짜는 각 지역에서 3일을 포함하지만 값의 빈칸은 따로 있습니다. 날짜 행의 존재와 측정값의 존재를 같은 것으로 세지 않습니다.
파일 이름 대신 바이트를 비교합니다
SHA-256은 파일 바이트에서 계산한 해시값입니다. 같은 원본을 사용했는지 대조할 때 유용합니다. 파일을 다른 이름으로 복사해도 바이트가 같으면 값이 같고, 스프레드시트로 다시 저장하면서 줄바꿈이나 따옴표가 달라지면 표의 뜻이 비슷해도 값이 달라집니다. 그래서 raw 폴더는 보존하고 작업 결과는 별도 파일로 저장합니다. 원본을 다시 저장한 뒤 새 해시를 써서 변경 사실을 숨기지 않습니다.
해시 일치는 파일이 옳게 측정되었다는 뜻이 아닙니다. 잘못된 숫자가 들어 있는 원본도 같은 바이트라면 같은 해시를 갖습니다. 해시는 입력 정체의 대조이고 품질은 단위·결측·중복·범위 등 별도 검사로 확인합니다. 또한 이번 검사기는 서명된 외부 원본 목록을 검증하지 않으므로 원본과 source의 해시를 함께 바꾸면 변경을 알아낼 수 없습니다. 제출 검토에서는 배포본과도 대조합니다.
따라하기에서는 제공된 파일을 바이너리로 읽고 hashlib.sha256으로 계산합니다. Python 문법을 지금 외울 필요는 없습니다. 완성된 확인 코드를 실행하고 어느 파일을 읽었는지, 출력된 두 값이 source.json의 sha256과 일치하는지를 확인합니다. 표준 라이브러리만 사용하므로 추가 패키지를 설치하지 않습니다. 더 자세한 CSV·JSON 입출력 문법은 더 읽기의 Python 장으로 연결합니다.
메타데이터를 검사하고 오류를 읽습니다
미션의 검증기는 source.json에서 traffic과 weather 기록을 각각 확인합니다. source_url, usage_conditions, extracted_on, period_start, period_end 중 하나가 빠지거나 비면 SOURCE로 시작하는 ValueError를 냅니다. PERIOD는 날짜 순서, COVERAGE는 표의 실제 최소·최대 날짜와 기록이 다른 상황, HASH는 원본 바이트와 저장한 해시가 다른 상황을 알려 줍니다. 오류 메시지의 자료 이름과 필드 이름을 보고 해당 항목부터 고칩니다.
JSONDecodeError가 나오면 source의 주장보다 파일 문법을 먼저 확인합니다. JSON은 키와 문자열에 큰따옴표를 사용하고 마지막 항목 뒤의 쉼표를 허용하지 않습니다. 한글이 깨지면 UTF-8 저장 여부를 확인합니다. 날짜 형식 오류는 월·일 순서를 추측해 바꾸지 않고 원본의 정의를 확인합니다. FileNotFoundError는 받은 ZIP의 루트에서 실행했는지와 raw 폴더가 있는지를 먼저 살펴봅니다.
작업을 마치면 교통과 날씨의 출처 기록을 나란히 읽습니다. 한 자료만 작성하고 다른 자료가 같은 제공자일 것이라고 추정하지 않습니다. 입력 교체가 필요하면 두 자료의 기록, 사전, 원본 해시, 점검 결과를 함께 새 버전으로 남깁니다. 이번 레슨의 제출물은 두 자료의 완전한 source.json과 원본 보존 여부입니다. 공개 자료로 바꾸는 단계에서는 직접 다운로드한 사실과 조건 확인의 근거를 추가합니다.
따라하기
표본 출처와 참고 포털 구분
data-data-contract 시작 ZIP의 source.json을 엽니다. traffic의 비어 있는 usage_conditions를 합성 교육 표본의 제한으로 채웁니다. source_url은 ZIP 경로, reference_url은 실제 자료 탐색 참고입니다. 두 URL 역할을 혼동하지 않습니다.
JSON 필드와 기간 순서 확인
코드를 source_check.py로 저장해 python3 source_check.py로 실행합니다. 이는 제공된 날짜 기록의 확인이며 실제 기관 수집을 수행하지 않습니다.
import json
from datetime import date
text = '{"extracted_on":"2026-10-09","period_start":"2026-09-01","period_end":"2026-09-03"}'
record = json.loads(text)
print('기간 순서:', date.fromisoformat(record['period_start']) <= date.fromisoformat(record['period_end']) <= date.fromisoformat(record['extracted_on']))실행 결과
기간 순서: True
줄바꿈 변경과 해시 비교
hash_check.py로 저장해 python3 hash_check.py로 실행합니다. 표의 의미가 비슷해도 원본 바이트 변경을 알아낼 수 있습니다.
import hashlib
a = b'region,date,vehicle_count\nA,2026-09-01,100\n'
b = a.replace(b'\n', b'\r\n')
print('동일 바이트:', hashlib.sha256(a).hexdigest() == hashlib.sha256(a).hexdigest())
print('줄바꿈 변경:', hashlib.sha256(a).hexdigest() == hashlib.sha256(b).hexdigest())실행 결과
동일 바이트: True 줄바꿈 변경: False
배포 원본의 SHA-256 기록 대조
ZIP 루트에서 아래 명령을 실행합니다. 두 값이 source.json의 sha256과 일치하는지 확인합니다. raw를 저장하거나 수정하지 않습니다. 날짜와 조건은 두 자료 각각 읽습니다.
python3 - <<'PY'
from pathlib import Path
import hashlib
for name in ['traffic', 'weather']:
print(name, hashlib.sha256(Path('raw', name + '.csv').read_bytes()).hexdigest())
PY실행 결과
traffic 56289b29c8f7271971ba8914fcd7c762b59ed61980340250735803709c2ce690 weather 0f8c94a0fce88e08e1f08eca860e69c6b3fd5641ad0fb0747db7beaabd1f90d6
확인 문제
실습
미션 ZIP의 source.json에 traffic/weather 각각의 제작자·표본 URL·참고 URL·사용 조건·추출일·관측 시작/끝·SHA-256을 기록합니다. 합성값을 실제 기관 관측으로 표시하지 않습니다. 원본 파일 두 개를 보존하고 해시를 대조합니다. 실제 자료 교체 시에는 자료 상세 페이지와 해당 조건 확인 근거를 별도로 제출합니다.
더 읽기
면접 질문
- 공개 데이터로 만든 그래프의 신뢰성을 확인하는 방법을 설명해 주시면 됩니다.