이번 글에서 Webpack, Parcel를 공부하면서 이러한 기술을 어떻게(How) 사용하는지 보다
대체 이걸 왜(Why) 사용하는지를 내가 이해한 방식을 토대로 글을 쓰려고 한다. 물론 뒤에 실습 코드도 나와있으니 인내심을 갖고 글을 쭉 읽은 후에 코드를 보면 어느정도 납득이 될 것이다.
먼저, What is parcel?
Parel은 Webpack과 함께 bundler 시장의 점유율을 나눠갖고 있는 모듈 번들러이다. 번들러(bundler)란 dependency가 있는 자바스크립트 파일들을 최적화, 압축하여 하나 혹은 여러개의 static 파일로 빌드해주는 컴파일러이다.
Why parcel?
질문에 대한 답을 하기 전에 먼저 브라우저 내부 엔진에 대한 이해가 필요하다(이전 포스팅을 참조). 브라우저의 렌더링 엔진에서 HTML 파일을 파싱해 DOM tree를 만드는 중에 <script src="./index.js"> 태그를 마주하면 엔진은 직전까지의 Render tree를 먼저 화면에 랜더링한다. 그리고 서버에 index.js 파일을 요청하여 응답을 받으면 계속 HTML을 파싱한다.
근데 우리가 만드는 어플리케이션은 하나의 js 파일만 있는 것이 아니다. SPA라면 수십개가 될 텐데 그럼 그때마다 script 태그를 마주하고 HTTP 통신을 해야한다. 그동안 유저는 로딩 바를 마주하게 되고 그것이 2초를 넘어가면 사이트를 떠나버린다. 심지어 리액트는 수십개의 의존성을 가지고있는 자바스크립트 파일 chunk이기 때문에 번들링을 하지 않는다면 더욱 로딩이 길어질 것이다. 그러므로 SPA에서는 모듈 번들링이 필수이다. 근데 그럼 이걸 우리가 직접 설정해야하는가?
그럴 필요는 없다. 사실 craete-react-app로 프로젝트를 생성하면 이미 잘 만들어진 모듈 번들러인 Webpack과 설정 파일이 있기 때문에 우리는 편하게 "npm run build"만 하면 된다. "npm eject"를 하고 직접 webpack 설정 파일을 수정할 일도 있겠지만 흔치는 않을 것이다. 하지만 내 프로젝트를 서버에 deploy할 때마다 command line에 치는 "npm run build"가 어떤 이상한 외계어가 적힌 파일들을 뿜어내고 그것을 서버에 올리는데 이러한 것들이 대체 어떠한 짓들을 하고 있는지 모르고 있기엔 자존심이 상하지 않는가? 이것이 Module bundler를 공부하는 이유이다.
And, babel
바벨 공식문서에는 바벨을 자바스크립트 컴파일러로 소개하고 있다. 이는 ES6+와 같은 최신 자바스크립트 문법을 모든 브라우저 Javascript Interpreter에서 실행될 수 있는 ES5 문법으로 transpile한다(트랜스파일러는 어떤 한 언어를 비슷한 수준의 추상화를 갖는 언어로 변경해주는 컴파일러의 한 종류이다)해준다. 이는 모듈 번들러인 Webpack이나 Parcel와 함께 큰 시너지를 낼 수 있다.
Start to coding
(1) 디렉토리 만들고, 필요한 리액트 모듈 설치
mkdir parcel
cd parcel
npm init -y
npm install react react-dom
(2) parcel, babel 설치
npm install --save-dev parcel-bundler babel-preset-env babel-preset-react
babel-preset이 무엇인지는 이 글에 매우 자세히 정리되어 있다.
(3) index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parcel</title>
</head>
<body>
<div id="app"></div>
<script src="index.js"></script>
</body>
</html>
위와 같은 파일을 만들어보자. 근데 뭔가 익숙하지 않은가? 그렇다 create-react-app 모듈이 해주는 것을 우리가 직접해보고 있다.
<div id="app"></div>을 리액트 공식 문서에는 "루트(root)" DOM 노드라고 부른다. (이 노드와 관련해서 SSR vs CSR 포스팅을 준비하고 있는데 Performance Optimization에 관심있는 분은 참고하길 바란다)
(4) index.js
import React from 'react';
import ReactDOM from'react-dom';
const App = () => {
return (
<h1>Hello World</h1>
)
}
ReactDOM.render(<App />, document.getElementById('app'));
ReactDOM.render() 함수를 통해서 "루트" DOM 노드안에, App 컴포넌트를 랜더링한다. 이는 리액트가 Virtual DOM을 만들고 "실제" DOM과 동기화하는 재조정 과정을 거친다. 자세한 내용은 리액트 관련 글로 정리해야겠다.
(5) package.json
{
"scripts": {
"start": "parcel index.html"
}
}
다음과 같이 react-script를 설정한다. 그럼 npm start를 프롬프트에 입력할 시 index.html 파일에 존재하는 모든 모듈들을 parcel가 번들링하기 시작한다. Parcel의 또 하나의 장점은 Webpack 처럼 config 파일을 일일이 설정해주지 않아도 된다는 점이다. 이는 매우 편하지만 되려 단점이 될 수도 있다.
(6) npm start
jeong-ideun@jeong-ideun-ui-MacBookPro parcel % npm start
> parcel@1.0.0 start /Users/jeong-ideun/parcel
> parcel index.html
Server running at http://localhost:1234
✨ Built in 1.28s.
index.html을 정상적으로 번들링해서 http://localhost:1234에서 Running까지 해주고 있다.
(7) 최종 확인
React 엘리먼트인 App이 정상적으로 DOM에 랜더링된 것을 확인할 수 있다.
- 그럼 이제 끝? 이게 과연 최적화일까?
모듈 번들러가 dependency가 있는 모듈들끼리 번들링해서 클라이언트에 보냄으로써 HTTP request 통신 횟수를 최소화하여 유저의 로딩 시간을 줄이는 것 까지는 이해가 됐다. 근데 그렇게 되면 하나의 번들 파일의 크기가 커지지 않는가? 이 무지막지하게 큰 번들을 브라우저에서 파싱하고 컴파일해야하니 로딩시간이 더 길어질수도 있지 않는가?
정답이다. 이러한 문제를 리액트 팀에서 인지하여 "Code spliting"이라는 개념이 생겨났다. 쉽게 말하면 하나의 큰 번들을 여러개의 번들로 쪼개자는 것이다. 예시를 들자면, 현 웹 페이지에서 필요한 자바스크립트만 번들링해서 브라우저로 보내는 것이다. 그러다가 유저가 "상세 페이지"를 누르면 그제서야 lazy하게 해당 자바스크립트 chunk(번들)을 서버로 요청하여 가져오는 것이다. 이러한 환상적인 최적화는 React.Lazy() 함수를 통해 가능한데 리액트 공식 문서에 매우 잘 정리되어있다.
- Webpack, parcel를 공부하며 배운 점.
사실 Babel, Webpack, Parcel를 처음 접할 때 이 기술들은 "실제 개발에 필요한 것이 아닌 optional한 기술"이라는 매우 얕은 생각을 갖고 있었다. 하지만 막상 공부를 해보니 개발을 둘러싸고 있는 환경을 뜯어보고 그 내부를 들여다보는 것이 개발자로서 성장하는데 매우 중요한 것임을 깨달았다. 단순히 코드만 짜는 코더가 아닌, UI/UX와 성능 최적화까지 생각하는 "개발자"의 반열에 오른 것 같아 매우 기쁘다.
번들링을 이해하려면 웹 브라우저의 동작 원리에 대해서 공부를 해야했고 거기서 랜더링 엔진, DOM 트리, 그리고 리액트의 Virtual DOM이라는 개념을 다시 한번 짚고넘어갈 수 있었다. 그리고 create-react-app이 내부적으로 어떤 것들을 해주는지를 알 수 있었고, eject해서 webpack.config.js 파일을 확인해본 것도 전체적인 틀을 갖추는데 도움이 됐다. (설정 파일에는 css관련한 loader가 많았고, babel-loader, file-loader도 Webpack이 해주고 있었다). 웹팩도 정리가 다 되는대로 포스팅하고 싶다.
'웹 프론트 개발' 카테고리의 다른 글
브라우저는 어떻게 동작하는가? (1) (1) | 2021.01.15 |
---|---|
Web speech API (Speech recognition api) (0) | 2020.12.27 |
Typescript 사용해보기 (0) | 2020.12.21 |