CRA 없이 배우는 Webpack
모듈 번들러 톺아보기
JavaScript 표준을 위한 움직임: CommonJS와 AMD
web (2)
[JS] 모듈의 역사와 문법(CommonJS, AMD, ESM 등)
ESM과 CommonJS의 차이
[React] Babel, Webpack 동작 원리 간단히 알아보기
Create-React-App의 Webpack 기본 설정 살펴보기
이에 따라 '하나의 js파일'로 '모듈별로 스코프를 나누어서' 관리하는 것이 요구된다.
하나의 파일에서 모듈을 관리하는 기본적인 방식은 클로저의 원리를 이용하는 것이다.
var wordIIFE = (function () {
var word = 'hello';
return {
getWord: function () {
return word;
},
};
})();
console.log(wordIIFE.getWord()); // hello
console.log(word); // ReferenceError
위와 같은 방식으로 각 모듈 내의 변수를 반환받으면서도, 외부에서 접근 및 수정하지 못하도록 모듈 패턴을 구현할 수 있다.
CommonJS는 구글이 공개한 V8 엔진의 성능에 고무받아 2009년 발표한 모듈화 라이브러리이다.
CommonJS가 적용된 환경(대표적으로 Node.js)에서 자바스크립트 파일은 각각의 Scope를 지니며, exports를 이용해 모듈을 공개하고, require() 함수를 이용해 모듈을 사용한다.
CommonJS는 모듈을 삽입하기 위해 blocking이 일어나기 때문에 실제 브라우저 환경에서 사용하기에는 느리다는 단점이 있으며, CommonJS의 대안으로 AMD(Asynchronous Module Definition)라는 non-blocking, async가 있다. 한편 ES6에서 공개된 ESM(ECMAScript Module)은 export - import
구문으로 작동되며, 파일의 비동기 삽입을 제한하는 대신 파일의 최적화에 집중하고 있다.
웹팩은 '모든' 웹 어플리케이션의 자원들을 모듈로 간주한다. 즉 자바스크립트 뿐만 아니라 HTML, CSS, 기타 font와 image와 같은 파일들을 포함한다.
웹팩은 1. 다양한 플러그인을 지원하여 확장성이 좋다. 2. 가장 대중적이다. 3. Hot Module Replacement(HRM)을 지원한다.(Dev Server)
웹팩을 적용하면 onClick.js, index.js 등 여러개의 자바스크립트 파일이 하나의 main.js로 변환된다. 이는 request 요청 횟수를 줄임을 의미하고, 이는 곧 패킷 유실과 네트워크 부하, 서버의 연산 감소 등을 의미한다.
웹팩에서 가장 강력하고 자주 쓰이는 것이 loader이다.
bebel-loader
를 통해 자바스크립트 파일을 ES5이하의 버전으로 컴파일 시킬 수 있다.
sass-loader
는 node-sass패키지를 통해 sass를 css로 컴파일 시키며,
css-loader
를 통해 문자열로 변환한 후 style-loader
를 통해 <style>
태그로 <head>
에 삽입하는 자바스크립트코드를 만든다.
즉 웹팩을 사용하면 html파일 하나와 main.js파일 하나만이 만들어지게 된다. 그 밖에 js파일이나 css파일은 없다.
npm install webpack webpack-cli
으로 webpack을 설치하고, webpack -w
로 웹팩을 실행하면 웹팩은 프로젝트 루트 파일의 webpack.config.js
를 바탕으로 번들링을 시작한다. 이 과정에서 웹팩은 파일 간의 의존성 문제도 해결한다.
CRA를 통해 만들어진 리액트 앱의 경우 npm run eject
을 통해 숨겨져 있던 설정을 변경할 수 있다. 이 작업은 되돌릴 수 없으며, 이후 추가되는 라이브러리의 의존성 문제를 직업 해결해야 함을 의미한다.
번들링이 시작하는 entry point를 지정한다. 해당 파일이 의존하는 모듈과 라이브러리들을 찾아낸다.
module.exports = {
entry: paths.appIndexJs,
};
path : 번들링된 결과물의 위치를 설정한다.
filename : 번들링된 결과물의 이름을 설정한다.
그 밖에 개발 모드에서 사용되는 경로와 production 모드에서 사용하는 경로를 나눈다. production시에는 chunck파일(즉, 웹팩으로 만들어진 main.js를 다시 분해한 파일)에 고유한 hash가 붙어 파일을 구분하게 해준다.
module.exports = {
output: {
// 빌드 폴더의 경로
path: paths.appBuild,
// 출력에서 생성된 require()에 /* filename */ 주석을 추가합니다.
pathinfo: isEnvDevelopment,
// 하나의 메인 번들과 비동기 청크당 하나의 파일이 있습니다.
// 개발 중에는 실제 파일을 생성하지 않습니다.
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// 코드 분할을 사용하는 경우 추가 JS 청크 파일도 있습니다.
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[name].[hash][ext]',
// webpack은 `publicPath`를 사용하여 앱이 제공되는 위치를 결정합니다.
// 후행(trailing) 슬래시가 필요합니다. 그렇지 않으면 파일 자산의 경로가 잘못됩니다.
// 홈페이지에서 "공개 경로"(예: / 또는 /my-project)를 추론했습니다.
publicPath: paths.publicUrlOrPath,
// sourcemap 항목을 원래 디스크 위치(original path)로 지정(Windows에서 URL 형식)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\\\/g, '/')),
},
}
SplictChunkPlugin으로 청크를 만들 때의 옵션을 지정한다.
CRA의 기본값은 production 모드일 때에 청크를 실행하며, 다음의 플러그인을 사용하여 최적화를 진행한다.
TerserPlugin
: 자바스크립트 코드 압축기이다.
OptimizeCSSAssetsPlugin
: CSS 압축기이다.
모듈을 해석하는데 필요한 정보이다. 가령 node_modules의 경로 등이 이 곳에 명시되어야 한다.
규칙에서 test로 로더를 적용할 파일명 규칙을, exclude에서 로더에서 제외할 파일명을 정의한 후, 불러올 loader들과 각 loader들의 옵션을 정의한다.
plugins 객체들을 모아둔 곳이다. new Plugin()의 형태로 새로운 익명 객체를 만들어둔다. 가령 HtmlWebpackPlugin은 번들링된 main.js를 참조하는 html파일을 빌드 과정에서 생성한다.
pakage.json에는 "scripts"라는 설정이 있다. 해당 설정에서 실행되는 scrips 폴더의 js파일들이 웹팩 빌드 설정을 의미한다. 가령 start.js는 웹팩 빌드 설정을 develpoment로 하여 빌드 후 실행하고, build.js는 웹팩 설정을 production으로 적용하여 빌드한 후, 해당 파일이 exports된 새로운 build 폴더를 반환하고 실행한다.