일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 대덕인재개발원
- 메소드오버로딩
- 추상메서드
- 한국건설관리시스템
- 정수형타입
- 환경설정
- 참조형변수
- exception
- Java
- 제네릭
- 자동차수리시스템
- NestedFor
- 어윈 사용법
- 예외처리
- 컬렉션프레임워크
- GRANT VIEW
- EnhancedFor
- 생성자오버로드
- 다형성
- 컬렉션 타입
- oracle
- 오라클
- cursor문
- abstract
- 자바
- 집합_SET
- 사용자예외클래스생성
- 예외미루기
- 인터페이스
- 객체 비교
- Today
- Total
거니의 velog
(15) 리액트와 API 서버 통신 5 본문
7. 수정/삭제 처리
* 수정/삭제 화면은 기존의 ModifyPage와 새롭게 작성할 ModifyComponent를 이용해서 처리한다. 코드를 구현하기 전에 수정/삭제 후에는 어떤 식으로 동작해야 하는지를 정리해 본다.
- 삭제(Delete 버튼) : 삭제 결과를 모달창으로 보여주고 '/todo/list'로 이동
- 수정(Modify 버튼) : 수정 결과를 모달창으로 보여주고 '/todo/read/번호'로 이동
* 수정/삭제 기능 모두 서버의 호출 결과를 이용해서 모달창을 보여주는 점은 공통적이고 이후의 이동 경로에서 차이가 난다.
(1) 수정/삭제 호출 기능 작성
* api/todoApi.js에는 수정과 삭제에 필요한 함수들을 추가한다.
import axios from "axios";
export const API_SERVER_HOST = "http://localhost:8080";
const prefix = `${API_SERVER_HOST}/api/todo`;
export const getOne = async (tno) => {
const res = await axios.get(`${prefix}/${tno}`);
return res.data;
};
export const getList = async (pageParam) => {
const { page, size } = pageParam;
const res = await axios.get(`${prefix}/list`, {
params: { page: page, size: size },
});
return res.data;
};
export const postAdd = async (todoObj) => {
const res = await axios.post(`${prefix}/`, todoObj);
return res.data;
};
export const deleteOne = async (tno) => {
const res = await axios.delete(`${prefix}/${tno}`);
return res.data;
};
export const putOne = async (todo) => {
const res = await axios.put(`${prefix}/${todo.tno}`, todo);
return res.data;
};
(2) 수정/삭제를 위한 컴포넌트
* 수정/삭제의 실제 처리는 components/todo/ModifyComponent를 추가해서 처리한다. 라우팅을 설계하는 단계에서는 수정/삭제 버튼을 페이지 역할을 하는 ModifyPage에서 작성했지만, 더 일관성 있는 개발을 위해서 버튼들을 ModifyComponent에서 처리하도록 변경할 것이다.
* components/todo 폴더에 ModifyComponent를 추가하고 버튼들과 필요한 상태(state) 데이터들을 준비한다.
import React, { useEffect, useState } from "react";
const initState = {
tno: 0,
title: "",
writer: "",
dueDate: "",
complete: false,
};
const ModifyComponent = ({ tno }) => {
const [todo, setTodo] = useState({ ...initState });
useEffect(() => {}, [tno]);
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4">
<div className="flex justify-end p-4">
<button
type="button"
className="rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
>
Delete
</button>
<button
type="button"
className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
>
Modify
</button>
</div>
</div>
);
};
export default ModifyComponent;
* ModifyPage 에서는 ModifyComponent를 import 해서 필요한 속성들을 지정한다.
import React from "react";
import { useParams } from "react-router-dom";
import ModifyComponent from "../../components/todo/ModifyComponent";
const ModifyPage = () => {
const { tno } = useParams();
return (
<div className="p-4 w-full bg-white">
<div className="text-3xl font-extrabold">Todo Modify Page</div>
<ModifyComponent tno={tno} />
</div>
);
};
export default ModifyPage;
* 실제 수정을 위한 내용은 구현되지 않은 상황이므로 화면에서는 ModifyComponent가 가진 테두리와 버튼들만이 보인다. 브라우저에서 특정 Todo를 조회한 후에 'Modify' 버튼을 클릭해서 수정/삭제 화면으로 이동하는 것을 확인한다.
(3) 서버 데이터 출력
* ModifyComponent는 우선 서버에서 내용물을 가져와서 출력하고 변경 가능하도록 구성할 필요가 있다. 이를 위해서 조회 기능과 같이 useEffect()를 사용해서 서버로부터 데이터를 가져오고, 등록 기능에 사용했던 <input>을 이용한다.
import React, { useEffect, useState } from "react";
import { getOne } from "../../api/todoApi";
const initState = {
tno: 0,
title: "",
writer: "",
dueDate: "",
complete: false,
};
const ModifyComponent = ({ tno }) => {
const [todo, setTodo] = useState({ ...initState });
useEffect(() => {
getOne(tno).then((data) => setTodo(data));
}, [tno]);
const handleChangeTodo = (e) => {
todo[e.target.name] = e.target.value;
setTodo({ ...todo });
};
const handleChangeTodoComplete = (e) => {
const value = e.target.value;
todo.complete = value === "Y";
setTodo({ ...todo });
};
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4">
<div className="flex justify-center mt-10">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">TNO</div>
<div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
{todo.tno}
</div>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">WRITER</div>
<div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
{todo.writer}
</div>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">TITLE</div>
<input
className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
name="title"
type={"text"}
value={todo.title}
onChange={handleChangeTodo}
></input>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">DUEDATE</div>
<input
className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
name="dueDate"
type={"date"}
value={todo.dueDate}
onChange={handleChangeTodo}
></input>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">COMPLETE</div>
<select
name="status"
className="border-solid border-2 rounded m-1 p-2"
onChange={handleChangeTodoComplete}
value={todo.complete ? "Y" : "N"}
>
<option value="Y">Completed</option>
<option value="N">Not Yet</option>
</select>
</div>
</div>
<div className="flex justify-end p-4">
<button
type="button"
className="rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
>
Delete
</button>
<button
type="button"
className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
>
Modify
</button>
</div>
</div>
);
};
export default ModifyComponent;
* 화면에서 수정이 가능한 부분은 <input>을 이용해서 처리하고 수정이 불가능한 부분은 <div>를 이용해서 출력한다.
(4) 수정/삭제와 모달창
* ModifyComponent의 기능들은 호출 후에 결과를 보여주기 때문에 모달창을 추가해야 한다. 삭제 후 모달창을 보여주고 난 후에 모달창이 닫히면서 삭제하도록 구성해야 하므로 화면에 ResultModal을 이용해서 이를 처리한다.
* 화면 이동 전에 수정이나 삭제 호출에 문제가 없는지 확인하기 위해서 handleClickModify()나 handleClickDelete()와 같은 함수를 지정하고 버튼에 이벤트 처리를 추가한다.
import React, { useEffect, useState } from "react";
import { getOne, putOne, deleteOne } from "../../api/todoApi";
const initState = {
tno: 0,
title: "",
writer: "",
dueDate: "",
complete: false,
};
const ModifyComponent = ({ tno }) => {
const [todo, setTodo] = useState({ ...initState });
// 모달창을 위한 상태
const [result, setResult] = useState(null);
useEffect(() => {
getOne(tno).then((data) => setTodo(data));
}, [tno]);
const handleClickModify = () => {
//버튼 클릭시
putOne(todo).then((data) => {
console.log("modify result: " + data);
setResult("Modified");
});
};
const handleClickDelete = () => {
//버튼 클릭시
deleteOne(tno).then((data) => {
console.log("delete result: " + data);
setResult("Deleted");
});
};
(...)
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4">
(...)
<div className="flex justify-end p-4">
<button
type="button"
className="inline-block rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
onClick={handleClickDelete}
>
Delete
</button>
<button
type="button"
className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
onClick={handleClickModify}
>
Modify
</button>
</div>
</div>
);
};
export default ModifyComponent;
* 브라우저상에서 수정/삭제 기능이 정상적으로 동작하는지 확인한다. 아직 화면 이동이 없기 때문에 개발자 도구의 Console 탭과 Network 탭을 이용해서 결과를 확인한다.
1. 삭제 테스트
2. 수정 테스트
* 정상적으로 서버의 호출이 이루어지는 것을 확인했다면 useCustomMove를 이용해서 화면 이동에 필요한 기능들을 가져오고 ResultModal이 close 될 때 호출하도록 변경한다.
import React, { useEffect, useState } from "react";
import { getOne, putOne, deleteOne } from "../../api/todoApi";
import useCustomMove from "../../hooks/useCustomMove";
import ResultModal from "../common/ResultModal";
const initState = {
tno: 0,
title: "",
writer: "",
dueDate: "",
complete: false,
};
const ModifyComponent = ({ tno }) => {
const [todo, setTodo] = useState({ ...initState });
//모달 창을 위한 상태
const [result, setResult] = useState(null);
//이동을 위한 기능들
const { moveToList, moveToRead } = useCustomMove();
const handleClickModify = () => {
putOne(todo).then((data) => {
console.log("modify result: " + data);
setResult("Modified");
});
};
const handleClickDelete = () => {
deleteOne(tno).then((data) => {
console.log("delete result: " + data);
setResult("Deleted");
});
};
//모달 창이 close될 때
const closeModal = () => {
if (result === "Deleted") {
moveToList();
} else {
moveToRead(tno);
}
};
useEffect(() => {
getOne(tno).then((data) => setTodo(data));
}, [tno]);
const handleChangeTodo = (e) => {
todo[e.target.name] = e.target.value;
setTodo({ ...todo });
};
const handleChangeTodoComplete = (e) => {
const value = e.target.value;
todo.complete = value === "Y";
setTodo({ ...todo });
};
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4">
{result ? (
<ResultModal
title={"처리결과"}
content={result}
callbackFn={closeModal}
></ResultModal>
) : (
<></>
)}
<div className="flex justify-center mt-10">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">TNO</div>
<div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
{todo.tno}
</div>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">WRITER</div>
<div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
{todo.writer}
</div>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">TITLE</div>
<input
className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
name="title"
type={"text"}
value={todo.title}
onChange={handleChangeTodo}
></input>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">DUEDATE</div>
<input
className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
name="dueDate"
type={"date"}
value={todo.dueDate}
onChange={handleChangeTodo}
></input>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-6 text-right font-bold">COMPLETE</div>
<select
name="status"
className="border-solid border-2 rounded m-1 p-2"
onChange={handleChangeTodoComplete}
value={todo.complete ? "Y" : "N"}
>
<option value="Y">Completed</option>
<option value="N">Not Yet</option>
</select>
</div>
</div>
<div className="flex justify-end p-4">
<button
type="button"
className="inline-block rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
onClick={handleClickDelete}
>
Delete
</button>
<button
type="button"
className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
onClick={handleClickModify}
>
Modify
</button>
</div>
</div>
);
};
export default ModifyComponent;
* 브라우저를 이용해서 수정 작업을 테스트해 보면 모달창이 보인 후에 조회 화면으로 이동하는 것을 확인할 수 있다. 이동시에 기존의 쿼리스트링 역시 동일하게 유지된다.
* 삭제의 경우 모달창이 닫힌 후에 목록 화면으로 이동하게 된다. 이 경우에도 쿼리스트링이 유지된다.
'SpringBoot_React 풀스택 프로젝트' 카테고리의 다른 글
(17) 상품 API 서버 구성하기 2 (0) | 2024.02.29 |
---|---|
(16) 상품 API 서버 구성하기 1 (0) | 2024.02.29 |
(14) 리액트와 API 서버 통신 4 (0) | 2024.02.28 |
(13) 리액트와 API 서버 통신 3 (0) | 2024.02.28 |
(12) 리액트와 API 서버 통신 2 (0) | 2024.02.28 |