[CodeStates-Section4]U3.React-번들링과 웹팩

소이뎁·2023년 1월 19일
2

CodeStates_Frontend_42기

목록 보기
35/39

후기

webpack이라는 자바스크립트 모듈 번들러를 배웠다. 번들링을 해주면 수많은 파일이 2,3개의 파일로 압축되면서 실행은 동일하게 되는 것을 볼 수 있었다. webpack을 배우면서 entry, target 등 익숙하지 않은 개념들을 보고 이것들이 무엇을 지칭하고 어떤 역할을 하는지 이해하기 어려웠다. 파일이 브라우저에서 어떻게 렌더링 되는지 완벽히 이해하고 있다면 훨씬 이해가 쉬울 것 같았다. 또한, webpack 뿐만 아니라 이후에 다른 모듈 번들러를 쓰게 되더라도 사용법을 빨리 익힐 수 있겠다는 생각이 들었다. 무작정 명령어나 코드 형식을 따라 쓰기보다는 작동 원리를 이해하려고 노력해야겠다.

Chapter1. 번들링
Chapter2. 웹팩
Chapter3. 웹팩의 핵심 컨셉
과제1. 간단한 웹앱 번들링 후 배포하기
Webpack 튜토리얼
Chapter4. 웹팩과 리액트
과제2. 리액트 웹앱 번들링 후 배포하기

<Chapter1. 번들링>

1.번들링이란

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

사용자가 브라우저를 열고 주소를 입력 -> 프론트엔드 개발자가 번들링한 여러 파일을 다운 -> 브라우저가 실행 -> 웹 애플리케이션 제공

2.번들링의 필요성

1) 파일 간의 충돌 방지

자바스크립트는 기본적으로 모듈 시스템을 지원하지 않음. 다시 말하면, 자바스크립트는 파일 스코프와 import, export를 지원하지 않음. 따라서 자바스크립트에서는 <script> 태그를 사용하여 외부의 자바스크립트 파일을 로드 시 분리된 자바스크립트 파일들의 전역 변수가 중복되는 문제가 발생할 수 있음. webpack과 같은 모듈 번들러를 사용하면 이러한 한계 극복 가능.

2) 성능 향상

개발 환경에서 가독성 향상을 위해 작성한 공백을 지우고, 배포에 필요한 파일만을 클라이언트에 전송하는 등 성능 최적화를 진행

3) 보안 향상

웹 페이지의 소스 코드가 읽기 쉽다면, 악의적인 사용자의 공격에 쉽게 노출

<Chapter2. 웹팩>

1.모듈 번들러(Module Bundler)

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

2) 등장 배경
모던 웹으로 발전하면서 세분화된 모듈 파일이 폭발적으로 증가. 이런 복잡성에 대응하기 위해 하나의 시작점(Ex. React App의 index.js 등)으로부터 의존성을 가지는 모듈을 모두 추적하여 하나의 결과물을 만들어내는 모듈 번들러가 등장.

2.Webpack

1) 정의

여러 개의 파일을 하나의 파일로 합쳐주는 모듈 번들러

2) Webpack에서의 모듈

JavaScript의 모듈에만 국한하지 않음. HTML, CSS, 이미지 파일(.jpg, .png)들도 전부 포함.

3) 빌드와 번들링

-빌드: 개발이 완료된 앱을 배포하기 위해 하나의 폴더(directory)로 구성하여 준비하는 작업. React 앱의 경우, npm run build를 실행하면 React build 작업이 진행되고, index.html 파일에 압축되어 배포에 최적화된 상태를 제공.

-번들링: 모듈 간의 의존성 관계를 파악해 그룹화 하는 작업.

4) Webpack의 필요성

-웹 애플리케이션의 로딩 속도와 성능 향상
Webpack으로 인해 브라우저에서 서버로 요청하는 파일의 숫자가 줄어듦
-네트워크 코스트 감소
같은 타입의 파일들은 묶어서 요청 및 응답을 받을 수 있음
-각종 loader를 사용 가능
babel-loader: 일부 브라우저에서 지원하지 않는 JavaScript ES6의 문법들을 ES5로 번환
vue-loader: Vue 컴포넌트를 일반적인 자바스크립트 모듈로 변환
scss-loader: scss 파일을 css 파일로 변환
-Production 모드 사용 가능(Webpack4 버전 이상부터)
Production 모드로 번들링을 진행할 경우, 코드 난독화, 압축, 최적화(Tree Shaking) 작업을 지원

<Chapter3. 웹팩의 핵심 개념>

module.exports = {
  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(),
    ]
  }
};

1.Target

-자바스크립트는 서버와 브라우저에서 모두 컴파일 할 수 있기 때문에 Target 명시
-Target의 기본값은 web
-브라우저 호환성과 연관

// web에서 es5 버전으로 컴파일
module.exports = {
  target: ["web", "es5"],
};

2.Entry Point(엔트리)

-webpack이 내부의 디펜던시 그래프를 생성하기 위해 사용해야 하는 모듈.
-Webpack은 이 Entry point를 기반으로 직간접적으로 의존하는 다른 모듈과 라이브러리를 찾아낼 수 있음.

//기본 값
module.exports = {
	...
  entry: "./src/index.js",
};

//지정 값
module.exports = {
	...
  entry: "./src/script.js",
};

3.Output(출력)

-생성된 번들을 내보낼 위치와 이 파일의 이름을 지정하는 방법을 webpack에 알려주는 역할

// dist 폴더에 bundle.js로 저장됨
module.exports = {
  output: {
    filename: 'bundle.js',// 필수
  },
};

//path로 경로 지정(path 모듈 사용)
const path = require('path');

module.exports = {
	...
  output: {
    path: path.resolve(__dirname, "docs"), // 절대 경로로 설정을 해야 합니다. 
    filename: "app.bundle.js",
    clean: true
  },
};

4.Loader(로더)

-Webpack은 기본적으로 JavaScript와 JSON 파일만 이해하지만 loader를 사용하면 다른 유형의 파일을 처리 가능
-module.rules 아래 정의해야 함
-use 배열의 마지막 요소부터 적용

module.exports = {
	...
  module: {
    rules: [
      {
        test: /\.css$/, // 필수
        use: [MiniCssExtractPlugin.loader, "css-loader"], // 필수
        exclude: /node_modules/,
      },
    ],
  },
};

-test: 변환이 필요한 파일들을 식별하기 위한 속성
-use: 변환을 수행하는데 사용되는 로더를 가리키는 속성
-exclude: 바벨로 컴파일하지 않을 파일이나 폴더를 지정. (반대로 include 속성을 이용해 반드시 컴파일해야 할 파일이나 폴더 지정 가능)

5.Plugins(플러그인)

-번들 최적화, 에셋 관리, 환경변수 주입 등의 광범위한 작업을 수행 가능
-다른 목적으로 플러그인을 여러 번 사용하도록 설정할 수 있기 때문에 new 연산자를 사용해 호출하여 플러그인의 인스턴스를 만들어줘함

const webpack = require('webpack');
//require로 플러그인 요청
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  ...
  // plugins 배열에 사용하고자 하는 플러그인 추가
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src", "index.html"),
    }),
    new MiniCssExtractPlugin(),
  ],
};

// html-webpack-plugin: 생성된 모든 번들을 자동으로 삽입하여 애플리케이션용 HTML 파일을 생성
// mini-css-extract-plugin: CSS를 별도의 파일로 추출해 CSS를 포함하는 JS 파일 당 CSS 파일을 작성해주게끔 지원

6.Optimization(최적화)

-Webpack은 버전 4부터는 선택한 항목에 따라 최적화를 실행
-대표적 옵션
minimize : TerserPlugin 또는 optimization.minimize에 명시된 plugins로 bundle 파일을 최소화(=압축)시키는 작업 여부를 결정
minimizer : defualt minimizer을 커스텀된 TerserPlugin 인스턴스를 제공해서 재정의

// mini-css-extract-plugin 에 관련된 번들을 최소화
module.exports = {
  ...
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(),
    ]
  }
};

<Webpack 튜토리얼>

1.JavaScript 번들 파일 생성하기

1) npm init

package.json 파일 생성되면 성공

npm init -y

2) 엔트리 파일에 관련 모듈 require

// src/underbar.js(관련 파일)
const _ = {
  once(func) {
    let result;
    let alreadyCalled = false;

    return function (...args) {
      if (!alreadyCalled) {
        alreadyCalled = true;
        result = func(...args);
      }
      return result;
    };
  },
};

module.exports = _; // 다른 파일에서 사용할 수 있게 export

// src/index.js(엔트리 파일)
const _ = require('./underbar.js')// 적용

const shout = (...sentences) => console.log(...sentences);

const loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas feugiat elit, ac tincidunt neque vestibulum at. Mauris a eros sit amet urna efficitur tempus."

const shoutOnce = _.once(shout);

shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);
shoutOnce(loremIpsum);

3) 웹팩 설치

프로젝트를 번들링하기 위한 라이브러리이긴 하지만, 실제 프로젝트에 사용하지 않기 때문에 devDependency 옵션을 설정하고 설치

npm install -D webpack webpack-cli

4) webpack.config.js 파일 생성

entry, ouput 작성

// webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'), // './dist'의 절대 경로를 리턴합니다.
    filename: 'app.bundle.js',
  },
};

5) 번들링하기

dist/app.bundle.js 생성되면 성공
node src/index.jsnode dist/app.bundle.js의 출력 결과물이 같음

npx webpack

// 다른 개발자와 협엽 시 package.json의 script에 "build": "webpack"을 넣어주어, npm run build로 번들링 실행 가능하게 하는 것이 좋음

2.CSS 파일을 JavaScript 파일에 포함시키기

1) css-loader, style-loader 설치

npm i -D css-loader style-loader

2) webpack.config.js 수정

module.rules 아래 정의
순서 주의

// webpack.config.js
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/,
      },
    ],
  },
};

3) 번들링 확인

dist/index.html을 열어 보면 style 요소가 생성되고 작성한 CSS가 담겨있는 모습 확인 가능

npm run build

3.번들에 HTML 포함시키기

1) html-webpack-plugin 설치

npm i -D html-webpack-plugin

2) webpack.config.js 파일 수정

모듈 설치, 플러그인 적용

const path = require("path");
// 모듈 설치
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "app.bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
        exclude: /node_modules/,
      },
    ],
  },
  //플러그인 적용
  plugins: [new HtmlWebpackPlugin({
    template: path.resolve(__dirname, "src", "index.html")
  })]
};

3) 번들링 확인

dist/index.html 파일이 새로 생성되면 성공
dist/index.html 파일에 <script defer="defer" src="app.bundle.js"></script>이 자동으로 추가됨

npm run build

<과제1. 간단한 웹앱 번들링 후 배포하기>

1. github page 배포하기

1) output의 'dist'를 'docs'로 변경

output: {
    path: path.resolve(__dirname, 'docs'),
    filename: 'app.bundle.js',
  },

2) 번들링

docs 폴더 생성되면 성공

npm run build

3) Github 설정 변경

push할 repository의 Settings -> Pages -> Build and deployment -> Branch -> /docs로 변경

4) git add, commit, push

5) github Settings -> Pages -> 상단에 Github Pages 생성

2.작동 모드 설정(development mode, production mode)

1) webpack.config.js에서 모드 설정

module.exports = {
  mode: 'development',
};

2) 번들링

npm run build

3.Output 관리

-[name].bundle.js: 번들파일명이 지정명이 아니라, 늘 동적으로 변하도록 생성, 빌드 파일이 여러 개일 때 유용
-clean: true: 사용하는 파일만 생성되도록 각 빌드 전에 /dist 폴더를 정리 가능

output: {
  path: path.resolve(__dirname, 'dist'),
  filename: '[name].bundle.js',// [name]으로 변경
  clean: true, // clean 옵션 추가
},

4.Asset 관리

1) mini-css-extract-plugin

CSS를 별도의 파일 추출하는 플러그인, style-loader와 함께 사용 불가

// 1.플러그인 설치
npm i mini-css-extract-plugin -D

// 2.webpack.config.js에 코드 추가
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //추가

module.exports = {
  entry: './script.js',
  output: {
    path: path.resolve(__dirname, 'docs'),
    filename: 'app.bundle.js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],// 기존의 style-loader 삭제 후 MiniCssExtractPlugin.loader 추가
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'index.html'),
    }),
    new MiniCssExtractPlugin(), //추가
  ],
};

// 3.번들링
npm run build

2) css-minimizer-webpack-plugin

CSS 최적화 및 축소하는 플러그인, CSS 파일을 축소하는 것이므로 CSS을 추출하는 mini-css-extract-plugin과 함께 쓰이는 경우가 많음

// 1.플러그인 추가
npm install css-minimizer-webpack-plugin --save-dev

// 2.webpack.config.js에 코드 추가
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');// 추가

module.exports = {
  entry: './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, 'index.html'),
    }),
    new MiniCssExtractPlugin(),
  ],
  // optimization 추가
  optimization: {
    minimizer: [new CssMinimizerPlugin()],
    // minimize: true, -> development mode일 경우에 추가
  },
};

5.개발용 서버

// 1.설치
npm install --save-dev webpack-dev-server

// 2.webpack.config.js에 코드 추가
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  mode: 'development',// 추가(없으면 오류 뜸)
  entry: './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, 'index.html'),
    }),
    new MiniCssExtractPlugin(),
  ],
  optimization: {
    minimizer: [new CssMinimizerPlugin()],
  },
  //devServer 추가
  devServer: {
    static: './dist',
  },
};

// 3.package.json 파일의 script에 추가
"start": "webpack serve --open"

// 4.실행
npm start

6.브라우저 호환성

ES6를 지원하지 않는 브라우저에서 실행 가능하게 target 속성을 활용

// 웹 플랫폼을 위한 런타임 코드를 생성하고 ES5 기능만 사용
module.exports = {
  // ...
  target: ['web', 'es5'],
  // ...
}

참고 사이트
https://yamoo9.gitbook.io/webpack/webpack/webpack-plugins/extract-css-files
https://velog.io/@xmun74/%EB%B2%88%EB%93%A4%EB%A7%81-Webpack

<Chapter4. 웹팩과 리액트>

1.리액트가 번들링이 필요한 이유

리액트는 프론트엔드 라이브러리로서 최소한의 기능을 제공하고자 가볍게 만들어짐
-> 시간이 지나면서 개발자의 다양한 니즈를 충족시키기 위해 더 많은 라이브러리를 필수적으로 사용해야만 했음
->개발자가 필요한 라이브러리를 골라서 번들링 할 수 있는 웹팩이 필요

2.리액트 개발에 꼭 필요한 라이브러리

1) react, react-dom

react: 리액트 컴포넌트와 Hooks, 라이프 사이클에 대한 정보가 모두 들어음
react-dom: 리액트 코드를 브라우저에 보여줌

2) babel

브라우저에서 JavaScript는 읽을 수 있지만 JSX는 읽을 수 없기 때문에 리엑트의 jsx를 js로 변경해주어 번들링 해주는 역할(loader 역할). create-react-app에 포함되어 있음.

3) css-loader

import '파일이름.css만 입력해도 CSS 적용되게 함

3.리액트 개발에 도움이 되는 라이브러리

1) react-hot-reloader

webpack-dev-server처럼 저장할 때 마다 변경사항을 개발 환경에 적용해주는 라이브러리. 리액트 상태를 유지시켜줌. React 17버전까지만 적용 가능.

webpack-dev-server -> 빌드된 파일만 반영
react-hot-loader -> 빌드 전 변경 사항도 반영

2) eslint

eslint는 JavaScript로 개발 시 자주 접하는 에러를 방지하기 위한 린터.

3) prettier

prettier는 JavaScript로 개발 시 통일성 있게 코드 형식을 맞출 수 있게 도와주는 툴. eslint와 조합해서 통일된 코드 형식까지 강요할 수도 있음.

<과제2. 리액트 웹앱 번들링 후 배포하기>

1.babel-loader 설치

// 1.webpack 설치
npm install -D webpack webpack-cli

// 2.webpack.config.js 작성
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.bundle.js',
  },
};

// 3.babel-loader 설치
npm install -D babel-loader @babel/core @babel/preset-env webpack

// 4.webpack.config.js에 코드 추가
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.bundle.js',
  },
  // 추가
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              ['@babel/preset-react', { runtime: 'automatic' }],
            ],
          },
        },
      },
    ],
  },
};

0개의 댓글