그래도 해야지. 어떡해

[React] Todo List 만들기 (#2 필터링 개선하기) 본문

개발일지/React

[React] Todo List 만들기 (#2 필터링 개선하기)

정원의쓸모 2023. 6. 17. 21:58

[React] Todo List 만들기 (#1 개선사항 정리)

문제점

‘active’ 필터링 후, 체크된 리스트가 다시 체크해제되는 오류 수정

Component 패널을 열어 확인해 보니, 리스트 첫 줄(좌측 사진)을 보면 check가 풀려있다.
그런데(우측 사진) class는 -completed가 들어갔고 checked: true로 되어있었다.


#1. 기존 코드

1.1 Header.jsx

<div>
     <button onClick={() => handleFilter('all')}>All</button>
     <button onClick={() => handleFilter('active')}>Active</button>
     <button onClick={() => handleFilter('completed')}>Completed</button>
</div>

1.2 TodoList.jsx

const [filter, setFilter] = useState('all');

// 1. 체크
const toggleCheck = (idx) => {
   setTodos(todos.map(item =>
        item.idx === idx ? { ...item, checked: !item.checked } : item
        // 1.2 아이템 check를 반대로 변경
   ))
};

// 2. 필터링
const filteredTodos = todos.filter(item => {
   if (filter === 'completed') {
       return item.checked;
   } else if (filter === 'active') {
       return !item.checked;
   } else {
       return true;
   }
});

return (
	...다른 코드
	<Header setFilter={setFilter}/>
	...다른 코드
)
  • 필터링과 체크박스의 코드 중 일부를 가져왔는데 감히 궁예를 해보자면… 아마 item.checked를 반대로 변경하는 부분에서 꼬여 오류가 발생하지 않았나싶다.
  • 좀 더 안전한 방법으로 필터링을 줘야겠다는 생각하면서 수정작업을 시작했다.

(추가) #2.

Components 패널을 다시 확인해 보니 'li'가 아닌 아이콘이 컴포넌트로 나눠져 있는 걸 보고 구조적으로 문제가 있다고 생각했다. 'li'컴포넌트로 나누고 필터링 값을 받아오는 형식으로 수정해야겠다.

 


🔧 목표

  • List 컴포넌트 분리하기
  • 배열을 이용한 필터링
  • 고유한 Key 값 가져오기

1. 카테고리를 배열로 만들기

1.1 초기값을 배열의 첫번째(all)로 지정한다.

const category = ['all', 'active', 'completed'];
const [filter, setFilter] = useState(category[0]); // all

1.2  // 2. 필터링 대신에 기존 todo와 해당하는 filter만 받아오도록 작성한다.

const getFilteredItems = (todos, filter) => {
    if (filter === 'all') { return todos; }
    return todos.filter(todo => todo.status === filter)
  };
const filteredTodos = getFilteredItems(todos, filter);

2. 고유한 키(key) 부여하기

2-1. 고유한 키는 컴포넌트 리스트에서 각 항목을 구분하는 데 사용된다. uuid는 고유한 식별자를 생성하기 위한 표준 방법 중 하나로 ‘uuid’를 설치하기로 했다.

 

  1. yarn add react-uuid로 모듈을 설치한다.
  2. id 값을 할당해줘야 하는 파일 안에 import를 해 함수를 실행시킨다.
import { v4 as uuidv4 } from 'uuid';

const handleSubmit = (e) => {
    ...다른 코드

    const addTodos = todos.concat({
      idx: todos.length,
      id: uuidv4(),
      text,
      check: false,
    })

    ...다른 코드
  };

3. 컴포넌트 분리하기

3-1. Todo.jsx를 새로 만들어서 분리해줬다.

import React from 'react';
import { BsX } from "react-icons/bs";

export default function Todo({todo, onToggle, onDelete}) {
  const { id, text, status } = todo;

  const handleChange = (e) => {
    const status = e.target.checked ? 'completed' : 'active';
    onToggle({ ...todo, status });
  }

  const handleDelete = () => onDelete(todo);
  return (
    <li>
      <input
        type="checkbox"
        id={id}
        checked={status === 'completed'}
        onChange={handleChange}
      />
      <label
        htmlFor={id}
        className={ `todo${status === 'completed' ? '-completed' : ''}`}
      >
        {text}
      </label>
      <button onClick={handleDelete}><BsX /></button>
    </li>
  );
}

4. 문제가 생겼다!

컴포넌트는 분리해줬는데 checked 상태에 상관없이 모든 statuscompleted다…

 

이제 active가 안 먹힘!!!!ㅠㅠ!!! 개발자 도구 열어…

 

5. 성공!

체크된 completed가 체크해제 없이 정상적으로 작동하는 것을 확인할 수 있다.

 

 


#. 🤔 기타

- (예상) Key가 필수적으로 있어야 하는가?

key는 완성 후 나중에 추가해도 되는 옵션이라고 생각했는데, key가 없을 때는 하나만 클릭해도 전체적으로 적용되다가 각각의 ‘li'마다 고유한 key를 넣어주니 하나씩 적용되어 필터링이 정상적으로 작동하는 것을 확인했다.

그런데 권장사항이라고 들었지, 필수적인 요구사항은 아니라고 들었는데… 이 부분은 필요에 따라 나중에 수정할 수도 있을 것 같다.

- 계획은 언제나 예상대로 진행되지 않는다.

물론, 컴포넌트를 나누지 않고 해결할 수도 있겠지만, 실제로 컴포넌트가 제대로 분리되지 않는 상태에서 상태관리가 어려워 나도 헷갈렸기 때문이다. 어차피 나중에 분리하려고 했으니까…

그리고 여러 번의 시도와 수정을 거치면서 코드를 이해하는 데에 집중하다 보니 기록을 중간중간 남기지 못했다는 점이 아쉽다.

사실, 개발자도구를 열기 전까지는 컴포넌트를 나누지 않아도 될 줄 알았다. 모든 개선 사항을 처리한 후, 마지막에 컴포넌트를 나누면 포스팅하기에도 편할 것 같았다. 주제가 명확하게 나뉘니까😂


🛫 마치며

포스팅하기까지 여러 번 엎었다. 다른 코드들을 따라하면서 필터링이 정상적으로 작동하는 것을 확인했지만, 납득이 안 가서… 내가 진정으로 이해하지 못했기 때문이다.

스스로 해결했다는 생각이 전혀 들지 않았고, 이 코드를 사용하지 않아도 작동할 수 있을까?라는 의문이 들었다. 따라서 A 방법으로 문제를 해결하면 다른 방법에선 의문이 들었던 부분은 제외하거나 가장 먼저 다루어보면서 이번 개선방향에 필요한 코드인지를 고민하는 시간이 되었다.

 

전체 코드는 아래 링크에서 확인할 수 있습니다.

https://github.com/CircleYoo/Todo-List

 

GitHub - CircleYoo/Todo-List

Contribute to CircleYoo/Todo-List development by creating an account on GitHub.

github.com

 

Comments