현재 코드
public StoreInfoResponse getStoreInfoWithoutCache(Long storeId, Long userId) {
Store store = getStoreOrThrow(storeId); // DB 직접 조회
store.increaseViewCount();
String representativeTag =
storeTagCountRepository
.findRepresentativeTagByStoreId(storeId)
.map(Tag::getLabel)
.orElse(null);
if (userId != null) {
User user = getUserOrThrow(userId);
store.setIsScrapped(storeScrapRepository.existsByStoreIdAndUserId(storeId, user.getId()));
} else {
store.setIsScrapped(null);
}
return StoreInfoResponse.from(
store, representativeTag, storeGoogleApiClient.getStoreOpeningHours(store));
}
Redis를 활용해서 문제를 해결해보기로 !!
Redis 설치 (Mac) → 로컬을 위해
brew install redis
brew services start redis
또는 도커 내부에 설치
프로젝트 내 설정
// Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// Spring Boot Cache
implementation 'org.springframework.boot:spring-boot-starter-cache' data:
redis:
host: localhost
port: 6379@EnableCaching 추가@Configuration
@EnableRedisRepositories
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.password:}")
private String redisPassword;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration =
new RedisStandaloneConfiguration(redisHost, redisPort);
redisStandaloneConfiguration.setPassword(redisPassword);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
StringRedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
@EnableCaching
@Configuration
public class RedisCacheConfig {
@Bean
public CacheManager contentCacheManager(RedisConnectionFactory cf) {
RedisCacheConfiguration redisCacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer())) // Value Serializer 변경
.entryTtl(Duration.ofMinutes(3L)); // 캐시 수명 30분
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}
서버에서 레디스 설치, 반영
# 설치
sudo apt update
sudo apt install redis-server
# 서비스 재시작
sudo systemctl restart redis
sudo systemctl enable redis-server
# 접속 확인
redis-cli ping # → PONG
ec2에 레디스 설치 > 도커에 같이 띄우기
version: '3.8'
services:
redis:
image: redis:7
container_name: redis
restart: always
ports:
- "***"
command: ["redis-server", "--requirepass", "****"]
volumes:
- redis-data:/data
volumes:
redis-data:
Redis 보안 설정은 환경 변수 기반으로 관리하며, 상세 구성은 생략합니다.
서버 환경에서는 Redis를 컨테이너 기반으로 분리 배포하여 운영했습니다.
사용하기
서비스로직에다가 애노테이션을 붙여주면 되고 이 때 서비스내에서 호출하는 경우에는 사용해도 먹히지 않습니다
@Cacheable(value = "view-id", key = "#viewId") (예시)RedisTemplate으로 직접 구현
@CacheEvict(value = "store-id", allEntries = true)@CachePut(value = "store", key = "#store.id")import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 60, // 60명의 유저가 동시에 요청
duration: '1s', // 1초간 유지 → 약 60TPS
};
export default function () {
const BASE_URL = '***;
const LOCAL_BASE_URL = '***';
const res = http.get(`***`);
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
}
K6을 활용해서 60TPS의 환경에서 테스트를 진행해보았다.

| avg (평균 응답 시간) | 272.91ms |
|---|---|
| min (최소값) | 18.33ms |
| med (중앙값) | 218.63ms |
| max (최대값) | 627.13ms |

| avg (평균 응답 시간) | 147.53ms |
|---|---|
| min (최소값) | 18.09ms |
| med (중앙값) | 146.54ms |
| max (최대값) | 381.83ms |

| avg (평균 응답 시간) | 181.68ms |
|---|---|
| min (최소값) | 38.58ms |
| med (중앙값) | 186.98ms |
| max (최대값) | 494.89ms |
캐싱 도입 전에는 평균 응답 시간이 273ms에 달했고, 최대 627ms까지 지연되는 경우도 발생했습니다. 하지만 Redis 기반 조회수·외부 API 결과 캐싱 구조를 설계한 이후, 평균 응답 시간은 147ms로 46% 단축, 응답 실패율은 0%로 개선되었습니다.
또한 외부 API 호출 캐싱을 통해 비용과 한도 문제를 해결할 수 있었습니다.