728x90
0. 상황
최근 프로젝트에서 유저가 특정 기능을 신청하는 API를 구현하고 있었습니다. 기획 요구사항은 명확했습니다. 신청 상태가 '대기(PENDING)인 요청은 요청자당 하나만 존재해야 한다는 것이었습니다.
즉, 이미 심사 중인 건이 있다면 중복으로 신청할 수 없도록 막아야 했습니다.
그래서 서비스계층에서 비즈니스 로직을 통해 이를 검증하도록 코드를 작성했습니다. DB에서 해당 유저의 PENDING 상태인 요청이 존재하는지 조회하고, 만약 존재한다면 예외를 던져 요청을 거부하도록 처리했습니다. 하지만 동시성에 대한 생각을 하게되었습니다.
1. 동시성이란
싱글 코어 혹은 멀티 코어 환경에서 여러 작업이 번갈아 가며 실행되어, 마치 동시에 실행되는 것처럼 보이는 성질인데
지금은 동시에 신청 API를 받아 DB 데이터에 접근할 때 타이밍이 절묘하게 겹칠 경우, 의도한 순서대로 로직이 흘러가지 않는 경쟁 상태가 발생하는 경우입니다.
2. 문제
Thread A가 조회를 마치고 저장을 하기 직전의 찰나에 Thread B가 조회에 들어오게 된다면, 아직 PENDING인 데이터가 없기 때문에 B가 거절되지않고, 통과할수있을 겁니다.
Check: DB를 조회해서 PENDING 상태가 있는지 확인한다.
Act: 없다면 신청 정보를 저장한다.
3. SpringBoot에서의 동시성
우선 SpringBoot가 자체적으로 해당 동시성 문제를 해결해줄수 있는지에 대해 찾아보았습니다. 일단 SpringBoot는 기본적으로 Tomcat이 쓰레드 풀에서 쉬고있는 쓰레드중 하나를 찾아서 할당하는데, 찾아 보았을때 기본적으로 겪고 있는 문제를 해결할 방안은 없어보입니다.
4.해결법
AOP로 Redis의 원자적 연산을 활용하여, 가장 먼저 도착한 요청 하나만 통과시키고 나머지는 거절하는 방식을 사용가능합니다.특정 키를 기준으로 해싱하여 Redis에 저장하고, 그뒤 아주잠깐뒤 동일한 키로 해싱된 키가 들어오면 거절하게끔하여,
컨트롤러 진입 단계에서 원천 차단함으로써 DB와 서버 리소스의 낭비를 막을 수있습니다.

728x90
'BackEnd > SpringBoot' 카테고리의 다른 글
| [SpringBoot] 통합테스트 (0) | 2025.11.23 |
|---|---|
| [SpringBoot] QueryDSL (0) | 2025.09.30 |
| 반환코드 204 vs 404 (0) | 2025.09.18 |