기존에 프로젝트를 세팅할 때 loader에 관해서는 크게 고민하지 않고 여러 블로그를 보며 셋팅을 했다. 이유를 크게 생각하지 않고 babel-loader와 ts-loader를 모두 넣어두었다. 좋은 계기로 엄청난 에러를 발견했고, 두 로더의 차이와 프로젝트에 어떤 적용을 했는지 자세히 적어보려고 한다.
에러 발견
children으로 배열을 스프레드 해놓은 엄청난 문법에러를 저질러놓았다. 하지만 기존의 설정으로는 빌드시에 이를 발견하지 못하고, 동작시에도 문제를 발견하지 못했다. 로더에 변화를 주기 전까지는 놀랍게도 이 에러를 발견하지 못했다. babel-loader와 ts-loader의 차이를 알아보기 위해 하나씩만 적용해 보았을 때야 이러한 문제를 찾을 수 있었다.
babel-loader에서는 이러한 에러를 잡아주었지만, ts-loader에서는 이를 잡아주지 못하고 정상 동작하였다. 두 로더의 차이로는 빌드 속도가 가장 큰 것 같았지만 적용하고 있는 프로젝트의 사이즈 정도에서는 크게 차이를 느끼지 못했다. loacal환경에서의 type checking을 위해 ts-loader를 사용하고 싶었기 때문에 이 이유를 더 알아보기 위해 webpack 설정과 각 로더의 설정 방식을 찾아보았다.
webpack
로더(Loader)는 웹팩이 웹 애플리케이션을 해석할 때 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, Images, 폰트 등)들을 변환할 수 있도록 도와주는 속성이다. test에 적용할 로더 유형을 정규 표현식으로 적어주고, use에 적용할 로더를 적어준다.
로더의 적용 순서
특정 파일에 대해 여러 개의 로더를 사용하는 경우 로더가 적용되는 순서에 주의해야 한다. 로더는 기본적으로 오른쪽에서 왼쪽 순으로 적용 된다.
module: {
rules: [
{
test: /\.scss$/,
use: ['css-loader', 'sass-loader']
}
]
}
위 코드는 scss 파일에 대해 먼저 Sass 로더로 전처리(scss 파일을 css 파일로 변환)를 한 다음 웹팩에서 CSS 파일을 인식할 수 있게 CSS 로더를 적용한다는 의미이다.
ts-loader
ts-loader로 react를 검사할 수는 없는가 궁금했다. ts-loader의 설정 방법을 알아보았다.
The tsconfig.json file controls TypeScript-related options so that your IDE, the tsc command, and this loader all share the same options.
tsconfig파일을 tsc에서만 사용하는줄 알았는데 ts-loader도 이 설정을 함께 사용한다. 그렇다면 이 tsconfig에서 react를 어떻게 다루는지를 알아보자.
jsx
jsx파일도 결국은 js파일로 전환되어야 한다. typescript에서도 jsx를 js로 컴파일 하는 것을 지원한다. jsx설정으로는 preserve, react, react-native, react-jsx, react-jsxdev 설정이 있다.
보다시피 jsx 파일을 js로 전환해 주는 것이지 문법적인 오류를 확인해주지는 못한다. 결론적으로 ts-loader만을 사용할 시 react의 문법 오류는 확인할 수 없는 것이다.
hot-module-replacement
아직 HMR을 지원하지 않고 있다. 적용할 수 있는 방법이 있기는 하지만 조금 불편하다.
1. transpileOnly를 to true로 설정한다.
2. HML 콜백 함수에 변경될 모듈을 다시 적어준다.
babel-loader
그렇다면 babel-loader에서는 어떻게 react문법을 확인해주는 걸까? 우선 babel의 기본 설정에서는 최소한 다음 세개의 패키지가 기본이라고 한다.
- @babel/core
- @babel/preset-env
- a Babel "runner" (@babel/cli, babel-loader, @rollup/plugin-babel, etc)
babel/preset-env는 지원해야 하는 환경을 명시할 수 있다. babel/core는 어떤 방식으로 babel을 사용하든 항상 필요한 패키지이다.
react와 함께 사용하기 위해서는 @babel/preset-react를 사용한다. 추가적으로 typescript를 사용할 경우 @babel/preset-typescript를 사용하라고 한다. 여기서 babel/preset-react 덕분에 react의 문법 에러를 발견할 수 있게 된다.
@babel/preset-typescript의 아쉬운 점
type checking을 하지 않는다
const str: string = 42
typescript에서는 다음 코드에서 오류가 발생하지만, @babel/preset-typescript는 오류없이 넘어간다. 이 문제가 나에게는 가장 아쉬운 점이었고 나머지는 그냥 알아두는 정도로 보면 될 것 같다
- no namespace
- no const enum
- 선언 병합이 안됨(enum, namespace ...)
- jsx 사용시 <>를 이용한 타입 단언이 안됨( jsx설정이 켜져있으면 <Foo>x 형식으로 타입 단언 불가, x as Foo로 써야함)
프로젝트에서는...
물론 tsc를 사용해서 그때그때 확인하거나, vscode에서 빨간줄로 보여주는 에러로 타입에러를 잡을 수 있다. 하지만 나는 덤벙쟁이이기 때문에 애초에 type에러가 발생하고 있다면 빌드가 불가능하게 만들고 싶었다. react + typescript조합에서는 ts-loader가 위험할 수도 있겠다는 생각이 들었기 때문에 ts-loader와 babel-loader를 적절하게 섞어 쓰기로 결정했다.
tsx파일의 type checking만 ts-loader를 사용하고, 이후 과정은 babel-loader에게 맡기기로 했다. ts파일은 ts-loader가 전담해서 진행하고, js와 jsx파일은 babel-loader가 전담한다. 이를 위해서 tsconfig의 jsx설정을 preserve로 설정해 tsx -> jsx파일로만 변환되게 하도록 하였다.
"compilerOptions": {
"jsx": "preserve",
}
local과 dev에서는 보다 정확한 확인을 위해 ts-loader와 babel-loader를 모두 적용하기로 했고, prod 모드에서는 이미 검증이 끝났다고 생각되어 빠른 빌드를 위해 babel-loader만 적용하였다.
// dev
module: {
rules: [{ test: /\.tsx?$/, use: ["babel-loader", "ts-loader"] }],
}
// prod
module: {
rules: [
{
test: /\.tsx?$/,
use: "babel-loader",
},
],
},
이전까지는 ts-loader와 babel-loader를 동시에 써 놓은 이유를 알지 못했는데 에러 덕분에 이유를 알 수 있었다. 고마워 에러야!
+ 추가
원활한 디버깅을 위해 local과 dev에서는 빌드 후에도 타입이 보이는 것이 좋다고 생각했다. "sourceMap": true로 두어 source map 적용 후에도 type을 확인할 수 있도록 하였다.
참고
https://joshua1988.github.io/webpack-guide/concepts/loader.html#loader
https://github.com/TypeStrong/ts-loader
https://www.typescriptlang.org/ko/docs/handbook/jsx.html
https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats
'Typescript' 카테고리의 다른 글
[Typescript] decorator, reflect-metadata, javascript Reflect (1) | 2023.11.05 |
---|---|
[Typescript] Template Literal Types (0) | 2022.10.02 |
[Typescript] 타입은 값들의 집합 (0) | 2022.09.18 |