다룰 내용
1. 타입스크립트 프로젝트를 세팅하는 방법 및 Js프로젝트 -> Ts프로젝트 마이그레이션 방법
2. React에서 자주 사용하는 타입
타입스크립트 프로젝트를 세팅하는 방법 및 Js프로젝트 -> Ts프로젝트 마이그레이션 방법
Create React App
타입스크립트 프로젝트 세팅하기
- 폴더 생성(typescript-react)
- npx create-react-app . --template typescript
- "create-react-app 이라는 것을 사용해서 현재폴더에 프로젝트를 만드는데, 타입스크립트 템플릿을 사용하겠다." 는 의미의 명령어이다.
- 제대로 생성이 되었다면, tsconfig.json 파일과 함께 프로젝트가 생성된다.
- . 으로 현재폴더를 만드는 것 대신, 아래와같이 사용할 수도 있다.
- npx create-react-app 원하는_폴더_이름 --template typescript
JavaScript프로젝트 -> TypeScript프로젝트 마이그레이션
IT분야에서 데이터나 소프트웨어같은 것을 한 시스템에서 다른 시스템으로 옮기는 것을 마이그레이션(Migration)이라고 부른다.
create React App으로 생성한 자바스크립트 프로젝트가 있다면, 프로젝트를 마이그레이션하는 가장 쉬운 방법은 타입스크립트 템플릿으로 새로운 프로젝트를 생선한 다음, 기존 소스코드 파일을 복사해오는 방법이다.
- 새로운 프로젝트를 타입스크립트 템플릿으로 생성하기.
- 기존 소스코드 파일 복사하기
- 새롭게 생성한 프로젝트의 src폴더에서 아래 파일을 제외하고 모두 지운다.
- src/react-app-env.d.ts (Create React App에서 미리 세팅해놓은 타입을 불러오는 파일)
- src/reportWebVitals.ts(성능 측정 기능)
- src/setupTests.ts(테스트 기능)
- 기존 프로젝트의 소스코드에서 src에 있는 파일들을 모두 복사해 새로운 프로젝트의 src폴더로 붙여넣는다.
- 새로운 프로젝트의 public 폴더내용도 지우고, 기존 프로젝트의 public폴더의 내용을 복사해온다.
- 새롭게 생성한 프로젝트의 src폴더에서 아래 파일을 제외하고 모두 지운다.
- 확장자 바꾸기
- src폴더에 있는 자바스크립트 파일의 확장자를 타입스크립트로 바꿔준다.
- 만약 파일에 jsx문법이 있다면 .tsx확장자로 바꾸고, 일반적인 자바스크립트 파일이라면 .ts로 바꾼다.
- 코드 수정하기
- 기존 자바스크립트 파일을 타입스크립트로 바꿔도 아직 자바스크립트 문법으로 작성된 코드여서 타입오류가 발생할 수 있다. 수정이 필요하다.
Vite 프로젝트
- 2023년 기준, React개발자들도 Vite라는 프로젝트 생성도구를 많이 사용한다.
- Create React App과 비교했을 때, 좀더 라이트한 기능의 프로젝트를 만들어주고, 빌드속도가 좀 더 빠르다고 알려져있다.
- 그리고 리액트 프로젝트 말고도 다양한 프로젝트를 생성하는 데 사용할 수 있다는 장점도 있다.
타입스크립트 프로젝트 세팅하기
- Vite에는 템플릿(생성할 프로젝트의 형태들을 미리 세팅해 놓은 틀)이 있다.
- 리액트 뿐 아니라, Vue, Svelte 등 여러 프로젝트의 템플릿을 제공한다.
- 우리는 그 중 타입스크립트를 위한 템플릿을 선택해 프로젝트를 생성해볼 것이다.
- 폴더 생성(typescript-react)
- npx create-vite-app . --template react-ts
- "Create Vite App을 사용해 현재 폴더에 프로젝트를 만드는데, 템플릿은 리액트 타입스크립트 템플릿을 사용하겠다." 는 의미이다.
- 제대로 생성되었다면 tsconfig.json 파일이 함께 생성될 것이다.
- 위 명령어대신 아래 명령어를 사용해도 된다.
- npx create-vite-app 원하는_폴더_이름 --template react-ts
- npm install
- Vite는 Create React App과 달리 패키지를 직접 설치해주어야한다.
- 제대로 패키지가 설치되었다면 node_modules 폴더가 생긴다.
JavaScript프로젝트 -> TypeScript프로젝트 마이그레이션
- 새로운 프로젝트를 타입스크립트 템플릿으로 생성하기.
- 기존 소스코드 파일 복사해오기
- 새로 생성한 프로젝트의 src 폴더에서 src/react-app-env.d.ts 파일을 제외하고 삭제한다.
- 기존 프로젝트의 소스코드에서 src폴더에 있는 파일들을 복사해온다.
- 기존 프로젝트의 소스코드에서 index.html, favicon.ico같은 파일들을 가지고 있다면 함께 옮겨준다.
- 파일 확장자 바꾸기
- src폴더 내의 자바스크립트 파일의 확장자를 타입스크립트로 변환한다.(.ts 또는 .tsx)
- 코드 수정하기
Next.js 프로젝트
타입스크립트 프로젝트 세팅하기
- 폴더 생성
- npx create-next-app .
- 설치중, 타입스크립트를 사용할 것이냐는 질문에 yes 를 선택.
- "npx create-next-app 원하는_폴더_이름 " 으로 설치해도 된다.
JavaScript프로젝트 -> TypeScript프로젝트 마이그레이션
- 폴더에 있는 자바스크립트 파일의 확장자를 타입스크립트로 바꾼다.
- 파일에 JSX문법이 있다면 .tsx, 없다면 .ts 로 바꾼다.
- 개발모드 실행하기
- 터미널에 "npm run dev"를 입력해 Next.js개발모드를 실행해준다.
- 한 번 실행하면 타입스크립트 파일을 인식하고 알아서 tsconfig.json같은 필요한 파일들을 생성해준다.
- 코드 수정하기
기존 패키지에서 타입을 찾을 수 없을 때
기존 프로젝트에서 리액트 말고도 다른 패키지를 사용했다면 추가로 타입을 설치해줘야할수도 있다.
@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코드로 변환하는 과정은 번들러가 처리할 수도 있다.
이 옵션은 프로젝트를 만들면서 자동 설정되니 자세히 알지 못해도 괜찮다.
우리가 알아야 하는부분은 아래와 같다.
- TypeScript에서 리액트를 사용할 때 트랜스파일링 과정이 필요하다.
- 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);
}
위와같은 코드가 있을 때, 에러가 네 군데에서 발생한다.
- usernameInput.focus();
- submitButton.addEventListener
- function handleClick(e)
- `${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 하니 됐다.
'TypeScript' 카테고리의 다른 글
TypeScript 사용 이유와 동작 원리 (0) | 2023.11.26 |
---|---|
TypeScript (0) | 2023.11.21 |
typeScript 프로젝트 생성 (0) | 2022.09.15 |
typeScript 기본 지식 (0) | 2022.09.15 |