@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
다음 글을 참고하여 직렬화 방법을 선택하였습니다.
https://velog.io/@bagt/Redis-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94-%EA%B4%80%EB%A0%A8-%EC%97%90%EB%9F%AC-feat.-RedisSerializer
StringRedisSerializer()의 특징
나중에 msc 확장할 생각이 있기 때문에, StringRedisSerializer를 선택했습니다.
@GetMapping("/posts/{postId}")
public ResponseEntity<ResponsePostOne> getPostOne(
@PathVariable Long postId) {
postService.addViewCntToRedis(postId);
ResponsePostOne postOne = postService.getPostOne(postId);
return ResponseEntity.status(HttpStatus.OK).body(postOne);
}
다음과 같은 Controller Api를 등록했습니다.
Post 하나를 조회 시 , addViewCntToRedis 메서드를 이용하여 조회수를 1 증가시키는 코드를 작성했습니다. 코드는 다음과 같습니다.
public void addViewCntToRedis(Long postId) {
String key = getKey(postId); //key생성 전략에 맞춰서 key값 얻는 메서드
//캐시에 값이 없으면 레포지토리에서 조회 있으면 값을 증가시킨다.
ValueOperations valueOperations = redisTemplate.opsForValue();
if(valueOperations.get(key)==null){
valueOperations.set( key,
String.valueOf(postRepository.findById(postId)
.orElseThrow(() -> new NotFoundException("게시물을 찾을 수 없습니다.")).getViews()),
Duration.ofMinutes(10));
valueOperations.increment(key);
}
log.info("value:{}",valueOperations.get(key));
}
캐시에 키에 해당하는 값이 없으면 db에서 조회한 뒤 캐시에 등록하고, 조회수를 1 증가 시켜주는 코드입니다.
Duration.ofMinutes와 같이 캐시에서 데이터가 존재할 수 있는 시간을 설정할 수 있습니다.
인메모리에 데이터가 존재하는 동안은, 계속 Post를 Get메서드로 다시 조회를 하더라도, 조회수가 증가되지 않으므로, 새로고침을 통한 조회수 중복 증가를 막을 수 있습니다.

다음과 같이 포스트를 조회 시,

Redis-cli.exe를 통해 캐시에 데이터가 등록된 것을 확인할 수 있습니다.
다음은 캐시에 등록된 수정된 조회 수를 db에 반영하기 위해 스케쥴러를 사용하는 코드 입니다.

메인 클래스에 @EnableScheduling을 등록하신 뒤 ,
@Scheduled(cron = "0 0/1 * * * ?")
public void deleteViewCntCacheFromRedis() {
Set<String> redisKeys = redisTemplate.keys("post:*:views");
Iterator<String> it = redisKeys.iterator();
while (it.hasNext()) {
String key = it.next();
String numericPart = key.substring(key.indexOf(":") + 1, key.lastIndexOf(":"));
Long postId = Long.parseLong(numericPart);
String s = (String) redisTemplate.opsForValue().get(key);
Long views = Long.parseLong(s);
//
postQueryRepository.addViewCntFromRedis(postId,views);
redisTemplate.delete(key);
redisTemplate.delete("post:"+postId+"views");
}
}
postService에 다음과 같이 1분마다 캐시를 db에 반영하는 스케쥴러를 이용한 db업데이트 코드입니다.


1분이 지난후 다시 검색을 해보면, 업데이트 쿼리가 날라가고 캐시가 비워진 것을 확인할 수 있습니다.