본문 바로가기
[WEB]/React

React - To Do Item 추가 기능

by hi_kmin6 2020. 3. 11.

 

오늘은 지난 글에 추가했던 ToDoTemplate에

할 일을 작성하고 입력하면 화면에 다시 보여 주도록 하는 기능을 넣어봤습니다.

 

작업을 위해서 사용하는 것은 LocalStorage입니다.

 

Chrome 브라우저를 사용하고 있기 떄문에 Chrome브라우저를 기준으로 설명하겠습니다.

 

만들고 있는 react App을 실행 시켜 해당 페이지에서 키보드의 [F12]

혹은 상단 툴바의 Toggle버튼을 눌러 [도구 더보기 ▶ 개발자 도구]를 눌러보겠습니다.

 

개발자 도구

 

그럼 위와 같이 개발자 도구가 나오며,

Elements가 써있는 탭에서 Application을 눌러 LocalStorage를 확인해보겠습니다.

 

 

LocalStorage

 

Application을 누른 뒤 왼쪽 영역에 [Storage ▶ LocaStorage http://localhost:3000/]을 누르시면 해당 페이지에서 읽기만이 가능한 저장공간을 확인할 수 있습니다.

 

이제 여기에 ToDoList의 할 일들을 저장시켜, 페이지를 나갔다가 들어와도 정보가 유지될 수 있도록 만들어 보겠습니다.

 

 


# 오늘의 개념  사항

1. Props & State 추가

 

지난 글 <ToDoTemplate Component 추가>에서는 props와 state가 어떤 것인지를 적고,

사용법은 얘기를 안 하고 바로 코드만 달랑 넣어놨기에 조금 더 구체적인 요소가 필요하다 생각했습니다.

 

React에서는 props를 사용할 때 순수 함수의 사용과 같이하길 당부하고 있다고 말씀드렸습니다.

순수 함수는 입력 값이 있었고, 이를 변형하지 않고 사용한 결과를 가져다주었습니다.

여기서 입력 값이 바로 props인 것입니다.

 

현재의 component에서 props라는 입력 값은 바로 위의 component, 부모 component로부터 받아옵니다.

 

제가 작성한 코드를 잠깐 사용하여 설명해보겠습니다.

 

render() {
    const { todoItems } = this.state;

    return (
      <div className="template-Container">
        <form className="template-Form">
          <input type="text" placeholder="오늘의 할일" />
        </form>
        <div className="template-list">
          {todoItems.map(item => {
            return <ToDoItem text={item.text} />;
          })}
        </div>
      </div>
    );
 }

 

ToDoTemplate Component의 render() 부분을 가져와 보았습니다.

 

ToDoTemplate에서는 할 일들을 가져와 각각의 것들 마다 ToDoItem을 이용해 화면에 출력하려 합니다.

ToDoItem에 입력해야 할 값은 어떤 할 일인지 보이기 위해 text값이 필요합니다.

 

즉 ToDoTemplate은 ToDoItem의 부모 Component이며, 입력 값으로 할 일의 text값을 넘겨줍니다.

 

위의 코드 중간 부분의 <ToDoItem text = {item.text}/>가 text라는 이름으로 props를 넘겨주는 부분입니다.

 

 

그럼 ToDoItem을 살펴보도록 하겠습니다.

 

 

import React from "react";

function ToDoItem(props) {
  return (
    <div className="item-container">
      <div className="item-remove">✖</div>
      <div className="item-text">{props.text}</div>
      <div className="item-check">✔</div>
    </div>
  );
}

export default ToDoItem;

 

먼저 첫 번째 줄 function ToDoItem(props)에서 알 수 있듯 props를 매개변수로 받고 있습니다.

그리고 중간 부분의 <div className="item-text">{props.text}</div>에서 props 중 text값을 사용하고 있습니다.

 

props는 객체로 값이 전달됩니다.

위의 ToDoItem에서는 'ToDoItem(props)'라고 전달받았습니다만 이것 외에 다른 방법으로

'ToDoItem({text})'라고 전달받는 방법도 있습니다.

이렇게 text값을 받아오면 '{props.text}'가 아닌 '{text}'로 사용해주면 됩니다.

 

 

ToDoItem은 현재 functional Component이기에 위와 같이 사용했습니다.

만약 class component였다면 다음과 같은 형식으로 props를 사용했을 겁니다.

 

import React from "react";

class ToDoItem extends React.Component{
  constructor(props){
    super(props);
  }

  render(){
    return(
      <div className="item-container">
        <div className="item-remove">✖</div>
    	<div className="item-text">{props.text}</div>
    	<div className="item-check">✔</div>
      </div>
    );
  }
}

export default ToDoItem;

 

functional props와는 다르게 class는 만들어질 때 생성자(Constructor)를 거쳐서 만들어지기 때문에,

부모의 Props를 가져오기 위해서 Super라는 키워드를 사용해서 가져옵니다.

 

가져온 props에 대한 사용은 마찬가지로 {props.text}로 사용하고 있습니다.

 

 


# 오늘의 작업

ToDoTemplate.js

 

import React from "react";
import ToDoItem from "./ToDoItem";

class ToDoTemplate extends React.Component {
  toDoList = [];

  constructor(props) {
    super(props);
    this.toDoList = JSON.parse(localStorage.getItem("toDoList")) || [];
    // localStorage에서 toDoList라는 이름의 데이터를 가져오되, 존재하지 않을 시에는 빈 배열을 반환 합니다.
    this.state = {
      todoItems: this.toDoList
    };
  }

  // List의 Items를 지우거나 추가할 때 LocalStorage와 State를 변경해주기위한 함수
  saveToDos = () => {
    localStorage.setItem("toDoList", JSON.stringify(this.toDoList));
    this.setState({ todoItems: this.toDoList });
  };

  // form에서 toDoItem을 입력 후 제출하는 이벤트에 대한 함수
  itemSubmit = event => {
    const todoTemplate = document.querySelector(".template-Container"),
      todoForm = todoTemplate.querySelector(".template-Form"),
      todoInput = todoForm.querySelector("input");

    const date = new Date(),
      dateCode =
        date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
    const newID =
      date.getHours() + "" + date.getMinutes() + "" + date.getSeconds();

    const toDoObj = {
      id: newID,
      text: todoInput.value,
      checked: false,
      writeDate: dateCode
    };

    this.toDoList.push(toDoObj);

    this.saveToDos();

    event.preventDefault();
    todoInput.value = "";
  };

  // render가 진행된 뒤에 eventListner를 추가
  componentDidMount() {
    const todoTemplate = document.querySelector(".template-Container"),
      todoForm = todoTemplate.querySelector(".template-Form");

    todoForm.addEventListener("submit", this.itemSubmit);
  }

  render() {
    const { todoItems } = this.state;

    return (
      <div className="template-Container">
        <form className="template-Form">
          <input type="text" placeholder="오늘의 할일" />
        </form>
        <div className="template-list">
          {todoItems.map(item => {
            return <ToDoItem text={item.text} />;
          })}
        </div>
      </div>
    );
  }
}

export default ToDoTemplate;

 

기존의 코드에 Component가 생성되면서 LocalStorage의 toDoList를 가져오며,

할 일을 입력하면 localStorage에 추가가 되고, state 또한 변경될 수 있도록 작성한 코드입니다.

 

toDoList = [];

constructor(props) {
  super(props);
  this.toDoList = JSON.parse(localStorage.getItem("toDoList")) || [];
  // localStorage에서 toDoList라는 이름의 데이터를 가져오되, 존재하지 않을 시에는 빈 배열을 반환 합니다.
  this.state = {
    todoItems: this.toDoList
  };
}

 

이 부분에서 toDoList라는 빈 배열을 class field로 생성했고,

Component가 생성되면서 localStorage로부터 "toDoList"라는 데이터를 가져옵니다.

가져온 데이터는 string데이터 이기 때문에 이를 JavaScript Object로 변환하여 toDoList에 저장합니다.

단, localStorage에 해당 정보가 없으면 빈 배열을 저장합니다.

 

그렇게 가져온 데이터를 state에 둡니다.

 

// render가 진행된 뒤에 eventListner를 추가
componentDidMount() {
  const todoTemplate = document.querySelector(".template-Container"),
    todoForm = todoTemplate.querySelector(".template-Form");

  todoForm.addEventListener("submit", this.itemSubmit);
}

componentDidMount()에서는 eventListner를 달아주었습니다.

아무래도 component가 render가 된 뒤에 querySelector를 사용할 수 있을 거 같기에 위의 방법처럼 진행했습니다.

 

// List의 Items를 지우거나 추가할 때 LocalStorage와 State를 변경해주기위한 함수
saveToDos = () => {
  localStorage.setItem("toDoList", JSON.stringify(this.toDoList));
  this.setState({ todoItems: this.toDoList });
};

// form에서 toDoItem을 입력 후 제출하는 이벤트에 대한 함수
itemSubmit = event => {
  const todoTemplate = document.querySelector(".template-Container"),
    todoForm = todoTemplate.querySelector(".template-Form"),
    todoInput = todoForm.querySelector("input");

  const date = new Date(),
    dateCode =
      date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
  const newID =
      date.getHours() + "" + date.getMinutes() + "" + date.getSeconds();

  const toDoObj = {
    id: newID,
    text: todoInput.value,
    checked: false,
    writeDate: dateCode
  };

  this.toDoList.push(toDoObj);

  this.saveToDos();

  event.preventDefault();
  todoInput.value = "";
};

위의 코드에서는 input에 할 일을 입력하여 전송하는 경우 할 일을 저장할 수 있게 하는 코드입니다.

 

상단의 saveToDos()는 할 일 목록에 대해서 추가나 삭제를 수행한 경우,

변화가 생긴 배열을 localStorage에 넣고 state를 변화시켜주기 위해서 만들었습니다.

 

itemSubmit()에서는 각각의 할 일의 정보를 다음과 같이 입력해 주었습니다.

 

1.  id

 작성 시간(시+분+초)을 기준으로 결정했습니다.

 추후 할 일 목록 삭제에서 고유한 값을 가지게 하여 다른 것들과 함께 지워지지 않게 하기 위함입니다.

 

2. text

 할 일의 문자 데이터, 즉 사용자가 입력하는 값입니다.

 

3. checked

 할 일을 마치고 나면 완료된 상태를 나타낼 수 있는 값입니다.

 새롭게 작성되는 할 일은 아직 수행되지 않은 일 이게 때문에,

 작성 시에 false로, 그리고 완료 후엔 true로 값을 넣어 줄 것입니다.

 추후 이 값들을 이용해 당일 목표 달성률을 계산해보고자 합니다.

 

4. writeDate

 일단 날짜별로 할 일 목록을 따로 작성할 수 있게 만들고자 하여 임의로 넣은 값입니다.

 고민을 좀 더 해보고 이 값을 사용하는 것이 아닌 다른 방법을 사용하고자 합니다.

 

위의 4개의 값을 갖은 객체가 toDoList라는 필드 배열에 추가되며,

이 추가된 배열이 localStorage에 있는 기존의 toDoList를 대체합니다.

또한 state를 업데이트시켜, 화면이 다시  render 됩니다.

 

 

 

비어있는 localStorage

 

 

Work1입력 대기 중

 

Work1입력 후

 

위와 같이 새로운 일을 입력하면 localStorage에 저장이 되며, 화면에 출력되는 모습을 볼 수 있습니다.

 

다음 글에서는 할 일들에 대해서 각각 삭제와 완료됨을 표시할 수 있게 기능을 추가해보도록 하겠습니다.

 

 

 

 


#참고 자료

https://velopert.com/3480

 

React 기초 입문 프로젝트 – 흔하디 흔한 할 일 목록 만들기 | VELOPERT.LOG

이 포스트는 Fastcampus 의 리액트 강의 에서 사용된 강의 자료로서, 부연설명이 조금 생략되어있습니다. 기초가 부족하시다면 좀 오래되긴 했지만 저의 강의목록 에서 나오는 3편, 4편, 5편, 7편을 가볍게 읽고오세요 (해당 강의들의 실습은 따라하지 않으셔도 됩니다) 0. 시작하기 이번에는 프론트엔드 기초를 다룰때면 흔히 만들게 되는 투두 리스트, 혹은 “할 일 목록” 을 구현해보겠습니다. (우리는 앞으로 위와 같은 프로젝트를 만들어가게 됩니다) D

velopert.com

 

 


#작성 록

2020.03.11

솔직히 공부하는 방법을 잘 모르겠습니다.

어디서 어떻게 하면 된다 하고 알려 줄 수 있는 문제가 아니기 때문에 어려운 것 같습니다.

그래도 지금처럼 무언가를 만들어보고 배운 것을 기록하는 것은 어떤 방법으로 공부하는지에 상관없이 도움이 될 거라 생각하고 꾸준히 해봐야겠습니다.

'[WEB] > React' 카테고리의 다른 글

React - CSS작업  (0) 2020.03.16
React - To Do Item 삭제  (0) 2020.03.12
React - ToDoTemplate Component 추가  (0) 2020.03.09
React - 시계 Component 추가  (0) 2020.03.05
React - React App 만들기&비우기  (0) 2020.03.04