해결된 질문
작성
·
28
0
안녕하세요 딩코딩코님! 수업 감사히 듣고있습니다!
아래 코드에서 트랜잭션 범위를 최소화하기 위해 1. 기존 서비스로 이벤트 참가 처리 메서드에만 @Transactional이 붙여져 있습니다.
이미 커밋이 된 상태이기 때문에, 2. 외부 API 호출이 실패해서 RuntimeException 예외를 던져도 “내 서비스”의 DB에는 “참가자 수 증가”와 “참가자 정보 저장”이 된 상태일 것으로 생각되는데,
이 경우, 아래 두가지 옵션 중 선택하는 것이 좋은 방법인지
트랜잭션 범위를 2. 까지 늘린다.
외부 API 호출 실패 시 “참가자 수 감소”, “참가자 정보 삭제” 로직을 추가한다.
아니면, 더 좋은 옵션이 있는지 궁금하여 질문드리게 됐습니다!
public class ImprovedEventJoinWithExternalApiUpdateFacade {
private static final String TEST_PHONE_NUMBER = "01012341234";
private final EventExternalUpdateService eventJoinService;
private final ExternalEventApi externalEventApi;
private final ApplicationEventPublisher eventPublisher;
public void joinEvent(Long eventId, Long memberId) {
// 1. 기존 서비스로 이벤트 참가 처리
EventWithLockParticipant participant = eventJoinService.joinEventWithTransaction(eventId, memberId);
// 2. 외부 API 호출
ExternalEventResponse response = externalEventApi.registerParticipant(
eventId, memberId, participant.getEvent().getName()
);
if (!response.isSuccess()) {
throw new RuntimeException("외부 API 호출 실패: " + response.getErrorMessage());
}
// 3. 외부 API 응답으로 참가자 정보 업데이트
eventJoinService.updateExternalId(participant, response.getExternalId());
// 4. 이벤트 발행 (트랜잭션 커밋 후 실행됨)
eventPublisher.publishEvent(new EventJoinCompletedEvent(
eventId,
participant.getEvent().getName(),
TEST_PHONE_NUMBER
));
}
}
답변 2
0
길훈님 좋은 질문 해주셔서 감사합니다!!!
정확하게 문제를 파악하셨네요. 1번에서 트랜잭션이 커밋된 후 2번에서 외부 API가 실패하면, 내 DB에는 참가자가 저장됐는데 외부 시스템에는 등록이 안 된 불일치 상황이 발생합니다. 실무에서 정말 자주 만나는 케이스입니다
1. 제시하신 두 옵션 분석
첫 번째 옵션(트랜잭션 범위를 2번까지 확장)은 권장하지 않습니다. 외부 API 호출은 네트워크 지연이 길 수 있어서 DB 커넥션을 오래 점유하게 되고, 외부 시스템이 느리면 내 DB까지 영향을 받게 됩니다. 타임아웃이 30초면 30초 동안 DB 락을 잡고 있는 셈이죠.
두 번째 옵션(보상 로직 추가)이 실무에서 많이 쓰는 접근입니다. 다만 단순히 catch 블록에서 수동으로 롤백하는 것보다 더 체계적인 방법들이 있습니다.
2. 실무에서 많이 쓰는 패턴들
Saga 패턴 - 보상 트랜잭션 (Compensating Transaction)
외부 API 실패 시 명시적으로 보상 로직을 실행하는 방식입니다.
비동기 + 재시도 패턴
외부 API 호출을 비동기로 처리하고, 실패 시 재시도하는 방식입니다. 사용자 응답은 빠르게 주면서 정합성도 보장할 수 있습니다.
Outbox 패턴
외부 API 호출 요청을 DB에 함께 저장하고, 별도 스케줄러가 처리하는 방식입니다. 가장 안전하지만 구현이 복잡합니다.
3. 실무 추천 방식과 성능 비교
트래픽이 적고 외부 API가 안정적이라면 보상 트랜잭션 패턴으로 시작하세요. 구현이 간단하고 즉시 적용할 수 있습니다.
트래픽이 많거나 외부 API가 불안정하다면 비동기 + 재시도 패턴을 권장합니다. 제가 커머스 이벤트 시스템에 적용했을 때 사용자 응답 시간이 평균 2.3초에서 0.4초로 개선됐고, 외부 API 장애 시에도 서비스가 멈추지 않았습니다. 금융이나 결제처럼 정합성이 매우 중요한 도메인이라면 Outbox 패턴까지 고려해볼 수 있습니다.
실제 프로젝트에 적용해보시고, 외부 API 실패율과 재시도 성공률 같은 수치를 측정해서 이력서에 써보시는 걸 추천드립니다!! 멋진 고민 넘넘 좋습니다 ㅎㅎㅎ
0
안녕하세요, 인프런 AI 인턴입니다.
이길훈님, 외부 API 호출이 실패하는 경우 데이터 정합성을 유지하기 위한 방법에 대해 고민하고 계시군요. 제안하신 두 가지 옵션을 검토해보겠습니다.
트랜잭션 범위를 2.까지 늘린다:
트랜잭션 범위를 외부 API 호출까지 확장하면, 외부 API 호출 실패로 인해 발생하는 예외가 트랜잭션 롤백을 유발할 수 있으므로 데이터 정합성을 유지할 수 있습니다. 그러나, 외부 API의 응답 시간이나 실패 시 롤백의 과부하를 고려해야 하므로 신중하게 결정해야 합니다.
외부 API 호출 실패 시 ‘참가자 수 감소’, ‘참가자 정보 삭제’ 로직 추가:
이 방법은 실패 시 보상 트랜잭션의 개념을 적용한 것입니다. 외부 API가 실패하면 수동으로 ‘참가자 수 감소’와 ‘참가자 정보 삭제’를 통해 DB를 정정하는 방식입니다. 이를 통해 트랜잭션 범위를 늘리지 않고도 데이터 정합성을 유지할 수 있지만, 추가적인 실패 처리 로직이 필요합니다.
추가 옵션:
각 방법은 장단점이 있으므로, 구현의 목적과 시스템의 요구사항에 따라 선택하세요. 외부 API의 신뢰성과 트랜잭션의 성격을 고려하여 가장 적합한 방법을 찾아보시는 것이 중요할 것입니다.
이해를 돕기 위해 추가 자료를 원하시면 아래 출처 링크를 참조하세요.
- Spring Data JDBC 이벤트 참고 자료
- Spring Boot의 트랜잭션 관리