[stock101] Redis 캐싱 적용하기 - 11일차

2026. 2. 3. 23:12·Archive(완료된 내용)/포트폴리오 강화

캐싱 적용해도 된다고 생각하는 이유 어차피 변경은 상폐아니면 비상폐만 다루면된다.

 

1. Redis 설정 (Config)

RedisTemplate<String, String>(JWT용)은 유지하면서, @Cacheable이 JSON 형식으로 데이터를 저장할 수 있도록 CacheManager를 등록합니다.

Java
 
@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(24))
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

2. 데이터 전송 객체 (DTO)

엔티티 전체 대신 필요한 필드만 담아 메모리를 아낍니다. (Getter와 기본 생성자 필수)

Java
 
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class StocksCacheDto {
    private Long id;
    private String stockCode;
    private String name;

    public static StocksCacheDto from(Stock stock) {
        return new StocksCacheDto(stock.getStockId(), stock.getStockCode(), stock.getName());
    }
}

3. 서비스 레이어 (Service)

@Cacheable로 자동 조회/저장을 구현하고, @CacheEvict로 캐시 삭제 기능을 만듭니다.

Java
 
@Service
@RequiredArgsConstructor
public class StockServiceImpl implements StockService {
    private final StockMapper stockMapper;

    @Override
    @Cacheable(value = "stocks", key = "'all'")
    public List<StocksCacheDto> getStockList() {
        List<Stock> stockList = stockMapper.selectStockList();

        if(stockList == null || stockList.isEmpty()) {
            throw new GlobalException(GlobalExceptionMessage.STOCK_NOT_FOUND);
        }

        return stockList.stream()
                .map(StocksCacheDto::from)
                .collect(Collectors.toList());
    }

    @Override
    @CacheEvict(value = "stocks", allEntries = true)
    public void clearStockCache() {
        // 캐시 삭제용 (본문 비어있음)
    }
}

4. 스케줄러 (Scheduler)

오전 9시와 오후 5시에 맞춰 캐시를 갱신(삭제 후 재조회)합니다.

Java
 
@Component
@RequiredArgsConstructor
public class StockScheduler {
    private final StockService stockService;

    // 초 분 시 일 월 요일 (09:00:00, 17:00:00)
    @Scheduled(cron = "0 0 9,17 * * *")
    public void refreshStockCache() {
        // 1. 기존 캐시 삭제
        stockService.clearStockCache();
        
        // 2. 캐시 미리 채우기 (Warm-up)
        stockService.getStockList();
        
        System.out.println("주식 캐시 업데이트 완료");
    }
}

5. 데이터 흐름 요약

  1. API 호출: 사용자가 /stocks 요청.
  2. Redis 확인: stocks::all 키가 있는지 확인.
  3. 결과 반환: * 있으면(Hit): DB 안 거치고 레디스에서 바로 반환 (속도 향상).
    • 없으면(Miss): DB 조회 → DTO 변환 → 레디스 저장 → 반환.
  4. 자동 업데이트: 스케줄러가 지정된 시간에 레디스 데이터를 삭제하여 최신화를 유지.

 

 

  • 호출 시도: 컨트롤러에서 stockService.getStockList()를 호출합니다.
  • 캐시 확인: 스프링 프록시가 레디스에 가서 **stocks::all**이라는 키가 있는지 확인합니다.
  • 결과 처리:
    • Cache Hit (데이터 있음): 레디스에서 JSON 문자열을 가져와 List<StocksCacheDto>로 역직렬화한 뒤 메서드 본문을 실행하지 않고 즉시 반환합니다.
    • Cache Miss (데이터 없음): 실제 서비스 메서드 안의 DB 조회 로직을 실행합니다.
  • 자동 저장: 메서드가 반환한 List를 레디스에 stocks::all 키값으로 저장합니다.

 

Redis캐싱이 효과가 좋은 경우

 

① DB 커넥션 풀(Connection Pool) 고갈 상황

스프링 부트의 기본 HikariCP 커넥션 풀은 보통 10개입니다.

  • 캐싱 없을 때: 500명이 동시에 오면, 10명이 DB를 쓰는 동안 나머지 490명은 줄 서서 대기해야 합니다. 이때 대기 시간이 2~3초를 훌쩍 넘기게 됩니다.
  • 캐싱 있을 때: Redis는 커넥션 하나로도 초당 수만 건을 처리할 수 있고, DB 커넥션을 점유하지 않습니다. 대기 시간 없이 500명 모두 즉시 응답을 받습니다.

② 데이터 가공 로직 추가

단순 조회가 아니라, 자바 코드 내에서 2,600건의 데이터를 정렬하고, 그룹화하고, 특정 조건으로 필터링하는 로직이 있다고 가정해 보세요.

  • 캐싱 없을 때: 500번의 요청마다 매번 CPU가 2,600번 루프를 돌며 계산해야 합니다.
  • 캐싱 있을 때: 이미 계산이 완료된 결과값(JSON 등)을 그대로 가져오기만 하면 됩니다.

③ 외부 API 호출이 섞여 있을 때

만약 주식 목록을 DB가 아니라 외부 증권사 API에서 가져온다면?

  • 500명이 동시에 외부 API를 때리면 호출 제한(Rate Limit)에 걸리거나 응답이 매우 느려집니다. 캐시를 쓰면 최초 1명만 API를 호출하고 나머지는 0.01초 만에 응답을 받습니다.
  •  
저작자표시 비영리 변경금지 (새창열림)

'Archive(완료된 내용) > 포트폴리오 강화' 카테고리의 다른 글

[order101] 최적화 및 시나리오 - 12일차  (0) 2026.02.05
[stock101] 성능 개선 해보기 - 11일차  (0) 2026.02.03
[Stock101] gemini api를 쓰자. - 10일차  (0) 2026.02.03
[Stock101] 프로젝트 기획 마무리 정리 - 10일차  (0) 2026.02.03
[Stock101] 프로젝트를 정리하자.- 9일차  (0) 2026.02.01
'Archive(완료된 내용)/포트폴리오 강화' 카테고리의 다른 글
  • [order101] 최적화 및 시나리오 - 12일차
  • [stock101] 성능 개선 해보기 - 11일차
  • [Stock101] gemini api를 쓰자. - 10일차
  • [Stock101] 프로젝트 기획 마무리 정리 - 10일차
오늘은치킨이닭
오늘은치킨이닭
개발로 세상을 밝히자.(억지 맞음)
  • 오늘은치킨이닭
    개발세밝
    오늘은치킨이닭
  • 전체
    오늘
    어제
    • 분류 전체보기 (80)
      • Project(마감 기한이 정해진 목표) (2)
        • Docker(도커) (1)
        • Django(장고) (0)
        • 부트캠프 (1)
      • Archive(완료된 내용) (59)
        • 재취업준비 (8)
        • 포트폴리오 강화 (24)
        • 부트캠프 (3)
        • 팁 (2)
        • 데이터베이스 (2)
        • SQL (12)
        • 백엔드 (5)
        • 프론트엔드 (1)
        • 유니티(Unity) (2)
      • Area(일생동안 지속 유지하는 활동,마감X) (16)
        • 게임 (2)
        • 코딩테스트 (12)
        • 운영체제 (0)
        • DB (2)
      • Resource(지속적 관심을 갖는 주제분야) (1)
        • 애니메이션 (0)
        • 내가 선정한 맛집 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    고클린 #cpu온도보는법 #cpu온도
    롤 #룬 #자동적용 #블리츠 #다운로드 #도움 #TIP #브론즈 #아이언 #브실골 #아브실
    명동
    맛집
    인포그래픽 #자기소개서 #자기소개 #명함삭제
    칼국수맛집
    명동맛집
    유니티 #설치 #방법 #다운
    DB #데이터베이스
    명동교자
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
오늘은치킨이닭
[stock101] Redis 캐싱 적용하기 - 11일차
상단으로

티스토리툴바