Key, Value 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈 소스 기반의 비관계형 데이터 베이스 관리 시스템 (DBMS)입니다.
데이터베이스, 캐시, 메세지 브로커로 사용되며 인메모리 데이터 구조를 가진 저장소입니다.
//redis를 사용하기 위한 의존성
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
//캐시
implementation 'org.springframework.boot:spring-boot-starter-cache'
spring:
data:
redis:
host: localhost
port: 6379
timeout: 6
@EnableJpaAuditing //auditing 기능 위한 추가
@SpringBootApplication
@EnableCaching // 레디스 캐싱 하기위해 추가, @Cacheable 같은 어노테이션을 인식 하게 함
public class WonyShopApplication {
public static void main(String[] args) {
SpringApplication.run(WonyShopApplication.class, args);
}
}
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${sping.data.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory(host,port);
}
//RedisConnectionFactory는 Redis 서버와의 연결을 관리하는 팩토리입니다.
// LettuceConnectionFactory는 Lettuce 클라이언트를 사용하여 Redis 연결을 설정합니다.
/*
RedisTemplate: Redis data access code를 간소화 하기 위해 제공되는 클래스이다.
주어진 객체들을 자동으로 직렬화/역직렬화 하며 binary 데이터를 Redis에 저장한다.
기본설정은 JdkSerializationRedisSerializer 이다.
StringRedisSerializer: binary 데이터로 저장되기 때문에 이를 String 으로 변환시켜주며(반대로도 가능) UTF-8 인코딩 방식을 사용한다.
GenericJackson2JsonRedisSerializer는 Redis에 저장되는 객체를 JSON 형식으로 직렬화하고, 역직렬화할 수 있도록 합니다.
*/
@Bean
public RedisTemplate<String,String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
//redisConnectionFactory() 메서드를 사용하여 RedisConnectionFactory를 가져와서 RedisTemplate에 설정합니다.
return redisTemplate;
}
@Bean
public GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
어디서든 redisTemplate를 받아와서 set, get, delete.. 등을 사용할 수 있으나 그렇게 사용하지 않고 RedisDao를 작성하여 사용한다.
redisTemplate객체를 통해 redis 저장소에 Key-Value 쌍으로 데이터를 조회, 저장, 삭제를 한다.
@Component
@RequiredArgsConstructor
public class
RedisDao {
private final RedisTemplate<String, String> redisTemplate;
/**
* 값을 저장
* @param key : email
* @param refreshToken
* @param refreshTokenTime
*/
public void setRefreshToken(String key, String refreshToken, long refreshTokenTime) {
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(refreshToken.getClass())); // 리프레쉬 토큰을 직렬화 하는 코드 ( 데이터 압축효과도 있음 )
redisTemplate.opsForValue().set(email, refreshToken, minutes, TimeUnit.MINUTES);
//opsForValue(): RedisTemplate에서 ValueOperations를 가져옵니다.
//ValueOperations 객체를 통해 Redis의 값을 저장, 조회, 삭제하는 작업을 수행합니다.
}
/**
* 키로 값을 조회
* @param key : email
* @return 해당 리프레쉬토큰
*/
public String getRefreshToken(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
*키로 값을 삭제
* @param key : email
*/
public void deleteRefreshToken(String key) {
redisTemplate.delete(key);
}
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
public void setBlackList(String accessToken, String msg, Long minutes) {
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(msg.getClass()));
redisTemplate.opsForValue().set(accessToken, msg, minutes, TimeUnit.MINUTES);
}
public String getBlackList(String key) {
return redisTemplate.opsForValue().get(key);
}
public boolean deleteBlackList(String key) {
return Boolean.TRUE.equals(redisTemplate.delete(key));
}
/***
* 레디스에 있는 모든 데이터를 삭제
*/
public void flushAll(){
redisTemplate.getConnectionFactory().getConnection().serverCommands().flushAll();
}
//serverCommands().flushAll()을 호출하여 Redis 서버의 flushAll() 명령을 실행하여 모든 데이터를 삭제합니다.
}
RedisCacheManager를 등록할 cacheConfig 설정
Spring CacheManager타입의 RedisCacheManager를 빈으로 등록해주면 Spring에서는 캐싱을 할 때 로컬 캐시에 저장하지 않고 redis에 저장하게 됩니다. 위와 같이 redisCacheManager를 등록해준다.
여기까지 끝났다면 스프링에서 레디스 캐시를 사용하기 위한 준비는 끝났다.
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(
RedisConnectionFactory connectionFactory, ResourceLoader resourceLoader) {
RedisCacheConfiguration defaultConfig
= RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())// value Serializer 변경
);
//disableCachingNullValues()를 호출하여 null 값 캐싱을 비활성화합니다. 이는 Redis에 null 값을 저장하지 않도록 설정합니다.
//serializeValuesWith()를 호출하여 값 직렬화를 구성합니다.
// 위의 코드에서는 GenericJackson2JsonRedisSerializer를 사용하여 값 직렬화를 수행합니다.
// 이는 값이 JSON 형식으로 직렬화되어 Redis에 저장되고 검색됩니다.
//구성이 완료된 RedisCacheConfiguration을 사용하여 RedisCacheManager를 생성합니다.
//이렇게 구성된 RedisCacheManager는 Spring Boot 애플리케이션에서 Redis 캐시를 관리하는 데 사용됩니다.
// 캐시 설정과 Redis 연결을 제어하고 캐시 작업을 수행할 때 RedisCacheManager를 주입받아 사용할 수 있다.
//redisCacheConfigMap은 Redis 캐시의 구성 정보를 담는 맵입니다.
Map<String, RedisCacheConfiguration> redisCacheConfigMap
= new HashMap<>();
redisCacheConfigMap.put(
CacheNames.USERBYUSERNAME,
defaultConfig.entryTtl(Duration.ofHours(4)) //entryTtl()을 호출하여 캐시 항목의 만료 시간(TTL)을 설정합니다. 캐시 수명 4시간
);
// ALLUSERS에 대해서만 다른 Serializer 적용
redisCacheConfigMap.put(
CacheNames.ALLUSERS,
defaultConfig.entryTtl(Duration.ofHours(4))
.serializeValuesWith( //serializeValuesWith()를 호출하여 값을 직렬화하는 방식을 설정합니다.
RedisSerializationContext
.SerializationPair
.fromSerializer(new JdkSerializationRedisSerializer())
)
);
redisCacheConfigMap.put(
CacheNames.LOGINUSER,
defaultConfig.entryTtl(Duration.ofHours(2))
);
return RedisCacheManager.builder(connectionFactory)
.withInitialCacheConfigurations(redisCacheConfigMap)
.build();
}
}
// //전체조회시
// redisCacheConfigMap.put(
// CacheNames.GETBOARD,
// defaultConfig.entryTtl(Duration.ofHours(4))
// .serializeValuesWith(
// RedisSerializationContext
// .SerializationPair
// .fromSerializer(new JdkSerializationRedisSerializer(resourceLoader.getClassLoader()))
// )
// );
/**
* 위의 코드는 캐시 이름을 상수로 정의하는 클래스인 CacheNames를 나타냅니다. 각 상수는 특정 캐시 영역을 식별하는 문자열 값을 가지고 있습니다.
* 이러한 캐시 이름 상수는 주로 Spring Cache와 같은 캐싱 기능을 사용할 때 캐시 이름을 지정하는 데 사용됩니다.
* 예를 들어, @Cacheable 애노테이션에서 cacheNames 속성에 캐시 이름을 설정할 때 이러한 상수를 사용할 수 있습니다.
*
* 상수를 사용하여 캐시 이름을 정의하면, 캐시 영역을 구분하고 캐시에 저장된 데이터를 관리하기 쉬워집니다.
* 또한, 캐시 이름을 상수로 정의함으로써 오타나 잘못된 캐시 이름 사용을 방지할 수 있습니다.
*/
public class CacheNames {
public static final String USERBYUSERNAME = "CACHE_USERBYUSERNAME";
public static final String ALLUSERS = "CACHE_ALLUSERS";
public static final String LOGINUSER = "CACHE_LOGINUSER";
}
/**
* 로그인 반환값으로 user를 userResponseDto 담아 반환하고 컨트롤러에서 반환된 객체를 이용하여 토큰 발행한다.
*/
@Cacheable(cacheNames = CacheNames.LOGINUSER, key = "'login'+ #p0.getEmail()", unless = "#result== null")
@Transactional
public UserResponse login(LoginRequest loginRequest) {
String email = loginRequest.getEmail();
String password = loginRequest.getPassword();
User user = userRepository.findByEmail(email).orElseThrow(
() -> new CustomException(ExceptionStatus.WRONG_EMAIL)
);
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new CustomException(ExceptionStatus.WRONG_PASSWORD);
}
return new UserResponse().of(user);// user객체를 dto에 담아서 반환
}
/**
* 로그아웃
* @param accessToken
* @param email
* @return
*/
@CacheEvict(cacheNames = CacheNames.USERBYEMAIL, key = "'login'+#p1")
@Transactional
public ResponseEntity logout(String accessToken, String email) {
// 레디스에 accessToken 사용못하도록 등록
Long expiration = jwtProvider.getExpiration(accessToken);
redisDao.setBlackList(accessToken, "logout", expiration);
if (redisDao.hasKey(email)) {
redisDao.deleteRefreshToken(email);
} else {
throw new IllegalArgumentException("이미 로그아웃한 유저입니다.");
}
return ResponseEntity.ok("로그아웃 완료");
}
UserRepository
public interface UserRepository extends JpaRepository<User, Long> {
@Cacheable(cacheNames = CacheNames.USERBYEMAIL, key = "'login'+#p0", unless = "#result==null")
Optional<User> findByEmail(String email);
}
로그인 후 레디스에 refreshToken 이 저장된 것을 확인 할 수 있다.
로그아웃 후 레디스에서 refreshToken이 삭제 된것을 확인 할 수 있다.
Redis는 redis-cli라는 인터페이스를 제공하여 캐시를 눈으로 확인할 수 있도록 지원한다.redis-cli를 설치하고,
@Cacheable(cacheNames = CacheNames.LOGINUSER, key = "'login'+ #p0.getEmail()", unless = "#result== null")
@Transactional
public UserResponse login(LoginRequest loginRequest) {
}
@Cacheable(cacheNames = CacheNames.USERBYEMAIL, key = "'login'+#p0", unless = "#result==null")
Optional<User> findByEmail(String email);
이 두 코드에 의해
2) "CACHE_USERBYEMAIL::logincustomer123@naver.com"
3) "CACHE_LOGINUSER::logincustomer123@naver.com"
캐싱에 저장에 저장됨을 볼 수 있다.
exists key
3. get key
해당 키에 대한 value 확인 하는 명령어
해당 키로 값을 조회하면 UserResponseDto 로 user 객체를 저장하고 있다.
get key
4.flushall
현재 저장되어 있는 모든key를 삭제 할 때에는 flushall 명령을 사용한다.
flushall
로그아웃 후 해당 키가 캐시에 존재 하지 않음을 확인 할 수 있다.
그리고 추후 레디스에서 accessToken을 사용하지 못하도록
redio.setBlackList(accessToken,"logout",expiration) 등록 했다. 시큐리티필터처리 과정에서 "logout" 문구가 있으면 에러메세지 처리 하도록 구현했다.
@Component
@RequiredArgsConstructor
public class
RedisDao {
private final RedisTemplate<String, String> redisTemplate;
public void setBlackList(String accessToken, String msg, Long minutes) {
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(msg.getClass()));
redisTemplate.opsForValue().set(accessToken, msg, minutes, TimeUnit.MINUTES);
}
Spring Cache 캐시 추상화 기본적인 사용법 @Cacheable @CachePut
Spring Data Redis
Redis-SpringBoot-Redis-를-활용하여-RefreshToken-성능-개선하기
[#2] Redis 캐시를 통해 읽기 성능 향상하기
https://melonplaymods.com/2023/06/10/zizh-6-st-johns-wort-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/slendytubbies2-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/scp-goi-the-horizon-initiative-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/biochemical-products-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/scp-people-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/armored-giant-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/mini-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/a-house-with-a-playground-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/wall-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/dollars-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/wha-2112-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/mi-28-helicopter-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/zombie-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/cat-statue-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/pistol-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/sherman-tank-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/zabuza-momochinpc-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/borie-or-boreas-type-095-katyusha-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/el-d-215-mod-for-melon-playground-2/
https://melonplaymods.com/2023/06/11/german-soldiers-from-wwiisvproplayer-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/kids-mod-for-melon-playground-2/
https://melonplaymods.com/2023/06/10/mlbb-kof-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/edward-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/doll-grabbing-machine-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/cats-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/superman-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/half-life-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/cactus-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/old-cannon-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/rainbow-exe-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/kfc-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/batman-mod-for-melon-playground-2/
https://melonplaymods.com/2023/06/11/flood-dragon-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/building-and-map-city-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/car-repair-shop-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/t-rex-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/usp-s-pistol-from-block-strike-mod-for-melon-playground/
https://melonplaymods.com/2023/06/10/stumble-guys-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/mercedes-c-class-mod-for-melon-playground/
https://melonplaymods.com/2023/06/11/cannibal-mod-for-melon-playground/
" ```java" 해주세요...