Todo 객체로 데이터 변경하기

최경락 (K_ROCK_)·2022년 5월 31일
0

리팩토링 개요

  • todo 를 저장할때 문자열 배열로 저장하던 것을 객체 배열로 바꾸어 주었다.
  • 기존에는 두가지의 상태를 사용해서 각각 저장 시켜주었는데, 아무래도 상태를 여러개 쓰는 것 보다 객체의 값을 이용하여 필터해주는게 좋지 않을까 하는 마음에 변경시켜주었다.
    → 추후에 로컬 스토리지를 사용할 때도 더 사용하기 쉬울 것 같기도 하고.
const todoList: string[] = ['todo1', 'todo2', ...]
// 형태의 데이터를 아래와 같이 수정하였다.

const todoList: { id: string; done: boolean; content: string }[] = [
    {
      id : 'uuidv4',
      done: false,
      content: 'todo1',
    },
    {
      id : 'uuidv4',
      done: true,
      content: 'todo2',
    },
  ];

//완료 여부를 done에 지정된 boolean 값으로 판단한다.

타입 지정

  • 기존 데이터의 타입은 string[] 이였다. 말 그대로 문자열로 이루어진 배열이였고, todo와 done 을 나누고 저장하는 데에 두 가지 상태를 이용했다.
  • 내가 필요한 키값을 총 3개로 지정하였다. 각각 id, done, content 로 각각 uuid 와 완료여부, 본문을 저장하는 역할을 하게끔 했다.
  • 사실 객체를 가진 배열을 타입지정을 어떻게 하는지 몰라 많이 해맸는데, 구글링을 통해 해결 할 수 있었다.
  • 내가 사용한 방법은 interface 를 하나 더 작성하여 참조하는 방법이였다.
// App.tsx

interface ITodo {
  id: string;
  done: boolean;
  content: string;
}

const [todoList, setTodoList] = useState<ITodo[]>([]);
// ITodo[] 는 위의 타입을 가진 객체의 배열을 나타낸다.

→ 나는 일단 인터페이스인 ITodo 를 필요한 모든 컴포넌트에 일일히 전부 선언해주었는데, 이런 수동적인 방법보단 한 곳에 타입을 저장하고 꺼내어 쓰는 방법이 무조건… 있을 것 같다.

  • 인터페이스를 사용하지 않는다면 아래와 같이 작성 할 수 있다.
const [todoList, setTodoList] = useState<
    { id: string; done: boolean; content: string }[]
  >([]);

props 가져오기

  • 데이터의 타입이 바뀌었으니, 당연히 props 를 사용하는 컴포넌트 들의 타입 지정도 변경되어야 했다.
  • 위에서 작성한 것과 같이 ITodo 인터페이스를 만들어 사용했다.
interface ITodo {
  id: string;
  done: boolean;
  content: string;
}

interface IProps {
  todoList: ITodo[];
  setTodoList: Dispatch<SetStateAction<ITodo[]>>;
}
  • 인터페이스를 사용하지 않는다면 아래와 같이 작성 할 수 있다.
interface IProps {
  todoList: { id: string; done: boolean; content: string }[];
  setTodoList: Dispatch<
    SetStateAction<{ id: string; done: boolean; content: string }[]>
  >;
}

→ props를 이용하는 다른 컴포넌트 들도 위와 동일하게 처리했다.

렌더링 로직 수정

  • 기존에는 해당하는 상태에 바로 mapfilter 를 이용하여 컴포넌트 들을 렌더링하면 됐지만, 수정 후에는 하나의 상태에 저장되므로, 방식의 변경이 필요했다.
  • 먼저 완료 여부에 따라 둘을 나누어야 했는데, 아래와 같이 filter 를 이용하여 처리했다.
const todos: ITodo[] = todoList.filter((todo) => !todo.done);
const dones: ITodo[] = todoList.filter((todo) => todo.done);

// 변수에도 타입 지정 잊지않기!
// todo.done 의 boolean 값을 이용한다.
  • 위와 같이 하나의 변수에 저장 한 뒤 map 을 이용하여 컴포넌트를 렌더링 해주었다.
{todos.map((todo, idx) => { // or dones.map
  return (
    <Todo // or Done
      key={idx}
      id={todo.id}
      content={todo}
      todoList={todoList}
      setTodoList={setTodoList}
    />
  );
})}
  • 위에서 지정한 변수의 길이를 이용하여 조건부 렌더링으로 안내 메시지를 렌더링하는 것으로 수정했다.
{todos.length === 0 && <div className="empty">비어 있습니다.</div>}
// or dones

삭제 로직 수정

  • 기존에는 해당하는 요소의 인덱스와 저장된 상태의 인덱스를 비교하여 일치하지 않는 요소들로 상태를 갱신하여 삭제를 구현하였다.
  • 하지만 상태가 하나로 관리되기 시작하면서 등록된 객체들의 인덱스 값이 고정되어 기존 함수가 정상적으로 작동하지 않았다.
  • 그래서 해당 객체에 id 프로퍼티를 추가하고, 해당하는 id 를 uuid 라이브러리를 이용하여 지정해주는 방식으로 수정하였고, 코드는 아래와 같이 수정하였다.
const deleteTodo = () => {
    setTodoList(
      todoList.filter((todo) => {
        return todo.id !== uuid;
      })
    );
  };

→ 비교하는 것이 id 일 뿐, 결과적으로 흐름은 동일하다.

Done 비우기 수정

  • 원래는 상태를 빈 상태로 바꿔주면 끝나는 것 이였지만, donetrue 인 항목을 제외하는 방향으로 수정해주어야했다.
const clearDoneList = () => {
    setTodoList(
      todoList.filter((todo) => {
        return todo.done === false;
      })
    );
  };

+

  • 객체를 사용하기 위해 인터페이스를 사용하면서 느낀 것은 인터페이스를 이해하기 위해서는 구조분해 할당을 떠올리면 이해가 쉬울 것 같다는 점.
  • 처음에 객체를 가진 배열의 타입을 어떻게 지정 해야하는가? 에 대한 이해가 잘 되지 않았다.
  • 구글링을 하면서 먼저 객체의 타입을 지정하는 것에 대해 이해하였고, 결론적으로 해당하는 타입을 가진 객체를 가진 배열이라고 생각해보니 이해가 되었다!
    → 결국 string[] 이랑 다를게 없다는 얘기

0개의 댓글