# 08. TODO APP (Read, Create)

### 들어가기전에

앞서 배운 내용들을 바탕으로 Todo APP 을 만들어보려고 합니다. \
Todo 는 크게 추가, 삭제, 수정, 필터링으로 이루어집니다.\
동작은 [여기](http://todomvc.com/examples/react/#/) 에서 확인 가능합니다.

![](/files/-LoneHW0W_KjG0sSAagP)

### 실습

> 프로젝트는 [초기 세팅 상태](https://simplereact.gitbook.io/simplereact/02.-componet#undefined-1)로 진행되어집니다. 코드량을 최대한 줄이고자 style 은 적용하지 않습니다.

먼저 Todo List 를 가져와 보여주는 부분부터 진행해보겠습니다.

위의 이미지에서 Input 부분이 Header 이고, List 가 표현되는 부분이 Content, 아래 Filter 부분이 Footer 입니다. 크게 3 덩어리로 분리하려고합니다. &#x20;

#### 기본 구성

3개의 Component 를 추가합니다.

```javascript
// src/components/header.js

import React from "react";

function Header() {
  return <div>Header</div>;
}

export default Header;
```

```javascript
// src/components/contents.js

import React from "react";

function Contents() {
  return <div>Contents</div>;
}

export default Contents;
```

```javascript
// src/components/footer.js

import React from "react";

function Footer() {
  return <div>Footer</div>;
}

export default Footer;
```

App.js 에서 가져오겠습니다.

```javascript
// App.js 

import React from "react";

import Header from "./components/header";
import Contents from "./components/contents";
import Footer from "./components/footer";

function App() {
  return (
    <div>
      <Header />
      <Contents />
      <Footer />
    </div>
  );
}

export default App;
```

Todo State 를 추가합니다. Todo 는 텍스트, 상태를 가집니다. 그리고 수정, 삭제시 필요한 고유한 id 를 부여해줘야합니다. 클라이언트에서 고유한 값을 판단하기 가장 쉽고 좋은것은 Date 를 이용하는 것 입니다.

```javascript
// App.js 

import React, { useState } from "react";

import Header from "./components/header";
import Contents from "./components/contents";
import Footer from "./components/footer";

function App() {
  // hook 을 이용하여 state 를 다룹니다.
  const [todos, setTodos] = useState([
    {
      id: Date.now(), // 고유한 id
      text: "리액트 공부하기", // 텍스트
      isDone: false // 완료, 미완료 상태
    },
    {
      id: Date.now(),
      text: "밥 먹기",
      isDone: true
    }
  ]);

  return (
    <div>
      <Header />
      <Contents />
      <Footer />
    </div>
  );
}

export default App;
```

만든 Todos State 를 해당 내용을 그려줄 Contents 에게 전달합니다.

```javascript
// App.js

<Contents todos={todos} />
```

####

#### List 그리기&#x20;

Contents 는 Props 로 Todo List 를 받습니다.

```javascript
// src/components/contents.js 

import React from "react";

function Contents({ todos }) {
  return <div>{JSON.stringify(todos)}</div>;
}

export default Contents;
```

![](/files/-LonjS0mnUsPGh8zAMdA)

Props 로 넘어오는것을 확인 할 수 있습니다. 이제 받은 데이터를 바탕으로 List 를 그려줍니다.

Todo Component 를 추가합니다. 이 Component 는 Todo 하나에 대한 상태를 업데이트 할 수 있는 Checkbox 와 텍스트를 가지고 있습니다.

```javascript
// src/components/contents.js 

import React from "react";

function Todo({ todo: { id, text, isDone } }) {
  return (
    <div>
      <input type="checkbox" checked={isDone} />
      <span>{text}</span>
    </div>
  );
}

function Contents({ todos }) {
  return (
    <div>
      {todos.map(todo => (
        <Todo todo={todo} key={todo.id} />
      ))}
    </div>
  );
}

export default Contents;
```

Checkbox 의 상태는 isDone 에 영향을 받습니다. 여기까지하면 일단 Read 는 다 했습니다.\
위 처럼 map 같이 loop 를 돌면서 Component 를 그려줄 때는 항상 고유한 key 값이 필요합니다.\
\=> 지금은 샘플 데이터가 Date.now 를 동시에 호출해서 만들기 때문에 경고가 납니다. 뒤에 샘플 데이터를 제거하면 경고가 사라질거에요

![](/files/-LonmMKwOeowVUP-4-eC)

####

#### Todo 추가&#x20;

이제 Input 에 적은 글을 바탕으로 새로운 Todo 를 만들어내는 것을 해야합니다.\
그 전에 생각해보아야 할 것이 있습니다.&#x20;

Input 은 Header 에 있고 Todo List 의 State 는 App.js 즉 부모에 있습니다.\
\=> 즉, 자식에서 만든 데이터를 부모의 데이터에 추가해야 되는 일이 발생한 것 입니다.\
\=> 이럴때는 보통 데이터를 가지고 있는곳에서 함수를 만들어 자식에게 제공합니다.\
\=> Todo List 를 가진 App.js 에서 Todos 데이터를 다룰 수 있는 함수를 만들어 자식 Header 에게 제공합니다.

App.js 에 handleAdd 라고 불리는 함수를 추가합니다. 해당 함수는 text 를 받아 새로운 Todo 를 만들고 자신이 가진 todos 에 push 해주는 역할을 합니다. 이 함수를 자식 Header 에게 전달합니다.

\=> 보통 내부에서 데이터를 다루는 함수는 handle, 외부에서 넘겨준 event 는 on 이라는 이름을 많이 사용합니다.

```javascript
// app.js

import React, { useState } from "react";

import Header from "./components/header";
import Contents from "./components/contents";
import Footer from "./components/footer";

function App() {
  const [todos, setTodos] = useState([
    {
      id: Date.now(),
      text: "리액트 공부하기",
      isDone: false
    },
    {
      id: Date.now(),
      text: "밥 먹기",
      isDone: true
    }
  ]);

  const handleAdd = text => { 
    setTodos([ // 기존의 데이터와 text 를 바탕으로 만들어진 새로운 todo 를 합칩니다
      ...todos,
      {
        id: Date.now(),
        text,
        isDone: false
      }
    ]);
  };

  return (
    <div>
      <Header onChange={handleAdd} />
      <Contents todos={todos} />
      <Footer />
    </div>
  );
}

export default App;

```

이제 Header 를 만들어보겠습니다. Header 는 text 라는 State 를 가지고 있고 이 State 는 Input 에 값에 따라 변경됩니다.

```javascript
// src/components/header.js

import React, { useState } from "react";

function Header({ onChange }) {
  const [text, setText] = useState("");

  const handleChange = e => { // input event 
    setText(e.target.value);
  };
  
  return (
    <div>
      <h1>TODO APP</h1>
      <input onChange={handleChange} value={text} />
    </div>
  );
}

export default Header;
```

![](/files/-LonrDKY3IT1JvpbG4XM)

이제 ADD 버튼을 추가하여 해당 버튼이 눌렸을 때 text 의 값을 부모에서 내려받은 onChange 에 전달하면 새로운 Todo 가 추가되는 것 입니다.

```javascript
// src/components/header.js

import React, { useState } from "react";

function Header({ onChange }) {
  const [text, setText] = useState("");

  const handleChange = e => {
    setText(e.target.value);
  };

  const handleClick = () => {
    console.log("text", text);
  };

  return (
    <div>
      <h1>TODO APP</h1>
      <input
        type="text"
        onChange={handleChange}
        value={text}
        placeholder="오늘은 어떤 일을 하시나요?"
      />
      <button onClick={handleClick}>추가</button>
    </div>
  );
}

export default Header;

```

![](/files/-Lonu8WDyncXQS8zg_ls)

handleClick 함수를 조금만 수정해주면됩니다.

```javascript
// src/components/header.js

const handleClick = () => {
  onChange(text);
  setText(""); // 추가 후 input 을 비워주기 위해 state 를 초기화 합니다.
};
```

그리고 App.js 에 있는 샘플 데이터를 제거합니다. todos 를 \[] 로 만들어줍니다.

```javascript
// app.js

import React, { useState } from "react";

import Header from "./components/header";
import Contents from "./components/contents";
import Footer from "./components/footer";

function App() {
  const [todos, setTodos] = useState([]); // 샘플 데이터 제거

  const handleAdd = text => {
    setTodos([
      ...todos,
      {
        id: Date.now(),
        text,
        isDone: false
      }
    ]);
  };

  return (
    <div>
      <Header onChange={handleAdd} />
      <Contents todos={todos} />
      <Footer />
    </div>
  );
}

export default App;
```

![](/files/-Lonv7KwFJOMmYM1jXAk)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://simplereact.gitbook.io/simplereact/08.-todo-app-create.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
