μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬ 기반의 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ μΊμ‹œ 좔상화λ₯Ό μ œκ³΅ν•˜μ—¬ μΊμ‹œ κ΄€λ ¨ λͺ¨λ“ˆμ— λŒ€ν•œ μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•˜κ³  @EnableCachingκ³Ό 같은 μ–΄λ…Έν…Œμ΄μ…˜μœΌλ‘œ 선언적 μΊμ‹œλ₯Ό κ°„λ‹¨ν•˜κ²Œ μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μŠ€ν”„λ§μ„ λ‹€λ£¨λŠ” λ§Žμ€ κ°œλ°œμžλ“€μ΄ 일뢀 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직의 μ„±λŠ₯ ν–₯상을 μœ„ν•΄μ„œ 자주 μ‚¬μš©λ˜κ² μ§€λ§Œ 생각보닀 λ„μž…μ΄ κ°„λ‹¨ν•œ 점으둜 인해 μ—¬λŸ¬κ°€μ§€ 뢀뢄듀을 신경쓰지 μ•Šκ³  μ‚¬μš©ν•˜κ³  μžˆμ„ κ°€λŠ₯성이 λ†’λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. 개인적인 κ²½ν—˜μ„ ν† λŒ€λ‘œ μŠ€ν”„λ§ μΊμ‹œλ₯Ό ν™œμš©ν•˜λŠ” κ²½μš°μ— μ–΄λ–€ 뢀뢄듀을 κ³ λ―Όν•˜λ©΄ 쒋은지 λ‹€λ£¨μ–΄λ³΄κ³ μž ν•©λ‹ˆλ‹€.

μΊμ‹œ ν”„λ‘œλ°”μ΄λ”μ™€ λ‹€μ–‘ν•œ μΊμ‹œ μ „λž΅μ„ κ²€ν† ν•΄μ•Ό

applicaiton.yml
spring.cache: type: caffeine caffeine.spec: maximumSize=1000,expireAfterAccess=PT5M cache-names: category, book

단일 μΈμŠ€ν„΄μŠ€λ‘œ μš΄μš©λ˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œλ„ λ ˆλ””μŠ€μ™€ 같은 μ™ΈλΆ€ λ©”λͺ¨λ¦¬ μ €μž₯μ†Œλ₯Ό μ˜μ‘΄ν•˜κΈ°λ„ ν•˜λŠ”λ° μŠ€μΌ€μΌ 아웃이 λ˜μ§€ μ•Šμ„ κ°€λŠ₯성이 λ†’λ‹€λ©΄ 차라리 Caffeine κ³Ό 같은 둜컬 μΊμ‹œ ν”„λ‘œλ°”μ΄λ”λ₯Ό μ„ νƒν•˜κ³  ν”„λ‘œλ°”μ΄λ”λ§ˆλ‹€ μ μš©ν•  수 μžˆλŠ” λ‹€μ–‘ν•œ μΊμ‹œ μ „λž΅μ„ κ²€ν† ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. Caffeine을 λ„μž…ν•˜λ”λΌλ„ κΈ°λ³Έ μ˜ˆμ œμ—μ„œ μ†Œκ°œλ˜λŠ” CaffeineSpec을 개발 μ‹œ 뿐만 μ•„λ‹ˆλΌ μš΄μ˜ν™˜κ²½μ—μ„œ κ·ΈλŒ€λ‘œ μ‚¬μš©ν•  κ°€λŠ₯성도 λ†’μŠ΅λ‹ˆλ‹€. λ˜ν•œ, μΊμ‹œλ§ˆλ‹€ 효율적인 μ „λž΅μ΄ λ‹€μ–‘ν•˜λ―€λ‘œ μŠ€ν”„λ§ λΆ€νŠΈ μžλ™ 섀정에 μ˜ν•œ 단일 μ „λž΅λ³΄λ‹€λŠ” μΊμ‹œ 별 μ „λž΅μ„ λ³„λ„λ‘œ μ‚¬μš©ν•˜λ„λ‘ 직접 κ΅¬μ„±ν•˜λŠ”κ²Œ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.

@Cacheable ν‚€ ꡬ성을 확인해야

applicaiton.yml
logging.level.org.springframework.cache: trace

기본적으둜 CacheInterceptor에 λŒ€ν•œ 둜그 λ ˆλ²¨μ„ TRACE둜 지정해두지 μ•ŠκΈ° λ•Œλ¬Έμ— @Cacheable에 μ •μ˜ν•œ 킀에 λŒ€ν•΄μ„œ κ²€ν† ν•˜μ§€ μ•Šκ³  μΊμ‹œλ˜λŠ”μ§€ μ—¬λΆ€λ§Œ ν™•μΈν•˜μ—¬ 잘λͺ»λœ ν‚€λ‘œ μΊμ‹œλ˜μ–΄λ„ λ¬΄μ‹œλ˜κ³  μžˆμ„ κ°€λŠ₯성이 λ†’μŠ΅λ‹ˆλ‹€. λ˜ν•œ, 주둜 ν‚€λ₯Ό ꡬ성할 λ•Œ #root.methodName 만 μ‚¬μš©ν•΄λ²„λ¦¬λ©΄ ν‚€ 쀑볡 λ°œμƒμ΄ 높아지고 ClassCastException이 λ°œμƒν•  수 μžˆλŠ”λ° λ‹€μŒκ³Ό 같이 항상 μ„œλΉ„μŠ€ 클래슀 이름도 ν‚€ ꡬ성에 ν¬ν•¨ν•˜λŠ” 것을 μΆ”μ²œν•©λ‹ˆλ‹€. λ˜ν•œ, ν‚€ μ •μ˜ μ‹œ μ‚¬μš©ν•΄μ•Όν•  νŒŒλΌλ―Έν„° μˆ˜κ°€ λ§Žλ‹€λ©΄ λ‚˜μ—΄ν•˜κΈ° λ³΄λ‹€λŠ” KeyGenerator μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„μ²΄λ₯Ό λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•˜λŠ” 것이 더 κ°„λ‹¨ν•©λ‹ˆλ‹€.

@Cacheable(cacheNames = "[cache-name]", key = "T(String).join(':', #root.targetClass.simpleName, #root.methodName)")

μΊμ‹œμ— λŒ€ν•œ ν‚€ μ •μ˜ μ‹œ 쀑볡에 λŒ€ν•œ 뢀뢄은 λŸ°νƒ€μž„ μ˜ˆμ™Έλ‘œ λ°œμƒν•˜κΈ° λ•Œλ¬Έμ— 사전에 μΈμ§€ν•˜κΈ° μ–΄λ €μš΄ 뢀뢄이기 λ•Œλ¬Έμ— μ£Όμ˜ν•΄μ•Όν•  λΆ€λΆ„μž…λ‹ˆλ‹€.

@CacheEvict μ—μ„œ 전체 μ‚­μ œλ₯Ό ν•΄μ•Όν• κΉŒ

@CacheEvict(cacheNames="[cache-name]", allEntries = true)

μš°λ¦¬κ°€ μΊμ‹œλ˜μ–΄μ§„ 일뢀 데이터가 κ°±μ‹ λ˜μ–΄μ•Όν•  λ•Œ @CacheEvict와 ν•¨κ»˜ allEntries 속성을 μ‚¬μš©ν•˜μ—¬ λ™μΌν•œ μΊμ‹œ 이름을 가진 λͺ¨λ“  데이터λ₯Ό μ‚­μ œν•˜κ²Œ λ©λ‹ˆλ‹€. 개발자 μž…μž₯μ—μ„œ κ°„λ‹¨ν•˜κ³  νŽΈλ¦¬ν•΄λ³΄μ΄μ§€λ§Œ μΊμ‹œ 이름에 λŒ€ν•΄ μ €μž₯λ˜λŠ” ν‚€ νŒ¨ν„΄μ΄ λ‹€μ–‘ν•΄μ§€λŠ” κ²½μš°μ— νŠΉμ • ν‚€ νŒ¨ν„΄μœΌλ‘œ κ΅¬μ„±λœ 일뢀 λ°μ΄ν„°λ§Œ κ°±μ‹ ν•˜λ©΄ λ˜μ§€λ§Œ λͺ¨λ“  데이터가 μ‚­μ œλ˜μ–΄ GC λΆ€ν•˜ 뿐만 μ•„λ‹ˆλΌ λ‚˜λ¨Έμ§€ 데이터에 λŒ€ν•œ DB λΆ€ν•˜λ„ λ‹€μ‹œ λ°œμƒν•œλ‹€λŠ” 점을 μƒκ°ν•΄λ³΄κ²Œ λ©λ‹ˆλ‹€. @CacheEvict μ—μ„œ ν‚€ νŒ¨ν„΄μ— μ˜ν•΄ μ‚­μ œν•˜λŠ” 방법을 μ§€μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 직접 λ³„λ„λ‘œ AOP둜 κ΅¬ν˜„ν•΄μ•Όν•©λ‹ˆλ‹€.

자체 ν˜ΈμΆœμ— μ˜ν•œ μΊμ‹œκ°€ μ μš©λ˜μ§€ μ•ŠμŒ

μΈν…”λ¦¬μ œμ΄ IDE μ—μ„œ 자체 ν˜ΈμΆœμ— λŒ€ν•œ μ½”λ“œκ°€ λ°œμƒν•˜λŠ” 경우 μΉœμ ˆν•˜κ²Œ @Cacheable 자기 호좜(μ‹€μ§ˆμ μœΌλ‘œ 타깃 객체 λ‚΄μ˜ λ©”μ„œλ“œκ°€ 타깃 객체 λ‚΄μ˜ λ‹€λ₯Έ λ©”μ„œλ“œλ₯Ό 호좜) μž…λ‹ˆλ‹€. μΊμ‹œ μ–΄λ…Έν…Œμ΄μ…˜μ΄ λŸ°νƒ€μž„ μ‹œμ— λ¬΄μ‹œλ©λ‹ˆλ‹€λΌλŠ” 문ꡬ둜 κ²½κ³ ν•΄μ£Όκ³  μžˆμŠ΅λ‹ˆλ‹€. 이와 같이 μŠ€ν”„λ§ μΊμ‹œ μ–΄λ…Έν…Œμ΄μ…˜μ— μ˜ν•œ 선언적 μΊμ‹œλŠ” μŠ€ν”„λ§ AOPλ₯Ό 톡해 ν”„λ‘μ‹œλ‘œ λ™μž‘ν•˜λ―€λ‘œ 자체 호좜(Self Invocation)에 μ˜ν•΄μ„œλŠ” μ μš©λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ, 자체 호좜둜 남지 μ•Šλ„λ‘ μ½”λ“œ 리뷰λ₯Ό μˆ˜ν–‰ν•΄μ•Όν•˜κ³  Self-Injection λ˜λŠ” 자체 호좜이 λ˜μ§€ μ•ŠλŠ” ꡬ쑰둜 λ¦¬νŒ©ν† λ§ ν•΄μ•Όν•©λ‹ˆλ‹€.

μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μΊμ‹œ λΆ€ν•˜ λͺ¨λ‹ˆν„°λ§

CaffeineCache cache = (CaffeineCache) cacheManager.getCache("sample");
log.info("sample - {}", cache.getNativeCache().stats());

μ„œλΉ„μŠ€ νšŒμ‚¬μ˜ 개발 쑰직처럼 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λͺ¨λ‹ˆν„°λ§μ„ μ§€μ†μ μœΌλ‘œ μˆ˜ν–‰ν•˜μ§€ μ•ŠλŠ” 개발자 μž…μž₯μ—μ„œλŠ” 선언적 μΊμ‹œ 적용으둜 인해 λ°œμƒν•  수 μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λΆ€ν•˜λ₯Ό κ²€ν† ν•˜μ§€ μ•Šκ³  μžμ—°μŠ€λ ˆ λ¬΄μ‹œλ  κ°€λŠ₯성이 λ†’μŠ΅λ‹ˆλ‹€. μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— λ‹€μ–‘ν•œ κΈ°λŠ₯을 μΆ”κ°€ν•˜λ©΄μ„œ μΊμ‹œλ₯Ό λ„μž…ν•˜λ‹€λ³΄λ©΄ μ„œλ‘œ λ‹€λ₯Έ μΊμ‹œλ‘œ 인해 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μ •μ˜ν•œ μΊμ‹œ μ œν•œλŸ‰μ„ λ„˜μ–΄μ„œκ±°λ‚˜ μΊμ‹œκ°€ λ¬΄μ˜λ―Έν•˜κ²Œ μ €μž₯되고 μ‚­μ œλ˜λŠ” 것이 반볡될 수 μžˆμŠ΅λ‹ˆλ‹€. λ§Œμ•½, Caffiene μΊμ‹œλ₯Ό λ„μž…ν•œλ‹€λ©΄ recordStats μ˜΅μ…˜μ„ μ‚¬μš©ν•˜κ³  μΊμ‹œμ— λŒ€ν•œ 톡계λ₯Ό 주기적으둜 λͺ¨λ‹ˆν„°λ§ν•  수 μžˆλŠ” 둜그λ₯Ό λ‚¨κΈ°λŠ”κ²Œ μ’‹μŠ΅λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ, μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ 둜컬 μΊμ‹œλ‘œ μΆ©λΆ„ν•œλŒ€λ„ λΆˆκ΅¬ν•˜κ³  λ¬΄μž‘μ • λ ˆλ””μŠ€λΌλŠ” μ™ΈλΆ€ μΊμ‹œ μ €μž₯μ†Œ κΈ°μˆ μ— μ˜μ‘΄ν•˜κ³  μžˆλŠ”μ§€ λ‹€μ‹œν•œλ²ˆ 생각해보도둝 ν•©μ‹œλ‹€.