// CongressmanService
@Transactional
public NewsListDTO findNewsList(String encryptedCongressmanId, Pageable pageable) {
final Long congressmanId = aesUtil.decrypt(encryptedCongressmanId);
final Congressman congressman = findById(congressmanId);
// 외부 api 호출
final String response = getApiResponse(buildUrlStringForNews(pageable, congressman));
return parseNewsResponse(response, pageable.getPageSize());
}
CongressmanService의 메서드인 findNewsList에서 국회 api를 호출하는 예시이다.
트랜잭션 내부에서 외부 api를 호출하고 있다.
하지만 트랜잭션 내부에서 외부 api를 호출하면 이러한 문제가 발생할 수 있다.
- Tomcat Thread Pool 고갈: Tomcat의 최대 쓰레드 수가 100개로 설정되어 있다면, 60초 동안 응답을 기다리는 동안 대부분의 쓰레드가 차단되어 추가적인 요청을 처리할 수 없게 만든다.
- DB Connection Pool 고갈: HikariCP의 커넥션 풀 갯수가 50개로 설정되어 있다면, 트랜잭션이 유지되는 동안 커넥션이 반환되지 않아 커넥션 풀이 고갈될 수 있으며 장애로 이어진다.
따라서 외부 api 호출은 CongressmanApiClient로 이동해준다.
// CongressmanApiClient
@Transactional(propagation = Propagation.NEVER)
public String getApiResponse(final Pageable pageable, final String baseUrl, final String apiKey, final Map<String, String> params) {
final String uriString = buildUrlString(pageable, baseUrl, apiKey, params);
// 외부 api 호출
return webClient.get()
.uri(uriString)
.retrieve()
.bodyToMono(String.class)
.doOnNext(this::logResponse)
.block();
}
이 때 @Transactional(propagation = Propagation.NEVER 를 달아주면 트랜잭션이 존재하는 상황에서 getApiResponse를 호출할 경우 IllegalTransactionStateException 가 발생한다.
따라서 getApiResponse가 트랜잭션 외부에서 호출됨을 보장할 수 있다.
// CongressmanApiService
public NewsListDTO findNewsList(final String encryptedCongressmanId, final Pageable pageable) {
// congressmanService : 트랜잭션
Congressman congressman = congressmanService.getCongressman(encryptedCongressmanId);
Map<String, String> params = Map.of(COMP_MAIN_TITLE, congressman.getName());
// congressmanApiClient : 트랜잭션 없이 외부 api 호출
JsonNode jsonNode = fetchApiResponse(pageable, API_NEWS_URL, newsApikey, params);
int totalPage = calculateTotalPages(extractTotalCount(jsonNode, NEWS_API_PATH), pageable.getPageSize());
return NewsListDTO.of(getContent(jsonNode, NEWS_API_PATH), totalPage);
}
CongressmanApiService에서 CongressmanService와 CongressmanApiClient에 의존해 트랜잭션 내부의 작업, 외부의 api 호출을 각각 해준다. 컨트롤러에서는 CongressmanApiService 에 의존하며 외부 api가 호출 로직이 필요한 경우에는 CongressmanApiService 의 메서드를 호출하면 된다. 😁
출처
이거 모르고 외부 API 호출하면 큰일납니다.::LEAPHOP TECH BLOG
이번 포스팅에서는 Propagation.NEVER를 활용해 트랜잭션 관리에서 발생할 수 있는 치명적인 실수를 예방하고, 외부 API 호출을 안전하게 처리하는 방법을 소개합니다.
blog.leaphop.co.kr
'스프링' 카테고리의 다른 글
| Pageable 커스텀 예외처리 (0) | 2025.06.07 |
|---|---|
| 외부 API 예외 처리 전략: 내 서비스와 매핑하는 방법 (0) | 2025.02.03 |
| spring 요청 Validation 검증, 예외 처리 (0) | 2025.01.16 |
| 테스트 시 @Value 사용 (0) | 2024.11.04 |
| dto를 사용해야 하는 이유 (0) | 2024.09.01 |
댓글