Spring 정적 리소스 관리와 Nginx Proxy Cache 적용
Spring 정적 리소스 관리
Spring에서 Web 프로젝트를 진행하다 보면 정적 리소스(css, js, image 등)가 브라우저에 cache 되어 새로 반영한 리소스가 보이지 않는 문제를 발견합니다. 그래서 정적 리소스의 이름을 변경하거나 버전을 뒤에 붙여 주어 브라우저가 새로운 리소스로 인식하게 만들어서 client(브라우저)에서 다시 내려받을 수 있도록 처리합니다.
<img src="/img/picture.png?20220217">
그러나 매번 변경된 리소스를 확인하여 버전을 수정하는 것은 번거로울 뿐만 아니라 시간을 낭비하는 일이 됩니다. 그래서 리소스에 서블릿에서 리소스 버전을 받아와서 동적으로 변경되도록 처리합니다.
<img src="/img/picture.png?${deployVersion}">
리소스의 변경이 있을 때 서블릿의 버전 정보를 수정 해주면 됩니다.
그러나 이 방법에도 단점이 있습니다.
- 매번 버전 정보를 수동으로 수정 해줘야 합니다.
- 뷰(html, jsp, thymeleaf 등)에 리소스 태그를 달 때 마다 버전코드를 붙여야 하는 번거로움이 존재하고 실수로 누락할 수도 있습니다.
- 버전 정보를 변경하면 변경한 리소스 뿐만 아니라 전체 리소스를 cache 해제하기 때문에 성능상 좋지 않습니다.
위에서 나열한 단점을 보안하기 위해 Spring에서 제공하는 VersionResourceResolver를 이용할 수 있습니다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
CacheControl cacheControl = CacheControl.maxAge(365, TimeUnit.DAYS);
registry.addResourceHandler("*/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(cacheControl)
.resourceChain(true)
.addResolver(new EncodedResourceResolver())
.addResolver(new VersionResourceResolver()
.addContentVersionStrategy("/**"))
.addTransformer(new CssLinkResourceTransformer());
}
}
적용이 완료되면 정적 리소스에 MD5 해시 값을 적용하여 client에게 전달합니다.
특정 파일이 수정되면 해시 값이 변경되고 새로운 이름이 되기 때문에 client(브라우저)에서 다시 내려 받고, 수정되지 않은 파일은 기존 해시 값을 유지 하기 때문에 브라우저 cache를 이용할 수 있습니다.
Nginx Proxy Cache
이제 Web Server에서 정적 리소스를 처리 설정을 해야 합니다.
만약 Web Server의 정적 리소스 디렉토리를 참조하게 하면 리소스 이름에 해시 값이 적용되어 있기 때문에 404 not found 에러가 발생합니다.
그래서 nginx에서 제공하는 proxy cache를 이용합니다.
client(브라우저)에서 요청이 오면 nginx(proxy server)가 cache storage에 cache된 리소스가 있으면 바로 응답하고 아니라면 proxy_pass로 설정된 upstream server(WAS)가 처리하도록 하는 방법입니다.
Nginx.conf cache 설정
http {
...
upstream was-server{
least_conn;
server 100.1.1.3:9090;
server 100.1.1.5:9090;
}
# cache storage
proxy_cache_path C:/nginx/cache/ levels=1:2 keys_zone=cache_zone:40m inactive=7d max_size=500m;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.mysite.com
access_log logs/access.log;
error_log logs/error.log;
location ~ ^/main {
proxy_cache_valid 200 60m;
proxy_cache cache_zone;
proxy_cache_key "$scheme$request_method$host$uri$is_args$args";
proxy_cache_methods GET;
expires 1M;
access_log off;
add_header Cache-Control "public";
add_header X-Proxy-Cache $upstream_cache_status;
proxy_pass http://was-server;
}
location ~* \.(?:css|js|gif|png|jpg|jpeg|ico|woff|woff2|mp4|mp3)$ {
proxy_cache_valid 200 60m;
proxy_cache cache_zone;
proxy_cache_key "$scheme$request_method$host$uri$is_args$args";
expires 1M;
access_log off;
add_header Cache-Control "public";
add_header X-Proxy-Cache $upstream_cache_status;
proxy_pass http://was-server;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
charset utf-8;
proxy_pass http://was-server;
}
}
}
적용 후 Chrome의 개발자 모드에서 캐시 사용 중지를 선택한 후 두 번째 호출 부터는 리소스의 cache가 HIT되는 것을 확인할 수 있습니다. (리소스가 브라우저에 cache된 상태라면 설정에서 데이터 삭제 후 시도)
Nginx Proxy Cache 적용 결과
- Nginx가 정적 리소스를 대신 처리 해주기 때문에 WAS 서버에 부하를 감소 시켰습니다.
- 정적 페이지의 경우 응답속도가 평균 3~4배 정도 개선되었습니다.
- css, js, image의 경우 응답속도가 약 30% 정도 개선 되었습니다.
추후 개선할 사항
- 현재 Nginx가 Window + Hard Disk에서 정적 페이지를 캐시하고 있는데 이를 Linux 서버 + Memcached로 정적 페이지를 캐시 해보고 성능 비교 해보기
참고
댓글남기기