개발 기록으로
BUILDING NOTE

추석 연휴 문 여는 병원·약국 찾기를 하루 만에 만든 기록

공공데이터포털 명절 비상진료기관 API와 카카오맵으로 내 위치 주변 연휴 진료 병원·약국을 거리순으로 보여주는 사이트를 만들고 Vercel에 배포했습니다. 문서와 다른 실제 응답, 좌표가 없는 데이터, 캐시 때문에 생긴 착시까지 정리합니다.

목차

추석 연휴 문 여는 병원·약국 찾기를 하루 만에 만든 기록

추석이 2주 남았습니다. 연휴에 아이가 갑자기 열이 나거나 약이 떨어졌을 때 어디가 문을 열었는지 찾는 일은 매년 반복되는데, 응급의료포털 E-Gen은 정보는 정확하지만 폰에서 급하게 쓰기엔 단계가 많습니다. 그래서 "내 위치 주변에서 지금 여는 곳"만 보여주는 한 페이지짜리 사이트를 만들었습니다.

어떤 데이터를 쓰나

공공데이터포털에 국립중앙의료원이 올린 「전국 명절 비상 진료기관 정보 조회 서비스」 Open API가 원천입니다. E-Gen이 보여주는 연휴 진료 정보와 같은 데이터이고, 회원가입 후 활용신청하면 바로 인증키가 나옵니다. 개발계정은 하루 1,000건 제한이라 호출 설계가 중요했습니다.

구조는 단순합니다.

  1. 브라우저가 현재 위치를 받아 카카오맵 Geocoder로 시·도 이름을 얻는다.
  2. Next.js Route Handler가 시·도 단위로 명절 API를 호출하고 6시간 캐시한다.
  3. 브라우저에서 haversine 거리를 계산해 반경·구분·날짜로 걸러 목록과 지도에 뿌린다.

시·도당 하루 최대 4번 호출이니 전국을 다 더해도 100건이 안 됩니다. 서비스키는 서버에만 두고, 서버로 보내는 건 시·도 이름뿐이라 현재 위치는 브라우저 밖으로 나가지 않습니다.

문서와 실제 응답이 달랐다

여기서부터가 진짜 개발기록입니다.

응답에 좌표가 없었습니다. 공공데이터포털 문서에는 wgs84Lat, wgs84Lon이 응답 항목으로 적혀 있는데, 실제 XML에는 주소·전화·진료시간·기관 ID(hpid)만 옵니다. 위치 기반 서비스인데 위치가 없는 셈이라 설계를 바꿔야 했습니다.

같은 기관의 다른 API 두 개(「전국 병·의원 찾기 서비스」, 「전국 약국 정보 조회 서비스」)에는 좌표가 있고 hpid가 공통 키입니다. 그래서 빌드 전에 전국 병의원 78,957건과 약국 25,416건을 받아 hpid → [위도, 경도] 인덱스를 3.3MB JSON으로 만들어 저장소에 넣고, 런타임에는 명절 API 결과와 조인만 합니다. numOfRows=5000까지 받아줘서 인덱스 생성은 API 22번 호출로 끝났습니다. 서울 2,452건을 조인해 보니 누락이 0건이었습니다.

필드명도 달랐습니다. 문서의 dutyDivNam, dutyDayEtc가 실제로는 dutyDivName, dutyDayetc이고, 날짜는 20260924가 아니라 2026-09-24로 옵니다. 파서를 문서 기준으로 짜 두었다면 조용히 0건이 나왔을 겁니다.

"날짜가 있다"와 "문을 연다"는 다른 뜻이었습니다. 모든 기관이 연휴 4일(9/24~9/27)을 dutyDay1~4에 똑같이 나열하고, 실제 진료하는 날에만 dutyDaytime이 채워집니다. 처음에는 시간이 없는 날을 "시간 미기재"로 보여줬는데, 서울 데이터를 세어 보니 추석 당일에 시간이 있는 곳은 2,452곳 중 974곳뿐이었습니다. 시간 없는 날은 휴진으로 처리하니 날짜 필터가 비로소 의미를 갖게 됐습니다.

캐시가 만든 착시

파서를 고쳤는데 브라우저에서는 날짜 필터가 여전히 안 먹었습니다. API를 직접 curl 하면 맞게 나오는데 화면만 옛날 데이터였습니다. 원인은 제가 붙인 Cache-Control: public, s-maxage=3600, stale-while-revalidate=21600이었습니다. s-maxage는 CDN용이라고만 생각했는데, Chrome은 stale-while-revalidate 창 안에서 만료된 응답을 그대로 쓰면서 뒤에서 재검증합니다. 즉 브라우저가 수정 전 응답을 재사용하고 있었습니다.

브라우저에는 max-age=0, must-revalidate를 주고, Vercel CDN에만 CDN-Cache-Control로 1시간 캐시를 주는 식으로 분리해 해결했습니다. 화면에 "자료 기준 시각"을 찍어 둔 덕에 알아챌 수 있었습니다.

카카오맵 도메인 등록은 두 군데가 있다

배포 후 지도가 안 떴습니다. 카카오 디벨로퍼스의 「제품 링크 관리 > 웹 도메인」에 배포 도메인을 넣었는데도 SDK가 401을 냈습니다. 실제로 검사하는 곳은 **「플랫폼 키 > JavaScript 키 > JavaScript SDK 도메인」**이었습니다. 같은 콘솔 안에 도메인을 등록하는 화면이 두 개라 헷갈리기 쉽습니다. curl -H "Referer: https://배포도메인/"로 SDK URL을 찔러 보면 200/401로 바로 확인됩니다.

디자인에서 한 선택

  • 카드마다 연휴 4일을 같은 칸에 두고 진료시간 또는 "휴진"을 넣는 연휴 스트립. 시간을 읽지 않아도 색 패턴으로 언제 여는지 보입니다.
  • 색은 의미로만 씁니다. 병의원은 적색, 약국은 녹색(간판 십자 색), 강조는 감색 하나.
  • 상단은 보름달·감나무 가지·까치를 SVG로 그려 넣어 "추석" 맥락을 줍니다. 모바일에서는 그림을 우측 하단에 겹쳐 배너 높이를 줄였습니다. 급할 때 컨트롤이 먼저 보여야 하니까요.
  • select 대신 세그먼트 컨트롤. 탭 수가 적습니다.

남은 것

  • 좌표 인덱스는 정적 파일이라 새로 생긴 기관은 npm run build:coords로 갱신해야 합니다. 응답에 missingCoords로 누락 개수를 넣어 두었습니다.
  • 명절 데이터는 연휴 1~2주 전에 올라옵니다. 설 연휴에는 날짜 라벨만 바꾸면 그대로 씁니다.
  • 진료 시간은 바뀝니다. 사이트에도 적어 두었지만, 방문 전에는 전화가 답입니다.