이전 글에서 "서울시 일반음식점 인허가 정보" API를 사용하게 된 이유에 대해 작성한 데에 이어, 이번에는 해당 API를 사용하며 겪었던 어려움과 그 해결과정을 적어보고자 한다.
"서울시 일반음식점 인허가 정보" API를 사용하며 가장 먼저 문제가 됐던 부분은 서울시의 모든 음식점 정보를 DB에 저장하는 과정에 많은 시간이 소요된다는 것이였다.
DB에 저장하는데 소요되는 시간을 측정해본 결과 약 12분 정도였고, 이는 사용자가 많이 없는 새벽 시간에 음식점 정보를 업데이트한다 라는 조건이 있더라도 사용자 경험을 저해하는 요소가 될 수 있다고 생각하여 개선이 필요하다고 판단했다.
초기에 작성했던 코드는 정말 단순하게 api를 호출하여 1000개의 데이터를 받아오고 그 데이터를 DB에 저장하는 과정을 마지막 데이터가 올 때까지 반복하는 형태였다.
이러한 초기 코드에서 시간을 지연시킬만한 부분은 2가지라고 생각했다.
첫째, 하나의 api 호출에 따른 작업이 모두 완료된 후에 다음 api를 호출하는 형태이기 때문에 이로 인한 api 호출 간의 시간 지연 발생.
둘째, 데이터를 약 1000개정도의 작은 단위로 자주 저장하기 때문에, DB의 쓰기 작업을 위한 각 트랜잭션의 잦은 커밋 및 네트워크 오버헤드로 인한 시간 지연 발생.
이러한 문제를 개선하기 위해서 선택한 방법은 api 호출 과정 비동기 처리와 bulk insert 였다.
첫번째 문제를 개선하기 위해 api 호출 과정을 Typescript(Javascript)의 핵심 특징 중 하나인 비동기로 처리하여 각 작업간 네트워크 요청의 완료를 기다리는 동안에 다른 작업을 수행할 수 있도록 했다.
두번째 문제를 개선하기 위해서는 각 api 요청마다 DB에 저장하는 대신 15개의 api 요청이 완료될 때까지 기다린 후 해당 요청들에 대한 데이터를 한번에 DB에 저장하는 bulk insert 방식을 활용했다.
여기서 15라는 숫자는, 처음엔 더 많은 데이터를 한번에 저장하는 것을 시도했지만 에러가 발생했고 이에 대해 검색해본 결과 너무 많은 양의 데이터 저장은 DB서버의 메모리 부족이나 자체적인 제한으로 인해 실패할 수 있다는 내용을 확인하여 적절한 값을 휴리스틱하게 찾아낸 것이다.
개선점을 적용한 최종 코드.
앞서 설명한 것처럼 초기에 작성한 코드를 개선하여 다시 저장 속도를 측정한 결과, 기존 12분에서 1분 20초까지 단축되어 약 900%의 속도 개선을 할 수 있었다.
글을 작성 하다보니, DB에 저장하는 부분 역시 비동기로 처리한다면 조금 더 저장속도를 개선할 수 있지 않을까라는 생각이 든다. 이 부분은 추후에 개선해 볼 예정이다.
"서울시 일반음식점 인허가 정보" API 사용 중 문제가 됐던 다른 하나는 좌표계 변환 과정에서 발생했다.
서비스에서 사용하는 네이버 지도의 경우 GPS가 사용하는 WGS84 좌표계를 사용하는 반면, 사용하려는 api는 중부원점 좌표계를 사용했기 때문에 이 두가지 좌표계간의 변환이 필요했다.
이때, 이 변환과정을 안드로이드(클라이언트) 측에서 처리하는 것 보다는 서버측에서 변환하여 보내주는 것이 좋다고 생각했고, 서버측에서 변환된 좌표계를 보내줄거라면 아예 위치 정보를 저장할 때부터 WGS84 좌표계로 변환하여 저장하는 것이 더 효율적이라고 판단했다.
좌표계 변환에는 Node.js 환경에서 사용할 수 있는 proj4 패키지를 사용했다. 처음 시도했던 코드를 간단하게 표현하면 아래와 같다.
const tm2097 = "+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs";
const wgs84 = "EPSG:4326";
const response = await axios.get(apiUrl);
response.data.LOCALDATA_072404.row.forEach((element) => {
const tmX = parseFloat(element.X);
const tmY = parseFloat(element.Y);
const [lon, lat] = proj4(tm2097, wgs84, [tmX, tmY]);
}
api 호출을 통해 받아온 데이터의 각 위치 정보를 위경도값으로 변환하는 코드
해당 코드에서 사용한 "tm2097"이라는 변수에 저장된 문자열은 Proj4 문자열로, 중부원점 좌표계를 WGS84 좌표계로 변환하는 방법을 검색하여 찾아낸 값이었다.
위의 코드를 통해 위치 정보를 변환하여 DB에 저장한 후, postgreSQL의 GUI기반 관리도구인 pgAdmin4를 활용하여 각 음식점들의 위치정보를 Geometry viewer를 통해 지도에 표시하여 확인해보았다.
약 12만개의 음식점 하나하나가 파란점으로 지도에 표시된 결과
위와 같이, 음식점들의 분포되어 있는 전체적인 형태를 보면 서울의 모양을 띄고 있지만, 위치가 충청도 천안 인근으로 표시되는 것을 확인할 수 있었다.
전체적인 결과를 보며 각 음식점들의 위치 정보에는 이상이 없고 변환과정에서 오차가 발생한 것이라고 생각했고, 처음에는 Proj4 문자열을 수정하여 정확한 위치로 옮겨보려고 했지만 약간의 변화에도 너무나 크게 변화하여 이 방법은 현실적으로 어려울 것이라고 판단했다.
이 문제를 해결하기 위해 좌표변환과 관련된 여러 문서를 확인하던 중, 각 좌표계에는 고유 EPSG 코드가 할당되어 있다는 사실을 알게 되었고 처음 사용했던 Proj4 문자열은 현재 국토지리정보원 표준인 EPSG:5186에 대한 정의라는 것을 확인했다.
이후 "서울시 일반음식점 인허가 정보" api의 좌표계를 확인해본 결과, EPSG:2097 좌표계임을 확인했고 이로 인한 변환과정의 오차가 있었을 것이라고 판단하여 "tm2097" 변수에 EPSG:2097 좌표계에 대한 Proj4 문자열을 저장하여 다시 변환해보았다.

결과는 성공적(처음엔 그렇게 생각했다...)이었다. 음식점들의 위치가 정상적으로 서울에 표시되는 것을 보며 들뜬 마음으로 각 음식점들의 상세 위치를 확인했고, 그 결과에 실망할 수 밖에 없었다.
| 네이버 지도 |
|---|
![]() |
| Geometry viewer |
|---|
![]() |
사진에서 볼 수 있는 것처럼 실제 네이버 지도에서 검색한 위치와 꽤나 큰 차이의 오차가 있었다. 다른사람이 등록한 맛집을 보고 찾아가야하는 우리의 서비스 특성상, 이러한 오차는 너무나도 치명적이라고 생각했고 정확성을 높일 필요가 있었다.
이러한 문제를 해결할 방법을 모색하던 와중, OSGeo 한국지부에서 작성한 글에서 보정된 중부원점 좌표계(EPSG:5174)에 대해 확인했고 해당 문자열을 사용해보기로 결정했다.

이번에는 정말로 성공적이였다. 위 사진에서 볼 수 있는 것처럼 네이버 지도와 비교했을 때도 큰 오차가 없을 정도로 정밀하게 위치정보가 저장되었다!!!
이것을 마지막으로 서울시 전체 음식점을 자체 DB에 저장하는 작업을 마무리하게 되었다. 그 과정에서 수많은 문제가 있었지만 동료와 함께 고민하고 해결해 나갔기 때문에 만족스럽게 그리고 성공적으로 마무리 할 수 있었던 것 같다.