Spring Boot와 Redisson 분산 락: @Transactional만 걸면 되는 거 아니야?

@Transactional만 걸면 되는 거 아니야?

 

서론

 

동시성 제어를 처음 접했을 때, 이런 생각이 들었다.

 

“동시성 제어? 그냥 @Transactional 달고 DB에서 Select … FOR UPDATE 처리하면 되는 거 아닌가?”

 

그렇게 생각하고 단일 데이터베이스 환경에서 비관적 락(Pessimistic Lock)을 걸며 뿌듯해했었다.

하지만 서비스가 확장되고 서버가 여러 대(Multi-Instance)로 분산되는 환경을 겪어보니, DB 락만으로는 커넥션 고갈과 트랜잭션 병목을 감당할 수 없다는 걸 알게 됐다.

 

이 글은 그런 과정을 통해 내가 직접 겪고 도입한 Redisson 분산 락에 대한 이해와 실무 구현을 담고 있다.

부족한 내용이 있을 수 있지만, 나처럼 막 시작한 사람에게는 도움이 될 거라 생각한다.

 


 

📚 왜 Lettuce 대신 Redisson인가?

 

Redis 기반 락을 구현할 때 Lettuce와 Redisson의 방식 차이를 이해하는 것이 중요합니다.

 


 

방식별 비교

 

1. Lettuce (Spin Lock 방식)

  • 락을 획득할 때까지 스핀 락(SETNX 반복 호출)을 시도함
  • 락 획득 실패 시 지속적인 Redis 요청으로 CPU 및 네트워크 부하 가중

 


 

2. Redisson (Pub/Sub 방식)

  • 락이 해제되면 Redis가 대기 중인 클라이언트에게 채널로 메시지를 발행(Publish)함
  • 대기 클라이언트는 메시지를 수신했을 때만 락 획득을 재시도하므로 Redis 부하가 극도로 낮음
@Aspect
@Component
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class DistributedLockAspect {

    private final RedissonClient redissonClient;
    private final AopForTransaction aopForTransaction;

    @Around("@annotation(distributedLock)")
    public Object lock(final ProceedingJoinPoint joinPoint, final DistributedLock distributedLock) throws Throwable {
        String key = CustomSpringELParser.getDynamicValue(joinPoint.getArgs(), distributedLock.key());
        RLock rLock = redissonClient.getLock(key);

        try {
            boolean available = rLock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), distributedLock.timeUnit());
            if (!available) {
                throw new IllegalStateException("락 획득 실패: " + key);
            }
            return aopForTransaction.proceed(joinPoint);
        } finally {
            try {
                rLock.unlock();
            } catch (IllegalMonitorStateException e) {
                // 이미 타임아웃되어 해제된 락 예외 처리
            }
        }
    }
}

 


 

정리하며

 

  • Redisson은 Pub/Sub 구조 덕분에 레디스 서버 부하 없이 안정적인 분산 락을 제공한다.
  • AOP 적용 시 ‘락 해제 시점’이 ‘트랜잭션 커밋 시점’보다 뒤에 오도록 AOP Order 우선순위를 세심히 설정해야 한다.

 

분산 환경에서 커스텀 어노테이션 기반 락을 도입해보니, 비즈니스 로직과 시스템 인프라 로직을 깔끔하게 분리할 수 있게 되었다.

댓글 남기기