React CRA를 이용하지 않고 프로젝트 생성(2)

9161 단어 typescriptReactReact

React CRA를 이용하지 않고 프로젝트 생성(1)에 이어서 추가로 Typescript를 사용 할 수 있게 구성해 보겠습니다.

타입스크립 관련 모듈을 설치 합니다.

// 타입스크립트를 사용하기 위한 기본 모듈
npm i -g typescript
or
npm i -D typescript

npm i -D @types/react @types/react-dom //리액트에서 사용할 타입 모듈
npm i -D ts-loader // 웹팩에서 타입스크립트 사용하기 위한 모듈

ts-loader는 tsconfig.json파일은 참고해서 동작하므로
tsconfig.json파일은 생성해 줍니다.

기본 tsconfig.json 생성
tsc --init // 글로벌 설치시
or
npx tsc --init // 로컬 설치시

tsconfig 환경을 아래와 같이 설정해준다.

{
    "compilerOptions": {
        "target": "es6", // 자바스크립트 버전 es6=es2015와 같고 현재 es2021까지 지원합니다.
        "module": "commonjs", // 어떤모듈로 생성할지 지정
        "rootDir": "./src", // 소스파일 위치
        "outDir": "./dist", // 배포파일 생성 위치
        "jsx": "react", // jsx 컴파일 방법 지정 (preserve, react, react-native)
        "moduleResolution": "node", // 모듈 해성방법 지정 (classic, node)
        "esModuleInterop": true, // es6와 commonjs간 모듈 호환성을 위해 true로 설정한다.
        "forceConsistentCasingInFileNames": true, // 대소문자를 정확히 구별할지 강제
        "strict": true, // 타입 체킹 동작 활성
        "skipLibCheck": true, // 모든 선언 파일 (.d.ts)의 타입 체킹을 스킵하도록 설정
        "sourceMap": true, // map파일 생성 여부
        "noImplicitAny": true, // <any> 타입을 허가하지 않음         
    },
    "exclude": ["node_modules"],
    "typeRoots": ["node_modules/@types"], //타입 스크립트가 정의되 있는 type을 찾는 위치 include 위치에 있는 .d.ts 파일은 자동으로 인식되므로 추가할 필요 없음
    "include": ["src/**/*"] // 티입 추가 위치
}

기존 webpack.config.js 파일을 아래와 같이 수정한다.

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

// 번들 파일 생성 위치
const bundlePath = path.resolve(__dirname, 'dist/');
const jsxRegex = /\.(js|jsx)$/; // js, jsx regex
const tsxRegex = /\.(ts|tsx)$/; // ts, tsx regex
//  /\.ts(x?)$/,

module.exports = (_env, argv) => {
    let entryPoints = {
        main: {
            path: './src/index.tsx',
            outputHtml: 'main.html',
            build: true
        },
        config: {
            path: './src/config.tsx',
            outputHtml: 'config.html',
            build: false
        }
    };

    let entry = {};

    // 웹팩 플러그인 추가 구성
    let plugins = [new webpack.HotModuleReplacementPlugin()];

    Object.entries(entryPoints).map(([key, value]) => {
        if (value.build) {
            entry[key] = value.path;
            if (argv.mode === 'production') {
                plugins.push(
                    new HtmlWebpackPlugin({
                        inject: true,
                        chunks: [key],
                        template: './public/template.html',
                        filename: value.outputHtml
                    })
                );
            }
        }
        return null;
    });

    let config = {
        entry,
        optimization: {
            minimize: true // 불필요한 코드 최적화
        },
        devtool: 'source-map', // Webpack의 출력물에서 디버깅을 하기위해 소스 맵을 허용합니다.
        module: {
            rules: [
                {
                    test: tsxRegex,
                    exclude: /(node_modules|bower_components)/,
                    use: [
                        {
                            loader: 'ts-loader'
                        }
                    ]
                },
                {
                    // test: /\.(js|jsx)$/,
                    test: jsxRegex,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                                '@babel/preset-react'
                            ],
                            plugins: []
                        }
                    }
                },
                {
                    test: /\.css$/,
                    use: ['style-loader', 'css-loader']
                },
                {
                    test: /\.(jpe?g|png|gif|svg)$/i,
                    loader: 'file-loader',
                    options: {
                        name: 'img/[name].[ext]'
                    }
                },
                // 모든 '.js' 출력 파일은 'source-map-loader'에서 다시 처리한 소스 맵이 있습니다.
                {
                    enforce: 'pre',
                    test: /\.js$/,
                    loader: 'source-map-loader'
                }
            ]
        },
        resolve: { extensions: ['*', '.js', '.jsx', '.ts', '.tsx'] },
        output: {
            filename: '[name].js',
            path: bundlePath
        },
        plugins
    };

    if (argv.mode === 'development') {
        config.devServer = {
            static: './public',
            host: 'localhost',
            // host: '192.168.0.242',
            // host: '0.0.0.0',
            port: 3008,
            historyApiFallback: {
                index: 'index.html'
            },
            hot: true
            // allowedHosts: ['com.arenacast.io'],
        };
        config.performance = {
            hints: false,
            maxEntrypointSize: 512000,
            maxAssetSize: 512000
        };
        config.output = {
            // publicPath: "/",
            path: path.resolve(__dirname, './dist'),
            filename: '[name].js'
        };
    }
    return config;
};

rules에 ts-loader 추가
resolve - extensions 에 .ts, .tsx 확장자 추가

타입스크립트로 index 모듈을 작성 한다.
index.tsx

import * as React from 'react';
// import * as ReactDOM from 'react-dom';
import * as ReactDom from 'react-dom/client';

import { Hello } from './components/hello';

const container = document.getElementById('example');
if (!container) throw new Error('Failed to find the root element'); // react18 에서 typescript지원부분 오류로 인한 에러 type업데이트 될때 까지 해당 코드를 사용한다.

// react18에서는 render가 createRoot로 변경
ReactDom.createRoot(container).render(
    <React.StrictMode>
        <Hello compiler="TypeScript" framework="React" />
    </React.StrictMode>
);

react18로 업데이트 되면서
몇가지 변경점이 생겼다
react-dom > react-dom/client로 변경
ReactDom.render > ReactDom.createRoot(container).render 로 변경

// react18 에서 typescript지원부분 오류로 인한 에러 type업데이트 될때 까지 해당 코드를 사용한다.
if (!container) throw new Error('Failed to find the root element');

실행해보자
npm run start

by Kazel

좋은 웹페이지 즐겨찾기