diff --git a/README.md b/README.md
index 3c0710d2..21e352d1 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,26 @@
-# react-todo-list-precourse
\ No newline at end of file
+# react-todo-list-precourse
+
+## 기능 요구 사항
+
+하루 또는 한 주의 할 일 목록을 업데이트하는 할 일 목록을 구현한다. **React 라이브러리를 사용하여 웹 앱으로 구현한다.**
+
+- 할 일을 추가하고 삭제할 수 있다.
+ - 할 일을 추가할 때 사용자는 Enter 키나 추가 버튼을 사용하여 할 일을 목록에 추가할 수 있어야 한다.
+ - 사용자가 아무것도 입력하지 않은 경우에는 할 일을 추가할 수 없다.
+- 할 일의 목록을 볼 수 있다.
+- 할 일의 완료 상태를 전환할 수 있다.
+
+### 선택 요구 사항
+
+- 현재 진행 중인 할 일, 완료된 할 일, 모든 할 일을 필터링할 수 있다.
+- 해야 할 일의 총개수를 확인할 수 있다.
+- 새로고침을 하여도 이전에 작성한 데이터는 유지되어야 한다.
+
+### 피드백
+
+- 키보드로 할일을 입력할 때 이벤트가 2번 실행됩니다. 이는 한글과 같은 문자를 입력할 때 키보드 이벤트를 사용하는 경우 발생하는 문제입니다. -> react isComposing
+- 완료한 할일의 삭제 버튼에 선 없애기
+- 함수 컴포넌트를 포함하여 함수는 15줄 이내로 작성
+- 컴포넌트와 유틸함수 분리
+- depth는 3 이내로
+- css 변경은 design을 사용할 것
diff --git a/index.html b/index.html
index b021b5c8..fdc8a04b 100644
--- a/index.html
+++ b/index.html
@@ -1,12 +1,12 @@
-
+
-
+ Todos📋
-
+
diff --git a/src/App.css b/src/App.css
new file mode 100644
index 00000000..5b841cae
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,131 @@
+#root {
+ font-family: Arial, sans-serif;
+ background-color: #f0f0f0;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ height: 100vh;
+}
+
+.container {
+ background-color: #fff;
+ padding: 20px;
+ border-radius: 5px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ width: 300px;
+ position: fixed;
+ top: 40px;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+h1 {
+ text-align: center;
+ color: #333;
+}
+
+.todo-input {
+ display: flex;
+ justify-content: space-between;
+}
+
+.todo-input input {
+ flex: 1;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 5px 0 0 5px;
+ outline: none;
+}
+
+.todo-input button {
+ padding: 10px;
+ border: none;
+ background-color: #28a745;
+ color: white;
+ border-radius: 0 5px 5px 0;
+ cursor: pointer;
+}
+
+.todo-input button:hover {
+ background-color: #218838;
+}
+
+ul {
+ list-style: none;
+ padding: 0;
+ margin-top: 20px;
+ max-height: 60vh;
+ overflow-y: scroll;
+}
+
+li {
+ background-color: #fff;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 5px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+li span {
+ flex-grow: 1;
+ margin-right: 10px;
+}
+
+li input[type="checkbox"] {
+ margin-right: 10px;
+}
+
+.completed span {
+ text-decoration: line-through;
+ color: #888;
+}
+
+.completed input[type="checkbox"] {
+ pointer-events: none;
+}
+
+li button {
+ background-color: #dc3545;
+ border: none;
+ color: white;
+ padding: 5px 10px;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+li button:hover {
+ background-color: #c82333;
+}
+
+.filters {
+ display: flex;
+ justify-content: center;
+ margin-top: 20px;
+}
+
+.filters button {
+ padding: 5px 10px;
+ border: 1px solid #ddd;
+ background-color: #fff;
+ cursor: pointer;
+}
+
+.filters button:not(:last-child) {
+ border-right: none;
+}
+
+.filters button.active {
+ background-color: #ddd;
+}
+
+.todo-footer {
+ text-align: left;
+ margin-top: 20px;
+ font-size: 14px;
+ color: #555;
+}
diff --git a/src/App.jsx b/src/App.jsx
new file mode 100644
index 00000000..07d8d7ac
--- /dev/null
+++ b/src/App.jsx
@@ -0,0 +1,95 @@
+import React, { useState } from "react";
+import "./App.css";
+
+function App() {
+ const [todos, setTodos] = useState([]);
+ const [newTodo, setNewTodo] = useState("");
+ const [filter, setFilter] = useState("all"); // 'all', 'active', 'completed'
+
+ const addTodo = () => {
+ if (newTodo.trim() === "") return;
+ setTodos([...todos, { text: newTodo, completed: false }]);
+ setNewTodo("");
+ };
+
+ const toggleTodo = (index) => {
+ const newTodos = [...todos];
+ newTodos[index].completed = !newTodos[index].completed;
+ setTodos(newTodos);
+ };
+
+ const deleteTodo = (index) => {
+ const newTodos = [...todos];
+ newTodos.splice(index, 1);
+ setTodos(newTodos);
+ };
+
+ const handleKeyDown = (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ addTodo();
+ }
+ };
+
+ const incompleteTodosCount = todos.filter((todo) => !todo.completed).length;
+
+ const filteredTodos = todos.filter((todo) => {
+ if (filter === "all") return true;
+ if (filter === "active") return !todo.completed;
+ if (filter === "completed") return todo.completed;
+ return true;
+ });
+
+ return (
+
+
Todos📋
+
+ setNewTodo(e.target.value)}
+ onKeyDown={handleKeyDown}
+ placeholder="Add a new task..."
+ />
+ Add
+
+
+
+ {incompleteTodosCount} item{incompleteTodosCount !== 1 ? "s" : ""} left
+
+
+ setFilter("all")} className={filter === "all" ? "active" : ""}>
+ All
+
+ setFilter("active")}
+ className={filter === "active" ? "active" : ""}
+ >
+ Active
+
+ setFilter("completed")}
+ className={filter === "completed" ? "active" : ""}
+ >
+ Completed
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 00000000..76fae189
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,88 @@
+body {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ background-color: #f0f0f0;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+.container {
+ background-color: #fff;
+ padding: 20px;
+ border-radius: 5px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ width: 300px;
+}
+
+h1 {
+ text-align: center;
+ color: #333;
+}
+
+.todo-input {
+ display: flex;
+ justify-content: space-between;
+}
+
+.todo-input input {
+ flex: 1;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 5px 0 0 5px;
+ outline: none;
+}
+
+.todo-input button {
+ padding: 10px;
+ border: none;
+ background-color: #28a745;
+ color: white;
+ border-radius: 0 5px 5px 0;
+ cursor: pointer;
+}
+
+.todo-input button:hover {
+ background-color: #218838;
+}
+
+ul {
+ list-style: none;
+ padding: 0;
+ margin-top: 20px;
+}
+
+li {
+ background-color: #fff;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 5px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+li.completed {
+ text-decoration: line-through;
+ color: #888;
+}
+
+li button {
+ background-color: #dc3545;
+ border: none;
+ color: white;
+ padding: 5px 10px;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+li button:hover {
+ background-color: #c82333;
+}
+
+li span {
+ cursor: pointer;
+}
diff --git a/src/main.js b/src/main.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/main.jsx b/src/main.jsx
new file mode 100644
index 00000000..2b349aa2
--- /dev/null
+++ b/src/main.jsx
@@ -0,0 +1,10 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App";
+import "./index.css";
+
+ReactDOM.createRoot(document.getElementById("app")).render(
+
+
+
+);