모래블로그

[스프링] Spring WebClient 본문

Framework/Spring

[스프링] Spring WebClient

별모래 2023. 11. 22. 17:18
728x90

WebClient를 쓰게 된 이유

교통약자지도를 만들면서 RestTemplate를 썼었는데, 이는 동기 방식이다.

승강기 유무를 알려주는 것이 우리 교통약자지도의 핵심 중 하나인데, 하나를 검색하면 8-10개의 검색 결과를 반환하고 거기에 승강기 유무까지 같이 반환하다보니 시간이 너무 오래 걸리는 문제가 발생했다.

각각 승강기 유무를 가져와야해서 호출하여 결과를 가져오는데 오래 걸렸다. 게다가 동기 방식이다 보니 결과를 가져올 때까지 계속 기다리게 되는 것이다. 그래서 처음에 무려 5분이 걸렸다..

거기에 충격을 받은 나는 팀원에게 이건 도저히 못쓸 것 같다. 다른 방법을 알아보자고 해서 그렇게 지인에게 물어봄 + 구글링을 통해 WebClient를 알게 되었다.

 

WebClient

WebClient란 스프링 5.0에서 추가된 인터페이스이고, 스프링 5.0 이전에는 비동기 클라이언트로 AsyncRestTemplate을 사용했었다. 하지만 스프링 5.0 이후부터는 WebClient를 사용할 것을 권장하고 있다고 한다.

 

WebClient의 특징

  • 싱글 스레드 방식 사용
  • Non-Blocking 방식을 사용
  • JSON, XML을 쉽게 응답받는다.

 

RestTemplate VS WebClient

 

Non-Blocking 이란?

시스템을 호출한 직후에 프로그램으로 제어가 다시 돌아와서 시스템 호출의 종료를 기다리지 않고 다음 동작을 진행한다. 호출한 시스템의 동작을 기다리지 않고 동시에 다른 작업을 진행할 수 있어서 작업의 속도가 빨라진다는 장점이 있다.

 

그래서 이를 이용하여 WebClient를 이용한 코드로 바꿨다.

 

 

아래는 바꾼 코드이다.

    public List<FindDto> findAddressByTmapAPI(String FindName, double longitude, double latitude) { // 티맵 api (통합검색(명칭검색))

        DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(tmap_url);
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);

        WebClient wc = WebClient.builder().uriBuilderFactory(factory).baseUrl(tmap_url).build();

        String encodedName = URLEncoder.encode(FindName, "UTF-8");

        ResponseEntity<String> result = wc.get()
                .uri(uriBuilder -> uriBuilder.path("/tmap/pois")
                        .queryParam("version", 1) //버전
                        .queryParam("searchKeyword", encodedName) // 검색 키워드
                        .queryParam("count", 10) // 개수
                        .queryParam("appKey", tmap_apiKey) // 서비스키
                        .queryParam("searchtypCd", "A") // 거리순, 정확도순 검색(거리순 : R, 정확도순 : A)
                        .queryParam("radius", 0) // 반경( 0: 전국반경)
                        .queryParam("centerLon", longitude) // 중심 좌표의 경도 좌표
                        .queryParam("centerLat", latitude) // 중심 좌표의 위도 좌표
                        .build())

                .retrieve() //response 불러옴
                .toEntity(String.class)
                .block();
                
                ...
                
}

 

사실 block이 들어있어서 결국 이것도 동기 방식을 쓰긴 한다.
하지만 아예 비동기로 하자니 데이터를 받기도 전에 결과가 나와버려서 결국 아무것도 받지 못해서 썼다. 그래도 확실히 RestTemplate를 썼을 때 보다 속도가 줄었다.

5분에서 50초 정도로 줄었다..!

하지만 누가 검색하는데 50초나 걸리는걸 쓰겠어..?

그래서 팀장이 병렬처리 방식을 쓰는 형태로 바꿨고,

5분 -> 50초 -> 20초 -> 6-8초까지 줄이는 기적을 맛봤다.

현재는 6-8초 정도에 로딩창 까지 넣어놔서 정말 많이 빨라졌다:)

 

여담

처음에 5분이 걸렸을 때 너무 충격을 받아서..

long start2 = System.currentTimeMillis();
long end2 = System.currentTimeMillis();
System.out.println("용의자 시간 : " + (end2 - start2) / 1000.0);

이를 통해 오래 걸리는 시간을 재봤는데, 아무리봐도 범인은 엘리베이터 API 였고
이 api 자체가 원래부터 느린 api 였다.

엘리베이터 호출 함수만 지우면 거의 1초 컷- 으로 결과가 나오고...ㅜㅜ
그래서 엘리베이터 함수만 주석처리하고 테스트하고 그랬다..

그래도 정말 많이 속도가 향상되었고, 이를 위해 힘써준 우리 팀장님 너무 멋지다..!! 최고!!👍👍

 

WebClient는 내가 예전에 RestTemplate와 차이점이 뭔지 찾아보면서 정리해뒀던거라 어딜 참고 했는지 모르겠다..

 

728x90