• 이 글은 React webpack from the scratch의 영상과 제 나름의 조사를 통해 작성한 글입니다.
  • 원본 소스는 버전이 바뀌면서 많이 다릅니다. 참조하실 분들은 저의 github에서 소스코드를 받으실 수 있습니다.

1. package.json

npm init
  • package.json 생성

2. react

npm install react react-dom

yarn일 경우

yarn add react react-dom
  • react
    • 리액트 라이브러리
  • react-dom
    • browser, dom, webapp 관리

3. babel

npm install --save-dev @babel/core babel-loader @babel/preset-react @babel/preset-env 

yarn일 경우

yarn add --dev @babel/core babel-loader @babel/preset-react @babel/preset-env 
  • @babel/core
    • 리액트는 es6를 사용하므로 여러 브라우저에서 사용가능하도록 es5문법으로 바꿔줌
  • @babel/preset-react
    • jsx -> javascript
  • @babel/preset-env
  • babel-loader
    • 자바스크립트 파일을 babel preset/pluginwebpack을 사용하여 es5로 컴파일 해주는 plugin
    • jsx -> javascript 로 컴파일
    • html webpack plugin

babel이 7로 업데이트 되면서 scoped package로 전환했다. 간단히 이야기하면 기존의 비공식적인 package들과 네이밍 컨벤션에 문제가 있어서 바꾸게 되었다. 영상에서는 babel-core라고 되어있지만 install하게되면 7, 8이 설치되므로 (특히 babel-loader) package 설정에 유의해야 할 듯하다.
babel-core -> @babel/core

4. webpack

npm install --save-dev webpack webpack-dev-server webpack-cli html-webpack-plugin
  • webpack
    • 모든 리액트 파일을 하나의 컴파일된 하나의 자바스크립트 파일에 넣기 위해
  • webpack-dev-server
    • live reload
  • webpack-cli
    • build 스크립트를 통해 webpack 커맨드를 사용하기 위해
  • html-webpack-plugin
    • 나중에 webpack.config.js에서 사용할 플러그인

--save-dev : 개발환경에서만 사용되는 라이브러리라는 것을 명시

4-1 용어 설명

  1. Loaders
  • 코드 불러옴
  • transform the source code of a module.
    • style-loader css를 dom에 추가
    • sass-loader SASS파일을 CSS로 컴파일
    • babel-loader babel로 javascript 코드를 transpile
  • do the pre-processing transformation of virtually any file format when you use sth like require("my-loader!./my-awesome-module") in your code
  1. Plugins
  • 컴파일러
  • Webpack의 핵심 기능
  • Webpack은 기본적으로 대부분의 source 코드를 bundle파일로 번환한다.
  • loader가 부족하면 pluging을 사용하여 webpack의 기능을 추가한다.
  1. preset
  • plugin의 집합
  • plugin이 필요할 때마다 매번 webpack에서 설정하는 것은 귀찮은 일이므로 plugin들을 모아놓은 preset을 한번에 추가하여 관리할 수 있습니다.

스크린샷 2019-10-03 오후 3.03.51.png
예를들어 위의 그림을 설명하겠습니다. 먼저 원래의 동작 먼저 보겠습니다. css loader에 의해 css파일에서 css코드들을 부릅니다. 그리고 csso loader에 의해 최적화 작업과 text를 추출하는 plugin에 의해 style.css라는 파일에 원하는 css 코드를 담습니다

webpack 설정이 들어가면, text plugin에 의해 css 파일에서 css 코드를 추출하고 webpack 안의 csso plugin에 의해 중복되는 코드를 최적하여 style.css에 담게됩니다.

정리하면 최적화를 둘 다 하지만 webpack plugin이 조금 더 밀도있는 작업을 하는 것을 알 수 있습니다.

4-2 webpack.config

  • webpack을 사용하기 위한 설정파일

webpack.config.js

const path = require('path')                                        // core nodejs 모듈 중 하나, 파일 경로 설정할 때 사용
const HtmlWebpackPlugin = require('html-webpack-plugin')            // index.html 파일을 dist 폴더에 index_bundle.js 파일과 함께 자동으로 생성, 우리는 그냥 시작만 하고싶지 귀찮게 index.html 파일까지 만들고 싶지 않다.!!

module.exports = {                                      // moduel export (옛날 방식..)
    entry: './src/index.js',                            // 리액트 파일이 시작하는 곳
    output: {                                           // bundled compiled 파일
        path: path.join(__dirname, '/dist'),            //__dirname : 현재 디렉토리, dist 폴더에 모든 컴파일된 하나의 번들파일을 넣을 예정
        filename: 'index_bundle.js'
    },
    module: {                                           // javascript 모듈을 생성할 규칙을 지정 (node_module을 제외한.js 파일을 babel-loader로 불러와 모듈을 생성
        rules: [
            {
                test: /\.js$/,                          // .js, .jsx로 끝나는 babel이 컴파일하게 할 모든 파일
                exclude: /node_module/,                 // node module 폴더는 babel 컴파일에서 제외
                use:{
                    loader: 'babel-loader'                // babel loader가 파이프를 통해 js 코드를 불러옴
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'                // 생성한 템플릿 파일
        })
    ]
}

위의 설정들을 크게 살펴보자

  • path
    • 파일의 경로를 지정
    • __dirname : 노드 변수로 현재 모듈의 디렉토리를 리턴합니다.
  • HtmlWebpackPlugin
    • 컴파일 이후 index.html 파일을 생성
    • template에 지정된 index.html에 모든 static 파일들을 긁어모은 index_bundle.js 파일을 <script src='index_bundle.js'></script> 형식으로 연결해줍니다.
  • module.export
    • 출력할 모듈

  • entry
    • 컴파일 할 파일
    • index.js
  • output
    • 컴파일 이후 파일
    • __dirname/dist/index_bundle.js
  • module
    • 모듈의 컴파일 형식
    • es6 문법을 es5으로 바꾸기 위해 webpack이 js, jsx를 포함한 모든 파일을 babel을 통하여 컴파일 시킵니다.
  • plugin
    • 사용할 plugins
    • 여기서는 htmlwebpackPlugin을 사용하여 index.html과 index_bundle.js에 연결해줍니다.

html-webpack-plugin

  • html-webpack-plugin은 script 태그안에 넣은 webpack 번들 파일과 함께 html5 파일을 생성합니다.
  • HTMLWebpackPlugin이 index.html의 script 태그안에 컴파일된 bundle 파일을 심어줄 것입니다.
  • new HtmlWebpackPlugin()을 위의 예시처럼 그냥 사용하는 것도 좋지만 이렇게 템플릿을 만들어 놓으면 커스터마이징 하기 편합니다.

4-3 정리

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: 'index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin()
  ]
}

위의 코드는 다음과 같이 dist/index.html파일을 생성합니다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Webpack App</title>
  </head>
  <body>
    <script src="index_bundle.js"></script>
  </body>
</html>

index_bundle.js가 webpack에 의해 컴파일, 번들화된 하나의 js 파일입니다.

5. index.html

다음은 웹페이지의 가장 기본이 될 index.html 파일을 src/index.html 경로로 아래와 같이 생성하겠습니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>My React App</title>
</head>
<body>
    <div id="app"></div>                // 템플릿
</body>
</html>

6. babelrc

  • babel-preset-envbabel-preset-react와 같이 preset을 사용하고 싶으면 root폴더에 .babelrc을 생성하여 사용하고자할 preset을 설정하면 됩니다.
  • plugin들을 각각의 npm dependency를 가지고 있습니다. 하지만 설치시 매번 .bablrc에 설정을 해야하므로 그 두가지를 모두 해결해줄 preset을 사용하면됩니다. preset을 설치하고 설정하므로서 preset이 가진 plugin들을 설정할 필요없이 사용할 수 있게 됩니다.
{
    "presets": [
        "@babel/env",
        "@babel/react"
    ]
}

@babel이 버전이 업데이트 되면서 더이상 babel-core 형태(-)의 dependency를 지원하지 않게되므로서 @babel/env 형태(/)를 사용해야 합니다.

index.js

import React from 'react';
import ReactDOM from 'react-dome';
import App from './component/App'

ReactDOM.render(<App/>, document.getElementById('app'))                       // dom에 render학 메인 app component, rendering 할 곳

App.js

import React from 'react'

export default class App extends React.Component {
    render(){
        return(
            <div>
                <h1>My React App</h1>
            </div>
        )
    }
}

package.json

"scripts": {
    "start":"webpack-dev-server --mode development --open --hot",                    // webpack-dev-server, --open : 자동으로 브라우저 열어줌, --hot : hot realod 저장했을 때 자동적으로 reload 해줌
    "build":"webpack --mode production"                                              // dist 폴더에 컴파일된 파일 다 넣어줌
  },

이제 지금까지 작성한 프로젝트를 실행해보겠습니다. 아래의 코드를 터미널에서 실행합니다.

npm start

그렇다면 다음과 같은 화면이 보일 것입니다.

스크린샷 2019-09-03 오후 4.34.24.png

이번에는 프로젝트를 build 해보겠습니다.

npm run build

그러면 /dist 폴더가 생성되면서 index_bundle.jsindex.html이 생성됨을 알 수 있습니다.

스크린샷 2019-09-03 오후 4.37.00.png

index.html을 먼저 보면 <script type="text/javascript" src="index_bundle.js"></script> script 태그가 body안에 생성되어 있는 것을 확인할 수 있습니다. 이는 webpack.config.js 파일의 plugins 에서 Htmlwebplugin의 도움으로 template: './src/index.html'템플릿에 컴파일 된 번들 파일이 script 태그로 자동적으로 들어가게 되는 것입니다.

스크린샷 2019-09-03 오후 4.37.27.png

이번에 index_bundle.js 파일을 살펴보면 js, jsx 파일들이 es6에서 es5로 컴파일하여 알아서 순서대로 묶어서 하나의 파일로 넣어 줌을 알 수 있습니다. 이는 자바스크립트의 모듈 태그 순서에 따라서 문제를 손쉽게 해결 해줄 수 있습니다.
스크린샷 2019-09-03 오후 4.42.42.png

조금 더 자세한 내용은 ES6, babel 그리고 webpack을 어떻게 사용할까을 참조하시기 바랍니다.

마치며

이번에 회사에서 관리자 페이지 작업을 맡게 되면서 react로 한번 만들어보고 싶었습니다. 사실 리액트를 사용하긴 했지만
주로 create react app 키워드만 사용하여 백그라운드가 어떻게 돌아가는지도 모르고 막 사용했던 것 같습니다. 조금 더 궁금한 부분도 많지만 추후 계속 업데이트 해나가야 될 것 같습니다.