오늘뭐먹 서비스는 “정확한 식당 데이터”가 가장 큰 가치다. 룰렛이 폐업한 가게를 추천하거나, 영업 종료 시간이 잘못된 가게를 알려준다면 사용자 신뢰가 무너진다. 이 글은 우리가 식당 데이터를 어떻게 수집·매칭·검증·갱신하는지 투명하게 공개한다.
데이터 파이프라인 개요
`` 공공데이터포털 (CSV) ↓ 1. 가져오기 PostgreSQL (raw) ↓ 2. 정제 + 매칭 네이버 지도 Local Search API ↓ 3. 보강 (좌표, 영업시간, 카테고리) PostgreSQL (enriched) ↓ 4. 자동 검증 + 사람 검수 사용자에게 노출 ``
전체 파이프라인은 공공 데이터(1차 출처), 네이버 데이터(2차 보강), 자동 검증, 사람 검수의 4단계로 구성된다.
1. 공공데이터포털 — 1차 출처
식당 정보의 근간은 공공데이터포털의 일반음식점 인허가 정보다. 이 데이터는 식품의약품안전처와 각 지방자치단체가 발급·관리하는 영업 허가 정보로, 다음 항목을 포함한다.
- 관리번호 (영업 허가 고유 ID)
- 사업장명
- 업종 분류 (한식·중식·양식·일식 등)
- 도로명/지번 주소
- 좌표 (TM 좌표계)
- 전화번호
- 영업 상태 (영업·폐업·휴업)
- 인허가 일자
이 데이터의 강점은 공식성이다. 영업 허가가 살아있다는 것은 법적으로 영업이 가능하다는 의미다. 약점은 갱신 주기가 길어 폐업이 반영되기까지 1~3개월이 걸린다는 점.
2. 좌표계 변환 — TM → WGS84
공공데이터는 TM 좌표계(중부원점)를 쓴다. 네이버 지도와 모바일 GPS는 WGS84를 쓴다. 두 좌표계를 매칭하지 않으면 “주소는 강남구인데 지도에는 서초구에 찍히는” 사태가 발생한다.
우리는 proj4 라이브러리로 TM(EPSG:5174)을 WGS84(EPSG:4326)로 변환한다. 변환 오차는 평균 1~3m로 도시 식당 검색에는 충분하다.
3. 네이버 매칭 — 2차 보강
공공데이터의 좌표는 종종 “건물 중심”을 가리킨다. 같은 빌딩에 10개 식당이 있으면 모두 같은 좌표다. 또한 영업시간·세분류 카테고리(한식 > 백반/국밥)·전화번호는 공공데이터에 누락이 많다.
우리는 네이버 지도 Local Search API와 매칭해 다음을 보강한다.
- 정확한 출입구 좌표 (보통 공공데이터 좌표에서 5~30m 이내)
- 네이버 카테고리 (예: “한식 > 백반/국밥”)
- 영업시간 (요일별·브레이크타임·라스트오더 포함)
- 네이버 플레이스 ID (외부 링크용)
- 전화번호 (공공데이터에 누락된 경우)
매칭 알고리즘
같은 식당을 양 쪽 데이터에서 매칭하려면 단순 이름 비교로는 부족하다. “인사동 두부마을” vs “인사동두부마을(인사동본점)” vs “두부마을 인사동점” — 사람은 같은 가게로 인식하지만 문자열 비교는 실패한다.
우리는 다음 단계의 매칭 점수를 계산한다.
- 좌표 거리: 50m 이내 후보만 추림 (가중치 40%)
- 이름 유사도: Jaro-Winkler + 한글 정규화(공백·괄호·체인명 제거) (가중치 40%)
- 카테고리 일치도: 공공 “한식” vs 네이버 “한식 > 백반” (가중치 20%)
점수 0.8 이상은 자동 매칭, 0.5~0.8은 사람 검수 큐로 들어간다.
4. 자동 검증 — 영업 상태 확인
매주 1회 다음을 자동 확인한다.
- 공공데이터 영업 상태: 폐업/휴업 신호가 뜨면
inactive_reason='csv_closed'또는'csv_suspended'로 표시 - 네이버 영업 상태: 네이버에서 “영업종료” 표시가 뜨면
inactive_reason='naver_closed' - 두 쪽 모두 OK:
is_active=TRUE로 유지
사용자에게는 is_active=TRUE + 영업시간 안에 있는 식당만 노출된다.
5. 사람 검수 — 마지막 안전망
자동화에 의존하기 어려운 경우가 있다.
- 매칭 점수 0.5~0.8 식당 (애매한 경우)
- 사용자가 “정보 신고”로 보낸 식당
- 네이버 API에 등록되지 않은 신규 가게
이런 식당은 관리자 페이지의 검수 큐로 들어가고, 정보가 확정된 후 활성화된다.
6. 영업시간 파싱
네이버 영업시간은 사람 친화적 자연어로 표시된다. “월~금 11:00~21:00 (브레이크 15:00~17:00)”처럼.
우리는 이를 구조화된 JSON으로 파싱한다.
``json { "regular": [ {"day": "월", "open": "11:00", "close": "21:00"}, {"day": "화", "open": "11:00", "close": "21:00"} ], "breakTime": [ {"start": "15:00", "end": "17:00"} ], "holiday": ["일"] } ``
파싱된 JSON으로 “현재 영업 중” 필터를 실시간으로 적용한다. 사용자가 14시에 검색하면 “14시에 브레이크타임인 식당”은 제외된다.
7. 갱신 주기
- 공공데이터: 주 1회 자동 동기화
- 네이버 매칭: 신규 식당만 매일 (속도 제한으로 인해 식당 간 5초+ 인터벌)
- 영업시간 재확인: 분기 1회
- 사용자 신고 반영: 1주 이내
8. 데이터 부족·오류는 어떻게 다루나
완벽한 데이터는 없다. 다음 한계를 명시한다.
- 매칭 실패 식당: 이름·좌표 차이로 네이버 매칭이 실패한 식당은 룰렛 후보에서 제외된다.
- 영업시간 변동: 임시 휴무·명절 휴업·실시간 마감은 반영이 늦다.
- 메뉴·가격: 우리 데이터에는 메뉴/가격이 없다. 네이버 지도 링크로 사용자가 직접 확인하도록 안내한다.
데이터 오류는 /contact에 “[정보 신고]” 태그로 보내주시면 1주 이내 확인한다.
9. 왜 메뉴·가격을 직접 제공하지 않나
다음 두 이유다.
- 저작권: 메뉴·가격은 가게가 작성한 콘텐츠다. 그대로 복사하는 것은 저작권 침해 위험.
- 갱신 속도: 가격은 가게 사정으로 자주 바뀐다. 우리 데이터가 일주일만 늦어도 사용자가 헛걸음할 위험.
대신 네이버 지도 링크로 바로 이동할 수 있게 해, 가장 최신 정보가 있는 곳에서 확인하도록 유도한다.
정리
오늘뭐먹의 식당 데이터는 다음 신뢰성 원칙을 따른다.
- 공식 출처 우선: 공공데이터포털 + 네이버 지도 API
- 자동 + 사람 검수 병행: 매칭 점수 기반 분기
- 불확실성 명시: 완전하지 않은 정보는 “정보 신고” 채널 운영
- 갱신 주기 공개: 사용자가 데이터 나이를 예측 가능
위 파이프라인은 계속 개선 중이다. 데이터 정확성에 대한 피드백은 언제든 환영한다.
— 운영자