하루살이 개발일지

Axios 비동기 통신 - get, post, delete, patch 본문

웹개발/React

Axios 비동기 통신 - get, post, delete, patch

harusari 2023. 7. 4. 10:54

Axios

  • 공식문서 :
    • axios 란 node.js와 브라우저를 위한 Promise 기반 http 클라이언트
  • 다시 말해 http를 이용해서 서버와 통신하기 위해 사용하는 패키지
yarn add axios

json-server 설정

  • 테스트용 db.json 설정
yarn json-server --watch db.json --port 3001
{
  "todos": [
    {
      "id": "1",
      "title": "react"
    }
  ]
}

GET

axios.get

  • get은 서버의 데이터를 조회할 때 사용
// url에는 서버의 url이 들어가고, config에는 기타 여러가지 설정을 추가할 수 있습니다.
// config는 axios 공식문서에서 확인하세요.

axios.get(url[, config]) // GET

공식 문서 : https://axios-http.com/kr/docs/req_config

 

요청 Config | Axios Docs

요청 Config 다음은 요청을 만드는 데 사용할 수 있는 config 옵션들 입니다. 오직 url만 필수입니다. method를 지정하지 않으면 GET방식이 기본값 입니다. { url: '/user', method: 'get', baseURL: 'https://some-domain.

axios-http.com


json-server API 명세서 확인

  • Axios를 사용해서 GET 요청 코드를 작성하기에 앞서, 어떤 방식으로 요청 해야할지는 내가 사용하는 json-server의 방식을 알아보아야 함
  • 즉, Axios는 GET 요청을 할 수 있도록 도와주는 패키지일뿐이지, “어떻게 요청을 해야하지?” 와 같은 방식에 대한 확인은 우리가 사용할 API 명세서를 보아야 한다는 뜻
    • 예를 들어 GET 요청을 할 때 path variable로 해야할지, query로 보내야할지는 API를 만든 사람이 하라는대로 해야 하기 때문

 

 

json-server

Get a full fake REST API with zero coding in less than 30 seconds. Latest version: 0.17.3, last published: 3 months ago. Start using json-server in your project by running `npm i json-server`. There are 315 other projects in the npm registry using json-ser

www.npmjs.com

 


axios로 데이터 받아오기

// App.js

import axios from "axios";
import { useEffect } from "react";

function App() {
  const fetchTodos = async () => {
    const response = axios.get("<http://localhost:3001/todos>");
    console.log("response", response);
  };

  useEffect(() => {
    // db로부터 값 가져오기
    fetchTodos();
  }, []);

  return <div>s</div>;
}

export default App;
  • 만약 다음과 같이 작성하고 response를 콘솔에 찍어보면 다음과 같은 결과가 나옴

 

  • promise의 pending
    • axios 요청을 한 직후에 이것이 찍혔기 때문
    • 응답을 받기 전에 찍혔음
    • 즉, 응답을 받을 때까지 기다려 줘야 함
  • 해결책 = await 사용
  • async 블록 안에서 await를 만나면 그 줄이 끝날 때까지 기다려 줌

 

const fetchTodos = async () => {
    const response = await axios.get("<http://localhost:3001/todos>");
    console.log("response", response);
  };

  • 정상적으로 값을 받을 수 있음
  • 의미있는 부분은 data 부분

useState로 받아온 data 저장하기

import "./App.css";
import axios from "axios";
import { useEffect, useState } from "react";

function App() {
  const [todos, setTodos] = useState([]);

  const fetchTodos = async () => {
    const { data } = await axios.get("<http://localhost:3001/todos>");
    setTodos(data);
  };

  useEffect(() => {
    // db로부터 값 가져오기
    fetchTodos();
  }, []);

  return (
    <div>
			//todos가 없을 수도 있는 경우 대비
      {todos?.map((item) => {
        return <div key={item.id}>{item.title}</div>;
      })}
    </div>
  );
}

export default App;

 


Post

초기 코드 세팅

import "./App.css";
import axios from "axios";
import { useEffect, useState } from "react";

function App() {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState({
    // id는 자동으로 붙여줄 것이기 때문에 굳이 없어도 됨
    title: "",
  });

  const fetchTodos = async () => {
    const { data } = await axios.get("<http://localhost:3001/todos>");
    setTodos(data);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
  };

  useEffect(() => {
    // db로부터 값 가져오기
    fetchTodos();
  }, []);

  return (
    <>
      <div>
        {/* input 영역 */}
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            value={inputValue.title}
            onChange={(e) => setInputValue({ title: e.target.value })}
          />
          <button>추가</button>
        </form>
      </div>
      <div>
        {/* data 영역 */}
        {todos?.map((item) => {
          return <div key={item.id}>{item.title}</div>;
        })}
      </div>
    </>
  );
}

export default App;
  • form태그 안에 button이 있으면 button은 기본적으로 type = submit을 가지고 있음
    • submit은 새로고침 기능이 있음
    • 이를 막아주기 위해 form태그의 event default를 막아주어야 함

handleSubmit에 post 요청하는 함수 추가하기

const handleSubmit = (e) => {
    e.preventDefault();
    postFormData();
  };

  const postFormData = async () => {
    axios.post("<http://localhost:3001/todos>", inputValue);
  };

  • 현재는 input을 입력하고 엔터 누르고 새로고침을 해야 데이터가 추가된 것을 콘솔에서 볼 수 있음
  • 컴포넌트도 같이 렌더링 되어야하는데 렌더링되지 않고 있어서 생기는 문제
    • 이유 : state값이 안 변해서

버그 수정

const postFormData = async () => {
    const { data } = await axios.post(
      "<http://localhost:3001/todos>",
      inputValue
    );
    setTodos([...todos, data]);
};
  • 현재 post로 데이터를 받아오지만 아무런 데이터 처리를 해주지 않아 id설정이 되지 않고, 그로 인해 map 하위의 div에서 key값 에러가 나는 상황
  • 따라서 해결은, 비동기를 활용해 받아온 데이터를 data안에 넣어준 후 id가 추가된 data를 setTodos에도 넣어줘서 렌더링도 해 주기

Post 요청 시 네트워크탭 확인

headers

  • Request URL을 통해서 우리가 의도한 URL로 post 요청을 보냈음을 알 수 있음
  • Request Method를 통해서 우리가 POST 메서드를 사용했음을 알 수 있음
  • Status Code를 통해서 201 코드를 받았고, 정상적으로 네트워크가 이루어졌음을 알 수 있음
    • status code는 자동으로 생성되는 것이 아니라 BE개발자가 직접 개발을 하고 설정한 code가 브라우저에게 보이게 됨
    • 그래서 만약 BE개발자가 구현을 해놓지 않았다면 문맥과 다른 status code가 브라우저에 보일 수 있음

 

payload

payload에서는 우리가 보낸 body를 확인 할 수 있음

 

 

response

  • response에서는 우리가 보낸 post에 요청에 대한 서버의 응답값을 확인할 수 있음
  • 이 Response 값은 자동으로 생성되는 것이 아니라, FE 개발자가 BE 개발자에게 요청한 것을 직접 개발을 해야 생기는 값
  • 우리가 사용한 json-server의 경우 POST 요청을 했을 때, 클라이언트가 보낸 body를 그대로 응답해주도록 만들어져 패키지이기 때문에 위와 같이 표시

 


Delete

axios.delete

axios.delete(url[, config])  // DELETE
  • DELETE는 저장되어 있는 데이터를 삭제하고자 요청을 보낼 때 사용

 

추가한 코드

//...

const handleDelete = async (id) => {
    await axios.delete(`http://localhost:3001/todos/${id}`);
    setTodos(todos.filter((todo) => todo.id !== id));
  };

//...

{todos?.map((item) => {
          return (
		         //...
              <button onClick={() => handleDelete(item.id)}>삭제</button>
						//...
  • 정상적으로 잘 삭제되고 화면에서도 사라지는 걸 볼 수 있음

Patch

axios.patch

  • patch는 보통 어떤 데이터를 수정하고자 서버에 요청을 보낼 때 사용하는 메서드
  • 다만, 이것은 http 환경에서 서로가 한 약속이자 문맥이기때문에, 수정을 하고자 반드시 patch, put 을 써야만 하는 것은 아님
  • BE에 의해서 POST를 통해서 “수정" 이라는 기능은 충분히 만들 수 있기 때문
  • 다만 이러한 약속들을 대부분의 개발자들이 지키고 있음
axios.patch(url[, data[, config]])  // PATCH

 

코드 적용

//...

const onUpdateButtonClickHandler = async () => {
    await axios.patch(`http://localhost:3001/todos/${targetId}`, {
      title: content,
    });

    setTodos(
      todos.map((item) => {
        return item.id === Number(targetId)
          ? { ...item, title: content }
          : item;
      })
    );
  };

//...

return (

//...
				<input
          type="text"
          placeholder="수정할 id"
          value={targetId}
          onChange={(e) => {
            setTargetId(e.target.value);
          }}
        />
        <input
          type="text"
          placeholder="수정할 내용"
          value={content}
          onChange={(e) => {
            setContent(e.target.value);
          }}
        />
        <button onClick={onUpdateButtonClickHandler}>수정</button>
//...