본문 바로가기

React

create-react-app 없이 react project만들기

webpack이 뭔지 babel이 뭔지는 대충 알고 있지만 여태 개인 프로젝트로 CRA 없이 react 앱을 만들어 본 적은 없다.

그래서 쓰는 글!

 

이 글의 목표

- react app을 만들 때 webpack과 babel이 왜 필요한지 이해한다

- react app을 만들 때 webpack과 babel은 어떻게 쓰이는지 이해한다

 

1. 원하는 폴더로 가서 init -y를 통해 package.json을 만든다. -y flag는 package.json파일을 만들 때 모든 config option으로 default값을 사용한다는 것을 의미한다.

init -y

 

2. react프로젝트니까 react와 react-dom 모듈을 설치해준다. 아래 명령어를 사용하면 package.json파일의 dependencies에 react와 react-dom 모듈이 들어가 있는 것을 볼 수 있다.

npm install react react-dom

 

3.. gitignore파일을 만들어준다. gitignore 파일은 git remote repo에 push할 때 올리지 않을 파일들을 명시할 때 사용한다.

vim .gitignore

.gitignore 파일에 대표적으로 아래 세 가지는 기본적으로 추가해주는 편이다.

 

  •  node_modules/
    npm으로 설치한 외부 모듈, 외부 모듈의 의존 모듈까지 모두 node_modules라는 디렉터리에 저장된다. package.json파일이 있으면 npm install 명령어를 통해 package.json에 들어있는 모듈이 node_modules 디렉터리에 자동으로 설치되기 때문에 굳이 node_modules를 git에 다 올려놓을 필요는 없다. 
  • .DS_Store
    Desktop Services Store의 약자로 애플에서 정의한 파일 포맷이다. 해당 폴더에 대한 메타 데이터를 저장하는 파일이다. 나는 맥을 사용하고 있으므로 추가해주었다.
  • dist
    dist는 Distribution folder의 약자로 webpack과 babel에 의해서 생성되는 build directory이다. 

 

4. app이라는 폴더를 만들어 주고, 그 안에 index.js, index.css, index.html 파일을 만들어준다. touch 명령어는 새로운 빈 파일을 만들때 사용한다.

mkdir app
touch index.js index.css index.html

 

5. index.js 파일 안에 react를 이용하여 아래의 코드를 입력해준다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const App = () => {
    return(
        <div>Hello World</div>
    )
}

ReactDOM.render(<App />, document.getElementById('app'));

 

6. index.html 파일 안에 아래의 코드를 입력해준다.

<!DOCTYPE html>
<html>
    <head><title>
        react app
    </title></head>
    <body>
        <div id="app"></div>
    </body>
</html>

하지만 브라우저는 React가 사용하는 문법인 jsx를 이해하지 못하기 때문에 error를 표시한다. 이 포인트가 webpack과 babel이 필요한 이유이다.

 

7. 그리고 npm을 이용해 아래 모듈들을 모두 설치해준다.

npm install --save-dev @babel/core @babel/preset-env @babel/preset-react webpack webpack-cli webpack-dev-server babel-loader css-loader style-loader html-webpack-plugin

--save-dev 는 개발환경에서만 사용할 라이브러리 것을 명시한다.  이렇게 하면 package.json의 devDependencies에 해당 라이브러리들이 들어간 것을 볼 수 있다.

 

babel은 ES6이상의 JS를 ES5로 변환하기 위해 사용한다. webpack에서 babel을 사용하려면 다음의 3개 모듈이 설치되어야 한다. babel-loader @babel/core @babel/preset-env 여기에 더불어, react의 문법인 jsx를 JS로 변환하는 작업이 필요하기 때문에 @babel/preset-react도 함께 설치해준다.

 

babel은 plugin이 없으면 아무 동작도 하지 않기 때문에, 따로 필요한 plugin을 설정해주어야 한다. 예를 들어 babel이 a, b, c라는 각각의 기능을 가진 plugin을 사용하고자 한다면, babel config 파일인 .babelrc파일에 plugin으로 a, b, c를 사용할 것이라고 명시해주어야 한다. 그런데 만약 이렇게 추가하고싶은 기능을 가진 plugin들이 한 100개가 넘어가고, 이렇게 수많은 plugin들을 일일히 명시한다는 것은 굉장한 고역일 것이다. 따라서 babel에서는 기본적으로 필요한 plugin들을 모아서 preset을 만들었다. 대부분의 환경에서 기본적으로 사용할 수 있도록 babel에서 공식적으로 만든 preset목록은 아래와 같다.

 

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

 

즉, preset을 이용하면 사용자가 일일이 .babelrc에 어떤 plugin을 사용할지 명시하지 않아도, preset만으로 자동으로 한번에 설치가 된다는 것을 의미한다. 우리는 위에서 두가지 preset을 썼는데, @babel/preset-env는 ES6이상으로 작성된 JS를 ES5로 컴파일 해주고 target으로 지정한 브라우저에 대해 코드변환을 최적화해준다. @babel/preset-react는 react에서 사용하는 jsx문법을 JS로 바꿔준다.

 

webpack은 간단하게 말하면 모듈 번들러로써, 개발하게 되면 수많은 모듈을 사용하게 되는데 이러한 모듈들을 webpack이 서로 묶어주어 하나의 파일로 만들어 준다. webpack-cli는 npm script를 통해 webpack 커맨드를 사용하기 위해서 필요하다. webpack-dev-server는 로컬에서 사용할 개발용 서버를 제공한다. 이 기능을 사용하여 소스 파일을 감시하면서 내용이 변경될 때 마다 번들을 다시 컴파일 한다. css-loader, style-loader, html-webpack-plugin는 아래에서 설명하도록 하겠다.

 

8, webpack의 configuration을 관리할 파일인 webpack.config.js를 프로젝트의 root 디렉토리에 만들어준다. 

touch webpack.config.js

 

9. webpack.config.js 파일은 webpack이 어떻게 코드 변환작업을 할지에 대한 상세정보를 명시하기 위해 사용한다.

var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = { 
    entry: './app/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'index_bundle.js'
    },
    module: {
        rules: [
            {test: /\.(js)$/, use:'babel-loader'},
            {test: /\.css$/, use:['css-loader', 'style-loader']}
        ]
    },
    mode: 'development',
    plugins: [
        new HtmlWebpackPlugin ({
            template: 'app/index.html'
        })
    ]
}

 

  •  entry: 어디서부터 빌드 작업을 시작할지 명시한다.
  •  module: 어떤 규칙에 따라 모듈을 이용하여 코드 변환 작업을 진행할지 명시한다. loader는 지정해준 파일에 어떠한 작업을 진행하는 역할을 한다고 보면된다. test키에는 어떤 타입의 파일에 적용할지를 정하고, use는 그 해당하는 파일 종류에 어떤 loader를 적용할지를 명시해준다. 위의 코드에 따르면, babel-loader는 webpack이 js파일을 대상으로하여  ES6 이상으로 작성된 JS 코드를 ES5 JS코드로 변환하게 만든다.(실제 변환은 babel이 함) css-loader와 style-loader는 css파일을 대상으로 적용된다. css-loader는 css를 javascript로 바꿔주고 styled-loader는 javascript로 변경된 css를 DOM에 추가해준다.
  • output: 변환을 거쳐 번들링된 파일을 어디에 저장할지(path), 어떤 파일이름으로 저장할지(filename) 명시
  • mode:  webpack을 이용해 빌드할 때, 여러 작업에 대한 default optimization 값이 development 모드인지  production 모드인지에 따라 다르기 때문에 이를 명시해주는 것이다. mode의 종류는 development, production, none이 있으며 none인 경우에는 각각의optimization 작업에 대한 값을 그냥 default값으로 지정한다는 의미이다.
  • plugins: loader로 부족한? 약간 아쉬운작업들을 plugin을 통해 webpack의 기능을 확장할 수 있다. 위의 코드를 예시로 HtmlWebpackPlugin은 번들이 완료된 파일을 <script/>를 이용해 로드한 html파일을 자동으로 생성해주는 플러그인이다.

 

10. 위의 코드에서 js파일의 경우 babel-loader를 사용하도록 작성하였는데, babel이 변환작업을 잘 할 수 있도록 babel-config 파일에 babel preset에 대해 명시해주어야 한다. 아래처럼 package.json 파일에서 babel config를 명시할 수도 있다.

"main": "index.js",
 "babel":{
    "presets" : [
      "@babel/preset-env",
      "@babel/preset-react"
    ]
 },
 "scripts": {
    "create": "webpack"
 }

대부분의 경우에는 root폴더에 .babelrc 파일을 따로 만들어서 아래와 같이 명시한다.

{
    "presets": [
        "@babel/env",
        "@babel/react"
    ]
}

 

그리고나서 npm run create를 터미널에서 실행시키면 webpack이 dist 폴더안의 bundler 파일을 index.html파일과 함께 넣어준다.

하지만 코드가 바뀔 때 마다 이러한 빌드 과정을 webpack 명령어 통해 매번 실행시키는 것은 매우 귀찮으므로 webpack-dev-server를 실행시켜 이러한 귀찮은 작업들을 줄일 수 있다. 

 

"scripts": {
    "start": "webpack-dev-server --open"
  }

 

이제 npm run start를 실행시키면 webpack-dev-server가 실행되고 --open을 사용하면 서버가 뜨면 자동으로 브라우저가 열리고 서버에 접속까지 해준다. 위에서 적었지만 webpack-dev-server는 로컬에서 사용할 개발용 서버를 제공하고, 소스 파일을 감시하고 내용이 변경될 마다 번들을 다시 컴파일한다.

 

이 모든 작업을 거치고 나면 디렉토리 모습은 아래와 같다. 참고로 README.md는 깃헙 repo의 프로젝트 파일에다 작업해서 들어있는 것이므로 무시해도됨.

 

 

  • 작동화면

결국 webpack은 지정해준 모듈(babel-loader, css-loader, styles-loader)들을 사용하여 entry로 지정해준 index.html부터 시작해서 모든 변환작업을 진행한다. babel loader는 webpack이 js파일들에 대해 변환 작업을 하게 만들고, 그 변환작업은 babel이 지정된 preset을 이용하여 진행한다. 

 

webpack과 babel이 어떻게 돌아가는지와 어렴풋하게 짐작만했던 용어들에 대해 짚고 넘어갈 수 있어서 속이 시원하다. 추가적인 config option들에 대해서는 더 살펴봐야 할 것 같고 대부분 사람들이 어떤 경우에 어떤 플러그인들을 사용하는지도 좀 더 찾아봐야할 것 같다.