Token 저장소를 RDB에서 Redis로 전환
앞선 글에서 살펴본 것처럼 인증 및 인가가 필요한 API 요청 때마다 Token 테이블에 접근해 블랙리스트 여부를 확인하느라 DB I/O가 발생했고, 해당 쿼리는 Slow Query로 지정되기도 했습니다.
이 I/O 부하를 줄일 방안을 고민하다가 In-memory DB를 떠올렸고, 그중에서도 Redis를 선택하게 되었습니다.
물론 가장 빠른 방법은 Caffeine처럼 애플리케이션 내부 메모리를 캐시로 활용하는 것이겠지만,
CareerBee는 현재 두 대 이상의 인스턴스로 운영되고 있습니다.
따라서 로컬 메모리를 쓰면 JVM 마다 토큰이 저장돼 인스턴스 간 토큰 정보를 공유할 수 없습니다.
따라서 전역(글로벌) 토큰 저장소가 필요했고, 이를 위해 Redis를 도입했습니다.
Redis 통신 Java 클라이언트 라이브러리
Feature Comparison: Redisson vs Lettuce | Redisson
Two popluar Java client libraries for Redis are Redisson and Lettuce. Redisson is a Redis Java client that includes many common implementations of distributed Java collections, objects, and services.
redisson.pro
Redis를 도입하기로 결정하였다면 SpringBoot에서 어떤 라이브러리로 통신할지 선택해야 합니다.
라이브러리로는 대표적으로 아래 두 가지가 있습니다.
Redissonlettuce
두 라이브러리 모두 SpringBoot에서 Redis와 통신하기 위한 Java Redis 클라이언트 라이브러리인데요.
둘 다 Redis를 사용하는데 활용되지만, 목적과 특징이 꽤 다릅니다.
Lettuce
Lettuce는 Java 기반의 비동기 / 스레드 세이프한 Redis 클라이언트입니다.
https://redis.github.io/lettuce/
Lettuce Reference Guide
Lettuce - Advanced Java Redis client Lettuce is a scalable thread-safe Redis client for synchronous, asynchronous and reactive usage. Multiple threads may share one connection if they avoid blocking and transactional operations such as BLPOP and MULTI/EXEC
redis.github.io
Lettuce는 아래와 같은 특징을 가지고 있습니다.
| 특징 | 설명 |
|---|---|
| Netty 기반 | 고성능 비동기 IO 처리 가능 |
| Thread-safe | 하나의 커넥션으로 여러 스레드에서 안전하게 공유 |
| 동기/비동기/반응형 API 제공 | async(), sync(), reactive() 형태로 지원 |
| Spring Data Redis의 기본 클라이언트 | SpringBoot 에서 기본으로 Lettuce 사용 |
| 자동 reconnect 및 재시도 | Redis 서버와의 연결이 끊기면 자동으로 재연결을 시도하며, 설정에 따라 재시도 정책도 지정할 수 있음 |
| 클러스터 및 Sentinel 지원 | Redis 클러스터와 Sentinel 구조 모두 지원하여, 데이터 흐름을 안전하게 제어 가능 |
| 낮은 GC 부담 | Netty 기반 구조는 GC 부담이 적고 CPU 자원 사용 효율적 |
| 스크립팅 및 함수 지원 | Lua 스크립트, Redis Fuctions 명령 지원 |
| Pub/Sub 및 Streaming API 제공 | 실시간 메시징(pub/sub) 및 스트리밍 인터페이스 활용 가능 |
| Command Pipelining 과 Flush 제어 | 비동기 방식으로 여러 커맨드를 한꺼번에 보내는 pipelining 지원 및 flush 제어로 요청 배치도 가능 |
| Sync/Async Connection Pooling 지원 | Apache Commons Pool 활용한 동기 풀과 비동기 풀을 모두 지원, 대규모 연결 필요시 유용 |
Redisson
Redisson은 Redis를 단순 key-value 저장소가 아닌, 분산 환경에서 사용할 수 있는 Java 객체 기반의 분산 도구로 확장하기 위한 클라이언트 라이브러리입니다.
즉, Lettuce보다 더 많은 고수준 기능을 제공하고 있습니다.
Redisson의 주요 특징은 다음과 같습니다.
| 특징 | 설명 |
|---|---|
| 분산 객체 지원 | Java에서 사용하던 Map, Set, Queue, Lock 등을 Redis 기반의 분산 구조로 제공 ex. RMap, RQueue, RSemaphore 등.. |
| 분산 락 제공 | ReentrantLock, FairLock, ReadWriteLock 등 분산 환경에서 안전하게 락을 걸 수 있음 |
| 분산 Semaphore/CountdownLatch | RSemaphore, RCountDownLatch 등 동기화 구조 제공 |
| 분산 캐시 기능 | Local + Redis 기반의 하이브리드 캐시 제공 |
| Cluster, Sentinel, Master-Slave 지원 | 다양한 Redis 운영 모드 지원 |
| 자동 재연결 및 failover 처리 | 장애 상황에서 자동 재시도 및 재연결 내장 |
| Netty 기반 비동기 처리 | Lettuce처럼 비동기 처리 기반 |
| 스크립트 및 트랜잭션 지원 | Lua scripting, Redis, Transactions 지원 |
Redisson과 Lettuce의 차이
https://redisson.pro/blog/feature-comparison-redisson-vs-lettuce.html?utm_source=chatgpt.com
미리 말하자면 ‘분산’이라는 단어를 많이 사용하게 됩니다.
여기서 말하는 분산은 Redis를 활용하여 여러 인스턴스 간에 데이터를 공유하거나 동기화할 수 있는 구조를 의미합니다.
예를 들어분산 컬렉션이란, 네트워크를 통해 여러 인스턴스가 동시에 접근하고 사용할 수 있는 컬렉션이라는 겁니다.
| 비교 항목 | Lettuce | Redisson |
|---|---|---|
| Redis 연결 방식 | 동일 | 동일 |
| Redis를 통한 데이터 공유 | 가능 | 가능 |
| 분산 락 구현 | 수동 구현 필요 | RLock으로 바로 사용 가능 |
| 분산 큐, 세마포어 등 | 직접 Redis 명령 구성 | 고수준 API 제공 |
| 코드 복잡도 | 높음 (명령 직접 조작) | 낮음 (Java처럼 사용) |
분산 컬렉션 (Distributed Collections)
- Lettuce : Map, Set, Queue, Deque, ScoredSortedSet, stream-Readis의 기본 명령 세트만 지원
- Redisson : 위 기능 외에도 JSON Store, Multimap, SortedSet, PriorityQueue, PriorityDeque, DelayedQueue, RingBuffer, TransferQueue 등 훨씬 풍부한 컬렉션 지원
분산 락 & 동기화 도구 (Distributed Locks & Synchronizers)
- Lettuce : 해당 기능 미지원
- Redisson : Lock, Semaphore, CountDownLatch, FiarLock, Fenced Lock, Spin Lock, MultiLock, ReadWrtieLock 등 다양한 락과 동기화 도구 제공
분산 오브젝트 (Distributed Object)
- Lettuce : Object holder, AtomicLong, Publish/Subscribe, AtomicDouble, Geospatial, BitSet, HyperLogLog 등 플레인 명령 기반 단순 객체
- Redisson : 위 기능 + JSON Holder, LongAdder, DoubleAdder, BinaryStream, RateLimiter 같은 고급 객체 제공
고급 캐싱 기능 (Advanced Caching)
- Lettuce : 캐싱 프레임워크 레벨 비지원
- Redisson : JCache API 구현, Near Cache 등을 통한 최대 45배 빠른 속도, read-through, write-through, wirte-behind 전략 지원
트랜잭션 (Transactions)
- Lettuce : 미지원
- Redisson : Java Transaction API 제공
분산 서비스 (Distributed Services)
- Lettuce : 해당 기능 미지원
- Redisson : ExecutionService, MapReduce, SchedulerService, RemoteService, LiveObjectService, RedisSearch 등 분산 처리 및 서비스 기능 포함
안정성 & 클라우드 지원 (Stability & Cloud Support)
- Lettuce : 해당 기능 미지원
- Redisson : AWS ElasticCache, Azure Cache 같은 완전 관리형 클라우드 서비스 친화적
⁇ Lettuce와 Redisson 사이의 궁금점
Redisson이 분산환경에서 장점을 가지고 있는데, Redis 단일 인스턴스 환경에서도 장점이 유효할까?
만약 SpringBoot 멀티 인스턴스 + Redis 단일 인스턴스 환경이라면 Redisson을 사용할 이유가 될까?
이는 둘의 차이점을 공부하며 가장 먼저 든 고민입니다.
결론부터 말하자면 이러한 환경에선 Redisson의 장점을 최대로 활용할 순 없겠지만, 여전히 유효하다고 생각합니다.
SpringBoot가 멀티 인스턴스로 구성된 환경이라면 하나의 중앙 Redis 인스턴스를 통해 여러 서버 간 데이터를 공유하거나 락을 제어해야 하는 상황이 자주 발생합니다.
이때 Redisson을 사용하면 복잡한 락 로직이나 작업 큐, 세마포어 같은 기능을 간단히 구현할 수 있고, 직접 Redis 명령어를 조합하지 않아도 되는 개발 편의성 또한 큰 장점이 됩니다.
따라서 Redis가 단일 인스턴스이더라도, 애플리케이션이 멀티 인스턴스 구조라면 Redisson의 고수준 분산 기능은 여전히 유효하며, 개발 생산성과 유지보수 측면에서 충분히 유효하다고 생각합니다.
다만, Redis가 단일 인스턴스일 경우 SPOF(Single Point of Failure)가 될 수 있습니다. 이를 위해서 Redis의 고가용성을 고려하여 Redis Cluster 또는 Sentinel로의 확장을 고려할 수 있겠습니다.
분산 락에서 Lettuce와 Redisson의 구조 차이
Lettuce 와 Redisson 모두 분산 락에 활용할 수 있지만, 두 라이브러리는 큰 차이점을 가지고 있습니다.
| 항목 | Redisson | Lettuce |
|---|---|---|
| 락 감지 방식 | Pub/Sub 기반 | Polling 기반 |
| 락 획득 실패 시 처리 | Redis에 이벤트 등록 후 대기 | Redis에 주기적으로 SETNX 재시도 |
| 서버 부담 | 낮음 (이벤트 발생시 알림) | 높음 (지속 Polling으로 Redis 부하) |
| 구현 난이도 | 낮음 (RLock.lock() 사용) | 높음 (Lua + retry + timeout 직접 구현) |
Redisson 은 Pub/Sub 기반으로
- 락을 획득하려 할 때, 이미 다른 인스턴스가 점유 중이라면?
- Redis의 Pub/Sub 채널에 subscribe 해서 해당 락이 해제되길 이벤트 방식으로 기다림
- 락 해제 이벤트가 오면 그때 다시 락 획득 시도
이러한 구조는 불필요한 반복 확인 없어 Redis 서버에 부하를 주지 않아 효율적입니다.
Lettuce는 Polling 방식으로
- SETNX로 락 획득 시도
- 실패 시 Thread.sleep() 등으로 기다렸다가 다시 시도
- 이런 식으로 반복해서 lock key가 없어질 때까지 시도
이 구조는 Redis에 불필요한 트래픽을 유발할 수 있으며, 구현 난이도 또한 높습니다.
'스프링부트' 카테고리의 다른 글
| PK기반 단건 조회시 컬럼 수로 성능 개선을 기대할 수 없는 건에 대하여 (5) | 2025.08.11 |
|---|---|
| 멀티 인스턴스에서 안정적인 SSE 구조 설계 (0) | 2025.07.13 |
| @Cacheable 은 무적인가? (2) | 2025.07.08 |
| 락을 활용해서 동시성 문제를 해결해보자 (3) | 2025.07.08 |
| double 타입의 위도, 경도 값 테스트시 부동소수점 오차 발생에 관하여 (1) | 2025.05.25 |