
들어가기
지난 번에 공부했던 React에서 활용하는 방법을 공부하기 위해 TypeScript 할 일 목록 만들기 앱 강좌를 보며 정리했습니다.
기본 세팅
import React, {FC} from 'react';
import './App.css';
const App: FC = () => {
return (
<></>
);
}
export default App;
- App의 유형이 Functional Component라는 의미로 FC로 넣어줍니다.
useState 사용하기
import React, { FC, ChangeEvent, useState } from 'react';
import './App.css';
const App: FC = () => {
const [task, setTask] = useState<string>("");
const [deadline, setDeadline] = useState<number>(0);
const [todo, setTodo] = useState([]);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
console.log(event.target.value);
};
return (
<div className='App'>
<div className="header">
<div className="inputContainer">
<input type="text" placeholder="Task..." onChange={handleChange}/>
<input type="number" placeholder="Deadline (in Days)..." onChange={handleChange} />
</div>
<button>Add Task</button>
</div>
<div className="todoList"></div>
</div>
);
}
export default App;
- useState에 꺽쇠를 붙여서 타입을 정의할 수 있습니다.
- 예: <타입>
input 핸들러의 타입 정의
import React, { FC, ChangeEvent, useState } from 'react';
import './App.css';
const App: FC = () => {
const [task, setTask] = useState<string>("");
const [deadline, setDeadline] = useState<number>(0);
const [todo, setTodo] = useState([]);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
console.log(event.target.value);
};
return (
<div className='App'>
<div className="header">
<div className="inputContainer">
<input type="text" placeholder="Task..." onChange={handleChange}/>
<input type="number" placeholder="Deadline (in Days)..." onChange={handleChange} />
</div>
<button>Add Task</button>
</div>
<div className="todoList"></div>
</div>
);
}
export default App;
- ChangeEvent를 react에서 불러와서 ‘HTMLInputElement’를 타입으로 줄 수 있습니다.
- 예 <HTMLInputElement>
이벤트 값을 Number로 처리하는 방법
import React, { FC, ChangeEvent, useState } from 'react';
import './App.css';
const App: FC = () => {
const [task, setTask] = useState<string>("");
const [deadline, setDeadline] = useState<number>(0);
const [todo, setTodo] = useState([]);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.name === "task") {
setTask(event.target.value);
} else {
setDeadline(Number(event.target.value));
}
};
return (
<div className='App'>
<div className="header">
<div className="inputContainer">
<input type="text" placeholder="Task..." name="task" onChange={handleChange}/>
<input
type="number"
placeholder="Deadline (in Days)..."
onChange={handleChange}
name="deadline"
/>
</div>
<button>Add Task</button>
</div>
<div className="todoList"></div>
</div>
);
}
export default App;
- useState에서 deadline을 number 타입으로 정의했는데, input의 value를 deadline에 넣으려고 하면 String타입이라고 에러가 납니다.
- input태그에는 type을 number로 주었기 때문에 숫자 밖에 올 수 없습니다.
- 그러니까 간단하게 자바스크립트의 기본 캐스팅 방법인 ‘number(value)’를 사용해서 타입을 맞춰 줍시다.
객체의 타입 관리
import React, { FC, ChangeEvent, useState } from 'react';
import './App.css';
import {ITask} from './Interfaces'
const App: FC = () => {
const [task, setTask] = useState<string>("");
const [deadline, setDeadline] = useState<number>(0);
const [todoList, setTodoList] = useState<ITask[]>([]);
const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
if (event.target.name === "task") {
setTask(event.target.value);
} else {
setDeadline(Number(event.target.value));
}
};
const addTask = (): void => {
const newTask = {taskName: task, deadline: deadline} ;
setTodoList([...todoList, newTask]);
console.log(todoList);
};
return (
<div className='App'>
<div className="header">
<div className="inputContainer">
<input type="text" placeholder="Task..." name="task" onChange={handleChange}/>
<input
type="number"
placeholder="Deadline (in Days)..."
onChange={handleChange}
name="deadline"
/>
</div>
<button onClick={addTask}>Add Task</button>
</div>
<div className="todoList"></div>
</div>
);
}
export default App;
- todoList의 state에 값을 객체로 넣으려고 합니다.
- 하지만 [...todoList, task]를 넣으면 객체가 정의되어 있지 않기 때문에 오류가 납니다.
- 이것을 해결하기 위해 TypeScript의 Interface를 사용합니다.
//Interface.tsx
export interface ITask {
taskName: string;
deadline: number;
}
- Interface를 활용하여 객체 내 항목들의 타입을 지정해 줍니다.
- 그리고 Interface를 불러와서 todoList의 타입으로 넣어 줍니다.
- 버튼을 눌렀을 때 작동하는 addTask 함수에서는 todoList를 인터페이스와 맞추기 위해서
- 새로운 변수 newTask를 생성해 줍니다.
- taskName은 task로 deadline은 deadline으로 입력된 값들의 타입을 정해준 다음 객체에 넣어줍니다.
- 그 변수를 다시 setTodoList에 넣어주면 타입에러가 없이 코드를 완성할 수 있습니다.
Props의 타입
import React from 'react';
import { ITask } from '../Interfaces';
interface Props {
task?: ITask;
}
const TodoTask = ({ task }: Props) => {
return (
<div>
{task?.taskName} {task?.deadline}
</div>
);
};
export default TodoTask;
- Prop을 넘겨 받을 때의 타입을 정의하기 위해서는 interface를 활용하면 됩니다.
- Props라는 인터페이스를 만들고 거기에 prop의 타입을 정의합니다.(이전에 선언했던 인터페이스를 다시 정의합니다.)
- prop에 ?를 추가하는 것으로 undefined의 경우에도 type에러가 나지 않도록 할 수 있습니다.
함수의 타입
import React, { FC, ChangeEvent, useState } from 'react';
import './App.css';
import TodoTask from './Components/TodoTask';
import {ITask} from './Interfaces';
const App: FC = () => {
const [task, setTask] = useState<string>("");
const [deadline, setDeadline] = useState<number>(0);
const [todoList, setTodoList] = useState<ITask[]>([]);
const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
if (event.target.name === "task") {
setTask(event.target.value);
} else {
setDeadline(Number(event.target.value));
}
};
const addTask = (): void => {
const newTask = {taskName: task, deadline: deadline} ;
setTodoList([...todoList, newTask]);
setTask("");
setDeadline(0);
};
const completeTask = (taskNameToDelete: string): void => {
setTodoList(todoList.filter((task) => {
return task.taskName != taskNameToDelete
}));
}
return (
<div className='App'>
<div className="header">
<div className="inputContainer">
<input type="text"
placeholder="Task..."
name="task"
onChange={handleChange}
value={task}
/>
<input
type="number"
placeholder="Deadline (in Days)..."
onChange={handleChange}
name="deadline"
value={deadline}
/>
</div>
<button onClick={addTask}>Add Task</button>
</div>
<div className="todoList">
{todoList.map((task: ITask, key: number) => {
return <TodoTask key={key} task={task} completeTask={completeTask} />
})}
</div>
</div>
);
}
export default App;
- 삭제 버튼을 만들기 위해 completeTask라는 함수를 만들었습니다.
- 이 함수는 할 일 목록에서 이름(taskName)을 인자로 받아서 todoList내에 동일한 이름이 있으면 삭제해주는 함수입니다.
- 함수를 Prop으로 넘겨줄 때 Type은 다시 interface를 활용합니다.
import React from 'react';
import { StringLiteralType } from 'typescript';
import { ITask } from '../Interfaces';
interface Props {
task?: ITask;
completeTask(taskNameToDelete?: String): void;
}
const TodoTask = ({ task, completeTask }: Props) => {
return (
<div className='task'>
<div className="content">
<span>{task?.taskName}</span>
<span>{task?.deadline}</span>
</div>
<button
onClick={() => {
completeTask(task?.taskName)
}}
>
X
</button>
</div>
);
};
export default TodoTask;
- 함수 내에서 return하는 값이 없으면 type을 ‘void’로 써 줍니다.
- 그리고 인자의 타입은 ‘문자’였기 때문에 ‘String’으로 썼습니다.
마치며
TypeScript로 To Do List 앱 만들기 강의를 보면서 React에서 TypeScript를 사용하는 기초적인 방법을 익혔습니다. TypeScript를 배우면서 react에서는 useState나 Component자체에 타입을 어떻게 적용시키면 좋을 지 궁금했었는데, 그 방법들을 시원하게 알 수 있었던 기회가 된 것 같습니다. 다음에 TypeScript를 활용해서 프로그래밍을 할 때 이번에 학습한 내용이 큰 도움이 될 것 같습니다.
참조
PedroTech : Todo List in ReactJS using TypeScript Tutorial
Comments