๐ฑ Spring @Cacheable์ ์ด์ฉํ ๋ฉ์๋ ๋ ๋ฒจ ์บ์ฑ ๐

์๋ ํ์ธ์, ์ฌ๋ฌ๋ถ! ์ค๋์ Spring ํ๋ ์์ํฌ์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ธ @Cacheable ์ด๋ ธํ ์ด์ ์ ์ด์ฉํ ๋ฉ์๋ ๋ ๋ฒจ ์บ์ฑ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ด ๊ธฐ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์๋ ๋ง๋ฒ ๊ฐ์ ๋๊ตฌ์์! ๐ฉโจ
์ฐ๋ฆฌ์ ์ฌ์ ์ ์์ํ๊ธฐ ์ ์, ์ ๊น ์์ํด ๋ณผ๊น์? ์ฌ๋ฌ๋ถ์ด ์ฌ๋ฅ๋ท(https://www.jaenung.net)๊ฐ์ ์ฌ๋ฅ ๊ณต์ ํ๋ซํผ์ ์ด์ํ๊ณ ์๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค. ์๋ง์ ์ฌ์ฉ์๋ค์ด ๋ค์ํ ์ฌ๋ฅ์ ๊ฑฐ๋ํ๊ณ , ์ ๋ณด๋ฅผ ์กฐํํ๋ ์ํฉ์์ ์ด๋ป๊ฒ ํ๋ฉด ์์คํ ์ ์๋ต ์๋๋ฅผ ๋์ผ ์ ์์๊น์? ๋ฐ๋ก ์ด๋ @Cacheable์ด ๋ฑ์ฅํ๋ ๊ฑฐ์ฃ ! ๐ฆธโโ๏ธ
๐ก Tip: ์บ์ฑ์ ์์ฃผ ์ฌ์ฉ๋๋ ๋ฐ์ดํฐ๋ฅผ ๋น ๋ฅด๊ฒ ์ ๊ทผํ ์ ์๋ ๊ณณ์ ์ ์ฅํด๋๋ ๊ธฐ์ ์ ๋๋ค. ๋ง์น ์ฌ๋ฌ๋ถ์ด ์์ฃผ ์ฌ์ฉํ๋ ๋ฌผ๊ฑด์ ์์ด ์ฝ๊ฒ ๋ฟ๋ ๊ณณ์ ๋๋ ๊ฒ๊ณผ ๊ฐ์์!
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก @Cacheable์ ์ธ๊ณ๋ก ๋ฐ์ด๋ค์ด ๋ด ์๋ค. ์ค๋น๋์ จ๋์? Let's go! ๐โโ๏ธ๐จ
๐ง @Cacheable: ์บ์ฑ์ ๊ธฐ๋ณธ ๊ฐ๋
@Cacheable์ Spring Framework์์ ์ ๊ณตํ๋ ์ด๋ ธํ ์ด์ ์ผ๋ก, ๋ฉ์๋์ ๊ฒฐ๊ณผ๋ฅผ ์บ์์ ์ ์ฅํ๊ณ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋์ผํ ์ ๋ ฅ์ ๋ํด ๋ฐ๋ณต์ ์ผ๋ก ์คํ๋๋ ๋ฉ์๋์ ์ฑ๋ฅ์ ๋ํญ ํฅ์์ํฌ ์ ์์ฃ .
@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 ๋ฉ์๋์ ๋์ ๊ณผ์ ์ ๋ณด์ฌ์ค๋๋ค. ์ฒ์ ํธ์ถ ์์๋ ์บ์์ ๋ฐ์ดํฐ๊ฐ ์์ด 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๋ถ์ผ๋ก ์ค์ ํฉ๋๋ค.
์ด ๋ค์ด์ด๊ทธ๋จ์ ์ฌ๋ฌ ์๋ฒ๊ฐ 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! ๐๐ฉโ๐ป๐จโ๐ป
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ