티스토리 뷰
Overviews
예제를 만드는 것은 간단합니다. 먼저 Spring Starter Project로 기본 프로젝트를 생성합니다. cache 관련 dependencies를 추가해주고 설정 Configuration 추가해 줍니다. ehcache.xml 파일을 클래스패스 아래 생성합니다.
Main class에 캐시를 활성 애노테이션을 추가해주고 샘플을 만들어서 테스트 합니다.
개발 환경
- Spring boot 2.x
- jdk 1.8
- Gradle 4.x
Dependencies 추가
1 2 3 4 5 6 7 | dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'net.sf.ehcache:ehcache-core:2.6.11' } | cs |
Configuration 추가
1 2 3 4 5 6 7 8 9 10 11 12 | @Configuration public class CustomCacheConfiguration extends CachingConfigurerSupport { @Bean public EhCacheManagerFactoryBean getEhCacheFactory(){ EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean(); factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); factoryBean.setShared(true); return factoryBean; } } | cs |
ehcache.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true"> <diskStore path="java.io.tmpdir" /> <cache name="userCache" maxEntriesLocalHeap="2000" maxEntriesLocalDisk="2000" eternal="false" diskSpoolBufferSizeMB="20" timeToIdleSeconds="1200" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LFU" transactionalMode="off"> <persistence strategy="localTempSwap" /> </cache> </ehcache> | cs |
캐시 활성화
1 2 3 4 5 6 7 8 9 | @SpringBootApplication @EnableCaching public class CacheExampleApplication { public static void main(String[] args) { SpringApplication.run(CacheExampleApplication.class, args); } } | cs |
예제
User.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | public class User implements Serializable{ private static final long serialVersionUID = 1L; private String userId; private String name; private String authType; private Date timestamp; private boolean isUpdate = false; public User() { super(); } public User(String userId, String name, String authType) { super(); this.userId = userId; this.name = name; this.authType = authType; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthType() { return authType; } public void setAuthType(String authType) { this.authType = authType; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public boolean getIsUpdate() { return isUpdate; } public void setIsUpdate(boolean isUpdate) { this.isUpdate = isUpdate; } @Override public String toString() { return "User [userId=" + userId + ", name=" + name + ", authType=" + authType + ", timestamp=" + timestamp + ", isUpdate=" + isUpdate + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((authType == null) ? 0 : authType.hashCode()); result = prime * result + (isUpdate ? 1231 : 1237); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((timestamp == null) ? 0 : timestamp.hashCode()); result = prime * result + ((userId == null) ? 0 : userId.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (authType == null) { if (other.authType != null) return false; } else if (!authType.equals(other.authType)) return false; if (isUpdate != other.isUpdate) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (timestamp == null) { if (other.timestamp != null) return false; } else if (!timestamp.equals(other.timestamp)) return false; if (userId == null) { if (other.userId != null) return false; } else if (!userId.equals(other.userId)) return false; return true; } } | cs |
캐시하고자 하는 객체는 기본적으로 Serializable 인터페이스를 implements해야 합니다.
Cache에서 Key를 지정하지 않는 경우에는 Key를 생성하기 위해서는 hashCode()를 오버라이딩해야 합니다.
UserService 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @Service public class UserService { @Cacheable(cacheNames="userCache") public User __getUser(final User user) { return cloneUser(user); } @Cacheable(cacheNames="userCache", key="#user.userId", condition = "#user.isUpdate != true") public User getUser(User user) { return cloneUser(user); } @CachePut(cacheNames="userCache", key="#user.userId") public User addUser(User user) { return cloneUser(user); } @CachePut(cacheNames="userCache", key="#user.userId") public User modifyUser(User user) { return cloneUser(user); } @CacheEvict(cacheNames="userCache", key="#user.userId") public void removeUser(User user) { cloneUser(user); } private User cloneUser(User user) { User rUser = SerializationUtils.clone(user); rUser.setTimestamp(new Date()); return rUser; } } | cs |
각 메소드에서는 결과값으로 날짜를 생성해서 캐시 여부를 체크합니다.
@CachePut : 캐시 생성
@Cacheable : 조회해서 없으면 캐시 생성하고 결과 반환, 캐시된 값이 있는 경우 해당 값 반환. key 정의 및 조건 정의 가능.
@CacheEvict : 캐시 삭제, allEntries를 이용해서 전체 삭제 가능.
여러개의 캐시 삭제 예제)
1 2 3 4 5 6 7 8 | @Caching(evict = { @CacheEvict(cacheNames="userCache", key="#param.userId"), @CacheEvict(cacheNames="authCache", key="#param.roleId") }) public int removeUserAuth(UserAuth param) { ... ... } | cs |
컨트롤러 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @RestController public class UserTestController { @Autowired private UserService userService; @GetMapping("/users/{userId}") public User getUser(User pUser) { return userService.getUser(pUser); } @PostMapping("/users") public User addUser(User pUser) { userService.addUser(pUser); return pUser; } @PutMapping("/users/{userId}") public User modifyUser(User pUser) { userService.modifyUser(pUser); return pUser; } @DeleteMapping("/users/{userId}") public User removeUser(User pUser) { userService.removeUser(pUser); return pUser; } } | cs |
Test
실행하고 브라우져에서 확인해 봅니다.
http://localhost:8080/users/1234
1 | {"userId":"1234","name":null,"authType":null,"timestamp":"2019-02-26T01:06:05.408+0000","isUpdate":false} | cs |
캐시가 적용되었다면 timestamp의 값이 변하지 않는 걸 확인 할 수 있습니다.
Test Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | @RunWith(SpringRunner.class) @SpringBootTest public class UserTestControllerTest { private User pUser; @Before public void setUp() { this.pUser = new User("test", "테스터", "web"); } @SuppressWarnings("unused") private static <T extends Serializable> T clone(final T object) { return SerializationUtils.clone(object); } @Autowired protected WebApplicationContext context; protected MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); } private User getUser(MockHttpServletRequestBuilder req) throws Exception { // 값을 가져와서 처리하기 MvcResult result = this.mockMvc.perform(req) .andExpect(status().isOk()) // .andExpect(jsonPath("$.userId").value("test")) // .andDo(print()) .andReturn(); // expected : {"userId":"test","userPwd":null,"name":null,"authType":null,"timestamp":"2019-02-22T07:24:33.784+0000","isUpdate":false} String jsonInString = result.getResponse().getContentAsString(); // Json String을 Map에 담기 final ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(jsonInString, User.class); } @Test public void getUserTest() throws Exception { User param = clone(pUser); MockHttpServletRequestBuilder req = get("/users/"+param.getUserId()); User rUser1 = getUser(req); Date ts1 = rUser1.getTimestamp(); User rUser2 = getUser(req); Date ts2 = rUser2.getTimestamp(); assertTrue(ts1.compareTo(ts2) == 0); } @Test public void getUserUpdateTest() throws Exception { User param = clone(pUser); MockHttpServletRequestBuilder req = get("/users/"+param.getUserId()); User rUser1 = getUser(req); Date ts1 = rUser1.getTimestamp(); req.param("isUpdate", "true"); User rUser2 = getUser(req); Date ts2 = rUser2.getTimestamp(); // 두개의 결과가 달라야만 한다. assertTrue(ts1.compareTo(ts2) != 0); } @Test public void addUserTest() throws Exception { User param = clone(pUser); param.setUserId("1234"); MockHttpServletRequestBuilder postReq = post("/users"); postReq.param("userId", param.getUserId()); postReq.param("name", param.getName()); postReq.param("authType", param.getAuthType()); User rUser1 = getUser(postReq); Date ts1 = rUser1.getTimestamp(); MockHttpServletRequestBuilder req = get("/users/"+param.getUserId()); User rUser2 = getUser(req); Date ts2 = rUser2.getTimestamp(); // 두개의 결과가 달라야만 한다. assertTrue(ts1.compareTo(ts2) == 0); } } | cs |
Test service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | @RunWith(SpringRunner.class) @SpringBootTest public class UserServiceTest { @Autowired private UserService userService; private User pUser; @Before public void setUp() { this.pUser = new User("test", "테스터", "web"); } @SuppressWarnings("unused") private static <T extends Serializable> T clone(final T object) { return SerializationUtils.clone(object); } @Test public void getUser() { User param = clone(pUser); User rUser1 = userService.getUser(param); Date ts1 = rUser1.getTimestamp(); User rUser2 = userService.getUser(param); Date ts2 = rUser2.getTimestamp(); assertTrue(ts1.compareTo(ts2) == 0); } @Test public void getUserUpdate() { User param = clone(pUser); User rUser1 = userService.getUser(param); Date ts1 = rUser1.getTimestamp(); // update 조건을 주면 캐시를 타지 않는다. param.setIsUpdate(true); User rUser2 = userService.getUser(param); Date ts2 = rUser2.getTimestamp(); // 두개의 결과가 달라야만 한다. assertTrue(ts1.compareTo(ts2) != 0); } @Test public void addUser() { User param = clone(pUser); param.setUserId("1234"); // 사용자 추가 User rUser1 = userService.addUser(param); Date ts1 = rUser1.getTimestamp(); // 사용자 조회 User rUser2 = userService.getUser(param); Date ts2 = rUser2.getTimestamp(); assertTrue(ts1.compareTo(ts2) == 0); } @Test public void modifyUser() { User param = clone(pUser); // 사용자 조회 User rUser1 = userService.getUser(param); Date ts1 = rUser1.getTimestamp(); // 사용자 수정 param.setAuthType("facebook"); User rUser2 = userService.modifyUser(param); Date ts2 = rUser2.getTimestamp(); assertTrue(ts1.compareTo(ts2) != 0); } @Test public void deleteUser() { User param = clone(pUser); // 사용자 조회 User rUser1 = userService.getUser(param); Date ts1 = rUser1.getTimestamp(); // 사용자 삭제 userService.removeUser(param); // 사용자 조회 User rUser2 = userService.getUser(param); Date ts2 = rUser2.getTimestamp(); assertTrue(ts1.compareTo(ts2) != 0); } } | cs |
Github 링크
'Spring Frameworks' 카테고리의 다른 글
Spring boot - Assert 사용 예제 (0) | 2019.03.05 |
---|---|
Spring boot - Object Mapper (0) | 2019.03.04 |
Spring boot - 빌드 profile 설정 (0) | 2019.02.20 |
Spring Boot - 복수 데이터소스 설정하기 (0) | 2019.02.14 |
Spring Boot에서 에러 페이지 처리하기 (3) | 2019.02.14 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- Spring Boot
- springboot
- 엑셀
- 타임리프
- UI
- cache
- REST
- 샘플
- 스프링
- Javascript
- SHEETJS
- example
- 스프링부트
- oracle
- thymeleaf
- ag grid
- mybatis
- listToMap
- java
- AG-GRID
- mapToList
- 메시지
- restful서비스
- sample
- RESTful
- lombok
- spring
- 예제
- 그리드
- 설정
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함