Node.js로 행정구역 데이터(시/군/구) 좌표 자동 변환하는 법

hannah·2026년 1월 13일

JavaScript

목록 보기
107/108
post-thumbnail

최근 개발 중 "경기도 수원시 장안구"와 같은 행정구역 데이터를 기반으로 날씨 정보를 보여줘야 하는 일이 있었다. 이를 위해서는 각 행정구역의 위도(lat), 경도(lon) 좌표가 필요했다.

수작업으로 2만 개가 넘는 데이터를 입력할 순 없으니 OpenWeatherMap Geo API와 Node.js를 이용해 자동 변환 스크립트를 짜기로 했다.

먼저 기본적으로 API를 호출하는 함수는 아래와 같이 작성했다.

// axios 설정: 타임아웃과 keepAlive 설정
const axiosInstance = axios.create({
  httpsAgent: new https.Agent({ keepAlive: true }),
  timeout: 10000,
});

// API 호출 로직 분리
const fetchGeo = async (query) => {
  return await axiosInstance.get(
    "https://api.openweathermap.org/geo/1.0/direct",
    {
      params: { q: `${query},KR`, limit: 1, appid: API_KEY },
    }
  );
};

🤯 문제 발생: 도 단위 검색의 실패

처음에는 단순하게 전체 주소 문자열(예: "경기도 수원시")을 API에 던지면 알아서 찾아줄 거라 생각했다. 하지만 테스트를 돌려보니 '시/군/구' 단위는 잘 찾아지는데, 유독 '도' 단위(경기도, 경상북도, 강원특별자치도 등)에서 검색 결과가 없다는 응답([])이 돌아왔다.

// 초기 시도: 그냥 검색어 던지기
let response = await fetchGeo("강원특별자치도"); 
// 결과: [] (좌표 못 찾음 -> null 반환)

💡 해결 전략 1: 이름을 단순하게 만들자 (정규식)

API가 "강원특별자치도"처럼 긴 한국의 행정구역 명칭을 완벽히 인식하지 못하는 것 같았다. 그래서 문자열을 쪼개고 다듬는 작업을 추가했다.

  1. 접미사 제거: '시', '군', '구'를 떼어내고 검색.
  2. 특별자치도 제거: '강원특별자치도' -> '강원'으로 변경하여 검색.
  3. '도' 제거: '경기도' -> '경기'로 변경하여 검색.

코드로는 정규식(replace)을 활용해 아래와 같이 재시도 로직을 구성했다.

// 1. '특별자치도'를 제거하고 검색 (예: 강원특별자치도 -> 강원)
const withoutSpecialDo = provinceName.replace(/특별자치도$/, "");
if (withoutSpecialDo !== provinceName && withoutSpecialDo.length >= 2) {
  await sleep(200); // API Rate Limit 고려
  response = await fetchGeo(withoutSpecialDo);
}

// 2. 그래도 없으면 '도'를 제거하고 검색 (예: 경기도 -> 경기)
if (!response.data.length) {
  const withoutDo = provinceName.replace(/도$/, "");
  if (withoutDo !== provinceName && withoutDo.length >= 2) {
    await sleep(200);
    response = await fetchGeo(withoutDo);
  }
}

이렇게 하니 꽤 많은 지역이 해결되었지만, 여전히 일부 지역은 좌표를 뱉어내지 못했다.

💡 해결 전략 2: 대표 도시 매핑 (Fallback)

가장 확실한 방법은 "도 단위 검색이 실패하면, 그 도의 도청 소재지(대표 도시) 좌표를 가져오는 것"이었다. 어차피 날씨 정보는 도 전체의 대략적인 위치만 있으면 되기 때문이다.

먼저 각 도의 대표 도시를 매핑한 객체를 만들었다.

// 각 도의 대표 도시 매핑 (도 단위 검색 실패 시 Fallback용)
const provinceCapitalMap = {
  경기도: "수원",
  강원특별자치도: "춘천",
  강원도: "춘천",
  경상북도: "대구",
  경상남도: "창원",
  충청북도: "청주",
  충청남도: "천안",
  전라북도: "전주",
  전라남도: "무안",
  제주특별자치도: "제주",
  제주도: "제주",
};

그리고 검색 로직의 마지막 단계에 이 매핑 테이블을 활용한 Fallback 로직을 추가했다.

// 3. 정규식 변환으로도 못 찾으면, 대표 도시와 함께 검색 (예: "경기도 수원")
if (!response.data.length && provinceCapitalMap[provinceName]) {
  const capital = provinceCapitalMap[provinceName];
  await sleep(200);
  response = await fetchGeo(`${provinceName} ${capital}`);
}

// 4. 최후의 수단: 대표 도시 이름만으로 검색 (예: "수원")
if (!response.data.length && provinceCapitalMap[provinceName]) {
  const capital = provinceCapitalMap[provinceName];
  await sleep(200);
  response = await fetchGeo(capital);
}

이 전략을 도입하자 "경기도", "강원특별자치도" 등 실패하던 모든 도 단위 지역의 좌표 변환이 성공적으로 이루어졌다!🎉


📝 마무리

외부 API를 사용할 때는 데이터가 내 입맛에 딱 맞게 들어오지 않는다. 결국 데이터 전처리와 적절한 예외 처리 전략을 세우는 것이 개발자의 몫임을 다시 한번 느꼈다.

이제 약 2만 개의 행정구역 데이터가 깔끔하게 좌표를 달고 DB에 들어갈 준비를 마쳤다.

Tip: API Key는 코드에 노출하지 말고 꼭 .env 파일로 분리하여 관리하는 것을 잊지 말자!

0개의 댓글