티스토리 뷰

728x90
SMALL

바벨

 웹개발자라면 최신 기술을 사용하고 싶다. 그런데 최신 기술을 지원하지 않는 브라우저(ie라던가... ie라던가... ie...)가 존재한다. 엄청난 최신 기술이 아니라 나온지 오~~래된 기술도 사용할 수 없는 브라우저들이 있다. 이런 경우에 어떻게 해야할까?? 옛날 기술을 사용한다... 라는 방법밖에 없었다. 없었다.. 라는 것은 지금은 아니라는 것인데 그 이유가 바로 바벨이다. 바벨은 자바스크립트 컴파일러인데 ECMAScript 2015+코드를 이전의 버전으로 바꿔주는 역할을 한다. 이 때문에 지금은 최신 문법을 간편하게 사용할 수 있는 것이다. 물론 우리가 설정을 해줘야 한다. 리액트 cra같은 경우는 이미 설정이 되어있어서 적용이 되는 것이고 바닐라 자바스크립트로 최신 문법을 사용하려면 설정을 해줘야 한다. 생각보다 설정은 어렵지 않다. 설정파일은 .babelrc 또는 babel.config.json 파일로 하면된다. 

또한 폴리필 설정도 바벨에서 해준다.(core-js) 폴리필은 밑에서 설명...

 

설정 파일 예시

target 부분만 간단히 말하면 1%이상의 브라우저에 지원을 하게 만들어달라는 것이다.

만약 주석부분의 browsers옵션을 넣으면 최신 몇번째 버전까지 지원을 하는지 할 수 있고, ie 적용을 할 수도 있다.

설정은 절대적인게 아니라 프로젝트에 따라 적절히 교체하자

ts를 사용하지 않는다면 ts만 빼면 된다.(타입스크립트와 jsx까지 바벨이 교환을 해준다.)

{
  "presets": [
    "@babel/preset-typescript",
    [
      "@babel/preset-env",
      {
        "targets": {
          "targets" : "> 1%, not dead"
          // sourceMap을 통해 컴파일 이전 상태에서 디버깅 하고 싶을 때 사용.
          // "browsers": ["last 2 versions"], 
        },
        "useBuiltIns": "usage",
        "corejs": { "version": 3, "proposals": true }
      }
    ]
  ],
  "plugins": []
}

 

버전 및 npm

    "@babel/preset-env": "^7.14.7",
    "@babel/preset-typescript": "^7.14.5",
    "babel-cli": "^6.26.0",

https://babeljs.io/docs/en/babel-preset-env

 

Babel · The compiler for next generation JavaScript

The compiler for next generation JavaScript

babeljs.io

 

웹펙

 이제 바벨을 왜 사용하는지 알았다. 그러면 웹펙은 왜 사용할까?? 이미 최신 기술을 사용할 수 있게 되었는데.. 일단 모듈화에 대해 알아야한다. 예전에는 script src로 여러개의 자바스크립트 파일들을 불러와서 자바스크립트를 실행했다. 이렇게 되면 가장 먼저 생각할 수 있는 문제점은 전역 변수다. 이를 막기위해서 즉시실행함수를 사용하기도 했었다. 어쨋든 이런 문제점을 해결하기 위해서 다양한 모듈이 등장하기 시작한다. 그리고 브라우저에서도 모듈을 지원하기 시작했다. 하지만... 역시나... 모듈을 지원하지 않는 브라우저가 존재한다. 그래서 웹펙이 등장하기 시작한다. 웹펙은 한마디로 모듈 번들러다. 즉 모듈을 합쳐준다는 말이다. 하나의 시작점으로부터 의존적인 모듈들을 모두 찾아내서 하나의 결과물을 만들어 낸다.

웹팩에는 mode, entry, output, resolve, module, plugin, devtool, devserver옵션이 있다.(더있는데 일단은 이정도만)

mode에 development와 production모드가 있다. 말그대로 개발용과 배포용이 따로 있는 것이다.

entry는 시작점의 경로, output은 결과물의 경로다.

resolve는 알아서 경로나 확장자를 처리할 수 있게 도와주는 옵션이다.

module에는 로더 속성을 넣는다. 로더는 타입스크립트 같은 다른 언어를 자바스크립트 문법으로 변환해 주거나 이미지를 data URL 형식의 문자열로 변환한다. 뿐만아니라 CSS 파일을 자바스크립트에서 직접 로딩할수 있도록 해준다.(김정환님 블로그)

plugin은 번들링된 결과물을 처리한다. 아래 코드에 주석을 보면 알 수 있다. module은 파일 단위로 처리한다는 것이 다르다.

 

예시 파일

webpack.config.js

import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';

const __dirname = path.resolve();
const mode = process.env.NODE_ENV || 'development'; // 기본값을 development로 설정

export default () => {
  return {
    mode: mode,
    entry: './src/app.ts',
    output: {
      filename: '[name].js',
      path: path.resolve(__dirname, 'dist'),
    },
    resolve: {
      modules: ['node_modules'],
      extensions: ['.js', '.ts'],
    },
    module: {
      rules: [
        {
          test: /\.(png|jpg|jpeg|gif|svg)$/i,
          use: {
            loader: 'file-loader',
            options: {
              name: 'assets/[name].[ext]?[hash]',
            },
          },
        },
        {
          test: /\.(js|ts)$/, // .js, .ts 확장자로 끝나는 모든 파일
          use: {
            loader: 'babel-loader', // babel-loader 적용
            options: {
              presets: ['@babel/preset-env', '@babel/preset-typescript'],
              plugins: [
                '@babel/proposal-class-properties',
                '@babel/proposal-object-rest-spread',
              ],
            },
          },
          exclude: /(node_modules)/,
        },
        {
          test: /\.s[ac]ss$/i,
          use: [
            mode !== 'production'
              ? 'style-loader'
              : MiniCssExtractPlugin.loader, // style-loader : 자바스크립트로 변경된 스타일을 동적으로 돔에 추가하는 로더, min~~로더 : 파일 쪼개기
            'css-loader', // CSS 파일을 모듈처럼 불러와 사용할 수 있게 도와줌
            'sass-loader', // sass 사용
          ],
          exclude: /node_modules/,
        },
      ],
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: './src/index.html',
      }), // HTML 파일을 후처리하는데 사용, 빌드 타임의 값을 넣거나 코드를 압축
      new CleanWebpackPlugin(), // 빌드 이전 결과물을 제거하는 플러그인
      new MiniCssExtractPlugin({
        // 컴파일 + 번들링 CSS 파일이 저장될 경로와 이름 지정
        // Options similar to the same options in webpackOptions.output
        // both options are optional
        filename: '[name].css',
      }),
    ],
    optimization: {
      minimizer:
        mode === 'production'
          ? [
              new OptimizeCSSAssetsPlugin(), // css 파일도 빈칸을 없애는 압축 (css-minimizer-webpack-plugin)도 유사
              new TerserPlugin({
                //  자바스크립트 코드를 난독화하고 debugger 구문을 제거
                terserOptions: {
                  compress: {
                    drop_console: true, // 콘솔 로그를 제거한다
                  },
                },
              }),
            ]
          : [],
    },
    devtool: mode === 'production' ? 'source-map' : 'inline-source-map',
    devServer: {
      contentBase: path.resolve(__dirname + '/dist'),
      index: 'index.html',
      port: 9000,
      writeToDisk: true,
      hot: true,
      proxy: {
        '/api/': {
          // /api/로 시작하는 url은 아래의 전체 도메인을 추가하고, 옵션을 적용
          target: 'http://localhost:3000', // 클라이언트에서 api로 보내는 요청은 주소를 3000으로 변경
          changeOrigin: true, // cross origin 허용 설정
        },
      },
    },
  };
};

 

개발모드 추가

import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';

const __dirname = path.resolve();
const mode = process.env.NODE_ENV || 'development'; // 기본값을 development로 설정

export default () => {
  return {
    mode: mode,
    entry: './src/app.ts',
    output: {
      filename: '[name].js',
      path: path.resolve(__dirname, 'dist'),
    },
    resolve: {
      modules: ['node_modules'],
      extensions: ['.js', '.ts'],
    },
    module: {
      rules: [
        {
          test: /\.(png|jpg|jpeg|gif|svg)$/i,
          use: {
            loader: 'file-loader',
            options: {
              name: 'assets/[name].[ext]?[hash]',
            },
          },
        },
        {
          test: /\.(js|ts)$/, // .js, .ts 확장자로 끝나는 모든 파일
          use: {
            loader: 'babel-loader', // babel-loader 적용
            options: {
              presets: ['@babel/preset-env', '@babel/preset-typescript'],
              plugins: [
                '@babel/proposal-class-properties',
                '@babel/proposal-object-rest-spread',
              ],
            },
          },
          exclude: /(node_modules)/,
        },
        {
          test: /\.s[ac]ss$/i,
          use: [
            mode !== 'production'
              ? 'style-loader'
              : MiniCssExtractPlugin.loader, // style-loader : 자바스크립트로 변경된 스타일을 동적으로 돔에 추가하는 로더, min~~로더 : 파일 쪼개기
            'css-loader', // CSS 파일을 모듈처럼 불러와 사용할 수 있게 도와줌
            'sass-loader', // sass 사용
          ],
          exclude: /node_modules/,
        },
      ],
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: './src/index.html',
      }), // HTML 파일을 후처리하는데 사용, 빌드 타임의 값을 넣거나 코드를 압축
      new CleanWebpackPlugin(), // 빌드 이전 결과물을 제거하는 플러그인
      new MiniCssExtractPlugin({
        // 컴파일 + 번들링 CSS 파일이 저장될 경로와 이름 지정
        // Options similar to the same options in webpackOptions.output
        // both options are optional
        filename: '[name].css',
      }),
    ],
    optimization: {
      minimizer:
        mode === 'production'
          ? [
              new OptimizeCSSAssetsPlugin(), // css 파일도 빈칸을 없애는 압축 (css-minimizer-webpack-plugin)도 유사
              new TerserPlugin({
                //  자바스크립트 코드를 난독화하고 debugger 구문을 제거
                terserOptions: {
                  compress: {
                    drop_console: true, // 콘솔 로그를 제거한다
                  },
                },
              }),
            ]
          : [],
    },
    devtool: mode === 'production' ? 'source-map' : 'inline-source-map',
    devServer: {
      contentBase: path.resolve(__dirname + '/dist'),
      index: 'index.html',
      port: 9000,
      writeToDisk: true,
      hot: true,
      proxy: {
        '/api/': {
          // /api/로 시작하는 url은 아래의 전체 도메인을 추가하고, 옵션을 적용
          target: 'http://localhost:3000', // 클라이언트에서 api로 보내는 요청은 주소를 3000으로 변경
          changeOrigin: true, // cross origin 허용 설정
        },
      },
    },
  };
};

 

참고 블로그

https://jeonghwan-kim.github.io/series/2019/12/10/frontend-dev-env-webpack-basic.html

 

프론트엔드 개발환경의 이해: 웹팩(기본)

1. 배경 먼저 모듈에 대해 이야기 해보자. 문법 수준에서 모듈을 지원하기 시작한 것은 ES2015부터다. import/export 구문이 없었던 모듈 이전 상황을 살펴보는 것이 웹팩 등장 배경을 설명하는데 수월

jeonghwan-kim.github.io

 

폴리필

폴리필은 웹 개발에서 기능을 지원하지 않는 웹 브라우저 상의 기능을 구현하는 코드를 말한다. 예를들면 map을 지원하지 않는 브라우저에서 다른 방법으로 구현하는 것을 말한다. mdn 사이트를 보면 대부분 폴리필이 작성되어 있다. 이를 보고 코드를 이해하는 것도 좋은 방법이다.

728x90
LIST
댓글
공지사항