Spring 10 - Redis 적용
in Web-Programming on Spring
Redis
Server 06 - Redis 개념에서 Redis에 대한 개념을 익혔다면, 이제 Spring과 엮어서 사용을 해본다. 위에 나와있는 링크에서도 Redis에 대한 개념이 나와있지만, 여기서 간단히 요약하자면, Redis는 REmote DIctionary Storage의 약자로 모든 데이터를 메모리에 저장하고 조회하는 in-memory DB 즉, 모든 데이터를 메모리로 불러와서 처리하는 메모리 기반의 key-value 구조의 NoSQL 데이터 관리 시스템이다.
Redis 설정
Redis를 사용하기 위해선 윈도우 한정으로 4가지 방법이 있다. 윈도우즈의 WSL을 이용해서 윈도우즈 내 리눅스 내 redis를 설치하는 방법, Docker를 이용해 Redis 이미지를 돌리는 방법, 그리고 https://github.com/microsoftarchive/redis/releases 에서 직접 설치 프로그램을 다운 받아 설치 후 사용하는 방법, 그리고 embedded-redis 라이브러리를 의존하여 사용하는 방법이 있다. 이 중 WSL이나 Docker를 사용하여 Redis를 설치하는 것이 남은 방법들 보다 성능적으로 그리고 안정성이 있다.
하지만, 본 작자는 빠르게 테스트를 하기 위해 embedded-redis 라이브러리를 사용하여 spring에 사용을 하였고. 이 과정을 본 작자가 사용하는 gradle 기준으로 설명한다.
embedded-redis 의존성 설정
우선 의존성 관리하는 부분인 build.gradle에 redis와 embedded-redis 관련 라이브러리들을 dependecies에 추가한다.
dependencies {
...
// radis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// embedded radis
implementation 'it.ozimov:embedded-redis:0.7.3'
...
}
여기서 추가 주의:
dependency추가해도 아래와 같이 오류가 나는 경우가 있다.
Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation (class org.slf4j.simple.SimpleLoggerFactory ...
이 경우에는 현재 스프링에 사용하고 있는 Logback과 slf4j이 충돌되어 나오는 상황이라 한다. 이럴땐 build.gradle 에서 아래와 같은 내용을 추가해주면 된다.
configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
exclude group: 'ch.qos.logback', module: 'logback-classic'
exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j'
}
}
의존성을 설정한 후 Redis 접속 관련 설정은 application.properties에서 아래와 같이 내용을 추가한다. 로컬로 사용하기 때문에 host는 localhost, port는 6379이다. 이 번호는 redis가 기본적으로 사용하는 포트번호라 한다.
# Radis Config
spring.redis.host=localhost
spring.redis.port=6379
RedisConfig 생성
의존성을 설정한 다음 Spring 설정 파일인 RedisConfig을 생성해야 한다. 코드는 다음과 같다.
@Configuration
public class RedisConfig {
private RedisServer redisServer;
@PostConstruct
public void startRedis() throws IOException{
System.out.println("REDIS START!!!");
redisServer = RedisServer.builder()
.port(6379)
.setting("maxmemory 128M")
.build();
redisServer.start();
}
@PreDestroy
public void stopRedis(){
if(redisServer != null){
redisServer.stop();
}
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(); // Lettuce Redis 클라이언트 사용
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
- RedisConfig:
@Configuration
어노테이션을 통해 Spring 설정 클래스임을 나타내며 어플리케이션 시작 후 설정과정에서 동작하게 된다. - startRedis():
@PostConstruct
어노테이션을 통해 어플리케이션 시작 시 RedisServer를 빌드하고 시작한다. - stopRedis():
@PreDestroy
어노테이션을 통해 어플리케이션 종료 시 RedisServer를 멈춘다. - redisConnectionFactory():
RedisConnectionFactory
는 Redis 서버와의 연결을 관리하는 객체이다. 여기서Lettuce
는 비동기 방식의 고성능 Redis 클라이언트이며@Bean
에 따라 스프링 빈으로 관리가 된다. - redisTemplate():
RedisTemplate
은 Redis와 데이터를 주고받는 데 사용되는 템플릿으로 이 템플릿을 통해 Redis 서버와 연결하여 데이터를 저장하거나 조회할 수 있다. Key-Value 형태의 NoSQL DB라RedisTemplate<String, Object>
구조로 되어 있다.
Controller & Service
이제 설정이 끝났으니, 바로 써먹어 보자. 우선 Service부터 살펴본다.
RedisService
RedisService에서 키-값 구조로 저장하고, 꺼내서 읽고, 수정하고, 삭제할 수 있다. 기존 RDBMS에서 JPA를 사용해서 CRUD를 처리하였다면, Redis는 redisTemplate
을 통해 CRUD를 처리할 수 있다. 그리고 자료구조는 기본적으로 String 형으로 키-값 구조로 관리될 수 있고, 추가적으로 리스트 형과 세부 필드가 있는 해시 형으로 관리될 수 있다.
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 1. String 데이터 저장
public void save(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
// 2. String 데이터 조회
public String findByKey(String key) {
return (String) redisTemplate.opsForValue().get(key);
}
// 3. String 데이터 삭제
public void deleteByKey(String key) {
redisTemplate.delete(key);
}
// 4. 해시(Hash) 데이터 저장
public void saveHash(String key, String field, String value) {
redisTemplate.opsForHash().put(key, field, value);
}
// 5. 해시(Hash) 데이터 조회
public String findInHash(String key, String field) {
return (String) redisTemplate.opsForHash().get(key, field);
}
// 6. 전체 해시(Hash) 데이터 조회
public Map<Object, Object> findAllInHash(String key) {
return redisTemplate.opsForHash().entries(key);
}
// 7. 리스트(List) 데이터 저장 (리스트 앞에 추가)
public void saveToList(String key, String value) {
redisTemplate.opsForList().leftPush(key, value);
}
// 8. 리스트(List) 데이터 조회
public List<Object> findAllInList(String key) {
return redisTemplate.opsForList().range(key, 0, -1); // 0부터 끝까지 리스트 조회
}
// 9. 키 존재 여부 확인
public boolean exists(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
}
RedisController
Service가 다 구축이되면 이제 Controller에서 해당 서비스와 API에 맞게 호출을 해주면 된다.
@RestController
@RequestMapping("/redis")
public class RedisController {
@Autowired
private RedisService redisService;
// 1. String 데이터 저장
@PostMapping("/save")
public String saveData(@RequestParam String key, @RequestParam String value) {
redisService.save(key, value);
return "Data saved with key: " + key;
}
// 2. String 데이터 조회
@GetMapping("/get")
public String getData(@RequestParam String key) {
String value = redisService.findByKey(key);
return value != null ? "Value: " + value : "Key not found";
}
// 3. String 데이터 삭제
@DeleteMapping("/delete")
public String deleteData(@RequestParam String key) {
redisService.deleteByKey(key);
return "Data deleted with key: " + key;
}
// 4. 해시(Hash) 데이터 저장
@PostMapping("/hash/save")
public String saveHashData(@RequestParam String key, @RequestParam String field, @RequestParam String value) {
redisService.saveHash(key, field, value);
return "Hash data saved with key: " + key + ", field: " + field;
}
// 5. 해시(Hash) 데이터 조회
@GetMapping("/hash/get")
public String getHashData(@RequestParam String key, @RequestParam String field) {
String value = redisService.findInHash(key, field);
return value != null ? "Value: " + value : "Field not found in hash";
}
// 6. 전체 해시(Hash) 데이터 조회
@GetMapping("/hash/getAll")
public Map<Object, Object> getAllHashData(@RequestParam String key) {
return redisService.findAllInHash(key);
}
// 7. 리스트(List) 데이터 저장
@PostMapping("/list/save")
public String saveListData(@RequestParam String key, @RequestParam String value) {
redisService.saveToList(key, value);
return "Value saved to list with key: " + key;
}
// 8. 리스트(List) 데이터 조회
@GetMapping("/list/getAll")
public List<Object> getAllListData(@RequestParam String key) {
return redisService.findAllInList(key);
}
// 9. 키 존재 여부 확인
@GetMapping("/exists")
public String checkIfKeyExists(@RequestParam String key) {
boolean exists = redisService.exists(key);
return exists ? "Key exists: " + key : "Key not found: " + key;
}
}
Redis 이용
Controller과 Service를 모두 구축한 다음 어플리케이션으로 실행한 다음, Contoller에 매핑되 경로와 메서드를 이용하여 Redis DB를 활용할 수 있다.
- String 데이터 저장:
POST http://localhost:9980/redis/save?key=myKey&value=myValue
- String 데이터 조회:
GET http://localhost:9980/redis/get?key=myKey
- String 데이터 삭제:
DELETE http://localhost:9980/redis/delete?key=myKey
- 해시 데이터 저장:
POST http://localhost:9980/redis/hash/save?key=user:1&field=name&value=John
- 해시 데이터 조회:
GET http://localhost:9980/redis/hash/get?key=user:1&field=name
- 전체 해시 데이터 조회:
GET http://localhost:9980/redis/hash/getAll?key=user:1
- 리스트 데이터 저장:
POST http://localhost:9980/redis/list/save?key=myList&value=item1
- 리스트 데이터 조회:
GET http://localhost:9980/redis/list/getAll?key=myList
- 키 존재 여부 확인:
GET http://localhost:9980/redis/exists?key=myKey