출처: https://www.youtube.com/watch?v=FEP2BzNzoRw&list=PLgXGHBqgT2TvpJ_p9L_yZKPifgdBOzdVH&index=24
문제 상황
- 7주년 쿠폰 발급 행사
- 1명의 사용자는 1개의 쿠폰을 발급 받아야 한다.
- 여러 브라우저에서 동시에 동일한 API를 호출한다면?
* 프론트엔드 관련 내용 생략
서버에 검증 로직을 추가
- validateAlreadyIssued 메서드를 통해 사용자가 쿠폰을 발급 받았는지 확인

-> DB에 한 사용자가 쿠폰을 여러 개 발급 받은 것을 확인!
2개 요청이 '동시'에 들어온다면?
- 두 개의 메서드가 동시에 DB 트랜잭션을 시작
- 발급 확인 메서드를 통과하게 됨
해결 방법
- 자바 동기화 동구
- 분산락
- 유니크 제약 조건
- 트랜잭션 격리 수준
- select ... for share, for update 등
-> 자바 동기화 도구 (Synchronized 키워드), 분산락(MySQL 네임드락) 활용
Synchronized란?
- 특정 영역, 메서드에 [하나씩 들어와!] 하는 키워드
- 암묵적 락
- 자바 제공

Synchronized 활용
- 쿠폰 발급 메서드에 synchronized 추가
- 테스트 코드 작성 -> 여전히 2개
- 이유
- Synchronized가 끝나고 DB 트랜잭션이 커밋됨
- @Transactional을 제거해서 해결 가능
- 한계
- 다른 사용자가 요청을 동시에 보냈을 때도 대기하게 됨 -> 비효율적
- 서버가 여러 개인 경우 한 사용자가 쿠폰을 여러 개 발급 가능

분산락이란?
- 다양한 프로세스가 공유 자원을 상호 배타적으로 사용하는 경우 유용한 도구
MySQL 네임드 락을 활용
- MySQL에서 임의의 문자열에 잠금을 설정

- 특징
- 한 세션에서 잠금을 유지하고 있으면, 다른 세션에서 해당 잠금을 획득할 수 없다.
- 획득한 잠금은 트랜잭션이 종료되어도 해제되지 않는다.
- 현재 세션에서 획득한 잠금만 릴리즈할 수 있다.
MySQL 네임드 락 활용
- tryLock: timeout초 만큼 락 획득 시도, 잠금
- releaseLock: 네임드 락 잠금 해제
- 같은 커넥션을 주입 받도록 구현
- 잠금을 획득한 커넥션과 다른 커넥션을 사용하여 릴리즈하면 실패
- 커넥션 풀링을 하는 경우 다른 스레드에서 락을 릴리즈 가능

- 락 점유한 후 비즈니스 로직 실행
- 데이터소스 분리
- 커넥션 풀 데드락 발생할 수 있기 때문

고민해볼 지점
- 데드락 발생
- 추가 인프라 구축 비용 발생
- 코드의 복잡도 상승 등
대안

'Spring & JPA' 카테고리의 다른 글
[테코톡] Lock & JPA Lock (0) | 2025.04.05 |
---|---|
[테코톡] JPA 연관관계 최적화 (0) | 2025.04.03 |
고급 매핑 - 3. 복합 키와 식별 관계 매핑 (0) | 2025.03.19 |
고급 매핑 - 2. @MappedSuperclass (0) | 2025.03.19 |
고급 매핑 - 1. 상속 관계 매핑 (0) | 2025.03.19 |