들어가기

지난 번에 공부했던 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

https://www.youtube.com/watch?v=bjnW2NLAofI

Last modified: 2022년 02월 01일

Comments

Write a Reply or Comment

Your email address will not be published.