Skip to content

Latest commit

 

History

History
193 lines (154 loc) · 18.1 KB

browserCache.md

File metadata and controls

193 lines (154 loc) · 18.1 KB

브라우저 캐싱

  • 캐싱이란 어떤 작업을 할 때 한 번 수행한 작업은 저장을 하는 것을 뜻한다.
  • 캐싱은 저장을 통해 다음에 작업을 수행할 때 작업에 드는 리소스를 줄이기 위한 목적으로 사용된다.
  • 다양한 작업에 캐싱이란 개념이 사용되지만 여기서는 브라우저가 서버로 부터 가져오는 파일을 캐싱하여, 다음에 서버에 접속할 때 동일한 이름의 파일은 서버에서 다시 다운로드 받지 않고, 브라우저에 저장되어 있는 파일을 사용하는 캐싱을 다룬다.

브라우저에서 캐싱을 사용하는 이유

  • 브라우저는 서버의 파일을 화면에 보여주기 위해 서버에서 파일을 다운 받고, 다운 받은 파일에 담긴 코드를 해석하여 브라우저에 띄운다. 서버에서 브라우저로 파일을 다운로드 하는 것은 시간이 걸리는 작업이다.
  • 웹 화면은 가능한 빠르게 로딩이 되어 유저의 브라우저 탐색 경험을 향상 시키는 것은 중요한 일이다. 이미 다운로드 받은 파일은 다시 다운로드 하지 않고 바로 보여주는 방식을 사용하면 유저의 브라우저 사용 경험을 향상 시킬 수 있기 때문에 기본적으로 브라우저는 캐싱을 적극 활용하는 방식을 사용한다.
  • 또한 네트워크는 불안한 경우가 많다. 열차를 타고 가다가 기지국의 통신 범위를 잠시 벗어날 수도 있고, 서버에 장애가 날 수도 있다. 이럴 때 브라우저가 파일을 캐싱해 놓고 있다면 일부 파일의 다운로드에 장애가 있을 때 캐싱된 데이터를 사용하여 어느 정도는 화면이 움직이는 동작을 할 수 있다.
  • 하지만 항상 브라우저가 동일한 파일을 보내는 건 아니다. 같은 url을 가진 파일이라도 내부의 데이터가 바뀐 파일을 보낼 수도 있다. 따라서 이런 데이터의 변경에 의존하지 않는 css, js, img, font 등의 파일에 대해서는 브라우저가 캐싱을 적극적으로 하지만 계속적으로 데이터가 변하는 html 이나 json 등의 결과물에 대해서는 캐싱을 하지 않는 경향이 있다.

캐싱을 사용하지 말아야 하는 경우

  • 다운로드 할 정적 파일이 변경되는 경우 브라우저의 캐시 정책으로 인해서 변경된 파일을 서버에서 다운받지 않는 경우가 있다.
  • 새로 배포를 했는데도 웹 사이트의 화면이나 기능에 변경이 없는 경우가 생길 수 있는데, 브라우저가 서버에서 새롭게 파일을 다운로드 받지 않고, 저장되어 있는 파일을 사용했기 때문이다.
  • 브라우저의 캐시를 무효화하는 방법으로는 크롬 브라우저의 경우, 개발자도구(inspect, development tools) -> 네트워크(network) -> Disable Cache의 체크 박스를 체크하고 개발자 도구를 켠 상태로 페이지 리로드를 하는 것이다.
  • 하지만, 일반 유저는 크롬 브라우저의 이러한 기능을 잘 활용할 수 없기 때문에, 사용자 측에서 특별한 조작을 하지 않아도 사이트의 변경이 일어난 이후에는 브라우저의 캐싱된 데이터를 이용한 웹 화면이 아니라 갱신된 사이트가 보이도록 해야 한다.

브라우저의 캐싱 무효화 방법

  • 브라우저는 URL이 동일한 대상에 대해 캐싱을 한다. 따라서 사이트 배포 때마다 동일한 파일에 대해서는 URL을 조금씩 바꾸어주면 URL이 같지 않으므로 캐싱을 하지 않고 새롭게 다운로드를 한다. URL의 변경을 통해서 사이트 유저는 직접 브라우저에서 캐시를 무효화하기 위한 조작을 하지 않아도 갱신된 사이트를 볼 수 있다.
  • URL을 바꾸기 위해서는 서버의 공유폴더에서 파일명이나 파일의 경로를 바꿔야 할까? 이러면 매번 사이트를 갱신할 때마다 코드가 변경된 파일의 저장 위치를 바꿔야 하는 불편함이 생긴다. 파일의 저장 위치를 변경하지 않고 url을 바꾸는 방법이 있는데 쿼리스트링 또는 쿼리파라메터라고 불리는 값을 붙여주는 것이다.
  • 동일한 파일명이나 파일경로를 가진 URL이라도 쿼리스트링이 서로 다르면 브라우저는 서로 다른 url로 인식한다.
https://github.com/search?q=webpack&type=repositories
  • 위의 url에서 ? 뒤에 나오는 코드가 쿼리스트링에 해당한다.
  • 쿼리란 질의어란 의미이다. 클라이언트(브라우저에 로딩된 웹 사이트)는 쿼리스트링을 통해서 서버에 다양한 옵션 값을 전달할 수 있고, 서버는 쿼리스트링이 요구하는 대로 결과 값을 보여준다.
  • 위의 예에서는 검색어를 지정하는 qwebpack이란 값을 넣었고, 어떤 대상들을 검색할지를 지정하는 typerepositories을 넣었다. 깃허브에서 리포지토리 중에서 웹팩과 관련되어 있는 대상을 검색하라는 쿼리를 가지고 있는 쿼리 스트링이다.
  • q에 대응하는 값으로 webpack이, 키 type에 대응하는 값으로 repositories가 들어간 형태이다.
  • q라는 변수에 webpack라는 값이 들어갔다고 볼 수도 있어서 쿼리 파라메터라고 부르기도 한다.

웹팩을 사용하여 캐시 무효화하기

  • 웹팩에서는 빌드되는 파일명이나 파일 경로를 매번 변경하는 방법 뿐만 아니라, url 뒤에 쿼리스트링을 붙이는 방법 모두를 사용할 수 있다.

entry의 파일 이름 변경하기

  • 웹팩은 자바스크립트를 번들링하는 도구이다. 물론 각종 로더를 사용해서 CSS, SASS, html 파일 등을 자바스크립트로 불러올 수 있다.
  • 캐시 문제를 해결하기 위해서 자바스크립트 파일의 이름을 변경하면, 변경된 이름의 파일을 html에서 불러와야 한다. 웹팩은 해시라는 기능을 제공하는데 해시를 사용하면 파일이름을 암호화 된 것 처럼 만들 수 있다. 빌드 할 때마다 매번 새로운 해시값이 만들어지기 때문에 브라우저 캐싱을 무효화 할 수 있는 기능을 제공하며, 해시라는 임의의 문자열을 사용하기 때문에 파일명이 무엇을 가리키는지 숨기는 역할을 하여 웹으로 제공되는 코드의 의미 파악을 어렵게하여 소스코드가 분석되기 어렵게 하여 세큐리티적으로 좀 더 좋은 결과물을 만들 수 있다.
  • html-webpack-plugin 플러그인으로 html 파일을 만든다면 webpack.config.js의 플러그인에서 new HtmlWebpackPlugin에 옵션으로 지정한 chunks 자바스크립트 청크명을 지정하는 것으로 자바스크립트를 html 파일에 주입한다. 따라서 웹팩이 자바스크립트 파일명을 해시화하더라도 html 파일에서는 해시화 된 이름을 가진 경로를 스크립트 태그의 경로에 할당한다.
// other setting code...
const config = {
    entry: {
        index: {
            import: "./src_study/js/index.js",
            filename: "js/[contenthash].js"
        },
        sub: {
            import: "./src_study/js/sub.js",
            filename: "js/[contenthash].js"
        },
    }
    // other configulations...
}

// other setting code...
  • 빌드된 파일명의 규칙에 대한 설명으로는 웹팩 공식문서를 참고하도록 하자.

템플릿 스트링

  • 템플릿 스트링은 생성되는 파일의 경로의 이름을 부여하기 위해 entryoutput에서 사용되는 명명 규칙이다.
  • [fullhash] : 컴파일 할 때마다 생성되는 해쉬로 동일한 명령 동일한 시점에 빌드된 대상에 부여된 fullhash는 모두 동일한 해시 값을 갖는다. 캐싱을 방지하기 위해 쿼리스트링에 부여하는 해시명으로 사용하기에 적합하다.
  • [name] : entry 키값의 청크명을 부여한다. 위의 예에서 index란 이름과 sub라는 이름이 [name] 부분에 할당된다.
  • [chunkhash] : 각각의 청크에 부여되는 해쉬값으로 청크마다 다른 해쉬 값이 만들어진다. 빌드 할 때마다 모든(변경된 파일 뿐만 아닌) 청크에 새로운 해시값이 부여된다. 생성되는 자바스크립트마다 서로 다른 파일명을 부여할 때 사용한다.
  • [contenthash] : 변경한 청크에 대해서만 새로운 해시값이 생성된다. 변경하지 않은 청크에는 기존의 해시값을 부여할 수 있기 때문에, 변경된 부분만 캐시를 무효화 할 수 있는 옵션으로 사용된다. 보통 프로덕션 환경에서는 chunkhash 보다 contenthash 옵션을 많이 사용한다.

HtmlWebpackPlugin에서 css, js 캐시 무효화 하기

  • HtmlWebpackPlugin을 사용할 때 옵션 중에 하나는 hash 옵션이 있다. 이 옵션은 설정을 하지 않으면 false로 적용하지 않는 것이 디폴트이지만, true로 활성화 할 수 있다.
  • hash: true를 옵션을 설정하면 브라우저에서 파일을 불러 올 때 entry로 빌드되는 파일을 불러올 때 ?2a52519a8884d9420bc3와 같은 쿼리 스트링 형식의 해시가 추가 된다. 이 해시는 컴파일 할 때마다 다른 값으로 생성되는 템플릿 스트링의 fullhash의 값을 사용한다. 번들링된 html 파일에서 js, css를 불러오는 PATH 뒤에 붙는 쿼리 스트링과 [fullhash]로 빌드된 자바스크립트 파일명에 남겨진 해시를 비교해 보면 서로 같다는 것으로 확인할 수 있다.
  • HtmlWebpackPlugin을 사용한다면 쿼리스트링으로 해시값이 entry로 만들어지는 파일의 url에 붙기 때문에 entry에 캐시 무효화를 위해 해시값을 만드는 fullhash, chunkhash, contenthash 등을 사용할 필요는 없다.
// other setting code...
const config = {
    // other configulations...
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src_study/pages/index.html',
            chunks: ['index'],
            hash: true,
        }),
        new HtmlWebpackPlugin({
            filename: 'subpage/index.html',
            template: 'src_study/pages/subpage.html',
            chunks: ['sub'],
            hash: true,
        }),
        // other plugin settings...
    ],
    // other configulations...
}

// other setting code...
  • 위 설정의 단점으로는 enrty를 기반으로 만들어지는 js파일 및 css파일에만 해시 값을 적용한다는 점이다. template 옵션으로 지정되는 파일의 href 또는 src 등의 속성으로 불러오는 주소에는 해시가 쿼리 스트링으로 추가되지 않는다.

에셋 캐시 무효화하기

  • 템플릿 스트링이나 HtmlWebpackPlugin에서 hash: true 옵션을 설정하는 방법은 entry로 지정되어 만들어지는 js와 css 파일에 적용되는 url 변경 방법이다.
  • src/assets 경로의 파일은 번들링 대상 폴더에 복사가 되도록 설정되어 있으면 entry에 지정되는 대상은 아니므로 템플릿 스트링이나 HtmlWebpackPlugin에서 hash: true 옵션으로 설정할 수 없다는 문제점이 있다.
  • 여기서는 설명을 위한 예제로 src 폴더 대신 src_study 폴더로 세팅이 되어 있는 상태이다.

webpack.config.js

// other setting code...
const config = {
  // other configulations...
  plugins: [
    // other plugin settings...
    new CopyPlugin({
      patterns: [{ from: "src_study/assets", to: "assets" }],
    }),
    // other plugin settings...
  ],
  // other configulations...
};
// other setting code...
  • CopyPlugin은 말 그대로 폴더 또는 파일을 빌드 폴더(dist 폴더) 내의 지정한 폴더로 복사를 하는 것이다.
  • 위의 설정은 src_study/assets 폴더 내의 모든 파일 및 폴더를 dist/assets 폴더 내로 복사를 하는 설정이다.
  • 앞서 브라우저의 캐시를 무효화하는 방법으로 url을 변경하는 방법이 있고, url을 변경하는 방법은 빌드되는 파일명이나 경로를 변경하는 방법 또는 쿼리스트링을 바꿔주는 방법이 있다고 하였다.
  • 웹팩에서 assets 폴더의 에셋 파일은 이미지 또는 폰트 파일으로 개발자가 직접 코드를 작성하지 않는 파일이 위치하는 곳이다.
  • 에셋으로 분류되는 파일은 대개 용량이 크며, 웹팩의 설정으로 이러한 파일들이 빌드가 되면 그 때 그 때 빌드의 결과 파일이 달라질 수 있게 되고, 용량이 큰 파일이 추가되고 삭제되는 일이 발생하여 깃으로 추적되는 에셋 파일의 기록이 차지하는 용량이 크게 증가하는 문제점이 발생한다. 이러한 문제를 방지하기 위해서 빌드되는 파일의 변경을 최소화해야 하고 가능한 하지 않는 방법으로 에셋 파일은 복사의 방식으로 빌드 폴더에 위치하게 된다.
  • 동일한 코드를 가진 파일의 경우 파일명만 바뀔 때 깃은 파일이름 변경(rename)으로 판단한다. 이러한 원리를 이용해서 빌드되는 에셋 파일의 파일명을 바꾸는 방법을 생각해 볼 수 있다. 빌드가 될 때마다 에셋 파일은 깃에서는 파일의 삭제와 추가로 판단하지 않고 rename이 되면서 깃으로 추적되는 용량의 증가를 억제할 수 있다.
  • patterns: [{ from: 'src/assets', to: 'assets/[name].[contenthash][ext]' }],와 같은 방식으로 패턴을 정해주면 새성되는 파일의 이름에 파일_이름.해시.확장자가 붙는 방법으로 복사를 할 수 있다.
  • 하지만 파일이름을 변경하는 방식을 사용하면 복사된 파일을 불러올 때도 파일명을 바꿔줘야 하는 문제가 생긴다. src/pages(현재 예제에서는 src_study/pages) 폴더의 html 파일 내에서 불러오는 에셋 파일의 이름이 빌드 때마다 변하게 되고 src/assets 안의 파일경로를 이용해서 빌드 폴더에 복사된 파일 이름이 생성되는 로직을 만들어야 한다. 이런 방식이 까다롭기 때문에 에셋 파일을 빌드했을 때 파일명이 바뀌는 방법을 사용하지 않는다. 대신 asset-loader를 사용한다.
  • 자바스크립트에서는 기본적으로 이미지, 폰트 등의 웹팩에서 에셋으로 분류되는 파일을 require 또는 import와 같은 키워드로 불러올 수 없다.
  • 에셋 파일은 용량이 크기 때문에 자바스크립트 코드 내에서 이 파일들이 담고 있는 코드를 처리하게 되면 많은 처리 부하 및 시간을 사용하게 된다. 따라서 이들 파일을 자바스크립트로 불러오는 것은 웹팩으로 빌드되었을 때의 파일의 주소를 가져오는 역할을 한다.
  • 에셋 모듈를 통해서 에셋 파일을 로드하게 되면, 대상 파일은 임의의 해시값의 파일명으로 빌드된다. 그런데 해시값의 파일명이 되어도 빌드된 경로로 연결되는 경로를 반환한다. 따라서 CopyPlugin으로 빌드될 때의 파일명에 해시값을 부여하는 방법이 변경된 해시 값의 파일의 경로를 지정하기 어려운 것과 달리 asset-module은 빌드된 파일명의 접근을 쉽게할 수 있다.

asset 모듈 설정하기

webpack.config.js

// other configulation...
const config = {
    // other configulation...
    module: {
        rules: [
            // other rules...
            {
                test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
                type: 'asset',
            },
            // other rules...
        ]
    }
}
// other configulation...
  • 에셋 모듈은 로더는 아니지만 웹팩5 이상의 버전에서 기본적으로 제공되는 모듈으로 modules의 rule에 type을 'asset'으로 적어주는 것으로 에셋 모듈을 사용할 수 있다.

asset 모듈을 사용하는 예제 코드

src_study/pages/index.html

<div class="img-wrapper">
    <img src="assets/img/icon-square-big.svg" / >
</div>
<div class="img-wrapper">
    <img src="<%= require('../assets/img/icon-square-big.svg') %>" />
</div>
<div class="img-wrapper">
    <img src="<%= require('../assets/img/icon-square-small-slack.png') %>"/>
</div>
  • 위와 같은 두 가지 방식으로 이미지를 로드 할 수 있다.
  • 첫 번째 방식은 이미지 주소를 그대로 사용하는 것이다. 이 때는 대상 이미지 주소가 빌드된 폴더를 기준으로 한 파일 경로와 동일해야 한다.
  • 두 번째 방식은 require 키워드를 사용해서 이미지를 불러오는 방식이다. 웹팩에서는 자바스크립트로 외부 코드를 불러오면 로더가 적용되는데, 에셋 모듈은 로더로 분류되지는 않지만 로더의 기능을 가지고 있다. 파일을 불러올 때 에셋 모듈의 대상은 빌드될 때 파일명이 변경되고 변경된 파일명을 가리키는 주소를 갖도록 한다. (에셋 모듈이 등장하기 전인 웹팩 4에서는 로더를 사용해서 에셋 파일을 불러왔다고 한다.)
  • 위의 예제를 보면 svg 파일은 base64으로 변환이 되고 png 파일은 빌드된 파일의 이름을 갖는 경로를 갖는데, 이는 이미지 파일의 용량 차이 때문이다. 8kb 이하의 에셋 파일은 이미지 파일이 아닌 base64의 이미지로 변환되고, 8kb 초과의 에셋 파일은 해시명을 갖는 파일로 빌드되고 이 파일을 불러오는 주소를 반환하게 된다.
  • 외부의 이미지 파일을 불러오지 않고 직접 코드로 파일을 불러오는 방식을 인라인 방식이라고 부르고, 별도의 파일로 분리되는 방식을 리소스 방식이라고 부른다. base64으로 처리된 대상은 인라인 방식으로 변환된 것이고, 해시명의 파일이 생성되고 이 해시된 파일 경로를 호출하는 방식으로 변환된 것은 리소스 방식으로 변환된 것이다.