프론트엔드 개발 환경 공부 #6 자주 사용되는 로더

Jake Seo·2021년 9월 17일
2

css-loader

자바스크립트에서 css 파일을 모듈로 불러올 수 있는 기능을 제공한다.

app_amd.js

import * as math from "./math_amd.js";
import "./app.css";

console.log("1+2 = ", math.sum(1, 2));

css-loader를 이용하면, 위와 같이 app.cssimport할 수 있다.

app.css

.text-blue {
    color: blue;
}

위는 예시로 작성한 css이다.

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>테스트용</title>
    </head>
    <body>
        <span class="text-blue">콘솔을 확인해봅시다.</span>
        <script src="./dist/main.js"></script>
    </body>
</html>

span 태그에 class="text-blue"를 넣어서 기존 글씨 색을 파란 색으로 바꿔보자.

npm run build

npm run build 결과 위와 같이 에러가 난다.

ERROR in ./src/app.css 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> .text-blue {
|     color: blue;
| }
 @ ./src/app_amd.js 2:0-19

이 파일 타입을 다루기에 적절한 로더가 필요할 것 같다고 에러 메시지를 띄워준다.

css-loader 설치하기

npm install css-loader

위 명령어로 css-loader를 설치하자.

웹팩에 css-loader 적용하기

// node.js의 CommonJS 를 사용한 모듈화
const path = require("path");

module.exports = {
    mode: "development",
    entry: {
        main: "./src/app_amd.js",
    },
    output: {
        path: path.resolve("./dist"),
        // output 이름을 동적으로 나타낼 수 있는 효과가 있다.
        // entry는 하나가 아니라 여러 개일 수도 있어서,
        // entry가 여러 개라면 output도 여러 개다.
        filename: "[name].js",
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'css-loader'
                ],
            },
        ],
    },

이전에 직접 만든 loader를 적용할 때와 비슷한데, 이번에는 경로에 그냥 'css-loader'를 입력해주었다. 그리고 받아서 번들링 할 확장자를 .css로 정해주었다.

이 과정에서 웹팩도 5버전으로 업그레이드했다.

화면 확인해보기

엥? 분명 css 파일도 번들링 했는데 화면이 똑같다.

main.js 열어보기

분명 main.js에는 내가 작성한 css가 올바르게 번들된 것 같다. 왜 적용이 안 됐을까?

적용이 되지 않은 이유

  • html 코드는 DOM(Document Object Model)으로 바뀌어야 브라우저가 인식할 수 있다.
  • css 코드는 CSSOM (CSS Object Model)으로 바뀌어야 브라우저가 인식할 수 있다.

아직 CSSOM이 된 게 아니라, 자바스크립트 파일 내용에만 CSS가 있어서 적용이 되지 않았다.

고로 우리는 어떻게든 번들링된 결과를 CSSOM으로 변환시켜주어야 한다. 일반적으로 CSSOM을 생성하려면 HTML에서 CSS를 직접 불러와야 한다.

하지만 웹팩의 style-loader를 이용해도 된다.

style-loader

이전에도 설명했듯, 번들된 css를 CSSOM(CSS Object Model)으로 변경시키기 위해서 필요하다.

설치

npm install style-loader

webpack.config.js에 추가하기

// node.js의 CommonJS 를 사용한 모듈화
const path = require("path");

module.exports = {
    mode: "development",
    entry: {
        main: "./src/app_amd.js",
    },
    output: {
        path: path.resolve("./dist"),
        // output 이름을 동적으로 나타낼 수 있는 효과가 있다.
        // entry는 하나가 아니라 여러 개일 수도 있어서,
        // entry가 여러 개라면 output도 여러 개다.
        filename: "[name].js",
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // 로더의 실행 순서는 뒤에서부터 앞이다.
                    "style-loader",
                    "css-loader",
                ],
            },
        ],
    },
};

css-loader의 위에 style-loader도 추가했다. 참고로 로더의 실행 순서는 뒤에서부터 앞이다. stack에서 pop연산과 비슷하게 생각하자.

결과 확인하기

css가 잘 적용되었다.

head 태그 부분에 style태그가 잘 들어간 것을 확인할 수 있다.

file-loader와 asset/resource

로더는 처음에 설명했듯, 사실 소스코드에서 사용하는 모든 파일을 모듈로 사용하게끔 만들 수 있다. CSS에서도 url() 함수에 이미지 파일 경로를 지정할 수 있다. 앞서 말한 경우들에 웹팩 4버전의 경우, file-loader 웹팩 5버전의 경우 asset/resource를 이용하여 파일을 처리한다.

웹팩v4 file-loader 공식문서
웹팩v5 asset/resource 공식문서

이미지 번들링해보기

위와 같이 이전에 생성했던 app.cssbackground-image를 추가해서 번들링해보자. npm run build 명령어를 입력하면 된다.

아무것도 설정 안했는데 자동으로 된다! 이게 어떻게 된 일일까?

webpack5에서는 css에서 사용되는 이미지 번들링을 자동으로 지원한다.

js파일에서 이미지 console로 찍어보기

app_amd.js

import * as math from "./math_amd.js";
import "./app.css";
import image from "./image.png";

console.log("1+2 = ", math.sum(1, 2));
console.log("image", image);

위와 같이 ./image.png 파일을 image라는 변수로 받아서 출력해보고 싶다.

웹팩 실행해보기

이건 잘 안 된다.

그렇다면, webpack.config.js 수정해보기

// node.js의 CommonJS 를 사용한 모듈화
const path = require("path");

module.exports = {
    mode: "development",
    entry: {
        main: "./src/app_amd.js",
    },
    output: {
        path: path.resolve("./dist"),
        // output 이름을 동적으로 나타낼 수 있는 효과가 있다.
        // entry는 하나가 아니라 여러 개일 수도 있어서,
        // entry가 여러 개라면 output도 여러 개다.
        filename: "[name].js",
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // 로더의 실행 순서는 뒤에서부터 앞이다.
                    "style-loader",
                    "css-loader",
                ],
            },
            {
                test: /\.png$/,
                type: "asset/resource",
            },
        ],
    },
};

공식문서에 의하면, 더이상 웹페이지에 사용되는 이미지 파일 같은 리소스는 use: ["file-loader"]를 안써도 된다고 나온다. 대신에 위와 같이 type: "asset/resource"라고 하면 된다고 나와있다.


웹팩 공식 홈페이지에서 위와 같이 검색하여 여러가지 정보를 찾아볼 수 있다.

웹팩 실행해보기 2

정상적으로 잘 실행된다.

결과 확인하기

콘솔에서 이미지 경로를 잘 출력해주고 있다. 정상적으로 적용됐다.

해시로 캐시를 피하는 방법

웹팩의 결과물을 보면, 이미지 이름이 해쉬로 저장되어 있다. 해쉬로 저장되는 이유는 캐시를 피하기 위함이다. 이름이 같은 채로 파일 내용이 달라지면, 캐싱된 것을 그대로 불러올 수도 있다.

그 이유는 브라우저는 특정 시간 내에 다시 같은 페이지를 접속했을 때, 서버에 리소스 요청을 하는 대신에 캐시에 있는 리소스들을 활용한다. 웹사이트를 이용하다가 인터넷을 끊고 새로고침을 해도 몇몇 이미지는 그대로 보이는 현상을 경험해본적이 있을 것이다.

그래서 이러한 캐싱은 보통 URI를 기반으로 동일한 URI에 접속했을 때 이루어지기 때문에, 쿼리스트링을 활용하여 쿼리스트링에 절대 겹칠 수 없는 Hash나 UUID등의 유니크한 값을 넣어 캐싱을 막는다.

그러면 브라우저에 다운로드한 파일을 무시하고 서버로 새로운 요청을 보내게 된다.

이것을 캐시 무력화라고 부르기도 한다.

위의 내용을 적용하기 위해서는 웹팩 설정에서 다음과 같은 부분을 추가해주면 된다

웹팩 5의 경우

  ...
            {
                test: /\.png$/,
                type: "asset/resource",
                generator: {
                    filename: "static/[name][ext]?[hash]",
                },
            },
  ...

마지막에 ?[hash]를 통해 쿼리스트링이 포함된 경로를 지정할 수 있다.

결과는 위와 같이 image.png?해쉬가 된다.

웹팩 4의 경우

{
  test: ...,
  loader: 'file-loader',
  options: {
    publicPath: './dist/',
    name: '[name].[ext]?[hash]'
  }
}

웹팩 4의 경우, 거의 흡사한데 아주 약간 다르다

자주 쓰는 이미지 타입 전부 지정해보기

            {
                test: /\.(png|jpg|gif|svg)$/,
                // type: "asset/inline",
                type: "asset/resource",
                generator: {
                    filename: "static/[name][ext]?[hash]",
                },
            },

보통 pngasset/resource로 처리하는 것이 아니라, png, jpg, gif, svg를 전부 asset/resource로 처리한다.

url-loader과 asset/inline

url-loader는 보통 작은 이미지 등을 사용할 때, Data URI Scheme 방식을 이용하여 넣는 것을 말한다. Data URI Scheme 방식을 이용하면 리소스를 외부에서 가져오지 않고, 파일 자체를 문서에 임베드 시킨다.

Data URI Scheme의 형태

data:[<media type>][;base64],<data>와 같은 형태로 이루어진다.

  • mediatype: MIME 타입을 의미한다. 생략 시에 기본값 text/plain;character=US-ASCII가 적용된다.
  • base64: 데이터가 텍스트가 아닌 경우 base64 인코딩된 데이터가 필요하다.

Data URI Scheme의 특징

  • HTML 문서 내에 삽입한다.
    • HTTP 요청 없이 진행된다.
    • 캐싱되지 않는다.
  • 용량
    • base64로 인코딩되어 원본 파일보다 용량이 크다.
  • 긴 문자열
    • 브라우저에 따라 문자열 길이 제한 때문에 용량이 큰 이미지 등은 처리가 어렵다.
    • 소스코드 상에 삽입하면 소스 가독성이 떨어진다.

Data URI Scheme 장점

  • 이미지 사이즈가 작은 경우, 오버헤드가 HTTP 요청보다 유리하다.
    • 작은 사이즈의 이미지를 위해 HTTP 요청 및 응답의 오버헤드를 받는 것 보다 작은 파일의 경우, Data URI Scheme를 사용하는게 더 작은 오버헤드일 수 있다.
    • 하지만, 큰 이미지의 경우에는 HTTP 요청의 오버헤드가 더 작을 수 있으니 주의해야 한다.
    • 효율적으로 쓰기 위해서는 HTTP 통신 스펙에 드는 문자열의 바이트 수와 base64로 변경했을 때 해당 파일이 커지는 바이트 수를 잘 비교하면 된다.
  • HTTPS와 같은 보안 프로토콜에서는 특히나 리소스를 불러오는데 시간이 많이 걸리는 편인데, 이 경우 Data URI Scheme을 쓰는게 이득인 경우가 많다.
  • 사용자가 다운로드를 많이 받는 이미지인 경우에는 HTTP 커넥션의 동시접속이 밀릴 때가 있는데, 이러한 일을 없애준다.
  • 멀티미디어 페이지를 하나의 파일로 관리할 수 있게 만들어준다. 이메일 등을 보낼 때 따로 이미지 첨부 경로 없이도 멀티미디어 이메일을 보낼 수 있다.

Data URI Scheme 단점

  • Data URI는 개별적으로 캐싱될 수 없다. 문서를 다시 다운받을 때마다 다시 다운로드된다.
  • 인터넷 익스플로러에서 지원이 잘 안되는 경우가 있고, IE8에서는 최대 크기가 32KB이다.
  • 단순한 스트림으로 첨부되어, 웹브라우저와 같은 처리환경에서 메타데이터, 데이터 압축, content negotiation과 같은 것을 제공하기 위해 멀티파트 등의 컨테이너를 사용할 수 없다.
    • 분할해서 보낼 수 없다는 뜻
  • 원래 파일의 크기보다 1/3가량 크기가 더 커져서 보안 소프트웨어가 컨텐츠를 필터링하기 더 어렵게 만든다.
  • 모바일 브라우저에서는 더 느리게 보일 수 있다.

Data URI Scheme 브라우저 호환성

참고 문서

asset/inline 사용해보기

            {
                test: /\.(png|jpg|gif|svg)$/,
                type: "asset/inline",
                // type: "asset/resource",
                // generator: {
                //     filename: "static/[name][ext]?[hash]",
                // },
            },

기존의 설정을 asset/resource에서 asset/inline으로 바꿔보았다.

결과

이미지가 1.1MB의 거대한 문자로 변했다. 이 문자가 Data URI Scheme이며, 이 문자를 이용해 HTML 문서에 이미지를 임베드 할 수 있다.

url-loader의 경우

            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: "url-loader",
            },

url-loader는 웹팩 4에서만 사용하는데, 위와 같이 사용하면 된다.

asset 타입의 가성비 챙기기

            {
                test: /\.(png|jpg|gif|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 4 * 1024, // 4kb
                    },
                },
            },

위와 같이 "asset"타입으로 한 뒤에 parser에 사이즈의 제한을 걸어두면, 해당 사이즈 미만은 asset/inline으로 처리하고, 해당 사이즈 이상은 asset/resource로 처리해서 네트워크 연결 오버헤드가 더 클만한 파일은 문서에 임베딩 시키고, 네트워크 연결 오버헤드가 더 크지 않은 큰 이미지 파일은 리소스로 관리하여 가성비를 챙길 수 있다.

결과 확인하기

큰 이미지는 리소스로 관리되고, 작은 이미지는 Data URI Scheme으로 관리되는 것을 볼 수 있다.

정리

  • 로더는 말 그대로 js에서 load할 수 없는 파일을 load하는 것을 도와준다.
    • 주로 로드하는 것으로는 이미지 파일, CSS 파일이 있다.
  • 웹팩의 설정파일인 webpack.config.jsmodule: { rules:[] }에 위치하게 된다.
    • test에 정규표현식으로 어떤 파일 이름 패턴에 적용할지 정할 수 있다.
    • use에 의존성으로 추가한 로더(css-loader, style-loader)를 입력해 이용할 수 있다.
    • type에는 웹팩5에서 지원하는 로더(asset, asset/resource, asset/inline)를 입력해 이용할 수 있다.
  • 이미지를 로드할 때는 리소스로 취급하여 로드하는 것과 Data URI Scheme으로 로드하는 방식이 있다.
    • 리소스로 취급하여 로드할 때는 웹팩 v5에서는 asset/resource, 웹팩 v4에서는 file-loader를 사용하면 된다.
    • Data URI Scheme으로 취급하여 로드할 때는 웹팩 v5에서는 asset/inline, 웹팩 v4에서는 url-loader를 사용하면 된다.
    • 웹팩 v5에서는 사실 asset이라는 타입 하나로 전부 로드할 수 있다.
  • CSS를 로드할 때는 파일 자체를 로드해주는 css-loader와 로드한 CSS를 CSSOM(CSS Object Model) 로 만들어주는 style-loader가 필요하다.
    • 웹팩 설정파일 내부 module: { rules: [ { ... use: "style-loader", "css-loader" } ] }와 같은 형식으로 입력하면 된다. use에 등록된 로더의 실행순서는 뒤에 있는 로더가 먼저 실행되기 때문에, 반드시 css-loader가 뒤로 가야 한다.
profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

1개의 댓글

comment-user-thumbnail
2022년 2월 25일

와 진짜 설명 잘해주시네요. 감사합니다!

답글 달기