230501 TIL #73 Local Cache

김춘복·2023년 5월 1일
0

TIL : Today I Learned

목록 보기
73/571

230501 Today I Learned

실전 프로젝트 5주차. 오늘은 성능개선을 위해 글로벌 캐시와 별도로 로컬 캐시를 적용했다.


Local Cache

  • Spring Security와 JWT 토큰을 프로젝트에서 사용중이기 때문에 토큰을 서버가 받을 때 토큰에 있는 email로 유저 정보를 DB에 찾는 로직이 매 Request마다 생겼다. 그래서 이 부분에 성능 상 개선을 위해 캐시를 적용하려 했으나 이미 적용된 Redis를 쓰기에는 부담이 갈 것 같아 로컬 캐시로 적용해보려 했다.

  • 우선 Caffeine 라이브러리를 추가한다.

  • build.gradle

//  Caffeine
    implementation 'com.github.ben-manes.caffeine:caffeine:3.0.4'
  • Config 를 작성한다.
    이미 Redis 글로벌 캐시가 적용되어있기때문에 해당 Config 파일에 캐시 매니저를 새로 만들었다.
    공통 캐시매니저를 만들어놓고 Redis 캐시매니저와 local 캐시매니저를 따로 만들었다.
    라이브러리를 추가하고 캐시매니저를 만들어 둔 이유는 TTL을 설정하기 위함이다.
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

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

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

  @Bean
  public CacheManager localCacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager("localCache");
    cacheManager.setCaffeine(Caffeine.newBuilder()
        .expireAfterWrite(1, TimeUnit.HOURS) // TTL 설정
        .maximumSize(200)); // 캐시 최대 갯수 설정
    return cacheManager;
  }

  @Bean
  public CacheManager redisCacheManager() {
    RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
        .fromConnectionFactory(redisConnectionFactory());
    return builder.build();
  }

  @Primary
  @Bean
  @Override
  public CacheManager cacheManager() {
    return new CompositeCacheManager(localCacheManager(), redisCacheManager());
  }

  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
        .commandTimeout(Duration.ofMillis(500)) // 타임아웃 설정
        .build();

    RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(host, port);

    return new LettuceConnectionFactory(serverConfig, clientConfig);
  }
  
  ...
  // 아래에 RedisTemplate
  • @Cacheable로 로컬 캐시를 적용한다.
    토큰에서 유저 정보를 불러올 때 DB를 조회하는 로직에 로컬캐시를 적용했다.
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

  private final UserRepository userRepository;

  @Override
  @Cacheable(cacheNames = "localCache", key = "#email")
  public UserDetails loadUserByUsername(String email) {
    User user = userRepository.findByEmail(email).orElseThrow(
        () -> new CustomException(ExceptionType.NOT_FOUND_USER_EXCEPTION)
    );
    return new UserDetailsImpl(user, user.getEmail());
  }
}
  • 사실 그렇게 부하가 가거나 ResponseTime이 큰 로직이 아닌 간단한 쿼리기 때문에 성능상 크게 이점은 없었다. 하지만 유저 한명이 여러 건을 조회할 경우 쿼리가 한번씩 나가는 것을 막을 수 있어 부하를 줄일 수 있을 것이다.
profile
Backend Dev / Data Engineer

0개의 댓글