[Spring] 외부 API 호출과 트랜잭션 분리

윤성철·2024년 6월 18일

Back-End

목록 보기
6/22
post-thumbnail

서론

사내 시스템을 개발 중, 외부 API 호출 ➡️ DB save 요청을 날리는 로직에 대해 궁금증이 생겼다. ❓

첫번째 고민은, 외부 api에 의존성이 높은 save 요청인데, 트랜잭션 분리를 하는게 맞을까하는 고민이었다.
두번째는 외부 api는 우리 서버단에서 핸들링하는 범위 밖의 영역인데, 트랜잭션 롤백이 일어났을 때, 데이터 정합성 이슈는 어떻게 해결할 것인가..

위 2가지 사안으로 긴 생각의 시간을 가졌다.

본론

외부 API 호출과 DB 관련 로직을 분리하는 방법으로 적용했다.

그이유는, 데이터 정합성 이슈를 해결하기 위해 위 두 로직을 한 트랜잭션 단위로 묶으면, 트랜잭션을 통해 맺고 있던 커넥션이 쭉 유지된 채 있을 것이다.

이러한 점이 문제가 되는 이유는 커넥션 풀을 사용하는데, 기본적으로 스프링은 10개의 커넥션을 가진 풀을 제공하기 때문이다.

만약 커넥션 풀이 10개고 사용자 10명이 해당 service를 의존하는 endpoint를 호출한다면 11번째 사용자는 커넥션을 획득하지 못하여 대기하다가 timeout error

참고로, 일반적으로 스프링에서 생성해주는 connection pool은 10개 정도

많은 레퍼런스를 찾아본 결과, 트랜잭션이 불필요한 로직은 상위 계층으로 빼고, 트랜잭션이 수행되는 비즈니스 로직은 하위 계층에서 transcation처리를 하는 방법을 도출할 수 있었다.

@Component
@RequiredArgsConstructor
Public class AllocateRegister {
	//예시 코드
	private final AllocateService allocateservice;
    private final APIClient apiclient;
    
   	public void allocate(ReqDto request){
      response = apiclient.call(reqeust)	//외부 api 호출  
      AllocateService.save(response);
    }
}

AllocateRegister 상위 클래스를 만들어서 서비스 계층 의존성 주입,
트랜잭션 로직은 AllocateService의 save메소드에서 실행된다.

@Service
@RequiredArgsConstructor
public class AllocateService {
	private final AllocateRepository allcoateRepository;
    
    @Transactional
    public save(Request request) {
   		allocateRepository.save(request);
    }
}

상위 클래스에서 외부 리소스 요청 처리하고, 하위 계층에서 db save로직을 수행한다.

  • connection pool이 늘어지는 이슈 개선
  • 트랜잭션 처리가 외부 리소스에 의존적이기 때문에, status code가 200이 떨어졌을 때만 로직 수행으로 데이터 정합성도 이전보다 개선되었다.

-참고 : https://tecoble.techcourse.co.kr/post/2022-09-20-external-in-transaction/

profile
내 기억보단 내가 작성한 기록을 보자..

0개의 댓글