코드스테이츠_S4U3 _2W_수,목

윤뿔소·2022년 11월 23일
0

CodeStates

목록 보기
38/47
post-thumbnail

이번 유닛은 용량을 줄이거나 파일을 최소화하여 유저에게 전달하는 과정을 배워 번들링해보자!
create-react-app도 webpack을 사용한다!

번들링

여러 제품이나, 코드, 프로그램을 묶어서 패키지로 제공하는 행위
프론트엔드 개발자의 번들링이란 사용자에게 웹 애플리케이션을 제공하기 위한 파일 묶음

번들링을 하기 전 원시적인 웹을 불러올 때 다양한 위험들이 있었다.

  • 두 개의 .js 파일에서 같은 변수를 사용하고 있어서, 변수 간 충돌이 일어났다.
  • 딱 한 번 불러오는 프레임워크 코드가 8MB라서, 인터넷 속도가 느린 국가의 모바일 환경에서 사용자가 불편을 호소한다.
  • 번들 파일 사이즈를 줄이기 위해서 파일의 공백을 모두 지웠는데, 가독성이 너무 떨어져서 코딩하기가 어렵다. 결국 그대로 공백을 되돌려서 코딩해야했다.
  • 배포 코드가 너무 읽기 쉬워 개발을 할 줄 아는 사용자가 프론트엔드 애플리케이션을 임의로 조작하여 피해가 발생했다.

다른 모든 SW 결과물들이 가지는 문제점들이다. 특히 게임에 많이 힘들었는데 패미컴 시절 한 게임에 40KB에 담아야 했기에 용량에 대한 효율을 극대화 했어야 했다. 웹에서도 마찬가지로 그런 문제들을 겪었다.

참고: 번들러 종류와 점유율

해외는 연말에 여행을 가고 쉬고 그래서 저렇게 드랍이 발생한다. 우리나라는,,,ㅠㅠ

Webpack

웹 프론트엔드에서 가장 많이 쓰는 모듈 번들러이다. 요즘은 Node.js에서도 많이 쓰는 추세다. 한글 번역에 자료도 많다! 꼭 공식 사이트 가서 봐보자

왜?! 번들링을 따로 해주는 이유는 웹 애플리케이션의 빠른 로딩 속도와 높은 성능을 위해서!

모듈 번들러란?

HTML, CSS, JavaScript 등의 자원을 전부 각각의 모듈로 보고 이를 조합해 하나의 묶음으로 번들링(빌드)하는 도구

  • 모던 웹으로 발전하며 JS 코드의 양이 절대적으로 많이 증가
  • 또 대규모의 의존성 트리를 가지고 있는 대형 웹 애플리케이션이 등장하므로써 세분화된 모듈 파일이 폭발적으로 증가
  • JS 언어의 특성에 따라 발생하기 쉬운 각 변수들의 스코프 문제를 해결해야 하고, 각 자원을 호출할 때에 생겨나는 네트워크 쪽의 코스트도 신경

그래서 이런 복잡성에 대응하기 위해 하나의 시작점(Ex. React App의 index.js 등)으로부터 의존성을 가지는 모듈을 모두 추적하여 하나의 결과물을 만들어내는 모듈 번들러가 등장!

웹팩 개념

핵심 개념으로 Target, Entry, Output, Loaders, Plugins, Mode, Browser Compatibility가 있다. 알아두면 웹팩에 대해서 잘 알 수 있을 것이다!

웹팩 config 예시자료

위에서부터 설명 해보겠다.

module.exports = {
  name: 'webpack-setting',
  mode: 'development', // 실서비스 : production
  devtool: 'eval', // 빠르게
  target: ["web", "es5"],
  entry: "./src/script.js",
  output: {
    path: path.resolve(__dirname, "docs"),
    filename: "app.bundle.js",
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src", "index.html"),
    }),
    new MiniCssExtractPlugin(),
  ],
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(),
    ]
  }
};
  • Target: 다양한 환경과 target을 컴파일, 기본값은 web

    • target: ["web", "es5"]: 브라우저와 동일한 환경에서 사용, 작성된 코드를 es5 버전으로 컴파일하겠다고 지정
    • esX를 넣으면 지정된 ECMAScript 버전으로 컴파일 가능! 다양한 환경 컴파일 가능
  • Entry: 입구, 코드의 “시작점", 리액트에선 index.js, 내부의 디펜던시 그래프를 생성하기 위해 사용해야 하는 모듈

    • entry: "./src/index.js": 기본값, script.js로 지정 가능하다!
  • Output: 출력, 생성된 번들을 내보낼 위치와 이 파일의 이름을 지정하는 방법을 웹팩에 알림

    output: {
      path: path.resolve(__dirname, "docs"), // 절대 경로로 설정을 해야 합니다. 
      filename: "app.bundle.js",
      clean: true
    }
    • 위의 예제에선 output.filenameoutput.path 속성(path 모듈을 사용 必)을 사용해서 번들 이름과 위치를 알려줌
    • 기본 출력 파일의 경우에는 ./dist/main.js, 폴더는 ./dist
  • Loaders: 로더, 웹팩은 JS와 JSON 파일만 이해해서 다른 유형의 파일을 처리하거나 모듈로 변환해 앱에 사용하거나 디펜던시 그래프에 사용한다!

    module: {
      rules: [
        {
          test: /\.css$/,
          use: [MiniCssExtractPlugin.loader, "css-loader"],
          exclude: /node_modules/,
        },
      ],
    },
    • test: 변환이 필요한 파일들을 식별하기 위한 속성(필수)
    • use: 변환을 수행하는데 사용되는 로더를 가리키는 속성(필수)
    • exclude: 바벨로 컴파일하지 않을 파일이나 폴더를 지정. (반대로 include 속성을 이용해 반드시 컴파일해야 할 파일이나 폴더 지정 가능)
    • 꼭꼭 module.rules 아래에 정의
  • Plugins: 번들을 최적화하거나 에셋을 관리하고, 또는 환경변수 주입 등의 광범위한 작업을 수행

    const webpack = require('webpack');
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    module.exports = {
      ...
      plugins: [
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, "src", "index.html"),
        }),
        new MiniCssExtractPlugin(),
      ],
    };
    1. require()를 통해 플러그인을 먼저 요청
    2. plugins 배열에 사용하고자 하는 플러그인을 추가
    3. new 연산자를 사용해 호출하여 플러그인의 인스턴스 만들기
    • html-webpack-plugin: 생성된 모든 번들을 자동으로 삽입하여 애플리케이션용 HTML 파일을 생성
    • mini-css-extract-plugin: CSS를 별도의 파일로 추출해 CSS를 포함하는 JS 파일 당 CSS 파일을 작성해주게끔 지원
  • Optimization: 버전 4부터 선택한 항목에 따라 최적화를 실행

    optimization: {
      // mini-css-extract-plugin 에 관련된 번들을 최소화하도록 지시
      minimizer: [
        new CssMinimizerPlugin(),
      ]
    }
    • minimize : TerserPlugin 또는 optimization.minimize에 명시된 plugins로 bundle 파일을 최소화(=압축)시키는 작업 여부를 결정
    • minimizer : defualt minimizer을 커스텀된 TerserPlugin 인스턴스를 제공해서 재정의 가능

사용법

실습을 거치며 사용법을 익혀보자..
번들링을 원하는 파일의 위치를 entry, 번들링의 결과물을 output

1. 웹팩 설치하기

npm install --save-dev webpack webpack-cli webpack-dev-server

npm run dev로 웹팩 실행

2. 웹팩하고싶은 프로젝트의 /rootwebpack.config.js 작성

위에 봤던 웹팩 개념들을 토대로 설정 쌓기, entryoutput 정보 입력

const path = require('path');

module.exports = {
  // 내 과제 마이아고라스테이츠에서는 "./src/script.js"이게 메인이라서 바꿈
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'), // './dist'의 절대 경로를 리턴
    filename: 'app.bundle.js',
  },
};

3. 번들링 하기

기본적으로 npx에 같이 입력하거나 위 같이 webpack.config.js에 작성된 내용을 토대로 나온다!

npx webpack (--entry 메인파일명 --output 웹팩된 내용 담는 폴더명)

# 예
npx webpack --entry ./source/index.js --output . /public/index_bundle.js

입력 후 아웃풋에 잘 됐는지 보기 위해 경로도 app.bundle.js로 바꾸기!

4. npm run build 설정하기

지금까지 여정을 다른 개발자에게도 알리기위해 JSON에 입력해줘야한다!

"scripts": {
  "build": "webpack",
  "test": "echo \"Error: no test specified\" && exit 1"
},

이제 npm run build 스크립트로 언제든 번들링 가능!
나중에 js 파일이 수정이 된다면 위 명령어로 꼭 다시 번들링 해줘야한다!

5. 로더를 추가해 번들링하기

다양한 파일을 로더로 변환 가능하다!
그냥 CSS 파일을 가져올 수 없기에 로더로 가져와야한다.
로더모듈인 css-loader 는 CSS를 JS파일 내에서 불러올 수 있고, style-loader 는 불러온 CSS를 style 요소 내에 담아준다.(sass는 따로 'sass-loader'로 불러와야함)

npm i -D css-loader style-loader

그다음 서버에 실행시켰을 때 index.html에 보여줄 기본적인 CSS를 index.html 이 아닌 index.js 에 import 해준다.

// ./src/script.js
require('../style.css');
// import "../style.css";

6. 설정파일 건드리기

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "app.bundle.js",
  },
  module: {
    rules: [
      {
				// 파일명이 .css로 끝나는 모든 파일에 적용
        test: /\.css$/,
				// 배열 마지막 요소부터 오른쪽에서 왼쪽 순으로 적용
				// 먼저 css-loader가 적용되고, styled-loader가 적용되어야 한다.
				// 순서 주의!
        use: ["style-loader", "css-loader"],
				// loader가 node_modules 안의 있는 내용도 처리하기 때문에
				// node_modules는 제외
        exclude: /node_modules/,
      },
    ],
  },
};

순서와 제외 파일들 꼭 알자!
JS 파일에 CSS 파일을 넣고 설정 건드렸으면 npm run build을 입력 후 결과 보기!

6. Plugin 추가해 번들링하기

HTML도 번들에 포함하는 작업을 위해 Plugin을 추가해보자!

npm i -D html-webpack-plugin

설정 파일에도 module 아래에 플러그인 설정 넣어주자

module: {
...
  plugins: [new HtmlWebpackPlugin({
    // 기본값인 src/index.html으로 파일 옮겨주자
    template: path.resolve(__dirname, "src", "index.html")
  })]
};

잘 된다면 npm run build 입력 후 dist/index.html파일이 생성 된 걸 볼 수 있다!
CSS는 app.bundle.js 파일에서 넣어주고, JS는 html-webpack-plugin이 자동으로 <script defer="defer" src="app.bundle.js"></script> 스크립트 요소를 추가해준다.

핵심

  • entry 파일이 필요한 모든 것을 webpack이 모아서 번들링을 해준다.
  • 번들링의 결과물이 output 경로로 산출된다.
  • loader는 JavaScript, JSON이 아닌 파일을 불러오는 역할을 한다.
  • 플러그인은 번들링 과정 중에 개발자가 원하는 다양한 작업을 할 수 있도록 도와준다.
    • 그 중 html-webpack-plugin은 번들링 과정 중 html 파일을 자신이 원하는 형태로 가공하여 번들에 포함할 수 있게 돕는다.
    • 로더와 다르게 플러그인은 기능이 천차만별이고 어렵기에 자주 쓰는 것을 공부하고 개발 환경을 구성할 때, 내가 필요한게 무엇인지를 정확하게 구분

리액트의 웹팩

그 전까지 VJS, DOM의 웹팩을 다뤘다면 이번엔 리액트로 웹팩을 해보자!
리액트와 웹팩을 할 땐 바벨로 해석후 웹팩에 전달해야한다!

npm install --save-dev webpack webpack-cli style-loader css-loader mini-css-extract-plugin html-webpack-plugin webpack-dev-server babel-loader @babel/core @babel/preset-env @babel/preset-react webpack-merge webpack-dev-server react-hot-reloader @pmmmwh/react-refresh-webpack-plugin react-refresh

기본적인 웹팩 라이브러리 + 로더, 플러그인과 바벨까지 추가했다!

  • babel-loader: Webpack(웹팩)에서 babel(바벨)을 다루기 위한 라이브러리
  • @babel/core: babel(바벨)로 컴파일할 예정
  • @babel/preset-env: babel(바벨)로 컴파일시 어떤 타겟으로 지정할지 설정하는 라이브러리
  • @babel/preset-react: React(리액트)를 babel(바벨)로 컴파일하기 위한 라이브러리
  • css-minimizer-webpack-plugin, mini-css-extract-plugin: styl-loadercss-loader로 번들링하면 header 태그 안에서 internal 방식으로 번들링 되는 것과 달리 external 방식으로 CSS 번들링하는 플러그인인 css-minimizer-webpack-plugin 설치, 더 최적화를 위해
  • webpack-merge, webpack-dev-server: 웹팩 설정파일을 두가지 모드로 설정하기 위해, 변경된 코드를 개발 서버에 반영해서 보여줄 수 있도록 설정 추가
  • react-refresh-webpack-plugin: 변경사항을 적용시켜주고, 리액트의 상태를 유지해주는 플러그인 설치
  • eslint, prettier, webpack-bundle-analyzer 선택

과제

전에 했던 나만의 아고라스테이츠 과제 레퍼런스에 번들링을 해봤따!

기본적인 번들링 코드를 제시하겠다.

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: "production",
  entry: './src/index.js',
  output: {
    path: path.join(__dirname, 'docs'),
    filename: '[name].bundle.js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env'],
              ['@babel/preset-react', { runtime: 'automatic' }],
            ],
          },
        },
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        exclude: /node_modules/,
      },
      // 이미지까지
      // {
      //   test: /\.(png|svg|jpg|jpeg|gif)$/i,
      //   type: 'asset/resource',
      //   exclude: /node_modules/,
      // },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      // 파비콘
      // favicon: './src/favicon.ico',
    }),
  ],
};

따로 webpack.config.dev.js에 핫로딩과 리프레쉬 플러그인을 얹어 개발 환경으로 만들어주는 webpack-dev-server모듈 등 다양한 기능을 가진 모듈들이 있다.

참고: create-react-app의 내장 번들러

리액트 하나로 개발하지 않는다. react, react-dom, react-scripts, create-react-app, react-router-dom, storybook, styled-component등 많은 부가 라이브러리 등이 필요한 게 리액트다.
그래서 '필수' 라이브러리를 구현하려고 create-react-app라는 툴 체인을 만들어 npx create-react-app만 입력해도 필수 툴들이 설치된다. 어떤 툴들이 설치되어 있을까?

{
	// ... 
  "dependencies": {
    "@babel/core": "^7.16.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
    "@svgr/webpack": "^5.5.0",
    "babel-jest": "^27.4.2",
    "babel-loader": "^8.2.3",
    "babel-plugin-named-asset-import": "^0.3.8",
    "babel-preset-react-app": "^10.0.1",
    "bfj": "^7.0.2",
    "browserslist": "^4.18.1",
    "camelcase": "^6.2.1",
    "case-sensitive-paths-webpack-plugin": "^2.4.0",
    "css-loader": "^6.5.1",
    "css-minimizer-webpack-plugin": "^3.2.0",
    "dotenv": "^10.0.0",
    "dotenv-expand": "^5.1.0",
    "eslint": "^8.3.0",
    "eslint-config-react-app": "^7.0.1",
    "eslint-webpack-plugin": "^3.1.1",
    "file-loader": "^6.2.0",
    "fs-extra": "^10.0.0",
    "html-webpack-plugin": "^5.5.0",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^27.4.3",
    "jest-resolve": "^27.4.2",
    "jest-watch-typeahead": "^1.0.0",
    "mini-css-extract-plugin": "^2.4.5",
    "postcss": "^8.4.4",
    "postcss-flexbugs-fixes": "^5.0.2",
    "postcss-loader": "^6.2.1",
    "postcss-normalize": "^10.0.1",
    "postcss-preset-env": "^7.0.1",
    "prompts": "^2.4.2",
    "react-app-polyfill": "^3.0.0",
    "react-dev-utils": "^12.0.1",
    "react-refresh": "^0.11.0",
    "resolve": "^1.20.0",
    "resolve-url-loader": "^4.0.0",
    "sass-loader": "^12.3.0",
    "semver": "^7.3.5",
    "source-map-loader": "^3.0.0",
    "style-loader": "^3.3.1",
    "tailwindcss": "^3.0.2",
    "terser-webpack-plugin": "^5.2.5",
    "webpack": "^5.64.4",
    "webpack-dev-server": "^4.6.0",
    "webpack-manifest-plugin": "^4.0.2",
    "workbox-webpack-plugin": "^6.4.1"
  },
  "devDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "optionalDependencies": {
    "fsevents": "^2.3.2"
  },
  "peerDependencies": {
    "react": ">= 16",
    "typescript": "^3.2.1 || ^4"
  },
  "peerDependenciesMeta": {
    "typescript": {
      "optional": true
    }
  }
}

어마어마 하다. 다양한 툴들이 설치된다. 리액트 특성상 계속 뭐가 붙여져서 무거워진다. 프론트엔드 개발자라면 웹팩을 통해 필요한 기능만 쏙쏙 골라와야한다.

Advanced Challenge

  • github page 배포

    • 번들을 github page로 배포
    • 주의점: output이 dist가 아니고 docs로 해야하고, github page 옵션을 변경해야 함(/(root)가 아닌 /docs로!)
  • 작동 모드 설정

    • development mode와 production mode의 차이에 대해서 학습하고, config file을 나눠보세요. (참고 링크)
      • mode: 'development': 최적화 플러그인 3개를 씀, 개발할 때 처럼 빠르게 빌드업 할 때 쓰자!
      • mode: 'production': 무려 7개를 씀, 최대로 최적화됐기에 배포할 때 씀!
  • Output 관리

    • 번들링 할 때 마다 dist 디렉터리를 정리되게. (참고 링크)
      • 아웃풋에 clean: true 입력하면 됨!!
    • app.bundle.js 가 아니라, output이 늘 동적으로 변하도록 생성해보세요.
      • filename: '[name].bundle.js',
  • Asset 관리

    1. 위의 사이트에서 보고 npm 다운 받기
    2. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
      const CssMinimizerPluain = require('css-minimizer-webpack-plugin'); 선언
    3. 코드 넣기
      ...
      optimization: {
        minimizer: [
           new CssMinimizerPlugin(),
         ],
       },
      plugins: [
        new MiniCssExtractPlugin(),
        ...
       ```
  • 개발용 서버

    • 라이브 서버와 비슷한 기능을 하는 webpack-dev-server를 적용합니다. (참고 링크)
      • webpack-dev-server설치 및 dev.js만들어서 설정
  • 브라우저 호환성

    • ES6를 지원하지 않는 브라우저에서 실행 가능하게 target 속성을 활용합니다. (참고 링크)
      • target: ['web', 'es5'],
  • 개발용 서버

    • 라이브 서버와 비슷한 기능을 하는 webpack-dev-server, react-hot-reloader 를 적용합니다. (참고 링크)
  • eslint, prettier

    • 리액트에 적합한 eslint, prettier 설정을 학습하고, 적용한다.
    • eslint-plugin-jsx-a11y 를 추가하여, JSX에서 웹 접근성을 지키는데 도움이 되는 룰을 적용한다.
      • 걍 설치해서 보면 됨

다음엔 parcel, snowpack, esbuild 이런 것도 해보자! 특히 parcel은 혼자 간단한 프로젝트할때 쉽다네?!

참고: 같은 과제 벨로그 쓰심, 과제1, 과제2

profile
코뿔소처럼 저돌적으로

0개의 댓글