👥🚨 좋아요 동시성 테스트에서 교착상태가 발생한 이유와 해결 방법

2026. 2. 14. 18:34·프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트

문제 상황

“여러 명이 동시에 좋아요를 누르는 상황”을 검증하기 위해
ExecutorService + CountDownLatch를 사용해 동시성 테스트를 작성했다.

하지만 테스트를 실행하면:

  • Article은 생성됨
  • like는 0
  • 테스트가 멈춘 것처럼 보임
  • 교착 상태 발생

처음에는 DB 문제처럼 보였지만, 실제 원인은 테스트 코드 구조 자체의 교착이었다.


1️⃣ 기존 테스트 구조

핵심 부분은 다음과 같았다.

ExecutorService pool = Executors.newFixedThreadPool(32);
CountDownLatch ready = new CountDownLatch(threads); // threads = 100
CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(threads);

각 작업 흐름:

ready.countDown();
start.await(); // 여기서 대기
likeService.toggleLike(...);
done.countDown();

그리고 메인 스레드:

ready.await();     // 100개가 다 준비될 때까지 대기
start.countDown(); // 시작 신호
done.await();

2️⃣ 왜 교착상태가 발생했는가?

핵심 원인은 스레드풀 크기(32)와 readyLatch(100)의 조합이다.

실행 흐름을 보면

  1. 스레드풀은 32개뿐이다.
  2. 처음 32개 작업만 실행된다.
  3. 그 32개는 start.await()에서 대기한다.
  4. 이 32개가 스레드를 전부 점유한다.
  5. 나머지 68개 작업은 큐에서 실행되지 못한다.
  6. 그런데 메인 스레드는 ready.await()로 100개가 모두 준비되길 기다린다.

하지만 68개는 실행 자체를 못 하므로
ready.countDown()이 절대 100까지 내려가지 않는다.

즉,

ready가 100이 되어야 start를 풀 수 있는데
start를 풀기 전에는 나머지 작업이 실행될 수 없다.

완전한 교착 구조다.


3️⃣ 해결 방법: readyLatch 제거 (현실적인 동시성 테스트)

“100개가 완전히 같은 순간에 시작”을 강제할 필요가 없다면
readyLatch는 제거하는 것이 가장 안전하다.

수정된 테스트:

@Test
void manyUsers_likeConcurrently() throws Exception {
    int threads = userEmails.size();
    ExecutorService pool = Executors.newFixedThreadPool(32);

    CountDownLatch start = new CountDownLatch(1);
    CountDownLatch done = new CountDownLatch(threads);

    for (String email : userEmails) {
        pool.submit(() -> {
            try {
                start.await();
                likeService.toggleLike(articleId, email);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                done.countDown();
            }
        });
    }

    start.countDown();
    boolean finished = done.await(30, TimeUnit.SECONDS);
    pool.shutdown();

    assertThat(finished).isTrue();

    em.clear();
    long likeCount = blogRepository.getLikeCount(articleId);
    long likeRows = likeRepository.countByArticleId(articleId);

    assertThat(likeCount).isEqualTo(threads);
    assertThat(likeRows).isEqualTo(threads);
}

4️⃣ 이게 동시성 테스트가 맞을까?

맞다.

다만 중요한 차이가 있다.

기존 목표

  • 100명이 “완전히 같은 순간”에 시작

실제 동작

  • 스레드풀 32개
  • 동시에 실행 가능한 작업은 최대 32개
  • 나머지는 큐에서 대기

즉,

동시성은 존재하지만, 강도는 32로 제한된 테스트


5️⃣ 그런데 오히려 이 방식이 더 현실적이다

실제 서버 환경을 생각해보자.

  • 톰캣 스레드 풀
  • DB 커넥션 풀
  • 트랜잭션 처리

현실에서는:

100명이 동시에 클릭해도
DB에 100개 쿼리가 “완전히 같은 순간”에 들어가지는 않는다.

항상 일정 수만 동시에 처리되고, 나머지는 큐잉된다.

따라서

  • 32개 동시 실행
  • 나머지 대기 후 순차 처리

이 구조는 오히려 현실 서버 환경을 더 잘 흉내 낸 테스트다.


6️⃣ 동시성 검증 관점에서 충분한가?

좋아요 토글과 같은 경우 검증 목표는 보통 다음이다.

  • 유실 업데이트가 없는가?
  • likeCount와 실제 like row 개수가 일치하는가?
  • 유니크 충돌이 안전하게 처리되는가?
  • 카운트가 음수로 내려가지 않는가?

이 목적에는

32개 동시 실행 테스트만으로도 충분히 의미 있다.

극단적인 “100개 완전 동시 시작”은
실무에서는 거의 필요하지 않다.

7️⃣ 최종 정리

교착 원인

  • 스레드풀 32개
  • readyLatch 100
  • start.await()에서 스레드 점유
  • ready.await()는 100까지 기다림
  • 실행 불가능 구조 → 교착

해결

  • readyLatch 제거
  • startLatch + doneLatch만 사용
  • 타임아웃을 둬서 무한 대기 방지

핵심 교훈

동시성 테스트는 “완전한 동시 시작”을 만드는 것이 목적이 아니라
여러 스레드가 동시에 같은 자원을 건드리는 상황을 재현하는 것이 목적이다.

'프로젝트 > 스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글

👥💡 좋아요(Like) 기능 설계: 상태, 집계, 동시성까지 고려한 구현 정리  (0) 2026.02.14
👥🚨 같은 유저가 좋아요를 동시에 연타할 때 동시성 테스트가 깨지는 이유  (0) 2026.02.14
👥🚨 좋아요 토글을 빠르게 연타하면 깨지는 이유와 해결 방법 (프론트 + 백엔드 동시성 정리)  (0) 2026.02.14
👥💡 원자적 방법이란? 벌크성 내용이랑 무슨 차이가 있는지?  (0) 2026.02.14
👥💡 조회수 동시성 문제 해결(원자적)  (0) 2026.02.14
'프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글
  • 👥💡 좋아요(Like) 기능 설계: 상태, 집계, 동시성까지 고려한 구현 정리
  • 👥🚨 같은 유저가 좋아요를 동시에 연타할 때 동시성 테스트가 깨지는 이유
  • 👥🚨 좋아요 토글을 빠르게 연타하면 깨지는 이유와 해결 방법 (프론트 + 백엔드 동시성 정리)
  • 👥💡 원자적 방법이란? 벌크성 내용이랑 무슨 차이가 있는지?
hak0622
hak0622
개발하면서 “이게 뭐지?”라는 순간마다 궁금한 점을 바탕으로 정리한 개발 블로그입니다.
  • hak0622
    궁금한 개발 이야기 Why?
    hak0622
  • 전체
    오늘
    어제
    • 분류 전체보기 (68)
      • 공부 (36)
        • 1. 자바 ORM 표준 JPA 프로그래밍 - 기본.. (35)
        • 시험 (1)
      • 프로젝트 (32)
        • 스프링 부트 3 백엔드 개발자 되기 Blog + .. (32)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
hak0622
👥🚨 좋아요 동시성 테스트에서 교착상태가 발생한 이유와 해결 방법
상단으로

티스토리툴바