지난 프로젝트인 "QR 코드 기반 간편 주문 서비스"를 리팩토링하는 과정에서 배운 점에 대해서 기록하려고 한다.
간편 결제를 위해 KakaoPay 단건 결제 API를 사용하는 KakaoPayService 코드를 리팩토링 하는 과정에서 RestTemplate 객체를 생성하는 부분에 경고줄이 있다는 것을 확인하였다. 경고를 읽어보면 단순 중복된 코드라 쓰여있어 대수롭게 넘길려 했다.
public String kakaoPayReady(Long orderId) {
RestTemplate restTemplate = new RestTemplate();
Order order = orderService.findOrderById(orderId);
...
}
@Transactional
public KakaoPayApproval kakaoPayApprove(String pg_token, Long orderId) {
RestTemplate restTemplate = new RestTemplate();
Order order = orderService.findOrderById(orderId);
...
}
하지만, 매 메서드마다 RestTemplate 객체를 매번 생성해서 사용해야 할까라는 생각이 들었다.
RestTemplate에 대해서 간단하게 알아보자.
RestTemplate은 Spring 3.0에서 제공하는 HTTP 통신을 위한 클라이언트 라이브러리이다. RestTemplate은 간단한 방식으로 RESTful 웹 서비스와 통신할 수 있도록 지원한다. 따라서 RestTemplate을 사용하여 HTTP 요청을 보내고 응답을 받을 수 있다.
따라서, 외부 API와 통신하거나 마이크로서비스간 통신을 위해 사용되곤 한다.
RestTemplate이 제공하는 메서드는 https://www.baeldung.com/rest-template 에서 확인하길 바란다.
다시 돌아와서.
RestTemplate 객체의 반복적인 생성을 해결하기 위해, RestTemplate을 스프링 Bean 등록을 통해 싱글톤으로 관리하는 방식으로 코드를 수정하려 했다.
또한 RestTemplate은 기본적으로 Connection pool을 사용하지 않으며, 많은 요청을 하면 TIME_WAIT로 인해 자원이 부족하는 문제가 발생할 수 있다. 따라서 Connection Pool을 설정하도록 커스텀마이징할 필요도 있었다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public RestTemplate buildRestTemplate(){
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 연결시간초과, ms
factory.setReadTimeout(5000); // 읽기시간초과, ms
HttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(100) // 최대 오픈되는 커넥션 수
.setMaxConnPerRoute(5) // IP,포트 1쌍에 대해 수행 할 연결 수
.build();
factory.setHttpClient(httpClient);
return new RestTemplate(factory);
}
}
위와 같이 WebConfigurer를 구현한 설정 클래스를 만들어 RestTemplate을 Bean으로 등록하였다
- RestTemplate의 경우 아무 설정이 없을 경우, 해당 요청에 응답이 올 때까지 무한정 대기하는 것이 기본설정이다. 외부 api 서버 장애 발생 시 무한정 대기하는 것을 방지하기 위해 커넥션을 설정하는 것이 바람직하다.
- 또한 항상 tcp 커넥션을 만드는 과정보다는 커넥션 풀을 만들어서 사용하는 것이 성능상 좋기 때문에 Apachi httpComponents를 사용하여 커넥션 풀을 만들어서 사용하는 것이 바람직하다.
@Service
@RequiredArgsConstructor
@Slf4j
public class KaKaoPayService {
private final RestTemplate restTemplate;
...
...
public String kakaoPayReady(Long orderId) {
...
kakaoPayResponse = restTemplate.postForObject(
new URI(HOST + "/v1/payment/ready"),
body,
KakaoPayResponse.class
);
...
}
...
}
이후 KakaoPayService 코드는 다음과 같이 수정할 수 있다.
느낀점
이번 리팩토링 과정에서 RestTemplate의 단점을 극복하기 위해 커스텀마이징하는 방법에 대해서 배울 수 있었다. 또한 Connection에 있어서 Timeout과 최대 연결 수에 대해서 중요성을 한번 더 느낄 수 있었다. 올해 초 웍스 모바일에서 인턴 과제를 수행하면서, 실무 멘토님께서 항상 내가 만든 서버가 "TPS가 얼마나 되는지, Timeout은 어떻게 처리했는지" 등 서버의 성능과 안정성에 대한 질문을 많이 하셨다. 과제를 수행할 당시는 사실 기능을 개발하기 급급해서 그런 부분에 대해서는 많이 신경을 쓰지 못했다. 지금 생각하면 조금 아쉽지만, 앞으로는 기능 개발뿐만 아니라 서버의 성능과 안정성에 대해서 충분히 고민하고 설계하는 습관을 가지는 개발자가 되려고 노력해야겠다.
'개발 > Spring' 카테고리의 다른 글
JWT (2) 스프링에서 JWT 사용하기 (0) | 2023.05.31 |
---|---|
[Spring] 벌크 연산 (0) | 2023.05.15 |
[Spring] 간편 결제 기능 팩토리 클래스 적용하기 (0) | 2023.05.02 |
[Spring] 동일한 클래스 내에서 내부 메서드 호출시 @Transactional 적용 안되는 이슈 (0) | 2023.04.02 |
[Spring] OSIV 에 대해서 (0) | 2023.03.24 |