์๋ ํ์ธ์~ ์ค๋๋ง์ ํฌ์คํ ์ ํ๊ฒ๋๋ค์.
์ค๋์ ์คํ๋ง๋ถํธ ๊ธฐ๋ฐ์ ํ๋ก์ ํธ์์ ์คํ๋ง์บ์๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ๊ณต์ ๋๋ฆฌ๋ ค๊ณ ํฉ๋๋ค.
์คํ๋ง์บ์๋ ๋ ๋์ค, ์นดํ์ธ, ehcache ๋ฑ๊ณผ ์ฐ๊ณํ์ฌ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ์,
์ด๋ฒ ํฌ์คํ ์์๋ ehcache์ ์ฐ๋ํ์ฌ ์ฌ์ฉํ ์์ ์ ๋๋ค.
์ฐ์ ๊ธฐ๋ณธ์ ์ธ ๊ฐ๋ฐํ๊ฒฝ์ ์๋์ ๊ฐ์ต๋๋ค.
- IntelliJ IDEA 2021.3.3 (Ultimate Edition)
- SpringBoot 2.7.3
- Java 18 (OpenJDK)
1. ํ๋ก์ ํธ ์์ฑ
์ฐ์ ๋น ๊นกํต ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด ๋ณผ๊ฒ์.
์ด๋ฏธ ์ฌ์ฉํ ํ๋ก์ ํธ๊ฐ ์๋ค๋ฉด ๊ฑด๋๋ฐ์ ๋ ์ข์ต๋๋ค :)
์, ๋น๊นกํต ํ๋ก์ ํธ ์์ฑ์ด ์๋ฃ๋์์ต๋๋ค.
2. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
์ด์ ๊ตฌํ ๋ฐ ํ ์คํธ์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ถ๊ฐํด๋ณผ๊ฒ์.
์ฐ์ ํ์ฌ build.gradle ํ์ผ์ ์๋์ ๊ฐ์ด ๋์ด์์๊ฑฐ์์
plugins {
id 'org.springframework.boot' version '2.7.3'
id 'io.spring.dependency-management' version '1.0.13.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '18'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
์ ๊ธฐ์ ์๋ 5๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ถ๊ฐํด์ค๋๋ค.
implementation 'org.ehcache:ehcache:3.10.1'
implementation 'org.springframework:spring-context-support:5.3.22'
implementation 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
implementation 'ch.qos.logback:logback-classic:1.2.11'
์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ํ์ํ์ง ํ๋์ฉ ๋ณผ๊ฒ์.
implementation 'org.ehcache:ehcache:3.10.1' | jcache ๊ตฌํ์ฒด์ธ ehcache ์ฌ์ฉ์ ์ํจ |
implementation 'org.springframework:spring-context-support:5.3.22' | ์คํ๋ง์บ์์ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๊ธฐ ์ํจ |
implementation 'org.projectlombok:lombok:1.18.24' annotationProcessor 'org.projectlombok:lombok:1.18.24' |
lombok ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๊ธฐ ์ํจ |
implementation 'ch.qos.logback:logback-classic:1.2.11' | ๋ก๊น ์ ์ํจ |
์ด๋ ๊ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ ๋ค ์บ์์ค์ ์ ์ข ํด์ฃผ๋๋ก ํ ๊ฒ์
3. ์บ์ ์ค์
com.example.demo.config ํจํค์ง๋ฅผ ๋ง๋ ๋ค ์๋์ ๊ฐ์ด CacheConfig ํด๋์ค๋ฅผ ์์ฑํด์ค๋๋ค.
package com.example.demo.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.cache.Cache;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;
import java.util.concurrent.TimeUnit;
@EnableCaching
@Configuration
public class CacheConfig {
private final CachingProvider cachingProvider = Caching.getCachingProvider();
private final javax.cache.CacheManager cacheManager = cachingProvider.getCacheManager();
@Bean
public CacheManager cacheManager() {
return new JCacheCacheManager(cacheManager);
}
@Bean
public Cache<Integer, Integer> commonCache() {
MutableConfiguration<Integer, Integer> configuration =
new MutableConfiguration<Integer, Integer>()
.setTypes(Integer.class, Integer.class)
.setStoreByValue(false)
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, 5)));
return cacheManager.createCache("commonCache", configuration);
}
}
์ ๋ด์ฉ์ ๊ฐ๋ตํ ์ดํด๋ณด๋ฉด cacheManager ๋น์ ํ๋ ๋ฑ๋กํด์ฃผ๊ณ ๊ทธ ์บ์๋งค๋์ ์ commonCache๋ผ๋ ์ด๋ฆ์ ์บ์ ๋น์ ํ๋ ๋ฑ๋กํด์ฃผ๋ ๊ฒ๋๋ค. ์ด commonCache๊ฐ key, value ํ์ด๋ฅผ ์ ์ฅํ๋ ํ๋์ ์๋์ด๋ผ๊ณ ์๊ฐํ์๋ฉด๋๊ณ ์. ์ฌ๊ธฐ์๋ ํค์ ๊ฐ์ ํ์ ์ด ๋ชจ๋ Integer ์ด๊ณ ๋ง๋ฃ์ ์ฑ ์ ์์ฑ์๊ฐ์ ๊ธฐ์ค์ผ๋ก 5์ด๋ก ์ค์ ํด์ฃผ์์ต๋๋ค.
4. API ๊ตฌํ ๋ฐ ํ ์คํธ
์ด์ commonCache์ ๊ฐ์ ์ ์ฅํ๊ณ ๋ง๋ฃ๋๊ธฐ ์ ๊น์ง ์บ์์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋๋ก ์๋น์ค ํด๋์ค๋ฅผ ํ๋ ๋ง๋ค์ด์ฃผ๊ฒ ์ต๋๋ค.
package com.example.demo.service;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class CacheService {
@Cacheable(cacheNames = "commonCache", cacheManager = "cacheManager", key = "#code")
public Integer getCachedValue(int code) {
System.out.println("๊ณ์ฐ์ค....");
// DB ์กฐํ ๋ฑ์ ๋ก์ง...
return (int) (Math.random() * 10);
}
}
com.example.demo.service ํจํค์ง๋ฅผ ๋ง๋ค๊ณ CacheService ํด๋์ค๋ฅผ ์์ฑํ์ต๋๋ค.
getCachedValue ๋ฉ์๋์ @Cacheable ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ฃผ๊ณ commonCache๋ฅผ ์ฌ์ฉํ๊ณ cacheManager๋ CacheConfig์ ๋ฑ๋กํ๋ cacheManager ์ด๋ฆ์ ์ ์ด์ฃผ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ key๋ก ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ฐ๋ code ๊ฐ์ ์ฌ์ฉํ๊ฒ ๋ค๊ณ ์ ์ธํด์ฃผ์์ต๋๋ค. ๋ง์ฝ key๋ก ์ด๋ค ๊ฐ์ฒด์ ๋ฉค๋ฒ๋ณ์๋ฅผ ์ฌ์ฉํด์ผํ๋ค๋ฉด key = "#obj.employeeId" ์ ๊ฐ์ด ์ฌ์ฉ๊ฐ๋ฅํฉ๋๋ค. (๊ณต์๋ฌธ์ ์ฐธ๊ณ )
CacheService.getCachedValue ๊ฐ ํธ์ถ๋๋ฉด code ๊ฐ์ key๋กํ์ฌ return๋ Integer ๊ฐ์ commonCache์ ์ ์ฅํ๊ฒ๋๊ณ ๋ง๋ฃ์๊ฐ์ธ 5์ด ์ด๋ด์ ๋์ผํ key๊ฐ(์ฌ๊ธฐ์๋ code ํ๋ผ๋ฏธํฐ ๊ฐ)์ผ๋ก ๋ฉ์๋ ํธ์ถ์ด ๋ฐ์ํ ๊ฒฝ์ฐ ๋ฉ์๋ ๋ด๋ถ์ ๋น์ฆ๋์ค ๋ก์ง์ ํ์ง์๊ณ ๋ฐ๋ก ์บ์์ ์ ์ฅ๋์ด์๋ ๊ฐ์ ๋ฐํํฉ๋๋ค. ์ด๋ฅผ ํ์ธํ๊ธฐ์ํด ๊ณ์ฐ์ค.... ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋๋ก ํ์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก ์ ์๋น์ค๋ฅผ ํธ์ถํ๋ ํ ์คํธ API ๋ฅผ ๋ง๋ค์ด ์ค๊ฒ์.
com.example.demo.controller ํจํค์ง๋ฅผ ์ ๊ท์ถ๊ฐํ๊ณ TestController๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
package com.example.demo.controller;
import com.example.demo.service.CacheService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
public class TestController {
private final CacheService cacheService;
@GetMapping("/cache")
public Integer getCachedValue(int code) {
log.info("Request accepted for code {}", code);
return cacheService.getCachedValue(code);
}
}
API ํธ์ถ์ด ๋ค์ด์ฌ๋๋ง๋ค Request accepted for code X ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋๋๋ก ํ๊ณ , ์บ์์๋น์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ํ์ต๋๋ค.
์ด์ DemoApplication์ ์คํํ๊ณ API๋ฅผ ํธ์ถํด๋ณผ๊ฒ์.
2022-09-14 12:32:59.783 INFO 47720 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.778 seconds (JVM running for 0.997)
2022-09-14 12:33:06.871 INFO 47720 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-09-14 12:33:06.871 INFO 47720 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-09-14 12:33:06.872 INFO 47720 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2022-09-14 12:33:06.882 INFO 47720 --- [nio-8080-exec-1] c.e.demo.controller.TestController : Request accepted for code 1
๊ณ์ฐ์ค....
2022-09-14 12:33:08.270 INFO 47720 --- [nio-8080-exec-2] c.e.demo.controller.TestController : Request accepted for code 1
2022-09-14 12:33:09.186 INFO 47720 --- [nio-8080-exec-3] c.e.demo.controller.TestController : Request accepted for code 1
2022-09-14 12:33:10.017 INFO 47720 --- [nio-8080-exec-4] c.e.demo.controller.TestController : Request accepted for code 1
2022-09-14 12:33:10.880 INFO 47720 --- [nio-8080-exec-5] c.e.demo.controller.TestController : Request accepted for code 1
2022-09-14 12:33:11.764 INFO 47720 --- [nio-8080-exec-6] c.e.demo.controller.TestController : Request accepted for code 1
2022-09-14 12:33:12.631 INFO 47720 --- [nio-8080-exec-7] c.e.demo.controller.TestController : Request accepted for code 1
๊ณ์ฐ์ค....
2022-09-14 12:33:13.538 INFO 47720 --- [nio-8080-exec-8] c.e.demo.controller.TestController : Request accepted for code 1
2022-09-14 12:33:14.408 INFO 47720 --- [nio-8080-exec-9] c.e.demo.controller.TestController : Request accepted for code 1
ํธ์ถ์ ๋ชจ๋ code๊ฐ 1์ ๋ฃ์ด์ ํ์๊ณ , ์ต์ด ํธ์ถ์ 12:33:06.882 ์ ํ๋ฉด์ ์๋น์ค ๋ด๋ถ๋ก์ง์ ์คํํ์ฌ ๊ฐ์ ๋ฐํํ์์ต๋๋ค. (๊ณ์ฐ์ค.... ์ถ๋ ฅ์ผ๋ก ํ์ธ)
์ดํ 12:33:11.764 ๊น์ง๋ ๊ณ์ฐ์ค.... ์ด ์ถ๋ ฅ๋์ง ์์ ๊ฒ์ผ๋ก ๋ณด์ ์บ์ฑ๋์ด์๋ ๊ฐ์ ๊ทธ๋๋ก ์๋ตํ๊ฑธ๋ก ๋ณผ ์ ์์ต๋๋ค.
5์ด๊ฐ ์ง๋ 12:33:12.631 ์ ๋ค์ด์จ ํธ์ถ๊ฑด์ ๊ณ์ฐ์ค.... ์ด ์ถ๋ ฅ๋๊ฑธ๋ก ๋ณด์ ์๋ต๊ฐ์ ๋ณ๋์ด ๋ฐ์ํ์ ๊ฒ์ผ๋ก ์ถ์ธก๋ฉ๋๋ค. (๋๋ค๊ฐ์ผ๋ก ์๋ตํ๋ ์๋น์ค๋ ์๋ต๊ฐ์ ๋ณ๋์ด ์์ ์๋ ์์ฃ )
์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ ์๋ต๊ฐ์ ๋ณ๋์ด ์๋๋๊ฐ ์๋๋ผ ์บ์๋์ด์๋ ๋ฐ์ดํฐ๊ฐ ๋ง๋ฃ๋๊ธฐ ์ ๊น์ง ๋ฉ์๋ ๋ด๋ถ๋ก์ง์ ์คํํ์ง ์๊ณ ์บ์์์ ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฆฌํดํด์ค๋ค๋ ์ ์ ๋๋ค.
์ด์์ผ๋ก 5๋ถ๋ง์ ์คํ๋ง์บ์์ ehcache๋ฅผ ์ด์ฉํ์ฌ ์บ์๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์์ต๋๋ค.
์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค~ ๐