diff --git a/.eslintrc.js b/.eslintrc.js
index 1a0050e..2bc29df 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -21,7 +21,12 @@ module.exports = {
sourceType: 'module',
},
rules: {
- 'prettier/prettier': 'error',
+ 'prettier/prettier': [
+ 'error',
+ {
+ endOfLine: 'auto',
+ },
+ ],
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 95957a4..0b90ba7 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -20,4 +20,3 @@ jobs:
run: npm run lint
- name: test
run: npm test
-
\ No newline at end of file
diff --git a/.prettierrc.js b/.prettierrc.js
index 2252fdc..171a241 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -13,4 +13,6 @@ module.exports = {
printWidth: 80,
// 화살표 함수가 하나의 매개변수를 받을 때 괄호를 생략
arrowParens: 'avoid',
+
+ endOfLine: 'lf',
};
diff --git a/public/index.html b/public/index.html
index aa069f2..e65acb3 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,4 +1,4 @@
-
+
diff --git a/src/App.css b/src/App.css
new file mode 100644
index 0000000..9c6296f
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,8 @@
+body {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+h1 {
+ text-align: center;
+}
diff --git a/src/App.js b/src/App.js
index ae5ce50..3286e49 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,13 +1,52 @@
+import { useState, useRef } from 'react';
import Form from './components/Form';
import Item from './components/Item';
+import './App.css';
function App() {
+ const [todos, setTodos] = useState([]);
+
+ const nextId = useRef(1);
+
+ const handleAddTodo = todo => {
+ const newTodo = {
+ id: nextId.current,
+ todo,
+ isCompleted: false,
+ };
+ setTodos([newTodo, ...todos]);
+ nextId.current += 1;
+ };
+
+ const handleCompleteTodo = (id, isCompleted) => {
+ setTodos(
+ todos.map(todo => (todo.id === id ? { ...todo, isCompleted } : todo)),
+ );
+ };
+
+ const handleDeleteTodo = id => {
+ setTodos(todos.filter(todo => todo.id !== id));
+ };
+
return (
Todo List
-
+
-
+ {todos.length === 0 ? (
+ - 할 일이 없습니다.
+ ) : (
+ todos.map(todo => (
+
+ ))
+ )}
);
diff --git a/src/components/Form.css b/src/components/Form.css
new file mode 100644
index 0000000..6d9042a
--- /dev/null
+++ b/src/components/Form.css
@@ -0,0 +1,6 @@
+.addButton {
+ width: 100px;
+ border: 0;
+ border-radius: 10px;
+ padding: 10px 20px;
+}
diff --git a/src/components/Form.js b/src/components/Form.js
index cb405e1..b07466d 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -1,11 +1,32 @@
import PropTypes from 'prop-types';
+import { useState } from 'react';
+import './Form.css';
function Form({ onAddTodo }) {
+ const [value, setValue] = useState('');
+ const onChange = e => {
+ setValue(e.target.value);
+ };
+ const onAdd = e => {
+ e.preventDefault();
+ if (value.trim() !== '') {
+ onAddTodo(value);
+ setValue('');
+ }
+ };
return (
-
-
-
-
+
);
}
diff --git a/src/components/Item.css b/src/components/Item.css
new file mode 100644
index 0000000..bf6b9ec
--- /dev/null
+++ b/src/components/Item.css
@@ -0,0 +1,20 @@
+.inputTodo {
+ width: 400px;
+ border: 1px solid #e0e0e0;
+ margin-right: 10px;
+ padding: 10px 20px;
+}
+.deleteButton {
+ width: 70px;
+ border: 0;
+ border-radius: 10px;
+ padding: 10px 20px;
+ margin-top: 10px;
+}
+.todo {
+ margin: 20px 10px;
+}
+li {
+ margin: 10px 0;
+ list-style: none;
+}
diff --git a/src/components/Item.js b/src/components/Item.js
index a4bd48e..7062400 100644
--- a/src/components/Item.js
+++ b/src/components/Item.js
@@ -1,32 +1,34 @@
import PropTypes from 'prop-types';
+import './Item.css';
-function Item({ id, todo, isCompleted, onDeleteTodo, onCompleteTodo }) {
+function Item({ id, todo, isCompleted, onCompleteTodo, onDeleteTodo }) {
return (
-
- {todo}
+ onCompleteTodo(id, !isCompleted)}
+ />
+ {todo}
-
+
);
}
Item.propTypes = {
- id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ id: PropTypes.number.isRequired,
todo: PropTypes.string.isRequired,
isCompleted: PropTypes.bool.isRequired,
- /**
- * 할 일 삭제 함수
- * @param {number|string} id 삭제할 할 일의 id
- */
- onDeleteTodo: PropTypes.func.isRequired,
- /**
- * 할 일 완료 함수
- * @param {number|string} id 완료할 할 일의 id
- * @param {boolean} isCompleted 완료 여부
- */
onCompleteTodo: PropTypes.func.isRequired,
+ onDeleteTodo: PropTypes.func.isRequired,
};
export default Item;