13.1 Redis 기반 글로벌 캐싱 및 분산 세션 공유
Redis는 단순히 빠른 인메모리 데이터베이스를 넘어, 여러 서버 인스턴스 간의 **공유 상태(캐시, 세션, 락)**를 동기화하는 분산 시스템의 핵심 인프라입니다.
1. 스프링 캐시 추상화(@Cacheable)와 Redis 연동
Spring의 캐시 추상화는 @Cacheable, @CacheEvict, @CachePut 애너테이션을 이용해 메서드 결과를 Redis에 자동으로 저장하고 꺼내오는 로직을 비즈니스 코드에서 완전히 분리시킵니다.
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
spring:
data:
redis:
host: localhost
port: 6379
# password: 운영 환경에서는 반드시 비밀번호 설정
@Configuration
@EnableCaching // 이 애너테이션 하나로 캐시 어노테이션 기능 전체 활성화
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 캐시 TTL 30분 설정
.disableCachingNullValues() // null 값은 캐시에 저장하지 않음
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())); // JSON 직렬화
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
// key에 해당하는 캐시 결과가 Redis에 있으면 쿼리 안 날리고 바로 리턴
@Cacheable(value = "products", key = "#productId")
public ProductDto getProduct(Long productId) {
return productRepository.findById(productId)
.map(ProductDto::from)
.orElseThrow(() -> new NotFoundException("상품을 찾을 수 없습니다."));
}
// 상품 수정 시 기존 캐시를 삭제 (다음 조회 시 DB에서 새로 읽어옴)
@CacheEvict(value = "products", key = "#productId")
public void updateProduct(Long productId, ProductUpdateDto dto) {
// ...업데이트 로직
}
}
2. HttpSession을 Redis로 분산 (Spring Session)
스케일 아웃(Scale-Out) 환경에서는 서버 인스턴스 A가 발급한 세션을 서버 인스턴스 B가 읽을 수 없습니다. Spring Session을 이용하면, 기존 HttpSession 코드를 단 한 줄도 바꾸지 않고 자동으로 세션 저장소를 Redis로 전환합니다.
implementation 'org.springframework.session:spring-session-data-redis'
spring:
session:
store-type: redis
timeout: 30m
이 설정 하나로 request.getSession()를 통한 모든 세션 R/W가 WAS 로컬 메모리 대신 Redis를 향하게 됩니다. 로드밸런서 뒤에 서버가 몇 대이든 동일한 세션을 공유하게 됩니다.