Springboot Cache 사용기[4] Redis 사용하기
Redis Cache를 사용하기 위해선 먼저 Redis를 사용할 줄 알아야 합니다. 그래서 하나씩 만들어보겠습니다.
Redis는 Nosql DB입니다. 영속성을 지원하며, In memory DB이므로 속도가 빠른 장점으로 Cache서버의 DB로서 많이 사용됩니다.
1. 디펜던시 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
build.gradle에 위의 디펜더 시를 추가해줍니다.
2. RedisConfig
@Configuration
@RequiredArgsConstructor
@EnableRedisRepositories
public class RedisConfig {
private final RedisProperties redisProperties;
@Bean
public RedisConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory(redisProperties.getHost(),redisProperties.getPort());
}
@Bean
public RedisTemplate<?,?> redisTemplate(){
RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
2-1 RedisConnectionFactory
- Java에서 지원하는 RedisClient는 2가지가 있습니다. (Jedis, Lettuce)
- Jedis는 멀티쓰레드에서 불안정하고, Pool의 한계 등으로 현재 Springboot 2.0 이상에서는 Lettuce가 탑재되고, 사용된다.
- RedisProperties는 Redis의 기본 설정들을 가지고 있습니다.
- RedisProperties를 이용해서 Redis의 Host와 Port를 설정해줍니다.
LettuceConnectionFactory에 설정을 부여해서, 클러스터에 대한 설정도 할 수 있지만 여기서는 진행하지 않겠습니다.
2-2 RedisTemplate
Redis에 데이터를 저장하는 방법은 총 2가지 있습니다.
1. CrudRepository 상속받아 사용
- Redis를 사용할 Repository를 만들고 CrudRepository를 상속받아 사용합니다.
2. RedisTemplate 정의해서 사용
- Redis에 직렬화 설정을 부여해서 저장하는데, 자동으로 직렬화를 수행해줍니다.
- ConnectionFactory를 Luttece를 설정해줍니다.
- setKeySerializer(new StringRedisSerializer()): String으로 들어오는 key에 대한 직렬화를 수행합니다. 즉 key에 대한 직렬화 설정을 해주는 메서드입니다.
- setValueSerializer(new GenericJackson2JsonRedisSerializer)): 저장하려 하는 Object객체를 직렬화 하여 저장합니다. 즉 Value에 대한 직렬화 설정을 하주는 메서드입니다.
- RedisTemplate<String,?>: key값은 항상 String이므로 String, Value는 와일드카드를 사용해서, 다양한 객체를 저장할 수 있도록 해줍니다.
2022.04.29 추가
sprigboot 2.0 이상부터는 config 작성없이 application.properties나 yml로 작성이 가능합니다.
spring:
redis:
host: localhost
port: 6379
3. CrudRepository 사용
CrudRepository를 상속받아서 Redis를 사용해보겠습니다.
3-1. RedisMember class
@Getter
@NoArgsConstructor
@RedisHash(value = "redisMember")
public class RedisMember{
@Id
private String id;
private String name;
private int age;
public RedisMember(String name, int age) {
this.id= name;
this.name = name;
this.age = age;
}
}
- @Id는 javaPersistent의 어노테이션입니다.
- @RedisHash: Redis에 저장될 때 사용되는 prefix입니다. ex) redisMember:key 사용됩니다.
- Id가 Key값으로 사용됩니다. 저희는 name을 key값으로 사용할 예정이므로 Id에도 key를 넣어줍니다. 만약 안 넣어줄 시 Redis에 입력될 때 랜덤 한 값으로 key값이 생성됩니다.
첫 번째는 id를 미지정, 두 번째는 id에 name으로 줬을 때입니다.
prefix로 redisMember가 들어간 것을 볼 수 있습니다.
3-2. MemberRedisRepository
public interface MemberRedisRepository extends CrudRepository<RedisMember,String> {
}
- CrudRepository<>를 상속받습니다.
- Id 값을 이용하여 데이터를 찾습니다.
간단한 테스트 코드를 보겠습니다.
@Test
@DisplayName("redisRepository 테스트")
void repositoryTest(){
memberRedisRepository.save(new RedisMember("hi",10));
RedisMember redisMember = memberRedisRepository.findById("hi").get();
assertThat(redisMember.getName()).isEqualTo("hi");
}
- RedisMember id,name= hi이며, age=10인 객체를 저장합니다.
- redis가 만약 설치되어 있다면, redis-cli를 입력하고 위처럼 입력한다면 값이 저장된 것을 확인할 수 있습니다.
- Id를 가지고 데이터를 찾으므로 값을 변경하고 싶다면, 다시 저장하시면 됩니다.
@Test
@DisplayName("redisRepository 데이터변경 테스트")
void repositoryUpdateTest(){
memberRedisRepository.save(new RedisMember("hi",10));
memberRedisRepository.save(new RedisMember("hi",11));
RedisMember redisMember = memberRedisRepository.findById("hi").get();
assertThat(redisMember.getAge()).isEqualTo(11);
}
- 삭제는 delete를 이용하면 됩니다.
전체를 반환하는 테스트를 해보겠습니다. JPA와 다르게 Iterable <> 형태로 반환됩니다.
@Test
@DisplayName("redisRepository 전체 반환 테스트")
void repositoryFindAllTest(){
memberRedisRepository.save(new RedisMember("hi1",10));
memberRedisRepository.save(new RedisMember("hi2",11));
Iterable<RedisMember> all = memberRedisRepository.findAll();
List<RedisMember> list = new ArrayList<>();
all.forEach(list::add);
assertThat(list.size()).isEqualTo(2);
}
- forEach의 람다 표현식으로 List에 담아서 사용합니다.
4. RedisTemplate 사용
위에서 선언한 RedisTemplate를 사용합니다.
먼저 사용가능한 자료구조와 사용하기 위한 객체를 살펴보겠습니다.
메소드명반환 오퍼레이션 Redis 자료구조
opsForValue() | ValueOperations | String |
opsForList() | ListOperations | List |
opsForSet() | SetOperations | Set |
opsForZSet() | ZSetOperations | Sorted Set |
opsForHash() | HashOperations | Hash |
Operations연산자를 얻어서 연산을 수행합니다.
4-1. ValueOperation
Value는 String에 대해서 연산을 진행합니다.
@Autowired
RedisTemplate<String, RedisMember> redisMemberRedisTemplate;
@Test
@DisplayName("redisTemplate value 테스트")
void redisTemplateTest(){
ValueOperations<String, RedisMember> stringRedisMemberValueOperations = redisMemberRedisTemplate.opsForValue();
stringRedisMemberValueOperations.set("hi", new RedisMember("hi", 10));
RedisMember redisMember = redisMemberRedisTemplate.opsForValue().get("hi");
assertThat(redisMember.getAge()).isEqualTo(10);
}
- 연산자를 먼저 얻습니다. 그 후 set을 이용해서 객체를 저장합니다.
- 객체를 찾을 때 지정한 key를 이용해서 데이터를 찾습니다.
- 여기서 알아야할 부분은 원래 Value는 String에 대해서 연산을 진행하지만, RedisConfig에서 객체에 대한 직렬화 설정을 부여했기 때문에 자동적으로 직렬화와 역직렬 화가 수행돼, RedisMember를 저장하고 데이터를 찾을 수 있습니다.
- 마찬가지로 업데이트 연산은 set(key, 변경)을 이용하면 됩니다.
4-2 ListOperation
List에 대한 연산을 진행합니다.
@Test
@DisplayName("redisTemplate list 테스트")
void redisTemplateListTest(){
ListOperations<String, RedisMember> stringRedisMemberListOperations = redisMemberRedisTemplate.opsForList();
stringRedisMemberListOperations.rightPush("redisMemberList",new RedisMember("hi1",10));
stringRedisMemberListOperations.rightPush("redisMemberList",new RedisMember("hi2",11));
Long size = stringRedisMemberListOperations.size("redisMemberList");
RedisMember result = stringRedisMemberListOperations.index("redisMemberList", 1);
List<RedisMember> redisMemberList = stringRedisMemberListOperations.range("redisMemberList", 0, 1);
stringRedisMemberListOperations.set("redisMemberList",0,new RedisMember("m1",10));
}
- rightPush: List에 추가합니다.
- size: 크기를 알 수 있습니다.
- index: 단 건의 데이터를 조회할 수 있습니다.
- range: 다수의 데이터를 조회할 수 있습니다.
- set: 데이터의 값을 변경할 수 있습니다.
4-3 SetOperation
set에 대한 연산을 진행합니다.
@Test
@DisplayName("redisTemplate set 테스트")
void redisTemplateSetTest(){
SetOperations<String, RedisMember> stringRedisMemberSetOperations = redisMemberRedisTemplate.opsForSet();
stringRedisMemberSetOperations.add("memberSet",new RedisMember("h1",10));
stringRedisMemberSetOperations.add("memberSet",new RedisMember("h2",10));
RedisMember randomRedisMember = stringRedisMemberSetOperations.pop("memberSet");
List<RedisMember> randomMemberSet = stringRedisMemberSetOperations.pop("memberSet", 2);
Set<RedisMember> memberSet = stringRedisMemberSetOperations.members("memberSet");
Boolean result = stringRedisMemberSetOperations.isMember("memberSet", new RedisMember("h1", 10));
Long re = stringRedisMemberSetOperations.remove("memberSet", new RedisMember("hi2", 10));
}
- add: set에 추가할 수 있습니다.
- pop(key): set에 저장된 데이터중에서 랜덤 하게 한 개의 데이터를 반환합니다.
- pop(key, count): set에 저장된 데이터중에서 랜덤 하게 count만큼의 데이터를 반환합니다.
- member(key): set에 저장된 모든 데이터를 반환합니다.
- isMember(key, 데이터): 데이터의 존재 유무를 판별합니다.
- remove(key,데이터): 데이터에 관한 삭제를 진행하며, 몇 건의 데이터가 삭제됐는지 반환됩니다.
4-4. ZSetOperation
위에서 본 set과는 다르게 값에 대해 가중치를 주어 정렬을 진행합니다.
@Test
@DisplayName("redisTemplate zSet 테스트")
void redisTemplateZSetTest(){
ZSetOperations<String, RedisMember> stringRedisMemberZSetOperations = redisMemberRedisTemplate.opsForZSet();
stringRedisMemberZSetOperations.add("memberZSet",new RedisMember("hi1",10),1);
stringRedisMemberZSetOperations.add("memberZSet",new RedisMember("hi2",10),2);
Long memberZSet = stringRedisMemberZSetOperations.count("memberZSet", 0, 1);
RedisMember result = stringRedisMemberZSetOperations.popMin("memberZSet").getValue();
}
- add(key, value, 가중치): 가중치를 기준으로 value에 대한 정렬을 진행합니다.
- count(key, min, max): min~max까지의 개수를 반환합니다.
- popMin(key).getValue(): 가장 작은 가중치를 가진 데이터를 반환합니다.
4-5. HashOperation
hash를 이용하여 key과 hashKey값을 통해서 데이터를 저장, 조회합니다.
hash를 사용하기 위해선 config에 hash key값과 value값을 직렬화 해주는 과정이 필요합니다.
아래와 같이 추가해줍니다.
@Bean
public RedisTemplate<String,?> redisTemplate(){
RedisTemplate<String, ?> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer()); // 추가
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); // 추가
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
@Test
@DisplayName("redisTemplate hash 테스트")
void redisTemplateHashTest(){
HashOperations<String, Object, Object> stringObjectObjectHashOperations =
redisMemberRedisTemplate.opsForHash();
stringObjectObjectHashOperations.put("memberHashOne","name",new RedisMember("hi3",10));
Map<String, RedisMember> map = new HashMap<>();
map.put("hi1",new RedisMember("hi1", 10));
map.put("hi2",new RedisMember("hi2", 10));
stringObjectObjectHashOperations.putAll("memberHashMap",map);
RedisMember result = (RedisMember) stringObjectObjectHashOperations.get("memberHashMap", "hi1");
RedisMember redisMember = (RedisMember) stringObjectObjectHashOperations.get("memberHashOne", "name");
}
- put(key, hashKey, value): hashKey값을 이용해서 데이터를 저장합니다.
- putAll(key, Map): map의 key값이 hashKey값으로 들어가게 됩니다.
- get(key, hashKey): key값과 hashKey값을 이용해서 데이터를 찾습니다.
정리하기
Redis 디펜더시를 추가하여 사용할 수 있고, RedisConfig를 작성
- RedisConnectionFactory를 지정하고, properties를 주입해준다.
- RedisTemplate: 연산을 위한 Template
Redis에 연산 진행 2가지 방법
1. CrudRepository 상속
- @RedisHash는 prefix를 지정한다.
- Id가 key값으로 들어가게 된다. 미지정 시 랜덤 한 값으로
2. RedisTemplate 사용
- connectionFactory를 설정
- key와 value에 대한 직렬화 방식을 설정해줘 자동으로 직렬화와 역직렬화가 진행
- Value, List, Set, ZSet, Hash에 대한 연산 방법
다음은 Redis의 사용방법을 활용해서 Cache를 적용해보겠습니다. 읽어주셔서 감사합니다.
모든 코드는 아래 링크에서 확인 가능합니다.
https://github.com/rlaehdals/springbootCache
'SpringBoot > cache' 카테고리의 다른 글
Springboot Cache 사용기[5] Redis 이용한 Cache 사용 (0) | 2022.04.16 |
---|---|
Springboot Cache사용기[3] Local-Memory-Cache 동작 과정 (0) | 2022.04.02 |
Springboot Cache 사용기[2] Local-Memory-Cache (2) | 2022.03.30 |
Springboot Cache 사용기[1] (0) | 2022.03.30 |
댓글
이 글 공유하기
다른 글
-
Springboot Cache 사용기[5] Redis 이용한 Cache 사용
Springboot Cache 사용기[5] Redis 이용한 Cache 사용
2022.04.16 -
Springboot Cache사용기[3] Local-Memory-Cache 동작 과정
Springboot Cache사용기[3] Local-Memory-Cache 동작 과정
2022.04.02 -
Springboot Cache 사용기[2] Local-Memory-Cache
Springboot Cache 사용기[2] Local-Memory-Cache
2022.03.30 -
Springboot Cache 사용기[1]
Springboot Cache 사용기[1]
2022.03.30