Post

[Redis] 인메모리 저장소의 이해와 핵심 데이터 타입 활용

1. 인메모리 저장소 개요


인메모리 저장소는 하드 디스크가 아닌 컴퓨터의 주 메모리(RAM)에 데이터를 직접 저장하는 아키텍처이다. 디스크 I/O가 발생하지 않아 읽고 쓰는 속도가 압도적으로 빠르다.

  • 주요 활용처:
    • 캐싱(Caching): 자주 조회되는 데이터를 미리 메모리에 적재하여 응답 속도 개선.
    • 세션 관리: 사용자 세션, 장바구니 정보 등을 메모리에 저장하여 빠른 상태 확인.
    • 실시간 데이터 처리: 주식 시세, 실시간 랭킹 등 잦은 업데이트가 발생하는 데이터 처리.

2. Redis vs Memcached 비교

인메모리 저장소 도입 시 가장 많이 고려되는 두 시스템의 차이점이다.

특성RedisMemcached
데이터 구조문자열, 리스트, 해시, 집합 등 다양한 타입 지원단순한 Key-Value (문자열, 숫자 위주)
영속성(Persistence)디스크 백업 지원 (AOF, Snapshot)미지원 (서버 재시작 시 데이터 소실)
분산 환경Redis Cluster를 통한 고가용성/확장성 지원기본 분산 캐싱을 통한 수평 확장 지원
성능 및 메모리매우 빠름 / 다양한 기능으로 메모리 사용량은 상대적으로 높음극도로 빠름 / 단순 구조로 메모리 효율 높음
선택 기준복잡한 데이터 구조 처리, 데이터 영구 보존이 필요할 때단순 캐싱 목적, 데이터 휘발이 허용되며 극강의 속도가 필요할 때

3. Redis 설치 및 Spring Boot 연동

3.1. Docker를 이용한 Redis 실행

복잡한 설정 없이 Docker를 통해 백그라운드에서 Redis 컨테이너를 실행한다.

1
2
3
4
5
`# Redis 컨테이너 실행 (포트 6379, 자동 재시작 설정)
docker run -d --name redis-server -p 6379:6379 --restart always redis

# CLI 접속 테스트
docker exec -it redis-server redis-cli`

3.2. Spring Boot 프로젝트 설정

Spring Boot 환경에서 Redis를 캐시 및 세션 저장소로 사용하기 위한 기본 설정이다.

build.gradle 의존성 추가

1
2
3
4
dependencies {
    implementation 'org.springframework.session:spring-session-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

application.yml 설정

1
2
3
4
5
6
7
8
# 

`spring:
  data:
    redis:
      host: localhost
      port: 6379
      password: # 운영 환경에서는 반드시 설정 필요`

세션 데이터 직렬화 설정 (SessionConfig.java)

기본 Java 직렬화 방식은 Redis CLI에서 읽기 어려우므로, JSON 형태로 저장되도록 직렬화 방식을 변경한다.

1
2
3
4
5
6
7
8
9
@Configuration
@EnableRedisHttpSession // Redis를 세션 저장소로 활성화
public class SessionConfig {

  @Bean
  public GenericJackson2JsonRedisSerializer springSessionDefaultRedisSerializer() {
    return new GenericJackson2JsonRedisSerializer();
  }
}

4. Redis의 5가지 핵심 데이터 타입과 활용

Redis의 가장 큰 장점은 목적에 맞게 최적화된 다양한 데이터 구조를 지원한다는 점이다. 각 타입별 특징과 실무 활용 예시를 정리한다.

4.1. String (문자열)

  • 특징: 가장 기본적인 형태로 단일 Key에 최대 512MB의 문자열/숫자 데이터를 저장한다.
  • 용도: 단순 데이터 캐싱, 세션 ID 저장, 조회수/좋아요 카운터 등.
  • 예시 (카운터 연산): Java
1
2
3
4
5
6
7
8
9
10
final String pageViewsKey = "page:views";

// 1. 카운트 1 증가
Long pageViewsAfterIncr = redisTemplate.opsForValue().increment(pageViewsKey);

// 2. 카운트 1 감소
Long pageViewsAfterDecr = redisTemplate.opsForValue().decrement(pageViewsKey);

// 3. 특정 값(5)만큼 증가
Long likesAfterIncrBy = redisTemplate.opsForValue().increment("item:123:likes", 5);

4.2. List (리스트)

  • 특징: 데이터의 삽입 순서(Insertion Order)를 유지하며, 양방향(Head, Tail)에서 입출력이 가능해 Queue나 Stack으로 활용할 수 있다.
  • 용도: 메시지 큐, 백그라운드 작업 대기열, 최근 검색어/활동 기록.
  • 예시 (작업 대기열 - FIFO 방식): Java
1
2
3
4
5
6
7
final String TASKS_KEY = "tasksQueue";

// Head(왼쪽)에 작업 데이터 순차 삽입
redisTemplate.opsForList().leftPushAll(TASKS_KEY, "Task1", "Task2", "Task3");

// Tail(오른쪽)에서 작업 데이터 추출 (선입선출)
String taskToProcess = redisTemplate.opsForList().rightPop(TASKS_KEY); // "Task1" 반환

4.3. Set (집합)

  • 특징: 중복 값을 허용하지 않고, 저장 순서를 보장하지 않는다. 교집합, 합집합, 차집합 등의 집합 연산에 최적화되어 있다.
  • 용도: 고유 방문자 추적, 이벤트 중복 참여 방지, 특정 태그를 공유하는 교집합 추출.
  • 예시 (고유 사용자 관리 및 교집합 추출): ```java // 중복된 “User1”은 한 번만 저장됨 redisTemplate.opsForSet().add(“event1”, “User1”, “User2”, “User3”, “User1”); redisTemplate.opsForSet().add(“event2”, “User2”, “User3”, “User4”);

// 두 이벤트의 공통 참여자 조회 (교집합) Set commonUsers = redisTemplate.opsForSet().intersect("event1", "event2"); // ["User2", "User3"]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
### 4.4. Hash (해시)

- **특징:** 하나의 Key 하위에 여러 개의 필드-값(Field-Value) 쌍을 저장한다. RDBMS의 단일 레코드(Row)나 JSON 객체와 유사하다.
- **용도:** 사용자 프로필 정보 관리, 상품 상세 메타데이터 저장.
- **예시 (사용자 프로필 저장 및 부분 조회):**
Java

```java
final String USER_PROFILE_KEY = "user:profile:100";

// 필드 단위 데이터 저장
redisTemplate.opsForHash().put(USER_PROFILE_KEY, "name", "John Doe");
redisTemplate.opsForHash().put(USER_PROFILE_KEY, "email", "john@example.com");

// 특정 필드만 조회
String name = (String) redisTemplate.opsForHash().get(USER_PROFILE_KEY, "name");

// 전체 필드-값 쌍 조회
Map<Object, Object> userProfile = redisTemplate.opsForHash().entries(USER_PROFILE_KEY);

4.5. Sorted Set (정렬된 집합)

  • 특징: Set과 동일하게 중복을 허용하지 않으나, 각 값에 가중치인 점수(Score)를 함께 저장하여 점수를 기준으로 자동 정렬한다.
  • 용도: 게임 리더보드, 실시간 검색어 랭킹, 시간 기반 정렬 데이터(타임라인).
  • 예시 (실시간 랭킹 시스템): Java
1
2
3
4
5
6
7
8
9
10
11
12
final String LEADERBOARD_KEY = "game:ranking";

// 플레이어와 점수 저장
redisTemplate.opsForZSet().add(LEADERBOARD_KEY, "Player1", 1500.0);
redisTemplate.opsForZSet().add(LEADERBOARD_KEY, "Player2", 2500.0);
redisTemplate.opsForZSet().add(LEADERBOARD_KEY, "Player3", 1800.0);

// 점수 업데이트 (100점 추가)
redisTemplate.opsForZSet().incrementScore(LEADERBOARD_KEY, "Player1", 100.0);

// 상위 2명 조회 (내림차순 기준)
Set<String> topPlayers = redisTemplate.opsForZSet().reverseRange(LEADERBOARD_KEY, 0, 1); // ["Player2", "Player3"]

5. 요약: 어떤 데이터 타입을 선택해야 할까?

데이터 타입핵심 특징최적의 활용 시나리오
String단순 Key-Value, 숫자 연산 특화API 응답 캐싱, 세션 저장, 단순 카운터 증감
List순서 보장, 양방향 데이터 입출력대기열 시스템 (Queue/Stack), 최신 피드 목록
Set중복 불가, 강력한 집합 연산 지원고유 참여자 관리, 관계 분석(교집합/차집합)
Hash객체 형태(필드-값) 데이터 저장속성이 많은 단일 엔티티(프로필, 상품) 캐싱
Sorted Set점수(Score) 기반의 자동 정렬실시간 랭킹 시스템, 리더보드, 우선순위 큐

비즈니스 요구사항과 데이터의 접근 패턴(순차적, 고유성 필요, 정렬 필요 등)을 명확히 파악하여 위 데이터 구조 중 가장 적합한 타입을 선택하는 것이 Redis 기반 시스템 설계의 핵심이다.

This post is licensed under CC BY 4.0 by the author.