2023. 3. 9. 17:16ㆍSpringboot/redis
설치
먼저 redis를 설치해보자.
https://github.com/microsoftarchive/redis/releases에서 원하는 버전을 다운받자.
msi파일을 실행하면 알아서 설치가 진행된다.
설치된 폴더에 가서 redis-cli.exe를 실행해보자
실행창에서 다음과 같이 테스트해보자.
ping을 입력해서 결과가 pong이 나오면 되고 ,
set key1 value1을 입력해 DB에 데이터를 저장하고 get key1을 통해 value1값을 얻을 수 있다.
간단한 redis 사용법은 다음 글을 참고하자.
https://velog.io/@rlaghwns1995/Redis-%EA%B8%B0%EB%B3%B8%EC%A0%95%EB%A6%AC
springboot에서 redis 사용하기
자바에서 제공하는 redis client는 jedis,lettuce2가지가 있는데 저는 lettuce를 사용합니다.
또 Spring에서 redis를 사용하는 방법은 repository,template 2가지 중 template를 사용합니다.
먼저 build.gradle에 dependency를 추가합니다.
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
설정파일(application.yml)에 host와 port를 설정합니다.
spring:
redis :
host : localhost
port : 6379
redis 설정파일을 만들어 줍니다. (연결에 lettuce, redis사용방법으로 template을 사용합니다)
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Bean
public RedisConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory(redisHost,redisPort);
// return new LettuceConnectionFactory(); 따로 입력안해도 설정파일에 있는 host,port알아서 읽음
//new JedisConnectionFactory(); Jedis를 연결해서 사용할 수도 있습니다.
}
// 저장하려는 자료 형태에 따라서 RedistTemplate을 추가해주면 된다.. 그리고 사용하려는 곳에서 @Autowired하면 됨
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
이제 redis를 사용하려는 곳에서 RedisTemplate 빈을 주입받아 사용하면 된다.
redisTemplate 기본 사용법
redisTemplate을 사용하는 방법은 간단하다.
- redis에서 제공하는 자료구조형태에 맞는 Operation을 얻는다.
- 얻은 Operation에서 제공하는 메소드를 https://velog.io/@rlaghwns1995/Redis-%EA%B8%B0%EB%B3%B8%EC%A0%95%EB%A6%AC 와 비교해서 사용한다.
Operation에서 제공하는 메소드와 redis 자료형태별로 제공하는 메소드가 비슷해서 사용이 어렵지 않다.
간단히 SpringbootTest에서 test해보자.
@SpringBootTest
class MyApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void StringTest(){
ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();
stringValueOperations.set("testKey", "value");
String getValue=stringValueOperations.get("testKey");
assertThat(getValue).isEqualTo( "value");
}
}
실제로 redis에서 get명령어를 통해 확인해보면 "value" 값이 들어와 있는걸 확인 할 수 있다.
원활한 테스트를 위해 삽일을 했다면 마지막에 삭제를 해줘야한다. 다음과 같이 삭제코드를 추가해서
StringTest() 테스트를 완성하자.
@SpringBootTest
class MyApplcationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void StringTest(){
ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();
stringValueOperations.set("testKey", "value");
String getValue=stringValueOperations.get("testKey");
assertThat(getValue).isEqualTo( "value");
//삭제
redisTemplate.delete("testKey");
String getValueAfterDelete = stringValueOperations.get("testKey");
assertThat(redisTemplate.hasKey("testKey")).isFalse();
assertThat(getValueAfterDelete).isNullOrEmpty();
}
}
※test에서는 Trasnaction을 적용해 test가 끝난 후 Rollback 하는게 좋지만,
RedisTemplate을 사용할 때는 Transaction 적용을 위해 설정할게 많아서 test 마지막에 삭제해주는걸로 했다.
Test 자체보다는 redisTemplate 기본 사용에 초점을 맞춘 글이니까..
String 말고도 redis에서 지원해주는 여러 자료구조형을 테스트해보자.
List
@Test
public void listTest() {
ListOperations listOperations = redisTemplate.opsForList();
listOperations.rightPush("fruit", "apple");
listOperations.rightPush("fruit", "grape");
listOperations.rightPush("fruit", "strawberry"); //fruit이라는 키로 계속 push하면 list형태로 저장됨. 왼쪽에서도 넣을 수 있다.
String apple = (String)listOperations.leftPop("fruit"); //한 개씩 뺄 수 있다. 오른쪽에서도 뺄 수 있다.
String grape = (String)listOperations.leftPop("fruit");
String strawberry = (String)listOperations.leftPop("fruit");
assertThat(apple).isEqualTo("apple");
assertThat(grape).isEqualTo("grape");
assertThat(strawberry).isEqualTo("strawberry");
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}
@Test
public void listTes2() {
ListOperations listOperations = redisTemplate.opsForList();
listOperations.rightPush("fruit", "apple");
listOperations.rightPush("fruit", "grape");
listOperations.rightPush("fruit", "strawberry"); //fruit이라는 키로 계속 push하면 list형태로 저장됨. 왼쪽에서도 넣을 수 있다.
List<String> list = listOperations.range("fruit", 0, listOperations.size("fruit") - 1);
assertThat(list).contains("apple","grape","strawberry");
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}
@Test
public void listTest3() {
ListOperations listOperations = redisTemplate.opsForList();
List<String> fruitList=new ArrayList<>();
fruitList.add("apple");
fruitList.add("grape");
fruitList.add("strawberry");
listOperations.rightPushAll("fruit",fruitList); //list를 한번에 넣을 수도 있다.
List<String> list = listOperations.range("fruit", 0, listOperations.size("fruit") - 1);
assertThat(list).contains("apple","grape","strawberry");
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}
Set
//set도 list와 방식은 비슷하다. 단지 저장되는 자료구조가 set일뿐.
// list의 pushALl 처럼 한번에 Set을 저장하는 방법은 없나보다. 반복문으로 add해야될
// store메소드의 파라미터를 잘 보면 Collection으로 key값들을 넘긴다. Set을 자료구조에 있는 키랑 합치는게 아님..
@Test
public void setTest(){
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("fruit","apple");
setOperations.add("fruit","apple");
setOperations.add("fruit","grape");
Set<String> fruit = setOperations.members("fruit");
long size=setOperations.size("fruit");
Set<String> setFromRedis=setOperations.members("fruit");
assertThat(size).isEqualTo(2);
assertThat(setFromRedis).contains("apple","grape");
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}
@Test
public void setUnionAndStoreTest(){
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("fruit1","apple");
setOperations.add("fruit1","apple");
setOperations.add("fruit1","grape");
setOperations.add("fruit2","melon");
setOperations.add("fruit2","watermelon");
setOperations.unionAndStore("fruit1","fruit2","fruit");
long size=setOperations.size("fruit");
Set<String> setFromRedis=setOperations.members("fruit");
assertThat(size).isEqualTo(4);
assertThat(setFromRedis).contains("apple","grape","melon","watermelon");
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}
sortedSet(zSet)
@Test
public void sortedSetTest(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet(); //zSet= sortedSet
zSetOperations.add("fruit","apple",0);
zSetOperations.add("fruit","apple",1); //이미 있으므로 저장안됨. apple은 score가 0
zSetOperations.add("fruit","grape",1);
long size= zSetOperations.size("fruit");
Set<String> setFromRedis = zSetOperations.range("fruit", 0, zSetOperations.size("fruit") - 1);
assertThat(size).isEqualTo(2);
assertThat(setFromRedis).containsExactly("apple","grape"); //순서보장해준다. 0,1 순
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}
Hash
@Test
public void hashTest(){
HashOperations hashOperations = redisTemplate.opsForHash();
Map<String, Integer> fruit= new HashMap<>(); //내가 하루에 먹는 과일 개수
fruit.put("apple",5);
fruit.put("grape",2);
fruit.put("banana",30);
hashOperations.putAll("fruit",fruit);
Map<String, Integer> mapFromRedis = hashOperations.entries("fruit");// {apple,5} {grape,2} {banana , 30} 의 entry 3쌍
int size= mapFromRedis.size();
assertThat(size).isEqualTo(3);
assertThat(mapFromRedis).containsKeys("apple","grape","banana");
assertThat(mapFromRedis).containsValues(5,2,30);
//삭제
redisTemplate.delete("fruit");
assertThat(redisTemplate.hasKey("fruit") ).isFalse();
}