[React Project] ToDo List 제작

SeokHun·2021년 1월 16일
0

Projects

목록 보기
2/3
post-thumbnail

기능 소개

  • Create
    • 사용자는 자신이 할 일을 Input 창에 입력할 수 있다
    • ADD 버튼을 누르면 입력한 일이 아래에 저장된다
  • Main
    • 자신이 기록한 할 일들이 아래에 리스트로 나타내어 진다
    • 할 일들은 자신이 했는지 안 했는지 체크할 수 있다
    • 자신에게 얼마만큼의 할 일이 남았는지 Display 한다
  • Update
    • 글자를 클릭하면 할 일을 수정할 수 있다 (엔터로 확인)
  • Delete
    • x를 클릭하면 할 일을 삭제할 수 있다

코드

GitHub : pyo-sh


1. Main Feature

1-1. 구조

React 사용 목적에 맞게 Component를 나누어서 작성

  • APP + Header : 프로그램이 무엇을 나타내는 지
    • 일들의 list를 가진 state 보유
    • TODOLIST 타이틀
    • 오늘의 날짜
    • 남은 일의 갯수
  • ToDoInput : 사용자가 일에 대해 입력 및 추가
    • Input : 사용자에게 입력받기
    • Button : 사용자에게 추가기능 제공
  • ToDoList : 할 일들을 Unordered List로 나열
  • ToDoItem : List 중 하나의 일
    • 할 일의 기능, 수정, 삭제 등을 제공

1-2. 할 일 추가하기

1) 입력 받기

HTML input 태그의 onChange 이벤트를 이용해 값을 받는다

Component의 State 값으로 String 변수를 이용

const [inputTodoValue, setInputTodoValue] = useState("");

onChange 이벤트가 발생할 때 마다 setState를 하여 값을 저장하고 있는다
State 값은 input의 값이 된다

const onChangeInputList = (event) => {
  const { value } = event.target;
  setInputTodoValue(value);
};
// ...
return // ...
  <input
    className="ToDo-Add-Input"
    onChange={onChangeInputList}
    value={inputTodoValue}
    />
// ...

2) 추가 하기

입력을 했다면 할 일을 추가할 수 있어야 한다
HTML Button 태그의 onClick 이벤트가 발생하면 일을 추가한다

Component의 State 값으로 id를 가지고 있어 일을 추가할 때 마다 고유한 id를 부여한다

const [id, setId] = useState(0);

onClick 이벤트 발생 시 입력한 값(inputTodoValue)가 있을 때 할 일을 추가(상위 Component의 setState) 한다

list에 현재 id 값을 부여하고 id + 1을 setState 하여 각 할 일이 고유한 id를 가지고 있게 한다

const onClickAddList = () => {
    if (inputTodoValue) {
      setList((prev) => [
        ...prev,
        {
          id: id,
          content: inputTodoValue,
          isComplete: false,
          isUpdating: false
        }
      ]);
      setId((prev) => prev + 1);
      setInputTodoValue("");
    }
  };
return // ...
	<button
		className="ToDo-Add-Button"
		onClick={onClickAddList}>
          ADD
	</button>
// ...

1-3. 저장한 일들 나열하기

배열로 저장하고 있는 객체들을 map() 함수를 이용해 HTML li 태그로 출력한다

하위 컴포넌트에서 출력을 담당하도록 하게하고 나열만 하여 기능을 명확히 했다

// ...
function ToDoList({ list, setList }) {
  // ...
  return (<ul className="ToDo-List">
      {list.map((value) => (
        <ToDoItem
          key={value.id}
          id={value.id}
          content={value.content}
          isComplete={value.isComplete}
          isUpdating={value.isUpdating}
          pressEnterKey={pressEnterKey}
          />
      ))}
    </ul>);
}

1-4. 클릭 기능

각각의 li 태그가 같은 onClick 이벤트를 실행할 예정이므로 이벤트 버블링을 활용한다
→ HTML ul 태그에 onClick 이벤트 하나를 두고 Event Target의 id를 통해 기능을 수행하도록 한다

id를 ToDo-[기능]-[번호] 로 부여하여 어떤 기능을 수행해야 하는지 알아내고 어느 타겟에 적용해야 하는지 알아낸다

const onClickList = (event) => {
  if (event.target.id && event.target.id.split('-').length > 1) {
	const buttonFunction = event.target.id.split('-')[1]
	// ... 기능 수행
}

1) 체크

list 객체의 속성 중 isComplete가 true 면 완료, false면 할 일이 남은 것이다
default 값은 false로 두었다

isComplete의 값에 따라 Button의 Display를 달리하였다

return // ...
  <button 
    className={isComplete ? "ToDo-Item-Done" : "ToDo-Item-Left"}
    id={"ToDo-Complete-" + String(id)}
    >
  	{isComplete ? "✓" : "-"}
  </button>
// ...

클릭 된 Button은 해당 객체의 isComplete 값만 바꾸게 된다

if(buttonFunction === "Complete"){
  setList(
    list.map((value) => {
      if (event.target.id === "ToDo-Complete-" + String(value.id)) {
        return { ...value, isComplete: !value.isComplete };
      }
      return value;
    })
  );
}

2) 삭제

삭제 Button을 누르면 List 배열 중에서 해당 객체만 삭제하면 된다
많은 방법이 있지만 filter() 함수를 사용했다

if(buttonFunction === "Delete"){
  setList(
    list.filter((value) => event.target.id !== "ToDo-Delete-" + String(value.id))
  );
}

3) 수정

객체는 isUpdating 이라는 속성을 가지고 있어 수정하고 있는지에 대한 값을 저장하고 있다
default 값은 false이고 클릭하면 isUpdating의 값을 true로 바꿀 뿐이다

if(buttonFunction === "Update"){
  setList(
    list.map((value) => {
      if (event.target.id === "ToDo-Update-" + String(value.id)) {
        return { ...value, isUpdating: !value.isUpdating };
      }
      return value;
    })
  );
}

isUpdating의 값에 따라 li 태그의 출력이 달라진다

return // ...
  {isUpdating
   ? <input
       className="ToDo-Item-Update"
       id={"ToDo-Updating-" + String(id)}
       onKeyPress={pressEnterKey}
       defaultValue={content}
       />
   : <p
       className="ToDo-Item-Content"
       id={"ToDo-Update-" + String(id)}
       >
     {content}
   </p>
  }
// ...

수정이 끝났다면 사용자는 Enter를 눌러 저장할 수 있다
onKeyPress 이벤트에서 13번(Enter)가 발생하면 Input 값을 저장하고 isUpdating을 false로 만든다

const pressEnterKey = (event) => {
  if(event.charCode === 13){
    setList(
      list.map((value) => {
        if (event.target.id === "ToDo-Updating-" + String(value.id)) {
          return {
            ...value,
            content: event.target.value,
            isUpdating: false
          };
        }
        return value;
      })
    );
  }
}

2. Sub Features

2-1. 남은 할 일의 갯수 출력하기

list의 배열 안 객체 중에 isComplete가 false인 객체가 몇 개 인지만 확인하면 된다

const renderComplete = () => {
  return list.reduce((accumulator, current) => {
    if (current.isComplete === false)
      return accumulator + 1;
    return accumulator;
  }, 0);
}

이 함수에서 반환된 값을 출력하기만 하면 된다

return // ...
	<div className="List-Count">할 일 {renderComplete()}</div>
// ...
  );

2-2. 오늘 날짜 출력하기

Date 객체를 이용해서 오늘의 날짜를 출력하도록 했다.

요일은 미리 배열을 선언하고 index에 따라 출력 값을 정하도록 하였다.

const renderDate = () => {
  if(nowDate){
    const dayToKorean = ['월', '화', '수', '목', '금', '토', '일'];
    const year = nowDate.getFullYear();
    const month = String(Number(nowDate.getMonth()) + 1);
    const date = nowDate.getDate();
    const day = nowDate.getDay();

    return year + '년 ' + month + '월 ' + date + '일 ' + dayToKorean[day] + '요일';
  }
  return '';
};

이 또한 반환된 값을 출력하기만 하면 된다

return // ...
    <div className="Header">
      <h1 className="Header-Title">TODOLIST</h1>
      <div className="Header-Day">{renderDate()}</div>
  </div>
// ...
  );

후기

작년에도 React로 Todo List 만들기를 두 번 정도 했었다
그때는 아무 것도 모르고 기능을 구현하는 것에만 초점에 두고 진행했었는데 역시 기초 공부를 한 뒤에 만드는 것이 얻어가는 것도 많은 것 같다
또한 똑같은 걸 만들더라도 여러번 하면 코딩 속도가 빨라지는 것 같다

디자인 실력은 어떻게 해도 늘지 않는 것 같다 ㅎ...

0개의 댓글