[타입스크립트] React에서 Typescript 사용하기(2)

📝 Todo-List를 만들며 Typescript에 익숙해지기

이번 포스트에서는 리액트와 타입스크립트를 통해 투두앱을 만들며 배운 점들(TIL)을 정리해보려 한다.

Type-Todo

투두리스트라고 해서 굉장히 빨리, 그리고 쉽게 만들 수 있을 것 같았는데… 막상 타입스크립트를 활용해서 실전에 적용해 보려고 하니 생각보다 시간이 오래 걸렸다.


📁 디렉토리 구조

components 구조


📚 TIL

지극히 개인적으로 내가 투두앱을 만들며 막혔던, 그리고 잘 모르겠었던 부분들만 모아서 정리해보고자 한다.

useState에 type 지정하기

생각보다 그렇게 복잡하고 어렵지는 않다. 만약 default값을 useState에 선언해 줬다면 굳이 type이나 interface를 생성해줄 필요도 없다.

1
2
// 이처럼, 기본 default값을 ''으로 지정해줬기 때문에, typescript는 해당 useState의 타입을 string으로 인식한다.
const [todo, setTodo] = useState('');

object로 이루어진 array의 타입 지정

처음에는 오브젝트가 앞으로 가야하나, 뒤로 가야하나, 아니면 안에 담겨야 하나 참 헷갈렸었다. 하지만 string으로 이루어진 배열의 타입을 정의하는 것과 같은 맥락이라고 생각하니 쉽게 해답을 찾을 수 있었다.

interface를 사용할 경우

1
2
3
4
5
6
7
8
9
// TodoBox.tsx

// 아래와 같이 interface를 정의하고,
interface Props {
todos: { id: number; text: string; isChecked: boolean }[];
}

// 이처럼 useState에 적용시켜주면 된다.
const [todos, setTodos] = useState<Props['todos']>([]);

type을 사용할 경우

1
2
3
4
5
// TodoBox.tsx

type TodosType = { id: number; text: string; isChecked: boolean }[];

const [todos, setTodos] = useState<TodosType>([]);

interface

타입들의 모음집이라고 생각하면 편할 것 같다.
여러 타입들을 모아둔 하나의 “큰 타입”인 것이다.

하지만 내가 간과한 것이 있는데, interface 안에서 정의한 여러 타입 중 몇 개만 선택적으로 골라서 쓸 수 있을 것이라 착각했었다. 내가 특정 변수에 interface를 타입으로 지정해줬다면, 해당 변수는 반드시 해당 interface 안에 포함된 모든 타입들을 포함시켜야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Todos {
id: number;
text: string;
isChecked: boolean;
}

// 이렇게 선택적으로는 절대 사용할 수 없다.
let newTodo: Todos;
newTodo = '책 읽기';

// 아래와 같이 모든 Todos의 타입들을 포함시켜야 한다.
let newTodo: Todos;
newTodo = { id: 0, text: '책 읽기', isChecked: false };

하지만…!! 어떤 interface의 프로퍼티를 선택적으로 사용할 수 있도록 하고싶다면, 아래와 같이 프로퍼티 뒤에 ?를 붙여주면 에러 없이 사용할 수 있다.

1
2
3
4
5
6
7
8
9
interface Todos {
id: number;
text: string;
isChecked?: boolean;
}

// isChecked가 포함되지 않았는데도 에러가 나지 않는다
let newTodo: Todos;
newTodo = { id: 1, text: '운동하기' };

setState props로 넘겨주기

상위 컴포넌트에서 하위 컴포넌트로 setState를 넘겨주는 방법에서 막혀 조금 헤맸다.

하지만 다시 생각해보니 그냥 하위 컴포넌트에서 setState에도 type을 지정해주면 되는 일이었다.

부모 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
// TodoBox.tsx

interface Props {
todos: { id: number; text: string; isChecked: boolean }[];
}
const [todos, setTodos] = useState<Props['todos']>([]);

// ...생략
return (
<TodoBox>
<TodoList todos={todos} setTodos={setTodos} />
</TodoBox>
);

하위 컴포넌트

1
2
3
4
5
6
7
8
9
10
// TodoList.tsx

// 이처럼 하위 컴포넌트에서 Props에 대한 타입들을 지정해주고,
interface Props {
todos: { id: number; text: string; isChecked: boolean }[] | [];
setTodos: (val: any) => void;
}

// 컴포넌트 내에서 해당 props들을 아래와 같이 지정해주면 된다.
export const TodoList = ({ todos, setTodos }: Props) => {//...생략}

styled-components로 props 넘겨주기

전혀 생각지도 못했던 styled-components에서의 props 활용법이다.
styled-components… 아무리 생각해도 참 대단한 것 같다.

1
2
3
4
5
6
7
8
9
10
// 원하는 태그 안에 props를 담아준 뒤,
<Task color={color}></Task>;

// styled-components에서 아래와 같이 해당 props를 선언해주면 된다!
const Task =
styled.div <
{ color: string } >
`
background-color: ${(props) => props.color};
`;

setState를 활용한 토글 기능 (하위 컴포넌트에서 실행)

React에서 여닫기 기능이나, 체크 기능 등을 활용하고자 할 때, toggle 방식의 setState를 자주 사용했었다.

하지만 타입스크립트에서 setState를 하위컴포넌트로 넘겨줬을 경우, setState 안에 prev 인자에도 type을 지정해줘야 해서 조금 헤맸다…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 하위컴포넌트.tsx

import { Dispatch, SetStateAction } from 'react';

interface Props {
setIsOpen: Dispatch<SetStateAction<boolean>>;
isOpen: boolean;
}

// ...생략

const handleOpen = () => {
// 이렇게 하면 에러가 난다...
setIsOpen((prev) => !prev);
// 이렇게 해야 오류가 나지 않는다!
setIsOpen((prev: any) => !prev);
};

🌐 타입스크립트 공부할 때 유용한 링크들

TypeScript 한글 문서

https://github.com/typescript-cheatsheets/react


[타입스크립트] React에서 Typescript 사용하기(2)

https://hoonjoo-park.github.io/typescript/4.reactAndTypescript2/

Author

Hoonjoo

Posted on

2022-02-02

Updated on

2022-02-08

Licensed under

Comments