소방용수·소화전 같은 공공 API를 활용 신청해 페이지별 JSON을 받으면, 세 가지가 어긋나 있습니다 — 1쪽과 2쪽의 필드명이 다르고, 같은 시설이 중복으로 들어오고, 좌표가 빈 행이 섞여 있습니다. 그대로 지도·통계에 넣으면 결측과 중복이 조용히 들어갑니다. 이 안내서는 그걸 검산 가능한 CSV로 바꿉니다.
- 페이지별 API 응답을 같은 조회 조건과 표준 스키마로 통합할 수 있습니다.
- 필드 별칭을 표준 컬럼으로 바꾸고 고유 ID 기준 중복을 식별할 수 있습니다.
- 선언 총건수·다운로드 행·고유 ID 수의 차이를 검산할 수 있습니다.
- 좌표 결측과 충돌 행을 자동 확정하지 않고 사람 검토용으로 분리할 수 있습니다.
페이지로 나뉘어 온다는 것
공공 API가 왜 이렇게 손이 가는지부터 이해하면 정리가 쉬워집니다. API는 데이터를 한 번에 다 주지 않고 페이지로 나눠 줍니다. 도서관 대출 목록을 100권씩 끊어 출력하는 것과 같습니다 — 한 번에 다 뽑으면 서버도 받는 쪽도 부담이라, 1쪽·2쪽·3쪽으로 나눠 보냅니다. 그래서 전체를 얻으려면 여러 쪽을 받아 이어 붙여야 합니다.
문제는 이 쪽들이 항상 같은 모양이 아니라는 데 있습니다. 여러 사람이 나눠 적은 명부를 합치는 상황을 떠올리면 됩니다. 한 사람은 "이름·전화"라고 적고 다른 사람은 "성명·연락처"라고 적으면, 두 명부는 같은 뜻인데 열 이름이 다릅니다. 페이지별 JSON도 그렇습니다 — 1쪽은 facilityId, 2쪽은 facility_id처럼 같은 값을 다른 이름으로 담아 옵니다.
여기에 두 가지가 더 겹칩니다. 쪽을 나누는 사이에 같은 시설이 두 쪽에 걸쳐 중복으로 들어오기도 하고, 어떤 행은 좌표 칸이 비어 있기도 합니다. 그래서 그냥 이어 붙이면 결측과 중복이 조용히 섞입니다. 이런 공공 API를 어디서 찾고 인증키를 어떻게 받는지는 소방 공공데이터·API 지도에, 표기·결측·중복을 다루는 일반 원리는 지저분한 출동 데이터 깔끔하게 정제하기에 있습니다. 이 안내서는 그중 공공 API에 특화된 정리를 다룹니다.
동작 원리
정규화는 원본 JSON을 곧바로 CSV에 쓰는 일이 아니라 응답 확인 → 필드 해석 → 표준 행 생성 → 중복·결측 분리 → 개수 검산의 순서입니다. 먼저 각 페이지의 조회 조건과 totalCount를 함께 보존합니다. 페이지마다 선언값이 다르면 서로 다른 시점이나 조건의 응답이 섞였을 수 있으므로 합치기 전에 멈춥니다.
다음으로 원본 키를 FIELD_ALIASES에서 찾아 표준 키 한 개로 옮깁니다. 어떤 별칭도 찾지 못하면 빈값을 만들어 조용히 진행하지 않고 "알 수 없는 필드"로 기록합니다. 표준 행이 만들어진 뒤에야 facility_id 같은 고유 키를 비교할 수 있습니다. 같은 ID의 여러 행 중 무엇을 남길지는 좌표 존재 여부와 원본 페이지 같은 근거를 보존한 상태에서 정합니다.
마지막에는 수를 세 방향으로 봅니다. 선언 총건수는 제공자가 말한 전체, 다운로드 행 수는 실제로 받은 원시 행, 고유 ID 수는 중복을 접은 분석 단위입니다. 세 수의 차이는 곧바로 오류의 정답을 말해 주지는 않지만, 어느 단계에서 누락·중복을 조사해야 하는지 보여 줍니다. 좌표 유효 행과 검토 행도 따로 세어 지도에 들어간 범위를 설명합니다.
세 가지 함정
- 필드명 차이 — 1쪽은
facilityId(camelCase), 2쪽은facility_id(snake_case) - 페이지 중복 — 같은
WF-006이 1쪽과 2쪽에 모두 - 좌표 결측 — 어떤 행은
latitude,longitude가 빈 문자열
선택지와 트레이드오프
원본의 변동성과 결과 사용 목적에 따라 정규화 방법을 고릅니다.
| 선택지 | 맞는 상황 | 장점 | 주의할 점 |
|---|---|---|---|
| 별칭표 기반 표준화 | 같은 뜻의 키 이름만 페이지·버전별로 달라질 때 | 원본 키 추가만으로 표준 스키마를 유지 | 뜻이 다른 필드를 이름만 비슷하다고 합치면 안 됨 |
| 응답별 명시적 변환 함수 | 구조나 중첩 위치가 크게 다른 응답을 다룰 때 | 응답 형식별 예외와 근거를 코드에 분명히 남김 | 형식이 늘 때 변환 함수와 검산도 함께 관리해야 함 |
| ID 기준 중복 제거 | 안정적인 고유 식별자가 있을 때 | 분석 단위를 한 시설 한 행으로 맞춤 | ID 누락·재사용·충돌 여부를 먼저 확인해야 함 |
| 중복을 검토 파일로만 분리 | 어느 행을 남길지 자동 기준이 불충분할 때 | 원본 충돌을 숨기지 않고 사람이 비교 가능 | 자동 산출물 완성은 늦어지지만 잘못된 병합을 막음 |
가상 상황: 소화전이 지도에 두 번 찍혔다
가상의 상황을 하나 들어 보겠습니다. 관내 소화전을 지도에 표시하려고 소방용수 API를 활용 신청해 3쪽을 받았습니다. 지도에 찍어 보니 개수가 이상합니다 — 어떤 소화전은 같은 자리에 두 번 찍혔고, 어떤 소화전은 아예 지도에서 사라졌습니다.
원인을 따라가 보면 위의 세 함정 그대로입니다. 두 번 찍힌 건 같은 WF-006이 1쪽과 2쪽에 모두 들어왔기 때문이고(중복), 사라진 건 좌표 칸이 비어 지도에 얹을 수 없었기 때문입니다(결측). 게다가 1쪽과 2쪽의 필드명이 달라, 좌표를 읽는 코드가 2쪽에서는 빈손으로 돌아왔습니다(필드명 차이).
눈으로 세면 "소화전 12개"인데 지도엔 13개가 찍히거나 11개만 보입니다. 이 어긋남을 지도에 얹기 전에 잡아야 합니다. 그 방법이 아래의 필드명 맞추기와 검산입니다.
가상 사례: 데이터 담당자의 조회 조건 혼합 발견
다음은 설명을 위한 별도의 가상 사례입니다. 데이터 담당자가 기간별 시설 현황을 비교하려고 저장해 둔 여러 페이지를 합칩니다. 지도 사례와 달리 좌표 표시보다 같은 조회 묶음인지 확인하는 것이 목적입니다. 입력 폴더에는 실행 시각과 조회 조건을 기록한 파일도 있고, 기록이 빠진 파일도 섞여 있습니다.
담당자는 파일명 순서만 믿지 않고 각 응답의 조회 조건과 선언 총건수를 먼저 목록으로 만듭니다. 조건이 다른 페이지와 출처를 설명할 수 없는 페이지는 같은 배치에 넣지 않고 검토 폴더로 옮깁니다. 조건이 맞는 묶음만 필드 별칭을 적용하고, 고유 ID 수와 원시 행 수를 비교합니다.
실패 가능성은 원하는 건수와 비슷하다는 이유로 서로 다른 조회 결과를 합치는 것입니다. 총건수가 우연히 같아도 같은 배치라는 증거는 아닙니다. 사람은 요청 조건·수집 시각·페이지 번호를 확인하고, 재현 가능한 묶음만 분석용 CSV 후보로 승인합니다.
필드명 맞추기
여러 표기를 하나의 표준 컬럼으로 모읍니다. 새 API에서 필드명이 다르면 이 표에 원본 이름만 추가하면 됩니다.
FIELD_ALIASES = {
"facility_id": ["facilityId", "facility_id", "FCLTY_ID"],
"facility_type": ["facilityType", "type", "FCLTY_TY"],
"sido": ["ctprvnNm", "sido", "CTPRVN_NM"],
"sigungu": ["signguNm", "sigungu", "SIGNGU_NM"],
"lat": ["lat", "latitude", "LAT"],
"lon": ["lon", "longitude", "LOT"],
}
실제로 어떻게 합쳐지는지 보겠습니다. 아래는 같은 시설이 두 쪽에서 다른 모양으로 온 가상의 예시입니다.
# 1쪽 응답 (일부) — camelCase, 좌표 있음
{"facilityId": "WF-006", "facilityType": "소화전",
"latitude": "37.5512", "longitude": "126.9882"}
# 2쪽 응답 (일부) — snake_case, 같은 시설이 중복 + 좌표 결측
{"facility_id": "WF-006", "type": "소화전",
"latitude": "", "longitude": ""}
FIELD_ALIASES로 필드명을 맞추면 두 행 모두 같은 표준 컬럼(facility_id, facility_type, lat, lon)으로 읽힙니다. 그다음 facility_id 기준으로 중복을 제거하면 WF-006은 한 행으로 합쳐지고, 좌표가 있는 1쪽 값이 남습니다.
facility_id,facility_type,lat,lon WF-006,소화전,37.5512,126.9882
핵심은 순서입니다. 필드명을 먼저 맞춰야 어느 행이 같은 시설인지 비교할 수 있고, 그래야 중복을 제거하고 좌표 있는 행을 고를 수 있습니다.
검산 — totalCount = 고유 시설 ID
핵심 검산은 두 가지입니다: 페이지마다 적힌 totalCount가 서로 같은지, 그리고 그 수가 중복 제거 후 고유 시설 수와 같은지.
왜 이 두 가지를 보느냐면, 각각 다른 종류의 사고를 잡기 때문입니다. totalCount는 API가 "전체 몇 건"이라고 스스로 밝힌 숫자입니다. 페이지마다 이 숫자가 다르면, 받는 도중에 조회 조건이 바뀌었거나 서로 다른 응답을 섞은 것입니다 — 이어 붙이면 안 되는 데이터라는 신호입니다. 두 숫자가 같다면, 이번엔 내가 받은 것이 그 숫자만큼인지를 봅니다. 선언은 12건인데 중복 제거 후 고유 ID가 12건이면 딱 맞고, 다르면 중복이나 누락이 있다는 뜻입니다.
metric,value declared_total_count,12 downloaded_rows,13 unique_facility_id,12 valid_rows_with_coordinates,11 review_rows,3 total_count_consistent,True
선언 총건수 12 = 고유 ID 12이면 통과(OK), 다르면 경고(WARN)입니다. 위 예에서 다운로드는 13행이지만(중복 1건 포함) 고유 ID는 12, 좌표가 있는 유효 행은 11, 검토 대상은 3행입니다.
[OK] totalCount 12건, 다운로드 13행, 고유 시설 12건 [OK] 좌표 포함 정리 11건, 검토 필요 3행 [OK] 저장: output/normalized_facilities.csv, output/review_items.csv, output/summary.csv
좌표가 있는 11건만 지도에 얹고, 좌표가 빈 행과 중복은 review_items.csv로 따로 빠져 사람이 봅니다. 자동으로 "정답"을 정하지 않고 애매한 것을 사람 앞에 남기는 것이, 이 파이프라인이 실수를 막는 방식입니다.
검산이 보장하는 것과 아닌 것
| 검산 항목 | 보장하는 것 | 보장하지 않는 것 |
|---|---|---|
| 페이지 간 totalCount 일치 | 같은 조회 조건의 응답인지 확인 | API 서버 원자료 정확성 |
| totalCount = 고유 시설 ID | 중복 제거 뒤 기대 건수 일치 | 시설 ID 자체의 정확성 |
| 좌표 숫자 변환 | 지도 입력 가능 행 분리 | 좌표가 실제 위치와 맞는지 |
| 검토 파일 분리 | 중복·결측을 사람이 보게 남김 | 자동 정답 판정 |
review_items.csv가 비어 있지 않으면 자동 지도화·보고 반영을 멈추고 원문을 확인합니다.내 업무에 적용하기
- 실제 API 응답은 공개 저장소에 올리지 않고 로컬
data/에만 저장합니다. - 합성 JSON으로 먼저 돌려 산출 파일을 확인합니다.
- 실제 API 문서에서 ID·유형·지역·좌표·기준연도 필드명을 확인합니다.
- 필드명이 다르면
FIELD_ALIASES에 원본 필드명을 추가합니다. summary.csv에서 totalCount·다운로드 행·고유 ID 일치를 확인합니다.review_items.csv가 비어 있지 않으면 지도화·보고 반영을 중단하고 원문을 확인합니다.
이렇게 정리된 normalized_facilities.csv는 이제 지도화나 통계로 넘어갈 수 있습니다. 좌표가 있는 유효 행만 지도에 얹고, 지역별 시설 수를 세는 집계는 출동 통계, CSV 한 장으로 월간 집계의 방법을 그대로 씁니다. 응답에 개인정보나 상세주소가 섞여 있다면 내보내기 전에 개인정보, AI에 넣기 전에 점검하기로 먼저 걸러냅니다.
serviceKey(인증키)와 원문 응답 전체는 코드·문서·프롬프트·공개 저장소에 넣지 마세요. 상세주소·내부 관리번호·시설 보안 정보도 결과에 포함하지 않습니다.문제 해결
| 증상 | 가능한 원인 | 처방 | 확인 |
|---|---|---|---|
| 특정 페이지의 열이 전부 비어 보임 | 원본 키가 별칭표에 없거나 중첩 위치가 다름 | 원본 응답의 키 구조를 확인해 명시적 별칭·변환 규칙 추가 | 변환 전후 샘플 행을 원본 값과 대조 |
| 중복 제거 뒤 예상보다 행이 크게 줄어듦 | ID가 누락됐거나 재사용된 행을 같은 값으로 묶음 | 빈 ID와 반복 ID를 먼저 검토 파일로 분리 | ID별 원시 행 수와 출처 페이지 확인 |
페이지마다 totalCount가 다름 | 조회 조건·수집 시점·응답 묶음이 섞임 | 병합을 멈추고 요청 조건별로 파일을 다시 구분 | 한 배치의 조건과 선언 총건수가 일관되는지 확인 |
| 좌표는 숫자인데 지도 위치가 의심됨 | 숫자 변환만 통과했고 위치 의미는 검증되지 않음 | 자동 확정하지 않고 원출처와 별도 위치 검토로 분리 | 좌표 유효와 위치 확인 상태를 다른 열로 기록 |
자주 묻는 질문
Q. totalCount와 고유 ID 수가 같으면 데이터가 정확한가요?
중복 제거 뒤 기대 건수가 맞는다는 검산입니다. ID 자체와 좌표·유형 값이 현실과 맞다는 보증은 아니므로 내용 검토가 별도로 필요합니다.
Q. 좌표가 빈 중복 행은 그냥 삭제해도 되나요?
좌표가 있는 같은 ID 행과 실제로 같은 시설인지 먼저 확인합니다. 원본 페이지와 충돌 내용을 검토 파일에 남기지 않으면 잘못된 병합을 추적할 수 없습니다.
Q. 새 필드명이 보이면 비슷한 표준 열에 자동 연결해도 되나요?
이름이 비슷해도 뜻과 단위가 다를 수 있습니다. API 설명과 샘플 값을 확인해 의미가 같은 경우에만 명시적으로 별칭을 추가합니다.
Q. review_items.csv가 있으면 나머지 정상 행은 바로 공개해도 되나요?
검토 행 분리는 데이터 품질 단계일 뿐 공개 승인 단계가 아닙니다. 정상 행에도 상세주소·관리번호 같은 공개 제한 정보가 없는지 별도로 확인해야 합니다.
핵심 정리
- 페이지를 합치기 전에 조회 조건과 선언 총건수가 같은 응답 묶음인지 확인합니다.
- 필드명을 표준화한 뒤에야 고유 ID 중복과 좌표 결측을 일관되게 비교할 수 있습니다.
- 선언 총건수·원시 행·고유 ID·좌표 유효 행은 서로 다른 범위를 설명합니다.
- 자동으로 결정할 근거가 부족한 충돌은 원본 근거와 함께 검토 파일로 남깁니다.
다음 단계
- 소방 공공데이터·API 지도 — 분석 목적에 맞는 공식 데이터 출처와 인증 여부를 확인하는 출발점입니다.
- 출동 통계, CSV 한 장으로 월간 집계 — 정규화된 고유 행을 지역·유형별로 집계하고 합을 검산할 수 있습니다.
- 소방용수시설 접근성 분석 지도 — 확인된 좌표를 공간 분석에 넘길 때 필요한 좌표·거리 한계를 이어서 다룹니다.