JSCODE Logo
프로그래밍 과외블로그후기멘토진
회사명 : JSCODE대표 : 박재성사업자 등록번호 : 244-22-01557통신판매업 : 제 2023-인천미추홀-0381 호
학원 명칭 : 제이에스코드(JSCODE)원격학원학원설립ㆍ운영 등록번호 : 제6063호

서울특별시 구로구 경인로 20가길 11(오류동, 아델리아)

Copyright ⓒ 2025 JSCODE - 최상위 현업 개발자들의 프로그래밍 교육 All rights reserved.

이용약관개인정보처리방침
← 블로그 목록으로 돌아가기

[실습] 좋아요 수 폭증으로 인한 DB 부하를 Redis로 해결하기 - 2

JSCODE 박재성
JSCODE 박재성
2026-01-12
author
JSCODE 박재성
category
Redis
createdAt
Jan 12, 2026
series
비전공자도 이해할 수 있는 Redis 중급/실전
slug
reduce-db-load-from-like-traffic-with-redis-part-2
type
post
updatedAt
Jan 12, 2026 12:17 AM

✅ 문제 원인

누군가 좋아요를 한 번 누를 때마다 DB에 계속해서 요청을 보낸다. 만약 1000명의 사용자가 글을 보고 좋아요를 누르면 1000번의 DB 쓰기 작업을 해야 한다. DB 부하의 원인은 크게 2가지이다.
  1. 한 번의 요청이 일어날 때마다 매번 DB에 요청을 보내야 한다.
  1. DB는 디스크에 데이터를 기록하기 때문에 비교적 속도가 느리다.
 
 

✅ 해결책

Write Back 전략을 활용하면 된다. Write Back 전략이란 클라이언트의 요청이 들어오면 먼저 Redis에 데이터를 쌓아두고 클라이언트에게 먼저 응답을 반환하고나서, Redis에 쌓인 데이터를 스케줄러가 주기적으로 DB에 한꺼번에 저장하는 방식을 뜻한다. 즉, Redis가 데이터를 쌓아두는 Queue의 역할을 한다.
notion image
 

✅ 실습

  1. Redis에 데이터를 쌓아두는 로직 추가하기
    1. 클라이언트이 요청이 들어오면 Redis에 데이터를 쌓아두고, 클라이언트에 먼저 빠르게 응답을 반환하도록 코드를 추가해보자.
       
      LikeService
      @Service @RequiredArgsConstructor public class LikeService { private final LikeRepository likeRepository; private final RedisTemplate<String, String> redisTemplate; ... public void likePostWithRedis(LikePostRequestDto likePostRequestDto) { // Redis에 넣을 value 값 생성 // (원래는 데이터를 JSON으로 직렬화를 많이 하는 편이지만, 편의상 간단한 문자열 조합으로 대체) Long userId = likePostRequestDto.getUserId(); Long postId = likePostRequestDto.getPostId(); String value = userId + ":" + postId; // like_queue라는 List에 데이터를 오른쪽으로 Push해서 넣는다. // Redis 명령어에서 'RPUSH like_queue [value]'와 동일하게 작동한다. redisTemplate.opsForList().rightPush("like_queue", value); } }
       
      LikeController
      @RestController @RequiredArgsConstructor @RequestMapping("/likes") public class LikeController { private final LikeService likeService; ... @PostMapping("/redis") public void likePostWithRedis( @RequestBody LikePostRequestDto likePostRequestDto ) { likeService.likePostWithRedis(likePostRequestDto); } }
       
  1. 스케줄러가 주기적으로 DB에 한꺼번에 저장하도록 코드 작성하기
    1. RedisIntermediateProjectApplication
      @EnableScheduling @SpringBootApplication public class RedisIntermediateProjectApplication { public static void main(String[] args) { SpringApplication.run(RedisIntermediateProjectApplication.class, args); } }
       
      LikeScheduler
      @Component @RequiredArgsConstructor @Slf4j public class LikeScheduler { private final RedisTemplate<String, String> redisTemplate; private final LikeRepository likeRepository; // 1초마다 실행 (실무에서는 데이터 양에 따라 조절) @Scheduled(fixedDelay = 1000) public void saveLikesToDb() { // 1. Redis에서 데이터를 1000개 꺼내오기 // (큐에 있는 데이터를 하나씩 꺼내서 리스트에 담기) List<Like> likesToSave = new ArrayList<>(); while (true) { // like_queue라는 List에서 왼쪽(첫 부분)에서 데이터를 꺼내온다. // Redis 명령어에서 'LPOP like_queue'와 동일하게 작동한다. String value = redisTemplate.opsForList().leftPop("like_queue"); if (value == null) break; String[] split = value.split(":"); Long userId = Long.parseLong(split[0]); Long postId = Long.parseLong(split[1]); likesToSave.add(new Like(userId, postId)); if (likesToSave.size() >= 1000) break; } likeRepository.saveAll(likesToSave); log.info("DB 저장 완료: {} 건", likesToSave.size()); } }
      참고) saveAll() 대신에 jdbcTemplate의 batchUpdate 쿼리를 사용하면 더 효율화를 시킬 수 있다. 하지만 이번 실습에서는 간단하게 테스트를 하기 위해 saveAll()을 사용했다.
       
  1. Spring Boot 서버 재실행시키기
    1. notion image
       
  1. 부하테스트 스크립트 살펴보기
    1. 이전에 실행시켰던 스크립트(script_1-1.js)와 코드가 대부분 똑같고, 새로 만든 API에 맞게 주소만 변경해주었다.
      scripts/script_1-2.js
      import http from 'k6/http'; import { check, sleep } from 'k6'; import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; export const options = { // 가상 유저(VUs) 1000명으로 설정 vus: 1000, // 테스트를 10초 동안 진행 duration: '10s', }; export default function () { // 랜덤 데이터 생성 // 유저는 1~10,000명, 게시글은 1~100개라고 가정 const userId = randomIntBetween(1, 10000); const postId = randomIntBetween(1, 100); // 좋아요 API const url = 'http://localhost:8080/likes/redis'; const payload = JSON.stringify({ userId: userId, postId: postId, }); const params = { headers: { 'Content-Type': 'application/json', }, }; // POST 요청 전송 const res = http.post(url, payload, params); // 응답 상태 코드 확인 (200 OK가 왔는 지 체크) check(res, { 'status is 200': (r) => r.status === 200, }); }
       
  1. k6 부하테스트 스크립트 실행시키기
    1. Spring Boot의 프로젝트 경로에서 아래 명령어를 실행시키자.
      $ k6 run scripts/script_1-2.js
      notion image
      부하 테스트를 진행해보면 1초에 약 30,000개의 요청을 처리하는 걸 확인할 수 있다. DB에 바로 저장하는 것이 아닌 Redis에 데이터를 먼저 쌓아두는 형식이라 TPS(1초에 처리할 수 있는 요청의 수)가 확 늘었다.
       
      그러고 Redis에 쌓아둔 데이터는 스케쥴러를 활용해 1000개씩 DB에 업데이트를 시키는 방식으로 처리한다.
      notion image
이런 구조로 인해 갑자기 폭증하는 트래픽을 DB가 직접적으로 받지 않고, Redis에 폭증한 트래픽을 쌓아두고 DB가 스케쥴러에 의해 차근차근 데이터를 처리하게끔 작동한다. 이로 인해 한 번에 몰려드는 트래픽으로부터 DB의 부하를 방지할 수 있으면서도, 클라이언트한테는 빠르게 응답할 수 있는 이점을 동시에 가져갈 수 있다.
이로써 좋아요 수 폭증으로 인한 DB 부하를 Redis로 해결했다.
 
author
category
Redis
createdAt
Jan 12, 2026
series
비전공자도 이해할 수 있는 Redis 중급/실전
slug
type
series-footer
updatedAt
Jan 12, 2026 12:26 AM
📎
이 글은 비전공자도 이해할 수 있는 Redis 중급/실전 강의의 수업 자료 중 일부입니다.