[새싹 프론트엔드] 221216 TIL-2 - TypeScript(5) To-do 앱 만들기

뒹귤·2022년 12월 19일
0

새싹 프론트엔드

목록 보기
40/41
post-thumbnail

📁 파일구조


📁 App.tsx

  • todo를 추가하고 삭제하는 함수를 만들어 각 컴포넌트에 props로 전달
import React, { useState } from "react";
import "./App.css";
import Todos from "./components/todo/Todos";
import Todo from "./components/todo/todo";
import InsertTodo from "./components/todo/InsertTodo";

function App() {
  // 할 일
  // 타입스크립트에서 빈 배열을 만들면  never타입이 됨
  // never 타입은 무조건 빈 배열, 바뀌지 x
  // 따라서 배열에 타입을 명시해줘야 함
  const [todos, setTodos] = useState<Todo[]>([]);

  const addTodoHandler = (item: string) => {
    const newTodo = new Todo(item);

    setTodos((prevTodo) => {
      return [...prevTodo, newTodo];
    });
  };

  // 삭제 기능
  const removeTodoHandler = (id: string) => {
    setTodos((prevTodos) => {
      return prevTodos.filter((todo) => todo.id !== id);
    });
  };

  return (
    <div>
      <InsertTodo onAddTodo={addTodoHandler} />
      <Todos items={todos} onRemoveTodo={removeTodoHandler} />
    </div>
  );
}

export default App;

📁 todo.ts

  • 클래스 구조를 todo.ts 파일에 따로 빼서 설정해주었다.
class Todo {
  id: string;
  text: string;

  constructor(todoText: string) {
    // 현재 날짜, 시간으로 id 설정
    this.id = new Date().toLocaleString();
    this.text = todoText;
  }
}

export default Todo;

📁 Todos.tsx

  • input으로 입력받은 값을 state 끌어올리기로 app.tsx의 addTodoHandler 함수로 전달
import React, { useState } from "react";
import { setTextRange } from "typescript";

// onAddTodo는 함수타입이라고 명시해야 함
// <{onAddTodo : 매개변수 : 타입 ) => 반환값타입}>
const InsertTodo: React.FC<{ onAddTodo: (item: string) => void }> = (props) => {
  // 타입스크립트에서는 e에 타입 명시해줘야 함
  // 이벤트 객체는 React.FormEvent로 타입 사용하면 됨
  const submitHandler = (e: React.FormEvent) => {
    e.preventDefault();
    if (text.trim() !== "") {
      props.onAddTodo(text);
      setText(" ");
    }
  };

  const [text, setText] = useState("");
  // input에서 일어나는 이벤트틑
  // React.FormEvent<HTMLInputElement> 라고 명시해야함
  const changeHandler = (e: React.FormEvent<HTMLInputElement>) => {
    // target 말고 currentTarget 사용함
    setText(e.currentTarget.value);
  };

  return (
    <div>
      <form onSubmit={submitHandler}>
        <h3>할일 추가</h3>
        <input type="text" onChange={changeHandler} value={text} />
        <button>추가</button>
      </form>
    </div>
  );
};

export default InsertTodo;

📁 Todos.tsx

  • props로 내려받은 todos 배열을 map() 함수를 돌려 TodoItem 컴포넌트에 전달
import React from "react";
import Todo from "./todo";
import TodoItem from "./TodoItem";

// Todos는 컴포넌트 타입 => React.FC (리액트의 함수형 컴포넌트)
// props의 타입 명시 (제네릭 타입 사용) => 객체 <{ items: string[] }>
const Todos: React.FC<{
  items: Todo[];
  onRemoveTodo: (id: string) => void;
}> = (props) => {
  return (
    <div>
      <ul>
        {props.items.map((item) => (
          <TodoItem
            key={item.id}
            text={item.text}
            id={item.id}
            onRemoveTodo={props.onRemoveTodo}
          />
        ))}
      </ul>
    </div>
  );
};

export default Todos;

📁 TodoItem.tsx

  • props로 전달받은 todo item을 리스트로 화면에 뿌려준다.
import React from "react";

const TodoItem: React.FC<{
  text: string;
  id: string;
  onRemoveTodo: (id: string) => void;
}> = (props) => {
  return <li onClick={() => props.onRemoveTodo(props.id)}>{props.text}</li>;
};

export default TodoItem;

느낀점

  • TypeScript에서는 모든 변수에 타입을 명시해줘야 해서 하나하나 다 타입을 작성하는 것이 생각보다 까다로웠다.
  • 특히 props로 받은 객체에 제네릭 타입을 지정해주는 것과, React.FC, React.FormEvent<HTMLInputElement> 로 컴포넌트와 이벤트에도 타입을 명시해야 하는 부분이 새로운 개념이라 적응하기 어려웠다.
  • TypeScript의 오류메세지가 친절해서 그냥 React를 쓸때보다 디버깅이 쉬웠다. 미리 타입을 지정해버리니까 오류가 나는 것을 미연에 방지하는 느낌이라 복잡한 프로젝트에서 유용하게 사용할 수 있을 것 같다.

새싹DT 기업연계형 프론트엔드 실무 프로젝트 과정 9주차 블로그 포스팅

profile
🌱 FE 

0개의 댓글