먼저 REST API를 통한 데이터 통신에 대해 간단한 CRUD 기능을 동작하는 예시를 통해 알아보자.
REST의 가장 큰 특징이라면 여러 엔드포인트를 가지며, 각각의 엔드포인트가 동일한 응답을 반환한다는 것이다. 이처럼 Graphql과 달리 고정된 요청과 응답을 해야한다는 점이 있고, 그만큼 사용되는 메서드도 4가지 밖에 없다.
✅ CRUD Operation을 적용하는 4가지 메서드
get, delete, post, put에 대한 모든 통신들중 중복되는 부분을 재사용할 수 있게 method, url, config을 인수로 받아오는 fetcher 함수를 작성해보자.
axios.get(url,[ config])
axios.delete(url,[ config])
axios.post(url, data, [ config])
axios.put(url, data, [ config])
// fetcher.js
axios.defaults.baseURL = "http://localhost:8000";
export const fetcher = async (method, url, ...rest) => {
const res = await axios[method](url, ...rest);
return res.data;
};
✚ ...rest
는 data, config 둘다 또는 하나만 들어오는 것에 대응하여 spread 연산자를 통해 유연하게 받기 위해서.
const MsgList = () => {
const [msgs, setMsgs] = useState([]);
...
const getMessages = async () => {
const msgs = await fetcher("get", "/messages");
setMsgs(msgs);
};
useEffect(() => {
getMessages();
}, []);
return <> {msgs.map((x) => <MsgItem ... /> } </>
);
};
const getMsgs = () => readDB("messages");
const messagesRoute = [
{
// GET MESSAGES
method: "get",
route: "/messages",
handler: (res) => {
const msgs = getMsgs();
res.send(msgs);
},
}
];
MsgInput이라는 글 작성 컴포넌트에서 onCreate함수를 실행시키면 post요청과 함께 body에는 작성 텍스트와 유저정보가 전달된다.
응답받은 값을 기존 데이터가 존재하는 값에 추가.
const MsgList = () => {
const [msgs, setMsgs] = useState([]);
...
const onCreate = async (text) => {
const newMsg = await fetcher("post", "/messages", { text, userId });
if (!newMsg) throw Error("error");
setMsgs((msgs) => [newMsg, ...msgs]);
};
return <MsgInput mutate={onCreate}/>
);
};
const getMsgs = () => readDB("messages");
const setMsgs = (data) => writeDB("messages", data);
const messagesRoute = [
{
// CREATE MESSAGE
method: "post",
route: "/messages",
handler: ({ body }, res) => {
try {
if (!body.userId) throw Error("no userId");
const msgs = getMsgs();
const newMsg = {
id: v4(),
text: body.text,
userId: body.userId,
timestamp: Date.now(),
};
msgs.unshift(newMsg);
setMsgs(msgs);
res.send(newMsg);
} catch (err) {
res.status(500).send({ error: err });
}
},
},
];
POST 요청은 수정한 텍스트와 유저정보 뿐만 아니라 고치려하는 데이터의 id값을 넘겨줘야 한다. 그래야 서버에서 해당 id값을 가지고 수정할 데이터를 탐색할 수 있다.
그리고 기존에 존해하는 데이터와 새롭게 작성된 데이터를 변경시켜준다.
const MsgList = () => {
const { query } = useRouter();
const userId = query.userId || query.userid || "";
const [msgs, setMsgs] = useState([]);
const [editingId, setEditingId] = useState(null);
...
const onUpdate = async (text, id) => {
const newMsg = await fetcher("put", `/messages/${id}`, { text, userId });
if (!newMsg) throw Error("error");
setMsgs((msgs) => {
const targetIndex = msgs.findIndex((msg) => msg.id === id);
if (targetIndex < 0) return msgs;
const newMsgs = [...msgs];
newMsgs.splice(targetIndex, 1, newMsg);
return newMsgs;
});
doneEdit();
};
const doneEdit = () => setEditingId(null);
return <MsgItem
startEdit={()=>setEditingId(x.id)}
isEditing={editingId === x.id}
/>
);
};
const setMsgs = (data) => writeDB("messages", data);
const messagesRoute = [
{
// UPDATE MESSAGE
method: "put",
route: "/messages/:id",
handler: ({ body, params: { id } }, res) => {
try {
const msgs = getMsgs();
const targetIndex = msgs.findIndex((msg) => msg.id === id);
if (targetIndex < 0) throw "메시지가 없습니다.";
if (msgs[targetIndex].userId !== body.userId)
throw "사용자가 다릅니다.";
const newMsg = { ...msgs[targetIndex], text: body.text };
msgs.splice(targetIndex, 1, newMsg);
setMsgs(msgs);
res.send(newMsg);
} catch (err) {
res.status(500).send({ error: err });
}
},
},
];
const MsgList = () => {
const { query } = useRouter();
const userId = query.userId || query.userid || "";
const [msgs, setMsgs] = useState([]);
const [editingId, setEditingId] = useState(null);
...
const onDelete = async (id) => {
const receivedId = await fetcher("delete", `/messages/${id}`, {
params: { userId },
});
setMsgs((msgs) => {
const targetIndex = msgs.findIndex((msg) => msg.id === receivedId + "");
if (targetIndex < 0) return msgs;
const newMsgs = [...msgs];
newMsgs.splice(targetIndex, 1);
return newMsgs;
});
};
return <MsgItem onDelete={()=> onDelete(x.id)}/>
);
};
params로 전달받은 id값과 일치하는 데이터를 찾은 후 해당 데이터를 삭제시켜준다.
const setMsgs = (data) => writeDB("messages", data);
const messagesRoute = [
{
// DELETE MESSAGE
method: "delete",
route: "/messages/:id",
handler: ({ params: { id }, query: { userId } }, res) => {
try {
const msgs = getMsgs();
const targetIndex = msgs.findIndex((msg) => msg.id === id);
if (targetIndex < 0) throw "메시지가 없습니다.";
if (msgs[targetIndex].userId !== userId) throw "사용자가 다릅니다.";
msgs.splice(targetIndex, 1);
setMsgs(msgs);
res.send(id);
} catch (err) {
res.status(500).send({ error: err });
}
},
},
];