웹 개발을 한번쯤 해봤으면 “모듈(Module)
”이라는 단어를 들어봤을 것이다. 자바스크립트 관련 코드를 작성하고 다른 코드에 재사용을 위해 특정 함수나 변수들을 export 하는 경우가 많다. 과거에는 모듈 개념에 대해서 크게 신경 쓰지 않았지만, 자바스크립트에 의존하는 기능이 복잡해지고 커지면서 코드의 가독성 및 유지 관리를 위해 모듈의 중요성을 보여주고 있다.
// index.html
<script src="./module.js"></script>
// 간단한 Module 파일 생성
// module.js
const combine = (a, b) => {
return a + b;
}
const DEFAULT_SIZE = 5;
export { combine, DEFAULT_SIZE }
무작정 모듈화 시키는 것은 문제를 발생시킬 수 있다. 아래와 같은 HTML 파일에 두 개의 자바스크립트 파일을 적용시키면 동작에는 문제가 없지만 예상 밖의 결과를 이끌어 낼 수 있다.
해당 코드는 getNumber 함수를 통해 10의 결과를 도출하고 싶지만, 아래 main.js에 중복 변수 선언으로 인해 다른 값으로 출력된다. 이렇게 다른 파일이라도 같은 변수명으로 선언하여 개발하는 경우가 많은데 이는 스코프 문제를 일으키고 의존성 관리에도 문제가 생겨 모듈화 구현에 힘들 수 있다.
// index.html
<script src="./module.js"></script>
<script src="./main.js"></script>
<script>
getNumber(); // 20
</script>
// module.js
var num = 10;
function getNumber() {
console.log(num);
}
// main.js
var num = 20;
function getNumber() {
console.log(num);
}
웹 자원들을 변환하기 위해 필요한 최초 진입점이다. 쉽게 말해서, 웹 root시점의 자바스크립트 파일 경로를 설정하는 것이다. 그리고 지정된 자바스크립트 파일은 웹 애플리케이션에 필요한 모든 의존도들이 담겨져 있어야 Webpack이 모듈들의 관계도를 분석하고 빌드할 수 있다.
// webpack.config.js
module.exports = {
entry: './src/index.js'
}
// index.js
import LoginView from './LoginView.js';
import HomeView from './HomeView.js';
const App = () => {
LoginView.init();
HomeView.init();
}
App();
entry를 빌드하고 나서 나온 결과물의 파일 경로를 지정하는 속성입니다. 속성 옵션들이 있어 상황에 맞게 옵션을 설정하고 결과 파일을 생성하면 된다.
// webpack.config.js
const path = require('path');
// 해당 파일의 경로 기준으로 ./dist/bundle.js 결과물 출력
module.exports = {
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
}
}
// filename 옵션들
// name : entry 파일명
// id : 모듈 ID
// hash : 매 빌드시 붙이는 고유 해시 값
// chunkhash : 각 모듈 내용 기준으로 생성된 해시 값
module.exports = {
output: {
filename: '[name, id, hash, chunkhash].bundle.js'
}
}
웹 자원들 중 자바스크립트 파일이 아닌 HTML, CSS, 이미지, 폰트 등들을 변환할 수 있는 속성이다. 예를 들어 index.js 안에 css 파일을 import한 상태에서 webpack을 이용해 결과물을 출력하면 css 파일을 해석할 수 없기에 그에 대응하는 loader가 필요하다는 에러가 뜰 것이다.
// webpack.config.js
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js'
},
// loader 부분
// npm i css-loader -D를 실행한 후
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' },
]
}
}
rules
배열 안에 test
, use
가 담긴 객체 한 쌍을 추가하여 원하는 loader를 사용할 수 있다.
test
: loader를 적용할 파일 유형(일반적으로 정규 표현식 사용)use
: 해당 파일에 적용할 loader의 이름필요한 loader들은 npm을 통해서 다운 받을 수 있고 종류도 다양하다. 그래서 babel, sass, file, vue, ts 등으로 속성에 등록하여 사용하기도 한다. 또한, 여러 개의 loader를 사용할 경우, 순서대로 적용하기 때문에 주의해야 한다.
Webpack의 기본 동작 이외에 추가적인 기능을 이용할 수 있게 하는 속성이다. 이것도 마찬가지로 npm을 통해서 설치 후 추가할 수 있고 loader랑 유사하지만 역할이 살짝 다르다. loader는 파일을 해석하고 변환하는 역할인 반면, plugin는 해당 결과물의 형태를 바꾸는 역할을 수행한다.
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin(), // Webpack으로 빌드한 결과물로 HTML 파일 생성
new webpack.ProgressPlugin() // Webpack의 빌드 진행률을 표시
]
}
아래와 같은 플러그인들이 자주 쓰인다고 한다.
mode
: 모드 설정, [’none’, ‘development’, ‘production’]
중으로 설정 가능, default 값은 ‘production’
resolve
: import 사용 시에 대한 절대경로 설정을 custom할 수 있는 속성devServer
: Webpack Dev Server라는 도구를 사용할 때 적용할 속성, proxy 및 HMR(Hot Module Replacement) 설정 용도로 사용devtool
: 개발 모드에 대한 속성, 대표적으로 소스 맵을 이용해 디버깅에 도움이 되는 용도로 사용처음부터 무작정 React 프로젝트를 이용할 때 당연하다고 생각하면서 개발했지만, 나중에 알게 된 것은 React도 Webpack을 포함한 상태로 빌드해주는 것이였다. 정확하게 말하면 CRA를 통해서 Webpack, Babel, ESLint 등 다양한 패키지를 포함한 상태에서 제공하기 때문에 우리가 React로 쉽게 웹 사이트를 개발할 수 있었다.
기업들은 꼭 CRA를 이용해서 개발하는 것도 아니였다. 이유는 기업에 제공하는 서비스별마다 개발 환경이 다르기 때문에 필요하지 않는 패키지들을 설치한 상태로 제공한다는 단점에 의해서 원하는 패키지들로 조합한 Boilerplate을 만들어 프로젝트를 구성한다.