React (with. CRA) 프로젝트에 Typescript 적용시키기

공부하고 기록하기·2023년 4월 28일
0

TypeScript

목록 보기
3/3
post-thumbnail

기존 CRA로 생성한 리액트 프로젝트를 타입스크립트로 마이그레이션하기로 했다. 타입스크립트로 마이그레이션 하기위해서는 다음과 같은 절차들이 필요하다.

🔥 필요한 패키지 설치

npm install --save-dev typescript @types/node @types/react @types/react-dom 
@typescript-eslint/eslint-plugin @typescript-eslint/parser css-loader style-loader

다음 패키지들은 개발 시에만 필요한 의존성(dependency) 패키지들이므로 --save-dev 플래그를 붙여서 설치한다.

  • typescript: TypeScript 언어를 사용할 수 있도록 도와주는 패키지
  • @types/node: Node.js에서 사용되는 라이브러리를 TypeScript에서 사용할 수 있도록 타입 정의 파일을 제공
  • @types/react: React 라이브러리를 TypeScript에서 사용할 수 있도록 타입 정의 파일을 제공
  • @types/react-dom: React DOM 라이브러리를 TypeScript에서 사용할 수 있도록 타입 정의 파일을 제공
  • @typescript-eslint/eslint-plugin: TypeScript와 함께 사용되는 ESLint를 지원하기 위한 플러그인
  • @typescript-eslint/parser: TypeScript 코드를 파싱하여 ESLint가 이해할 수 있는 AST(Abstract Syntax Tree)로 변환해주는 파서
  • css-loader: webpack에서 CSS 파일을 해석하고, CSS 모듈을 가져오기 위한 로더
  • style-loader: webpack에서 CSS 파일을 해석하고, 스타일 태그를 HTML에 삽입하기 위한 로더

🔥 tsconfig.json 파일 생성

npx tsc --init

🔥 tsconfig.json 설정

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "allowJs": true, // TypeScript 컴파일러가 JavaScript 파일도 컴파일
    "alwaysStrict": true,
    "baseUrl": ".", // 절대 경로를 사용할 때 기준 경로를 지정
    "esModuleInterop": true, // CommonJS 모듈을 ES6 모듈로 변환할 때 import 구문을 사용
    "downlevelIteration": true, // ES6 이터레이터를 ES5 코드로 변환할 때 이터레이터 코드를 낮은 버전의 JavaScript 엔진에서도 실행
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true, // 각 파일이 독립된 모듈로 처리
    "jsx": "react", // JSX 구문을 사용
    "lib": ["dom", "es2017"], // 사용할 라이브러리
    "module": "esnext", // 사용할 모듈 시스템
    "moduleResolution": "node", // 모듈 해석 방식
    "noEmit": true, // 컴파일 결과를 출력하지 않도록 지정
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "paths": {
      "@/*": ["src/*"]
    },
    "resolveJsonModule": true,
    "skipLibCheck": true, // 선언 파일 (.d.ts 파일)의 타입 체크를 비활성화. 컴파일 시간이 더 빨라짐
    "strict": true,
    "target": "es5",
    "incremental": true // 증분 컴파일을 활성화하여 컴파일 속도를 높임
  },
  "exclude": [
    "node_modules",
    "shorturl.js",
    "sentry.js",
    "injectEnv.js"
  ], // 컴파일하지 않을 파일 또는 디렉토리를 지정
  "include": ["**/*.ts", "**/*.tsx"] // 컴파일 할 파일 또는 디렉토리를 지정
}

🔥 .d.ts 파일 생성

나의 경우에는 각각의 import 구문에서 에러가 발생했다.

  • svg 파일 import 에러
  • .module.css 파일 import 에러

원인은 이러하다. 일부 라이브러리 및 모듈은 해당 파일 내부에서만 선언되지 않고 외부 패키지나 라이브러리에서 가져와 사용하는 경우가 많다. 이러한 경우 해당 모듈이 어떤 타입으로 선언되어 있는지 TypeScript에 알려주어야 한다.

이러한 경우 src폴더에 custom.d.ts 파일을 생성하고 다음과 같은 선언을 작성함으로써 TypeScript에서 해당 모듈 및 파일의 타입 선언을 인식할 수 있도록 하면 에러를 해결할 수 있다.

// src/custom.d.ts

declare module "*.svg" {
  const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
  export default content;
}
declare module "*.css";

추가한 custom.d.ts 파일을 tsconfig.json 파일에서 include 해줘야 한다. 만약 custom.d.ts 파일을 include 설정에 명시하지 않았다면, TypeScript 컴파일러는 해당 파일을 인식하지 못하기 때문이다.

{
	"compilerOptions": {
		...
		"include": ["src/custom.d.ts"],
	}
}

🔥 각 모듈마다 import React from ‘react’; 추가

jsx 문법을 사용할 경우 import React from 'react';를 추가하지 않으면 다음과 같은 에러가 발생한다. 따라서 각 모듈마다 React를 import 해주어야 한다.

리액트를 사용하고 있는데 React를 import해주지 않아서 발생한 에러다. 역시 타입스크립트는 자바스크립트보다 엄격하다. 그만큼 코드의 안정성이 높아지는 것이니 좋다고 생각한다.

'React'() UMD 전역을 참조하지만 현재 파일은 모듈입니다. 대신 가져오기를 추가해 보세요.

🔥 절대경로 설정

baseUrl과 paths 설정을 사용하면 타입스크립트에서 상대경로 대신 절대경로로 모듈을 불러올 수 있다. 예를 들어, 아래와 같은 설정을 하면 src 폴더 내부에 있는 모듈들을 @ 식별자를 사용해 불러올 수 있다.

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["*"]
    }
  }
}

하지만 이 설정만 추가해서는 제대로 동작하지 않는다. tsconfig.json 파일에서 baseUrlpaths 설정만 추가하고 이후 과정을 건너뛴다면, 컴파일러는 타입스크립트 코드를 컴파일할 때 설정된 절대경로를 인식하지 못해 모듈을 찾지 못하고 컴파일 에러가 발생한다.

이를 해결하기 위해서는, 추가적으로 webpack 설정이 필요하다. 웹팩에서는 타입스크립트 컴파일러가 인식할 수 있도록 설정을 추가할 수 있다.

CRA 프로젝트에서는craco 패키지를 사용하여 CRA의 Webpack 설정을 커스터마이징하는 방법이 있다.

  1. 먼저 craco 패키지를 설치한다.
npm install @craco/craco --save-dev
  1. 프로젝트 루트 디렉토리에 craco.config.js 파일을 생성한다.
const path = require("path");

module.exports = {
  webpack: {
    alias: {
      "@assets": path.resolve(__dirname, "src/assets"),
      "@components": path.resolve(__dirname, "src/components"),
      "@hooks": path.resolve(__dirname, "src/hooks"),
      "@pages": path.resolve(__dirname, "src/pages"),
      "@recoil": path.resolve(__dirname, "src/recoil"),
      "@utils": path.resolve(__dirname, "src/utils"),
    },
  },
};
  1. package.json 파일의 scripts에서 start, build 명령어를 수정한다.
"scripts": {
  "start": "craco start",
  "build": "craco build"
}
  1. 프로젝트 루트 디렉토리에 tsconfig.paths.json 파일을 생성한다. tsconfig.paths.json 파일은 꼭 필요한 것은 아니지만, tsconfig.json 파일에 paths 설정을 넣어놓으면 해당 설정이 매우 길어질 수 있다. 따라서 보통 tsconfig.paths.json ****파일을 만들어서 paths 설정을 따로 관리한다.
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@assets/*": ["./assets/*"],
      "@components/*": ["./components/*"],
      "@hooks/*": ["./hooks/*"],
      "@pages/*": ["./pages/*"],
      "@recoil/*": ["./recoil/*"],
      "@utils/*": ["./utils/*"]
    }
  }
}
  1. tsconfig.json 파일에는 다음과 같이 extends 속성을 이용해서 tsconfig.paths.json 파일을 참조하도록 설정한다.
{
  "extends": "./tsconfig.paths.json",
  "compilerOptions": {
    // 컴파일러 옵션 설정
  }
}

이러한 과정을 거치면 컴파일러가 절대경로를 제대로 인식하여 import 구문 에러가 발생하지 않는다.

  • 참고 ↓

https://stackoverflow.com/questions/44717164/unable-to-import-svg-files-in-typescript

profile
Better than yesterday

0개의 댓글