Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ jobs:
run: npm run lint
- name: test
run: npm test

2 changes: 2 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ module.exports = {
printWidth: 80,
// 화살표 함수가 하나의 매개변수를 받을 때 괄호를 생략
arrowParens: 'avoid',

endOfLine: 'lf',
};
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경하신 이유가 있을까요?

<html lang="en">
<head>
<meta charset="utf-8" />
Expand Down
8 changes: 8 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
body {
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
text-align: center;
}
43 changes: 41 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className="App">
<h1>Todo List</h1>
<Form />
<Form onAddTodo={handleAddTodo} />
<ul>
<Item />
{todos.length === 0 ? (
<li>할 일이 없습니다.</li>
) : (
todos.map(todo => (
<Item
key={todo.id}
id={todo.id}
todo={todo.todo}
isCompleted={todo.isCompleted}
onCompleteTodo={handleCompleteTodo}
onDeleteTodo={handleDeleteTodo}
/>
))
)}
</ul>
</div>
);
Expand Down
6 changes: 6 additions & 0 deletions src/components/Form.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.addButton {
width: 100px;
border: 0;
border-radius: 10px;
padding: 10px 20px;
}
29 changes: 25 additions & 4 deletions src/components/Form.js
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<input type="text" />
<button type="button">추가</button>
</div>
<form onSubmit={onAdd}>
<input
className="inputTodo"
type="text"
value={value}
onChange={onChange}
placeholder="할 일을 입력해주세요."
/>
<button className="addButton" type="submit">
추가
</button>
</form>
);
}

Expand Down
20 changes: 20 additions & 0 deletions src/components/Item.css
Original file line number Diff line number Diff line change
@@ -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;
}
32 changes: 17 additions & 15 deletions src/components/Item.js
Original file line number Diff line number Diff line change
@@ -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 (
<li>
<input type="checkbox" />
<p>{todo}</p>
<input
type="checkbox"
checked={isCompleted}
onChange={() => onCompleteTodo(id, !isCompleted)}
/>
<span className="todo">{todo}</span>
<div>
<button type="button">삭제</button>
<button
className="deleteButton"
type="button"
onClick={() => onDeleteTodo(id)}
>
삭제
</button>
</div>
</li>
);
}

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;