header wave

Post

TS) Babel & Webpack

2022-10-13 AM 11/12
#sourcemap
#linter
#webpack

타입스크립트는 문법도 훌륭하지만, 타입스크립트는 IDE에서 타입 오류의 수정과 리팩토링을 편하게 만들어주는 자동완성 기능이 매우 효과적이다.

타입스크립트를 설치하면 bin 디렉터리에 tsc 및 tsserver라는 두 파일이 생긴다.

tsserver 파일은 IDE의 생산성을 돕는 타입스크립트 언어 지원 기능이 있다.

타입스크립트 코드를 입력할 때, IDE는 메모리에 컴파일하는 tsserver와 통신한다.

소스맵 파일의 도움으로 브라우제서 직접 타입스크립트 코드를 디버깅 할 수 있다. 린터를 사용하면 정해진 코딩 스타일 그대로 코드를 깔끔하게 작성할 수 있다.

소스맵

웹 브라우저나 자바스크립트 엔진에서는 자바스크립트가 동작하기 때문에 타입스크립트 코드를 컴파일해야 한다. 어느 프로그램이든 소스 코드를 디버거에 제공해야 한다. 소스맵 파일을 사용하면 타입스크립트 코드를 디버깅할 수 있다.

소스맵 생성 커맨드)

npx tsc greeter.ts --sourcemap true                                                                                  

트랜스파일 전 ts 파일이 있어야 디버깅 할 수 있다.

소스맵 예)

{
  "version": 3,
  "file": "greeter.js",
  "sourceRoot": "",
  "sources": [
    "greeter.ts"
  ],
  "names": [],
  "mappings": "AAAA;IAAA;IAIA,CAAC;IAHQ,gBAAQ,GAAf,UAAgB,IAAY;QAC1B,OAAO,CAAC,GAAG,CAAC,aAAM,IAAI,CAAE,CAAC,CAAC;IAC5B,CAAC;IACH,cAAC;AAAD,CAAC,AAJD,IAIC;AAED,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC"
}

소스맵 파일은 .map 확장자로 자바스크립트 코드 조각을 원래 언어에 맵핑하는 json 형식의데이터를 포함하고 있다. 타입스크립트로 작성된 자바스크립트 프로그램을 디버깅하면, 브라우자가 컴파일 중에 생성된 소스맵 파일을 다운로드 하며, 엔진이 자바스크립트르 실행하더라도, 타입스크립트 코드 내 브레이크 포인트를 설정할 수 있다.

inlineSources 옵션을 true로 설정하면 .js.map 파일 내에 타입스크립트 코드가 포함된다.

npx tsc greeter.ts --sourcemap true --inlineSources true                                                                                      

소스맵 파일 내에 트랜스 파일 전 타입스크립트 코드가 포함되므로, 타입스크립트 파일이 없어도 디버깅이 가능하다.

배포 서버에 js.map 파일을 배포해도 브라우저에서 다운받은 코드 크기는 증가하지 않는다. 브라우저는 사용자가 개발 도구를 열 경우에만 소스맵 파일을 다운로드 한다.

만약 사용자가 타입스크립트 소스 코드를 읽지 못하게 하려는 경우 배포 서버에 소스맵 파일을 배포하면 안된다.

ESLint

린터란 일정한 코드 스타일을 유지하도록 도와주는 도구이다. 모든 파일에서 홑 따옴표만 사용 가능하며 불필요한 괄호를 제거하는 등 코딩 스타일 관련 규칙을 만들 수 있다.

npm init -y

npm install typescript eslint

npx eslint --init

위의 설정을 완료하면 .eslintrc.json 파일이 생성된다.

{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "overrides": [
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest"
    },
    "plugins": [
        "@typescript-eslint"
    ],
    "rules": {
    }
}

“rules” 내에 커스텀 룰을 지정해서 사용할 수 있다.

사용가능한 룰들은node_modules/@typescript-eslint/eslint-plugin/dist/configs/recommended.js 에 위치한다.


off 또는 0 : 규칙을 미적용

warn 또는 1 : 규칙에 어긋날 경우 경고 발생

error 또는 2 : 규칙에 어긋날 경우 오류 발생


웹팩을 사용한 번들링

브라우저는 서버에 요청을 보내, CSS, 이미지, 비디오 등 파일이 포함된 HTML 문서를 가져온다.

만약 모든 파일을 각각 배포한다면, 브라우저는 웹 사이트 하나를 보기 위해 요청을 수백 번 이상 보내야 하며 전체 용량도 수메가바이트 이상이 될 수도 있다.

실제 애플리케이션은 수백, 수천 개의 파일로 구성되어 있고 최소화, 최적화, 번들링 과정을 거쳐 최종 배포된다.

여러 파일을 하나로 묶으면 다운로드 속도가 빨라지고 성능도 높아진다.

tsc는 .ts 파일에서 생성된 코드를 단일 파일로 만들 수 있다. module 컴파일러 옵션이 System 이거나 AMD 인 경우에만 가능하다. 출력 파일에는 자바스크립트 라이브러리 등 의존성이 포함되지 않는다.

개발 및 프로덕션 배포 및 빌드 환경을 다르게 구성할 수 있다. 배포 단계에서 최적화와 최소화를 추가하고 개발 단계에서 파일을 묶을 수 있다.

이전에는 Grunt, Gulp 같은 빌드 도구를 사용했지만, 지금은 Webpack, Rollup, Browserify 같은 도구를 사용한다.

wepack4 -> webpack5로 버전업해서 튜토리얼을 진행했다!

npm install webpack webpack-cli webpack-dev-server -D 
// package.json
{
  "name": "webpack-javascript",
  "description": "Code sample for the TypeScript Quickly book",
  "homepage": "https://www.manning.com/books/typescript-quickly",
  "license": "MIT",
  "scripts": {
		"start": "npx webpack-dev-server",
    "bundleup": "npx webpack-cli"
  },
  "dependencies": {
    "chalk": "^2.4.1"
  },
  "devDependencies": {
    "webpack": "^4.46.0",
    "webpack-cli": "^3.3.12"
  }
}
// webpack.config.js

const { resolve } = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "index.bundle.js",
    path: resolve(__dirname, "dist"),
  },
  target: "node", // do not inline built-in Node.js modules (e.g. os, path, crypto, etc.)
  mode: "production", // optimize the file size of the output bundle
  devServer: {
    // to set default path in webpack5
    static: {
      directory: ".",
    },
  },
};{
  "name": "03",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundleup": "npx webpack-cli",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "ts-loader": "^9.4.1",
    "typescript": "^4.8.4",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  },
  "dependencies": {
    "chalk": "^5.1.0"
  }
}
// server.js

const Webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server");
const webpackConfig = require("./webpack.config.js");

const compiler = Webpack(webpackConfig);
const devServerOptions = { ...webpackConfig.devServer, open: true };
const server = new WebpackDevServer(devServerOptions, compiler);

const runServer = async () => {
  console.log("Starting server...");
  await server.start();
};

runServer();
// index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="dist/index.bundle.js"></script>
  </head>
  <body></body>
</html>

웹팩을 사용한 타입스크립트 번들링

ts-loader , typescript 가 필요하다.

일반적으로 빌드 자동화 도구는 빌드 프로세스 중에 필요한 추가 작업을 제공한다.

웹팩은 빌드를 커스터마이징 할 수 있는 로더와 플러그인을 제공한다.

웹팩 로더는 한 번에 한 파일만 처리하지만 플러그인은 파일 그룹을 처리할 수 있다.

웹팩 로더는 소스 파일을 입력으로 사용하고 다른 파일을 출력(메모리 또는 디스크)으로 생성하는 변환기다.

ex) json-loader는 입력 파일을 가져와서 JSON으로 구문분석 한다.

타입스크립트를 자바스크립트로 변환하기 위해 ts-loader를 추가하며, tsconfig.json을 추가해서 tsc가 컴파일 할 수 있도록 한다.

// tsconfig.json

{
  "compilerOptions": {
    "target": "es2018",
    "moduleResolution": "node"
  }
}

moduleResolution 옵션은 import 구문이 있는 경우 tsc 모듈을 해결하는 방법을 알려준다.

import {a} from “moduleA” 구문이 있다면 tsc는 moduleA가 있는 위치를 알아야 한다.

모듈해석(module resolution) 에는 Classic과 Node 두 방법이 있다.

  • Class 해석 방식
    • tsc가 moduleA.ts에서 moduleA의 정의를 찾고, moduleA.d.ts 에서 타입 정의를 찾는다.
  • Node 해석 방식
    • node_modules 디렉토리에 모듈을 찾는다.

// webpack.config.js

const { resolve } = require("path");

module.exports = {
  entry: "./src/index.ts",
  output: {
    filename: "index.bundle.js",
    path: resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      // 모듈 규칙(로더 구성, 파서 옵션 등등)
      {
        test: /\.ts$/, // 확장명이 ts인 파일에 적용
        exclude: /node_modules/, // node_modules 내 ts파일은 무시한다.
        use: "ts-loader", // 기존 tsconfig.json 옵션을 사용해 타입스크립트를 컴파일 한다.
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".js"], // Add .ts extension to the resolve config to be able to import TS files in your source code.
  },
  target: "node", // do not inline built-in modules (e.g. os, path, crypto, etc.)
  mode: "production", // optimize the file size of the output bundle
  devServer: {
    // to set default path in webpack5
    static: {
      directory: ".",
    },
  },
};

현재는 rules에 로더가 하나지만, 여러 로더를 추가할 수 있다.

ex) css-loaders는 css 파일을 처리하며, file-loader은 이미지 파일 등을 처리하기 위해 import/require 구문을 url로 해석해 번들로 보내는 역할을 한다.

웹팩 플러그인

웹팩 로더가 한 번에 하나씩 파일을 변환하는 경우, 플러그인은 모든 파일에 액세스 할 수 있으며, 로더가 시작되기 전 후에 파일을 처리할 수 있다.  


예를 들어 SplitChunksPlugin 플러그인을 사용하면 번들을 개별 청크 단위로 나눌 수 있다.  


만약 앱이 main, admin이라는 두개의 모듈로 나뉘어져 있고 각 모듈마다 번들을 만들려 한다면?  


이 모듈은 앵귤러 프레임워크를 사용하고 있다. 두 엔트리 포인트(main 및 admin)를 지정하면, 각 번들에는 앵귤러 코드와 애플리케이션 코드가 모두 포함된다. (`중복` )


이를 방지하기 위해 SplitChunksPlugin 플러그 인을 사용해 웹팩이 두 모듈의 앵귤러 코드를 걷어내고, 별도로 서로 공유 가능한 앵귤러 번들을 만든다.


UglyfyJSPlugin 플러그인은 변환된 모든 파일의 코드를 압축한다.(가장 널리 사용되는 자바스크립트 압축 & 최적화 플러그인)


webpack.config.js 파일 내 “mode”로 dev, pro등 모드를 설정할 수 있다.

바벨 트랜스파일러

모든 브라우저가 ECMA 스크립트의 모든 기능을 지원하지 않기에 발생하는 크로스 브라우징 이슈를 해결해주는 자바스크립트 트랜스파일러가 바벨이다.

npm install -D @babel/core @babel/cli @babel/preset-env
  • @babel/core 바벨 트랜스파일러
  • @babel/cli 커맨드 라인 인터페이스
  • @babel/preset-env 프리셋 패키지
// package.json 파일에서 babel 커맨드를 생성한 것임

{
  "name": "03",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "babel": "npx babel src -d dist"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.19.3",
    "@babel/core": "^7.19.3",
    "@babel/preset-env": "^7.19.4"
  },
  "dependencies": {
    "chalk": "^5.1.0"
  }
}

// .babelrc

{
  "presets": ["@babel/preset-env"]
}

@babel/preset-env는 @babel/preset-ES6, @babel/preset-es2016, @babel/preset-es2017과 정확히 동일하다. 즉 자바스크립트 버전을 나타내는 것임

browserslist 파일을 통해 브라우저 버전을 설정할 수 있음.

// .browserslistrc

last 2 chromes versions
last 2 firefox versions

TS와 바벨을 함께 쓰려면

npm install -D @babel/preset-typescript

일반적으로 프리셋은 여러 플러그인으로 구성되지만, preset-typescript는 @babel/plugin-transform-typescript 한가지로 구성된다.

@babel/plugin-transform-typescript 는 타입스크립트 문법 플러그인 @babel/plugin-syntax-typescript과 플러그인 유틸 헬퍼 함수가 있는 @babel/helper-plugin-utils 사용해 타입스크립트를 파싱한다.

@babel/plugin-transform-typescript 는 타입스크립트를 자바스크립트 코드로 변환하지만, 타입스크립트 컴파일러는 아니다.

바벨은 타입스크립트 코드를 지운다. const x : number = 10; 을 const x = 10으로 바꾼다.

타입 검사를 하지 않기 때문에 타입스크립트 컴파일러보다 훨씬 더 빠르다.

💡 @babel/plugin-transform-typescript 가 지원하지 않는 기능도 있다. 참고

💡 PROCESS
타입스크립트 → 타입검사(tsc) → 타입스크립트 코드 → 자바스크립트(@babel/preset-typescript)

타입스크립트 → 타입검사(tsc) → 타입스크립트 코드 → 자바스크립트(@babel/preset-typescript)

타입검사를 바벨 트랜스파일에 추가하려면

// package.json

"check_types" : "tsc --noEmit src/index.ts"
npm run check_types && npm run babel

(두 npm 스크립트 사이에 && 키워드를 추가하면 순차적으로 실행, &는 병렬적으로 실행)

타입스크립트 + 바벨 + 웹팩

{
  "name": "03",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundleup": "npx webpack-cli"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.19.3",
    "@babel/preset-env": "^7.19.4",
    "@babel/preset-typescript": "^7.18.6",
    "babel-loader": "^8.2.5", // 바벨로더를 추가해준다.
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  },
  "dependencies": {
    "chalk": "^5.1.0"
  }
}

// webpack.config.js

const { resolve } = require("path");

module.exports = {
  entry: "./src/index.ts",
  output: {
    filename: "index.bundle.js",
    path: resolve(__dirname, "dist"),
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: "babel-loader", // webpack의 ts-loader가 타입스크립프 파일을 읽는 것이 아니라 babel-loader가 타입스크립트 파일을 읽는다.
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".js"], // Add .ts extension to the resolve config to be able to import TS files in your source code.
  },
  target: "node", // do not inline built-in modules (e.g. os, path, crypto, etc.)
  mode: "production", // optimize the file size of the output bundle
};