build.gradle
의존성 추가implementation 'org.springframework.boot:spring-boot-starter-data-redis'
application.yml
작성spring:
profiles:
default: local
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: 사용자 이름
password: 패스워드
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
data:
redis:
port: 6379
host: localhost
logging:
level:
org.springframework.cache: trace
RedisConfig
레디스 Configuration 코드 작성import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
// ⭐Lettuce 라이브러리를 활용해 Redis 연결을 관리하는 객체를 생성하고
// ⭐Redis 서버에 대한 정보(host, port)를 설정한다.
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
}
}
RedisCacheConfig
스프링 부트 캐시 Configuration 코드 작성import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public CacheManager boardCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
.defaultCacheConfig()
// ⭐Redis에 Key를 저장할 때 String으로 직렬화
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()
)
)
// ⭐Redis에 Value를 저장할 때 Json으로 직렬화
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()
)
)
// ⭐인메모리 데이터베이스는 저장량이 한정되어있으므로 TTL을 설정
.entryTtl(Duration.ofMinutes(1L));
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}
Service
측에서 레디스 캐싱을 직접적으로 활용할 수 있게끔 추가import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import spring.spirngbootredis.repository.BoardRepository;
import spring.spirngbootredis.domain.Board;
import java.util.List;
@Service
public class BoardService {
private final BoardRepository boardRepository;
@Autowired
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
// ⭐@Cacheable : 메서드 결과를 캐싱하고 동일한 메서드가 동일한 인자로 호출될 경우 데이터베이스로부터 조회하는 것이 아니라 캐시된 결과를 반환한다.
// ⭐@Cacheable 어노테이션은 Cache Aside 전략을 기반으로 작동한다.
// ⭐cacheNames : 캐시 이름, key : Redis에 저장할 키, cacheManager : 사용할 cacheManager의 Bean이름 지정
@Cacheable(cacheNames = "getBoards", key= "'board:page:' + #page + ':size' + #size", cacheManager = "boardCacheManager")
public List<Board> getBoards(int page, int size) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<Board> pageOfBoards = boardRepository.findAllByOrderByCreatedAtDesc(pageable);
return pageOfBoards.getContent();
}
}
개인 프로젝트에 Redis를 도입하려던 차에 Redis에 대한 이해가 부족하여, 성능 개선에 대한 궁금증을 해결하기 위해 공부를 시작했다. 새로운 기술을 도입하는 것은 문제를 해결하기 위한 첫걸음에 불과하다고 생각한다. 기술 자체의 강력한 기능이나 장점도 중요하지만, 그 기술이 실제로 무엇을 해결하려는 것인지, 어떤 상황에 적합한지를 고려하는 것이 더 중요하다. 단순히 "안 써본 기술이니 한 번 도입해보자"는 접근보다는, 기술의 적합성과 문제 해결 목표에 맞게 신중하게 활용할 수 있도록 많은 공부를 해야 한다는 것을 깨달았다.