🚨상황 발단
사이드 프로젝트로 진행 중인 Tim.gg 서비스가 있다.
기능 중에 라이엇 외부 API를 활용하는 부분이 있다.
이 기능의 목적은 최근 10개의 매치 정보를 가져와서 승/패 횟수를 파악하는 것이다.
팀원의 기존코드로 작성했을 때 HttpClient로 작성되었었다.
작동하는데에는 문제가 없었지만 무언가 읽기 힘들다는 느낌이 들었었다.
보면 알겠지만 URL을 일일이 메서드 안에 정의해줘야 한다.
매번 반복되는 HttpRequest, HttpResponse도 코드의 양을 늘이는 주범이다.
코드의 또 하나 문제인 부분이
여기서 List<String>으로 매치정보를 가져오고 있다.
그 후 일일이 동기 방식으로 MatchSummaryDTO에 저장하고 있다.
10개의 정보를 하나씩 해결하니까 웹사이트에서 은근히 처리시간이 길다는 느낌을 받았다.
따라서 이 부분을 비동기식으로 바꾸어야겠다고 생각이 들었다.
정리하자면 내가 해야될 것
1. 중복되는 외부 API호출 부분 분리
2. 매치 정보 동기 방식 -> 비동기 방식
그래서 어떤 기술을 사용할까?
무엇보다 협업 프로젝트이다 보니 가독성이 좋아야했다.
찾아보니 HttpInterface라는 기술이 있었다.
이 기술은 연결할 URL을 인터페이스 단에서 선언하는 방식이다. (하지만 HttpClient와는 호환되지 않는다.)
흔히 사용하는 컨트롤러의 어노테이션 매핑과 비슷하다. (신세계... 정말 유지보수하기 쉬울 것 같다)
자세한 내용은 https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface
REST Clients :: Spring Framework
WebClient is a non-blocking, reactive client to perform HTTP requests. It was introduced in 5.0 and offers an alternative to the RestTemplate, with support for synchronous, asynchronous, and streaming scenarios. WebClient supports the following: Non-blocki
docs.spring.io
링크를 달아두겠다.
그리고 RestClient와 WebClient중에서 고민을 했었다.
WebClient는 리액티브 프로그래밍으로 동기/비동기 기능을 제공한다. 이 하나의 기술로 내게 필요한 조건(동기,비동기 모두 필요)을 만족한다는 게 매력적이었다.
하지만 단점이 있었다.
1. 외부 라이브러리 추가 (WebFlux) -> 사실 한 줄 딸각 추가하면 되니까 큰 문제는 안된다.
2. 추가적인 학습이 필요함(처음 접하는데 Mono, Flux와 같은 객체를 보고 겁을 먹었다..) -> 이게 크다.. 나는 단지 매치 정보 10개 비동기로 하고 싶을 뿐인데 여기에 더 많은 시간투자를 하는건 낭비라고 생각했다.
반면 RestClient는 나에게 딱 맞는 해결책이었다.
왜냐면 스프링 프레임워크에 포함된 HTTP 클라이언트로 추가적인 라이브러리가 필요없다.
그리고 설정이 굉장히 심플했다. WebClient와 마찬가지로 플루언트 API기법(메서드 체이닝) 방식이라 가독성이 좋았다.
그리고 비동기가 필요한 부분은 스트림의 CompletableFuture을 사용하기로 했다.
필요한 부분이 단순히 10개의 매치정보를 비동기로 하는 것이 목표이기 때문에 충분하다고 판단했다.
전후 비교 해보자
구분 | 동기 처리 | 비동기 처리 | 개선율 |
총 실행 시간 | 1,496ms | 435ms | 70% 단축 |
처리 방식 | 순차적 호출 | 병렬 호출 | - |
개별 API 응답 시간 | 129~168ms | 동일 | - |
동시 처리 개수 | 1개 | 10개 | 10배 증가 |
List<CompletableFuture<MatchSummaryDTO>> futures = matchIds.stream()
.map(matchId -> CompletableFuture.supplyAsync(() -> {
try {
DetailMatchInfoDTO detail = requestMatchInfo(matchId, puuid, runeData);
return //내용 생략
} catch (Exception e) {
log.error("매치 요약 정보 생성 중 오류 발생: {}", matchId, e);
return null;
}
}, matchInfoExecutor))
.toList();
사전에 매치 10개를 비동기로 하기위한 스레드풀은 빈으로 등록해놓았다.
이것을 활용해 CompletableFuture.supplyAsync()를 이용하여 비동기 처리를 진행완료하였다.
사실 10개라 그런가 엄청 큰 시간차이는 느끼지 못했다. (자세히보면, 스핀이 비동기가 좀 더 빨리 사라진다.👍)
하지만..!
유지보수 측면에서는 기존 url하드코딩 + HttpClient방식의 코드보다
RestClient + HttpInterface를 통해 정말 큰 성과를 얻었다고 볼 수 있다.
앞으로 다른 라이엇 API를 이용해서 확장할 때 큰 도움이 될 것 같다.
'티모지지' 카테고리의 다른 글
[Riot API] 비동기 전적조회 리팩토링으로 응답속도 개선하기 (2차) (0) | 2025.07.08 |
---|---|
TIMO.GG를 만들면서 (2) | 2025.07.07 |