Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Description Todo 페이지의 기능을 구현하였습니다. @5unk3n 님과 함께 LiveShare로 페어 프로그래밍을 하였습니다! ## Changes - TODO CRUD에 필요한 api를 작성하였습니다. - [디렉토리 구조 예시](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/pull/3)에 맞추어 [src/apis/](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/tree/feat/%237-todo/src/apis) 경로에 [todo.ts](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/blob/feat/%237-todo/src/apis/todo.ts)를 작성하였습니다. - [src/apis/instance.ts](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/blob/main/src/apis/instance.ts) 의 AuthInstance를 사용하여 구현하였습니다. - [/todo](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/blob/feat/%237-todo/src/pages/Todo.tsx) 페이지에서 Todo를 추가, 조회, 수정, 삭제를 할 수 있도록 구현하였습니다. - [TodoList](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/blob/feat/%237-todo/src/components/TodoList/TodoList.tsx)와 [TodoItem](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/blob/feat/%237-todo/src/components/TodoItem/TodoItem.tsx) 컴포넌트를 작성하였습니다. - TODO 조회 ![조회](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/assets/93248349/d9b5cde7-cbb6-4b4b-809f-b1467e228efb) ```javascript // src/components/TodoList/types.ts export interface TodoType { id: number todo: string isCompleted: boolean userId: number } ``` Todo의 type을 위와 같이 정의하였고, ```javascript // src/apis/todo.ts export const getTodosRequest = async (): Promise<TodoType[]> => { const { data } = await AuthInstance.get('/todos') return data } ``` ```javascript // src/components/TodoList/TodoList.tsx function TodoList() { const [todos, setTodos] = useState<TodoType[]>([]) ... const getTodo = () => { getTodosRequest() .then((todos) => setTodos(todos)) .catch((e: AxiosError) => alert(e.message)) } useEffect(() => { getTodo() }, []) ... } ``` ```get``` 요청 성공시, 서버로부터 받아온 값을 todos에 ```useState<TodoType[]>([])```로 저장하였습니다. ```javascript // src/components/TodoList/TodoList.tsx <ul> {todos.map((todo) => ( <TodoItem key={todo.id} id={todo.id} todo={todo.todo} isCompleted={todo.isCompleted} updateTodo={updateTodo} deleteTodo={deleteTodo} /> ))} </ul> ``` 또한, ```map()```을 사용하여 Todo 리스트의 내용을 렌더링 하였습니다. - TODO 추가 ![추가](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/assets/93248349/c19143e8-589f-472a-9cef-4219e4a8a957) ```javascript // src/apis/todo.ts export const createTodoRequest = async (todo: string): Promise<TodoType> => { const { data } = await AuthInstance.post('/todos', { todo }) return data } ``` ```javascript // src/components/TodoList/TodoList.tsx function TodoList() { const [todos, setTodos] = useState<TodoType[]>([]) const [newTodo, setNewTodo] = useState('') const createTodo = (todo: string) => { createTodoRequest(todo) .then((createdTodo) => { setTodos((prevTodos) => [...prevTodos, createdTodo]) setNewTodo('') }) .catch((e: AxiosError) => alert(e)) } ... } ``` ```javascript // src/components/TodoList/TodoList.tsx <div> <label htmlFor="addTodo"> <input id="addTodo" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} data-testid="new-todo-input" /> </label> <button onClick={() => createTodo(newTodo)} type="button" data-testid="new-todo-add-button"> 추가 </button> </div> ``` input의 ```onChange``` value를 newTodo에 ```useState('')```로 저장하였고, 추가 버튼을 누르면 서버로 ```post``` 요청을 보냅니다. ```post``` 요청 성공시, 해당 값을 [Spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)를 사용하여 todos에 추가 하도록 하였습니다. - TODO 수정 ![수정](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/assets/93248349/f7f15044-8171-4a1d-934b-65e17b23c690) ```ts // src/components/TodoItem/TodoItem.tsx function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) { ... const [todoModify, setTodoModify] = useState('') const [isEditMode, setIsEditMode] = useState(false) ... // 수정 버튼을 눌렀을 때 const onEditButtonClicked = () => { setTodoModify(todo) setIsEditMode(true) } ... return ( <li> <label> <input type="checkbox" checked={todoCheck} onChange={onTodoCheckChanged} /> {isEditMode ? ( <input data-testid="modify-input" type="text" onChange={onTodoModifyChanged} value={todoModify} /> ) : ( <span>{todo}</span> )} </label> {isEditMode ? ( <> <button data-testid="submit-button" onClick={onSubmitButtonClicked}> 제출 </button> <button data-testid="cancel-button" onClick={onCancelButtonClicked}> 취소 </button> </> ) : ( <> <button data-testid="modify-button" onClick={onEditButtonClicked}> 수정 </button> <button data-testid="delete-button" onClick={onDeleteButtonClicked}> 삭제 </button> </> )} </li> ) } ``` `isEditMode` 상태에 따라 렌더링 요소를 결정하고, 수정 버튼을 통해 토글합니다. ```ts // src/components/TodoItem/TodoItem.tsx // 제출 버튼을 눌렀을 때 const onSubmitButtonClicked = () => { updateTodo(id, todoModify, isCompleted) setIsEditMode(false) } ``` 제출 버튼 클릭 시 TodoList 컴포넌트에서 prop으로 받은 `updateTodo`함수를 실행합니다. ```ts // src/components/TodoList/TodoList.tsx const updateTodo = (id: number, todo: string, isCompleted: boolean) => { updateTodoRequest(id, todo, isCompleted) .then((updatedTodo) => setTodos((prevTodos) => prevTodos.map((prevTodo) => (prevTodo.id === id ? updatedTodo : prevTodo)) ) ) .catch((e: AxiosError) => alert(e.message)) } ``` ```ts // src/apis/todo.ts export const updateTodoRequest = async ( id: number, todo: string, isCompleted: boolean ): Promise<TodoType> => { const { data } = await AuthInstance.put(`/todos/${id}`, { todo, isCompleted }) return data } ``` `put` 요청 성공시 todos에서 같은 id를 가진 todo를 변경하도록 했습니다. - TODO 삭제 ![삭제](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/assets/93248349/fb10f879-91c6-49d9-8ab0-83ba0b76f049) ```ts // src/components/TodoItem/TodoItem.tsx function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) { ... // 삭제 버튼을 눌렀을 때 const onDeleteButtonClicked = () => { deleteTodo(id) } ... <button data-testid="delete-button" onClick={onDeleteButtonClicked}> 삭제 </button> ... } ``` 삭제 버튼 클릭 시 TodoList에서 prop으로 받은 `deleteTodo` 함수를 실행합니다. ```ts // src/components/TodoList/TodoList.tsx const deleteTodo = (id: number) => { deleteTodoRequest(id) .then(() => setTodos((prevTodos) => prevTodos.filter((prevTodo) => prevTodo.id !== id))) .catch((e: AxiosError) => alert(e.message)) } ``` ```ts // src/apis/todo.ts export const deleteTodoRequest = async (id: number) => { return AuthInstance.delete(`/todos/${id}`) } ``` `delete` 요청 성공 시 todos에서 `.filter()` 메서드를 사용해 같은 id를 가진 todo를 삭제하도록 했습니다. - TODO 체크 ![체크](https://github.com/wanted-pre-onboarding-team-12th-7/pre-onboarding-12th-1-7/assets/93248349/6a73374c-df0b-4713-9bc1-8544ce5be00f) ```ts // src/components/TodoList/TodoList.tsx function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) { const [todoCheck, setTodoCheck] = useState(isCompleted) ... // TODO의 체크박스가 변경 되었을 때 const onTodoCheckChanged = () => { updateTodo(id, todo, !todoCheck) setTodoCheck(!todoCheck) } ... <label> <input type="checkbox" checked={todoCheck} onChange={onTodoCheckChanged} /> {isEditMode ? ( <input data-testid="modify-input" type="text" onChange={onTodoModifyChanged} value={todoModify} /> ) : ( <span>{todo}</span> )} </label> } ``` todo의 완료 여부는 `todoCheck` 상태로 결정됩니다. TODO 수정 기능과 같이 `updateTodo`함수를 사용하며 `todoCheck`를 not 연산자로 변경해 요청합니다.
- Loading branch information