본문 바로가기

FrontEnd/react

CRA 없이 React 프로젝트 구성하기

webpack이나 babel에 추가적인 설정을 위해서 cra 를 사용하지 않고 react 프로젝트를 직접 구성해야 할 때가 있다.

이 때를 위해 CRA 없이 React 프로젝트를 구성해보자. 

 

해당 프로젝트 : https://github.com/easdkr/react-widthout-cra

 

GitHub - easdkr/react-widthout-cra

Contribute to easdkr/react-widthout-cra development by creating an account on GitHub.

github.com

 

사용한 주요 패키지의 버전 

  • babel : 7.18.5
  • react : 18.2.0
  • webpack:5.73.0
  • webpack-cli:4.10.0
  • webpack-dev-server:4.9.2

 

1. 프로젝트 초기화 

npm init -y

2. React 패키지 설치

yarn add react react-dom

3. typescript, react type 패키지 설치

yarn add -D typescirpt @types/react @types/react-dom

4. typescript config 초기화

node_modules/.bin/tsc --init //전역으로 typescript가 설치되어있다면 그냥 tsc --init

5. tsconfig.json 설정

 

tsconfig.json

{
  "compilerOptions": {

    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "lib": ["DOM", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "ES2021", "ES2022"],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    "jsx": "preserve",                                /* Specify what JSX code is generated. */
  
    /* Modules */
    "module": "ESNext",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    "baseUrl": "src",                                  /* Specify the base directory to resolve non-relative module names. */
    "types": [
      "react/next",
      "react-dom/next",
    ],                                      /* Specify type package names to be included without being referenced in a source file. */

    /* JavaScript Support */
    "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */

    /* Emit */
     "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    "outDir": "./dist",                                   /* Specify an output folder for all emitted files. */

    /* Interop Constraints */
    "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */

    /* Completeness */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "exclude": ["node_modules"],
  "include": ["**/*.ts", "**/*.tsx", "src"]
}

6. babel 패키지 설치

yarn add -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
  • babel-loader : webpack에서 babel을 loader로 사용하기 위한 패키지
  • babel : babel의 핵심기능을 가진 패키지
  • babel/preset-env : ECMAScript2015+를 위한 프리셋 패키지인데 IE가 없어지는 지금 필요한지는 잘 모르겠다. 
  • babel/preset-typescript : 타입스크립트를 변환하기 위한 프리셋

 

7. babel config

babel.config.json

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

8. webpack 패키지 설치 

yarn add -D webpack webpack-cli webpack-dev-server html-webpack-plugin ts-loader
  • webpack-dev-server : 개발 도중 변경사항 확인을 위한 패키지
  • html-webpack-plugin : html 수정을 자동으로 하는 플러그인 
  • ts-loader : ts 코드를 js로 변환 

 

9. webpack 설정 파일 생성

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const prod = process.env.NODE_ENV === 'production';

module.exports = {
    mode: prod ? 'production' : 'development',
    devtool: prod ? 'hidden-source-map' : 'eval',
    entry: './src/index.tsx',
    
    resolve : {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        modules: [
            path.join(__dirname, "src"),
            "node_modules"
          ]
    },

    module : {
        rules : [
            {
                test: /\.tsx?$/,
                use: ['babel-loader', 'ts-loader']
            },
        ]
    },

    output : {
        path: path.join(__dirname, '/dist'),
        filename: 'bundle.js'
    },

    plugins:[
        new webpack.ProvidePlugin({
            React: 'react',
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new webpack.HotModuleReplacementPlugin(),
    ],

    devServer : {
        historyApiFallback: true,
        port : 3000,
        hot: true,
        compress:true,
        static: {
            directory: path.join(__dirname, 'public')
        }
    },

}

webpack 5의 설정 관련 사항은 https://webpack.kr/configuration/mode/ 여기서..

여기서 resolve.modules 설정은 절대경로 import 를 위해 추가함 

 

10. package.json scripts 수정

package.json

...

"scripts": {
    "dev": "webpack serve --mode development --open --hot",
    "build": "webpack --mode production",
    "prestart": "npm run build",
    "start": "webpack --mode development"
 },
  
 ...

11. 엔트리 포인트 코드 작성

src/index.html

<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>React without cra</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>​

src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const rootDom = document.getElementById('root');
if (!rootDom) throw new Error('Failed to find the root element');
const root = ReactDOM.createRoot(rootDom);

root.render(
    <App/>
)

src/App.tsx

import Button from "Components/Button";
import React from "react";

export default function App() {
    return (
        <>
            <Button>Test</Button>
        </>
    )
};

*Button 컴포넌트는 예제 프로젝트 github 참고하고 아무 컴포넌트나 띄워보자!