리액티브 레디스 마스터 노트 시리즈 1편. Redis가 단순 캐시가 아닌 인메모리 데이터 구조 서버인 이유, WebFlux + Reactive Redis가 표준이 된 흐름, Lettuce 드라이버의 Netty 기반 논블로킹 구조, Spring Boot 3.x + Spring Data Redis Reactive 의존성 + 설정, ReactiveRedisConnectionFactory 자동 구성, Docker로 Redis 띄우기, redis-cli 기본 사용까지.
이 글은 리액티브 레디스 마스터 노트 시리즈의 첫 번째 편입니다. WebFlux 마이크로서비스 + Redis 통합은 더 이상 옵션이 아닙니다. 다만 전통 RedisTemplate은 블로킹 — WebFlux 흐름과 안 맞습니다. ReactiveRedisTemplate이 그 자리.
이 시리즈 7편은 Redis 기초·ReactiveRedisTemplate·자료구조·WebFlux 캐싱·성능·Pub/Sub·고급 주제까지. 1편의 목표 — 왜 리액티브 Redis인지, Spring Boot에 어떻게 연동하는지 손에 잡히게.
이 시리즈는 Redis 공식 문서, Spring Data Redis 가이드, Lettuce 클라이언트 문서, Project Reactor 학습 자료 등 공개 자료를 참고해 한국어 학습 노트로 풀어쓴 자료입니다.
로컬에 Docker로 Redis를 띄우고 Spring Boot WebFlux 프로젝트에서 ReactiveRedisTemplate으로 GET/SET 한 번 해 보면 흐름이 한 번에 잡혀요.
처음 리액티브 레디스가 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, **"왜 Reactive Redis인가"**가 막연합니다. 그냥 RedisTemplate 쓰면 안 되나? 둘째, Lettuce·Jedis·Redisson 클라이언트가 한 번에 등장합니다. 셋이 어떻게 다른가?
해결법은 한 가지예요. WebFlux = 논블로킹 = 블로킹 라이브러리 X. Lettuce = Netty 기반 = 자연스러운 논블로킹. 이 한 줄이 핵심. 클라이언트 비교는 5편에서 깊게.
Redis — 단순 캐시 아닌 인메모리 데이터 구조 서버
일반 사람 인식: "Redis = 캐시"
실제: "Redis = 인메모리 데이터 구조 서버"
Redis가 다루는 자료구조:
- String — 단순 키-값
- Hash — 객체 (필드-값 쌍)
- List — 큐·스택
- Set — 중복 없는 집합
- Sorted Set (ZSet) — 점수 기반 정렬 집합
- Stream — 로그·이벤트
- GeoSpatial — 지리 좌표
- HyperLogLog — 카디널리티 추정
- Bitmap — 비트 단위
여기서 정말 중요한 시험 함정 — Redis = "키-값 캐시"는 빙산의 일각. ZSet으로 실시간 랭킹, GeoSpatial로 위치 검색, Stream으로 이벤트 큐, Pub/Sub으로 실시간 통신. 거의 모든 인메모리 데이터 패턴을 커버.
왜 인메모리?
디스크 SSD: ~100us 지연
디스크 HDD: ~10ms 지연
메모리 RAM: ~100ns 지연 (1000~100000배 빠름)
대신 휘발성 → AOF·RDB로 디스크 백업 (7편).
WebFlux + Reactive Redis 흐름
전통 RedisTemplate — 블로킹
@Autowired
private RedisTemplate<String, String> redisTemplate;
public String getUser(String id) {
return redisTemplate.opsForValue().get("user:" + id); // 블로킹!
}
WebFlux의 핵심 = 논블로킹 이벤트 루프. 블로킹 호출이 들어오면 이벤트 루프가 멈춤 → 처리량 폭락.
ReactiveRedisTemplate — 논블로킹
@Autowired
private ReactiveRedisTemplate<String, String> redisTemplate;
public Mono<String> getUser(String id) {
return redisTemplate.opsForValue().get("user:" + id); // 논블로킹
}
Mono/Flux 반환. WebFlux 흐름과 자연스럽게.
여기서 정말 중요한 시험 함정 — WebFlux + RedisTemplate(블로킹) = 안티패턴. 한 줄의 블로킹 호출이 전체 처리량 망가뜨림. 항상 ReactiveRedisTemplate.
Redis 클라이언트 3종
| 클라이언트 | 특징 | Reactive |
|---|---|---|
| Jedis | 단순·전통, 동기 | X (블로킹) |
| Lettuce | Netty 기반, 비동기 | O (Reactive 표준) |
| Redisson | 고수준 API + 분산 락 | O (ReactiveRedissonClient) |
여기서 시험 함정이 하나 있어요. Spring Boot 2.0+ 기본 = Lettuce. Jedis는 옛날 표준. 새 프로젝트는 Lettuce 또는 Redisson.
Spring Boot 의존성
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
// Redisson (선택)
implementation 'org.redisson:redisson-spring-boot-starter:3.x'
}
spring-boot-starter-data-redis-reactive = Reactive Redis Template + Lettuce 자동 포함.
설정 (application.yml)
spring:
data:
redis:
host: localhost
port: 6379
password: ${REDIS_PASSWORD:}
timeout: 5s
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
Spring Boot 자동 구성:
ReactiveRedisConnectionFactory(Lettuce 기반)ReactiveRedisTemplate<String, String>ReactiveStringRedisTemplate
Docker로 Redis 띄우기
# 기본 — 인증 X
docker run -d -p 6379:6379 --name redis redis:7-alpine
# 영속화 + 인증
docker run -d -p 6379:6379 --name redis \
-v redis-data:/data \
redis:7-alpine \
redis-server --appendonly yes --requirepass "secret"
docker-compose
version: '3.8'
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
volumes:
redis-data:
redis-cli 기본 명령
# 컨테이너 진입
docker exec -it redis redis-cli
# 기본
> SET name "Alice"
OK
> GET name
"Alice"
> KEYS *
1) "name"
> DEL name
(integer) 1
> PING
PONG
여기서 시험 함정이 하나 있어요. KEYS * 운영 환경 절대 X. O(N) 동기 명령 = Redis 전체 멈춤. 운영은 SCAN을 점진적으로.
> SCAN 0 MATCH user:* COUNT 100
1) "256"
2) 1) "user:1"
2) "user:2"
첫 ReactiveRedisTemplate 코드
@Service
public class UserCacheService {
@Autowired
private ReactiveRedisTemplate<String, String> redisTemplate;
public Mono<Void> set(String id, String value) {
return redisTemplate.opsForValue()
.set("user:" + id, value, Duration.ofMinutes(5))
.then();
}
public Mono<String> get(String id) {
return redisTemplate.opsForValue().get("user:" + id);
}
public Mono<Boolean> delete(String id) {
return redisTemplate.delete("user:" + id).map(n -> n > 0);
}
}
WebFlux Controller에서:
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserCacheService cache;
@GetMapping("/{id}")
public Mono<ResponseEntity<String>> get(@PathVariable String id) {
return cache.get(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
}
TTL — Time To Live
자동 만료 — 캐시의 핵심.
redisTemplate.opsForValue().set("key", "value", Duration.ofMinutes(5));
> SET key value EX 300 # 300초 후 만료
> TTL key # 남은 시간
(integer) 295
> PERSIST key # TTL 제거 (영구)
여기서 정말 중요한 시험 함정 — TTL 없는 캐시 = 메모리 누수. 모든 캐시 키에 TTL 필수. 운영 환경 표준.
Database 분리
Redis 16개 데이터베이스 (0~15):
spring:
data:
redis:
database: 0 # 기본
> SELECT 1
> KEYS * # database 1만 보임
여기서 시험 함정이 하나 있어요. Redis Cluster는 database 1개만 (0). 단일 인스턴스에서만 다중 DB 가능. Cluster 환경에선 키 prefix로 분리.
환경 비교
| 환경 | 권장 |
|---|---|
| 개발 | Docker (단일 노드) |
| 스테이징 | Docker Compose |
| 운영 | Redis Sentinel(HA) 또는 Redis Cluster |
운영 — 6편(Cluster·HA)에서 다룸.
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 1편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- Redis = 인메모리 데이터 구조 서버 (단순 캐시 X)
- 자료구조 — String·Hash·List·Set·ZSet·Stream·GeoSpatial·HLL·Bitmap
- 인메모리 =
100ns (디스크 1000100000배 빠름) - 휘발성 → AOF·RDB 백업 (7편)
- WebFlux + RedisTemplate = 안티패턴
- WebFlux = 논블로킹, RedisTemplate = 블로킹
- ReactiveRedisTemplate이 답
- Mono/Flux 반환, WebFlux 흐름 자연스러움
- 클라이언트 3종 — Jedis(블로킹) / Lettuce(Netty, 표준) / Redisson(고수준+분산락)
- Spring Boot 2.0+ 기본 = Lettuce
- 의존성 —
spring-boot-starter-data-redis-reactive - 자동 구성 —
ReactiveRedisConnectionFactory+ReactiveRedisTemplate - 설정 — host/port/password/timeout/pool
- Docker 띄우기 —
docker run -d -p 6379:6379 redis:7-alpine - 영속화 —
--appendonly yes - redis-cli — SET·GET·DEL·KEYS·SCAN·PING
KEYS *운영 X (O(N) 멈춤) → SCAN 사용- TTL 없는 캐시 = 메모리 누수
- 모든 캐시 키 TTL 필수
- Database 16개 (0~15) — 단일 인스턴스만
- Redis Cluster = DB 1개 (0)
- 운영 환경 — Sentinel(HA) 또는 Cluster
시리즈 다른 편
- 1편 — Redis 기본·Spring Boot 연동 (현재 글)
- 2편 — Template·Redisson·Serializer
- 3편 — 자료구조 5종
- 4편 — WebFlux 캐싱
- 5편 — 성능
- 6편 — Pub/Sub·WebSocket
- 7편 — 고급 (Transaction·Persistence·GeoSpatial·ACL)
공식 문서: Redis Documentation / Spring Data Redis Reference 에서 더 깊이.
다음 글(2편)에서는 ReactiveRedisTemplate의 5가지 Operations·Redisson과의 차이·Serializer 설정까지 풀어 갑니다.