Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20241201 Redis 적용을 위한 공부... 본문

개발Article

[TIL]20241201 Redis 적용을 위한 공부...

최밤빵 2024. 12. 1. 09:32

프로젝트에 Redis를 도입한다고 한다. 레디스는 데이터의 빠른 액세스를 가능하게 하는 인메모리 데이터 저장소로 알려져 있고, 데이터 캐싱, 세션 관리, 간단한 키-값 저장소로 많이 사용된다. 지금까지 레디스를 적용해본적이 없고, 본격적인 도입에 앞서 학습이 필요했기때문에, 기능구현을 빨리 끝마치고 레디스 적용을 위한 학습과 코드를 작성해본 걸 기록하게 됐다...!! 

 

1. 레디스 도입 목적과 학습 시작

레디스를 도입하려는 이유는 캐싱을 통한 응답 속도 개선자주 사용되는 데이터의 빠른 접근을 목적으로 한다. 특히, PUBG와 관련된 데이터들이 빈번히 호출되기 때문에, 일부 데이터를 캐싱하여 성능을 최적화 할 수 있다.

레디스는 키-값 구조를 가지고, 데이터는 메모리에 저장되기 때문에 데이터 액세스 속도가 매우 빠르다. 기본적으로 캐시(Cache)로 많이 사용되지만, 다양한 데이터 구조(List, Set, Hash 등)를 지원하여 여러 용도로 활용될 수 있다.

2. 프로젝트에 레디스 적용하기 위한 환경 설정

프로젝트에 레디스를 도입하기 위해서는 우선 스프링 부트 프로젝트에 관련 설정을 추가해야 한다. 레디스를 적용하기 위한 초기 설정은 크게 두 가지로 나뉜다.

  1. 의존성 추가: spring-boot-starter-data-redis 의존성을 추가한다.
  2. 레디스 설정: 레디스 연결을 위한 설정 파일을 구성한다.

의존성 추가build.gradle 파일에 다음과 같은 내용을 추가함으로써 이루어진다.

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

레디스를 연결하기 위해 설정 클래스를 작성한다. (연습코드) 

package com.sample.pugbrecords.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

@Configuration
@EnableRedisRepositories
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory("localhost", 6379);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory());
        return template;
    }
}

LettuceConnectionFactory를 사용하여 레디스와의 연결을 설정하고, RedisTemplate을 통해 레디스에 데이터를 저장하고 조회할 수 있다. 여기서는 기본적으로 로컬 레디스 서버(localhost:6379)에 연결하도록 설정했다. 실제 배포 환경에서는 해당 설정을 환경 변수로 관리하거나 클라우드 환경의 레디스 서버로 변경해야 한다.


2-1 Lettuce와 Jedis 비교

Lettuce는 비동기, 이벤트 기반의 네티(Netty) 라이브러리를 사용하는 클라이언트이다. 높은 성능을 제공하고 스레드 세이프하여 여러 스레드에서 공유할 수 있다는 점이 특징이다. 비동기 특성 덕분에 대규모 트래픽을 효율적으로 처리하는 데 적합하다.

 

Jedis는 동기 방식으로 작동하고, 연결 풀을 사용하여 다중 연결을 관리한다. 하지만 스레드 세이프하지 않기 때문에 여러 스레드에서 사용하려면 주의가 필요하다. 상대적으로 단순하고 직관적인 사용법을 제공하고, 소규모 프로젝트나 복잡한 트래픽 관리가 필요 없는 경우에 적합할 수 있다.

 

JedisConnectionFactory를 사용한 Redis 설정 예시

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // JedisConnectionFactory를 사용하여 Redis 연결을 설정한다.
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName("localhost");
        jedisConnectionFactory.setPort(6379);
        return jedisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory());
        return template;
    }
}

→ 설정의 대부분은 LettuceConnectionFactory와 비슷하지만, 연결 방식에서 차이가 있다.


2-2 프로젝트에 Lettuce와 Jedis 중 어떤 걸 사용할까?

내 프로젝트는 PUBG와 같은 대규모 게임 데이터를 처리하는 서비스로서, 높은 동시성 처리와 성능이 요구된다. 비동기적으로 작동하기때문에 스레드 세이프한 특성을 가진 Lettuce가 더 적합하다고 판단했다. 특히, 여러 API 요청을 동시에 처리해야 하는 상황에서 Lettuce의 비동기 특성은 전체적인 성능 향상에 큰 도움이 될 수 있을 것 같다. 

Spring Boot에서는 기본적으로 Lettuce를 지원하기에, 이를 활용하면 추가적인 설정 없이도 편리하게 사용할 수 있다는 점도 중요한 고려 요소였다. 이러한 이유들로 인해 현재 프로젝트에서는 LettuceConnectionFactory를 사용하여 Redis와 연결할까 한다. 반면, 만약 프로젝트가 상대적으로 적은 수의 사용자와 단순한 데이터 저장소 역할만을 수행한다면, Jedis도 충분히 유효한 선택이 될 수 있다. 하지만 이번 프로젝트의 경우 Lettuce의 비동기 성능이 큰 이점이 되지않을까 싶다. 


3. 간단한 캐시 적용 예시만들어 보기

레디스를 학습한 후 적용하기 위해 간단한 캐시 기능을 구현해봤다. 우선, 클랜 플레이어의 랭크 스탯을 조회할 때, 해당 데이터를 캐시하여 다음 조회 시 빠르게 응답할 수 있도록 했다. (연습코드)

서비스 클래스에서의 레디스 적용 예시

package com.sample.pugbrecords.clanPlayers.service;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class ClanPlayersService {

    private final RedisTemplate<String, Object> redisTemplate;

    public ClanPlayersService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public RankedStatsResponseDto getPlayerRankedStats(String platform, String playersId, String seasonsId) {
        String cacheKey = "rankedStats::" + playersId + "::" + seasonsId;
        // 캐시에서 데이터 조회
        RankedStatsResponseDto cachedStats = (RankedStatsResponseDto) redisTemplate.opsForValue().get(cacheKey);
        if (cachedStats != null) {
            return cachedStats;
        }
        // 레디스에 캐시된 데이터가 없으면 외부 API 호출 및 캐시 저장
        RankedStatsResponseDto stats = callExternalApi(platform, playersId, seasonsId);
        redisTemplate.opsForValue().set(cacheKey, stats, 10, TimeUnit.MINUTES);
        return stats;
    }

    private RankedStatsResponseDto callExternalApi(String platform, String playersId, String seasonsId) {
        // 실제 외부 API 호출 로직 (간소화된 형태)
        return new RankedStatsResponseDto();
    }
}

RedisTemplate을 사용하여 캐싱된 데이터를 우선적으로 조회하고, 캐시가 존재하지 않을 경우에만 외부 API 호출을 수행한 후, 해당 데이터를 캐시에 저장하도록 한다.반복적인 외부 API 호출을 줄이고, 응답 시간을 줄일 수 있다.

캐시 키는 플레이어 ID와 시즌 ID로 구성하여, 각 플레이어의 랭크 스탯을 고유하게 식별할 수 있도록 하고, 데이터의 유효기간을 10분으로 설정하여, 일정 시간이 지난 후 갱신될 수 있도록 했다. 


4. 레디스 적용학습을 하면서 배운 점

레디스를 처음 적용해보면서 캐싱 전략의 필요성과 TTL 설정의 중요성을 배웠다. 캐시는 시스템의 성능을 향상시키지만, 모든 데이터를 캐시하는 것은 메모리 낭비를 초래할 수 있기 때문에, 어떤 데이터를 캐싱할지 명확하게 정의하고, 적절한 유효기간(TTL, Time To Live)을 설정하는 것이 중요하다. 레디스를 사용하는 가장 큰 장점 중 하나는 데이터의 빠른 접근성이지만, 데이터가 일관성 있게 관리되지 않으면 잘못된 데이터가 클라이언트에 전달될 수 있다.