공공데이터포털은 무엇이고 어디서 시작하는가
공공데이터포털(data.go.kr)은 정부와 공공기관이 보유한 데이터를 한 곳에서 개방하는 사이트입니다. 행정안전부가 운영하고, 근거 법은 「공공데이터의 제공 및 이용 활성화에 관한 법률」입니다. 병원·약국 같은 의료기관 정보부터 건축물대장, 행정동 인구, 대중교통, 날씨까지 들어 있습니다.
제공 형태는 크게 둘입니다. 파일 데이터(CSV·SHP 등을 내려받는 것)와 오픈API(주소로 호출해 받는 것)입니다. 포털 첫 화면의 인기 데이터·최신 데이터 영역에도 이 두 탭이 나란히 있습니다. 뒤에서 보겠지만 이 구분이 실제 개발에서 꽤 중요합니다.
시작 절차는 간단합니다. 회원가입을 하고, 쓰려는 데이터 상세 페이지에서 활용신청을 누르고, 마이페이지에서 서비스키(인증키)를 받습니다. 대부분의 API는 신청 즉시 또는 하루 안에 승인됩니다. 서비스키는 URL 쿼리에 `serviceKey=` 로 붙여 보냅니다. 이때 키에 포함된 특수문자를 URL 인코딩하지 않으면 인증 실패가 나므로, 코드에서 반드시 인코딩해 붙입니다.
첫 함정 — 모르는 파라미터를 거부하지 않고 무시한다
병원과 약국 위치를 지도에 올리는 도구를 만들면서 건강보험심사평가원 약국정보서비스를 붙였습니다. 서울 것만 받으려고 지역 파라미터를 넣었는데, 여기서 문제가 생겼습니다.
`Q0=서울특별시`를 넣고 부르니 총건수가 25,771건으로 왔습니다. 서울 약국이 그렇게 많을 리가 없어 파라미터를 아예 빼고 다시 불렀습니다. 역시 25,771건이었습니다. 즉 그 파라미터는 처음부터 아무 일도 하지 않았습니다.
원인은 파라미터 이름이었습니다. `Q0`는 국립중앙의료원이 제공하는 다른 약국 API의 파라미터이고, 심평원 약국정보서비스는 `sidoCd`(시도코드)를 씁니다. 서울은 `110000`입니다. 제대로 넣어 다시 부르니 5,883건이 나왔습니다.
여기서 중요한 건 숫자 차이가 아니라 **실패하는 방식**입니다. 잘못된 파라미터를 보냈는데도 서버는 400도, 경고도 주지 않았습니다. HTTP 200에 정상적인 JSON 구조로 응답했고, 데이터도 진짜 약국 데이터였습니다. 다만 범위가 서울이 아니라 전국이었을 뿐입니다. 서울 약국 수를 4.4배로 부풀려 받으면서 아무런 오류 신호도 보지 못한 것입니다.
이런 실패는 로그에 안 남고 눈으로도 안 잡힙니다. 데이터를 몇 건 열어 봐도 형태가 멀쩡하니 이상함을 못 느낍니다. 지도에 찍어 보고 나서야 서울 밖 좌표가 섞인 걸 알게 되는 식입니다.
그래서 첫 검증은 총건수 비교다
이 경험 이후로 공공데이터 API를 붙일 때 첫 검증을 하나로 고정했습니다. **필터를 뺀 총건수와 넣은 총건수를 나란히 찍어 보는 것**입니다. 두 값이 같으면 그 필터는 안 먹은 것입니다.
실제로 같은 방식으로 병원 API도 확인했습니다. 필터 없이 부르면 79,797건(전국), `sidoCd=110000`을 넣으면 19,880건(서울)이 나옵니다. 값이 달라지므로 이쪽은 필터가 정상 작동합니다. 약국은 같은 값이 나왔으니 안 먹은 것이고요. 판정에 1분도 안 걸립니다.
총건수는 대개 응답의 `totalCount` 필드에 있고, `numOfRows=1`로 불러도 그 값은 전체 기준으로 옵니다. 그래서 데이터를 한 건도 안 받고 총건수만 비교할 수 있습니다. 파이프라인을 짜기 전에, 데이터를 한 줄이라도 뜯어보기 전에 이것부터 합니다.
같은 원리를 다른 필터에도 적용합니다. 시군구·종별·과목 등 필터를 하나 더 걸 때마다 총건수가 줄어드는지 봅니다. 안 줄어들면 그 필터는 없는 것과 같습니다.
포털에 있는 것과 API로 받을 수 있는 것은 다르다
두 번째로 자주 어긋나는 지점입니다. 「그 데이터 공공데이터포털에 있어요」와 「그 데이터 API로 받을 수 있어요」는 다른 말입니다.
지도 하나를 만드는 데 필요했던 다섯 가지를 포털에서 실제로 검색해 보면 이렇게 갈립니다. 병원·약국·건축물대장은 오픈API가 있어 주기적으로 호출할 수 있지만, 행정동 경계와 행정동 인구는 파일 데이터로만 있습니다. 경계를 API로 검색하면 엉뚱한 결과(가뭄분석 같은 것)가 나오고 경계 폴리곤 API는 잡히지 않습니다.
이 차이가 파이프라인 설계를 정합니다. 병원·약국은 개폐업이 계속 생기므로 API로 주기 조회하는 게 맞습니다. 반면 행정동 경계와 인구는 행정동 수만큼 고정된 값이라 파일을 한 번 받아 적재하면 됩니다. 갱신 주기가 다른 자료를 같은 방식으로 다루려다 보면 일이 불필요하게 늘어납니다.
그래서 데이터를 고를 때 「있는가」 다음에 바로 「어떤 형태로 있는가」를 봅니다. 상세 페이지 주소가 `openapi.do`로 끝나면 API, `fileData.do`로 끝나면 파일입니다.
| 필요한 자료 | 제공 형태 | 데이터셋 번호 |
|---|---|---|
| 병원 정보 (심평원) | 오픈API | 15001698 |
| 약국 정보 (심평원) | 오픈API | 15001673 |
| 건축물대장 (국토교통부 건축HUB) | 오픈API | 15134735 |
| 행정동 경계 (국토교통부 센서스경계) | 파일 | 15125055 |
| 행정동 인구 (행정안전부) | 파일 | 15097972 |
여러 데이터셋을 잇는 키는 무엇인가
데이터를 여러 개 받으면 결국 붙여야 합니다. 의료기관 쪽에서 그 열쇠는 요양기호(`ykiho`)입니다. 건강보험심사평가원이 요양기관마다 부여하는 코드이고, 병원 API와 약국 API가 같은 체계를 씁니다. 그래서 두 데이터셋을 그대로 조인할 수 있습니다.
이름이나 주소로 붙이려 하면 반드시 깨집니다. 같은 기관이 표기만 다르게 들어오는 경우가 흔하고(「○○의원」과 「○○ 의원」), 주소는 도로명과 지번이 섞입니다. 코드가 있는데 문자열로 매칭하지 않습니다.
다른 도메인에서도 같은 원리를 찾습니다. 건축물 쪽은 대장 키가 있고, 행정구역은 행정동코드(`admCd`)가 있습니다. 데이터셋을 받기 전에 「이걸 무엇으로 다른 것과 이을 것인가」를 먼저 정하면 나중에 되돌아오는 일이 줄어듭니다.
실제로 붙일 때 순서
정리하면 이 순서입니다. 첫째, 포털에서 데이터를 찾고 상세 페이지 주소로 API인지 파일인지 확인합니다. 둘째, 활용신청 후 서비스키를 받고 URL 인코딩해 호출합니다. 셋째, 필터 없이 한 번, 필터를 넣고 한 번 불러 총건수를 비교합니다. 같으면 파라미터 이름이 틀린 것이므로 문서의 요청변수 표를 다시 봅니다.
넷째, 조인 키를 정합니다. 다섯째, 갱신 주기에 따라 API 주기 조회와 파일 1회 적재로 경로를 나눕니다. 여섯째, 적재한 총건수를 어딘가에 기록해 둡니다. 다음에 값이 크게 달라지면 원천이 바뀐 것인지 내 호출이 바뀐 것인지 대조할 근거가 됩니다.
마지막으로, 공공데이터는 기관마다 규격이 제각각이라는 것을 전제로 다룹니다. 같은 「약국 정보」인데도 제공 기관이 다르면 파라미터 이름이 다르고, 어떤 API는 틀린 입력을 조용히 무시합니다. 문서를 읽되 문서를 믿지 말고, 총건수로 확인하는 습관이 결국 시간을 아껴 줍니다.
