본문 바로가기

TypeScript

TypeScript-React

다룰 내용

1. 타입스크립트 프로젝트를 세팅하는 방법 및 Js프로젝트 -> Ts프로젝트 마이그레이션 방법

2. React에서 자주 사용하는 타입

 

타입스크립트 프로젝트를 세팅하는 방법 및 Js프로젝트 -> Ts프로젝트 마이그레이션 방법

Create React App

타입스크립트 프로젝트 세팅하기

  1. 폴더 생성(typescript-react)
  2. npx create-react-app . --template typescript
    • "create-react-app 이라는 것을 사용해서 현재폴더에 프로젝트를 만드는데, 타입스크립트 템플릿을 사용하겠다." 는 의미의 명령어이다.
    •  제대로 생성이 되었다면, tsconfig.json 파일과 함께 프로젝트가 생성된다.
    • . 으로 현재폴더를 만드는 것 대신, 아래와같이 사용할 수도 있다.
    • npx create-react-app 원하는_폴더_이름 --template typescript

JavaScript프로젝트 -> TypeScript프로젝트 마이그레이션

IT분야에서 데이터나 소프트웨어같은 것을 한 시스템에서 다른 시스템으로 옮기는 것을 마이그레이션(Migration)이라고 부른다.
create React App으로 생성한 자바스크립트 프로젝트가 있다면, 프로젝트를 마이그레이션하는 가장 쉬운 방법은 타입스크립트 템플릿으로 새로운 프로젝트를 생선한 다음, 기존 소스코드 파일을 복사해오는 방법이다.

  1. 새로운 프로젝트를 타입스크립트 템플릿으로 생성하기.
  2. 기존 소스코드 파일 복사하기
    • 새롭게 생성한 프로젝트의 src폴더에서 아래 파일을 제외하고 모두 지운다.
      • src/react-app-env.d.ts (Create React App에서 미리 세팅해놓은 타입을 불러오는 파일)
      • src/reportWebVitals.ts(성능 측정 기능)
      • src/setupTests.ts(테스트 기능)
    • 기존 프로젝트의 소스코드에서 src에 있는 파일들을 모두 복사해 새로운 프로젝트의 src폴더로 붙여넣는다.
    • 새로운 프로젝트의 public 폴더내용도 지우고, 기존 프로젝트의 public폴더의 내용을 복사해온다.
  3. 확장자 바꾸기
    • src폴더에 있는 자바스크립트 파일의 확장자를 타입스크립트로 바꿔준다.
    • 만약 파일에 jsx문법이 있다면 .tsx확장자로 바꾸고, 일반적인 자바스크립트 파일이라면 .ts로 바꾼다.
  4. 코드 수정하기
    • 기존 자바스크립트 파일을 타입스크립트로 바꿔도 아직 자바스크립트 문법으로 작성된 코드여서 타입오류가 발생할 수 있다. 수정이 필요하다.

Vite 프로젝트

  • 2023년 기준, React개발자들도 Vite라는 프로젝트 생성도구를 많이 사용한다.
  • Create React App과 비교했을 때, 좀더 라이트한 기능의 프로젝트를 만들어주고, 빌드속도가 좀 더 빠르다고 알려져있다.
  • 그리고 리액트 프로젝트 말고도 다양한 프로젝트를 생성하는 데 사용할 수 있다는 장점도 있다.

타입스크립트 프로젝트 세팅하기

  • Vite에는 템플릿(생성할 프로젝트의 형태들을 미리 세팅해 놓은 틀)이 있다.
  • 리액트 뿐 아니라, Vue, Svelte 등 여러 프로젝트의 템플릿을 제공한다.
  • 우리는 그 중 타입스크립트를 위한 템플릿을 선택해 프로젝트를 생성해볼 것이다.
  1. 폴더 생성(typescript-react)
  2. npx create-vite-app . --template react-ts
    • "Create Vite App을 사용해 현재 폴더에 프로젝트를 만드는데, 템플릿은 리액트 타입스크립트 템플릿을 사용하겠다." 는 의미이다.
    • 제대로 생성되었다면 tsconfig.json 파일이 함께 생성될 것이다.
    • 위 명령어대신 아래 명령어를 사용해도 된다.
    • npx create-vite-app 원하는_폴더_이름 --template react-ts
  3. npm install
    • Vite는 Create React App과 달리 패키지를 직접 설치해주어야한다. 
    • 제대로 패키지가 설치되었다면 node_modules 폴더가 생긴다.

JavaScript프로젝트 -> TypeScript프로젝트 마이그레이션

  1. 새로운 프로젝트를 타입스크립트 템플릿으로 생성하기.
  2. 기존 소스코드 파일 복사해오기
    • 새로 생성한 프로젝트의 src 폴더에서 src/react-app-env.d.ts 파일을 제외하고 삭제한다.
    • 기존 프로젝트의 소스코드에서 src폴더에 있는 파일들을 복사해온다.
    • 기존 프로젝트의 소스코드에서 index.html, favicon.ico같은 파일들을 가지고 있다면 함께 옮겨준다.
  3. 파일 확장자 바꾸기
    • src폴더 내의 자바스크립트 파일의 확장자를 타입스크립트로 변환한다.(.ts 또는 .tsx)
  4. 코드 수정하기

Next.js 프로젝트

타입스크립트 프로젝트 세팅하기

  1. 폴더 생성
  2. npx create-next-app .
    • 설치중, 타입스크립트를 사용할 것이냐는 질문에 yes 를 선택.
    • "npx create-next-app 원하는_폴더_이름 " 으로 설치해도 된다.

JavaScript프로젝트 -> TypeScript프로젝트 마이그레이션

  1. 폴더에 있는 자바스크립트 파일의 확장자를 타입스크립트로 바꾼다.
    • 파일에 JSX문법이 있다면 .tsx, 없다면 .ts 로 바꾼다.
  2. 개발모드 실행하기
    • 터미널에 "npm run dev"를 입력해 Next.js개발모드를 실행해준다.
    • 한 번 실행하면 타입스크립트 파일을 인식하고 알아서 tsconfig.json같은 필요한 파일들을 생성해준다.
  3. 코드 수정하기

기존 패키지에서 타입을 찾을 수 없을 때

기존 프로젝트에서 리액트 말고도 다른 패키지를 사용했다면 추가로 타입을 설치해줘야할수도 있다.

@types 타입 설치하기

사용하던 패키지중 타입스크립트 파일로 바꾸고나면 import구문에 주의 표시가 생기는 경우가 있다.
주의 내용은 "타입 정의 파일이 없다" 인데, 아래는 react-modal에 대한 예시이다.

 

import Modal from 'react-modal'
// Could not find a declaration file for module 'react-modal' ... 하는 에러 발생

 

npm install --save-dev @types/react-modal

 

모든 패키지가 이렇게 설치해야하는 것은 아니다.
npm 사이트에 들어가보면, Definitely Typed로 타입을 제공해주는 패키지인지 아닌지 표시해주고있다.

 

  • react-modal 은 우측으로 "DT" 가 붙어있으므로 Definitely Typed로 타입을 제공해주고 있다.
    따라서 @types/reat-modal 을 설치해도 된다.

 

  • axios의 경우, 패키지 이름 옆에 "TS" 가 붙어있고, 기본적으로 타입정의를 제공하는 패키지이기때문에 따로 타입정의를 설치해줄 필요가 없다.

React에서 자주 사용하는 타입

React와 TypeScript의 관계

  • tsconfig.json > compileOptions.jsx 옵션의 값은 현재 'preserve'로 되어있다.
  • jsx옵션은 tsx파일 타입스크립트에서 jsx문법으로 작성된 파일들을 변환할 때 어떤 형태로 변환할것인지 정하는 옵션이다.
  • 해당 옵션의 값을 'react' 또는 'react-jsx' 로 설정하면 tsc(타입스크립트 컴파일러)는 tsx파일을 브라우저가 바로 실행할 수 있는 형태의 js코드로 변환해준다.
  • 'preserve' 값으로 설정하면, 별다른 트랜스파일링 없이 jsx문법을 그대로 유지한다.


Create React App, Vite, Create Next App 모두 각각 다른 옵션을 사용하고 있다.
그 이유는 각 프로젝트에서 타입스크립트를 어떤식으로 처리해서 자바스크립트코드로 만드는지 전략이 다르기 때문이다.
'preserve'로 설정해서 jsx파일을 그대로 둔 경우, jsx파일을 js코드로 변환하는 과정은 번들러가 처리할 수도 있다.
이 옵션은 프로젝트를 만들면서 자동 설정되니 자세히 알지 못해도 괜찮다.
우리가 알아야 하는부분은 아래와 같다.

  1. TypeScript에서 리액트를 사용할 때 트랜스파일링 과정이 필요하다.
  2. TypeScript컴파일러가 JSX를 JS로 바꿀지, JSX를 그대로 둘디 tsconfig.json에서 설정할 수 있다.

HTML DOM

HTML DOM의 기본타입에 대해 알아보자.

const usernameInput = document.getElementById('username');
const submitButton = document.getElementById('submit');

usernameInput.focus(); // 1번에러
submitButton.addEventListener('click', handleClick); // 2번 에러

function handleClick(e) { // 3번 에러
    e.preventDefault();
    const message = `${usernameInput.value}님 반갑습니다!`; // 4번 에러
    alert(message);
}

위와같은 코드가 있을 때, 에러가 네 군데에서 발생한다.

  1. usernameInput.focus();
  2. submitButton.addEventListener
  3. function handleClick(e)
  4. `${usernameInput.value}님 반갑습니다!`;

1번 에러 : possibly 'null' 에러로 해당 변수가 null일 수 있다는 것.

2번 에러 : possibly 'null' 에러이다. 두번째 줄을 아래와같이 수정할 수 있다.

const submitButton = document.getElementById('submit') as HTMLButtonElement;

3번 에러 : 파라미터로 받고있는 e 의 타입을 지정해주지 않아서 생기는 에러이다. 아래와같이 수정할 수 있다.

function handleClick(e: Event)
// or
function handleClick(e: UIEvent) 
// or
function handleClick(e: MouseEvent)

 

4번 에러 : 'value' dose not exist on type 'HTMLElement' 에러로 HTMLElement타입에는 value가 없다는 에러이다. valu는 input태그 같은 것에 있는 것이기 때문이다. 맨 윗줄을 아래와같이 수정할 수 있다.

const usernameInput = document.getElementById('username') as HTMLInputElement;

 

 

addEventListener에 inline함수를 정의한 경우에는 아래와같이 작성한다.

submitButton.addEventListener('click', function (e) {
    ...
});

인라인으로 함수를 정의하는 경우, 파라미터의 타입을 추론 가능하기때문에 타입을 명시해주지 않아도 된다.

위 코드에서 e는 MouseEvent로 추론된다.


컴포넌트

컴포넌트의 타입을 정하는 방법

export default function Button({ className = '', id, children, onClick}) {
  const classNames = `${styles.button} ${className}`;

  return (
      <button className={classNames} id={id} onClick={onClick} >
        {children}
      </button>
  );
}

위 코드에 타입을 지정해보자. props는 객체이기때문에 interface로 지정할 것이다.

interface Props {
    className?: string;
    id?: string;
    children?: ReactNode;
    onClick: any;
}

export default function Button({ className = '', id, children, onClick}: Props) {
  const classNames = `${styles.button} ${className}`;

  return (
      <button className={classNames} id={id} onClick={onClick} >
        {children}
      </button>
  );
}

children의 타입은 ReactNode인 것을 알아두자.

 

아래의 코드는 InputHTMLAttributes 를 상속하는 props를 만든 코드이다.
Input태그의 기본속성의 property가 많은 경우, 하나하나 타입을 지정하기 어렵기때문에
이미 만들어져있는 Attributes를 상속하는 타입을 생성할 수 있다.

interface Props extends InputHTMLAttributes<HTMLInputElement>{}

export default function Input({ className = '', ...rest }: Props) {
  const classNames = `${styles.input} ${className}`;
  return <input className={classNames} {...rest} />;
}

Hook

react hook에 타입을 사용하는 방법

1. useState

const [values, setValues] = useState({username: '', password:''})

보통 useState는 타입추론이 잘 되기때문에 타입을 명시하지 않아도 된다.

타입을 명시하고싶다면 아래와같이 쓸 수 있다.

const [values, setValues] = useState<{
  username: string, password: string
}>({
  username: '', password:''
})

 

초기값이 빈배열인경우에는 타입을 명시하지않으면 never타입을 갖게되므로 타입을 명시해줘야한다.

const [names, setNames] = useState<string[]>([]);

 

2. useRef

const formRef = useRef<HTMLFormElement>(null);

useRef에서는, 제네릭으로 대상이되는 node 의 타입을 정해줄 수 있다.

초기값으로 null을 주지않으면 타입에러가 발생한다.(undefined가 올 수 없다는 에러)


EventHandler

function handleChange(e: ChangeEvent<HTMLInputElement>) {
  const { name, value } = e.target;
  const nextValues = {
    ...values,
    [name]: value,
  };
  setValues(nextValues);
}

파라미터 e 의 타입은 ChangeEvent이고, 구체적인 타입을 제네릭으로 작성해주었다.

function handleClick(e: MouseEvent) {
  e.preventDefault();

  const message = `${values.username}님 환영합니다`;
  alert(message);
}

위 코드의 e: MouseEvent 에서 MouseEvent는 react것이 있고, typeScript에서 기본적으로 제공해주는 애도 있는데, react것을 사용해야한다. 임포트문이 잘 들어갔는지확인하자.

import {MouseEvent} from 'react';

 

 

 

이벤트를 구체적으로 명시하지 않아도 될때에는 아래와같이 SyntheticEvent를 사용해도 된다.
SyntheticEvent는  MouseEvent <- UIEvent <- SyntheticEvent 이렇게 상속을 해주고있다.

function handleClick(e: SyntheticEvent) {
  e.preventDefault();

  const message = `${values.username}님 환영합니다`;
  alert(message);
}

 

 

그리고 이벤트핸들러 자체에 타입을 지정할 수 있는 방법은 아래와 같다.

interface Props {
    className?: string;
    id?: string;
    children?: ReactNode;
    onClick: (e: MouseEvent) => void;
}

이 때에도 MouseEvent는 react에서 import한 것이어야한다.
아래와같이 작성할수도있다.
하지만 다른함수들과의 일관성을 위해 위와같이 작성할 것이다.

interface Props {
    className?: string;
    id?: string;
    children?: ReactNode;
    onClick: MouseEventHandler<HTMLButtonElement>
}

 


Context

context에서 type지정하기

type Locale = 'ko' | 'en';

interface LocaleContextValue {
  locale: Locale;
  setLocale: (value: Locale) => void;
}

const LocaleContext = createContext<LocaleContextValue>({
  locale: 'ko',
  setLocale: () => {},
});

 


타입정의(d.ts)파일 이란?

  • 타입스크립트파일은 웹브라우저에서 실행할 때, 타입스크립트 컴파일러를 통해 자바스크립트 파일로 변환된다.
  • 이 때 타입스크립트의 문법은 제거되고 실행가능한 자바스크립트 코드만 남는다.
  • 타입스크립트 코드 중에서는 실행할 수 있는 코드와, 정의를 위한 코드가 있는데 이 중 정의를 위한 코드는 사라지게 된다는 의미이다.
  • 그리고 d.ts파일은 일반적인 타입스크립트 파일과 다르게, 타입의 정의만 들어있는 타입이다.
  • d : Definition

타입정의 파일은 자바스크립트로만 작성된 모듈을 타입스크립트에서 불러오거나, 자바스크립트로만 개발된 패키지를 타입스크립트 프로젝트에서 설치해서 사용할 때 import 로 불러오기만 해서는 모듈 안에 있는 것들의 타입을 알 수 없기때문에 any타입으로 불러오게된다는 문제점을 해결해준다.


예제코드 실행하는데 발생한 오류!

  • npm run start 하니까 발생한 에러
  • error:03000086:digital envelope routines::initialization error
export NODE_OPTIONS=--openssl-legacy-provider

 를 입력해주고 다시 npm run start 하니 됐다.

 

https://velog.io/@bi-sz/Error-VS-Code-Vue.js-%EC%8B%A4%ED%96%89-%EC%98%A4%EB%A5%98-error03000086digital-envelope-routinesinitialization-error

'TypeScript' 카테고리의 다른 글

TypeScript 사용 이유와 동작 원리  (0) 2023.11.26
TypeScript  (0) 2023.11.21
typeScript 프로젝트 생성  (0) 2022.09.15
typeScript 기본 지식  (0) 2022.09.15