REST API 통신

박정호·2023년 1월 14일
0

API

목록 보기
2/6
post-thumbnail

🚀 Start

먼저 REST API를 통한 데이터 통신에 대해 간단한 CRUD 기능을 동작하는 예시를 통해 알아보자.



📡 Rest API

REST의 가장 큰 특징이라면 여러 엔드포인트를 가지며, 각각의 엔드포인트가 동일한 응답을 반환한다는 것이다. 이처럼 Graphql과 달리 고정된 요청과 응답을 해야한다는 점이 있고, 그만큼 사용되는 메서드도 4가지 밖에 없다.

CRUD Operation을 적용하는 4가지 메서드

  • Create : 생성(POST)
  • Read : 조회(GET)
  • Update : 수정(PUT)
  • Delete : 삭제(DELETE)


⚙️ fetcher

get, delete, post, put에 대한 모든 통신들중 중복되는 부분을 재사용할 수 있게 method, url, config을 인수로 받아오는 fetcher 함수를 작성해보자.

  • get: axios.get(url,[ config])
  • delete: axios.delete(url,[ config])
  • post: axios.post(url, data, [ config])
  • put: 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 연산자를 통해 유연하게 받기 위해서.



📄 GET

Client

  • useEffect로 렌더링되는 순간 getMessages가 실행되고 응답값으로 data를 받아오면 끝.

const MsgList = () => {
  const [msgs, setMsgs] = useState([]);
   ...
  const getMessages = async () => {
    const msgs = await fetcher("get", "/messages");
    setMsgs(msgs);
  };

  useEffect(() => {
    getMessages();
  }, []);

  return <> {msgs.map((x) => <MsgItem ... /> } </>
  );
};

Server


const getMsgs = () => readDB("messages");

const messagesRoute = [
  {
    // GET MESSAGES
    method: "get",
    route: "/messages",
    handler: (res) => {
      const msgs = getMsgs();
      res.send(msgs);
    },
  }
  
];


✏️ POST

  • MsgInput이라는 글 작성 컴포넌트에서 onCreate함수를 실행시키면 post요청과 함께 body에는 작성 텍스트와 유저정보가 전달된다.

  • 응답받은 값을 기존 데이터가 존재하는 값에 추가.

Client


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}/>
  );
};

Server

  • 요청받은 값을 가지고 newMsgs라는 새로운 객체데이터를 생성한뒤 기존의 데이터에 unshift. 그리고 값을 다시 반환시켜준다.
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 });
      }
    },
  },
  
];


📝 UPDATE

Client

  • 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}		
		 />
  );
};

Server

  • id값은 요청받은 url의 params에 해당하는 값을 받으며, 해당 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 });
      }
    },
  },
  
];


🗑 DELETE

Client

  • Delete 요청의 경우 삭제할 데이터의 id값을 전달해줘야 한다. 그리고 응답받은 값과 일치하는 데이터를 찾아서 삭제시켜준다.

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)}/>
  );
};


Server

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 });
      }
    },
  },
  
];
profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글