๐ŸŒฑ Spring @Cacheable์„ ์ด์šฉํ•œ ๋ฉ”์„œ๋“œ ๋ ˆ๋ฒจ ์บ์‹ฑ ๐Ÿš€

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŒฑ Spring @Cacheable์„ ์ด์šฉํ•œ ๋ฉ”์„œ๋“œ ๋ ˆ๋ฒจ ์บ์‹ฑ ๐Ÿš€

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ Spring ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ธ @Cacheable ์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•œ ๋ฉ”์„œ๋“œ ๋ ˆ๋ฒจ ์บ์‹ฑ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ์ˆ ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๋„๊ตฌ์˜ˆ์š”! ๐ŸŽฉโœจ

์šฐ๋ฆฌ์˜ ์—ฌ์ •์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, ์ž ๊น ์ƒ์ƒํ•ด ๋ณผ๊นŒ์š”? ์—ฌ๋Ÿฌ๋ถ„์ด ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)๊ฐ™์€ ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์„ ์šด์˜ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค. ์ˆ˜๋งŽ์€ ์‚ฌ์šฉ์ž๋“ค์ด ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ๊ฑฐ๋ž˜ํ•˜๊ณ , ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์‹œ์Šคํ…œ์˜ ์‘๋‹ต ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ์„๊นŒ์š”? ๋ฐ”๋กœ ์ด๋•Œ @Cacheable์ด ๋“ฑ์žฅํ•˜๋Š” ๊ฑฐ์ฃ ! ๐Ÿฆธโ€โ™‚๏ธ

๐Ÿ’ก Tip: ์บ์‹ฑ์€ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์— ์ €์žฅํ•ด๋‘๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ๋งˆ์น˜ ์—ฌ๋Ÿฌ๋ถ„์ด ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฌผ๊ฑด์„ ์†์ด ์‰ฝ๊ฒŒ ๋‹ฟ๋Š” ๊ณณ์— ๋‘๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„์š”!

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ @Cacheable์˜ ์„ธ๊ณ„๋กœ ๋›ฐ์–ด๋“ค์–ด ๋ด…์‹œ๋‹ค. ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's go! ๐Ÿƒโ€โ™‚๏ธ๐Ÿ’จ

๐Ÿง  @Cacheable: ์บ์‹ฑ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

@Cacheable์€ Spring Framework์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ, ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋™์ผํ•œ ์ž…๋ ฅ์— ๋Œ€ํ•ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๋ฉ”์„œ๋“œ์˜ ์„ฑ๋Šฅ์„ ๋Œ€ํญ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์ฃ .

@Cacheable์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋Š” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค: ๋ฉ”์„œ๋“œ๊ฐ€ ์ฒ˜์Œ ํ˜ธ์ถœ๋  ๋•Œ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ , ์ดํ›„ ๋™์ผํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์‹ค์ œ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์บ์‹œ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

@Cacheable ๋™์ž‘ ์›๋ฆฌ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์บ์‹œ ํ™•์ธ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ ์บ์‹ฑ ์บ์‹œ์— ์—†์Œ ์บ์‹œ์— ์žˆ์Œ

์ด ๊ทธ๋ฆผ์„ ๋ณด๋ฉด @Cacheable์˜ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ๋จผ์ € ์บ์‹œ๋ฅผ ํ™•์ธํ•˜๊ณ , ์บ์‹œ์— ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์—†๋‹ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜์ฃ .

์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์ด ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•œ๋‹ค๋ฉด, ์˜ˆ๋ฅผ ๋“ค์–ด ์ธ๊ธฐ ์žˆ๋Š” ์žฌ๋Šฅ ๋ชฉ๋ก์ด๋‚˜ ์ž์ฃผ ์กฐํšŒ๋˜๋Š” ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ์บ์‹ฑํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ์‘๋‹ต ์†๋„๋ฅผ ํฌ๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๐Ÿš€

๐ŸŽญ ๋น„์œ ๋กœ ์ดํ•ดํ•˜๊ธฐ: @Cacheable์€ ๋งˆ์น˜ ์—ฌ๋Ÿฌ๋ถ„์˜ ๋‡Œ์™€ ๊ฐ™์•„์š”. ํ•œ ๋ฒˆ ๋ฐฐ์šด ๋‚ด์šฉ์€ ๊ธฐ์–ตํ•ด๋‘์—ˆ๋‹ค๊ฐ€ ๋‚˜์ค‘์— ๊ฐ™์€ ์งˆ๋ฌธ์„ ๋ฐ›์œผ๋ฉด ๋‹ค์‹œ ์ƒ๊ฐํ•  ํ•„์š” ์—†์ด ๋ฐ”๋กœ ๋Œ€๋‹ตํ•  ์ˆ˜ ์žˆ์ฃ . ์ด๊ฒƒ์ด ๋ฐ”๋กœ ์บ์‹ฑ์˜ ํ•ต์‹ฌ ์•„์ด๋””์–ด์ž…๋‹ˆ๋‹ค!

์ด์ œ @Cacheable์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์ดํ•ดํ•˜์…จ๋‚˜์š”? ์ข‹์Šต๋‹ˆ๋‹ค! ๋‹ค์Œ์œผ๋กœ ์ด ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ž์„ธํžˆ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's dive deeper! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ› ๏ธ @Cacheable ์‚ฌ์šฉํ•˜๊ธฐ: ๊ธฐ๋ณธ ์„ค์ •

์ž, ์ด์ œ @Cacheable์„ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ๋งˆ์น˜ ์š”๋ฆฌ ๋ ˆ์‹œํ”ผ๋ฅผ ๋”ฐ๋ผํ•˜๋“ฏ์ด, ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿณ

1. ์˜์กด์„ฑ ์ถ”๊ฐ€ํ•˜๊ธฐ

๋จผ์ €, Spring Boot ํ”„๋กœ์ ํŠธ์— ์บ์‹ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Maven์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด pom.xml์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•˜์„ธ์š”:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  

Gradle์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด build.gradle ํŒŒ์ผ์— ๋‹ค์Œ ์ค„์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:


implementation 'org.springframework.boot:spring-boot-starter-cache'
  

๐Ÿ’ก Tip: ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ ํ”Œ๋žซํผ์„ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ์˜์กด์„ฑ ๊ด€๋ฆฌ๊ฐ€ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ์˜์กด์„ฑ ์ถฉ๋Œ์ด๋‚˜ ๋ฒ„์ „ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ, ํ•ญ์ƒ ์ตœ์‹  ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ณ  ํ˜ธํ™˜์„ฑ์„ ์ฒดํฌํ•˜์„ธ์š”!

2. ์บ์‹ฑ ํ™œ์„ฑํ™”ํ•˜๊ธฐ

์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค๋ฉด, ์ด์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์บ์‹ฑ ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค์— @EnableCaching ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
  

์ด @EnableCaching ์–ด๋…ธํ…Œ์ด์…˜์€ Spring์—๊ฒŒ "์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์บ์‹ฑ์„ ์‚ฌ์šฉํ•  ๊ฑฐ์•ผ!"๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

3. @Cacheable ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉํ•˜๊ธฐ

์ด์ œ ์‹ค์ œ๋กœ ์บ์‹ฑํ•˜๊ณ  ์‹ถ์€ ๋ฉ”์„œ๋“œ์— @Cacheable ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ๋ด…์‹œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท์—์„œ ์‚ฌ์šฉ์ž์˜ ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณผ๊นŒ์š”?


import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(value = "userProfile", key = "#userId")
    public UserProfile getUserProfile(Long userId) {
        // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„์„ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ์ง
        // ์ด ๋ถ€๋ถ„์ด ์‹œ๊ฐ„์ด ๋งŽ์ด ๊ฑธ๋ฆฐ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ์‹œ๋‹ค
        return userRepository.findProfileByUserId(userId);
    }
}
  

์ด ์ฝ”๋“œ์—์„œ @Cacheable ์–ด๋…ธํ…Œ์ด์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค:

  • value = "userProfile": ์ด ์บ์‹œ์˜ ์ด๋ฆ„์„ "userProfile"๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • key = "#userId": ์บ์‹œ์˜ ํ‚ค๋กœ ๋ฉ”์„œ๋“œ์˜ userId ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด, ๊ฐ™์€ userId๋กœ getUserProfile ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค Spring์€ ๋จผ์ € "userProfile" ์บ์‹œ์—์„œ ํ•ด๋‹น userId์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์žˆ๋‹ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์บ์‹œ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์—†๋‹ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•œ ํ›„ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

@Cacheable ๋™์ž‘ ๊ณผ์ • getUserProfile(1) ์บ์‹œ ํ™•์ธ DB ์กฐํšŒ ๊ฒฐ๊ณผ ์บ์‹ฑ ์บ์‹œ miss ์บ์‹œ hit

์ด ๊ทธ๋ฆผ์€ @Cacheable์ด ์ ์šฉ๋œ getUserProfile ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘ ๊ณผ์ •์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ฒ˜์Œ ํ˜ธ์ถœ ์‹œ์—๋Š” ์บ์‹œ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด DB๋ฅผ ์กฐํšŒํ•˜์ง€๋งŒ, ์ดํ›„ ๋™์ผํ•œ userId๋กœ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ์บ์‹œ์—์„œ ๋ฐ”๋กœ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

๐ŸŒŸ ์„ฑ๋Šฅ ํ–ฅ์ƒ์˜ ๋น„๋ฐ€: ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋Š” ์ž์ฃผ ์กฐํšŒ๋˜์ง€๋งŒ ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€๋Š” ์•Š๋Š” ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ @Cacheable์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ํšŸ์ˆ˜๋ฅผ ํฌ๊ฒŒ ์ค„์—ฌ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”!

์ž, ์ด์ œ @Cacheable์˜ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ์‹œ์ž‘์— ๋ถˆ๊ณผํ•ด์š”! ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ๋” ๋ณต์žกํ•œ ์ƒํ™ฉ์—์„œ @Cacheable์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Ready for more? Let's go! ๐Ÿš€

๐Ÿง™โ€โ™‚๏ธ @Cacheable์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

๊ธฐ๋ณธ์ ์ธ @Cacheable ์‚ฌ์šฉ๋ฒ•์„ ๋งˆ์Šคํ„ฐํ•˜์…จ๋‚˜์š”? ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค! ์ด์ œ ๋” ๋ณต์žกํ•œ ์ƒํ™ฉ์—์„œ @Cacheable์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณผ ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. ๋งˆ์น˜ ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ๊ณ ๊ธ‰ ์ฃผ๋ฌธ์„ ์ตํžˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์šฐ๋ฆฌ๋„ @Cacheable์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ํ•˜๋‚˜์”ฉ ์ตํ˜€๋ณผ๊นŒ์š”? ๐ŸŽฉโœจ

1. ์กฐ๊ฑด๋ถ€ ์บ์‹ฑ

๋•Œ๋กœ๋Š” ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ์บ์‹ฑ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. @Cacheable ์–ด๋…ธํ…Œ์ด์…˜์˜ condition ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”.


@Cacheable(value = "userProfile", key = "#userId", condition = "#userId > 1000")
public UserProfile getUserProfile(Long userId) {
    // ๋ฉ”์„œ๋“œ ๋กœ์ง
}
  

์ด ์˜ˆ์ œ์—์„œ๋Š” userId๊ฐ€ 1000๋ณด๋‹ค ํฐ ๊ฒฝ์šฐ์—๋งŒ ์บ์‹ฑ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์บ์‹œ ์‚ฌ์šฉ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์ฃ !

2. ๋‹ค์ค‘ ์บ์‹œ ์‚ฌ์šฉ

ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ ๊ฒฐ๊ณผ๋ฅผ ์—ฌ๋Ÿฌ ์บ์‹œ์— ์ €์žฅํ•ด์•ผ ํ•  ๋•Œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. @Cacheable ์–ด๋…ธํ…Œ์ด์…˜์€ ์ด๋Ÿฐ ์ƒํ™ฉ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.


@Cacheable(value = {"userProfile", "userDetails"}, key = "#userId")
public UserProfile getUserProfile(Long userId) {
    // ๋ฉ”์„œ๋“œ ๋กœ์ง
}
  

์ด ์„ค์ •์€ ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ "userProfile"๊ณผ "userDetails" ๋‘ ์บ์‹œ์— ๋ชจ๋‘ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก Tip: ์žฌ๋Šฅ๋„ท์—์„œ ์ด ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋ฉด, ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ์—ฌ๋Ÿฌ ์ปจํ…์ŠคํŠธ(์˜ˆ: ์ผ๋ฐ˜ ํ”„๋กœํ•„ ๋ทฐ, ์ƒ์„ธ ์ •๋ณด ๋ทฐ)์—์„œ ๋น ๋ฅด๊ฒŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ฃผ์˜ํ•˜์„ธ์š”! ๋„ˆ๋ฌด ๋งŽ์€ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์š”.

3. ๋™์  ํ‚ค ์ƒ์„ฑ

๋•Œ๋กœ๋Š” ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํžˆ ์œ ๋‹ˆํฌํ•œ ์บ์‹œ ํ‚ค๋ฅผ ๋งŒ๋“ค๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿด ๋•Œ SpEL(Spring Expression Language)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ ์œผ๋กœ ํ‚ค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


@Cacheable(value = "userProfile", key = "T(java.util.Objects).hash(#userId, #includePrivate)")
public UserProfile getUserProfile(Long userId, boolean includePrivate) {
    // ๋ฉ”์„œ๋“œ ๋กœ์ง
}
  

์ด ์˜ˆ์ œ์—์„œ๋Š” userId์™€ includePrivate ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ์œ ๋‹ˆํฌํ•œ ์บ์‹œ ํ‚ค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

4. ์บ์‹œ ๋งŒ๋ฃŒ ์„ค์ •

์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜์›ํžˆ ์œ ํšจํ•  ์ˆ˜๋Š” ์—†๊ฒ ์ฃ ? Spring์˜ @Cacheable์€ ์ง์ ‘์ ์ธ ๋งŒ๋ฃŒ ์„ค์ •์„ ์ œ๊ณตํ•˜์ง€ ์•Š์ง€๋งŒ, ์ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.


@Cacheable(value = "userProfile", key = "#userId", unless = "#result.lastUpdated < T(java.time.Instant).now().minusSeconds(3600)")
public UserProfile getUserProfile(Long userId) {
    UserProfile profile = // ํ”„๋กœํ•„ ์กฐํšŒ ๋กœ์ง
    profile.setLastUpdated(Instant.now());
    return profile;
}
  

์ด ์˜ˆ์ œ์—์„œ๋Š” ๊ฒฐ๊ณผ์˜ lastUpdated ํ•„๋“œ๋ฅผ ํ™•์ธํ•˜์—ฌ 1์‹œ๊ฐ„(3600์ดˆ) ์ด์ƒ ์ง€๋‚œ ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์บ์‹œ ๋งŒ๋ฃŒ ํ”„๋กœ์„ธ์Šค ์บ์‹œ ์š”์ฒญ ๋งŒ๋ฃŒ ํ™•์ธ ๋ฐ์ดํ„ฐ ๊ฐฑ์‹  ์บ์‹œ ์—…๋ฐ์ดํŠธ ๋งŒ๋ฃŒ๋จ ์œ ํšจํ•จ

์ด ๋‹ค์ด์–ด๊ทธ๋žจ์€ ์บ์‹œ ๋งŒ๋ฃŒ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์บ์‹œ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋งŒ๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ , ๋งŒ๋ฃŒ๋œ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•œ ํ›„ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

5. ์บ์‹œ ๋™๊ธฐํ™”

๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์บ์‹œ๋ฅผ ์–ด๋–ป๊ฒŒ ๋™๊ธฐํ™”ํ• ๊นŒ์š”? Spring์€ @CachePut๊ณผ @CacheEvict ์–ด๋…ธํ…Œ์ด์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


@CachePut(value = "userProfile", key = "#userId")
public UserProfile updateUserProfile(Long userId, UserProfileUpdateDto updateDto) {
    // ํ”„๋กœํ•„ ์—…๋ฐ์ดํŠธ ๋กœ์ง
}

@CacheEvict(value = "userProfile", key = "#userId")
public void deleteUserProfile(Long userId) {
    // ํ”„๋กœํ•„ ์‚ญ์ œ ๋กœ์ง
}
  

@CachePut์€ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ํ•ญ์ƒ ์บ์‹œ์— ์ €์žฅํ•˜๋ฉฐ, @CacheEvict๋Š” ์ง€์ •๋œ ์บ์‹œ ํ•ญ๋ชฉ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

๐Ÿš€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ: ์žฌ๋Šฅ๋„ท์—์„œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ์ž์ฃผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒฝ์šฐ, @CachePut์„ ์‚ฌ์šฉํ•˜์—ฌ DB ์—…๋ฐ์ดํŠธ์™€ ๋™์‹œ์— ์บ์‹œ๋„ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ญ์ƒ ์ตœ์‹  ์ •๋ณด๋ฅผ ๋น ๋ฅด๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด์š”!

์ด๋Ÿฌํ•œ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด, ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์—์„œ๋„ ํšจ์œจ์ ์ธ ์บ์‹ฑ ์ „๋žต์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์€ ํ–ฅ์ƒ๋˜๊ณ , ์„œ๋ฒ„ ๋ถ€ํ•˜๋Š” ์ค„์–ด๋“ค์ฃ !

์ž, ์ด์ œ @Cacheable์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋“ค์„ ์–ด๋–ป๊ฒŒ ์กฐํ•ฉํ•˜๊ณ  ํ™œ์šฉํ• ์ง€๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฐฝ์˜๋ ฅ์— ๋‹ฌ๋ ค์žˆ์–ด์š”! ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ @Cacheable์„ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ๋“ค์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Ready for some pro tips? Let's go! ๐Ÿ„โ€โ™‚๏ธ

๐ŸŽฏ @Cacheable ์‹ค์ „ ํ™œ์šฉ ํŒ

์—ฌ๋Ÿฌ๋ถ„! ์ง€๊ธˆ๊นŒ์ง€ @Cacheable์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๊นŒ์ง€ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด์ œ๋Š” ์ด ์ง€์‹์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ๋“ค๊ณผ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ ํ”Œ๋žซํผ์—์„œ @Cacheable์„ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? Let's dive in! ๐ŸŠโ€โ™‚๏ธ

1. ์บ์‹œ ํฌ๊ธฐ ๊ด€๋ฆฌ

์บ์‹œ๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๋ฌด๋ถ„๋ณ„ํ•œ ์บ์‹ฑ์€ ์˜คํžˆ๋ ค ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์„ ์ €ํ•˜์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ์บ์‹œ ํฌ๊ธฐ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.


@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<cache> caches = new ArrayList<>();
        caches.add(new ConcurrentMapCache("userProfile", CacheBuilder.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(1, TimeUnit.HOURS)
                .build().asMap(), false));
        cacheManager.setCaches(caches);
        return cacheManager;
    }
}
  </cache>

์ด ์„ค์ •์€ "userProfile" ์บ์‹œ์˜ ์ตœ๋Œ€ ํฌ๊ธฐ๋ฅผ 1000๊ฐœ ํ•ญ๋ชฉ์œผ๋กœ ์ œํ•œํ•˜๊ณ , ๊ฐ ํ•ญ๋ชฉ์€ 1์‹œ๊ฐ„ ํ›„ ๋งŒ๋ฃŒ๋˜๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก Tip: ์žฌ๋Šฅ๋„ท์—์„œ ๊ฐ€์žฅ ํ™œ๋ฐœํ•œ 1000๋ช…์˜ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„๋งŒ ์บ์‹ฑํ•˜๋ฉด, ์ „์ฒด ํŠธ๋ž˜ํ”ฝ์˜ ์ƒ๋‹น ๋ถ€๋ถ„์„ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 80-20 ๋ฒ•์น™์„ ๊ธฐ์–ตํ•˜์„ธ์š”!

2. ์บ์‹œ ์ •์ฑ… ์ตœ์ ํ™”

๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์บ์‹ฑํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์บ์‹œ ์ •์ฑ…์„ ์ ์šฉํ•ด๋ณด์„ธ์š”.


@Cacheable(value = "frequentlyAccessedProfiles", key = "#userId", unless = "#result == null")
public UserProfile getFrequentlyAccessedProfile(Long userId) {
    // ์ž์ฃผ ์ ‘๊ทผ๋˜๋Š” ํ”„๋กœํ•„ ๋กœ์ง
}

@Cacheable(value = "rarelyAccessedProfiles", key = "#userId", unless = "#result == null", condition = "#userId > 10000")
public UserProfile getRarelyAccessedProfile(Long userId) {
    // ๋“œ๋ฌผ๊ฒŒ ์ ‘๊ทผ๋˜๋Š” ํ”„๋กœํ•„ ๋กœ์ง
}
  

์ด ์˜ˆ์ œ์—์„œ๋Š” ์ž์ฃผ ์ ‘๊ทผ๋˜๋Š” ํ”„๋กœํ•„๊ณผ ๋“œ๋ฌผ๊ฒŒ ์ ‘๊ทผ๋˜๋Š” ํ”„๋กœํ•„์„ ๋‹ค๋ฅธ ์บ์‹œ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋“œ๋ฌผ๊ฒŒ ์ ‘๊ทผ๋˜๋Š” ํ”„๋กœํ•„์€ ์กฐ๊ฑด๋ถ€๋กœ ์บ์‹ฑ๋ฉ๋‹ˆ๋‹ค.

3. ๋ถ„์‚ฐ ์บ์‹œ ์‚ฌ์šฉ

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋กœ์ปฌ ์บ์‹œ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Redis๋‚˜ Memcached์™€ ๊ฐ™์€ ๋ถ„์‚ฐ ์บ์‹œ ์‹œ์Šคํ…œ์„ ๋„์ž…ํ•ด๋ณด์„ธ์š”.


@Configuration
@EnableCaching
public class RedisCacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(10)))
                .build();
        return cacheManager;
    }
}
  

์ด ์„ค์ •์€ Redis๋ฅผ ์บ์‹œ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ธฐ๋ณธ TTL(Time To Live)์„ 10๋ถ„์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ถ„์‚ฐ ์บ์‹œ ์‹œ์Šคํ…œ ์„œ๋ฒ„ 1 ์„œ๋ฒ„ 2 ์„œ๋ฒ„ 3 Redis ๋ถ„์‚ฐ ์บ์‹œ

์ด ๋‹ค์ด์–ด๊ทธ๋žจ์€ ์—ฌ๋Ÿฌ ์„œ๋ฒ„๊ฐ€ Redis ๋ถ„์‚ฐ ์บ์‹œ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋น ๋ฅธ ์‘๋‹ต ์†๋„๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. ์บ์‹œ ์›Œ๋ฐ์—…

์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์‹œ ์บ์‹œ๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด ์ดˆ๊ธฐ์— ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์บ์‹œ ์›Œ๋ฐ์—… ์ „๋žต์„ ๊ตฌํ˜„ํ•ด๋ณด์„ธ์š”.


@Component
public class CacheWarmer implements ApplicationListener<contextrefreshedevent> {

    @Autowired
    private UserService userService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        List<long> topUserIds = userService.getTopUserIds(1000);
        for (Long userId : topUserIds) {
            userService.getUserProfile(userId);  // ์ด ํ˜ธ์ถœ๋กœ ์บ์‹œ๊ฐ€ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค
        }
    }
}
  </long></contextrefreshedevent>

์ด ์ฝ”๋“œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ์ƒ์œ„ 1000๋ช…์˜ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„์„ ๋ฏธ๋ฆฌ ์บ์‹œ์— ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

5. ์บ์‹œ ๋ชจ๋‹ˆํ„ฐ๋ง

์บ์‹œ์˜ ํšจ์œจ์„ฑ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์บ์‹œ ํžˆํŠธ์œจ, ๋ฏธ์Šค์œจ ๋“ฑ์˜ ์ง€ํ‘œ๋ฅผ ์ถ”์ ํ•ด๋ณด์„ธ์š”.


@Configuration
@EnableCaching
public class CacheMonitoringConfig extends CachingConfigurerSupport {

    @Bean
    @Override
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<cache> caches = new ArrayList<>();
        caches.add(new MonitoringCache("userProfile", new ConcurrentMapCache("userProfile")));
        cacheManager.setCaches(caches);
        return cacheManager;
    }
}

public class MonitoringCache implements Cache {
    private final Cache delegate;
    private final String name;
    private final AtomicLong hits = new AtomicLong();
    private final AtomicLong misses = new AtomicLong();

    // ๊ตฌํ˜„ details...
}
  </cache>

์ด ์„ค์ •์€ ์บ์‹œ ์ž‘์—…์„ ๋ž˜ํ•‘ํ•˜์—ฌ ํžˆํŠธ์ˆ˜์™€ ๋ฏธ์Šค์ˆ˜๋ฅผ ์นด์šดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์— ์—ฐ๋™ํ•˜๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์บ์‹œ ์„ฑ๋Šฅ์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿš€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ: ์žฌ๋Šฅ๋„ท์—์„œ ์บ์‹œ ํžˆํŠธ์œจ์ด ๋‚ฎ์€ ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค๋ฉด, ํ•ด๋‹น ๋ฐ์ดํ„ฐ์˜ ์ ‘๊ทผ ํŒจํ„ด์„ ๋ถ„์„ํ•ด๋ณด์„ธ์š”. ์บ์‹œ ํ‚ค ์„ค๊ณ„๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜, ์บ์‹œ ์œ ํšจ ๊ธฐ๊ฐ„์„ ์กฐ์ •ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ํฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์ด๋Ÿฌํ•œ ์‹ค์ „ ํŒ๋“ค์„ ์ ์šฉํ•˜๋ฉด, @Cacheable์„ ๋”์šฑ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ ํ”Œ๋žซํผ์—์„œ๋Š” ์ด๋Ÿฐ ์„ธ์„ธํ•œ ์ตœ์ ํ™”๊ฐ€ ์ „์ฒด ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์–ด์š”!

์ž, ์ด์ œ @Cacheable์˜ ์‹ค์ „ ํ™œ์šฉ ํŒ๋“ค์„ ๋ชจ๋‘ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•  ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”? ๊ธฐ์–ตํ•˜์„ธ์š”, ์บ์‹ฑ์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ์ƒ ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹œ์Šคํ…œ ํŠน์„ฑ์— ๋งž๊ฒŒ ์กฐ์ •ํ•˜๊ณ  ์ตœ์ ํ™”ํ•˜์„ธ์š”. ๊ทธ๋Ÿผ, ๋ฉ‹์ง„ ์บ์‹ฑ ์—ฌํ–‰์ด ๋˜์‹œ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค! Happy coding! ๐Ÿš€๐Ÿ‘จโ€๐Ÿ’ป๐Ÿ‘ฉโ€๐Ÿ’ป

๐ŸŽ“ ๊ฒฐ๋ก : @Cacheable์˜ ๋งˆ๋ฒ•์„ ํ˜„์‹ค๋กœ

์šฐ๋ฆฌ๋Š” ๊ธด ์—ฌ์ •์„ ํ†ตํ•ด Spring์˜ @Cacheable ์–ด๋…ธํ…Œ์ด์…˜์˜ ์„ธ๊ณ„๋ฅผ ํƒํ—˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ, ๊ทธ๋ฆฌ๊ณ  ์‹ค์ „ ํ™œ์šฉ ํŒ๊นŒ์ง€, @Cacheable์˜ ๋ชจ๋“  ๊ฒƒ์„ ์‚ดํŽด๋ณด์•˜์ฃ . ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ ์ด ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

๊ธฐ์–ตํ•˜์„ธ์š”, @Cacheable์€ ๋‹จ์ˆœํ•œ ์–ด๋…ธํ…Œ์ด์…˜ ๊ทธ ์ด์ƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์„ฑ๋Šฅ์ด๋ผ๋Š” ๋งˆ๋ฒ•์„ ๋ถˆ์–ด๋„ฃ๋Š” ์ฃผ๋ฌธ๊ณผ๋„ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ ํ”Œ๋žซํผ์—์„œ @Cacheable์„ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ค๊ณ  ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ชจ๋“  ๋งˆ๋ฒ•๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, @Cacheable๋„ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์บ์‹œ ํฌ๊ธฐ, ๋งŒ๋ฃŒ ์ •์ฑ…, ๋™๊ธฐํ™” ์ „๋žต ๋“ฑ์„ ์‹ ์ค‘ํžˆ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋ฉฐ, ์ง€์†์ ์ธ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์บ์‹ฑ์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ํ•ญ์ƒ ๋ช…์‹ฌํ•˜์„ธ์š”.

๐Ÿ’ก ์ตœ์ข… ์กฐ์–ธ: @Cacheable์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ "์ด ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œํ•ด์•ผ ํ•˜๋Š”๊ฐ€?"๋ผ๋Š” ์งˆ๋ฌธ์„ ๋จผ์ € ํ•ด๋ณด์„ธ์š”. ๋ชจ๋“  ๊ฒƒ์„ ์บ์‹œํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ ์ ˆํ•œ ๊ฒƒ์„ ์บ์‹œํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์žฌ๋Šฅ๋„ท์˜ ๊ฒฝ์šฐ, ์ž์ฃผ ์กฐํšŒ๋˜์ง€๋งŒ ์ž˜ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ(์˜ˆ: ์‚ฌ์šฉ์ž ํ”„๋กœํ•„, ์ธ๊ธฐ ์žฌ๋Šฅ ๋ชฉ๋ก ๋“ฑ)๊ฐ€ ์บ์‹ฑ์˜ ์ข‹์€ ํ›„๋ณด๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Cacheable์˜ ์„ธ๊ณ„๋Š” ๊ด‘๋Œ€ํ•˜๊ณ  ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ ๋‹ค๋ฃฌ ๋‚ด์šฉ์€ ์‹œ์ž‘์— ๋ถˆ๊ณผํ•ฉ๋‹ˆ๋‹ค. ๊ณ„์†ํ•ด์„œ ํ•™์Šตํ•˜๊ณ , ์‹คํ—˜ํ•˜๊ณ , ์ตœ์ ํ™”ํ•˜์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด @Cacheable์˜ ๋งˆ๋ฒ•์œผ๋กœ ๋”์šฑ ๋น›๋‚˜๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค!

์ž, ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋งŒ์˜ ์บ์‹ฑ ์ „๋žต์„ ์ˆ˜๋ฆฝํ•˜๊ณ  ๊ตฌํ˜„ํ•  ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ์žฌ๋Šฅ๋„ท์„ ๋„˜์–ด, ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ๊ฐ€ @Cacheable์˜ ํž˜์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™”ํ• ์ง€ ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ์ƒˆ๋กœ์šด ์žฅ์„ ์—ด ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”? Let's cache it and make it fast! ๐Ÿš€๐Ÿ’พ

ํ–‰์šด์„ ๋น•๋‹ˆ๋‹ค, ๊ทธ๋ฆฌ๊ณ  ์ฆ๊ฑฐ์šด ์ฝ”๋”ฉ ๋˜์„ธ์š”! Happy caching! ๐ŸŽ‰๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป