
최근 개발 중 "경기도 수원시 장안구"와 같은 행정구역 데이터를 기반으로 날씨 정보를 보여줘야 하는 일이 있었다. 이를 위해서는 각 행정구역의 위도(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 반환)
API가 "강원특별자치도"처럼 긴 한국의 행정구역 명칭을 완벽히 인식하지 못하는 것 같았다. 그래서 문자열을 쪼개고 다듬는 작업을 추가했다.
코드로는 정규식(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);
}
}
이렇게 하니 꽤 많은 지역이 해결되었지만, 여전히 일부 지역은 좌표를 뱉어내지 못했다.
가장 확실한 방법은 "도 단위 검색이 실패하면, 그 도의 도청 소재지(대표 도시) 좌표를 가져오는 것"이었다. 어차피 날씨 정보는 도 전체의 대략적인 위치만 있으면 되기 때문이다.
먼저 각 도의 대표 도시를 매핑한 객체를 만들었다.
// 각 도의 대표 도시 매핑 (도 단위 검색 실패 시 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파일로 분리하여 관리하는 것을 잊지 말자!