일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- testcode
- 테스트 코드
- Github Actions
- rabbitmq
- swagger
- AWS
- docker
- aop
- DevOps
- JWT
- algorithm
- 객체지향원칙
- MSA
- Java
- spring boot
- querydls
- Intellij
- Til
- 아키텍처
- Kafka
- springboot
- EC2
- trouble shooting
- Redis
- 어노테이션
- JPA
- CI/CD
- 프로그래머스
- 유효성 검사
- 멀티 모듈
- Today
- Total
개발노트
25.03.05 Redis란 무엇인가? & RedisTemplate 사용법 본문
개요
redis를 사용해보려한다. 개념과 특징을 정리하고 자료형의 주요 메서드를 알아본 다음 RedisTemplate 를 구현하는 법도 정리해본다.
Redis란?
Redis(Remote Dictionary Server)는 오픈 소스 인메모리 데이터 저장소로 주로 캐시, 세션 저장소, 메시지 브로커 등의 용도로 사용된다.
Redis의 특징
인메모리 데이터 저장소
- 데이터를 메모리 ( RAM )에 저장하여 읽기/쓰기 속도가 매우 빠름
- 디스크보다 접근 속도가 빠르므로 캐싱에 최적
Key-Value 구조
- 지원 데이터 타입
- 문자열(String)
- 리스트(List)
- 셋(Set)
- 정렬된 셋(Sorted Set)
- 해시(Hash)
퍼시스턴스(Persistence) 지원
- 데이터를 디스크에 저장하여 데이터 손실 방지 가능
- 디스크 저장 방식
- RDB (Redis Database Snapshot): 일정 간격으로 저장
- AOF (Append Only File): 모든 명령을 로그로 기록
분산 처리 및 스케일링
- Redis Cluster를 활용하면 수평 확장(Sharding) 가능
- 여러 Redis 노드를 Replication(복제) 설정하여 부하 분산
Pub/Sub 기능
- 메시지 브로커 역할 가능 (예: 채팅 시스템, 알림 서비스)
Redis 자료형
String (문자열)
- 가장 기본적인 데이터 타입 (Key-Value 구조)
- 문자열뿐만 아니라 숫자 값도 저장 가능
SET key "Hello" # key에 "Hello" 저장
GET key # "Hello"
SET count 1 # 숫자 저장 가능
INCR count # count = count + 1 (존재하지않는 키값이면 자동으로 생성)
DECR count # count = count - 1
APPEND key " Redis" # "Hello Redis" (기존 값에 추가)
STRLEN key # 문자열 길이 반환 (11)
List (리스트, 배열)
- 순서가 있는 데이터 저장 (배열과 유사)
- FIFO(Queue)나 LIFO(Stack) 구조로 활용 가능
- index는 동일하게 0 부터 시작
LPUSH list "A" # 리스트 앞에 "A" 추가
LPUSH list "B" # 리스트 앞에 "B" 추가
RPUSH list "C" # 리스트 끝에 "C" 추가
LRANGE list 0 -1 # ["B", "A", "C"] (전체 목록 조회)
LPOP list # "B" (왼쪽에서 하나 제거)
RPOP list # "C" (오른쪽에서 하나 제거)
LLEN list # 리스트 길이 반환 (1)
Set (집합)
- 중복을 허용하지 않는 데이터 저장
- 빠른 멤버 조회 및 집합 연산 가능
SADD mySet "A" # "A" 추가
SADD mySet "B" # "B" 추가
SADD mySet "A" # 중복 값이므로 추가되지 않음
SMEMBERS mySet # ["A", "B"]
SISMEMBER mySet "A" # true (A가 존재하는지 확인)
SREM mySet "B" # "B" 삭제
SCARD mySet # 개수 반환 (1)
교집합 찾는 예시
SADD user1_likes "골프" "테니스" "농구"
SADD user2_likes "테니스" "축구"
SINTER user1_likes user2_likes # 공통 관심사 ["테니스"]
Sorted Set (정렬된 집합, 랭킹 시스템)
- Set과 유사하지만 각 값에 점수(Score)가 있음
- 점수를 기준으로 자동 정렬됨
ZADD ranking 100 "User1" # User1 점수 100
ZADD ranking 200 "User2" # User2 점수 200
ZRANGE ranking 0 -1 # ["User1", "User2"] (오름차순 조회)
ZREVRANGE ranking 0 -1 # ["User2", "User1"] (내림차순 조회)
ZRANK ranking "User1" # 0 (User1 순위)
ZREM ranking "User1" # User1 삭제
Hash (JSON과 유사한 필드-값 저장)
- Key-Value 안에 또 다른 Key-Value를 저장 (Object 저장 가능)
- 데이터베이스의 행(Row)과 비슷한 구조
HSET user:1 name "승현" # user:1의 name 필드 저장
HSET user:1 age 27 # user:1의 age 필드 저장
HGET user:1 name # "승현" (특정 필드 값 가져오기)
HGETALL user:1 # {"name": "승현", "age": "27"} (전체 데이터 조회)
HDEL user:1 age # age 필드 삭제
HEXISTS user:1 age # false (age 필드가 존재하는지 확인)
Pub/Sub (메시지 브로커 역할)
- 비동기 이벤트 시스템을 구축할 때 사용
- 게시(Publish)와 구독(Subscribe) 방식
SUBSCRIBE channel1 # 채널 구독
PUBLISH channel1 "Hello Redis" # 메시지 전송
정리
✔ String → 기본 문자열 저장
✔ List → 순서가 있는 배열 (Queue, Stack 가능)
✔ Set → 중복 없는 데이터 저장
✔ Sorted Set → 점수 기반 정렬된 데이터
✔ Hash → JSON과 유사한 구조
✔ Pub/Sub → 실시간 메시지 시스템
RedisTemplate
Spring Boot에서 Redis와 쉽게 상호작용할 수 있도록 제공하는 템플릿 클래스이다.
Redis는 Key-Value 기반의 데이터 저장소이며, Java에서 효율적으로 사용하려면
직렬화, 데이터 타입, 연결 관리등의 작업이 필요하다.
이를 쉽게 처리할 수 있도록 도와주는 것이 RedisTemplate이다.
직렬화 설정(String, JSON 등)을 통해 데이터 저장 방식을 조정 가능
객체(Object)를 JSON으로 변환하여 저장 가능 (GenericJackson2JsonRedisSerializer 사용)
다양한 Redis 명령어를 쉽게 사용할 수 있음 (opsForValue, opsForList, opsForHash 등)
기능 | 설명 | Redis 명령어 예시 |
opsForValue() | 문자열(String) 데이터 저장 | SET key value, GET key |
opsForList() | 리스트(List) 데이터 저장 | LPUSH key value, LRNAME Key |
opsForSet() | 집합(Set) 데이터 저장 | SADD key value, SMEMBERS key |
opsForHash() | 해시(Hash) 데이터 저장 | HSET key field value, HGET key field |
opsForZSet() | 정렬된 집합(Sorted Set) 데이터 저장 | ZADD key score value, ZRANGE key |
사용 예시
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 문자열(Key) 직렬화: 사람이 읽을 수 있도록 StringRedisSerializer 사용
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 값(Value) 직렬화: JSON 형식으로 저장
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void saveStringData() {
redisTemplate.opsForValue().set("message", "Hello, Redis!");
String value = (String) redisTemplate.opsForValue().get("message");
System.out.println("저장된 값: " + value);
}
// 실행 결과 : 저장된 값: Hello, Redis!
객체(Object) 저장 및 조회 (JSON 직렬화)
enericJackson2JsonRedisSerializer을 사용하면 Java 객체를 JSON으로 저장
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
public void saveUser() {
User user = new User("승현", 25);
redisTemplate.opsForValue().set("user:1001", user);
User savedUser = (User) redisTemplate.opsForValue().get("user:1001");
System.out.println("저장된 유저: " + savedUser.getName() + ", 나이: " + savedUser.getAge());
}
// 실행 결과 : 저장된 유저: 승현, 나이: 25
리스트(List) 저장 & 조
public void saveListData() {
redisTemplate.opsForList().rightPush("numbers", "one");
redisTemplate.opsForList().rightPush("numbers", "two");
List<Object> numbers = redisTemplate.opsForList().range("numbers", 0, -1);
System.out.println("저장된 리스트: " + numbers);
}
// 실행 결과 : 저장된 리스트: [one, two]
해시(Hash) 저장 & 조회
public void saveHashData() {
redisTemplate.opsForHash().put("user:1002", "name", "Jane");
redisTemplate.opsForHash().put("user:1002", "age", 30);
String name = (String) redisTemplate.opsForHash().get("user:1002", "name");
Integer age = (Integer) redisTemplate.opsForHash().get("user:1002", "age");
System.out.println("저장된 유저 정보: 이름=" + name + ", 나이=" + age);
}
// 실행 결과 : 저장된 유저 정보: 이름=Jane, 나이=30
집합(Set) 및 정렬된 집합(Sorted Set) 저장 & 조회 / 랭킹 조회 / 조회수
@RestController
@RequiredArgsConstructor
public class ArticlesController {
private final RedisTemplate<String, Object> redisTemplate;
/**
*
* -- 1. 내 블로그 별 조회수를 Redis 로 확인하고 싶다.
* -- 1. 블로그 url의 path는 '/articles/{id}' 형식이다.
* -- 2. 로그인 여부와 상관없이 새로고침 될때마다 조회수가 하나 증가한다.
* -- 3. 이를 관리하기 위해 적당한 데이터 타입을 선정하고,
* -- 4. 사용자가 임의의 페이지에 접속할 때 실행될 명령을 작성해보자.
*
* -- String - INCR(++), DECR(--)
* -- INCR articles:{id}
* INCR articles:1
* INCR articles:2
*/
@GetMapping("/articles/{id}")
@ResponseBody
public String getViews(@PathVariable("id") Long id){
redisTemplate.opsForValue().increment("articles:" +id);
return String.valueOf(redisTemplate.opsForValue().get("articles:" +id));
}
/**
* -- 2. 블로그에 로그인한 사람들의 조회수와 가장 많은 조회수를 기록한 글을 Redis로 확인하고싶다.
* -- 1. 블로그 url의 path는 '/articles/{id}' 형식이다.
* -- 2. 로그인 한 사람들의 계정은 영문으로만 이뤄져 있다.
* -- 3. 이를 관리하기 위해 적당한 데이터 타입을 선정하고,
* -- 4. 사용자가 입의의 페이지에 접속할 때 실행될 명령을 작성해보자.
* -- 4. 만약 상황에 따라 다른 명령이 실행되어야 한다면, 주석으로 추가해보자.
*
* -- Set
* sadd articles:1 alex
* sadd articles:1 brad
* sadd articles:1 chad
* scard articles:1
*
* sadd articles:2 alex
* sadd articles:2 chad
* scard articles:2
*
* sadd articles:1 alex
*
* -- sadd의 결과에 따라 명령어를 실행하거나 말거나
* -- 0은 스킵
* -- 1인 경우? sorted set
* zadd articles:ranks 1 articles:1
* zincrby articles:ranks 1 articles:1
*
* -- 가장 높은 조회수를 보고싶다면
* zrevrange articles:ranks 0 0
* zrange articles:ranks 0 0 REV
*/
@GetMapping("/articles/rank/{id}/{username}")
public String getRank(@PathVariable("id") Long id, @PathVariable("username")String username){
String userKey = "articles:" + id;
String rankKey = "articles:ranks";
// 사용자 조회 여부 확인 (sadd : 새로운 유저가 조회하면 1 , 아니면 0 반환)
Long added = redisTemplate.opsForSet().add(userKey,username);
// 새로운 사용자라면 조회수 증가
if (added != null && added == 1){
redisTemplate.opsForZSet().incrementScore(rankKey,userKey,1);
}
Long views = redisTemplate.opsForSet().size(userKey);
// 특정 게시글을 조회수 확인
return "Article " + id + " 조회수 : " + views;
}
@GetMapping("/articles/top")
public String getTopArticle() {
String rankKey = "articles:ranks";
// 조회수가 가장 높은 게시글 가져오기 (최상위 1개)
Set<ZSetOperations.TypedTuple<Object>> topArticle =
redisTemplate.opsForZSet().reverseRangeWithScores(rankKey, 0, 0);
if (topArticle != null && !topArticle.isEmpty()) {
ZSetOperations.TypedTuple<Object> article = topArticle.iterator().next();
return "가장 많이 본 게시글: " + article.getValue() + " (조회수: " + article.getScore() + ")";
}
return "조회된 게시글이 없습니다.";
}
}
'DataBase' 카테고리의 다른 글
25.04.16 Redis 에서 루아(Lua) 스크립트란? (0) | 2025.04.16 |
---|---|
25.03.08 캐싱 전략 과 예시 (0) | 2025.03.08 |