최근 사이드 프로젝트로 크롬 익스텐션을 만들고 있다. 개인적으로 브라우저 북마크 기능을 굉장히 자주 사용하는데, 읽음 / 안읽음 표시나 각종 데이터 필드에 대한 정렬 등 다양한 기능을 가진 북마크 관리 어플리케이션을 만들고자 한다.
이번 프로젝트에서는
- 지금까지 미뤄왔던
- 내가 익숙했던 환경에서 최대한 벗어나
- 개인적으로 조금은 도전적일 수 있는 과제들을 풀며
Comfort zone을 벗어나고, 그 과정에서 배운 내용을 정리하려고 한다.
이번 글은 프로젝트의 Webpack 설정에 대한 기록이다.
이후 내용들은 크롬 익스텐션, 그 중에서도 popup 형태의 앱에 대한 설정이므로 참고 부탁드립니다.
늘 프로젝트를 시작할때는 항상 아무것도 없는 폴더를 보며 막막함과 기대감이 동시에 든다. 그래도 새로 만드는 것 보다 쉽고 재밌는 일은 없다는 생각 ㅎㅎ
먼저 npm init 후 의존성을 설치한다. webpack-cli는 터미널에서 webpack 명령어를 실행할 수 있도록 해준다.
npm init -y
npm install react react-dom
npm install -D webpack webpack-cli
Wepback config 파일 생성
package.json이 위치한 root 디렉터리에 webpack.config.js를 다음과 같이 설정한다.
const path = require('path');
module.exports = {
entry: {
popup: './src/popup.jsx'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js', // 각 파일 네임과 동일한 빌드 결과물 산출, 한 가지 차이는 jsx -> js
},
};
Webpack은 자바스크립트 기본 모듈 번들러이다.
여러 개의 자바스크립트 모듈간의 의존성 관계를 밝혀, 하나의 파일로 묶어주겠다(bundle)는 것이다.
그렇기에 Webpack이 참조할 최상위 진입점(entry)과, 번들링의 결과물로 나올 output을 명시할 수 있다.
node.js에서 파일 경로를 다룰 때 사용되는 path 모듈을 사용해서 진입점과 결과물의 경로를 지정해준다
output의 filename 에서 사용된 [name]의 경우 진입점에서 사용된 파일 이름을 그대로 사용하겠다는 의미이다.
한 가지 주목할 만한 점은 jsx 파일로 진입했지만, 번들 결과물은 js 파일이라는 점이다. 차차 어떻게 가능한지 살펴보자.
"scripts": {
"build": "webpack --config webpack.config.js"
}
package.json에 build 명령어를 정의하고 npm run build를 실행하면, 당연히 에러가 난다.
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
ERROR in ./src/popup.jsx 6:4
Module parse failed: Unexpected token (6:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| const Popup = () => {
| return (
> <div>popup</div>
| )
| }
webpack 5.73.0 compiled with 1 error and 1 warning in 104 ms
error와 warning이 각각 하나씩 발생한다.
warning은 모드를 설정하라는 내용인데, 개발 환경이냐 배포 환경이냐를 말한다. 일단은 급한 error부터 해결 해보자.
error는 앞서 언급한 jsx => js 변환 문제이다.
Webpack은 기본적으로 js, json만을 해석할 수 있으므로 jsx 파일을 넣으니 에러가 발생하는 것이다.
즉, jsx를 js로 트랜스파일하는 과정이 필요하다.
Webpack의 Loader를 사용하면 Webpack이 js, json을 제외한 다른 포맷의 파일들을 해석할 수 있게 해준다.
Wepback Loader 설정
const path = require('path');
module.exports = {
entry: {
popup: './src/popup.jsx'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react']
}
}
}],
},
};
즉 우리는 Loader를 통해 JSX라는 non-javascript 코드를 js 코드로 해석하도록 할 것이다. 이 때 Babel을 도입할 수 있다.
Babel은 자바스크립트 트랜스파일러로, 여러 브라우저 환경에서 동작하도록 자바스크립트 구문을 변환해준다.
또한, React의 JSX등 특수한 문법으로 작성된 코드를 자바스크립트 코드로 해석할 때도 자주 사용된다.
참고로 Babel의 동작은 기본적으로 구문의 변환이다.
그렇기에 Promise등 자바스크립트의 built-in 구현체들이 포함되지는 않는다.
이런 구현체들은 polyfill로 추가해야 하는데, 별도의 옵션을 지정해주어야 한다.
더 자세한 내용은 이 글의 범위를 벗어나므로 다루지 않겠다.
Babel 트랜스파일 과정에서 각각의 세세한 문법에 적용할 수 있는 규칙들은 plugin으로 지정하게 되는데,
preset은 이러한 여러 플러그인들의 집합으로, 여러 규칙을 한 번에 적용시킬 때 사용할 수 있다.
우리가 필요한 JSX 해석의 경우 @babel/preset-react 프리셋을 사용할 수 있다.
Loader 설정에 babel-loader를 추가하고, option으로 프리셋을 추가했다.
이제 build를 실행시키면 정상적으로 popup.js 파일을 아웃풋으로 받아볼 수 있다.
하지만 아직 해결 할 문제가 남아있다.
- 기타 정적 파일들은 빌드 결과물에 포함되지 않았을 것이다.
- 웹팩 빌드 결과물인 js파일이 html에 주입되지 않았을 것이다.
Wepback Plugin 설정
Webpack의 Plugin을 사용하면 웹팩에 추가적인 동작을 하도록 할 수 있다. Loader와 혼동할 수 있는데, Loader는 단순 파일 해석에 대한 지원이라면, Plugin은 Loader로는 해결할 수 없는 기본 동작을 추가한다고 이해할 수 있다.
예를 들면 이번 프로젝트에서는 아래 동작들을 추가하려고 한다.
- html 파일에 번들된 js파일이나 기타 정적 리소스들이 링크되어 있어야 한다
- 빌드 결과물에 정적 리소스들을 별도의 폴더로 저장해야한다
플러그인을 설치하고, 설정 파일을 다음과 같이 변경한다.
npm install --save-dev html-webpack-plugin
npm install --save-dev copy-webpack-plugin
plugins: [
new HtmlWebpackPlugin({
template: './src/popup.html',
filename: 'popup.html',
}),
new CopyPlugin({
patterns: [
{ from: "public" },
],
})
]
개발, 프로덕션 환경 설정을 위한 webpack-merge
이제, 아까 warning으로 받아봤던 mode 설정을 해보자. webpack-merge 패키지를 사용하면 여러 웹팩 config 파일을 merge시켜서 사용할 수 있다.
즉 개발 / 배포 환경에서 사용할 config을 각각 따로 두고, 지금까지 작성했던 config 파일은 두 환경에서 merge하여 공용으로 사용하도록 할 수 있다.
npm install --save-dev webpack-merge
const { merge } = require("webpack-merge");
const config = require('./webpack.config.js');
module.exports = merge(config, {
mode: 'development',
devtool: 'inline-source-map'
})
개발 환경에 대한 설정을 위해 webpack.dev.js 라는 이름으로 파일을 만들어 위와 같이 설정한다. inline-source-map은 빌드된 결과물로부터 실제 소드코드에서 에러가 발생한 위치를 파악할 수 있도록 해주는 개발 툴이다.
const { merge } = require("webpack-merge");
const config = require('./webpack.config.js');
module.exports = merge(config, {
mode: 'production',
})
wepback.prod.js로 파일 이름을 짓고 위와 같이 설정해주었다.
"scripts": {
"dev": "webpack --watch --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
package.json에 위와 같이 scripts를 설정하면, 이제 개발과 배포 환경 분리 설정이 완료되었다.
Chrome extension에서는 별도의 dev-server 없이 브라우저 상에서 `chrome-extension://{extension의 id}` 로 접근하면 변경 사항을 확인할 수 있다. 다만 파일이 변경될 때 마다 자동으로 빌드해야 변경 사항이 반영되므로 --watch 옵션을 부여했다.
후기
여러 문서들을 보며 이론적으로 장황한 이야기에 지레 겁먹었는데, 그럴 필요는 없었던 것 같다. 직접 해보니 생각보다 직관적으로 이해할 수 있는 개념들이었다.
또한 이 글을 보시는 분들 중 번들러 설정을 안 해본 분들이 계신다면 꼭 해보시기를 감히 추천드린다. 단순히 설정을 어떻게 하느냐를 공부하는 것이 아니라, 모던 프론트엔드 프로젝트의 전반적인 구조에 대한 이해와 추가적인 학습할 요소들을 많이 얻을 수 있는 경험이었다.
'개발 > React' 카테고리의 다른 글
useEffect cleanup에서 퍼널의 상태를 clear하기 전에 생각할 것. 응집도 있는 퍼널 관리에 대한 생각들 (0) | 2023.06.16 |
---|---|
React의 new JSX Transform, emotion의 css prop 설정법 (0) | 2022.06.13 |
Next.js의 Link와 Client/Server-side-rendering (0) | 2022.01.20 |
React Component npm에 publish 하기 ~ 꺽쇠, +, - 등의 아이콘 컴포넌트 (0) | 2021.08.25 |
댓글