투두리스트 앱을 리액트로 만들기 위해선 우선
구조부터 정해야 한다.

투두리스트 앱은 이러한 구조를 가지고 있다.
가장 최상위인 App.jsx 가 여러 자식 컴포넌트를 가지고 있는 구조이다.
App.jsx 는 상태를 관리한다. 자식 컴포넌트에서 사용될 수 있는 state 를 저장한다.
Input 은 사용자 입력을 받는 컴포넌트이다.
Item 은 투두리스트에서 하나의 요소를 나타낸다. 삭제, 완료 여부를 결정할 수 있다.
List 는 배열을 순회하면서 전체 할 일 목록을 출력한다.
import TodoInput from "./component/TodoInput";
import './App.css';
import { useRef, useState } from "react";
import { useEffect } from "react";
import List from "./component/List";
export default function App() {
const [list, setList] = useState([]); // 할 일 목록
const [input, setInput] = useState(''); // 사용자 입력
// 모든 컴포넌트의 변수는 계속 초기값으로 초기화된다. 따라서 ref 를 써야함
let idx = useRef(2); // 할 일 고유 번호
// 컴포넌트가 처음 마운트될 때 실행(샘플 리스트 2개 넣어둠)
useEffect(()=>{
setList([
{
idx:1,
todo:'Spring 공부하기',
done:true,
},
{
idx:2,
todo:'React 공부하기',
done:false,
}
]);
},[]);
// 새로운 할 일 추가 기능
const insertTodo = () => {
idx.current++;
// 새 할 일을 객체로 생성
let todo = {idx:idx.current, todo:input, done:false};
//console.log('to-do : ', todo);
console.log('to-do', todo);
setList([...list, todo]); // 기존 리스트를 복사해서 ...list로 풀고, 새 항목 todo를 뒤에 추가해서 새로운 리스트로 만든다
setInput(''); // 추가 후 입력창 지우기
}
// 입력창에 사용자가 타이핑할 때마다 호출됨
const typing = (e) => {
setInput(e.target.value);
}
const toggle = (id) => {
console.log(id, ' 번 checked 변화 발생');
// 1. list 에서 id 번에 item 을 가져오기
// 특정한 조건을 만족하는 인덱스를 반환
let index = list.findIndex(item=>{
return item.idx === id;
});
console.log(index, list[index]);
// 2. 해당 아이템의 done 값을 변경하기
// 2.1 list 의 안전을 위해 복사본을 만든다
let copy_list = [...list];
// 2.2 특정 인덱스의 done 값을 반전하여 저장
copy_list[index].done = !copy_list[index].done;
// 2.3 최종 데이터를 덮어 쓴다
setList(copy_list);
}
const del = (id) => {
console.log(id + ' 번 삭제');
let index = list.findIndex(item=>{
return item.idx === id;
});
let copy_list = [...list];
copy_list.splice(index, 1); // 인덱스로부터 한개 지워라
setList(copy_list);
}
return(
<div className="app">
<TodoInput onInsert={insertTodo}
value={input}
onTyping={typing}/>
<List list={list} toggle={toggle} del={del}/>
</div>
);
}
App.jsx 는 프로젝트의 최상위 컴포넌트로서 상태 관리, 함수를 만들어서 자식 컴포넌트에게 전달을 한다. 기본적으로 함수는 여기서 전부 만들어서 자식 컴포넌트에게 넘겨주는 방식이다.
export default function TodoInput({onInsert, value, onTyping}) {
const KeyUpHandler = (e) => {
if(e.keyCode === 13) {
onInsert();
}
}
return(
<div className="input">
<h2>할 일 관리</h2>
<hr />
<input type="text" value={value} onChange={onTyping}
onKeyUp={KeyUpHandler}/>
<button onClick={onInsert}>추가</button>
</div>
);
}
TodoInput.jsx 는 사용자에 입력을 받는 부분이다. 사용자가 입력한 내용을 받아서 리스트에 추가한다. 여기서 사용자가 추가 버튼을 누르지 않고, 키보드의 엔터키만 쳐도 추가가 되게끔 하기 위해 KeyUpHandler() 함수를 사용했다. 엔터키는 키코드가 13이므로 13이면 onIsert() 함수가 실행되도록 했다. 이렇게 하면 엔터키를 입력하면 할 일이 리스트에 추가된다.
export default function Item({children, done, done_yn, toggle, idx, del}) {
let class_name = `text ${done_yn}`;
return (
<div className="item">
<input className="chk" type="checkbox"
onChange={()=>{toggle(idx)}}
checked={done}/>
<div className={class_name}>{children}</div>
<div className="delete" onClick={()=>{del(idx)}}>[삭제]</div>
</div>
);
}
여기서는 투두리스트의 요소 하나를 나타낸다. 체크박스가 있어서 체크박스를 누르면 체크가 되었다가 해제되는 동작이 있고, 삭제 버튼을 누르면 추가했던 리스트가 삭제되는 기능이 있다.
import Item from "./Item.jsx";
export default function List({list, toggle, del}) {
//console.log(list);
const items = list.map(item => {
//console.log(item);
return <Item
key={item.idx} // key 라는 이름으로는 props로 못넘김(유니크 키:사용은 안하지만 필수로 있어야함)
idx={item.idx} // 따라서 idx 라는 이름으로 따로 만들어서 넘김
done={item.done}
done_yn={item.done === true ? "done" : ""}
toggle={toggle}
del={del}
>{item.todo}</Item>; // 칠드런은 태그와 태그 사이 값
});
return (
<div>
{items}
</div>
);
}
List.jsx 에서는 배열을 순회하면서 Item 컴포넌트를 하나하나 렌더링 한다.
