공공부하자개발 · 영어 학습 노트
자바
실무 확장Excel · 파일 업로드 · DB 연동0/22 완료
  • 01Excel(XLSX) 구조와 순수 JDK로 읽기/쓰기
  • 02Apache POI로 Excel 업로드/다운로드
  • 03파일 업로드/다운로드 서버 (HttpServer)
  • 04JDBC 기초와 트랜잭션 (H2)
  • 05MyBatis 어노테이션 매퍼로 쿼리 연동
  • 06MyBatis XML 매퍼 · Oracle 방언 · PageHelper · Spring Boot
  • 07REST API 서버와 JSON
  • 08Vue 3 SPA 와 Java 서버 연동
  • 09@Scheduled 운영
  • 10로깅 실무: 레벨·계층, MDC 추적, 예외·성능, 마스킹, 롤링, JSON 로그
  • 11외부 API 연동
  • 12테스트 실무
  • 13암호화·개인정보 보호
  • 14인코딩·한글 실무
  • 15@Transactional 심화
  • 16긴 작업 비동기 처리와 진행률
  • 17SFTP·FTP 파일 연계
  • 18로컬 캐시와 @Cacheable
  • 19메일·알림 발송
  • 20웹 보안 체크리스트
  • 21빌드 도구와 폐쇄망 의존성 반입
  • 22성능 측정: p50·p95·p99, 측정 계층, JFR, JMH 함정, 자체 부하 테스트, 병목 순위
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 실무 확장 › 18 / 22

로컬 캐시와 @Cacheable

TTL, 크기 제한, 스탬피드 방지, 무효화, Caffeine 설정
섹션 7진행 0 / 22
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

6. 연습 문제

문제 1. Cache 에 만료 시각을 조회하는 `expireAt(key)` 추가

Cache 에 특정 키의 만료 시각(long, epoch millis)을 반환하는 expireAt(K key) 를 추가하세요. 키가 없거나 이미 만료됐으면 -1 을 반환합니다. 힌트: store.get(key) 로 Entry 를 꺼내 expireAtMillis 를 확인합니다.

java
public long expireAt(K key) {
    Entry<V> e = store.get(key);
    if (e == null || e.expireAtMillis <= clock.millis()) {
        return -1;
    }
    return e.expireAtMillis;
}

Entry 의 필드가 private 이므로 Cache 클래스 내부에 메서드를 추가해야 접근할 수 있습니다.

문제 2. 음수 캐시용 `Optional` 래퍼 메서드 추가

Cache<K, V> 를 감싸서, 없는 값도 Optional.empty() 로 짧게 캐시하는 getOptional(key, loader) 를 추가하세요. 힌트: V 대신 Optional<V> 를 캐시 값 타입으로 쓰는 별도 Cache<K, Optional<V>> 인스턴스를 만듭니다.

java
Cache<String, Optional<String>> negativeCache =
    new Cache<>(Clock.systemUTC(), Duration.ofMinutes(1), 100);

Optional<String> result = negativeCache.get("missing-id",
    id -> Optional.ofNullable(lookupFromDb(id)));

일반 캐시와 음수 캐시는 TTL 이 다른 경우가 많아 인스턴스를 분리하는 편이 관리하기 쉽습니다.

문제 3. `demoStampede` 를 20개 스레드로 늘려도 loader 가 1번만 실행되는지 확인

Main.demoStampede 의 threadCount 를 20으로 바꾸고 다시 실행해, loader 실행 횟수가 여전히 1인지 확인하세요. 힌트: CountDownLatch start 로 모든 스레드를 동시에 출발시키는 구조는 그대로 두고 개수만 바꾸면 됩니다.

java
int threadCount = 20; // 10 -> 20 으로 변경

스레드 수를 늘려도 store.compute 가 키 단위로 원자적이므로 결과는 항상 1입니다.

문제 4. Spring `@Cacheable` 에 조건부 캐싱(`condition`) 적용

CodeService.findByGroup 에 groupCode 가 "TEMP" 로 시작하면 캐시하지 않도록 condition 속성을 추가하세요. 힌트: condition 은 메서드 실행 전에 평가되는 SpEL 식입니다.

java
@Cacheable(cacheNames = "codes", key = "#groupCode",
           condition = "!#groupCode.startsWith('TEMP')")
public List<CodeDto> findByGroup(String groupCode) {
    return codeMapper.selectByGroup(groupCode);
}

condition 은 실행 전 평가, unless 는 실행 후 결과를 보고 평가한다는 차이를 기억해 두면 두 속성을 헷갈리지 않습니다.

문제 5. 캐시 적중률이 낮을 때 원인 3가지 나열

운영 중인 캐시의 hit ratio 가 계속 낮게(10% 미만) 나온다면 의심할 원인 3가지를 이 레슨 내용에서 골라 적어 보세요.

  • self-invocation 으로 캐시 애너테이션 자체가 동작하지 않는 경우
  • 키 설계가 잘못돼 같은 조회인데 매번 다른 키로 저장되는 경우(예: 파라미터 순서·null 처리 차이)
  • TTL 이 너무 짧아서 값이 채워지자마자 만료되는 경우

세 가지 모두 코드를 고치지 않고 로그나 캐시 키 목록만 확인해도 원인을 좁힐 수 있습니다.

연습 문제
    이전 섹션5 자주 하는 실수 (Tip)6 / 7다음 섹션7 정리