-
쿠폰 서비스 만들기 (2/3)SW 마에스트로 2025. 6. 4. 00:24
소프트웨어 마에스트로에서 진행했던 '쿠폰 서비스 만들기'
쿠폰 서비스 만들기(1/3) : 기능적 요구사항 구현, 테스트 코드 작성, 동시성 문제 원인 파악
쿠폰 서비스 만들기(2/3) : 단일 인스턴스 환경에서 락을 걸 수 있는 방법들
쿠폰 서비스 만들기(3/3) : 멀티 인스턴스 환경에서 락을 걸 수 있는 방법
지난 1편에서는 기능적 요구사항 구현과 쿠폰 발급 상황에서 왜 데드락이 걸리는지를 살펴보았다.
이번에는 단일 인스턴스에서 락을 통해 동시성을 보장할 수 있는 방법들에 대해서 다룬다.
왜 락인가?
락을 걸게 되면 동시 요청이 오더라도 락을 획득한 요청만 처리하고 나머지 요청은 대기 상태가 되기 때문
결과적으로 데이터 정합성을 보장
어떤 락을 사용해야 하는가?
- 비관락 : 충돌이 많이 발생한다고 가정하고 락을 걸어 정합성 보장
- 낙관락 : 충돌이 드물다고 가정하고 동시 접근을 허용하며 커밋 단계에서 충돌 감지
낙관락을 사용하여 동시성 제어하기
낙관락의 작동원리는 데이터와 "버전 정보"를 같이 읽고 비교하는 방식이다.
비슷한 예로 CAS(Compare-And-Swap) 알고리즘이 있다.
CAS를 쿼리로 표현하면 다음과 같다.
update set value = next_value where value = current_valueJPA에서는 `@Version`으로 낙관락을 간단하게 구현할 수 있었다.
version은 커밋이 일어날 때마다 1씩 증가한다.
CAS 알고리즘처럼 version값이 현재 version값이 같은지로 체크한다.
... @Column(name = "CREATED_AT") private LocalDateTime createdAt; @Version private long version; ..하지만 테스트 결과는 동시성 제어 실패..
낙관락 에러와 데드락이 발생했다.
데드락 발생한 원인에 대해서 생각해 보자면
낙관락은 다른 트랜잭션과의 충돌 및 교착상태가 일어날 수 있음
데이터 수정시점에만 비교하는 방식이기 때문
따라서 애초에 충돌이 잦은 경우에는 맞지 않는 해결방안이라고 생각한다.

Optimistic Lock 에러 로그 비관락을 사용하여 동시성 제어하기
결국 락을 이용하여 요청을 순차적으로 처리하는 방향으로 정했다.
여기서는 비관락 중 `synchronized`, `Reentrantlock`를 사용했다.
`synchronized`의 작동원리는 해당 클래스의 객체 this에 락을 거는 모니터 락을 사용한다.
하지만 단순히 `synchronized`를 걸었을 때는 실패했다.
락 해제 시점이 트랜잭션 커밋시점보다 빠르기 때문
이렇게 되면 락을 해제하고 트랜잭션이 커밋하기 전에 다른 트랜잭션이 값을 읽을 수 있다.
synchronized 동기화 실패 예시
- 스레드 A 트랜잭션 시작
- 스레드 A가 sync 락 얻고 작업 수행
- 스레드 A sync 락 풀기
- 스레드 B가 sync 락 얻고 작업 수행
- 스레드 A가 작업 커밋
- 스레드 B가 작업 커밋
아래와 같은 순서로 진행되기 위해서는
동기화 메서드와 트랜잭션을 분리해야 한다.
Spring에서는 `@Transactional(Transactional.TxType.REQUIRES_NEW)`를 통해 구현할 수 있다.
이렇게 하는 이유는 새로운 트랜잭션으로 선언하지 않으면 부모 트랜잭션을 따라가게 되고 결국 위와 같은 문제가 나올 수 있음
- 락 획득
- 트랜잭션 시작
- 트랜잭션 종료
- 락 해제
@Transactional self-invocation (in effect, a method within the target object calling another method of the target object) does not lead to an actual transaction at runtime
@Transactional은 AOP 기반 동작 → 같은 객체의 다른 메서드를 호출하면 프록시 적용 X`synchronized` vs `Reentrantlock`
추가적으로 위 두 가지 락 방법의 차이는 공정성(fair), 타임아웃이 있음
공정성의 경우 락을 획득한 지 오래된 요청에 우선순위를 부여하는 방식결론
낙관락과 비관락으로 쿠폰 발급 프로세스를 제어해보면서,
낙관락의 경우 완벽하게 동시성 문제를 해결해주지는 않는다는 것을 알았다.
그렇기 때문에 다른 멘토님들 또한 낙관락의 개념은 알고있지만 실제로 사용해본적은 없다고 한다.
지금은 단순히 연습용으로 무료 쿠폰(?)이지만
내가 "10만원짜리 쿠폰 발급 시스템을 운영한다고 했을 때 낙관락을 사용할 것인가?"에 대해서는 "아니다"라고 답할 것이다.
'SW 마에스트로' 카테고리의 다른 글
내가 사용한 애자일(1/2) (0) 2025.12.15 타임존 맞추기 (4) 2025.07.30 쿠폰 서비스 만들기 (3/3) (0) 2025.06.22 쿠폰 서비스 만들기 (1/3) (0) 2025.05.30 소프트웨어 마에스트로 16기 합격 후기 (0) 2025.05.29