본문 바로가기

Diary/TIL

2024-04-17) Redis 캐시 적용

 

Redis를 사용하여 Java Spring 애플리케이션에서 로그인 인증이 진행되는 UserDetails와 검색 결과인 Page를 캐싱.

 

Redis를 캐시로 채택한 이유:

  • 복잡한 자료형 직렬화 지원: Redis는 리스트, 해시, 집합과 같은 복잡한 자료형을 직렬화하는 기능을 제공
  • 고급 기능 제공: 대기열, 분산 락과 같은 고급 기능을 제공하여 Memcached보다 유연하고 강력한 기능을 활용가능
  • 성능: 낮은 지연 시간과 높은 처리량을 제공하여 실시간 애플리케이션에 적합

 

의존성 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

redis 캐시 설정: 각 캐시별로 다른 저장 시간을 추가

@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(10)) // 기본 캐시 수명 10분

    RedisCacheConfiguration userDetailsCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1)); // userDetails 캐시 수명 1시간

    RedisCacheConfiguration flightSearchCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(5)); // flightSearch 캐시 수명 5분

    Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
    cacheConfigurations.put("userDetailsCache", userDetailsCacheConfig);
    cacheConfigurations.put("flightSearchCache", flightSearchCacheConfig);

    return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(defaultCacheConfig)
            .withInitialCacheConfigurations(cacheConfigurations)
            .build();
}

캐시 적용(유저 인증정보)

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    @Cacheable(value = "userDetailsCache", key = "#username")
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // UserDetails를 DB에서 로드하는 로직
        return findUserByUsername(username);
    }

 

page<Flight> 검색결과 캐싱을 위한 자료형

public class FlightPageWrapper implements Serializable {
    private List<Flight> content;
    private int pageNumber;
    private int pageSize;
    private long totalElements;
}
@Cacheable(value = "flightSearchCache", key = "#pageable.pageNumber + '-' + #pageable.pageSize")
    public FlightPageWrapper searchFlights(Pageable pageable) {
        Page<Flight> flightPage = flightRepository.findAll(pageable);
        return new FlightPageWrapper(flightPage.getContent(), pageable.getPageNumber(), pageable.getPageSize(), flightPage.getTotalElements());
    }

 

프로젝트가 완료된 후, Page<Flight>List<Flight>로 변환하여 저장하는 방법도 가능했을 것이라고 생각할 수 있지만, 포장된 형태가 캐싱과 역직렬화에 더 적합하다고 판단하였다.