실전 프로젝트 5주차. 오늘은 성능개선을 위해 글로벌 캐시와 별도로 로컬 캐시를 적용했다.
Spring Security와 JWT 토큰을 프로젝트에서 사용중이기 때문에 토큰을 서버가 받을 때 토큰에 있는 email로 유저 정보를 DB에 찾는 로직이 매 Request마다 생겼다. 그래서 이 부분에 성능 상 개선을 위해 캐시를 적용하려 했으나 이미 적용된 Redis를 쓰기에는 부담이 갈 것 같아 로컬 캐시로 적용해보려 했다.
우선 Caffeine 라이브러리를 추가한다.
build.gradle
// Caffeine
implementation 'com.github.ben-manes.caffeine:caffeine:3.0.4'
@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
@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());
}
}