본문 바로가기

코드스테이츠 SEB FE 41기/Section 별 내용 정리

section4/Unit3/[HTML/CSS] 심화(11/24)

반응형

웹팩과 리액트

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

1. react, react-dom

너무나 당연한 이야기지만, 리액트 컴포넌트와 Hooks, 라이프 사이클에 대한 정보가 모두 들어있는 리액트와 이 리액트 코드를 브라우저에 보여줄 수 있는 react-dom은 꼭 필요하다.

2. babel

React를 학습하기 전, JSX부터 배워야 했었다. 그런데, 브라우저에서 JavaScript는 읽을 수 있지만 JSX는 읽을 수 없다. 지금까지 React를 JSX로 작성해왔는데브라우저에서 내가 만든 React 애플리케이션을 볼 수 있었던 이유는 create-react-app에 포함되어 있는 babel이 jsx를 js로 변경해주어 번들링을 해줬기 때문이다. 참고로 babel은 JSX를 JavaScript로 변경하여 entry에서 불러올 수 있게 만들어줬기 때문에 로더의 일종으로 볼 수 있겠다.

3. css-loader

create-react-app으로 만들어진 애플리케이션을 보면 import 'aaa.css' 와 같이 입력해도 CSS가 적용되던 것을 알 수 있다. css-loader가 필요하다는 것을 쉽게 알 수 있다.

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

1. react-hot-reloader

react-hot-reloader는 webpack-dev-server처럼 저장할 때 마다 변경사항을 개발 환경에 적용해주는 라이브러리이다. react-hot-reloader는 리액트 상태를 유지시켜준다.

2. eslint

eslint는 JavaScript로 개발 시 자주 접하는 에러를 방지하기 위한 린터이다. eslint 역시 많은 config와 plugin이 있는데, 이를 잘 조합하면 리액트에서 자주 접하는 에러를 미리 발견하는데 도움이 된다.

3. prettier

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


과제 : 리액트 웹앱 번들링하고 배포하기

1. package.json 생성

npm init -y

2. 리액트 관련 라이브러리 설치

npm install -D react react-dom
  • react : 리액트 컴포넌트와 Hooks, 라이프 사이클에 대한 정보가 들어있는 코어 라이브러리
  • react-dom : 리액트와 DOM 연결

3. 웹팩 관련 라이브러리 설치

npm install -D webpack webpack-cli webpack-dev-server html-webpack-plugin
  • webpack-cli : 터미널에서 웹팩 명령어 실행 가능하게 해줌
  • webpack-dev-server : 파일 변경사항 실시간으로 빌드하는 개발 서버 구동 (dev 서버)
  • html-webpack-plugin : html 파일에 번들링 된 js 파일을 삽입

4. 바벨 관련 라이브러리 설치(바벨도 로더에 포함되는 개념)

npm install -D babel-loader @babel/cli @babel/core @babel/preset-env @babel/preset-react
  • babel-loader :  바벨 설정 파일(babel.config.js)을 읽어 해당 설정에 맞게 변환. 바벨과 웹팩을 연결.
  • @babel/cli : 터미널에서 바벨명령어 사용가능하도록 CLI 제공
  • @babel/core : babel의 핵심기능을 포함해서 반드시 설치해야 함
  • @babel/preset-env : ES6+ 코드를 ES5 코드로 변환시켜줌
  • @babel/preset-react : 리액트(JSX)를 js로 인식가능

5. 최상위에 babel.config.js 또는 .babelrc 생성

module.exports = {
	presets: [
        ['@babel/preset-env'],
        ['@babel/preset-react', { runtime: 'automatic' }],
      ],
}

6. 로더 라이브러리 설치 (babel, html, css, style)

npm install -D css-loader style-loader
  • css-loader : CSS를 JS파일 내에서 불러올 수 있게 함
  • style-loader : CSS를 DOM(style 태그) 안에 담아줌

7. webpack.config.js 생성

const path = require("path");
const HtmlWebPackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: 'app.bundle.js',
    path: path.resolve(__dirname, 'docs'),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: "/node_modules",
        use: "babel-loader",
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
	new HtmlWebPackPlugin({
	  template: './public/index.html',
	})
  ]
};

8. docs/index.html 열어서 확인해보기

서버를 안켜서 밑부분은 안나오지만, 정상적으로 번들링 된 것은 확인할 수 있다.

npm run start를 했을 때 흰 화면만 나오고 에러가 떠서 내가 번들링 과정에서 뭔가를 실수한 줄 알고 정말 애를 많이 먹었다 ㅠㅠ 이것때문에 한시간 넘게 삽질했다...관련 stackoverflow 글을 거짓말안치고 다 읽어본 것 같다.. 하지만 적용이 1도안됐다^^7
왜냐?!?!?!
구글링해보니 번들링 후에는 index.html을 확인해봐야하는 것이였다..(두둥) 어제 바닐라J로만 작성되어있던 웹앱을 번들링했을 때처럼!
저 화면을 보고 안도의 한숨을 내쉬었다... 기가 다 빨린 느낌~!
여하튼 오늘 과제는 튜토리얼없이 처음부터 혼자서 해야해서'리액트 웹팩으로 시작하기', '리액트 웹팩 실행' 등등 구글링을 정말 열심히했다!!!! 와우~!!

작동 Mode 설정 - dev, prod

개발 development mode와 배포 production mode에 따라 config file을 나누기

1. base에 dev나 prod 합치기 위해 webpack merge 설치

npm i -D webpack-merge

2. webpack.config.js를 3개로 분할
webpack.config.base.js를 기준으로 +dev나 +prod 하는 구조

webpack.config.js --> webpack.config.base.js 로 변경 
webpack.config.dev.js 
webpack.config.prod.js

3. 각 파일 생성 후 작성

//webpack.config.dev.js
const { merge } = require("webpack-merge");
const baseConfig = require("./webpack.config.base");

module.exports = merge(baseConfig, {
  mode: "development",
  devServer: {
    port: 3001,
  },
});
//webpack.config.prod.js
const { merge } = require("webpack-merge");
const baseConfig = require("./webpack.config.base");

module.exports = merge(baseConfig, {
  mode: "production",
});

4. package.json 추가

// package.json
"scripts": {
    "build": "webpack --config webpack.config.prod.js",
    "dev": "webpack-dev-server --open --config webpack.config.dev.js"
  },

 

Output 관리

1. 번들링 할 때 마다 디렉터리를 정리하기 (참고 링크)

  • output에 clean: true 추가
      // webpack.config.js
      output: {
           filename: '[name].bundle.js',
           path: path.resolve(__dirname, 'docs'),
          clean: true,
         },

2. 번들파일명 동적으로 생성하기 - [name].bundle.js

// webpack.config.js
output: {
     filename: '[name].bundle.js',
   },

 

Asset 관리 - CSS에 minify 적용

  • style-loader
    : css파일을 style태그로 만들어 head안에 넣어준다. 대신에 mini-css-extract-plugin 사용하기도 한다.
  1. mini-css-extract-plugin : CSS 파일을 별도 파일로 추출(extract)한다. CSS 코드가 포함된 JS 파일 별로 CSS 파일을 생성한다.
  2. css-minimizer-webpack-plugin : CSS를 최적화(압축)한다.
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: "./src/index.js",
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'docs'),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: "/node_modules",
        use: "babel-loader",
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'], // 수정
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: './public/index.html',
    })
  ],
  optimization: {  // 여기부터 추가
    minimizer: [new CssMinimizerPlugin()],  
  },
};

 

개발용 서버

webpack-dev-server처럼 저장할때마다 변경사항을 적용시며주고, 리액트의 상태를 유지해주는 플러그인 설치한다.
react-refresh-webpack-plugin

npm i -D @pmmmwh/react-refresh-webpack-plugin react-refresh

webpack.config.dev.js 수정

// webpack.config.dev.js
const { merge } = require("webpack-merge");
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const baseConfig = require("./webpack.config.base");

module.exports = merge(baseConfig, {
  mode: "development",
  devServer: {
    port: 3000,
    hot: true,
  },
  plugins: [new ReactRefreshPlugin()]
});

 

eslint 설치

코드에 문제가 없는지 검사하기 위해서 설치한다.

npm install -D eslint eslint-plugin-react @babel/eslint-parser
//pacakge.json scripts에 추가 

 "lint": "eslink ./src",

웹 접근성에 대해서 지켜야하는 부분을 알려주는 eslint rule을 추가 설치한다.

npm install -D eslint-plugin-jsx-a11y

따로 설정파일을 작성해야 한다. 

// .eslintrc.js
module.exports = {
  parser: "@babel/eslint-parser",
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true,
  },
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:jsx-a11y/recommended",
  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2018,
    sourceType: "module",
  },
  settings: {
    react: {
      version: "18.2.0",
    },
  },
  plugins: ["react"],
  rules: {
    "react/react-in-jsx-scope": 0,
    "react/jsx-uses-react": 0,
    "react/prop-types": 0,
  },
};

 

prettier 설치

코드 형식 통일을 위해 설치한다.

  • vscode extension으로 설치하거나, 직접 설치하는 방법이 있다.
npm install -D prettier
//pacakge.json scripts에 추가 

"pretty": "prettier --write ./",

따로 설정 파일을 만들어줘야 한다.

// .prettierrc.js
module.exports = {
  singleQuote: true,
  jsxSingleQuote: true,
};

 

webpack.config.base.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: "./src/index.js",
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'docs'),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: "/node_modules",
        use: "babel-loader",
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: './public/index.html',
    })
  ],
  optimization: {  // 여기부터 추가
    minimizer: [new CssMinimizerPlugin()],  
  },
};

webpack.config.dev.js

const { merge } = require("webpack-merge");
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const baseConfig = require("./webpack.config.base");

module.exports = merge(baseConfig, {
  mode: "development",
  devServer: {
    port: 3000,
    hot: true,
  },
  plugins: [new ReactRefreshPlugin()]
});

 

webpack.config.prod.js

const { merge } = require("webpack-merge");
const baseConfig = require("./webpack.config.base");

module.exports = merge(baseConfig, {
  mode: "production",
});
반응형