본문 바로가기
부트캠프 개발일지 2023-2024/Bootcamp 생활기록

[6주차] 리액트로 Todo List 만들기

by whereanna00 2023. 11. 7.

 

1. 리액트 셋업

더보기

1. 프로젝트 생성하기

 1. vs code 를 켜줍니다.

 2. terminal 에 들어갑니다.

 3. 아래 명령어를 통해 프로젝트 파일을 생성할 파일을 생성합니다. 

 

 ** 먼저, 아래 명령어를 통해 현재 내가 있는 위치와 현재 위치에 있는 모든 파일 리스트를 확인합니다.

ls

 

 

** 바탕화면에서 Github 라는 파일까지 가려면, 아래의 순서대로 명령어 cd 를 사용하며 들어가줍니다.

cd Desktop
cd Github

 

 

** 프로젝트를 담을 폴더를 만들어줍니다. 폴더 이름은 standard 로 해보겠습니다.

mkdir standard

 

 

** 방금 만든 폴더 안으로 이동합니다.

cd standard

 

 

** 이제 프로젝트 파일을 만들어봅니다. 프로젝트 파일 이름은 todolist1107 로 해보겠습니다.

 yarn create react-app todolist1107

 

 

보통 프로젝트 파일을 만들기까지에는 약 20초에서 60초까지 걸립니다. 조금만 기다려주세요.

 

 

**프로젝트 파일이 만들어졌다면, 터미널에서 제시해주는 프로젝트 시작 명령어를 실행해주세요. 

cd todolist1107
yarn start

 

 

4. vs code 에서 프로젝트파일들이 들어있는 폴더를 열어주세요. 저의 경우에는 todolist1107 을 열면 되겠죠?

5. terminal 에 들어가 다시 프로젝트 활성화를 해주세요

yarn start

 

 

 

<참고>

 

[5주차] 리액트입문: React 프로젝트 처음 생성하기

Git bash 명령어 정리 git bash 명령어 정리 pwd : 현재 폴더 cd : directory 이동, change directory? cd.. : 상위폴더 이동 cd../폴더명 : 해당 폴더로 바로 이동 자동완성 : 조금 타이핑하다가 tab키 드라이브 이동

whereannalee.tistory.com

 

 

 

 

2. 프로젝트 초기설정

  1. App.js 파일에 컴포넌트 'App'의 return 부분을 모두 삭제해줍니다.

 

 

 

2. 와이어프레임 구상하기 (구조 짜기)

더보기

1.  큰 단위로 먼저 나누기

 

보통의 html 구조와 같이,

우리는 header-main-footer 로 크게 3 영역으로 나눕니다.

 

이걸 코드로 표현해볼까요?

현재 우리는 App.jsx 파일에 있습니다. (App.js 를 App.jsx 로 바꿔주세요)

 

우리가 현재 3가지 영역으로 나눈 구조는 화면에 보여지는 부분이죠?

그러면 화면에 보이게 해야 합니다.  html 코드처럼 화면에 보이게 하고 UI 작업을 하려면 우리는 JSX 문법을 사용해야 합니다.

 

JSX 문법을 그럼 어디다가 써야 할까요?

바로 컴포넌트의 반환값 안에 써야 합니다.

return () 이 안을 말합니다.

JSX 문법 규칙 : 오직 하나의 태그가 감싸는 형태여야 한다.

 

import './App.css';

function App() {
  return (
    <div>
      <header>
        헤더입니다.
      </header>
      <main>
        메인입니다.
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

화면

 

코드로 표현하게 되면 이렇습니다. 보시다시피, <div>태그가 크게 감싸고 있고, 그 안으로 자식 요소들로 3 가지 영역으로 나눠져 있습니다.

 

 

구분을 하기 위해 background color 를 추가합니다.

// App.jsx
import './App.css';

function App() {
  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        메인입니다.
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

/* App.css */
.outer_frame > header {
  background-color: rgb(155, 210, 155);
}

.outer_frame > main {
  background-color: rgb(249, 196, 201);
}

.outer_frame > footer {
  background-color: rgb(187, 223, 255);
}

 

2.  상세 프레임 짜기

 

아주 상세하게까지는 아니더라도, 대충 main 부분을 어떻게 나누고 그 안에 어떤 요소들이 들어갈 지 생각합니다.

 

 

3.  input (입력창) 영역

더보기

1.  상세 뼈대 만들어주기

 

제목을 입력하는 input 태그 하나, 내용을 입력하는 input 태그 하나, 그리고 버튼을 만들어줍니다.

이 세 요소를 form tag 로 묶어줘 좀 더 시맨틱한 코딩을 합니다.

 

// App.jsx
import './App.css';

function App() {
  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form>
          제목: <input/>
          내용: <input/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">

        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

 

 

4.  리스트 (카드가 붙는) 영역

더보기

1.  카드 데이터  배열만들기

 

todo list 에서 어떤 부분이 '변하는' 부분인가요?

바로 카드 안에 있는 내용입니다. 그러므로, 리액트 환경에서 우리는 카드 안에 있는 데이터의 state를 변화시킴으로써 화면에 리랜더링을 해줘야 합니다. 그럼 먼저 카드 데이터를 담는 배열을 생성합니다.

 

초기값은 일단 카드 세개를 넣어놓았습니다.

 

// App.jsx
import './App.css';
import {useState} from 'react';

function App() {
  const [toDoList, setToDoList] = useState(
    [{
      id: 1,
      title: 'title1',
      content: 'content1',
      isDone: false
    },
    {
      id: 2,
      title: 'title2',
      content: 'content2',
      isDone: false
    },
    {
      id: 3,
      title: 'title3',
      content: 'content3',
      isDone: false
    },
    ]
  );

  
  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form>
          제목: <input/>
          내용: <input/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">

        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

 

2.  카드 데이터, 화면에 어떻게 보여줄 지 정하기

이제 이것을 화면에 보여주기 위한 작업을 해야 합니다.

컴포넌트의 return 값 즉 JSX 부분이 화면에 보여지는 영역입니다.

이 영역에서 초기값으로 만들어둔 카드 데이터(배열로 이루어진)를 어떤 형식 또는 모양으로 화면에 보여줄지를 적습니다.

 

카드 영역은 총  두 개 영역으로 나뉘는데, 

1) 해야 할 일 목록

2) 완료된 목록

입니다.

 

따라서 첫번째 영역인 해야할일 목록의 제목을 넣어주고,

카드들이 쌓일 영역을 아래와 같이 적어줍니다.

 **JSX안에서 자바스크립트 요소가 들어올 땐 중괄호를 사용합니다. 

  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form>
          제목: <input/>
          내용: <input/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <h1>해야 할 일 목록</h1>
          {
            toDoList
          }
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );

 

'해야할 일 목록' 영역에서 우리가 가지고 있는 초기값(카드 3개)을 어떤 형태로 화면에 보이게 할까요?

<아이디><제목><내용><isDone여부>

 

위 형태로 화면에 나오면 좋을 것 같습니다.

그러면, 우리가 카드 개수만큼 쓰면 코드가 길어지니 배열 메소드를 이용해 알아서 반복시켜줘봅시다.

 

배열 안에 있는 요소 하나 하나를 돌며, 원하는 데이터 모양으로 가공시켜 반환한는 map 을 써봅시다.

 

예를 들어,

카드 첫번째 정보가 담겨져있는 첫번째 요소를

{ id: 1, title: 'title1', content: 'content1', isDone: false }

 

 

map 이 정한(개발자가 정한) 틀에

        {
            toDoList.map(function(todo){
              return (
                <div>
                  <p>{todo.id}</p>
                  <h3>{todo.title}</h3>
                  <p>{todo.content}</p>
                  <p>{todo.isDone.toString()}</p>
                </div>
              );
            })
          }

 

넣어 가공해 반환하면 어떤 결과가 나올까요?

이렇게 나오게 됩니다.

 

**JSX 안에서 자바스크립트 요소가 들어올 땐 중괄호를 사용합니다. 

**toString() 은 문자열화 api 

 

 

return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form>
          제목: <input/>
          내용: <input/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <h1>해야 할 일 목록</h1>
          {
            toDoList.map(function(todo){
              return (
                <div>
                  <p>{todo.id}</p>
                  <h3>{todo.title}</h3>
                  <p>{todo.content}</p>
                  <p>{todo.isDone.toString()}</p>
                </div>
              );
            })
          }
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );

 

 

결과가 잘 나왔습니다.

 

+ 아이디 값

아이디 보완하기 

아이디를 숫자를 이용하여 붙이는 것도 좋지만, UUID 를 통해 아이디를 지정해주는 것이 추후 예상치 못한 버그를 방지하는 방법입니다. 

 

terminal 에서 

npm install react-uuid
yarn start

 

후에 import 해주시면 됩니다.

 

// App.jsx
import './App.css';
import {useState} from 'react';
import uuid from 'react-uuid';

function App() {
  const [toDoList, setToDoList] = useState(
    [{
      id: uuid(),
      title: 'title1',
      content: 'content1',
      isDone: false
    },
    {
      id: uuid(),
      title: 'title2',
      content: 'content2',
      isDone: false
    },
    {
      id: uuid(),
      title: 'title3',
      content: 'content3',
      isDone: false
    },
    ]
  );


  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form>
          제목: <input/>
          내용: <input/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <h1>해야 할 일 목록</h1>
          {
            toDoList.map(function(todo){
              return (
                <div>
                  <h3>{todo.title}</h3>
                  <p>{todo.content}</p>
                  <p>{todo.isDone.toString()}</p>
                  <p>{uuid()}</p>
                </div>
              );
            })
          }
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

 

리액트에서 UUID 사용하기

UUID는 Universally Unique IDentifier, 범용 고유 식별자의 약자로 고유한 아이디 값을 생성할때 사용할 수 있는 표준이다.

velog.io

 

+ map 메소드에 대한 Error  해결하기

리액트 환경에서 map 메소드를 사용할 때, 위와 같은 에러가 뜨는 경우가 있습니다. 이것은 리스트 자식요소들이 모두 고유한 키값을 가져야 한다는 뜻입니다. 우리는 uuid()를 key 로 넣어줍니다. key를 넣는 위치는, return 값에서 가장 첫 요소(부모요소)에 넣어주면 됩니다.

 

              return (
                  <div key={uuid()} className="card">
                    <h3>{todo.title}</h3>
                    <p>{todo.content}</p>
                    <p>{todo.isDone.toString()}</p>
                    <p>{uuid()}</p>
                  </div>
                );
              })

 

 

 

3.  완료 목록 코드 작성하기

1) 해야 할 일 목록

2) 완료된 목록

을 아래 형태처럼 div 로 한 번 더 감싸줍니다.

 

우리는 해야할일과 완료한일을 isDone의 불리언 타입 여부로 나눌 것이기 때문에 카드가 화면에 뿌려지는 로직은 할일목록영역과 완료목록이 동일합니다. 따라서 해야할일 목록에서 쓴 map 함수를 완료목록에도 동일하게 복사 붙여넣기 해줍니다.

 

// App.jsx
import './App.css';
import {useState} from 'react';
import uuid from 'react-uuid';

function App() {
  const [toDoList, setToDoList] = useState(
    [{
      id: uuid(),
      title: 'title1',
      content: 'content1',
      isDone: false
    },
    {
      id: uuid(),
      title: 'title2',
      content: 'content2',
      isDone: false
    },
    {
      id: uuid(),
      title: 'title3',
      content: 'content3',
      isDone: false
    },
    ]
  );


  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form>
          제목: <input/>
          내용: <input/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <div className="list_ing_area">
            <h1>해야 할 일 목록</h1>
            {
              toDoList.map(function(todo){
                return (
                  <div key={uuid()} className="card">
                    <h3>{todo.title}</h3>
                    <p>{todo.content}</p>
                    <p>{todo.isDone.toString()}</p>
                    <p>{uuid()}</p>
                  </div>
                );
              })
            }
          </div>
          <div className="list_done_area">
          <h1>완료된 목록</h1>
            {
              toDoList.map(function(todo){
                return (
                  <div key={uuid()} className="card">
                    <h3>{todo.title}</h3>
                    <p>{todo.content}</p>
                    <p>{todo.isDone.toString()}</p>
                    <p>{uuid()}</p>
                  </div>
                );
              })
            }
          </div>
          
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

 

여기서 아직 끝난게 아니죠!

toDoLists 에 있는 객체로 이루어진 요소 하나 하나를 필터링을 하여 해야할일과 완료한일로 각각 넣어줘야 합니다.

 

그럼 순서는

toDoLists - > 요소의 key인 isDone의 값에 따른 filter() 필터링 -> 후에 나온 데이터들로 map()

가 되겠죠.

 

          <div className="list_done_area">
          <h1>완료된 목록</h1>
            {
              toDoList.filter(function(todo){
                return todo.isDone === true;
              }).map(function(todo){
                return (
                  <div key={uuid()} className="card">
                    <h3>{todo.title}</h3>
                    <p>{todo.content}</p>
                    <p>{todo.isDone.toString()}</p>
                    <p>{uuid()}</p>
                  </div>
                );
              })
            }
          </div>

 

 

 

 

 

 

5. 입력창 영역 완성하기

더보기

1.  입력박스(제목,내용) 안에 내용이 화면에 보이게 하기

 

 

화면에 보이게 하는 것은 모두 setState를 통해 이루어져야 가능합니다.

따라서 제목과 내용에 대한 setState를 선언합니다.

 

  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

 

 

 

input 태그는 form 태그로 감쌌는데요. 그 이유는 좀 더 시맨틱한 코드작성을 위해서입니다. 

form 태그가 값을 전달하기 위해선 submit이 필요한데요. button 태그가 submit을 내장하고 있습니다. 

button 태그에는 type="submit" 으로 명시해주는 것을 권장합니다.

//jxs        
        
        <form>
          제목: <input value={title} onChange={titleHandler}/>
          내용: <input value={content} onChange={contentHandler}/>
          <button type="submit">입력</button>
        </form>

 

 

 

사용자가 입력하는 값 = event.target.value

input 의 입력값을 변하게 하는 onChange 속성을 이용하여 event가 일어났을 때 제목부분과 내용부분에 값을 리랜더링할 수 있도록 setTitle과 setContent에 각각 넣어줍니다.

 

 

  function titleHandler(event) {
    setTitle(event.target.value);
  }
  function contentHandler(event) {
    setContent(event.target.value);
  }

 

 

 

 

 

2.  입력버튼을 눌러 카드 추가하는 기능

 

입력버튼을 누르는 이벤트에 따라서 새로운 카드가 추가되는 기능을 만들어보겠습니다.

현재 form 태그에 onSubmit 이 들어가, 자동적으로 버튼을 누르는 이벤트가 onSubmit 과 연결됩니다.

	<form onSubmit={addHandler}>
          제목: <input value={title} onChange={titleHandler}/>
          내용: <input value={content} onChange={contentHandler}/>
          <button type="submit">입력</button>
        </form>

 

 

카드가 추가되는 이벤트를 따로 함수로 빼서 로직을 구현해봅시다.

  function addHandler(event){
    event.preventDefault();
    const newToDo = {
      title: title,
      content: content,
      isDone: false,
      id: uuid(),
    }
    setToDoList([...toDoList, newToDo]);
  }

 

먼저, form 태그의 제출 동시에 새로고침이 되는 것을 막기 위해 event.preventDefault(); 를 넣어줍니다.

제출 버튼을 누르면, 생기는 객체요소를 선언해줍니다.

그리고, 그것을 setToDoList 에 spreadOperator를 사용해 기존 toDoList를 펼쳐놓은 객체요소들의 모음들과 newToDo를 함께 합쳐서 다시 배열로 묶어 넣어 데이터를 set 합니다.

 

지금까지의 로직을 정리하면

입력된 데이터를 통해 newToDo 라는 요소를 기존 ToDoList 요소에 추가해 ToDoList의 값을 변경한다 -> 그렇게 업데이트된 ToDoList에서 isDone 이 true냐, flase냐에 따라 두 영역에서 각각 해당하는 데이터만 가져와 map 메소드로 해당 요소 모두를 돌아 화면에 랜더링한다.

 

 

 

 

 

6.  삭제버튼 기능

더보기

1.  삭제버튼 프레임 만들기

 

return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form onSubmit={addHandler}>
          제목: <input value={title} onChange={titleHandler}/>
          내용: <input value={content} onChange={contentHandler}/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <div className="list_ing_area">
            <h1>해야 할 일 목록</h1>
            {
              toDoList.filter((todo) => todo.isDone === false)
                .map((todo) => {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button>삭제</button>
                      <button>완료</button>
                    </div>
                  );
                })
            }
          </div>
          <div className="list_done_area">
          <h1>완료된 목록</h1>
            {
              toDoList.filter((todo)=> todo.isDone === true)
                .map((todo)=> {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button>삭제</button>
                      <button>완료취소</button>
                    </div>
                  );
                })
            }
          </div>
          
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );

 

 

2.  삭제버튼 기능 구현

button에 onClick을 주고, deleteHandler 라는 함수를 줍니다.

이 함수는 App 컴포넌트 안에서 정의될 것이기 때문에, 현재 map 함수 안에 있는 todo 에 대한 정의가 이루어지지 않은 상태입니다. 

 

따라서 아래와 같이 todo.id 를 함께 넘겨줘야 이벤트가 정상적으로 실행됩니다.

  function deleteHandler(id) {
    const deletedToDoList = toDoList.filter((item)=> {
      return item.id !== id;
    })
    setToDoList(deletedToDoList);
  }
<button onClick={() => deleteHandler(todo.id)}

 

 진행중인 목록과 완료된 목록의 각 삭제버튼에 같은 로직을 적용해주면 됩니다.

 

 

 

 

 

더보기
// App.jsx
import './App.css';
import {useState} from 'react';
import uuid from 'react-uuid';

function App() {
  const [toDoList, setToDoList] = useState(
    [{
      id: uuid(),
      title: 'title1',
      content: 'content1',
      isDone: false
    },
    {
      id: uuid(),
      title: 'title2',
      content: 'content2',
      isDone: true,
    },
    {
      id: uuid(),
      title: 'title3',
      content: 'content3',
      isDone: false
    },
    ]
  );

  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  function titleHandler(event) {
    setTitle(event.target.value);
  }
  function contentHandler(event) {
    setContent(event.target.value);
  }
  function addHandler(event){
    event.preventDefault();
    const newToDo = {
      title: title,
      content: content,
      isDone: false,
      id: uuid(),
    }
    setToDoList([...toDoList, newToDo]);
  }
  function deleteHandler(id) {
    const deletedToDoList = toDoList.filter((item)=> {
      return item.id !== id;
    })
    setToDoList(deletedToDoList);
  }

  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form onSubmit={addHandler}>
          제목: <input value={title} onChange={titleHandler}/>
          내용: <input value={content} onChange={contentHandler}/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <div className="list_ing_area">
            <h1>해야 할 일 목록</h1>
            {
              toDoList.filter((todo) => todo.isDone === false)
                .map((todo) => {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button onClick={() => deleteHandler(todo.id)}>삭제</button>
                      <button>완료</button>
                    </div>
                  );
                })
            }
          </div>
          <div className="list_done_area">
          <h1>완료된 목록</h1>
            {
              toDoList.filter((todo)=> todo.isDone === true)
                .map((todo)=> {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button onClick={() => deleteHandler(todo.id)}>삭제</button>
                      <button>완료취소</button>
                    </div>
                  );
                })
            }
          </div>
          
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

 

7.  완료버튼 기능

더보기

1.  완료버튼 프레임 만들기

 

 <button onClick={() => doneHandler(todo.id)}>완료</button>

 

2.  완료버튼 기능 만들기

완료버튼을 클릭한 id 값을 받은 요소를 찾아 -> isDone 을 true로 바꿔주기

 

  function doneHandler(id) {
    const newToDoList = toDoList.map((item)=> {
      if(item.id === id){
        return {...item, isDone: !item.isDone};
      } else {
        return item;
      }
    })
    setToDoList(newToDoList);
  }

 

 

 

 

8.  완료취소 버튼 기능

더보기

1.  완료취소 버튼 프레임 만들기

<button onClick={() => doneHandler(todo.id)}>완료취소</button>

 

 

2.  완료취소 버튼 기능 만들기

완료취소의 로직은 무엇일까요?

버튼을 선택한 아이디값을 가진 요소를 찾아 -> isDone을 false로 바꿔주면 됩니다.

 

방금 만들었던 '완료'버튼과 같은 로직입니다.

따라서 완료로직을 그대로 복사해서 붙여줍니다.

 

	<h1>완료된 목록</h1>
            {
              toDoList.filter((todo)=> todo.isDone === true)
                .map((todo)=> {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button onClick={() => deleteHandler(todo.id)}>삭제</button>
                      <button onClick={() => doneHandler(todo.id)}>완료취소</button>
                    </div>
                  );
                })
            }

 

 

 

여기까지 하셨다면, 기능적으로 모두 구현이 완료되었습니다!

전체코드보기

더보기
// App.jsx
import './App.css';
import {useState} from 'react';
import uuid from 'react-uuid';

function App() {
  const [toDoList, setToDoList] = useState(
    [{
      id: uuid(),
      title: 'title1',
      content: 'content1',
      isDone: false
    },
    {
      id: uuid(),
      title: 'title2',
      content: 'content2',
      isDone: true,
    },
    {
      id: uuid(),
      title: 'title3',
      content: 'content3',
      isDone: false
    },
    ]
  );

  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  function titleHandler(event) {
    setTitle(event.target.value);
  }

  function contentHandler(event) {
    setContent(event.target.value);
  }

  function addHandler(event){
    event.preventDefault();
    const newToDo = {
      title: title,
      content: content,
      isDone: false,
      id: uuid(),
    }
    setToDoList([...toDoList, newToDo]);
  }

  function deleteHandler(id) {
    const deletedToDoList = toDoList.filter((item)=> {
      return item.id !== id;
    })
    setToDoList(deletedToDoList);
  }

  function doneHandler(id) {
    const newToDoList = toDoList.map((item)=> {
      if(item.id === id){
        return {...item, isDone: !item.isDone};
      } else {
        return item;
      }
    })
    setToDoList(newToDoList);
  }

  return (
    <div className="outer_frame">
      <header>
        헤더입니다.
      </header>
      <main>
        <form onSubmit={addHandler}>
          제목: <input value={title} onChange={titleHandler}/>
          내용: <input value={content} onChange={contentHandler}/>
          <button type="submit">입력</button>
        </form>
        <div className="list_area">
          <div className="list_ing_area">
            <h1>해야 할 일 목록</h1>
            {
              toDoList.filter((todo) => todo.isDone === false)
                .map((todo) => {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button onClick={() => deleteHandler(todo.id)}>삭제</button>
                      <button onClick={() => doneHandler(todo.id)}>완료</button>
                    </div>
                  );
                })
            }
          </div>
          <div className="list_done_area">
          <h1>완료된 목록</h1>
            {
              toDoList.filter((todo)=> todo.isDone === true)
                .map((todo)=> {
                  return (
                    <div key={uuid()} className="card">
                      <h3>{todo.title}</h3>
                      <p>{todo.content}</p>
                      <p>{todo.isDone.toString()}</p>
                      <p>{uuid()}</p>
                      <button onClick={() => deleteHandler(todo.id)}>삭제</button>
                      <button onClick={() => doneHandler(todo.id)}>완료취소</button>
                    </div>
                  );
                })
            }
          </div>
          
        </div>
      </main>
      <footer>
        푸터입니다.
      </footer>
    </div>
  );
}

export default App;

 

 

9.  로컬스토리지 추가

728x90
반응형