CRA 방식이 아닌 방법으로 직접 설치하기

개요

CRA로 설치하면 너무 편하지만, React를 직접 설치 하는 방법을 알아보자. \
서비스 개발하다보면 CRA의 기본 설정 값을 변경해야 할때가 온다. SSR, code splitting 등등... \
이럴 때를 대비해 먼저 공부해두면 좋다.

Library 설치

npm init 으로 empty project를 만들고 아래의 Library를 추가한다. \
webpack을 설치하는 이유는 번들링하기 위함인데, Babel를 필수적으로 사용해야 한다. \
React는 JSX문법으로 이루저 있는데, 이 문법이 브라우저에서는 작동을 안한다. \
그래서 Babel를 이용해서 브라우저가 이해할 수 있는 문법으로 변환해주어야 하기 때문이다.

express 설치하는 이유는 root path가 아닌 path로 접근 시 404에러가 발생하기 때문이다. \
이유는 SPA이다보니, 각각의 path마다 페이지가 존재 하는게 아니라 하나의 페이지에서 여러 path를 client단에서 제어하기 때문에 직접 다양한 path로 접근 시 에러가 발생한다.

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

Webpack 설정

Project Root Directory에 webpack.config.js를 만들고 아래와 같이 작성한다. \
설정파일에 대해 설명하자면,

  • index.js 중심으로 번들링이 시작되고, path에 이미 어떤 파일이 있으면 지우고 시작한다.
  • code splitting이 optimization object에 이루어지고 있다.
  • webpack은 기본적으로 js파일만 번들링이되기 때문에 css 파일을 읽으려면 추가 설정을 해야한다.
  • css-loader가 css 파일을 읽고, style-loader가 읽어드린 css 파일 정보를 html 문서에 추가한다.
  • 번들링 후 code splitting된 js 파일과 css정보를 추가해야할 html문서는 HtmlWebpackPlugin이 생성해주고 있다.

webpack.config.js

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

module.exports = {
    entry: './src/index.js',
    output: {
        filename: "[name].bundle.js",
        path: path.resolve(__dirname, './dist'),
        clean: true
    },
    mode: 'development',
    optimization: {
        splitChunks: {
            chunks: "all",
            name: false
        },
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader','css-loader']
            },
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/env', '@babel/preset-react']
                    }
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "src/index.html"
        }),
    ]
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

Client Side React Source Code

리엑트 소스를 간단하게 추가해보자.

index.js

import React from 'react'
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import App from "./component/App";

ReactDOM.render(<BrowserRouter><App/></BrowserRouter>, document.getElementById('root'))

App.js

import React from 'react'
import { Routes, Route } from "react-router-dom";
import Home from "./Home";
import About from "./About";

export default function App() {
    return (
      <div>
        <h1>hello world</h1>
        <Routes>
            <Route exact path={'/'} element={<Home/>}/>
            <Route path={'/about'} element={<About/>}/>
        </Routes>
      </div>
    )
}

Server Side Source Code

const http = require('http');
const express = require('express')
const path = require('path')
const fs = require('fs')
const app = express();

const index = fs.readFileSync(path.resolve(__dirname, '../dist/index.html')).toString();
app.use('/', express.static(path.resolve(__dirname, '../dist')));
app.get('*', (req, res) => {

    res.send(index);
})

const server = new http.Server(app);
server.listen(3000, (err) => {
    if (err) {
        return console.error(err);
    }
});

실행 및 테스트

webpack으로 client side에 대해 번들링을 하여 server에서 읽어드릴 index.html를 획득하고, 그다음에 server 코드를 실행해서 앱을 실행한다.

"npm run dev": "webpack --config webpack.config.js && node src/server.js"