간단한 Todo List, 처음 TS를 접하는 사람들에게는 간단하지 않다...!
(React, Typescript, JsonSever를 이용했습니다.)
1. TodoForm - Todo의 할 일을 추가하는 헤더부분
1) 헤더 입력창 텍스트
: useState를 통해 입력창이 비어있는 지 확인할 수 있습니다. 비어 있지 않다면 handleSubmit 함수를 통해 onAdd를 호출합니다.
preventDefalut()는 꽤 자주 사용하는 함수로, 바로 제출되는 이벤트를 막습니다. 모달창에서 모달안의 영역을 클릭했을 경우에도
모달이 닫히는 걸 방지할 때 유용히 사용됩니다.
//TodoFormProps 인터페이스정의 : 입력받을 값은 문자열임을 알려줌
export interface TodoFormProps {
onAdd: (text: string) => void;
}
//TodoForm은 리액트 함수 컴포넌트,TodoFormProps 타입의 onAdd 를 인자로 받아서 사용할 것
// text의 디폴트는 빈문자이고, handleSubmit을 이용해 이벤트 객체를 받아
//preventDefalut()로 바로제출되는 이벤트를 막고
//입력된 text가 비어있지 않다면 onAdd 호출하고 text를 초기화시킨다
const TodoForm: React.FC<TodoFormProps> = ({ onAdd }) => {
const [text, setText] = useState("");
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!text) {
return;
}
onAdd(text);
setText("");
};
2) 렌더링 될 부분 (실제 화면에 보이는 부분)
// input과button(헤더부분) 요소를 렌더링 onChange 이벤트 핸들러를사용하여 입력값이 변할 때 text 상태 업데이트
return (
<form onSubmit={handleSubmit}>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button className="button">Add</button>
</form>
);
};
// 다른곳에서 사용 가능하도록 내보내기
export default TodoForm;
2. TodoList - 항목 리스트 만들기
1) 항목 인터페이스
: 타입스크립트에서 인터페이스는 정말 자주 사용합니다.
타입을 지정해주기 위해 사용하는 데, 확장성이 좋고 즉 재사용성이 높아 많이 사용하는 방법입니다.
한 번 지정해주면 어디서든 갖다쓸 수 있어 사용하기 편리합니다.
// Todo 항목 인터페이스 (id,text, done의 속성을 갖음)
export interface Todo {
id: number;
text: string;
done: boolean;
}
//Props 인터페이스, 함수 속성을 포함
export interface TodoListProps {
todos: Todo[];
onDeleteTodo: (id: number) => void; // 항목 삭제
onToggleDone: (id: number) => void; // 항목의 상태 토글
}
2) 첫 항목 세팅
: useEffect를 사용하여, 첫 렌더링 됐을 경우 이미 저장된 항목이 있다면 항목을 불러와서 세팅할 수 있도록 합니다.
타입을 지정해 주어야 하기 때문에 제네릭을 사용하여 Todo 객체의 배열이라는 것을 명시했습니다.
제네릭이란 useState<Todo[]>로 표시한 부분처럼 <> 안에 해당 타입을 명시하는 형태 입니다. 이를 사용하는 이유는
정확한 타입을 명시하므로서 타입의 안정성을 높이고, 한 번 지정해 준 타입이므로 후에 useState<T> 로 해당 타입을
재사용할 수 있기 때문입니다.
→ useEffect로 서버에서 정보를 받아와 이를 setTodo로 상태 변경 해주어 저장된 todo를 확인합니다.
const TodoList: React.FunctionComponent<TodoListProps> = ({
onToggleDone,
onDeleteTodo,
}) => {
const [todos, setTodos] = useState<Todo[]>([]); // 배열 형태의 Todo 객체를 가진 배열을 사용
// useEffect를 사용하여 컴포넌트가 처음 렌더링 될 때, 서버에서 todo를 받아와 상태로 설정
useEffect(() => {
fetch("http://localhost:4000/todos")
.then((response) => response.json())
.then((data) => setTodos(data));
}, []);
3) 항목 추가
//TodoForm 컴포넌트에서 입력된 값을 서버에 POST 요청으로 보내고, 서버에서 응답받은 데이터를 상태에 추가
const handleAddTodo = (text: string) => {
const newTodo: Todo = {
id: todos.length + 1,//id 는 고유한 값이어야하므로 길이에 +1 한 값을 주어 각각이 고유한 값을 갖도록 함
text,
done: false,
};
fetch("http://localhost:4000/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newTodo), // 새로운 항목을 JSON형식으로 변환하여 바디로 보냄
})
.then((response) => response.json()) //응답을 받았다면, JSON 데이터를 파싱
.then((data) => setTodos([...todos, data])); //todo 항목 배열에 추가
};
4) 항목의 완료, 미완료 상태
// 항목의 완료,미완료 상태를 보여주는 함수, 인자로 id가 들어가고 이는 number타입임
const handleTodoToggle = (id: number) => {
const updatedTodos = todos.map((todo) => {
if (todo.id === id) { //todo 배열에서 해당 id의 todo를 찾기
return { ...todo, done: !todo.done };
}
return todo; //done의 반대로 설정한 후
});
setTodos(updatedTodos); // 업데이트 된 todo배열을 setTodo 상태로 저장
fetch(`http://localhost:4000/todos/${id}`, { //업데이트 할 항목의 url
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ done: !todos.find((todo) => todo.id === id)?.done }), // 업데이트 할 값을 JSON 형식으로 보내기
// 이전 상태에 따라서 값을 바꾸기 위해서 현재 todo의 done값이 아닌,이전에 저장된 todo의 done값을 반대로 설정하는 것
});
};
5) 항목삭제
// 항목을 삭제하기 위한 함수 id: number 타입을 인자로 받음
const handleDeleteTodo = (id: number) => {
//filter를 통해 id에 해당하는 항복을 제거시킴
const filteredTodos = todos.filter((todo) => todo.id !== id);
setTodos(filteredTodos); //항목 업데이트
//해당 url을 fetch하여 삭제시킴
fetch(`http://localhost:4000/todos/${id}`, {
method: "DELETE",
});
};
export default TodoList;
3. App.tsx - 합쳐서 렌더링!
import React, { useState } from 'react';
import TodoList, { Todo } from './components/TodoList';
//Todo 상태관리
export const App: React.FC = () => {
return (
<div>
<TodoList todos={todos} onToggleDone={handleTodoToggle} onDeleteTodo={handleDeleteTodo} />
</div>
);
};
4. Json Server (가짜 API 이용하기!)
1)JSON Server 설치
npm install -g json-server
2) 서버에서 사용할 파일 생성
db.json
객체 형태로 작성해 주어야 한다
{
"todos": [
{
"id": 1,
"text": "아침먹기",
"done": false
},
{
"id": 2,
"text": "점심먹기",
"done": true
},
{
"id": 3,
"text": "저녁먹기",
"done": false
}
]
}
3)JSON Server 시작
json-server --watch db.json --port 4000
5. CSS
#root {
height: 100vh;
}
.construct{
display:flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 80vh;
position: relative;
}
.header{
position: fixed; /*위치 상단 고정*/
top : 0px;
}
.list{
position: fixed;
height: 50vh;
overflow-y: scroll; /*영역 벗어나면 스크롤*/
margin-top: 20vh;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 4px 9px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 13px;
margin: 4px 2px;
cursor: pointer; /* 커서 스타일 변경 */
border-radius: 4px;
}
button:hover {
opacity: 0.8; /* 마우스 오버 효과 */
}
'프로젝트 회고 > 2) project' 카테고리의 다른 글
position:fixed 고정 후, 스크롤 (0) | 2023.07.28 |
---|---|
프론트의 서버 인증 (쿠키, 토큰, 세션) 2 (0) | 2023.07.20 |
프론트의 서버 인증 (쿠키, 토큰, 세션) 1 (0) | 2023.07.20 |
Redux-toolkit (Todo-List에서 사용하기) (0) | 2023.06.19 |