개발 낙서장

[Spring] Redis로 인기 검색어 구현 본문

Java

[Spring] Redis로 인기 검색어 구현

권승준 2024. 12. 18. 16:42

Redis를 활용해 사이트에서 검색된 검색어 중 가장 많이 검색된 검색어 10개를 출력하는 기능을 구현하고자 한다.

왜 Redis?

사실 그냥 DB에 검색어 Count로 저장해도 되겠지만 굳이 Redis를 사용하는 이유가 뭘까

  1. 속도가 빠름
    Redis는 In-Memory 기반 DB이기에 다른 별도 저장소(디스크)에 저장되는 DB에 비해 읽기/쓰기 속도가 매우 빠르다.
  2. 유연한 자료구조
    기본적으로 Key-Value 형태로 저장되지만 String, List, Set, Hash, Sorted Set, JSON 등 다양한 자료구조를 지원한다.
    특히 이 중에서 Sorted Set을 지원하기에 인기 검색어 같은 랭킹 시스템 구현이 용이하다.
  3. 쉽고 간편한 구현
    Redis 관련 라이브러리도 즐비하고 커뮤니티나 레퍼런스 등 참고할만한 자료가 방대하다. 또한, 별도의 쿼리 작성 없이 단순한 코드 작성만으로 구현이 가능하다.

실제로 구현을 해보니 기존 코드에서 몇 줄만 추가하는 것으로 인기 검색어 기능이 바로 구현됐다.

Redis 설치

먼저 도커에 Redis를 설치해줘야 한다.
Redis 또한 DB 중 하나이기 때문에 DB를 설치하고 서버를 띄워야 사용이 가능하다.

# Redis 이미지 다운
docker pull redis

# Redis 이미지 확인
docker images

# Redis 컨테이너 생성 및 실행(name : 이름, p : 포트 번호, d : 백그라운드 실행)
docker run --name name-redis -p 6379:6379 -d redis

# Redis 컨테이너 실행 확인
docker ps

위 사진처럼 도커에 Redis 컨테이너가 띄워졌으면 성공이다.

Redis 설정

build.gradle

    // Redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

application.properties

# Redis(Default port = 6379)
spring.data.redis.host=localhost
spring.data.redis.port=6379

RedisConfig

@Configuration
@EnableRedisRepositories
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String redisHost;

    @Value("${spring.data.redis.port}")
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 키 직렬화
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 값 직렬화
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        return redisTemplate;
    }
}
  1. EnableRedisRepositories
    RedisRepository를 사용할 수 있게 해주는 어노테이션이다. Spring JPA 처럼 자바 객체에 Redis를 매핑할 수 있게 해준다.
    나는 현재 사용하지 않았지만 추후 프로젝트를 확장할 때 필요할 수 있을 것 같아서 추가했다.
  2. Lettuce
    Lettuce는 Redis 클라이언트 중 하나라고 한다. Jedis라는 클라이언트도 있다고 한다.
    특징으로는 단일 스레드에서 동작하며 논블러킹 비동기 프로그래밍, 스레드 안전하다고 한다.
    아직 체감은 되지 않으나 Redis는 개발에서 필수적으로 사용되는 DB이므로 좀 더 깊게 알아볼 필요가 있는 것 같다.
  3. RedisTemplate
    별도 쿼리 없이 Redis에 접근할 수 있게 해주는 RedisTemplate이다. Key-Value 형식으로 저장되고 접근할 수 있으며 다양한 메소드를 지원한다.
    직렬화 방식과 연결 등 사전 설정을 한 RedisTemplate를 Bean으로 등록한다.

구현

@Service
@RequiredArgsConstructor
public class BookService {

    private final RestTemplate restTemplate;
    
    // RedisTemplate 주입
    private final RedisTemplate<String, String> redisTemplate;

    // . . .

    public String searchBook(SearchBookRequestDto requestDto) {
    
        // . . .

        ResponseEntity<SearchBookResponseDto> exchange = restTemplate.exchange(req, SearchBookResponseDto.class);

        // JSON으로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonResponse = "";
        try {
            jsonResponse = objectMapper.writeValueAsString(exchange.getBody());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

	// Sorted Set(opsForZSet) 자료구조 사용
        // 검색 시 해당 검색어의 점수를 올려 후에 점수로 인기 검색어 판별
        redisTemplate.opsForZSet().incrementScore("query", requestDto.getQuery(), 1);

        return jsonResponse;
    }

    public List<String> popularityQuery() {
    	// Sorted Set을 통해 내림차순으로 10개(0 ~ 9) 출력
        Set<String> zset = redisTemplate.opsForZSet().reverseRange("query", 0, 9);

        if(zset != null && !zset.isEmpty()) {
            return zset.stream().toList();
        }

        return new ArrayList<>();
    }
}

검색어를 통해 검색에 성공하면 Redis에 해당 검색어의 점수를 올린다.
이후 Redis에서 상위 10개의 데이터를 꺼내 출력해주면 인기 검색어 구현 끝

Postman으로 테스트하면 이런식으로 상위 10개 검색어가 List로 출력이 잘 된다.

허접한 화면이지만 어쨌든 구현에는 성공했다...

'Java' 카테고리의 다른 글

[JSP] API 연동 및 데이터 출력  (1) 2024.12.13
[JSP] SpringBoot + JSP 설정  (0) 2024.12.12
[JAVA] compareTo  (0) 2024.11.26
[Spring] @RequestPart 테스트 HttpMediaTypeNotSupportedException  (0) 2024.05.17
Spring + Redis Cache  (0) 2024.05.13
Comments