React+ 1: React + TypeScript 세팅

Ho Kim·2022년 11월 16일
1

리액트 플러스

목록 보기
1/3

참고 링크 :
1. CRA없이 React+TypeScript 환경 구축하기
2. [Babel] Transfile과 Pollyfill 그리고 Babel에서 Pollyfill 적용 방식

create-react-app으로 생성하면 앱이 너무 무겁게 돌아가니 좀 가볍게 꼭 필요한것만 넣어서 구성해보자.

1. pacakge 설치

먼저 npm 환경을 만들어준다.

npm init -y

1) 리액트 모듈 설치

npm i react react-dom react-router-dom

리액트 관련 모듈을 설치한다.

  1. react
    react 컴포넌트를 정의 하는데 필요한 기능이 포함되어 있다.

  2. react-dom
    DOM과의 연결점을 제공한다.
    ex) DOM에 Element 생성

  3. react-router-dom
    라우터(페이지 이동)에 필요한 기능이 포함되어있다.

2) 바벨 설치

Babel은 현재 및 이전 브라우저 또는 환경에서 ECMAScript 2015+ 코드를 이전 버전과 호환되는 JavaScript 버전으로 변환하는 데 주로 사용되는 도구 체인이다.

즉 최신 버전의 JavaScript를 이전 버전과 호환되도록 다시 변환시켜주는 일종의 컴파일러라고 보면 된다.

npm i @babel/core @babel/preset-react @babel/preset-env @babel/preset-typescript @babel/plugin-transform-runtime

바벨 관련 모듈을 설치한다.

  1. @babel/core
    babel 기본 기능이 포함되어 있다.

  2. @babel/preset-react @babel/preset-env @babel/preset-typescript
    많이 쓰이는 툴은 프리셋이 npm에 올라와있다. env, react, typescript에 대해 바벨 프리셋을 설치해 사용한다.

  3. @babel/plugin-transform-runtime
    이미 사용한 helper code를 재사용 할 수 있게 도와준다.

3) 타입스크립트 설치 및 설정

3-1) 타입스크립트 모듈 설치

npm i typescript @types/react @types/react-dom
  1. typescript
    typescript를 읽을 수 있는 표준 기반 JavaScript로 컴파일한다.

  2. @types/react
    react에서 사용되는 type 정의를 추가한다.

  3. @types/react-dom
    react-dom에서 사용되는 type 정의를 추가한다.

3-2) 타입스크립트 설정

기본 설정 파일을 생성한다.

tsc --init

기본 설정 파일 내부에서는 기능이 주석으로 포함되어있으므로 잘 보고 필요한 기능을 추가하면 된다.

{
  "compilerOptions":
  {
  /* 기본 옵션 */
  "incremental": true, /* incremental compilation 사용 */
  "target": "es5", /* ECMA스크립트 대상 버전 지정: 'ES3'(기본값), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020' 또는 'ESNEXT' */
  "module": "commonjs", /* 모듈 코드 생성을 지정. 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020' 또는 'ESNext'. */
  "lib": [], /* 컴파일에 포함할 라이브러리 파일을 지정. */
  "allowJs": true, /* Javascript 파일의 컴파일을 허용. */
  "checkJs": true, /* .js 파일의 오류를 보고. */
  "jsx": "preserve", /* JSX 코드 생성을 지정. 'preserve', 'react-native', 'react-jsx' 또는 'react-jsxdev'. */
  "declaration": true, /* 해당하는 '.d.ts' 파일을 생성. */
  "declarationMap": true, /* 해당하는 각 '.d.ts' 파일에 대한 소스 맵을 생성. */
  "sourceMap": true, /* 해당하는 '.map' 파일을 생성. */
  "outFile": "./", /* 연결하여 단일 파일로 출력. */
  "outDir": "./", /* 출력 위치 지정. */
  "rootDir": "./", /* 입력 파일의 루트 디렉터리를 지정. --outDir로 출력 디렉터리 구조를 제어하는 데 사용. */
  "composite": true, /* 프로젝트 컴파일 활성화 */
  "tsBuildInfoFile": "./", /* 증분 컴파일 정보를 저장할 파일 지정 */
  "removeComments": true, /* 출력할 주석을 내보내지 않음. */
  "noEmit": true, /* emit을 출력하지 않음. */
  "importHelpers": true, /* 'tslib'에서 emit helpers를 가져옵니다. */
  "downlevelIteration": true, /* 'ES5' 또는 'ES3'를 대상으로 할 때 'for-of', spread 및 destructuring에서 반복 가능한 항목을 완벽하게 지원. */
  "isolatedModules": true, /* 각 파일을 별도의 모듈로 변환('ts.transpileModule'과 유사). */
  /* 엄격한 유형 확인 옵션 */
  "strict": true, /* 모든 엄격한 유형 검사 옵션을 활성화. */
  "noInmplicitAny": true, /* 'Any' 유형의 표현식 및 선언에 오류를 발생 시킴*/
  "strictNullChecks": true, /* 엄격한 Null 검사를 사용. */
  "strictFunctionTypes": true, /* 기능 유형을 엄격하게 검사. */
  "strictBindCallApply": true, /* 함수에 대해 엄격한 'bind', 'call' 및 'apply' 메서드를 활성화. */
  "strictPropertyInitialization": true, /* 클래스에서 속성 초기화를 엄격하게 검사합. */
  "No Implicit"true, /* 'Any' 유형을 암시하는 'this' 표현식에 오류를 제기. */
  "alwaysStrict": true, /* strict 모드에서 구문 분석하고 각 소스 파일에 대해 "엄격하게 사용"을 내보냄. */
  /* 추가 점검*/
  "no Unused Locals": true, /* 사용하지 않는 로컬 변수에 대한 오류를 보고. */
  "noUnusedParameters": true, /* 사용되지 않는 매개 변수에 대한 오류를 보고. */
  "noImplicitReturns": true, /* 함수의 모든 코드 경로가 값을 반환하지 않을 때 오류를 보고. */
  "noFallthroughCasesInSwitch": true, /* 스위치 문에서 누락케이스 오류를 보고. */
  "noUncheckedIndexedAccess": true, /* index signature 결과에 'undefined' 포함 */
  "noPropertyAccessFromIndexSignature": true, /* 요소 액세스를 사용하려면 index signature에서 선언되지 않은 속성이 필요. */
  /* 모듈 Resolution 옵션 */
  "moduleResolution": "node", /* 모듈 Resolution 전략을 지정. 'node'(Node.js) 또는 'classic'(TypeScript 이전 1.6). */
  "baseUrl": "./", /* 절대적이지 않은 모듈 이름을 확인하는 기본 디렉터리. */
  "paths": {}, /* 가져오기를 'baseUrl'을 기준으로 조회 위치에 다시 매핑하는 일련의 항목. */
  "rootDirs": [], /* 런타임에 결합된 내용이 프로젝트 구조를 나타내는 루트 폴더 목록. */
  "typeRoots": [], /* 형식 정의를 포함할 폴더 목록. */
  "types": [], /* 컴파일에 포함할 선언 파일을 입력. */
  "allowSyntheticDefaultImports": true, /* 기본 내보내기 없이 모듈에서 기본 가져오기를 허용. 코드 출력에는 영향을 주지 않고 검사만 입력. */
  "esModuleInterop": true, /* 모든 가져오기에 대해 네임스페이스 개체를 생성하여 CommonJS와 ES 모듈 간의 상호 운용성을 내보냄. 'AllowSyntheticDefaultImports'를 의미. */
  "preserveSymlinks": true, /* Symlinks의 실제 경로를 확인하지 않음. */
  "allowUmdGlobalAccess": true, /* 모듈에서 UMD 글로벌 액세스를 허용. */
  /* 소스 맵 옵션 */
  "sourceRoot": /* 디버거가 소스 위치 대신 TypeScript 파일을 찾아야 하는 위치를 지정. */
  "mapRoot": /* 디버거가 생성된 위치 대신 지도 파일을 찾아야 하는 위치를 지정. */
  "inlineSourceMap": true, /* 별도의 파일 대신 소스 맵이 있는 단일 파일을 내보냄. */
  "inlineSources": true, /* 단일 파일 내에서 소스 맵과 함께 소스를 내보냄. '--inlineSourceMap' 또는 '--sourceMap'을 설정해야함. */
  /* 실험 옵션 */
  "experimentalDecorators": true, /* ES7 장식자에 대한 실험적 지원을 가능하게 함. */
  "emitDecoratorMetadata": true, /* 데코레이터에 대한 방출형 메타데이터에 대한 실험적 지원을 가능하게 함. */
  /* 고급 옵션 */
  "skipLibCheck": true, /* 선언 파일의 형식 검사를 스킵. */
  "forceConsistentCasingInFileNames": true /* 동일한 파일에 대한 대소문자가 일치하지 않는 참조를 허용하지 않음. */
  }
}

생성된 tsconfig.json의 내용을 다음과 같이 채워넣는다.

{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "baseUrl": "./",

    "isolatedModules": true,
    "strict": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
  }
}

webpack 버전이 올라가면서 더이상 node의 기본 모듈을 불러오지 않게 되었는데, 그로 인해 os, fs, url등을 사용하려는 경우 오류가 발생한다.
moduletarget을 잘못설정하면 fs모듈을 찾을 수 없다는 오류가 뜬다.

4) 웹팩 설치 및 설정

4-1) 웹팩 모듈 설치

npm i webpack webpack-cli webpack-dev-server webpack-merge
npm i babel-loader css-loader style-loader ts-loader 
npm i interpolate-html-plugin html-webpack-plugin clean-webpack-plugin

npm i dotenv
  1. webpack
    Webpack은 모듈 번들러다. 브라우저에서 사용하기 위해 JavaScript 파일을 번들로 묶어준다.

  2. webpack-cli
    webpack CLI는 사용자 지정 webpack 프로젝트를 설정할 때 개발자가 속도를 높일 수 있도록 유연한 커맨드를 제공한다.
    자세한 커맨드 내용은 링크를 참조하면 된다.

  3. webpack-dev-server
    실시간 재로딩을 제공하는 개발 서버를 실행시킨다.

  4. webpack-merge
    구성 객체를 병합해준다. 개발 웹팩 설정과 배포 웹팩 설정을 다르게 사용해야 할 때 사용한다.

  5. babel-loader css-loader style-loader ts-loader
    웹팩용 각종 로더이다. 다른 확장자의 파일을 모듈처럼 임포트 할 수 있게 돕는다.

  6. html-webpack-plugin
    기본이 되는 html 파일 위치 지정을 웹팩을 통해 지정한다.

  7. interpolate-html-plugin
    html에서 웹팩에서 지정한 변수를 %변수명% 형식으로 사용할 수 있게 한다.

  8. clean-webpack-plugin
    빌드가 성공하면 빌드 결과물 중 사용되지 않는 모든 웹팩 자산을 제거한다.

  9. dotenv
    환경변수를 편하게 사용하게 해주는 모듈이다.
    루트 위치에 .env 파일을 생성하고 다음과 같이 변수명 앞에 REACT_APP을 붙여 값을 지정한다.

REACT_APP_PORT=3000
REACT_APP_MODE=development

4-2) 웹팩 설정

먼저 모든 웹팩 설정의 기본이 될 webpack.common.js 파일을 생성한다.
내용은 다음과 같다.

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const InterpolateHtmlPlugin = require("interpolate-html-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

const dotenv = require("dotenv");
dotenv.config();

module.exports = {
    mode: process.env.MODE,

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

    devServer: {
        allowedHosts: "all",
        port: process.env.PORT,
        liveReload: true,
        historyApiFallback: true,
        historyApiFallback: {
            disableDotRule: true,
        },
    },

    resolve: {
        fallback: { path: false, fs: false, os: false },
        extensions: [".js", ".jsx", ".ts", ".tsx"],
    },

    module: {
        rules: [
            {
                test: /\.tsx?$/,
                exclude: /nodeModules/,
                use: ["babel-loader", "ts-loader"],
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"],
            },
            {
                test: /\.(png|svg)$/,
                use: [
                    {
                        loader: "file-loader",
                        options: {
                            name: "images/[name].[ext]?[hash]",
                        },
                    },
                ],
            },
        ],
    },

    plugins: [
        new webpack.DefinePlugin({
            "process.env": JSON.stringify(process.env),
        }),
        new InterpolateHtmlPlugin({
            PUBLIC_URL: "static",
        }),
        new HtmlWebpackPlugin({ template: "./public/index.html" }),
        new CleanWebpackPlugin(),
    ],
};

다음으로 webpack.dev.js에 개발용 설정을 추가한다.

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
    mode: "development",
    devtool: "eval",
    devServer: {
        historyApiFallback: true,
        port: 3000,
        hot: true,
    },
});

다음으로 webpack.prod.js에 배포용 설정을 추가한다.

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
    mode: "production",
    devtool: "hidden-source-map",
});

5. npm 스크립트 구성

{
  ...
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "start-prod": "webpack serve --open --config webpack.prod.js",
    "start": "webpack-dev-server --config webpack.dev.js --open --hot"
  },
  ...
 }

2. 디렉토리 구성

디렉토리는 다음과 같이 구성했다.


index.tsx

import React from "react";
import {createRoot} from "react-dom/client";
import App from "./App";

createRoot(document.getElementById("root") as HTMLElement).render(<App />);

App.tsx

import React from "react";
import { BrowserRouter, Route, Routes, Link } from "react-router-dom";

import Home from "./pages/Page_Home"
import Sub from "./pages/Page_Sub";

const App = () => {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Home />}></Route>
                <Route path="/world" element={<Sub />}></Route>
            </Routes>
        </BrowserRouter>
    );
}

export default App;

pages\Page_Home.tsx

import React from "react";

const Home = () => {
    return (
        <>
		hello
        </>
    );
};

export default Home;

pages\Page_Sub.tsx

import React from "react";

const Sub = () => {
    return <>world</>;
};

export default Sub;

위와 같이 작성한 뒤 npm start해주면 다음과 같은 결과를 확인할 수 있다.


코드 링크:
https://github.com/hokim2407/react-typescript/tree/891768c6fe3695a54fe9f89e87f071847651b473

0개의 댓글