개발관련 잡다/HTTP

http와 스프링 (4) : Cache Control 과 정적자원들의 관리

아라한사 2019. 11. 10. 09:17

쉬어가기.. 과거의 기록 리마인드 우려먹기(^0^)

 

시리즈 목차

https://adunhansa.tistory.com/261

 

http와 스프링(0) - 연재를 시작하며..

운 좋게, 좋은 스터디원분들과 좋은 책을 만나 HTTP 스터디를 시작하며 다음의 연재글을 시작해보려고 합니다. 목차 http와 스프링 뒤적뒤적 연재 시리즈~ --- 1편 - Encoding - Brotli 적용해보기 https://adunha..

adunhansa.tistory.com

0 . 개요

사실 정적자원관리에 있어서는 권남님의 정리가 너무 정리가 잘 되어져있어서, 딱히 정리를 할 필요성을 못 느낍니다만

(그리고 현재 시점에서 더 알고 싶지도 않은;; 귀차니즘이..;; )

(하지만 글쓴 김에 조금 더 알아보았다고 한다)

그냥 다시 한번 정적자원을 만들어보고 캐시가 어떻게 되나 한번 보고 가도록 하겠습니다.

 

먼저 다음의 링크들을 봐두시면 좋습니다.

 

http://kwon37xi.egloos.com/4735742

 

웹의 정적 리소스의 버전 명시를 통한 캐시 전략

나는 언제부터인가 웹의 정적 리소스(*.js,*.css, *.jpg,...) 들은 항상 1년간 캐시하도록 설정을하고 있다.이렇게 할 경우 일단 한 번 읽은 리소스는 다시 읽지 않기 때문에 대역폭을 아끼고 성능을 향상 시키는데 크게 일조를 한다.하지만 여기에는 심각한 문제가 있다.css가 수시로 변경되고 *.js 파일들도 개발하면서 계속 변경된다. 동일한 이

kwon37xi.egloos.com

https://kwonnam.pe.kr/wiki/web/%EC%8B%A0%EA%B7%9C%EC%84%9C%EB%B9%84%EC%8A%A4?s[0]=%EC%84%9C%EB%B9%99&fbclid=IwAR0296NK8E7s6oYqmFzaXhbZ_sVwxPL418ZiitVZThLwxI8SB1VRDLoHjiA#%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%99%80_%EC%A0%95%EC%A0%81_%EB%A6%AC%EC%86%8C%EC%8A%A4_%EC%84%9C%EB%B9%99_%EC%8B%9C%EC%8A%A4%ED%85%9C_image_static_resources

 

 

1.시나리오와 목표

 

정적파일을 만들어서 캐시가 되었을 때의 문제점 살펴보고,

캐시를 재갱신할 버젼전략을 세워봅니다.

 

 

HTTP를 조금 배운 아라한사 개발자는 자신있게 정적자원들같은 부분에 캐시를 적용해보려고합니다.

HTML 캐시 옵션을 보다가 Cache Control 이라는 부분이 있다는 것을 알게 되서 조금 파봅니다.

 

HTTP Cache Control 속성을 좀 더 알아보겠습니다. 


https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

 

Cache-Control

The Cache-Control general-header field is used to specify directives for caching mechanisms in both requests and responses. Caching directives are unidirectional, meaning that a given directive in a request is not implying that the same directive is to be

developer.mozilla.org

웁스 영어군요.. 우선 한글 블로그를 읽어서 개념에 좀 더 친숙해지기로 합니다. 

https://mr-zero.tistory.com/195

 

[자료] cache-control 옵션

출처 : http://www.joy24.net/101 브라우저 캐시의 필요성 브라우저 캐시는 클라이언트 시스템에 있는 디스크 또는 메모리를 이용하기 때문에 클라이언트 캐시(client cache)라고도 합니다. 한 번 접속했던 웹 사..

mr-zero.tistory.com

자, 그럼.. 스프링에 적용을 하기 위해 레퍼런스 사이트를 뒤적뒤적해봅니다.

 

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#web-properties

 

대략 spring.resources.cache 관련부분이 cache control 부분과 연관되어져있는 것을 확인할 수가 있습니다. 

 

우선 정적자원 하나를 넣어보고 어떻게 보여지나 보겠습니다.

다음과 같은 컨트롤러, HTML, CSS파일을 하나 만들어볼 것입니다.

 

껀트롤러

package com.arahansa.etc

import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping

@Controller
@RequestMapping("/test")
class ResourceTestController {

    @GetMapping("/resource")
    fun resourceTestPage(): String{
        return "etc/resource"
    }

}

HTML

<!DOCTYPE html>
<html lang="en"
    xmlns:th="http://www.thymeleaf.org"
>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>

<h1>아 나는 스타일링을 받을 듯한 기분이에요..</h1>

<span class="blue">오늘 머리를 블루클럽 가서 잘라볼까요1</span>

</body>
</html>

CSS

h1{color:red}
span.blue{
    color:blue;
}

 

application.yml 에는 캐시 속성은 아직 처리 안하고

자 이제..  로컬웹서버를 띄우고 페이지 상태코드를 한번 살펴봅니다. 

 

2. 잠시 Last Modified 의 등장

 

첫 로딩..  오엇..? Last Modified 가 응답으로 같이 오는군요..

 

아직 자세한 부분은 보진 않았지만 아마도 Resource 에서 정보를 같이 처리해주는 것같긴합니다만..

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/Resource.html#lastModified--

 

좀 더 상세히 보기 위하여 이번에는 curl 을 사용해보았습니다.

 

Last-Modified 정보가 날라옵니다.

Sat, 09 Nov 2019 22:36:48 GMT 에서 이 GMT 라는 것은 그리니치 평균시라는 것을 나타냅니다.

 

Last-Modified 란? 대략 다음의 링크가 있겠는데 자세한 내용은 좀 더 나중에 적기로 하고

지금은 좀 더 cache control 과 궁극적인 목표(?)에 대하여 집중하겠습니다.

링크만..몇개..

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified

https://tools.ietf.org/html/rfc7232#section-2.2

 

RFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests

[Docs] [txt|pdf] [draft-ietf-http...] [Tracker] [Diff1] [Diff2] [Errata] PROPOSED STANDARD Errata Exist Internet Engineering Task Force (IETF) R. Fielding, Ed. Request for Comments: 7232 Adobe Obsoletes: 2616 J. Reschke, Ed. Category: Standards Track green

tools.ietf.org

그리니피 평균시란?

https://ko.wikipedia.org/wiki/%EA%B7%B8%EB%A6%AC%EB%8B%88%EC%B9%98_%ED%8F%89%EA%B7%A0%EC%8B%9C

 

자 그럼 다시 돌아와서.. 정적자원에 Last-Modified 를 돌려준다는 것을 알았지만,

이 LastModified 하나만 믿기에는 부족한 점이 있겠습니다.

 

저는 AWS 서버에 잠시 재배포를 하였는데 재배포를 하고나니 같은 정적자원임에도 불구하고

Last-Modified 가 다시 계산되어져서 나옵니다.

 

원래 하려던 것으로 돌아가서 이제 cache control 을 줘보도록 하겠습니다.

(참고사항 : curl 해서 Server 정보에 Server 버젼정보가 나오는데 저 버젼명시는 가려져야합니다)

3. Cache Control 

저는 spring.resources.cache.cachecontrol.max-age 에 31536000 을 주었는데요. 이것은 1년치 캐시를 한다는 뜻입니다. 

전 습관성으로 이렇게 적었지만, 다른 분들은 바뀌는 파일들은 적당한 시간을 분배해주시길 바랍니다^^

 

https://www.flightpedia.org/convert/31536000-seconds-to-day.html

 

여기서 다시 궁금증 : 영원히 바뀌지 않을거면, 2년이고 10년이고 하면 되지 왜 1년인것인가요? 

RFC 2068 에서는 변경할 일이 없는 콘텐츠더라도 최대 1년의 캐시 수명을 설정하는 가이드라인이 있다고 합니다. 

https://tools.ietf.org/html/rfc2068#section-14.21

 

 

자 그러면 다시 한번 재배포를 해보도록 하겠습니다.

 

응답 헤더에서 Cache-Control max-age=31536000 이 나옴을 볼 수가 있습니다.

다시한번 새로고침을 해보면..?

 

 

다음과같이 200 이지만 cache control 로 인하여 디스크 캐시를 사용함을 볼 수가 있습니다.

메모리캐시와 디스크캐시의 차이점은 윗 한글블로그에 잘 설명되어져있습니다.

 

자 이제 서버를 재배포해도? 결과는 동일하게 disk cache 를 사용하게 됩니다.

 

---

 

자 여기까지 캐시도 적용했다 안심했는데 문제가 발생합니다. style.css 에서 blue 에 표현된 내용에서 이제 폰트의 굵기도 조금 굵게 해주고 싶습니다. 

자 변경변경.. 

 

하지만 웬걸? 주소에 접속해보니 전혀 CSS 가 먹히지 않습니다 

당연한 얘기겠지만, /css/style.css 라는 주소는 cache-control 때문에 디스크캐시를 사용하기 때문입니다.

개발 QA를 하다보면 흔히 만나는 상황#334 이기도 합니다.

 

개발자 : 아 나는 배포했고 내 컴퓨터에 잘 나오니 퇴근해볼까?

QA : ㅇㅇ개발자님, 이거 전혀 수정이 안되었는데요?

개발자 : 아? 고뤠요? 제 컴퓨터에서는 잘 되는데?! 아
혹시 크롬 캐시 리프레시해보셨나요? 단축키가..어쩌고저쩌고

QA : 아 네 인제서야 되네요! 잘나오네요. 개발자님~짱짱맨~ (어휴.. 매번 이걸 눌러야하니?)

개발자 : 네 앞으로 안될때는 캐시재갱신을 다시 한번 해주세요.. =3=3

대략 이런 상황이..

 

4. Version 전략과 스프링에서의 버젼전략

이런 상황을 타개하기 위한 방법은 

이미 링크로 첨부한 권남님의 위키에도 잘 나와있긴하지만 조금 더 알아보겠습니다. 

 

첫째로 정적 파일끝에 버젼을 명시하는 방법도 있기도하고, ex ) style.css?ver=2013

스프링에서는 Version Strategy 전략을 사용하기도 합니다. 

스프링의 옵션상에서 볼 수 있는 전략은 두가지가 있습니다. 

 

하나는 Fixed Version Strategy 이고, 다른 하나는 Content Version 전략입니다.

하나하나 사용만 해가면서 알아보겠습니다.

 

 

4.1 Fixed Version Strategy

버젼전략부터 살펴보겠습니다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/resource/FixedVersionStrategy.html

 

FixedVersionStrategy (Spring Framework 5.2.1.RELEASE API)

A VersionStrategy that relies on a fixed version applied as a request path prefix, e.g. reduced SHA, version name, release date, etc. This is useful for example when ContentVersionStrategy cannot be used such as when using JavaScript module loaders which a

docs.spring.io

우선 버젼전략의 하나를 명시하게 되면

spring.resources.chain.cache 는 true 로 활성화되게 되어지고.

 

다음과 같이 Fixed Version Strategy 를 application.yml 에서 키고

 

thymeleaf 뷰 html 에서는 

 

단순히 href 가 아니라 thymeleaf th:href 속성을 사용하여서 속성명을 지정하게 됩니다.

그렇다면 화면상에서는 어떻게 될까요?

캐시재갱신을 하지 않았음에도 블루클럽 글씨가 굵어졌습니다. 어떻게 이런 일이 일어난 것일까요?

소스를 살펴보시면 명시한 버젼이 정적파일 경로 앞에 위치하게 된 것을 보실 수가 있습니다. 

 

4.2 ContentVersion Strategy

다음에 보실 전략은 content version 전략입니다. 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/resource/ContentVersionStrategy.html

단순히 이정도로 enable 시킬 수가 있습니다.

 

 

화면은 버젼전략과 같지만, 소스는 다릅니다.

리소스의 내용을 MD5 해시를 해서 파일명 사이에 해시를 붙여줘서 파일전략을 하게 해주는 것입니다.

 

제가 글쓰다가 아침을 먹어야해서..급히 마무리합니다.

이상 Cache Control 과 스프링에서의 Cache Control 방법, 버젼전략을 살펴보았습니다..

감사합니다.

 

5. 추억팔이

 

잠시 옛날 생각이 나서 가져와보았습니다.

저때의 고민이었던 HTML 압축은 몇번 관련 플러그인을 찾아보긴하였지만,

귀차니즘의 문제로 아직도 해결하지 않고 있습니다^0^a

 

한때는 페북질을 열심히해서 이런저런 페친분들이 댓글을 남겨주셨지만, 

지금은 잊혀진 조금 쉰(?) 취미개발자(?!) 이라는..