이번시간에는 Spring boot에서 지원해주는 캐시와 spring boot의 에러 페이지를 알아보자.
Spring boot 에서는 다양한 캐시들을 지원한다. 기본적으로 아무 설정 하지 않았을 경우에는 ConcurrentMapCacheManager의 ConcurrentHashMap을 사용해서 캐시를 사용하고 JSR-107 (JCache) 명세에 따른 캐시도 지원한다. 그 구현체들은 EhCache, Hazelcast, Infinispan, apache ignite 등이 있다.(이 외에도 더 있던거 같았다.) 이외에도 JSR-107 표준 명세로는 따르지 않았지만 Redis, Caffeine, Guava등이 존재하고 있다. Caffeine 캐시 같은 경우에는 JCache도 지원한다.
일단 캐시를 사용할 수 있게 디펜더시를 받자.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
기본적으로 spring-boot-starter-cache를 디펜더시 받으면 된다. 그리고 나서 @EnableCaching를 선언하면 캐시 설정은 끝났다.
//...
@EnableCaching
public class SpringBootCleanBlogApplication {
//...
}
그리고 나서 바로 사용해도 된다. 아래와 같이 캐시를 사용할 메서드 위에 @Cacheable 어노테이션을 사용해서 키를 지정해주면 된다.
//...
@Cacheable("blog.category")
public Page<Category> findAll(Pageable pageable) {
//...
}
위와 같이만 해도 캐시 설정은 다 되었다. 하지만 좀 더 캐시를 구체적으로 다룰 필요가 있다. 캐시의 expired 시간이라던지 모니터링이라던지 기타 등등 좀더 나은 캐시를 사용해 보자.
일단 JSR-107 (JCache) 구현체인 EhCache를 설정 해보자.
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
일단 필요한 라이브러리인 cache-api 와 ehcache를 받으면 된다. 그리고 다음과 같이 설정을 하자.
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> cm.createCache("blog.category", initConfiguration(Duration.ONE_MINUTE));
}
private MutableConfiguration<Object, Object> initConfiguration(Duration duration) {
return new MutableConfiguration<>()
.setStatisticsEnabled(true)
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(duration));
}
blog.category라는 키에 1분동안 캐시를 한다고 지정해줬다. Duration 에는 ONE_DAY, ONE_HOUR, THIRTY_MINUTES, TEN_MINUTES 기타 등등 여러가지의 시간이 있기는 하지만 거기에 없는 시간이 있다면 만들면 된다.
public static final Duration TEN_SECONDS = new Duration(TimeUnit.SECONDS, 10);
설정 속성중에 statisticsEnabled 모니터링을 할 수 있는 속성이다. 해당 캐시의 통계를 확인 할 수 있다. jconsole로 확인 가능하다. 잘되나 확인하기 위해 아래와 같이 카테고리 리스트에 캐시를 설정했다. 다른 곳에 하고 싶다면 키 설정만 잘 해주면 된다.
@Transactional(readOnly = true)
@Cacheable("blog.category")
public Page<Category> findAll(Pageable pageable) {
log.info("blog.category cache");
return categoryRepository.findAll(pageable);
}
일분에 한번
blog.category cache
로그가 출력되면 성공적으로 캐시가 설정 되었다. 1분에 한번은 너무 기니 10초 정도만 설정 후 테스트 해보자.
JSR-107 캐시 중 에 다른 라이브러리를 사용하고 싶다면 설정 코드는 고치지 않고 해당하는 디펜디시만 바꾸면 된다.
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-spring4-embedded</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-jcache</artifactId>
</dependency>
infinispan 캐시를 사용하고 싶다면 위와 같이 설정 하면 된다.
아래는 hazelcast 디펜더시 방법이다.
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
다음은 apache.ignite 캐시 디펜더시 이다. Spring 문서에는 apache.ignite 없지만 JSR-107 표준을 잘 따랐다면 문제 없이 동작 할 듯 하다. 테스트를 해본 결과 되긴 하나 뭔가 조금 찜찜한 구석이 몇가지 있던거 같았다. WARN 로그도 뜨고 그랬던거 같았는데 자세히 보지는 않았다. 그냥 JSR-107 구현체로 만들어 졌다고 하길래 테스트를 해본거 뿐이다. Spring 문서에도 없어 Spring을 사용할 때는 그닥 사용할 일이 없을 거 같다.
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version>1.7.0</version>
</dependency>
이번에는 간단하게 Caffeine 사용해 보자.
일단 카페인 캐시를 디펜더시 받자.
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
디펜더시를 받은 후에 application.properties에 다음과 같이 작성하자.
spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=60s
maximumSize은 캐시의 최대 사이즈고 expireAfterWrite은 expired시간이다. 분단위로 하고 싶다면 m으로 하면 된다. 시간은 h로 하면되나?
필자도 디테일하게 설정방법을 아직 모른다. 나중에 사용할 기회가 된다면 한번 알아봐야겠다. 문서도 잘 되어있는거 같으니 한번 살펴보는 것도 나쁘지 않다.
Guava 설정 방법도 거의 동일하다. 구아바 라이브러리를 디펜더시 받은후에 프로퍼티에 다음과 같이 해주면된다.
spring.cache.guava.spec=maximumSize=500,expireAfterAccess=600s
아까 위에서 말했듯이 카페인 캐시는 JCache 도 지원한다. 디펜더시만 살펴보자.
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>jcache</artifactId>
<version>2.3.3</version>
</dependency>
위와 같이 jcache를 디펜더시 받으면 된다. spring boot에서는 버전관리를 해주지 않는다. 그러므로 버전정보를 입력해야된다.
캐시를 하고 싶은곳에 @Cacheable을 선언한 후에 Duration time만 원하는 시간으로 해준다면 쉽게 캐시를 설정 할 수 있다.
이렇게 카테고리쪽에 캐시를 달아봤다. 현재 카테고리쪽에는 어울리지 않을 수도 있지만 개발을 하다가 캐싱하고 싶은 부분이 있다면 위와 같이 하면 될 것이다.
이번에는 블로그의 에러 페이지를 만들어보자. Spring boot 1.4 부터는 너무나도 간단하게 에러페이지를 만들 수 있다. 뷰 템플릿을 사용한다면 templates 폴더 밑에 error라는 폴더를 만들고 뷰 템플릿을 사용하지 않는다면 static아래 error폴더를 만들면 된다. error폴더 밑에 해당하는 에러코드로 파일명을 만들면 된다. 예를들어 404 에러이면 404.html을 만들면 되고 500일 경우에는 500.html을 만들면 된다.
또한 400대 에러를 통 틀어서 4xx.html로 파일명을 만들어도 된다.
필자는 4xx.html을 만들었다. 모든 에러 코드를 넣기엔 양이 많으니 한개로 퉁 쳤다.
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layouts/main">
<head>
<meta charset="UTF-8"/>
<title th:text="Ooops, + ${error}">Ooops, page not found</title>
<style type="text/css">
.wrapper {
width: 100%;
text-align: center;
}
.big {
font-size: 100%
}
</style>
</head>
<body>
<div class="wrapper" layout:fragment="content">
<div class="big">
<h1>¯\\_(ツ)_/¯</h1>
<br />
<h2 th:text="${error}"></h2>
</div>
</div>
</body>
</html>
한번 404에러를 내보자. 없는 url을 만들어서 입력해보자.
이번에는 지원하지 않는 요청 메서드에러이다.
에러페이지도 나쁘지 않게 나왔다. 가만보니까 상단 배경이미지 위에 글씨가 없다. 이것 또한 에러가 나오게 만들어주자.
main.htm에 navSection을 넣어 준 곳에 가서 아래와 같이 살짝 변경해주자.
<h1 th:if="${navSection}" style="color:#ffffff" th:text="${navSection}">Clean Blog</h1>
<h1 th:unless ="${navSection}" style="color:#ffffff" th:text="${error}">Error</h1>
navSection 있다면 navSection를 출력해주고 그렇지 않다면 error를 출력해주면 된다. 다시 확인 해보자.
나쁘지 않게 나왔다. 한번 5xx 대 에러 페이지도 만들어 보자.
기존의 페이지와 똑같다. 텍스트 이모티콘만 변경했다. 5xx.html을 만들고 아래와 같이 넣자.
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layouts/main">
<head>
<meta charset="UTF-8"/>
<title th:text="Sorry, + ${error}">Sorry, </title>
<style type="text/css">
.wrapper {
width: 100%;
text-align: center;
}
.big {
font-size: 100%
}
</style>
</head>
<body>
<div class="wrapper" layout:fragment="content">
<div class="big">
<h1>(๑′°︿°๑)</h1>
<br />
<h2 th:text="Sorry, + ${error}"></h2>
</div>
</div>
</body>
</html>
위와 같이 500대 에러가 났을 경우에는 5xx.html을 보여 준다.
이렇게 캐시와 에러페이지를 만들어 봤다. 10편 안에 마무리 될듯 하다.
현재까지의 소스는
여기에 있다.
[spring-boot] 블로그를 만들자 (1)
[spring-boot] 블로그를 만들자. (2) JPA
[spring-boot] 블로그를 만들자. (3) Category 와 Comment
[spring-boot] 블로그를 만들자. (4) thymeleaf
[spring-boot] 블로그를 만들자. (5) markdown, catetory
[spring-boot] 블로그를 만들자. (6) 댓글과 Navigation
[spring-boot] 블로그를 만들자. (7) 캐시와 에러페이지
[spring-boot] 블로그를 만들자. (8) GitHub login
[spring-boot] 블로그를 만들자. (9) CI 와 배포