
단순한 이론 공부를 넘어, 실제 서비스(블로그 조회수, 랭킹)에서 Redis 명령어가 어떻게 활용되는지 실습해 보겠습니다.
시나리오: 블로그 글(/articles/{id})에 접속할 때마다 로그인 여부와 관계없이 조회수가 1씩 증가합니다.
# 1번 글 조회수 증가
INCR articles:1
# 2번 글 조회수 증가
INCR articles:2
# [Tip] 날짜별 조회수 관리 시
INCR articles:1:today
# 날짜가 바뀌는 시점에 키 이름 변경
RENAME articles:1:today articles:1:2026-05-08
시나리오: 로그인한 사용자(영문 계정)의 중복 없는 조회수와 전체 인기 글 순위를 확인합니다.
# 1. 특정 글에 방문한 유저 기록 (중복 방지)
SADD articles:1 alex
SADD articles:1 brad
SADD articles:1 chad
# 해당 글의 유니크 방문자 수 확인
SCARD articles:1
# 2. 전체 인기 글 랭킹 관리 (Sorted Set)
# 1번 글에 조회수 1 추가
ZINCRBY articles:ranks 1 articles:1
# 2번 글에 조회수 1 추가
ZINCRBY articles:ranks 1 articles:2
# 3. 랭킹 조회
# 전체 순위 확인 (오름차순)
ZRANGE articles:ranks 0 -1
# 가장 높은 조회수를 기록한 글 1개 조회 (내림차순)
ZREVRANGE articles:ranks 0 0
앞서 다룬 시나리오들을 실제 Spring Boot 프로젝트에 구현해 봅시다.
JPA 스타일로 주문 데이터를 Hash 자료구조에 저장하는 방식입니다.
ItemOrder.java (Domain)
@Getter @Setter @Builder
@NoArgsConstructor @AllArgsConstructor
@RedisHash("order")
public class ItemOrder {
@Id
private String id;
private String item;
private Integer count;
private Long totalPrice;
private String status;
}
OrderController.java
@RestController
@RequestMapping("orders")
public class OrderController {
private final OrderRepository orderRepository;
public OrderController(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@PostMapping
public ItemOrder create(@RequestBody ItemOrder order) {
return orderRepository.save(order);
}
@GetMapping
public List<ItemOrder> readAll() {
List<ItemOrder> orders = new ArrayList<>();
orderRepository.findAll()
.forEach(orders::add);
return orders;
}
@GetMapping("/{id}")
public ItemOrder readOne(@PathVariable("id") String id) {
return orderRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
@PutMapping("/{id}")
public ItemOrder update(@PathVariable String id, @RequestBody ItemOrder order) {
ItemOrder target = orderRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
target.setItem(order.getItem());
target.setCount(order.getCount());
target.setTotalPrice(order.getTotalPrice());
target.setStatus(order.getStatus());
return orderRepository.save(target);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable("id") String id) {
orderRepository.deleteById(id);
}
}
Postman같은 클라이언트로 API를 호출해보면 Redis에 데이터가 CRUD되는 것을 확인할 수 있습니다.
원하는 키 형식을 직접 제어하고 increment 기능을 활용합니다.
RedisConfig.java
조회수(Integer)를 처리하기 위해 GenericToStringSerializer를 활용한 설정입니다.
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Integer> articleTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Integer> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
return template;
}
}
ArticleController.java
@RestController
@RequestMapping("articles")
public class ArticleController {
private final ValueOperations<String, Integer> ops;
public ArticleController(RedisTemplate<String, Integer> articleTemplate) {
ops = articleTemplate.opsForValue();
}
@GetMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void read(@PathVariable("id") Long id) {
// 접속 시마다 Redis의 articles:{id} 키 값 증가
ops.increment("articles:%d".formatted(id));
}
}
이번 실습을 통해 Redis의 기본 명령어가 Spring Boot 환경에서 어떻게 코드로 녹아드는지 살펴보았습니다.
Repository는 객체 단위의 영속성이 필요할 때,
Template은 특정 데이터 구조의 기능을 세밀하게 사용해야 할 때(예: 조회수 증가) 적합합니다.