네, 가능합니다. JUnit만으로도 스프링 부트 애플리케이션의 결제 로직에 대해 동시성(Concurrency) 테스트를 해볼 수 있는데, 대략 다음과 같은 패턴을 씁니다.
---
1. 테스트 구조 개요
1. 멀티스레드로 동시 요청 시뮬레이션
ExecutorService + CountDownLatch를 사용해서 여러 스레드가 “동시에” 결제 API(또는 서비스 메서드)를 호출하도록 함.
2. 실제 빈 주입 & 트랜잭션 관리
@SpringBootTest + @Autowired 로 PaymentService나 TestRestTemplate 등을 주입
테스트에서는 기본 @Transactional 롤백을 끄거나, 실제 커밋이 일어나도록 설정해야 진짜 동시성 이슈(예: 이중 청구 등)를 검증할 수 있음.
---
2. 샘플 코드
@SpringBootTest
@TestInstance(Lifecycle.PER_CLASS)
public class PaymentConcurrencyTest {
@Autowired
private PaymentService paymentService;
// 실제로 DB에 커밋이 일어나도록 롤백을 끔
@BeforeAll
void disableRollback(TestInfo info) {
// 만약 @Transactional 테스트가 기본 롤백이라면
// @Commit 어노테이션을 클래스나 메서드에 붙여도 됩니다.
}
@Test
@Commit // 실제 커밋
void 동시에_100명_결제_테스트() throws InterruptedException {
int threads = 100;
ExecutorService exec = Executors.newFixedThreadPool(threads);
CountDownLatch readyLatch = new CountDownLatch(threads);
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch doneLatch = new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
exec.submit(() -> {
readyLatch.countDown(); // 준비 완료
try {
startLatch.await(); // 시작 신호 대기
// --- 실제 호출: 결제 서비스 메서드 또는 REST API ---
paymentService.pay(orderId, amount);
// ---------------------------------------------
} catch (Exception e) {
e.printStackTrace();
} finally {
doneLatch.countDown();
}
});
}
// 모든 스레드가 준비되면
readyLatch.await();
// 동시 시작
startLatch.countDown();
// 끝날 때까지 대기
doneLatch.await();
exec.shutdown();
// 검증: 중복 결제 방지, 최종 결제 횟수는 1건인가?
long successCount = paymentRepository.countByOrderId(orderId);
assertEquals(1, successCount, "동시에 결제 요청이 와도 1건만 처리되어야 합니다.");
}
}
주요 포인트
1. CountDownLatch
readyLatch: 모든 워커 스레드가 준비됐음을 메인 스레드에 알림
startLatch: 메인 스레드가 동시에 “시작” 신호를 주면 스레드들이 동시에 실행
doneLatch: 모든 작업 완료를 메인 스레드가 대기
2. @Commit (or @Transactional(propagation = NOT_SUPPORTED))
테스트에 기본 붙는 롤백을 끄고 실제 DB 커밋을 해줘야 동시성 문제(예: DuplicateKeyException, 중복처리 등)를 확인할 수 있음.
3. 실제 환경과 최대한 유사하게
테스트용 인메모리 DB(H2) 대신 MySQL/PostgreSQL 컨테이너를 쓰면 더 현실적인 동시성 검증이 가능합니다.(Testcontainers 추천)
---
3. 추가 팁
트랜잭션 격리 레벨 설정을 바꿔보며(READ_COMMITTED, SERIALIZABLE 등) 어떻게 동작이 달라지는지 실험
낙관적 락 vs 비관적 락 전략을 각각 적용해보고, 동시성 시나리오에 맞는 방어 로직을 검증
REST API 계층까지 포함하려면 TestRestTemplate이나 WebTestClient로 스레드 내에서 HTTP 호출
---
이 구조를 활용하시면 JUnit 환경에서도 충분히 결제 로직의 동시성 이슈를 잡아보실 수 있습니다. 필요하시면 더 구체적인 예제(락 처리, 트랜잭션 설정 등)도 제공해드릴게요!
'4차산업혁명의 일꾼 > Spring' 카테고리의 다른 글
Java Spring 기반에서 Mock Server와 AOP를 결합한 테스트 전략 (2) | 2025.06.04 |
---|---|
테스트 전략에서 Mock Server와 **AOP (Aspect-Oriented Programming)**를 활용하는 방식 (0) | 2025.06.04 |
스프링7에서 도입되는것 (4) | 2025.05.28 |
JWT 생성과 시큐리티 필터 연계 (0) | 2024.12.26 |
스프링부트에서 React 연결 (0) | 2024.12.26 |