diff --git a/eslint.config.js b/eslint.config.js
index 238d2e4..85f281a 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -29,10 +29,13 @@ export default [
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
+ "no-unused-vars" : "off",
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
+
],
+ 'react/no-unknown-property': ['error', { ignore: ['css'] }],
},
},
]
diff --git a/package.json b/package.json
index 9f1bcb2..ef82f8c 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "@emotion/react": "^11.13.3",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
diff --git a/src/App.jsx b/src/App.jsx
index 852a9fe..4cd76c4 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,5 +1,57 @@
+/** @jsxImportSource @emotion/react */
+import { useState, useEffect } from 'react';
+import { css } from '@emotion/react';
+import TodoInput from './components/TodoInput';
+import TodoList from './components/TodoList';
+import TodoHeader from './components/TodoHeader';
+
+
+const containerStyle = css`
+ max-width: 400px;
+ margin: 100px auto;
+ padding: 20px;
+ background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 100%);
+ border-radius: 20px;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+`;
+
+
function App() {
- return
React Todo
;
+ const [todos, setTodos] = useState(() => {
+ // 로컬 스토리지에서 저장된 todos 가져오기
+ const savedTodos = localStorage.getItem('todos');
+ return savedTodos ? JSON.parse(savedTodos) : []; // 저장된 값이 있으면 파싱하여 사용, 없으면 빈 배열
+ });
+
+ useEffect(() => {
+ // todos 상태가 변경될 때마다 로컬 스토리지에 저장
+ localStorage.setItem('todos', JSON.stringify(todos));
+ }, [todos]);
+
+ // 새 할 일을 추가하는 함수
+ const addTodo = (text) => {
+ setTodos(todos.concat({ text, completed: false })); // 새로운 할 일을 기존 할 일 리스트에 추가
+ };
+
+ // 특정 인덱스의 할 일을 삭제하는 함수
+ const deleteTodo = (index) => {
+ setTodos(todos.filter((_, i) => i !== index)); // 해당 인덱스의 할 일을 제외하고 새로운 리스트 생성
+ };
+
+ // 특정 인덱스의 할 일의 완료 상태를 토글하는 함수
+ const toggleTodo = (index) => {
+ setTodos(todos.map((todo, i) =>
+ i === index ? { ...todo, completed: !todo.completed } : todo
+ )); // 할 일의 완료 상태를 반전시킴
+ };
+
+ return (
+
+
+
+
+
+ );
}
-export default App;
+export default App;
diff --git a/src/components/TodoButton.jsx b/src/components/TodoButton.jsx
new file mode 100644
index 0000000..15e4bab
--- /dev/null
+++ b/src/components/TodoButton.jsx
@@ -0,0 +1,23 @@
+/** @jsxImportSource @emotion/react */
+import { css } from '@emotion/react';
+
+const buttonStyle = css`
+ padding: 10px;
+ cursor: pointer;
+ background-color: #ff7f7f;
+ border: none;
+ border-radius: 5px;
+ color: white;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+ margin-left: 8px;
+`;
+
+function TodoButton({ onClick, children }) {
+ return (
+
+ {children} {/* 버튼 내부의 내용 (아이콘이나 텍스트) */}
+
+ );
+}
+
+export default TodoButton;
diff --git a/src/components/TodoHeader.jsx b/src/components/TodoHeader.jsx
new file mode 100644
index 0000000..8077879
--- /dev/null
+++ b/src/components/TodoHeader.jsx
@@ -0,0 +1,14 @@
+/** @jsxImportSource @emotion/react */
+import { css } from '@emotion/react';
+
+const titleStyle = css`
+ font-size: 24px;
+ text-align: center;
+ margin-bottom: 20px;
+`;
+
+function TodoHeader() {
+ return 📚 TodoList ;
+}
+
+export default TodoHeader;
diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx
new file mode 100644
index 0000000..6714f5f
--- /dev/null
+++ b/src/components/TodoInput.jsx
@@ -0,0 +1,57 @@
+//** @jsxImportSource @emotion/react */
+import { useState } from 'react';
+import { css } from '@emotion/react';
+
+const inputContainerStyle = css`
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+const inputStyle = css`
+ padding: 10px;
+ width: 200px;
+ margin-right: 10px;
+ border: none;
+ border-radius: 5px;
+ outline: none;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+`;
+
+const buttonStyle = css`
+ padding: 10px;
+ cursor: pointer;
+ background-color: #ff7f7f;
+ border: none;
+ border-radius: 5px;
+ color: white;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+`;
+
+function TodoInput({ addTodo }) {
+ const [input, setInput] = useState(''); // 입력 값을 관리하기 위한 상태 선언
+
+ // 폼 제출 시 호출되는 함수
+ const handleSubmit = (e) => {
+ e.preventDefault(); // 기본 폼 제출 동작을 막음
+ if (!input.trim()) return; // 입력 값이 공백일 경우 아무 작업도 하지 않음
+ addTodo(input);
+ setInput('');
+ };
+
+ return (
+
+ );
+}
+
+export default TodoInput;
\ No newline at end of file
diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx
new file mode 100644
index 0000000..ee00191
--- /dev/null
+++ b/src/components/TodoItem.jsx
@@ -0,0 +1,38 @@
+/** @jsxImportSource @emotion/react */
+import { css } from '@emotion/react';
+import TodoButton from './TodoButton';
+
+// Todo 항목의 기본 스타일 정의
+const itemStyle = css`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 0;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.5);
+ cursor: pointer;
+ color: #fff;
+`;
+
+// 완료된 Todo 항목 스타일 정의
+const completedStyle = css`
+ text-decoration: line-through; // 완료된 항목을 취소선으로 표시
+ color: #aaa;
+`;
+
+// 개별 Todo 항목을 나타내는 컴포넌트
+function TodoItem({ todo, index, deleteTodo, toggleTodo }) {
+ return (
+
+ {/* 할 일 텍스트 표시 */}
+
{todo.text}
+
+ {/* 할 일 완료 상태를 토글하는 버튼 */}
+ toggleTodo(index)}>✔️
+ {/* 할 일을 삭제하는 버튼 */}
+ deleteTodo(index)}>🗑️
+
+
+ );
+}
+
+export default TodoItem;
\ No newline at end of file
diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx
new file mode 100644
index 0000000..6be7e7d
--- /dev/null
+++ b/src/components/TodoList.jsx
@@ -0,0 +1,36 @@
+/** @jsxImportSource @emotion/react */
+import { css } from '@emotion/react';
+import TodoListSection from './TodoListSection';
+
+
+const listContainerStyle = css`
+ width: 300px;
+ max-width: 100%;
+ margin: 0 auto;
+`;
+
+function TodoList({ todos, deleteTodo, toggleTodo }) {
+ const pendingTodos = todos.filter((todo) => !todo.completed); // 완료되지 않은 할 일만 필터링
+ const completedTodos = todos.filter((todo) => todo.completed); // 완료된 할 일만 필터링
+
+ return (
+
+ {/* TO DO 섹션 */}
+
+ {/* DONE 섹션 */}
+
+
+ );
+}
+
+export default TodoList;
diff --git a/src/components/TodoListSection.jsx b/src/components/TodoListSection.jsx
new file mode 100644
index 0000000..4d1bdc9
--- /dev/null
+++ b/src/components/TodoListSection.jsx
@@ -0,0 +1,26 @@
+/** @jsxImportSource @emotion/react */
+import { css } from '@emotion/react';
+import TodoItem from './TodoItem';
+
+const sectionStyle = css`
+ margin-bottom: 20px;
+`;
+
+function TodoListSection({ title, todos, deleteTodo, toggleTodo }) {
+ return (
+
+
{title} ({todos.length})
+ {todos.map((todo, index) => (
+
+ ))}
+
+ );
+}
+
+export default TodoListSection;