게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌2 - Webpack - 7년 전 등록 / 4년 전 수정

웹팩5로 CSS와 기타 파일 번들링하기

안녕하세요. 이번 시간에도 지난 시간에 이어 웹팩5 강좌를 계속 하겠습니다. 오늘은 CSS와 기타 이미지, 폰트 파일을 번들링하는 방법에 대해 알아보겠습니다.

지난 시간 JS 번들링에 대해서는 상식적으로 이해가 가죠? entry에서 시작해서 loader과 plugins의 가공 과정을 거쳐 output으로 나오는 거요. entry의 파일들부터 시작해서 import나 require로 엮어진 모든 js들을 하나 또는 소수의 파일로 묶습니다.

놀라운 것은... import나 require로 css랑 기타 이미지, html 또는 폰트 파일도 묶을 수 있습니다. import랑 require은 자바스크립트 모듈 구문인데 어떻게 그게 가능하냐고요? 저도 정확한 내부 과정은 잘 모르겠지만 그게 가능합니다. 대신 그게 가능하도록 설정을 해주어야 합니다.

일단 CSS부터 번들링하도록 설정해주겠습니다.

npm i -D style-loader css-loader mini-css-extract-plugin

css-loader는 css 파일들을 읽어주고 style-loader는 읽은 css 파일들을 style 태그로 만들어 head 태그 안에 넣어줍니다. 만약 style 태그 대신 css파일로 만들고 싶은 경우에 mini-css-extract-plugin을 사용하면 됩니다.

{
  module: {
    rules: [{
      // 전 시간 babel-loader
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader'],
    }],
  }
}

위와 같이 하면 됩니다. test 부분은 css파일에 적용하는 거라서 익숙한데 loader 대신 use가 있습니다. 여러 개의 로더를 동시에 사용할 때는 use를 사용합니다. (사실 loader 그대로 해도 됩니다) 아, 로더는 연달아서 사용이 가능합니다. babel-loader처럼 하나로 사용할 수도 있지만, style-loader과 css-loader처럼 두 개를 한 번에 사용할 수도 있습니다. 나중에 less-loader, sass-loader, postcss-loader과 함께 세 네개를 동시에 사용하기도 합니다. 뒤에서부터 실행되므로 먼저 css-loader로 처리한 뒤 그 결과물을 style-loader로 한 번 더 처리하는 겁니다.

이제 entry의 js파일 상단에서 require('app.css');를 하면 알아서 읽어서 style 태그로 만들어줍니다. 신기하죠?

지금까지의 방법은 css를 style 태그로 만드는 거였습니다. 이제는 여러 css를 하나의 css 파일로 합치는 방법을 살펴보겠습니다. 아까 설치한 mini-css-extract-plugin을 사용합니다. webpack.config.js 제일 윗 부분에 require 해줍니다.

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

style-loader를 대체하지만 플러그인인만큼 module과 plugins에 모두 써줘야 합니다.

{
  module: {
    rules: [{
      // 전 시간 babel-loader
    }, {
      test: /\.css$/,
      use: [MiniCssExtractPlugin.loader, 'css-loader'],
    }],
  },
  plugins: [
    // 기타 플러그인
    new MiniCssExtractPlugin({ filename: 'app.css' });
  ]
}

css-loader에서 처리한 결과물을 받아서 filename에 적힌 파일명으로 만들어줍니다.

이제 webpack을 실행하면 output에서 설정한 path 경로에 app.css라는 파일이 생겼을 겁니다. 그 파일을 기존에 css를 넣던 방식대로 link 태그로 head에 넣어주면 됩니다.

참고로 서버 사이드에서 사용할 때는 style-loader와 css-loader 대신 css-loader/locals로 적어주면 됩니다. css-loader 2버전부터는 로더 options로 exportOnlyLocals를 붙여주면 됩니다. 예를 들어 위의 코드에서 style-loader와 css-loader를 지우고 css-loader?exportOnlyLocals로 바꾸면 되는 것이죠. 서버는 css를 지원하지 않기 때문에 다르게 사용해야 합니다. 여기서 에러가 많이 나는데 절대로 style-loader를 넣어서는 안 됩니다! style-loader는 서버 사이드 렌더링을 지원하지 않습니다.

나중에 더 나아가 css modules(css-loader?modules, 물음표 뒤에 modules는 옵션을 의미합니다)나 sass(sass-loader), less(less-loader), postcss(postcss-loader)를 사용할 수도 있는데 그것까지는 다루지 않겠습니다. 공식 문서에 잘 나와있습니다.

이번에는 기타 파일들에 대한 번들링 방법을 알아보겠습니다. 먼저 추가적인 로더를 설치해줍니다.

npm i -D file-loader url-loader

file-loader는 특정 파일을 그대로 내보내줍니다. 아무리 그래도 png랑 js랑 합친다는 건 말이 안 되죠? 웹팩도 한계가 있습니다. url-loader가 좀 특이합니다. 설정한 사이즈보다 작은 이미지나 폰트 파일을 인라인화 합니다. 아니 이미지랑 폰트를 어떻게 인라인화 하냐고요? base64로 인코딩하면 됩니다. base64가 뭐냐고요? 다음과 같은 겁니다. 

undefined

엄청나게 길죠? data-uri라고도 하는데요. 작은 파일은 따로 http 요청을 하느니 그냥 문자열로 인코딩해서 불러오겠다는 고육지책입니다. 원래라면 이미지 주소를 써야할 곳에 data:image/타입;base64, 이렇게 한 후 인코딩한 문자열들을 넣어주면 브라우저가 이미지로 인식합니다. 인라인화되어서 편한 것도 있지만 용량은 실제로 33% 정도 증가합니다.

{
  module: {
    rules: [{
      ... // 바벨로더와 css 로더들
    }, {
      test: /\.(ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
      loader: 'url-loader',
      options: {
        name: '[hash].[ext]',
        limit: 10000,
      },
    }],
  }
}

test 부분이 길어졌습니다. 다양한 파일들을 지원하려고 하는 만큼 정규식도 길어집니다. options가 중요합니다. limit(바이트 단위)보다 작은 파일은 base64로 인코딩해서 인라인화합니다. name에서는 지난 시간 output처럼 [hash], [name] 등을 쓸 수 있습니다. [ext]는 현재 확장자를 그대로 하겠다는 뜻입니다. 잠깐, 인라인화 하는데 결과물 이름은 왜 적어줄까요? 바로 limit보다 큰 파일은 알아서 file-loader가 처리하여 파일로 내보내주기 때문입니다.

역시 js 파일에서 require('./이미지경로/zero.png');같이 하면 알아서 인라인화하거나 파일로 만들고 경로도 알아서 연결해줍니다. 아까 css보다 더 신기하죠? 리액트의 경우 보통 아래처럼 사용합니다.

<img src={require('이미지경로')} />

이미지나 폰트 외의 다른 파일(html, jade, pug, ejs)은 각각에 해당하는 로더가 다 있기 때문에 그걸 사용하시면 됩니다. 역시 import나 require을 하면 알아서 웹팩이 다 해결해줍니다. 웹팩 만세!

용량 관계로 다음 시간에 웹팩의 강력한 기능 중 하나인 코드 스플리팅청크에 대해 알아보겠습니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

9개의 댓글이 있습니다.
3년 전
감사합니다!!

혼자서 한참 찾고 있었는데 정리 너무 잘 해주셔서 쉽게 했습니다
4년 전
안녕하세요 강좌가 너무잘정리되어있네요. 감사히보고있습니다. exclude가 문제라 계속 찾아보고있던 중인데..ㅠㅡㅜ 며칠째 해결이안되네요 scss파일에서 @import a/path1 @import a/path2 @import b/path1 @import b/path2
이렇게 a폴더하위와 b폴더하위의 scss파일을 import하고있는데 eclude : /a/를 아무리해도 a에있는 값을 들고와서 최종값이 됩니다. path.resolve(__dirname, './../src/a/')이렇게해도 안먹구요..ignoreplugin을 써도 안됩니다. 어떤부분이 문제인걸까요 ㅠㅡㅜ
4년 전
123
6년 전
안녕하세요. webpack 처음으로 사용해보는 학생입니다. 다름이아니라, freemaker에서 css를 인식을 못해서 질문하게 됬습니다. 혹시 틀린 부분 알수있을까요?

[config]
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: {
presets: [
['es2015', {modules: false}]
]
}
}]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},

[.ftl]
\u003c!DOCTYPE html>
\u003chtml lang="ko">
\u003chead>
\u003cmeta charset="utf-8" />
\u003ctitle>helloworld\u003c/title>
\u003clink rel="stylesheet" type="text/css" href="myStyle.css">
\u003c/head>
\u003cbody>
\u003cform method="post" action="Show">
ID \u003cinput type="text" name="id" id="userId"/>
PW \u003cinput type="password" name="pwd" id="userPwd"/>
E-Mail \u003cinput type="email" name="email" id="userEmail">
\u003cinput type="submit" value="Submit">
\u003c/form>
\u003cbutton class="positive ui button">ok\u003c/button>
\u003cscript src="/dist/js/vendor.js">\u003c/script>
\u003c/body>


[.CSS]
body{
background: cadetblue;
}

그리고 ftl과 css는 같은 위치에 있습니다.
6년 전
혹시 require(CSS경로)를 webpack entry에 있는 js파일에 쓰셨나요?
6년 전
추가해서 성공했습니다. 감사합니다 ㅎㅎㅎ 혹시 semantic ui 사용할때도 위와 같은 방법으로 적용하는건가요?
6년 전
아아 네네 semantic ui도 require하셔야 같이 번들링됩니다.
6년 전
ㅜㅜ webpack 설정하는게 너무 힘드네요. semantic.css랑 sematic.js 엔트리에서 설정하고 dist된 js, html로 넣었는데 작동을 안하네요... require로 config에 넣어야하나요?
6년 전
아마 js를 글로벌로 export하는 작업이 필요할 것입니다. 시맨틱을 근데 웹팩으로 빌드하는 이유가 있나요?
6년 전
package에서 dependencies와 devDependencies의 차이와 역할에 대해서 좀 더 알려주실 수 있으신가요?
6년 전
개발환경에 쓸 빌드도구와 태스팅 도구들을 거기에 넣어요
6년 전
npm i -D 에서 -D 는 무슨뜻인가요? --save-dev를 줄여서 사용한것인가요?
6년 전
6년 전
감사합니다~ 웹팩은 참 볼때마다 신기하기도하고 감사하기도 하고ㅋㅋㅋ 그런 라이브러리 네요:) url-loader에서 이미지랑 폰트를 인라인화 한다는 건, 이미지나 폰트의 바이너리(?) 코드를 인코딩해서 그대로 html에 포함시킨다는 거겠죠?
6년 전
네네 바이너리를 data-uri로 인코딩해서 포함시키는 거죠.
6년 전
감사합니다!
6년 전
npm i -D style-loader css-loader extract-text-wepback-plugin 부분에 wepback 오타 있습니다 : )
6년 전
감사합니다. 수정했습니다.
7년 전
강좌 잘 보고 있습니다.~
npm 설치하는데 extract-text-wepback-plugin wepback 으로 되있네요~ㅎ

css 설정 부분에 test 가... text 로...
7년 전
감사합니다~