착한가격업소 6,500곳을 지도에 올리기까지

알뜰 밥집 지도를 서울 밖으로 넓히면서 가장 큰 도움을 준 건 행정안전부가 공개하는 착한가격업소 데이터였습니다. 로그인 없이 받을 수 있고, 분기마다 갱신되고, 무엇보다 다른 데는 없는 값이 들어 있습니다.

붙이는 일이 간단할 줄 알았는데 아니었습니다. 기록으로 남겨 둡니다.

1. 가격이 있는 데이터는 흔하지 않다#

식당 가격은 어디에도 정리돼 있지 않습니다. 메뉴판은 손으로 바뀌고, 포털 정보는 몇 년 전 것이기 일쑤입니다. 그래서 지도에 가게 이름만 찍히고, 결국 하나씩 눌러 봐야 하는 지도가 됩니다.

착한가격업소 데이터에는 업소마다 대표 메뉴와 가격이 최대 네 칸까지 들어 있습니다. 게다가 저희가 부르는 "착한 밥집"과 정부가 지정한 "착한가격업소"는 사실상 같은 개념이라, 성격이 안 맞는 데이터를 억지로 끌어다 쓰는 것도 아니었습니다.

파일은 공공데이터포털에서 CSV 로 받습니다. 인코딩이 EUC-KR 이라 UTF-8 로 읽으면 전부 깨지고, 주소와 메뉴명에 쉼표가 흔해서 split(',') 로 자르면 열이 밀려 엉뚱한 값이 가격 칸에 들어옵니다. 첫 관문부터 조용히 틀리기 좋은 구조입니다.

2. 사진 인화 300원이 가장 싼 밥집이 되었다#

이 데이터에는 밥집만 있는 게 아닙니다. 미용실·목욕탕·사진관·인쇄소까지 들어 있어서 외식업만 골라내야 합니다. 그래서 업종명으로 걸렀는데 —

기타비요식업

이 글자 안에 "요식"이 들어 있습니다. 포함 규칙만 쓰면 그대로 통과합니다. 그래서 사진 인화 300원짜리가 "가장 싼 밥집"으로 목록 맨 위에 올라왔습니다. 가격도 정상이고 좌표도 정상이라 코드 어디에서도 에러가 나지 않았습니다.

같은 종류의 결함을 여러 번 겪었고, 에러를 내지 않는 결함 편에 따로 모아 두었습니다.

3. 대표 메뉴로 최저가를 고르면 안 된다#

업소마다 메뉴가 최대 네 개 들어 있으니, 처음엔 그중 가장 싼 것을 대표로 골랐습니다. "가장 싼 밥집"을 찾는 서비스니까요.

그런데 이런 가게가 있습니다.

김치찌개 3,000원 · 사리 1,000원

최저가를 고르면 이 집의 가격은 사리 1,000원이 됩니다. 지도에 1,000원이 찍히고, 그 가격을 보고 찾아간 사람은 3,000원을 냅니다. 부가 메뉴가 그 집의 대표 가격이 되는 건, 저희가 쇼핑에서 걸러내는 미끼가격과 정확히 같은 왜곡입니다.

지금은 첫 번째 메뉴를 대표로 씁니다. 데이터를 만드는 지자체도 첫 칸에 대표 메뉴를 넣습니다. 그리고 1만 원 이상인 곳은 아예 담지 않습니다 — 저희 지도의 기준선은 "한 끼 만 원 미만"입니다.

4. 없는 것 — 좌표#

데이터에 좌표가 없어서, 업소를 지역 검색으로 다시 찾아 좌표와 도로명주소를 붙입니다. 못 찾은 곳은 건너뜁니다. 지도에 못 찍는 가게를 목록에만 넣으면 "지도에 없는 검색 결과"가 생기고, 그건 지도를 못 믿게 만드는 가장 빠른 길입니다.

5. 같은 이름의 동네가 전국에 있다#

여기서 가장 오래 붙들렸습니다.

동구는 부산·대구·인천·광주·대전·울산에 전부 있습니다. 광주시는 경기도에도 있고 광역시에도 있습니다. 고성군은 강원과 경남에 하나씩 있습니다. 지역명만 붙여 검색하면 엉뚱한 도시의 동명이인 가게가 돌아옵니다.

문제는 그다음이었습니다. 후보가 맞는지 확인하려고 주소를 대조했는데, 주소에서 구·군을 뽑는 규칙이 첫 번째로 나오는 시·군·구였습니다. 그런데 주소의 첫 조각은 시도명입니다. 서울특별시 종로구 … 에서 잡히는 건 종로구가 아니라 서울특별시였습니다.

이 느슨함이 한 지역을 통째로 날렸습니다. 원본 CSV 의 시도명과 검색이 돌려주는 시도명이 행정구역 명칭 개편으로 달라진 지역이 있었는데, 시도만 대조하던 규칙이 전부 탈락시켜 369건이 지도에 0곳이 됐습니다. 화면에는 "이 지역에는 착한가격업소가 없습니다"라고 나왔을 뿐입니다.

지금은 행정구역명 대신 도로명과 법정동을 봅니다. 백서로, 서석동 같은 이름은 개편에 흔들리지 않고 전국에서 훨씬 드물어서, 시도명보다 안전하면서 동시에 더 엄격합니다. 점수로 판단합니다.

맞은 항목 점수 근거
도로명 3 전국에서 겹치는 일이 드물다
법정동 3 마찬가지
시·군·구 2 동구만으로는 부족하다
시도 1 개편·약칭이 잦아 보조 신호

3점 미만이면 버립니다. 엉뚱한 좌표로 지도에 찍는 것보다 없는 편이 낫습니다.

6. 캐시가 실패를 기억한다는 문제#

전국 6,500곳의 좌표를 매번 새로 찾으면 재실행 때마다 같은 시간이 다시 듭니다. 그래서 결과를 파일에 남깁니다. 못 찾은 것(null)도 남깁니다 — 없는 걸 매번 다시 찾는 게 가장 비싸니까요.

여기에 함정이 하나 있었습니다. 위의 대조 규칙을 고쳐도, 캐시에 남은 실패 기록 때문에 영영 다시 시도하지 않습니다. 고쳤는데 화면은 그대로인 상태가 됩니다. 고친 게 안 나가는 것도 결함입니다.

지금은 캐시에 규칙 버전을 함께 적어 두고, 규칙을 손대면 버전을 올려 실패 기록만 무효로 만듭니다. 성공한 좌표는 그대로 재사용합니다 — 좌표는 규칙과 무관하게 옳습니다.

7. 6,500곳, 13분#

한 건씩 순서대로 조회하면 28분이 걸립니다. 그렇다고 한꺼번에 던지면 요청 제한에 걸립니다. 그래서 워커 몇 개가 나눠 처리하되 전역 최소 간격을 두는 방식으로 초당 8건을 유지했습니다. 13분으로 줄었습니다.

여기서도 한 번 틀렸습니다. 대기 시간을 계산한 뒤 잠들었다가 깨어나서 다음 슬롯을 잡으면, 워커 넷이 같은 값을 읽고 같이 자다가 한꺼번에 깨어납니다. 간격이 지켜지는 것처럼 보이지만 실제로는 뭉쳐서 나갑니다 — 8건/초를 의도했는데 30건/초가 나갔습니다. 슬롯은 잠들기 전에 미리 예약해야 합니다.

공시가와 제보는 섞지 않는다#

이렇게 붙인 가격은 지도에서 공시가로 표시됩니다. 사용자가 남긴 가격 제보와는 칸이 다릅니다. 둘을 평균 내면 보기에는 깔끔하지만, 출처가 다르면 신뢰도도 다릅니다.

공시가는 제보가 없는 가게에도 기준선을 만들어 줍니다. 제보는 그 기준선이 낡았을 때 바로잡아 줍니다. 두 층이 각각 하는 일이 다릅니다.

그래서 지도에 무엇이 보이나#

전국 시·도의 만 원 미만 밥집이 가격과 함께 찍혀 있습니다. 지도를 옮기면 그 자리의 가게와 값이 바로 나오고, 훑는 것만으로 "이 동네에서 이 가격이면 착한 편인지"가 보입니다. 찾는 방법은 착한 밥집 찾는 법에 정리했습니다.

공공데이터를 쓴다는 건 공짜로 얻는다는 뜻이 아니었습니다. 업종 한 글자, 메뉴 한 칸, 주소 한 조각에서 값이 조용히 틀어지고, 그 결과는 언제나 화면에 정상으로 보입니다. 그래서 이 글에 실패를 하나씩 적었습니다. 다음에 같은 데이터를 쓰실 분이 같은 자리에서 멈추지 않도록요.

지도에서 가격이 틀렸거나 없는 가게를 보시면 제보해 주세요. 로그인은 필요 없습니다. 학생과 자취생의 한 끼를 지키는 이 지도는, 결국 다녀온 사람들이 남긴 값으로 채워집니다.