Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Graph
- Selection Sorting
- 윤성우의 열혈 자료구조
- 알기쉬운 알고리즘
- 메모리구조
- C 언어 코딩 도장
- list 컬렉션
- Algorithm
- 윤성우 열혈자료구조
- C programming
- insertion sort
- 이스케이프 문자
- JSON
- Stack
- 이것이 자바다
- buffer
- datastructure
- stream
- Serialization
- 혼자 공부하는 C언어
- s
- coding test
- R
Archives
- Today
- Total
Engineering Note
[SW Engineering] 동시성 문제 테스트 환경 구축기 본문
동시성 문제 테스트 환경 구축기
문제 정의
상품 주문 로직을 개발하다 보면 여러 사용자가 동시에 주문을 하거나, 한 사용자가 네트워크 오류·중복 클릭 등으로 동일한 주문이 여러 번 요청될 수 있습니다. 이런 경우 재고가 음수로 내려가거나, 중복 주문이 발생하는 장애가 생길 수 있습니다.
하지만 JUnit 테스트에서 이런 동시 요청 상황을 시뮬레이션할 도구가 마땅치 않아, 직접 간단한 테스트 도구를 구현하기로 했습니다.
해결 방법
JUnit 환경에서 Thread Pool을 활용해 멀티쓰레딩 동시 요청을 구현했습니다.
ExecutorService executor = Executors.newFixedThreadPool(10);
- ExecutorService를 이용해 직접 new Thread()를 만들지 않고 쓰레드 풀 방식으로 작업을 실행했습니다.
- newFixedThreadPool(10)의 의미는 최대 10개의 쓰레드를 만들어 재사용한다는 뜻입니다.
- 예를 들어 100개의 주문 요청을 동시에 보내더라도, 내부적으로는 동시에 최대 10개 요청만 실행됩니다.
- 나머지 요청은 큐에 쌓였다가 쓰레드가 비면 순차적으로 실행됩니다.
즉, 100개의 요청을 보내면 10개의 쓰레드 동시에 실행 → 종료되면 대기 중 요청이 차례로 실행되는 방식으로 동시 요청 환경을 만들 수 있습니다.
동기화 문제
동시 요청을 보낸 뒤에는 반드시 **주문 개수와 재고 차감 결과를 검증(assert)**해야 합니다.
문제는, 작업 쓰레드가 모두 끝나기 전에 메인 테스트 쓰레드가 검증을 실행할 수 있다는 점입니다. 이렇게 되면 아직 처리가 덜 끝난 상태에서 결과를 확인하게 되어, 테스트가 올바르지 않게 됩니다.
이를 해결하기 위해 CountDownLatch를 사용했습니다.
CountDownLatch latch = new CountDownLatch(요청개수);
- 각 쓰레드가 작업을 마칠 때마다 latch.countDown()을 호출합니다.
- 메인 쓰레드느 latch.await()을 통해 모든 쓰레드가 완료될 때까지 대기합니다.
이렇게 하면 모든 요청이 끝난 시점에만 검증 로직이 실행되므로, 동시성 처리 여부를 정확히 확인할 수 있습니다.
결과 및 구현 코드
- 문제: 동시 요청 테스트 환경이 없어서 동시성 검증이 불가능했다.
- 해결: JUnit + ExecutorService + CountDownLatch로 간단한 테스트 도구를 구현했다.
- 효과: 원하는 만큼의 동시 요청을 보내고, 모든 요청이 끝난 시점에 정확히 검증할 수 있게 되었다.
private int 주문_동시요청(int 요청개수) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
CountDownLatch latch = new CountDownLatch(요청개수);
AtomicInteger exceptionCount = new AtomicInteger(0);
for (int i = 0; i < 요청개수; i++) {
executor.submit(() -> {
try {
orderService.placeOrder(userId, productId, 1);
} catch (OutOfStockException e) {
e.printStackTrace();
exceptionCount.incrementAndGet(); // 재고 부족 예외 카운트
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
latch.await();
executor.shutdown();
return exceptionCount.get();
}
'SW Engineering' 카테고리의 다른 글
[SW Engineering] JPQL join, fetch join (0) | 2025.09.23 |
---|---|
[SW Engineering] Spring Boot 배포 최적화 (0) | 2025.09.18 |
[SW Engineering] 페이지네이션 성능 측정 최적화(Offset vs Cursor Pagination 성능 비교 (평균 17배 개선)) (0) | 2025.09.08 |
[SW Engineering] 비동기 통신 직접 만들면서 익히기 (0) | 2025.07.21 |
동기(Synchronous) vs 비동기(Asynchronous) (2) | 2025.06.17 |
Comments