외부 API 호출 - jinkshower/galmanhae GitHub Wiki

외부 API 호출, 데이터를 처리-적재하는 과정에서 사용한 기술, 고민, 개선점을 다룹니다.

사용하는 데이터

API 호출에 사용한 기술

  • OpenFeign : Resttemplate는 이제 업데이트 되지 않을 예정이라 사용하지 않았고. Feign의 경우 선언적인 api 호출 코드를 만들 수 있음 → 가독성이 좋고 보일러플레이트 코드가 적어서 빠르게 적용할 수 있을거라 예상되어 사용.

  • WireMock : 실제 외부 API 호출은 제한량이 있어 테스트를 원하는 만큼 돌릴 수 없고 서비스에 영향을 줌. 객체로 직접 모킹할 경우 readtimeout, 다양한 예외 상황을 모킹하는게 힘들고 코드량이 많아짐. 이 문제점을 해결하기 위해 찾아본 결과 많은 레퍼런스를 가진 wiremock사용.

(OpenFeign 클라이언트 호출을 스프링의 @PostConstruct + CompletableFuture로 진행했을 때, 데드락이 생기는 이슈가 발생함) -> 해당 부분을 OpenFeign 오픈소스에 이슈로 등록하여 메인테이너들에게도 알림

개선 전 : CSV파일 + 한 쓰레드에서 호출

  • 서비스에 필요한 데이터는 서울 인구 밀집지역 115곳(이하 장소)의 이름, 코드, 위도, 경도, 기상청 계산에 따른 격자 xy좌표.
  • 해당 데이터들이 산재되어 있어 프로젝트에 쓸 하나의 CSV파일을 만드는 프로그램 작성 (Python을 이용, shp파일에서 장소이름, 코드, 위경도 추출, 이후 기상청 계산에 맞는 람베르트 좌표계 계산을 적용)
  • 이 CSV파일을 읽어 날씨, 인구 API를 for문으로 각각 호출, 받은 데이터로 Place 객체를 만들어냄

문제

  1. 장소 목록이 업데이트 되면 수동으로 매번 확인, 다시 프로그램을 돌려 csv파일을 업데이트해야함(배포++)
  2. 한 쓰레드에서 모든 API호출(약 300여번)을 동기식으로 하기에 다른 작업이 모두 대기해야 하고 하나의 작업이 실패하면 예외가 실행 쓰레드까지 올라와 이 후 작업은 시행되지 않음 -> 모든 작업이 성공해도 1~2분 소요

개선 후 : 파일 다운로드, 파싱 자동화 + 병렬 처리

  • 장소 목록 zip파일을 다운로드하고 shp파일을 파싱, db 적재 과정을 자동화하는 코드 작성. 애플리케이션 시작시 실행되게 하여 매 배포마다 최신의 장소 목록과 sync를 맞출 수 있게 함.
  • CompletableFuture을 사용하여 외부 API 호출을 병렬 처리, 각 작업이 별도의 스레드 풀에서 CPU 코어를 최대로 활용하게 변경하여 90%의 성능 향상 (1,2분 -> 7,8초)

현재 newCachedThreadPool을 사용하고 있는데, 블락된 스레드때문에 100여개의 쓰레드가 생성되고 있는 상황. 적은 수의 쓰레드로 API 호출할 방법 모색 중 -> 한정된 서버 자원을 낭비하지 않기 위해 시간적으로 손해는 보겠지만 20개의 고정된 스레드풀을 유지하는 것으로 변경. 성능 모니터링 이후 너무 시간이 오래걸리거나 스레드 유지시에도 메모리에 부담이 가면 호출 방법 자체를 바꿀 예정

  • Circuit Breaker적용 : 외부 API에 장애가 있을 경우 빠른 피드백으로 서버의 쓰레드 회전율을 유지하기 위해 적용