
오늘은 지금까지 배운 지식을 활용하여 일정 관리 애플리케이션을 만들어보겠습니다!
✍ example
$ cd todo-app
$ yarn add sass classnames react-icons
sass 설치 classnames는 조건부 스타일링을 더 편하게 하기 위해 설치 ✍ example
body{
margin: 0;
padding: 0;
background: #e9ecef;
}
✔ 결과 확인

앞으로 만들 컴포넌트
1. TodoTemplate : 앱 타이틀 보여주기
2. TodoInsert : 새로운 항목을 입력, 추가
3. TodoListItem : 각 항목에 대한 정보를 보여주기
4. TodoList : 여러 개의 TodoListItem 컴포넌트로 변환하여 보여주기
✍ example
//TodoTemplate.js
import './TodoTemplate.scss';
const TodoTemplate=({children})=>{
return(
<div className="TodoTemplate">
<div className="app-title">일정 관리</div>
<div className="content">{children}</div>
</div>
);
};
export default TodoTemplate;
//App.js
import TodoTemplate from './components/TodoTemplate';
const App=()=>{
return <TodoTemplate>Todo App을 만들자! </TodoTemplate>
};
export default App;
✍ example
//jsconfig.json
{
"compilerOptions": {
"target": "es6"
}
}
jsconfig.json 파일을 최상위 디렉토리에 만들면 됨.✔ 결과 확인

✍ example
//TodoTemplate.scss
.TodoTemplate {
width: 512px; //width가 주어진 상태에서 좌우 중앙 정렬
margin-left: auto;
margin-right: auto;
margin-top: 6rem;
border-radius: 4px;
overflow: hidden;
}
.app-title {
background: #22b8cf;
color: white;
height: 4rem;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.content {
background: white;
}
✔ 결과 확인

✍ example
//TodoInsert.js
import{MdAdd} from 'react-icons/md';
import './TodoInsert.scss';
const TodoInsert=()=>{
return(
<form className="TodoInsert">
<input placeholder="할 일을 입력하세요"/>
<button type="submit">
<MdAdd/>
</button>
</form>
);
};
export default TodoInsert;

import{아이콘 이름} from 'react-icons/md';위에서 사용하고 싶은 아이콘을 선택하여 import구문을 이용해 컴포넌트 처럼 사용가능✔ 결과 확인

일정 관리 항목이 보일 TodoListItem과 TodoList를 만들어보자.
✍ example
//TodoListItem.js
import{
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';
//TodoList.js
import TodoListItem from "./TodoListItem";
import'./TodoList.scss';
const TodoList=()=>{
return(
<div className="TodoList">
<TodoListItem/>
<TodoListItem/>
<TodoListItem/>
</div>
);
};
export default TodoList;
✔ 결과 확인

✍ example
//App.js
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import { useState } from 'react';
const App=()=>{
const[todos, setTodos]=useState([{
id:1,
text:'리액트 기초 알아보기',
checked: true,
},
{
id:2,
text:'컴포넌트 스타일링 보기',
checked: true,
},
{
id:3,
text:'일정 관리 앱 만들어 보기',
checked:false,
},
]);
return (<TodoTemplate>
<TodoInsert/>
<TodoList todos={todos}/>
</TodoTemplate>
)
};
export default App;
✍ example
//TodoList.js
import TodoListItem from "./TodoListItem";
import'./TodoList.scss';
const TodoList=({todos})=>{
return(
<div className="TodoList">
{todos.map(todo=>(
<TodoListItem todo={todo} key={todo.id}/>
))}
</div>
);
};
export default TodoList;
map()을 통해 TodoListItem으로 이뤄진 배열로 변환하여 렌더링. ✍ example
//TodoListItem.js
import{
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';
import cn from 'classnames';
import './TodoListItem.scss';
const TodoListItem=({todo})=>{
const{text, checked}=todo;
return(
<div className="TodoListItem">
<div className={cn("checkbox", {checked})}>
{checked ? <MdCheckBox/> :<MdCheckBoxOutlineBlank/>}
<div className="text">{text}</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
✔ 결과 확인

✍ example
//TodoInsert.js
import { useState, useCallback } from 'react';
import{MdAdd} from 'react-icons/md';
import './TodoInsert.scss';
const TodoInsert=()=>{
const[value, setValue]=useState('');
const onChange=useCallback(e=>{
setValue(e.target.value);
}, []);
return(
<form className="TodoInsert">
<input
placeholder="할 일을 입력하세요"
value={value}
onChange={onChange}/>
<button type="submit">
<MdAdd/>
</button>
</form>
);
};
export default TodoInsert;
현재 state가 잘 업데이트되고 있는지 console.log를 찍어보지 않고도 확인하는 방법이 있다.
=> 리액트 개발자 도구를 사용하면 됨.


Components이 나타남. ✍ example
//App.js
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import { useState, useRef, useCallback } from 'react';
(...)
const nextId=useRef(4);
const onInsert=useCallback(
text=>{
const todo={
id: nextId.current,
text,
checked: false,};
setTodos(todos.concat(todo));
nextId.current+=1;
},
[todos],
)
return (<TodoTemplate>
<TodoInsert onInsert={onInsert}/>
<TodoList todos={todos}/>
</TodoTemplate>
)
};
export default App;
✍ example
//TodoInsert.js
(...)
const TodoInsert=({onInsert})=>{
const[value, setValue]=useState('');
const onChange=useCallback(e=>{
setValue(e.target.value);
}, []);
const onSubmit=useCallback(
e=>{
onInsert(value);
setValue('');
e.preventDefault();
},
[onInsert, value],
)
(...)
✔ 결과 확인

✍ example
const array=[1, 2, 3, 4, 5, 6, 7];
const biggerThanFive=array.filter(number=>number>5);
//결과: [6. 7]
✍ example
//App.js
(...)
const onRemove=useCallback(
id=>{
setTodos(todos.filter(todo=>todo.id!==id));
},
[todos],
);
(...)
✍ example
//App.js
(...)
const onToggle = useCallback(
id => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo,
),
);
},
[todos],
);
(...)
todo.id === id ? ... : ... 삼항연산자 사용-todo.id와 현재 파라미너 id값이 같을 때에는 새로운 객체를 생성하지만 id값이 다르면 변화를 주지 않으므로 map을 사용해서 변화가 필요한 원소만 업데이트!!✔ 결과 확인
