
왜 하게 되었냐면
회사에서 기존 CRA로 만든 프로젝트를 커스텀엘리먼트화 하여 외부로 보내는 작업을 맡게 되었다.
아직 구체적인 기획안과 협의안이 나오지 않은 상태이나 기본적으로 할 수 있는 방법을 알아야 하기 때문에 공부를 할 필요성이(드디어!!) 생겼다!
샘플을 만들며 webpack,babel,loader 등의 세부 세팅을 공부하며 정리해보겠다 :)
공식문서에 링크된 툴체인 직접만들기를 시작으로 다양한 자료들을 참고하며 상황에 맞춰 세팅을 진행했다.
Webpack
사용 이유
웹팩을 사용하게 된 이유는 CRA로 이루어진 프로젝트이다보니 build 실행 시 여러개의 js파일이 생성되었고 요구사항은 하나의 js를 통해 호스팅된 주소에서 원하는 이벤트를 발생시키는 것이었다.
그래서 CRA를 뒤로하고 샘플부터 tsx로 만들어져있는 컴포넌트들을 하나의 js로 번들링할 계획을 가지고 시작하였다.
세팅
웹팩의 기능과 역할 그리고 세팅법에 관한 글은 하단에 무수히 많은 참고글을 보는것이 도움이 될것으로 판단되어 대부분 생략하고 진행하며
새롭게 알게된 설정값이나 webpack5로 넘어오면서 변경된 부분들에 대해서는 언급하고 지나가겠다.
1. 사용한 로더(Loader)
1.1 Style
1.1.1 sass-loader
sa(c)ss를 css로 컴파일
1.1.2 css-loader
스타일 시트를 import 구문으로 불러오기 위해 css파일을 모듈로 바꾸는 역할
1
import "./style.css";
1.1.3 style-loader
js모듈화된 css 를 style 노드로 생성
s(c)ass 와 css를 모두 커버할 생각이 었으므로 test에 적용된 정규식은 아래와 같고
웹팩 로더는 use안에서 여러가지가 실행될 때 배열의 뒤에서 부터 앞으로 실행된다고 한다.
그래서 sass-loader 를 통해 s(c)ass 를 css로 컴파일 하고 css-loader를 통해 css를 동적으로 생성할 수 있는 js로 변경해준 다음 style-loader를 통해 css를 style노드에 얹을 수 있게 만들어 주었다.
PS. stlyed-components와 같은 css-in-js는 다른방식의 설정이 필요하다. npm 공식문서에 가면 볼 수 있다.
1
2
3
4
5
6
7
8
9
10
{
test: /\.(s[ac]|c)ss$/i,
use: [
//웹팩 로더는 한 파일에 대해 여러가지가 실행되는데 배열의 뒤에서부터 앞으로 작동한다.
'style-loader', // js로 생성된 css를 styles 노드로 생성
'css-loader', // css 를 js로
// 'postcss-loader' //css파일 따로 extract하지 않을 것이므로 제외,
'sass-loader', // sass -> css로 컴파일
],
},
1.2 Files(asset)
webpack 5 이상의 버전을 적용했기 때문에 type:asset으로 진행되어 기존의4점 이하의 버전대에서의 설정과 다소 상이하다.
기존 url, file loader 사용시에 options에서 publicPath를 적용할 때 file과 url을 동일한 path를 적용해줬었다는 점만 기억하면 좋을 것 같다.
또한 브라우저의 캐싱을 통해 파일이 업데이트 되어도 브라우저 내에서 반영되지 않는 현상을 피하기 위한 이름설정(hash)도 기억해놓으면 좋다.
1.2.1 url-loader
background: url(“…”) 과 같은 url을 로드 해주는 것 파일을 base64 URI로 변경해주는 역할
1.2.2 file-loader
import/require 로 선언되어 있는 파일또는 url을 지정된 output 으로 내보내주고 public URI의 파일로 반환해준다. 소스코드에서 사용하는 모든 파일을 모듈로 사용하게끔 만들어주는 것, 파일을 모듈형태로 지원하고 웹팩아웃풋에 파일을 옮겨주는 것이 file-loader이다.
웹팩 5부터 url-loader, file-loader를 대신할 수 있게 되며 asset으로 설정 시 자동으로 base64로 컴파일(인라인번들 base 64, <= 8kb)하든지 images폴더로 넣어주든지 하게 해준다
1
2
3
4
5
6
7
8
9
10
11
12
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset', //웹팩 5부터 url-loader, file-loader를 대신할 수 있게 되며 asset으로 설정 시 자동으로 base64로 컴파일(인라인번들 base 64, <= 8kb)하든지 images폴더로 넣어주든지 하게 해준다
/**
* 큰 사이즈의 파일을 인라이닝 하고 싶으면 limit을 정해줄 수 도 있다 하단 parser 참고
*/
parser: {
dataUrlCondition: {
maxSize: 30 * 1024,
},
},
},
1.3 TypeScript
1.3.1 ts-loader
typescript (es6) 를 javascript (es6) 로 변경해주는 것이다. TypeScript에 관한 옵션은 두가지가 있다.
- ts를 js로 컴파일링할 때 옵션은 tsconfig.json파일에서 진행한다.
- 웹팩으로 번들링 시 진행되는 ts-loader에 관한 옵션은 webpack.config.js 내의 해당 로더의 options에서 설정이 가능하다.
첫번째로는 내가 설정한 tsconfig.json이다. 자세한 설명은 주석으로 코드내에 남기는 것이 더 좋은것 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"compilerOptions": {
"outDir": "./dist/", //컴파일한 파일의 위치 지정
"sourceMap": true, // 컴파일 후 source map 생성을 위해 true 설정 > dist에 **.map파일 생성됨
"noImplicitAny": true, // any 타입인 경우 에러 발생 설정
"module": "commonjs", //컴파일을 위해 사용될 모듈
"target": "es6", //컴파일 버전 타겟
"jsx": "react", // jsx처리
"esModuleInterop": true, //import 시 es6로 타겟팅하는 경우 컴파일 에러를 바로잡기 위한 설정
"baseUrl": "./src" // base Directory 설정
},
"exclude": ["node_modules", "**/*.spce.ts", "**/*.test.ts"],
"include": ["./src", "index.d.ts"]
}
두번째로는 내가 설정한 ts-loader에 관한 옵션이다. isDevelopment는 cross-env 모듈을 사용하여 스크립트 실행 시 개발모드에서는 true가 되는 변수이다.
ts-loader의 경우 dev-server를 적용하기 위해 @pmmmwh/react-refresh-webpack-plugin를 사용하였으므로 getCustomTransformers에 컴파일링이 되기전 개발서버인 경우 ReactRefreshTypeScript가 한번 작동되는 것을 볼 수 있다.
이렇게 설정해주지 않으면 초기 빌드가 잘되었다 하더라도 hot-server 상태에서 수정 후 저장 시 컴파일에서 깨져버린다.
자세한 것은 하단 참고 또는 이 링크를 참고하면 좋다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
options: {
getCustomTransformers: () => ({
before: [isDevelopment && ReactRefreshTypeScript()].filter(
Boolean
),
}),
transpileOnly: isDevelopment,
},
},
],
},
1.4 source-map-loader
source-map-loader 는 모든 js 엔트리에서 존재하는 소스맵을 추출해내는 역할을 한다. source-map은 빌드한 파일(번들링 완료된 파일)과 원본파일을 용이하게 연결시켜주는 파일이다. 코드는 원본파일에서 수정하지만 변경되는 사항에 대해 빠르게 번들링 되어 적용되는 것에 한 몫을 하는 것이다.
소스 맵을 이용해 빌드한 파일(번들링 완료된 파일)의 특정 부분이 원본 소스의 어떤 부분인지 확인하는 것이라고 하면 더 이해가 쉬울 것 같다.
devtool에 관한 옵션을 찾아보면 이것저것 많은데 .. 고퀄리티의 소스맵을 만들어주는 옵션이 source-map이라는 옵션이다
빌드시간이 좀 느리긴하다.
1
2
3
4
5
6
7
8
9
10
11
12
{
{
{
test: [/\.js?$/, /\.ts?$/, /\.jsx?$/, /\.tsx?$/],
enforce: 'pre',
exclude: /node_modules/, //node_modules는 제외
use: ['source-map-loader'],
},
},
// Webpack의 출력물에서 디버깅을 하기위해 소스 맵 사용을 위한 옵션중 source-map 사용
devtool: 'source-map',
}
1.5 babel-loader
js(es6)를 js(es5)로 변환해주므로 다양한 브라우저에서 es6가 잘 돌아가도록 도움을 준다.
원래 별도의 bable.config.js파일로 관리를 하려 하였으나 당장 필요한 설정도 몇개 없어서 웹팩안에 해당 preset에 관한 설정을 집어 넣었다.
여기에서도 개발서버 설정을 위해 개발모드 인 경우에만 플러그인 으로 서 react-refresh/babel이라는 별도의 플러그인 을 사용하도록 세팅되어있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
//추가 설정 없으면 babelrc.를 참조하게 되어있다
loader: 'babel-loader',
options: {
cacheDirectory: true, //바벨이 recompile하는데 쓰이는 큰용량의 소스를 줄여줌
presets: [
'@babel/preset-env', // in files only using JSX, -> TSX는 해당이 안되는것.. 같음
['@babel/preset-react', { runtime: 'automatic' }], // Runtime automatic with React 17+ allows not importing React
],
//하단 플러그인은 react-refresh-typescript에서 변경되는 ts를 바벨로더(js를 변환하는 용도이므로)가 못읽는 현상이 발생하기 때문
plugins: [isDevelopment && 'react-refresh/babel'],
},
},
},