memostack
article thumbnail
블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.tistory.com/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
반응형

완료 목록 조회 기능 개발

이전 글에서는 '할 일' 목록 조회 기능을 개발했지만, 이번엔 완료 목록 조회 기능을 개발한다.

 

할 일과 완료한 일을 구분하기 위해 checked 라는 플래그(Flag) 값이 필요하다.

  • true: 완료한 일
  • false: 아직 완료하지 않은 일

 

체크박스 체크 또는 체크 해제 기능

우선, 체크 박스를 체크 했을때, checked의 값이 바뀌도록 해보자

 

InputBox.jsx

import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';

const InputBox = ({ todoList, setTodoList }) => {
  const [text, setText] = useState('');
  const inputRef = useRef(null);

  // input 값 가져오기
  const onChangeInput = (e) => {
    setText(e.target.value);
  };

  const onClickAddButton = () => {
    // todoItemList에 값 추가
    const nextTodoList = todoList.concat({
      id: todoList.length,
      text,
      checked: false, // (1)
    });
    setTodoList(nextTodoList);

    // input 값 초기화 및 포커싱
    setText('');
    inputRef.current.focus();
  };

  return (
    <div className="todoapp__inputbox">
      {/* 아이템 내용 입력 input */}
      <input
        type="text"
        name="todoItem"
        value={text}
        ref={inputRef}
        placeholder="할 일을 입력해주세요"
        className="todoapp__inputbox-inp"
        onChange={onChangeInput}
      />
      {/* 입력 후 아이템 추가 버튼 */}
      <button
        type="submit"
        className="todoapp__inputbox-add-btn"
        onClick={onClickAddButton}
      >
        추가
      </button>
    </div>
  );
};

// props 값 검증
InputBox.propTypes = {
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired,
    }).isRequired
  ),
  setTodoList: PropTypes.func.isRequired,
};

export default InputBox;
  • (1): 처음 아이템을 등록할때는 checked의 값을 false로 하여, 완료하지 않은 항목으로 설정한다.

 

ToDoItem.jsx

checkbox를 클릭하면, todoItem의 checked 값이 toggle 되도록한다.

import React from 'react';
import PropTypes from 'prop-types';

const ToDoItem = ({ todoItem, todoList, setTodoList }) => {
  const onChangeCheckbox = () => {
    const nextTodoList = todoList.map((item) => ({
      ...item,
      // id 값이 같은 항목의 checked 값을 Toggle 함
      checked: item.id === todoItem.id ? !item.checked : item.checked,
    }));

    setTodoList(nextTodoList);
  };

  return (
    <li className="todoapp__item">
      {/* 아이템 완료 체크 / 체크 해제를 위한 체크박스 */}
      <input
        type="checkbox"
        className="todoapp__item-checkbox"
        checked={todoItem.checked} // (1)
        onChange={onChangeCheckbox} // (2)
      />
      {/* 아이템 내용 */}
      <span className="todoapp__item-ctx">{todoItem.text}</span>
      {/* 수정 버튼 */}
      <button type="button" className="todoapp__item-edit-btn">
        ✏
      </button>
      {/* 삭제 버튼 */}
      <button type="button" className="todoapp__item-delete-btn">
        🗑
      </button>
    </li>
  );
};

ToDoItem.propTypes = {
  todoItem: PropTypes.shape({
    id: PropTypes.number,
    text: PropTypes.string.isRequired,
  }),
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired,
    })
  ),
  setTodoList: PropTypes.func.isRequired,
};

export default ToDoItem;
  • (1): 체크 또는 체크해제를 구분할 수 있도록, input의 checked에 todoItem의 checked를 넣는다.
  • (2): 변화가 생기면 todoItem의 checked 값을 Toggle 시킨다.

 

할 일 목록과 완료한 목록 나누기

Home.jsx

checkedList로 ToDoItemList 컴포넌트에서 '할 일 목록'을 보여줄건지 '완료한 목록'을 보여줄건지 설정한다.

  • false: 체크가 안됨 -> 해야할 할 일
  • true: 체크 됨 -> 완료한 일
import React, { useState } from 'react';
import InputBox from '../components/InputBox';
import ToDoItemList from '../components/ToDoItemList';

const Home = () => {
  const [todoList, setTodoList] = useState([]);

  return (
    <div className="homepage__container">
      {/* ToDo Item을 추가할 수 있는 input 박스 */}
      <InputBox todoList={todoList} setTodoList={setTodoList} />

      {/* 할 일 Item 리스트 */}
      <ToDoItemList
        title={'할 일'}
        todoList={todoList}
        setTodoList={setTodoList}
        checkedList={false} // (체크되지 않은) 할 일 목록
      />

      {/* 완료한 Item 리스트 */}
      <ToDoItemList
        title={'완료한 항목'}
        todoList={todoList}
        setTodoList={setTodoList}
        checkedList={true} // (체크되어 있는)완료한 목록
      />
    </div>
  );
};

export default Home;

 

ToDoItemList.jsx

props로 checkedList를 받아와서 todoItem.checked와 동일한 아이템만 출력한다.

import React from 'react';
import PropTypes from 'prop-types';
import ToDoItem from './ToDoItem';

const ToDoItemList = ({ title, todoList, setTodoList, checkedList }) => (
  <div className="todoapp__list">
    {/* props로 부터 title 값을 전달 받음 */}
    <p className="todoapp__list-tit">{title}</p>

    <ul className="todoapp__list-ul">
      {todoList && // todoList가 있을때만 출력
        todoList.map((todoItem) => {
          // checkedList 값에 따라 '할 일 목록' 또는 '완료한 목록'을 출력
          if (checkedList !== todoItem.checked) return null;

          return (
            // map을 이용하여 ToDoItem을 출력
            <ToDoItem
              key={todoItem.id}
              todoItem={todoItem}
              todoList={todoList}
              setTodoList={setTodoList}
            />
          );
        })}
    </ul>
  </div>
);

ToDoItemList.propTypes = {
  title: PropTypes.string.isRequired,
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired,
    })
  ),
  setTodoList: PropTypes.func.isRequired,
  checkedList: PropTypes.bool.isRequired,
};

export default ToDoItemList;
화면에 아무것도 출력하고 싶지 않을때는 null을 return 하면 된다.

 

완료한 아이템 스타일 입히기

완료한 일은 색상과 폰트 스타일, 취소선을 넣어서 완료한 느낌(?)이 풍기도록 스타일을 입힌다.

완료한 일은 수정할 수 없도록 기획했으므로, 완료한 일의 경우엔 수정 버튼이 보이지 않도록 한다.

 

ToDoItem.jsx

import React from 'react';
import PropTypes from 'prop-types';

const ToDoItem = ({ todoItem, todoList, setTodoList }) => {
  const onChangeCheckbox = () => {
    const nextTodoList = todoList.map((item) => ({
      ...item,
      // id 값이 같은 항목의 checked 값을 Toggle 함
      checked: item.id === todoItem.id ? !item.checked : item.checked,
    }));

    setTodoList(nextTodoList);
  };

  return (
    <li className="todoapp__item">
      {/* 아이템 완료 체크 / 체크 해제를 위한 체크박스 */}
      <input
        type="checkbox"
        className="todoapp__item-checkbox"
        checked={todoItem.checked}
        onChange={onChangeCheckbox}
      />
      {/* 아이템 내용 */}
      <span
        className={`todoapp__item-ctx ${
          todoItem.checked ? 'todoapp__item-ctx-checked' : ''
        }`}
      >
        {todoItem.text}
      </span>
      {
        // 수정 버튼
        // 완료한 일인 경우에는 null을 반환하여 보이지 않도록 함
        !todoItem.checked ? (
          <button type="button" className="todoapp__item-edit-btn">
            ✏
          </button>
        ) : null
      }

      {/* 삭제 버튼 */}
      <button type="button" className="todoapp__item-delete-btn">
        🗑
      </button>
    </li>
  );
};

ToDoItem.propTypes = {
  todoItem: PropTypes.shape({
    id: PropTypes.number,
    text: PropTypes.string.isRequired,
  }),
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired,
    })
  ),
  setTodoList: PropTypes.func.isRequired,
};

export default ToDoItem;

 

App.css

이탤릭체로 기울이고, 취소선을 넣는다. 색상은 회색으로 설정한다.

.todoapp__item-ctx-checked {
  font-style: italic;
  text-decoration: line-through;
  color: #868e96;
}

 

결과 화면 확인

결과 화면

 

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

https://github.com/bluemiv/react_todo_app

 

GitHub - bluemiv/react_todo_app: React로 만든 ToDo Web Application

React로 만든 ToDo Web Application. Contribute to bluemiv/react_todo_app development by creating an account on GitHub.

github.com

 
반응형
블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.tistory.com/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
profile

memostack

@bluemiv_mm

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!