웹팩(2/4); 기능 사용하기

Chullin
27 min readApr 10, 2019

--

안녕하세요. 개발자 chullin입니다. 드디어, 연재 글의 두 번째 글을 써보게 되었군요. 개인적으로 아주 감격스럽습니다. 지금껏 연재글이 성공한 적이 없었는데, 적어도 계획되어 있는 4편 중 절반까지는 쓴 것이니까요. 예정된 퍼블리시 날짜도 맞춰서 그런지 자신감이 붙네요. 시작이 반이라는 말이 정말 실감나는 하루에요.

.

글묶음

.

1️⃣ 웹팩; 프롤로그 ⭕️

2️⃣ 웹팩; 기능 이해하기 ⭕️

3⃣-1웹팩; JS 모듈화 역사 돌아보기(1)⭕

3⃣-2웹팩; JS 모듈화 역사 돌아보기(2)X

4️⃣ 웹팩; 미래 점쳐보기 ❌

오늘은 기능이에요.

.

YES. It’s About Webpack Functionality!

오늘 알아보려고 하는 것은 웹팩의 기능이에요. ‘기능’이라 함은 어떤 역할을 하는가? 라는 질문에 대한 대답이에요. 굳이 이렇게 명시화 하는 이유는, 설명하지 않으려는 내용을 분명히 하고자 함이에요. 설명하지 않으려는 주제는 “왜”의 영역이에요.

NO. It’s not About “Why the hell that functionality Poped Up”?

오늘은 “왜 그런 기능을 만들게 되었는가?”라는 식의 웹팩 존재 이유를 따져보는 질문에 대한 대답은 의도적으로 피할 예정입니다. 이 문제는 세 번째 연재 글[웹팩; 역사 돌아보기]에서 대답해보려고 해요. 오늘의 글에서는 기능, 즉 어떻게 웹팩을 쓰면 되는 것인지 알아보는 데에 충실해보려고 하는데, 그 요약은 다음과 같습니다.

.

TL; DR

.

- 웹팩은 모듈 번들러입니다. 머리끝(entry)부터 시작해서 Loader/Rules라는 트럭을 타고 다니며, 손끝 발끝 말초신경까지 연결되어있는 모든 모듈들을 하나의 Output으로 묶어줍니다.

- 웹팩이 번들링을 시작해서 Output을 만들어내기 전에 여러 PlugIn을 적용할 수 있는데, 이런 플러그인들은 웹개발을 즐겁게 해줍니다. 개발자도 행복해지고, 유저도 행복해져요.

.

웹팩이 뭐길래 내 작업물에 껴있는 것인가?

.

제가 마주한 문제부터 써보도록 할게요. 저는 vue로 작업을 하고 있었어요. 일단, 다음과 같이 프레임워크를 따왔어요.

vue init chullin

그러면, vue로 웹개발 작업을 할 수 있는 최소한의 boiledplate가 마련된 거에요. 그리고서, 어떻게 하면 웹환경에서 개발작업물을 확인 할 수 있을지 살펴보니,

npm run dev

라더라구요. 이것은 alias(긴 명령문 치기 귀찮아서 짧게 만든 명령문)니까, package.json에서 해당 alias가 무엇의 약자인지 찾아보니까 다음과 같았어요.

webpack-dev-server  – inline  – progress  – config build/webpack.dev.conf.js

으으. 웹팩 컨피겨레이션 파일을 열어보니 복잡하더라구요. 이때부터였습니다. 언젠가 웹팩 한 번 정리해야겠구나 생각했던 시기가 말이지요. 모르고 넘어가기에는 아쉽고, 뭔가 이름부터 참으로 거창해서 걱정부터 들더군요. 구글링 해보니, 웹팩이 현대 웹의 많은 곳에서 쓰이는 것 같더라구요. 그래서 더욱 더 학습에 박차를 가했네요.

.

웹팩 공식 페이지로 가볼까?

.

웹팩 공식 페이지로 가서 컨셉을 읽어보면, 기본적으로 소제목들이 [Entry, Output, Loaders, PlugIns, …] 이렇습니다. 찬찬히 읽어보고 이해해보면, 다음의 두 문장으로 요약할 수 있었어요.

.

“웹팩은 모듈 번들러로서, Entry 코드 파일에서부터 시작해서 Loader/Rules라는 트럭을 타고 다니며, 연결되어있는 모든 말초 모듈들까지 다 묶어서 하나의 Output을 내어줍니다.”

.

“웹팩은 다 묶어낸 Output에 갖은 plugIn을 적용하면, 유저가 해당 웹페이지에 들어왔을 때 빠르다는 느낌을 받을 수 있구요, 개발자는 웹개발 효율을 더욱 높일 수 있습니다.”

이제 이번 글을 통해, 이 두 문장의 의미를 찬찬히 살펴보고자 해요. 일단 첫 번째 문장부터 살펴보도록 하죠.

entry에서 시작해 output까지, 개발자가 설정해야 할 5 가지.

웹팩 개발진은 웹팩 도큐먼트도 함수같이 써놓았어요. 무엇을 인풋으로 받아서 무엇을 아웃풋으로 내놓는지 써주었다는 점에서, 웹팩을 이해하는 과정은 어떤 함수 하나를 이해하는 식이었고, 읽기 편했어요. 저도 함수를 설명하듯이 웹팩을 설명할 수 있겠어요. 실제 웹팩이 자신을 설명하는 그 모양도 하나의 함수네요.

인풋이 웹팩이라는 블랙박스로 들어가면 아웃풋이 나와요.

웹팩 컨피겨레이션 파일을 보면, 다음과 같은 모양이에요. 저는 제가 설명하려는 핵심적인 부분들만 가져왔어요.

module.exports = {
entry: {
app: ‘../path/to/main.js’
},
output: {
path: ‘../path/to/dist’
},
module: {
loaders: [],
rules: [],
plugins:[],
},
}

어찌보면, 참으로 복잡해보이는데, 사실 안그래요. 여기서 설정해야 할 것은 enty, output, loaders, rules, plugins 총 5가지이구요. 사용자마다 크게 달라질 법한 파트는 module 오브젝트 안에 있는 loaders, rules, plugins 뿐이거든요.

.

entry 그리고 output

.

일단, 웹팩 컨피겨레이션에서는 entry를 설정해야 해요.

module.exports = {
entry: {
app: ‘../path/to/main.js’
},

...
}

어디서부터 번들링을 시작할 것인지 설정하는 것이지요. 예를 들어볼게요. main.js가 다음과 같은 모양이라고 쳐보지요.

//main.js
const Bar = require('./bar.js');
Bar.bar();

main.js에서는 bar라는 모듈을 임포트해서 사용하네요. bar라는 모듈 안에는 어떤 기능이 담겨 있겠지요? 무슨 기능이 담겼는지 구체적으로 설명할 필요는 없으니, 비워두어 볼게요.

//bar.js
module.exports.bar = function() {
/*void*/
console.log("function bar is exported!")
/*void*/
}

웹팩의 기능은 간단해요. main.js에서부터 시작해 main.js에서 임포트한다고 선언한 bar라는 모듈까지 하나로 묶어서 아웃풋으로 내어주는 것이지요. 그러면, 그 아웃풋은 어디에 저장이 되는가? 그것도 사용자들이 결정해주면 되어요. output이 존재하는 이유지요.

module.exports = {
...,
output: {
path: ‘../path/to/dist/output.js’
},

...
}

여기 path 설정을 해주면 되어요. 즉, output에는 웹팩이라는 함수를 거쳐서 만들어낸 아웃풋을 어디에 저장할 것인지 써주면 되는 것입니다. 저는 ‘../path/to/dist/output.js’ 에 저장한다고 했으니, 이 기나긴 경로 끝에 저장되어있는 output.js 파일에 저장된 것이에요. main.js에서부터 bar.js 모듈까지 하나로 묶인 파일이 바로 output.js 파일인 것이구요.

시작으로 설정된 자바스크립트 파일에서부터 말단모듈로 설정된 bar.js까지 하나의 파일로 묶이는 식인 것입니다.

어떻게 묶였는지 궁금하면, output.js파일을 열어보면 되어요. 의존관계를 다루는 코드까지 붙어있기 때문에 복잡하게 느껴지겠지만, 잘 찾아보면 해당 output.js 코드 안에는 main.js 파일도 있을 것이고, bar.js 파일도 들어있어요.

그리고 이 아웃풋으로 만들어진 파일을 html파일의 한 줄로 넣어주면 끝인거에요. (이 작업은 플러그인을 잘 찾아쓰면 자동화될 수 있구요. 해당 플러그인 이름은 htmlwebpackplugin이구요.)

<!doctype html>
<html>
<head>

</head>
<body>

<script src=‘../path/to/dist/output.js’/>
...
</body>
</html>

원래 같았으면, main.js도 html에 스크립트 태그를 붙여서 올리고, bar.js도 html에 스크립트 태그 달고 올렸어야 했는데, 이제 번들된 output.js 파일 하나만 올리면 되는 것이에요.

<!doctype html>
<html>
<head>

</head>
<body>
...
<script src=‘./main.js’></script>
<script src=‘./bar.js’></script>
...
</body>
</html>

저희는 두 개의 파일만 다뤘지만, 복잡도가 높아진 거대한 웹앱을 상상해보세요. 임포트하는 파일들이 수 백, 수 천개가 될 수도 있어요. 그 파일들마다 스크립트 태그 달고 올리는 작업을 생각해보세요. 개발자도 지루하겠지만, 해당 웹페이지에 들어온 유저도 웹브라우저가 그 모든 과정을 받아오는 네트워킹 과정동안 지루하게 기다려야 할 것이에요. 비극이지요.

<!doctype html>
<html>
<head>

</head>
<body>
...
<script src=‘./main.js’></script>
<script src=‘./foo.js’></script>
<script src=‘./bar.js’></script>
<script src=‘./foooo.js’></script>
...
이렇게 스크립트 태그가 수 백개….? 개오바 ㅇㅈ?
</body>
</html>

물론, 웹팩과 같은 모듈번들러는 웹팩 이전에도 browserify도 있었고, require.js등을 통해서도 모듈 관리를 직접 해줄 수 있었어요. 하지만, 그런 방식으로 관리하는 것도 웹팩에 비하면 귀찮은 방식들인 것은 마찬가지에요. 이들에 대한 구체적인 언급은 연재글 3편에서 하도록 할게요.

아직 웹팩의 핵심이 익숙하지 않다면, 연습 한 번 더 하면 됩니다.

.

이를테면, 연습문제 2번

.

예제에 변화를 가져와보죠. bar.js가 말단 모듈이 아니라고 생각해보기로 해요. bar.js는 다음과 같이 다른 모듈을 가져오는 식이라고 생각해보기로 해요.

//bar.js
const Foo = require(‘./foo.js’)
module.exports.bar = function(){
Foo.foo()
}

그리고 foo.js는 다음과 같이 생겼다고 가정해보지요. 앞선 사례와 같은 이유로, foo도 채울 필요가 없으니, 일단 비워둘게요.

//foo.js
module.exports.foo = function(){
//
}

.

이 상황에서, 웹팩 번들링을 한다면?

.

웹팩 번들러 함수를 통해 아웃풋으로 나온 output.js 파일에는 main.js, 그리고 main.js에서 임포트한 bar.js, 그리고 bar.js에서 임포트한 foo.js 파일이 묶여있을 것입니다. 정 궁금하시다면, 번들링 아웃풋이 담겨있는 output.js파일을 한번 열어보세요. 다 묶여있을 것입니다.

.

자, 이러면, 웹팩의 기본적인 그림은 전부 다 이해한 것입니다. 제가 설명하고자 한 첫 번째 문장의 핵심도 설명이 끝났어요.

.

“웹팩은 모듈 번들러로서, Entry 코드 파일에서부터 시작해서 Loaders/Rules라는 트럭을 타고 다니며, 연결되어있는 모든 말초 모듈들까지 다 묶어서 하나의 Output을 내어줍니다.”

.

첫 번째 문장에는 Loader/Rules라는 트럭을 타고다닌다(?)고 하던데요?

맞아요. 그것은 설명을 따로 안드렸지요? 로더가 등장했던 것은, 파일의 끝이 .js로 끝나지 않는 파일들(예를 들어, css, img, svg … 등등)을 묶어주려고 함이에요. 죄다 찾아서 하나로 묶어주겠다는 목적은 같지요.

그래서, 웹팩 메인 화면의 소개 이미지 중, 오른편에 static assets에는 단순히 .js파일만 들어가는 것이 아니라, .css나 .jpg 혹은 .png도 들어갈 수 있는 것이에요. loaders 혹은 rules 덕분이지요.

웹페이지에는 여러 아이콘을 넣어줘야 하기 때문에 svg 파일도 들어가고, 때로는 사진 파일을 올리려고 img 파일도 들어가고, 디자인을 위해 css도 들어가요. 만약에 css는 css대로, svg는 svg대로, img는 img대로 하나의 파일로 묶인다면, 개발자는 파일 유지 및 관리가 편안해질 것이고, 또 유저는 그만큼 렌더링 시간을 아낄 수 있겠지요?

.

문제는 svg, css, img는 자바스크립트처럼 모듈화되어 관리되지 않는다는 점에 있어요. .js파일들은 require 혹은 import를 통해서 모듈을 가져오지만, svg, css, img는 그렇지 않잖아요. .js파일들을 일괄적으로 관리한다는 일념하에 시작한 웹팩은 다른 타입의 파일들도 일괄적으로 관리하고자, 하나의 기능을 추가했어요. 그 것이이 loaders라고 생각하시면 됩니다.

.

그래서 loaders로 묶인 배열 안에, *.svg로 끝나는 파일들 하나로 묶어달라는 로더도 넣으면 되구요. *.css로 끝나는 파일들 하나로 묶어달라는 로더도 넣으면 되어요. 로더는 요러요러한 타입의 파일도 올리기 위한 하나의 트럭이지요.

.

예를 들어서, css 형식의 파일들을 style-loader, css-loader로 찾아서 하나의 파일로 묶어서 관리하려고 한다고 치면, 아래와 같이 넣으면 되겠지요?

module.exports = {

module: {
loaders: [{
test: /\.css$/,
use: [‘style-loader’, ‘css-loader’]
}],

},

}

맞긴 한데요. 이건 과거의 방식이에요.

이제는 rules에서 관리를 해요. 현재 2019년은 webpack v4.0이 출시된 상황이고 v.5.0을 앞두고 있는 상황인데, loaders에서 rules로 바뀐 것은 webpack v.2.0에서부터 였어요. 과거에 이미 바뀌었지요. 그렇다고 해서 웹팩 개발진이 loaders를 아예 없애면, 이미 이 방식으로 개발을 진행한 수백 수천만의 개발자들을 너무 고통스럽게 만드는 것이라 아직까지도 남겨두었어요. (물론 미래에는 deprecate될 것이라고 하지요.)이 과정이 궁금하신 분들은 이 링크를 찾아가보시면 될 것이에요.

loader 대신에 rules를 사용해요. 그래서 아래처럼 써야, 웹팩 개발진의 기대에 맞춘 개발자가 되겠지요.

module.exports = {

module: {
loaders: [],
rules: [{
test: /\.css$/,
use: [‘style-loader’, ‘css-loader’]
}]
},

}

왜 loaders에서 rules로 바뀌었는가?

사실, rules를 쓰라고 했으니, 그 말을 따라서 rules에 쓰면 되어요. 그런데, 왜 이렇게 바뀌었는지 생각해볼 필요가 있어요. 개발진이 마주했던 문제를 우리도 한번 복기해보면 좋잖아요. 하지만, 이 질문 즉, “왜 loader 대신 rules를 쓰는가?”는 오늘 글의 핵심인 “기능”과 거리가 멀어요. “이유”를 다루고 있으니까요. 이 질문에 대한 답은 다음의 연재 글에서 밝혀보도록 하고, 오늘은 기능, 사용 방법에 대해서만 신경 써보도록 할게요.

.

중간 정리

.

지금까지의 긴 설명글을 정리하자면, 웹팩 모듈 번들러는 .js 파일들은 .js파일대로 모듈들을 싹다 끌어 모아서 하나의 파일로, .css 파일은 .css파일대로 싹다 끌어모아서 하나의 파일로 만들어주는 기능을 하는 것입니다. 말 그대로 번들링, 묶어주는 것이지요.

entry, output, loaders, rules 기능을 언급했으니, 이러면 PlugIns만 남았네요.

module.exports = {
entry: {
app: ‘../path/to/entryfile/which/might/be/main.js’
},
output: {
path: ‘../path/to/outputfile/which/might/be/dist’
},
module: {
loaders: [],
rules: [],
plugins:[], // 이 친구만 남았어요!
},
}

.

PlugIns는 참깨라면의 고추기름 후첨물이에요.

.

인지적 복잡도를 낮추기 위해서 위와 같은 비유를 들어보았어요. 그런데, 그다지 틀린 말은 아닐거에요. output을 만들어내기 전에 몇 가지 기능들을 후첨하려고 Plugins를 쓰곤 하기 때문이에요.

참깨라면의 후첨 고추기름은 더 맛을 끌어올리기 위함이고, 웹팩의 후첨 plugIns는 개발자와 클라이언트 유저의 사용성을 높이기 위함이지요.

물론, 웹팩 개발진은 plugIn은 웹팩의 backbone이라고까지 언급했다는 점에서, 고추기름 후첨물보다는 조금 더 중요한 입지를 차지하고 있겠다는 생각은 듭니다.

웹팩 공식 페이지에서 소개하고 있는 플러그인들은 다음의 링크에서 찾아볼 수 있어요. 대략 25개 정도 있는데요.

링크는 여기에요.

물론, 조미료와 향신료의 종류가 다양한 것처럼, 웹팩 플러그인도 공식적인 플러그인 말고도, 써드파티 플러그인들도 있어요. 이 링크에서 찾아볼 수 있어요.

스크롤 내리면 더 많아요!

각자 쓰고싶은 플러그인을 골라서 쓰면 되는 것이에요. 개인 취향 따라, 필요 따라 선택하면 되어요. 그런 이유로, 저는 모든 플러그인을 언급하지는 않을게요. 요리학원 가서 수 십개 향신료 각각의 맛만보다가 시간 다 흘러가면 얼마나 지루하겠어요. 해당 향신료를 어디에 써서 어떤 맛있는 플레이트가 나오는지가 중요한 것처럼, 저도 제 웹개발물에 어떤 플러그인을 쓰게 되었는지만 간단히 소개해보고자 해요. 그래야 향신료도 더 유용해 보일 것이구요.

.

SPA 블로그에 사용하면 유익한 웹팩 플러그인

.

저는 SPA(Single Page Application) 개인 블로그를 개발하고 있어요. 유저의 사용성 그리고 제 웹개발환경을 고려해서 제가 개인 블로그에 꼭 쓰고싶은 웹팩 플러그인들을 선정해보았어요.

.

(1) PrerenderSPAPlugin

: SPA의 SEO 문제 일부를 해결.

.

SPA는 기본적으로 SEO(검색엔진최적화)문제를 안고가요. 왜냐하면, SPA는 기본적이고 원초적인 형태의 html을 바탕으로, 유저의 인터랙션에 따라 클라이언트 사이드에서 동적으로 페이지를 생성하기 때문이에요.

예를 들어서, 저의 블로그 기본 html는 정말 이렇게 생겼어요. 제 서버가 제공하는 유일한 html이에요. 제 블로그 컨텐츠들이 정말 1도 안담긴 껍데기 html이에요. vue로 spa를 만들면 이런거에요.

<html>
<head>
...(별로 중요하지 않은 정보.)
</head>
<body>
<div id="app"></div>
<!--ㄹㅇ body가 이게 전부입니다.-->
</body>

<script>
...(별로 중요하지 않은 정보)
</script>
<html>

유저가 제 웹사이트로 들어온다면, (1) 위에 작성된 html, (2) vue의 여러 기능들을 구현하는 .js 코드, (3) 제 포스팅 글 컨텐츠가 담긴 데이터 세 가지를 브라우저로 다운받아요. 그리고는 클라이언트 사이드에서 유저의 인터랙션에 맞춰서 새로운 페이지를 라우팅해주어요. 즉, 제 포스팅이 100개가 있다고 할 때, 유저가 제 서버에 들락거리면서 포스팅을 읽는 것이 아니에요. 제 서버에는 딱 한번만 들락거리고, 그때 (1)html, (2) .js 코드, (3) 포스팅 컨텐츠 데이터 모두를 다운받고서, 클라이언트 사이드에서 글을 읽고 하는 식이에요.

중요한 것은, 순전히 클라이언트 사이드에서 모든 일들이 벌어진다는 것이에요. 클라이언트 유저는 해당 html, .js코드들, 그리고 포스팅 글 컨텐츠 데이터를 다운받고 나면, 더 이상 서버와 네트워킹하지 않아요.

이 때 생기는 문제점은, 구글 크롤러는 저의 각 포스팅 글 컨텐츠 별로 이 글이 얼마나 중요한지(즉, 검색 상단에 올려야할지 말지) 평가할 수 있는 그 어떤 정보도 알 수 없다는 점에 있어요.

구글 크롤러는 방문자 수 등등 여러 기준을 가지고서 이 웹페이지가 사람들이 많이 찾는지 아닌지를 판단하는 기계에요. 어떤 특정 검색어를 기준으로 어떤 웹페이지 A에 들락날락하는 유저들이 많다면, 해당 웹페이지 A를 검색 상단에 올려주는 식이에요. ‘들락날락’을 개발자 스럽게 표현한다면, ‘네트워크 횟수’인데요. SPA 사이트는 클라이언트가 처음에 제 사이트에 들어왔을 때 (1)html, (2)js코드, (3) 포스팅 데이터 세가지 모두를 다운을 받고 나면, 더 이상의 들락날락이 없어요. 즉 네트워크가 없는 것이고, 즉, 구글 크롤러가 판단할 수 있는 정보가 없는 것이에요.

<html>
<head>
...(별로 중요하지 않은 정보.)
</head>
<body>
<div id="app"></div>
</body>

<script>
...(별로 중요하지 않은 정보)
</script>
<html>

구글 크롤러는 저 아무 의미값도 없는 껍데기에 불과한 html만 카운트할 것이에요. 유저가 다운받은 것은 저 html이 전부잖아요. 그 안의 포스팅이 100개가 있든 1000개가 있든 각각의 포스팅별로 방문 수를 카운트 하는 식이 아니게 되는 것이지요.

제 포스팅 중 어떤 포스팅은 잘 쓴 것이라서 사람들이 많이 읽는 것이 있을 것이고, 또 어떤 포스팅은 못쓴 것이라서 사람들이 잘 안읽을 수도 있어요. 하지만, 그 모든 것이 카운트가 안된다는 게 바로 SEO 문제에요. 즉, SPA의 경우, 크롤러는 껍데기만 크롤링하고, 실제 포스팅 별 카운트에 대해서는 계산조차 못하는 문제가 생겨요. 이것이 바로, SPA의 SEO 문제입니다.

이러한 문제는 1) 서버사이드 렌더링으로 해결(vue의 경우 nuxt.js가 있어요)하거나, 2) 프리 렌더링으로 해결할 수 있어요. 저는 24시간 돌아가는 서버를 쓰는 것이 아니므로 1) 서버사이드 렌더링으로는 해결이 어려워요. 그렇다면, 남아있는 해결책은 프리랜더링인데, 이 PrerenderSPAPlugin이 프리랜더링을 할 수 있게 도와줍니다.

그래서 쓰게 되었어요.

.

(2) svgSpriteWebpackPlugin

: .svg 형식의 파일을 한꺼번에 모아 하나의 파일로 묶는 기능.

저는 .svg형식의 아이콘을 사용할 예정이에요. 그런데, 이 형식의 아이콘들을 여러개 올려야 할 때, 각각 서로 다른 파일에서 관리한다면, 클라이언트 사이드에서 이 각각의 파일들을 다운로드 받는 데에 있어서 네트워킹 시간 비용이 많이 들 것이에요. 쪼만한 파일들 때문에 네트워킹 시간이 과도하게 많이 드는 것은 좋지 않죠. 설령, 현재 사용하는 아이콘들이 그다지 많지 않다고 해도 나중에 갯수가 많아지면 분명히 이 네트워킹 시간비용 문제는 커질 것이에요.

아이콘 파일들을 개별적으로 올리는 것보다 파일 하나로 묶어서 올리면, 클라이언트의 네트워킹 시간 부담이 줄어들어요. 그 과정을 ‘svg 파일들을 묶어서 sprite 만든다’라고 부르더라구요. svgSpriteWebpackPlugin은 이 과정을 도와주는 플러그인이에요.

그래서 사용하려구요.

**혹여나, 이미지를 어떻게 묶는다는 것인지 감이 잘 오지 않는다면, 여기서 확인해보세요. 혹은 다음 사이트에서 svg sprite generator(iconizr.com)를 GUI로 한번 경험해보시는 것도 좋을 듯 싶어요.

.

(3) HotModuleReplacementPlugin

: 런타임에 페이지 새로고침 없이 모듈을 자동갱신해서 개발 경험 향상.

말 그대로, 코드를 변경한 뒤에 브라우저 새로고침을 누르지 않아도 브라우저가 새롭게 변경된 코드로 업데이트되는 식이에요. 코드를 변경할 때마다 새로고침을 눌러야하는 수고가 없어지니, 생산성이 조금이나마 올라갈 수 있을 것입니다.

물론 개발환경에서만 작동하는 플러그인입니다. 프로덕션 환경에서는 작동하지 않아요. 그래서도 안되겠구요. 이 HMR(Hot Module Replacement)플러그인 관련해서는 이 블로그를 참고하면 유용하겠어요.

(4) TerserPlugin

: 결과물로 나온 output.js 파일의 사이즈를 조금 줄여줘요.

사실, 이 플러그인을 사용하려는 이유는 최적화 문제 때문이에요. Terser는 output으로 나오는 파일의 사이즈를 줄여주는 기능을 해요. 압축기능이죠. 클라이언트 사이드에서 저의 웹페이지에 들어와 필요한 것들을 다운받을 때 사이즈가 작으면 좀 더 빠르게 다운받을 수 있을 것이에요.

아, 그리고 uglifyJSWebpackPlugin도 TerserPlugin과 같은 기능을 해요. 하지만, uglifyJSWebpackPlugin를 사용하는 대신 TerserPlugin을 사용할 예정이에요 . 두 가지 이유가 있어요.

첫째 이유는, uglifyJSWebpackPlugin 개발자들이 해당 플러그인을 더 이상 유지보수를 진행하지 않고 있는 반면, TerserPlugin은 계속 진행하고 있으니까요. 궁금하시다면, 해당 이슈를 트래킹해보셔도 좋겠어요. (이슈링크)

둘째 이유는, minify 속도에서도 차이를 보이고 있기 때문이에요. 몇배로 차이가 나요. 이 역시 이슈링크를 남겨둘테니 한번 확인해보셔도 좋겠어요.(이슈링크)

참고로, 이 플러그인은 웹팩 컨피겨레이션 파일의 mode값을 production으로 해두면 자동으로 활성화된다는 점 알아두시면 좋겠어요. 웹팩을 사용하는 개발자들이 플러그인 컨피겨레이션에 쏟는 자잘한 시간을 아껴주려는 심산으로 웹팩 개발진이 기본적으로 심어둔 7개의 플러그인 중 하나에요. mode값을 production으로 설정할 수 있는 방법은 다음과 같아요.

module.exports = {
mode:'production',
entry: {
...
},
output: {
...
},
module: {
...
},
}

간단하죠?

.

(5) SourceMapDevToolPlugin

: 난독화된 파일의 소스맵을 그려서 개발자의 디버깅을 도와줍니다.

파일 하나로 싹 묶여있으면(게다가 terserPlugin을 써서 난독화까지 되어있는 경우), 에러 트래킹이 어려워집니다. 트래킹을 겨우겨우 해도 terserPlugin때문에 난독화되어있으면 도대체 읽을 수도 없구요.

이 코드는 terserPlugIn으로 난독화된 코드에요. 코드 자체가 세멘틱하지 않아서 읽을 수가 없어요.

이런 상황에서 SourceMapDevToolPlugin을 사용하면 소스맵을 그려주고, 어디서 에러가 났는지 확인이 가능하게 됩니다.

terserPlugin 혹은 uglifyJSPlugin을 사용한다면, 이 플러그인도 하나의 세트로 사용하는 것이 좋겠어요.

(6) ExtractTextWebpackPlugin

: css파일을 독립적으로 관리하고, .js파일과 동시에 로드할 수 있어서, 더 빠른 속도로 렌더링할 수 있어요.

이 플러그인이 없다면, css파일이나, js파일을 한꺼번에 하나의 파일로 묶어낼 것이에요. css파일도 너무 크고 js파일도 너무 크다면, 클라이언트 사이드에서 로드할 때 시간이 너무 오래걸린다는 문제가 있어요.

하지만, 이 플러그인은 css파일과 js파일을 구분해 묶어내고, 각기 서로다른 파일로 저장합니다. 그러면, 클라이언트 사이드에서는 css와 js코드가 뒤범벅된 하나의 큰 코드파일을 다운받는 것이 아니라, .js파일과 .css파일 하나씩 다운받게 되어요.

핵심은, 이 .js코드와 .css코드가 클라이언트 단에서 동시적 로딩이 가능하다는 점입니다. 그러면, 클라이언트 유저는 불필요한 로딩시간을 기다리지 않아도 될 것이구요.

이 밖에도, mode가 프로덕션으로 설정되었을 때, 제공되는 플러그인들은 다음과 같아요. 아까 언급한 terserPlugin과 아래 6개의 플러그인을 합해 총 7개의 플러그인이 디폴트로 제공되는 것이에요.

  • NoEmitOnErrorsPlugin
  • FlagDependencyUsagePlugin
  • FlagIncludedChunksPlugin
  • ModuleConcatenationPlugin
  • OccurrenceOrderPlugin
  • SideEffectsFlagPlugin

아직 각각이 무슨 기능을 하는지 구체적으로 찾아보지는 않았는데요. 하나씩 알아가게 되면, 연재글의 번외편으로라도 플러그인에 대한 설명을 넣어보도록 할게요. 오늘 다 알아보는 것은 저에게도 부담이고, 독자분들께도 부담이고… (라고 변명해보고…)

무료로 플러그인 쇼핑하는 맛이 쏠쏠하던데, 여러분도 한번 경험해보시면 좋겠네요. 말씀드린 플러그인 중 몇 개는 이미 쓰고 있었고, 몇 개는 아직 안쓰고 있는데, 이 기회에 한번 다 적용시켜봐야겠어요. 자, 이렇게 두 번째 문장에 대한 설명까지도 마무리가 되었네요.

“웹팩은 다 묶어낸 Output에 갖은 plugIn을 적용하면, 유저가 해당 웹페이지에 들어왔을 때 빠르다는 느낌을 받을 수 있구요, 개발자는 웹개발 효율을 더욱 높일 수 있습니다.”

.

웹팩. 결국 그냥 싹다 끌어모아주는 것에 불과한 것이네? 플러그인으로 후첨해주고?

맞아요. 그것이 웹팩의 역할이에요. 죄다 끌어모아 하나의 파일로 만들어주는 것이지요. 개략적으로 보자면, 관련되어있는 모든 모듈들을 인자로 받는 concat함수를 쓰는 것과 마찬가지일 것이에요. concat함수를 쓰면, 아웃풋으로 싹 다 묶인 결과물이 나오잖아요.

var array1 = [‘a’, ‘b’, ‘c’];

var array2 = [‘d’, ‘e’, ‘f’];

console.log(array1.concat(array2));

// expected output: Array [“a”, “b”, “c”, “d”, “e”, “f”]

이런 것처럼, 웹팩도 모듈을 싹다 concat하는 식으로 이해하시면 편할 것이에요.

하지만, devil은 detail에 있고, 성공도 detail에 있어요.

웹팩 개발진은 작년에 웹팩 V.4.0을 출시하면서까지 해결하고 싶었던 문제는 바로 “어떤 식으로 번들링해서 빠르게 빌드할 것인가?”였어요. 속도를 높일 수 있는 방식, 즉 관련되어있는 모듈들을 묶어내는 방식을 어떻게 설정할 것인지가 중요했지요.

이 영상은 v.4.0이 출시되었을 때의 컨퍼런스영상이에요. 이 영상을 보시면, 웹팩을 통해 빌드 시간을 얼마나 단축시킬 수 있었는지 확인해보실 수 있어요. 7~8시간 걸리던 것을 거의 10분 안짝으로 줄였다고 하니, 웹팩의 성능 감이 잡히시나요?

어떻게 속도를 높일 수 있었을까요? devils in the details는 무엇이었을까요? 웹팩 개발진은 어떤 문제를 해결했던 것일까요?

.

이 문제는 세 번째 글 “웹팩; 역사 돌아보기.”에서 다뤄볼게요.

.

세번째 연재글은 dependency graph, tree shaking가 주제가 되겠어요. 그리고 오늘 글에서 다루지 않았던, ‘loaders를 사용하지 않고 왜 rules를 사용하게 되었는가?’라는 질문도 다음 글에서 다뤄보도록 하겠습니다.

3⃣-1웹팩; JS 모듈화 역사 돌아보기(1)⭕

3⃣-2웹팩; JS 모듈화 역사 돌아보기(2)X

(만약, 링크가 아직 활성화가 안되었다면, 제가 글을 아직 올리지 않은 것이니, follow해주셨다가 나중에 확인해보셔도 좋겠습니다.)

.

TL; DR

- 웹팩은 모듈 번들러입니다. 머리끝(entry)부터 시작해서 Loaders/Rules라는 트럭을 타고 다니며, 손끝 발끝 말초신경까지 연결되어있는 모든 모듈들을 각각 파일타입별(.js, .css, .svg, .img …)로 하나의 Output으로 묶어줍니다.

- 웹팩은 Output을 만들어내기 직전에 여러 PlugIn을 적용해 웹개발을 즐겁게 해줍니다.

* 뒷단의 dependency graph, tree shaking이 웹팩 기능의 핵심적인 역할을 하고 있을 것이에요.

.

P.S. 감사의 말.

.

앞서 1편에서도 이야기했었지만, 웹팩은 나온지 어느새 6년이 되어가고 있어요. 그래서, 구글링 해보시면 알겠지만, 웹팩의 기능이 무엇인지 설명하신 분들이 참 많아요. 제가 이번 글을 쓸 때 열심히 참고했던 블로거 분들은 다음과 같습니다.

1. 김정환 블로그: 웹팩의 기능을 간결하게 설명해주셨습니다.

2. zero.cho 블로그: 웹팩의 기능을 간결하게 설명해주셨습니다.

3. 박주성 tistory: 웹팩을 어떻게 사용해야 유익한지 실무자의 입장에서 설명해주셨습니다.

4. 네이버 D2 블로그: 웹팩의 성능 및 유용한 플러그인을 설명해주셨습니다.

.

위 순서로 읽어보시면, 큰 도움이 되지 않을까 싶어요. 이 수많은 블로그 글 사이에서 제 글을 하나 더 올리는 이유는, 개인적으로 공부하기 위함이기도 하고, 한번 정리해보려는 마음도 있기 때문이에요. 남들에게 설명하지 못하면, 제대로 아는 것이 아니라고 생각하거든요. 정리해서 올렸다가 지적받으면 받는대로 저 나름 성장이구요. 감사함과 함께 오늘의 글을 마치겠습니다. 이상 개발자 Chullin 이었습니다.

--

--