-
[Next.js + TS] 초기세팅 - (4) Babel 설정Front-End(Web)/React - 프레임워크(React, Next) 2021. 11. 2. 02:39반응형
Next.js + TS 초기세팅에 대한 글을 쓰면서, 마지막으로 Babel 세팅에 대한 내용을 정리하려고 하였으나 이전에 개념만으로도 글이 길어졌다.
* [Next.js + TS] 초기세팅 - (3) Babel 개념 : https://abangpa1ace.tistory.com/196
이번 포스팅에서는, Babel을 실제로 세팅하는 방법, 그리고 Next.js 및 TS환경을 좀 더 낫게 사용하기 위해 내가 적용했던 몇 가지 항목들을 설명해보고자 한다.
💛 Babel 과 Typescript
바벨을 설정하기 이전에, Typescript 환경에서 바벨을 쓰는 것의 목적? 의의? 를 이해하기 위한 선행학습이 필요했다.
사실, Typescript tsc(tsconfig.json) 역시 TS를 JS로 컴파일하는데 관한 설정을 담고 있다.
그런 반면, Babel 역시 JS버전 컴파일뿐만 아니라 TS의 트랜스파일링까지 관여할 수 있다는 관련글들을 보아왔다.
둘의 역할이 일부 중첩되는 것을 느꼈으며, 그렇다면 불필요한 이중작업이거나 혹은 설정충돌 등의 위험성이 없을까 생각하게 되었다.
- babel.config.js vs tsc
우선 놀라운 점은, babel과 tsc는 완벽하게 상관이 없다는 것이다!
Webpack에서 .ts파일을 처리하기 위해, 기본적으로 ts-loader가 요구된다.
하지만, 우리는 Babel을 사용하면서 babel-loader를 사용, 여기서 TS처리를 위한 @babel/preset-typescript 프리셋을 적용한다.
그렇기에, babel은 독자적으로 TS를 해석 및 처리하고, 여기에 tsconfig.json이 전혀 관여되지 않는 것이다.
- babel-loader : 타입체킹을 하지 않아 컴파일 속도가 빠르다. @babel/preset-typescript가 필요하며, 타입체크를 위한 플러그인이 필요함
- ts-loader : 타입체킹을 해서 컴파일 속도가 느리다. transpileOnly 옵션으로 속도를 높이거나, 타입체킹을 별도로 실행하는 등 방법을 사용하곤 한다.
- Using Babel with Typescript
그렇기에, Babel의 TS처리방식은 babel.config.json에서 별도로 설정하게 된다.
Typescript 공식문서 역시 사용조건에 따라 각각의 loader를 권장하나, 일정 규모이상의 프로젝트는 Babel과 tsc의 혼용을 추천한다.
- 소스 입력 파일과 빌드 출력 결과가 거의 비슷한가요? tsc
- 여러 잠재적인 결과물을 내는 빌드 파이프라인이 필요하신가요? babel로 트랜스파일링, tsc로 타입검사
위에서 두 번째 방법은, Babel의 preset-typescript로 JS파일을 생성한 후, tsc를 사용한 타입검사 및 .d.ts 파일을 생성하는 복합적인 접근방식이다.
Babel은 Typescript를 지원하기 때문에 기존 빌드 파이프라인으로 작업할 수 있을뿐만 아니라,
Babel이 코드타입을 검사하지 않기 때문에 JS 컴파일 시간이 빨라지며, tsc에서 별도로 타입체킹을 가져가는 방법인 것이다.
1) 파일이름 변경 : .js -> .ts
파일이 /src 디렉토리에 있다는 가정하에, 아래 커맨드 구문을 통해서도 바꿀 수 있다.
find src -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} ;
2) Babel에 Typescript 설정 추가
* 의존성 모듈
npm install --save-dev @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
* 설정파일 반영(babel.config.js)
{ "presets": [ "@babel/typescript" ], "plugins": [ "@babel/proposal-class-properties", "@babel/proposal-object-rest-spread" ] }
3) check-types 옵션 추가
* package.json 설정
"scripts": { "check-types": "tsc" }
이 명령은 단순히 Typescript 컴파일러(tsc)를 호출한다. 이를 Typescript 셋팅 시 설치해줘야 한다.
* tsconfig.json 설정
{ "compilerOptions": { // Target latest version of ECMAScript. "target": "esnext", // Search under node_modules for non-relative imports. "moduleResolution": "node", // Process & infer types from .js files. "allowJs": true, // Don't emit; allow Babel to transform files. "strict": true, // Disallow features that require cross-file information for emit. "isolatedModules": true, // Import non-ES modules as default imports. "esModuleInterop": true, "declaration": true, "emitDeclarationOnly": true, }, "include": [ "src" ] }
여기서 중요한 "compilerOptions"의 몇 가지 옵션들을 설명해보겠다.
- isolatedModules : 각각 소스코드 파일을 모듈화하며, Babel이 TS파일을 안전하게 트랜스파일 하는지 확인한다. Babel과 같은 외부도구를 사용할 때 true로 설정하는 것이 좋다.
- declaration : .ts 파일의 .d.ts 파일 또한 출력물에 포함된다.
- emitDeclarationOnly : declaration 파일만 출력물에 포함된다. noEmit 옵션과 같이 사용 불가하다.
💛 Babel 설정하기
* .bablerc 설정
{ "presets": ["next/babel"], "plugins": [ [ "styled-components", { "ssr": true, "displayName": true, "preprocess": false } ], [ "module-resolver", { "root": ["."], "alias": { "@": "./src" }, "extensions": [".js", ".jsx", ".tsx"] } ] ] }
- presets
필요한 모든 플러그인을 일일히 설정하는 것은 번거롭다. 그렇기에, 목적에 맞는 여러 플러그인을 번들링한 preset을 기본적으로 활용한다.
- preset-env : ES6 변환을 위해 주로 사용, 가장 많이 사용된다.
- preset-react : React 변환을 위해 주로 사용
- preset-typescript : Typescript 트랜스파일을 위해 주로 사용
* preset 목록 : https://babeljs.io/docs/en/presets
내 프로젝트에서는 Next.js 환경을 사용했으므로, 이에 적합한 ["next/babel"] 을 프리셋으로 설정했다.
- Plugins
코드를 변환해주는 각각의 feature들을 플러그인이라고 한다.
1) babel-plugin-styled-components
Next.js 환경에서 Styled Components를 사용할 때 발생하는 이슈때문에 적용하게 된 플러그인이다.
Styled Components로 각각 네이밍된 컴포넌트들에 hash된 class명이 할당되므로, 스타일이 완벽히 적용하기 전에 렌더가 되버리는 것이다.
그렇기에, Styled Components 스타일링을 SSR로 실행하는 플러그인 옵션을 설정해야 한다.
// 설치 yarn add -D babel-plugin-styled-components
// 적용 { "plugins": [ ..., [ "styled-components", { "ssr": true, // 서버 사이드 렌더링 옵션 "displayName": true, // tag 클래스명에 컴포넌트명이 접두사로 붙어 디버깅에 유리 "preprocess": false, // experimental feature라고 하며, 끄는 것이 일반적 } ], // 혹은 플러그인 자체로 설정하기도 한다. ["babel-plugin-styled-components"], ], }
* _document.tsx 설정
나도 프로젝트에서 빠트린 부분이다. (왠지, 리렌더링 시에 적용되지 않던 이슈가 있었는데, 이 부분이 원인이었던 듯 하다.)
_app.tsx 다음으로 실행되며, 통상 <Head> 웹 접근성이나 <body> 커스텀에 쓰이나, Styled-components 관련 설정도 필요하다.
import Document, { Html, Head, Main, NextScript, DocumentContext } from "next/document"; import { ServerStyleSheet } from "styled-components"; class MyDocument extends Document { static async getInitialProps(ctx: DocumentContext) { const sheet = new ServerStyleSheet(); const originalRenderPage = ctx.renderPage; try { ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />), }); const initialProps = await Document.getInitialProps(ctx); return { ...initialProps, styles: ( <> {initialProps.styles} {sheet.getStyleElement()} </> ), }; } finally { sheet.seal(); } } render() { return ( <Html> <Head> // 생략 </Head> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
- sheet는 ServerStyleSheet 사용을 위한 모듈 인스턴스, renderPage는 SSR 시 css-in-js 커스텀을 위한 모듈이다.
- enhanceApp 에서 react tree를 수집하면서, App 내 컴포넌트들의 모든 스타일을 collectStyles() 로 수집한다.
- 마지막에, initialProps와 함께 styles 프로퍼티에 getStyleElement() 를 커스텀해줘서 스타일 태그가 형성되게끔 한다.
2) babel-plugin-module-resolver
// Use this: import MyUtilFn from 'utils/MyUtilFn' // Instead of that: import MyUtilFn from '../../../../utils/MyUtilFn'
Module Resolver 를 사용하는 이유는 매우 간단하다. 위 예시처럼 절대경로를 사용해서, depth에서 올 수 있는 혼선가능성과 가독성을 개선하기 위해서이다.
// 설치 yarn add -D babel-plugin-module-resolver
// 적용 { "plugins": [ ..., [ "module-resolver", { "root": ["."], "alias": { "@": "./src" }, } ], ], }
- root : 모듈경로를 분석하는 기준(루트) 경로를 설정한다.(String or Array) 통상, "./" 혹은 "./src" 로 설정.
- alias : 특정 경로에 대해 별칭을 설정한다. assets 등 경로단축에도 많이 사용하나, 통상 루트나 src를 "@", "~" 등 특수문자로 축약하는 용도로도 사용됨.
- 이외에도, extensions(확장자), transformFunctions(자동완성 메서드) 등의 옵션들이 있다.
* tsconfig.json 추가 수정
Typescript 컴파일 시 절대경로 참고를 위해, 루트의 tsconfig.json에 baseUrl, paths를 설정해줘야 한다.
"@" alias가 루트(baseUrl, "./")의 src 폴더인 것을 알려줘야 한다. 그리고, include에 .ts, .tsx를 추가한다.
{ "compilerOptions": { // ... "baseUrl": ".", "paths": { "@": [ "src/*" ] }, "include": [ "next-env.d.ts", "**/*.ts", "**/*.tsx" ], }, }
* 기타 조치
- next.config.js : webpack(config) 옵션 추가 => config.resolve.modules.push(__dirname);
- Cache 초기화 : babel은 hot loader가 적용되지 않아 캐쉬설정을 초기화해야함 => yarn start --reset-cache
- vscode 에디터 재시작
Babel 설정파일 작성법을 끝으로 포스팅이 마무리되었다.
단순히, .babelrc 파일 작성법을 기록하고자 시작했던 포스팅이었지만, 생각보다 이전에 선행으로 고민되어야 할 부분이 많았다.
- Babel은 기본적으로 JS 컴파일을 위한 도구이다. 여기서 TS, SSR 등을 설정하는 방법뿐만 아니라 그 원리(의의)도 중요!
- Babel 설정법은 한가지가 아니다. 설정파일도 babel.config.js 와 .babelrc 가 존재하며, 이 차이에 대해 알아봐야했다.
- Babel 설정을 설명하기 위해 용어설명도 필수였다.(preset, plugin) 또한, tsc, _document.tsx 등 다른 컴포넌트와 설정파일의 추가설정도 이루어져야 했다.
- 절대경로의 경우, 내가 회사 프로젝트에서 경험해보고 매우 용이함을 느낀 부분이다. 필수적이진 않으나, 빠르고 효율적인 개발을 위해 복잡하더라도 루트지정과 alias(@) 설정은 매우 유용하다!
Next.js, Typescript 들은 더 나은 코드와 환경을 위해 현대 개발환경에서는 필수적으로 적용되는 부분들이다.
이들의 문법과 사용법을 익히는 것 못지 않게, 환경을 구성하는 설정을 이해하고 더 나은 추가설정을 진행할 수 있는 부분이 향후 리드 개발자가 되었을 때, 개발자들이 일하기 좋은 레포지토리를 만드는 시발점이라 생각한다.
이번 프로젝트를 (끝까지 완주하진 못했으나) 준비 및 진행하면서,
Next.js 및 TS 사용법을 익힌 것보다, 동기들에게 더 나은 환경을 구성해주기 위해 직접 설정을 해본 경험이 더 소중함을 느꼈다.
[참고]
- [Next.js + TS + Styled] danmin20 님의 블로그 : https://velog.io/@danmin20/Next.js-Typescript-Styled-component-%EC%89%BD%EA%B2%8C-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0
- [babel-loder & ts-loader] 우아한 테크캠프 Github 블로그 : https://github-wiki-see.page/m/woowa-techcamp-2021/store-4/wiki/babel-loader-%26-%40babel-preset-typescript-vs-ts-loader-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%A0%95%EB%A6%AC
- [Babel with Typescript] TOAST UI : https://ui.toast.com/weekly-pick/ko_20181220
- [ServerStyleSheet] swlh 님의 블로그 : https://medium.com/swlh/server-side-rendering-styled-components-with-nextjs-1db1353e915e
- [절대경로 설정] react-native-seoul 님의 블로그 : https://medium.com/react-native-seoul/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-module-resolver-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-b28f607fd0bb
반응형'Front-End(Web) > React - 프레임워크(React, Next)' 카테고리의 다른 글
[Craco] Craco 란? (0) 2022.01.18 [React Router/lib.] React Router v6 (0) 2021.12.14 [Next.js + TS] 초기세팅 - (3) Babel 개념 (0) 2021.10.22 [Next.js + TS] 초기세팅 - (2) ESLint, Prettier (0) 2021.10.14 [Next.js + TS] 초기세팅 - (1) CRA, tsconfig.json (0) 2021.10.11