npx create-react-app 앱이름 --template typescript
.jsx
대신 JSX 문법을 사용하는 .tsx
를 사용@types
패키지는 바닐라 JS와 TS 프로젝트 사이에서 번역기 역할을 한다.react
나 react-dom
라이브러리를 사용하고, TS 및 개발 툴이 제공하는 기능과 자동 완성 같은 기능을 사용하기 위해서 언어 변환이 필요하다.@types/react
와 @types/react-dom
은 타입 표기 기능을 자바스크립트 기반의 라이브 러리에 추가해 주는 거다.타입스크립트를 사용하여 리액트 코드를 보완할 수 있다.
타입스크립트는 타입 추론을 사용할 때 사용한다. 아직은 사용할게 없음 ..^ㅇ^
타입스크립트의 핵심 기능을 익히기 위해 To Do App을 만들어 보자.
function Todos(props) {
return <ul></ul>;
}
export default Todos;
현재 props
의 tyep을 명시하지 않았기 때문에 any
type이라고 뜬다.
그렇기 때문에 타입스크립트로부터 어떤 지원도 못 받는다.
명시적으로 any
타입을 지정하면 이 경고는 사라진다.
따라서 명시적으로 타입을 설정하자!
📝 타입스크립트가 어디까지 경고 메시지를 보낼 것인지는
tsconfig.json
에서 설정할 수 있다.
function Todos(props: { items: string[] }) {
return <ul>{props.itmes}</ul>;
}
export default Todos;
객체
이다.itmes={문자열배열 데이터}
를 itmes 프롭으로 보낼 거니까, 문자열 배열
을 값으로 가지는 값을 items프롭으로 받는다.props.items
을 매핑하여 배열의 각 요소를 꺼내 쓸 수 있다.props: { items: string[], children: any }
children
과 같은 기본 props를 사용할 수 있도록 하기 위함이다.import React from "react";
const Todos: React.FC<{ itmes: string[] }> = (props) => {
return (
//...
)
};
export default Todos;
React.FC
@types/react
패키지에 정의된 타입으로, Todos 컴포넌트의 함수가 함수형 컴포넌트(FunctionComponent, FC) 타입, 즉 함수형 컴포넌트로 동작한다는 것을 명확히 하는 것이다.<>
를 추가한 후 T 같은 식별자를 넣어 새로운 제네릭 타입을 만들지 않고 내부적으로 사용되는 FC의 제네릭 타입에 구체적인 값을 넣어 사용할 수 있다.그런데 저렇게 해보는 중, 원래는 props에 마우스 커서를 가져다 대면 items 뿐만 아니라 children도 표시가 되어야 하는데 난 items만 뜨고 children은 계속 뜨지 않았다.
왜 그런건지 구글링 해보니 리액트 18 업데이트 후 타입스크립트에서 FC children에 대한 부분이 바꼈다고 한다 ^^!ㅠ 어떻게 이렇게 딱!바뀔 수가...
type Props = {
children: React.ReactNode;
};
type Props = {
children?: React.ReactNode;
};
PropsWithChildren
사용type Props = {
title?: string;
};
const Component: React.FC<React.PropsWithChildren<Props>> = ({children}) => {
return (
<div>{children}</div>
import React from "react";
type Props = {
items: string[];
children?: React.ReactNode; //children 옵셔널하게 넣기..
};
const Todos: React.FC<Props> = (props) => {
return (
<ul>
{props.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
};
export default Todos;
import "./App.css";
import Todos from "./components/Todos";
const todos = ["test 1", "test 2"];
function App() {
return (
<div>
<Todos items={todos} />
</div>
);
}
export default App;
잘되는구만!
프로젝트에서 사용할 다양한 데이터 모델을 따로 models 폴더를 파서 만들어 보자.
컴포넌트 생성하지 않을 거니까 .ts
파일로 생성하자.
오호 기시감이 드는구만.. 이거 nodejs에서 몽구스 스키마 정하는거랑 비슷한거 아니여!!! 🤓
타입을 정의할 때는 type
, interface
로 타입을 생성할 수 있다. 혹은 class
로 객체를 생성해도 된다.
📝 각 생성자에 대한 차이점
📝 Interface와 Class의 차이점
class는 객체 팩토리로 사용되기 때문에, 객체의 모양과 동작에 대한 청사진을 정의하여 다음 클래스 속성을 초기화하고 메서드를 정의한다.
하나 만들어 놓고 찍어 내고 싶을땐 class 를 사용하면 된다고 한다.
이건 나중에 좀 더 공부를 해보자구..
//Todo의 형태 정의하는 파일
//바닐라 자바스크립트에서 class 사용하던거 처럼 하면 된다.
class TodoClass {
//형태 정의시에는 타입스크립트에서 class를 사용할 때는, 클래스에 추가할 프로퍼티가 있는 경우 바닐라에서 하듯이 생성자를 통해 추가할 필요가 없다.
//대신 class에 바로 추가할 수 있다.
id: string;
text: string;
}
export default TodoClass;
id
, text
에 빨간 밑줄이 그이는데 마우스 커서를 가져다 대면, "text 프로퍼티를 초기화하는 부분이 없고, 생성자에서 값이 할당되지 않았습니다."라는 메시지가 뜬다.//Todo의 형태 정의하는 파일
class TodoClass {
//타입 지정 (TS만 있는 부분)
id: string;
text: string;
//생성자 만들어 값 할당 (JS에도 있는 부분)
//인수로 todoText 보내기, id는 생성자 안에서 동적으로 만들기
constructor(todoText: string) {
this.text = todoText;
this.id = new Date().toISOString(); //임의 id 만들기 위해 만들어진 날짜 id로 넣어주자
}
}
export default TodoClass;
이제 Todo 클래스를 사용할 준비를 끝냈기 때문에 App.tsx에서 사용할 수 있다.
new TodoClass()
로 투두를 생성하고 인자(todoText
)를 보낸다.import "./App.css";
import Todos from "./components/Todos";
import TodoClass from "./models/todo";
//이제 todos 배열은 문자열 배열이 아닌, TodoClass 객체 배열이다.
const todos = [
new TodoClass("test1"), //new Todo(todoText)로 투두를 생성하고 인자를 보낸다.
new TodoClass("test2")
];
function App() {
return (
<div>
<Todos items={todos} />
</div>
);
}
export default App;
{}[]
이렇게 수정해도 되겠지만 좀 더 분명한 방법이 있다.정의된 클래스
는 새로운 객체를 생성하는 생성자
역할을 할 뿐만 아니라 타입
역할도 한다! items: TodoClass[];
id
프로퍼티와 text
프로퍼티를 가진다.item.id
, item.text
)를 사용할 수 있게 된다.import React from "react";
import TodoClass from "../models/todo";
type Props = {
items: TodoClass[]; //items은 TodoClass 객체로 채워진 배열
// 배열의 객체는 문자열 타입을 가진 id프로퍼티와 text프로퍼티를 가진다.
children?: React.ReactNode; //옵셔널
};
const Todos: React.FC<Props> = (props) => {
return (
<ul>
{props.items.map((item) => (
<li key={item.id}>{item.text}</li>
//따라서 items의 각요소의 id, text 프로퍼티를 사용할 수 있다.
))}
</ul>
);
};
export default Todos;
Todo: React.FC
<{text: string}>
const Todo: React.FC<{ text: string }> = (props) => {
return <li>{props.text}</li>;
};
export default Todo;
//...
const Todos: React.FC<Props> = (props) => {
return (
<ul>
{props.items.map((item) => (
<Todo key={item.id} text={item.text} />
))}
</ul>
);
};
//...