Redis 개념 및 설치 가이드
Redis란?
메모리(RAM)에 데이터를 저장하는 key-value 저장소다.
디스크에 저장하는 MySQL과 달리 조회 속도가 극단적으로 빠르다.
| 항목 | MySQL | Redis |
|---|---|---|
| 저장 위치 | 디스크 | RAM |
| 조회 속도 | 수십~수백 ms | 수십 μs (1,000배 이상) |
| 데이터 영속성 | 영구 보존 | 서버 재시작 시 소멸 |
| 주 용도 | 핵심 비즈니스 데이터 | 캐시, 세션, 임시 데이터 |
한 줄 요약: 자주 쓰는 데이터를 빠른 메모리에 미리 올려두는 창고.
핵심 개념
Key-Value 구조
모든 데이터는 key 하나에 value 하나를 매핑한다.
ai:cache:3f7a2b... → "JWT는 Header.Payload.Signature 세 부분으로..."
user:session:abc → "{ userId: 1, role: ADMIN }"
otp:user01 → "382910"
TTL (Time To Live)
키에 만료 시간을 설정하면 자동으로 삭제된다.
ai:cache:3f7a2b... (TTL: 86400초 = 24시간)
otp:user01 (TTL: 300초 = 5분)
TTL이 지나면 Redis가 알아서 삭제하므로 별도 삭제 로직이 불필요하다.
maxmemory-policy
Redis가 설정된 최대 메모리(100MB)에 도달하면 어떤 키를 삭제할지 결정하는 정책.
| 정책 | 동작 |
|---|---|
allkeys-lru | 전체 키 중 가장 오랫동안 사용하지 않은 키 삭제 |
noeviction | 삭제 안 함 (메모리 초과 시 오류 발생) |
volatile-lru | TTL 있는 키 중 LRU 삭제 |
캐시 용도에는 allkeys-lru가 적합하다.
EC2 설치 과정
1. 패키지 설치
sudo dnf install -y redis6
Amazon Linux 2023 기준 redis6 패키지명을 사용한다.
설치 후 실행 파일 경로: /usr/bin/redis6-server, /usr/bin/redis6-cli
2. 설정 파일 수정
sudo vi /etc/redis6/redis6.conf
주요 설정:
bind 127.0.0.1 # localhost만 허용 (외부 접근 차단)
port 6379
requirepass 비밀번호 # 인증 필수
maxmemory 100mb # 최대 메모리 제한
maxmemory-policy allkeys-lru # 초과 시 LRU 삭제
bind 127.0.0.1: 서버 내부에서만 접근 가능하도록 제한한다.
EC2 보안 그룹에서도 6379 포트를 외부에 열지 않는다.
3. 서비스 등록 및 시작
sudo systemctl enable redis6 # 서버 재시작 시 자동 시작
sudo systemctl start redis6
sudo systemctl status redis6 # 상태 확인
4. 연결 확인
/usr/bin/redis6-cli -a '비밀번호' ping
# 응답: PONG
5. 환경변수 등록
guide.env에 비밀번호를 추가한다.
REDIS_PASSWORD=비밀번호
Spring Boot 연동
build.gradle 의존성
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
이 의존성 하나로 StringRedisTemplate Bean이 자동 생성된다.
application.yaml 설정
spring:
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
ai:
cache:
ttl-hours: ${AI_CACHE_TTL_HOURS:24}
환경변수가 없으면 localhost:6379로 연결을 시도한다.
StringRedisTemplate
Spring이 자동으로 생성하는 Redis 클라이언트 래퍼.
직접 Redis 프로토콜을 다루지 않아도 Java 메서드로 조작할 수 있다.
// 저장 (TTL 포함)
redis.opsForValue().set("key", "value", Duration.ofHours(24));
// 조회
String value = redis.opsForValue().get("key");
// 삭제
redis.delete("key");
// 패턴으로 키 목록 조회
Set<String> keys = redis.keys("ai:cache:*");
guide 프로젝트 적용 사례: AI 응답 캐시
동작 흐름
질문 수신 (WEB / Slack)
│
▼
AiCacheService.get(question) ← Redis 조회
│
┌────┴────┐
HIT MISS
│ │
▼ ▼
캐시 응답 AnthropicService.ask() ← Claude API 호출
(즉시) │
▼
AiCacheService.put() ← Redis에 저장 (TTL 24시간)
│
▼
사용자 응답
캐시 키 생성
private String hash(String question) {
String normalized = question.toLowerCase().trim().replaceAll("\s+", " ");
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] bytes = digest.digest(normalized.getBytes(StandardCharsets.UTF_8));
// bytes → hex 변환
}
- 정규화: 소문자 변환, 앞뒤 공백 제거, 연속 공백 단일화
- SHA-256 해시: 동일 질문 → 동일 키, 길이에 무관하게 64자리 고정
예시:
"JWT 인증 흐름 알려줘" → ai:cache:3f7a2b9c...
"jwt 인증 흐름 알려줘" → ai:cache:3f7a2b9c... (동일 키)
캐시 적용 범위
| 상황 | 캐시 적용 여부 | 이유 |
|---|---|---|
| 첫 질문 (히스토리 없음) | O | 동일 질문이면 동일 답변 |
| 스레드 대화 (히스토리 있음) | X | 앞 맥락에 따라 답변이 달라짐 |
캐시 무효화
docs 파일이 수정되면 자동으로 모든 AI 캐시를 삭제한다.
DocsWatchService (파일 변경 감지)
│
├─ markdownService.reloadDoc() → 문서 재로딩
├─ guideMenuService.refresh() → 메뉴 갱신
└─ aiCacheService.evictAll() → Redis 캐시 전체 삭제
문서가 바뀌었는데 이전 답변이 24시간 동안 남아있는 문제를 방지한다.
Redis 장애 대응 (Fallback)
public Optional<String> get(String question) {
try {
return Optional.ofNullable(redis.opsForValue().get(...));
} catch (Exception e) {
log.warn("Redis 조회 실패 (fallback): {}", e.getMessage());
return Optional.empty(); // 빈 값 반환 → API 직접 호출로 이어짐
}
}
Redis가 다운되어도 서비스는 중단되지 않는다. 캐시를 못 쓰는 것뿐이고, API를 직접 호출하여 정상 응답한다.
Redis 모니터링
현재 상태 확인
/usr/bin/redis6-cli -a '비밀번호' info memory
used_memory_human: 853.66K # 현재 사용 메모리
maxmemory_human: 100.00M # 최대 허용 메모리
캐시 키 목록 확인
/usr/bin/redis6-cli -a '비밀번호' keys "ai:cache:*"
캐시 전체 삭제 (수동)
/usr/bin/redis6-cli -a '비밀번호' flushdb
보안 체크리스트
- [ ]
bind 127.0.0.1설정 (외부 접근 차단) - [ ]
requirepass비밀번호 설정 - [ ] EC2 보안 그룹 6379 포트 미개방
- [ ] 비밀번호는 환경변수(
REDIS_PASSWORD)로 관리 — 소스코드에 하드코딩 금지 - [ ]
maxmemory설정으로 메모리 상한선 지정