JSCODE Logo
JSCODE 박재성JSCODE 제이온JSCODE 시니
과외유튜브블로그후기
완강 후기 이벤트블로그 리뷰 이벤트AWS SAA 합격 후기 이벤트
회사명 : JSCODE대표 : 박재성사업자 등록번호 : 244-22-01557통신판매업 : 제 2023-인천미추홀-0381 호

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

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

이용약관개인정보처리방침

비전공자도 이해할 수 있는 Redis 중급/실전

Redis로 ‘쓰기 작업’에 대한 DB 부하 줄이기

Redis의 수많은 자료 구조를 효율적으로 배우려면?
List 자료 구조 (RPUSH, LRANGE, LPOP 등)
[실습] 좋아요 수 폭증으로 인한 DB 부하를 Redis로 해결하기 - 1
[실습] 좋아요 수 폭증으로 인한 DB 부하를 Redis로 해결하기 - 2

Redis로 동시성 이슈 해결하기

String 자료 구조 (SET, GET, DEL, SETNX)
[실습] 재고를 차감할 때 동시성 이슈로 인한 오차가 발생하지 않도록 만들기 - 1
[실습] 재고를 차감할 때 동시성 이슈로 인한 오차가 발생하지 않도록 만들기 - 2
[실습] 재고를 차감할 때 동시성 이슈로 인한 오차가 발생하지 않도록 만들기 - 3

Redis로 인기 검색어 구현하기

Sorted Set 자료 구조 (ZADD, ZRANGE, ZINCRBY)
[실습] 많이 검색한 키워드 Top 10을 보여주는 '인기 검색어 기능’ 만들기 - 1
[실습] 많이 검색한 키워드 Top 10을 보여주는 '인기 검색어 기능’ 만들기 - 2

Redis로 API 요청 횟수 제한 기능 구현하기 (Rate Limiting)

Sorted Set 자료 구조 (ZCARD, ZREMRANGEBYSCORE, EXPIRE)
[실습] 악의적인 요청을 방지하기 위해, 10초당 API 요청 횟수를 5회로 제한하기 - 1
[실습] 악의적인 요청을 방지하기 위해, 10초당 API 요청 횟수를 5회로 제한하기 - 2

Redis를 활용해 내 위치를 기준으로 반경 1km에 위치한 카페 찾기

Geospatial 자료 구조 (GEOADD, GEOPOS, GEODIST, GEOSEARCH)
[실습] 내 위치를 기준으로 반경 1km에 위치한 카페 찾기 - 1
[실습] 내 위치를 기준으로 반경 1km에 위치한 카페 찾기 - 2

Redis로 하루 동안 서비스에 방문한 사용자 수(DAU) 카운팅하기

Set 자료 구조 (SADD, SMEMBERS, SCARD)
Bitmap 자료 구조 (SETBIT, GETBIT, BITCOUNT)
Set과 Bitmap의 공통점 / 차이점
[실습] 하루 동안 서비스에 방문한 사용자 수(DAU) 카운팅하기 - 1
[실습] 하루 동안 서비스에 방문한 사용자 수(DAU) 카운팅하기 - 2
[실습] 하루 동안 서비스에 방문한 사용자 수(DAU) 카운팅하기 - 3

Redis를 세션 스토리지로 활용하기

Hash 자료 구조 (HSET, HGET, HGETALL)
서버가 2대 이상일 경우, Redis로 세션 스토리지를 구축해야 하는 이유
[실습] 인증 기능을 로컬 세션 스토리지로 구현했을 때 발생하는 문제점 - 1
[실습] 인증 기능을 로컬 세션 스토리지로 구현했을 때 발생하는 문제점 - 2
[실습] Redis로 세션 스토리지 구축하기
← 블로그 목록으로 돌아가기

[실습] 많이 검색한 키워드 Top 10을 보여주는 '인기 검색어 기능’ 만들기 - 1

JSCODE 박재성
JSCODE 박재성
2026. 02. 24.
author
JSCODE 박재성
category
Redis
createdAt
Jan 12, 2026 12:08 AM
isPublic
isPublic
series
비전공자도 이해할 수 있는 Redis 중급/실전
slug
build-popular-search-keywords-feature-part-1
type
post
updatedAt
Feb 24, 2026 11:00

✅ 인기 검색어 기능 구현

실제 서비스를 구현하다보면 특정 기준으로 순위별 데이터를 보여줘야 하는 경우가 많다. 예를 들어, 인기 게시글이라던지, 인기 검색어라던지, 인기순으로 영화 데이터를 보여줘야 된다던지, 상품을 판매순으로 보여줘야 되는 것들이 전부 순위별 데이터를 보여줘야 하는 경우이다.
다양한 예시 중에서 ‘인기 검색어 기능’의 예를 가지고 RDB 기반으로 구현된 코드를 먼저 살펴보자. 그런 다음 Redis로 직접 구현하는 방식을 직접 경험해보자.
 
 

✅ 실습 진행하기

효율적인 실습을 위해 검색 API, 인기 검색어 Top 10 조회 API를 미리 만들어뒀다.
  1. 검색 API, 인기 검색어 조회 API 살펴보기
    1. SearchKeyword
      @Entity(name = "search_keywords") @Getter @NoArgsConstructor public class SearchKeyword { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true) private String keyword; private Long count; public SearchKeyword(String keyword) { this.keyword = keyword; this.count = 1L; } public void increaseCount() { this.count++; } }
       
      SearchController
      @RestController @RequiredArgsConstructor @RequestMapping("/search") public class SearchController { private final SearchService searchService; @GetMapping() public void search(@RequestParam String keyword) { searchService.search(keyword); } @GetMapping("/top10") public List<String> getTop10Keywords() { return searchService.getTop10Keywords(); } }
       
      StockService
      @Service @RequiredArgsConstructor public class SearchService { private final SearchRepository searchRepository; @Transactional public void search(String keyword) { SearchKeyword searchKeyword = searchRepository.findByKeyword(keyword) .orElse(new SearchKeyword(keyword)); searchKeyword.increaseCount(); searchRepository.save(searchKeyword); } @Transactional(readOnly = true) public List<String> getTop10Keywords() { return searchRepository.findTop10ByOrderByCountDesc().stream() .map(SearchKeyword::getKeyword) .toList(); } }
       
      StockRepository
      public interface SearchRepository extends JpaRepository<SearchKeyword, Long> { Optional<SearchKeyword> findByKeyword(String word); List<SearchKeyword> findTop10ByOrderByCountDesc(); }
       
  1. Spring Boot 실행시키기
    1. notion image
       
  1. 부하 테스트 스크립트 살펴보기
    1. scripts/script_3-1.js
      import http from 'k6/http'; import {check} from 'k6'; import {randomItem} from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; export const options = { // 가상 유저(VUs) 100명으로 설정 vus: 100, // 테스트를 10초 동안 진행 duration: '10s', }; // 검색어 리스트 정의 const keywords = [ 'spring', 'java', 'redis', 'mysql', 'jpa', 'k6', 'performance', 'test', 'load', 'stress', 'docker', 'kubernetes', 'aws', 'cloud', 'microservices', 'python', 'javascript', 'react', 'vue', 'angular' ]; export default function () { // 1. 검색 API 호출 // 랜덤한 키워드 선택 const keyword = randomItem(keywords); // 검색 요청 const searchRes = http.get(`http://localhost:8080/search?keyword=${keyword}`); check(searchRes, { 'search status is 200': (r) => r.status === 200, }); // 2. 인기 검색어 조회 API 호출 const top10Res = http.get('http://localhost:8080/search/top10'); check(top10Res, { 'top10 status is 200': (r) => r.status === 200, }); }
       
  1. 부하 테스트 진행하기
    1. $ k6 run scripts/script_3-1.js
      notion image
      검색 API와 인기 검색어 조회 API 둘 다 총 합해서 1346번 요청을 보냈음을 알 수 있고, 두 API 평균 TPS는 123인 것을 알 수 있다.
      RDB를 기반으로 인기 검색어 Top 10을 조회하려면 count(검색 횟수)를 기준으로 매번 정렬을 해서 조회해야 하기 때문에 성능 측면에서 비효율이 발생한다. 따라서 성능을 개선하기 위해 RDB 대신에 Redis를 활용해 구현해보자.
      참고) RDB의 인덱스를 활용해서 성능을 개선하는 것도 하나의 방법이지만, 여기서는 극도로 성능을 끌어올려야 한다는 가정하에 Redis를 도입하는 상황이라고 가정하자.
 
📎
이 글은 비전공자도 이해할 수 있는 Redis 중급/실전 강의의 수업 자료 중 일부입니다.