20230218 [react] - axios

lionloopy·2023년 2월 18일
0

리액트

목록 보기
14/18

axios

:브라우저와 node.js에서 사용할 수 있는 Promise(비동기)기반 HTTP클라이언트 라이브러리이다.
yarn add axios
yarn add json-server

Create Read Update Delete
GET: read
POST: create, update, delete

GET

:서버의 데이터를 조회할 때 사용한다.

db.json

{
  "todos": [
    {
      "id": "1",
      "title": "react"
    }
  ]
}

App.js

import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.

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

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

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

  console.log(todos); 
  return <div>App</div>;
};

export default App;

1.axios를 import한다.
2.db로 부터 값을 가져오기 위해 fetchTodos함수를 하나 만든다.
(axios를 통해서 get요청을 하는 함수)
3.비동기 처리를 해야하므로 async/await 구문을 통해서 처리한다.
4.axios.get(url)을 통해서 쉽게 데이터를 가져올 수 있다.
url/뒤에는 db.json의 이름을 적어주면 된다.
5.useEffect를 사용하여, 최초 mount됐을 때, db를 가져오도록 한다.
6.useEffect에서 fetchTodos함수를 호출해서 실행한다.
7.이렇게 받아온 data를 우리가 컴포넌트 안에서 쓰기 위해 state로 받아온다.
8.서버로부터 fetching한 데이터를 useState의 state로 set한다.
9.return문에서 map으로 돌리면 화면에 출력되는 data값들을 볼 수 있다.
+이때 나는 yarn start로 localhost:3000번을 사용하고 있지만, json data는 yarn json-server --watch db.json --port 4000명령어를 실행했을 때 이 포트로 가게 된다. (4000번대부터 시작) 따라서 axios.get으로 잘 불러와야 한다
=> 다른 작업들이 끝나고 렌더링이 되면서 useEffect순서가 되면, 안에 있는 함수가 실행된다. fetchTodos함수를 실행하고, 이 함수는 axios.get구문을 통해 json data를 불러와 data에 담는다. 이것을 state로 관리하고, 현재 최신상태를 이 불러온 data로 하여 UI에 보일 수 있도록 한다.

POST

:서버에 데이터를 추가해달라고 요청하는 방식이다.

App.js

import axios from "axios";
import React, { useEffect, useState } from "react";

function App() {
  const [todos, setTodos] = useState(null);
  const [input, setInput] = useState({
    title: "",
  });
  const onSubmitHandler = async () => {
    axios.post("http://localhost:4000/todos", input);
    setTodos([...todos, input]);
  };
  const onDeleteHandler = async (id) => {
    axios.delete(`http://localhost:4000/todos/${id}`);
    setTodos(
      todos.filter((item) => {
        return item.id !== id;
      })
    );
  };
  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data);
  };
  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <div>
      <div>
        {/* input영역 */}
        <form
          onSubmit={(event) => {
            event.preventDefault();
            onSubmitHandler();
          }}
        >
          <input
            type="text"
            value={input.title}
            onChange={(event) => {
              setInput({
                title: event.target.value,
              });
            }}
          />
          <button>추가</button>
        </form>
      </div>
      <div>
        {/* 데이터 영역 */}
        {todos?.map((item) => {
          return (
            <div key={item.id}>
              {item.id}:{item.title}
              <button onClick={() => onDeleteHandler(item.id)}>삭제</button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

1.input값을 받아 값을 추가하기 위해 input영역을 만들고, 이 state를 관리하기 위해 input state값을 만든다.
2.json방식은 id값이 자동으로 들어가게 된다. 따라서 useState초기값에 title만 빈 값으로 미리 넣어주면 된다.
3.input에 onChange로 event를 받으면, title값은 setInput을 사용하여 input값이 받아온 event.target.value로 변하게 한다.
4.form태그에 버튼이 들어가게 되면 자동적으로 버튼은 submit기능을 가지고, submit기능은 제출이 되면 자동적으로 새로고침이 된다. 이것을 막기 위해 evnet.preventDefault()를 사용한다.
5.onSubmitHandler함수를 만들고, 그 기능은 axios.post로 서버에 input데이터를 추가해달라고 요청을 한다. 그리고 그것을 보여주기 위해 setInput값을 기존 값 복사와 함께 input을 넣는다.
6.이제 삭제버튼을 추가하고, 삭제를 눌렀을 때 해당 부분이 삭제되도록 한다.
7.axios.delete를 넣고 주소값으로 백틱을 넣어서 id값을 넣어주도록 한다. id에 따라 삭제할 수 있도록 한다. 그런데 여기까지만 코드를 짜면 <삭제 클릭 - 삭제가 반영됨 - 새로고침 해야 보여짐>으로 불편하게 작동한다.
8.따라서 UI를 보여주기 위해 setTodos를 활용한다.
=> get으로 불러온 데이터들은 todos에 담아지고 있다. 이때 사용자에게 받아온 값으로 관리하고 싶기 때문에, input을 만들고 input관리는 handler로 한다. 이 handler안에서 post방식으로 input값으로 받아온 데이터를 json data안에 추가해준다. 그리고 삭제할 때는 그 json data안에 저장된 id 값을 찾아 삭제하게 만들고 그것을 UI에 보여주기 위해 set함수를 활용한다.

PATCH

:데이터를 수정하는 방식이다.

App.js

import axios from "axios";
import React, { useEffect, useState } from "react";

function App() {
  const [todos, setTodos] = useState(null);
  const [input, setInput] = useState({
    title: "",
  });
  const [target, setTarget] = useState(""); //id잡을 것
  const [content, setContent] = useState(""); //title잡을 것

  const onSubmitHandler = async () => {
    axios.post("http://localhost:4000/todos", input);
    // setTodos([...todos, input]);
    fetchTodos();
  };
  const onDeleteHandler = async (id) => {
    axios.delete(`http://localhost:4000/todos/${id}`);
    setTodos(
      todos.filter((item) => {
        return item.id !== id;
      })
    );
  };
  const onUpdateHandler = async () => {
    axios.patch(`http://localhost:4000/todos/${target}`, {
      title: content,
    });
    setTodos(
      todos.map((item) => {
        if (item.id == target) {
          return { ...item, title: content };
        } else {
          return item;
        }
      })
    );
  };
  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data);
  };
  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <div>
      <div>
        {/* 수정 영역 */}
        <input
          type="text"
          placeholder="수정할 id"
          value={target}
          onChange={(event) => {
            setTarget(event.target.value);
          }}
        />
        <input
          type="text"
          placeholder="수정할 title"
          value={content}
          onChange={(event) => {
            setContent(event.target.value);
          }}
        />
        <button onClick={onUpdateHandler}>수정</button>
      </div>
      <div>
        {/* input영역 */}
        <form
          onSubmit={(event) => {
            event.preventDefault();
            onSubmitHandler();
          }}
        >
          <input
            type="text"
            value={input.title}
            onChange={(event) => {
              setInput({
                title: event.target.value,
              });
            }}
          />
          <button>추가</button>
        </form>
      </div>
      <div>
        {/* 데이터 영역 */}
        {todos?.map((item) => {
          return (
            <div key={item.id}>
              {item.id}:{item.title}
              <button onClick={() => onDeleteHandler(item.id)}>삭제</button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

1.수정하는 영역을 위해 input두개와 버튼을 만들고, 하나의 input은 id, 하나의 input은 title을 잡아내는 영역이다.
2.이 input들을 관리하기 위해 각각 state를 만든다.
3.각각 onChange로 input의 value값을 받아오도록 하고,
4.onUpdateHandler함수를 만든다.
5.axios.patch를 활용하고 주소로는 백틱을 넣어 target을 잡아오도록 한다. 그리고 객체로 title:content를 넣어 title은 content와 같다는 것을 적어준다.
6.그리고 수정된 것을 바로 보여주기 위해 setTodos를 활용하고, 그 안에서 map을 돌려 todos의 각각의 id와 입력한 id값인 target이 같은 것들을 찾아내도록 한다.
7.그러면 잘 작동하는 것을 볼 수 잇으나, 값을 추가할 때 id값이 바로바로 추가되지 않는 것을 볼 수 있다.
8.따라서 제출하면 새로고침이 되는 onSubmitHandler에 가서 setTodos보다는 fetchTodos함수를 다시 호출하여 바로바로 데이터를 불러올 수 있도록 한다.
=> input값을 state로 관리하며, axios.patch구문을 통해 json data에서 target,content값을 찾아 내용을 바꾸는 로직이다.

axios vs fetch

fetch
:ES6부터 도입된 자바스크립트 내장 라이브러리이다.
Promise기반 비동기 통신 라이브러리이며,
axios처럼 데이터를 다루기 쉽고, 내장 라이브러리이기 때문에 별도의 설치나 import가 필요 없다.

단점

  • 미지원 브라우저가 존재한다.
  • 개발자에게 불친절한 response
  • axios에 비해 부족한 기능

axios vs fetch

const url = "http://~"
fetch(url)
.then((response) => response.json())
.then(console.log)

const url = "http://~"
axios.get(url).then((response) => console.log(response.data))

axios interceptor

interceptor
:흐름을 가로채서 코드 상의 관여를 할 수 있게 하는 것
1.요청이 처리되기 전(http 요청이 서버에 전달되기 전)
2.응답의 then(성공) 또는 catch(실패)가 처리되기 전

  • 요청 헤더 추가
  • 인증 관리(JWT)
  • 로그 관련 로직 삽입
  • 에러 핸들링
import axios from "axios";

const instance = axios.create({
  baseURL: "http://localhost:4000",
});

instance.interceptors.request.use(
  //요청을 보내기 전 수행
  function (config) {
    console.log("인터셉트 요청 성공");
    return config;
  },

  //오류 요청을 보내기 전 수행
  function (error) {
    console.log("인터셉트 요청 오류");
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  //서버로부터 정상 응답을 받는 경우
  function (response) {
    console.log("인터셉트 응답 성공");
    return response;
  },

  //서버로부터 오류 응답을 받는 경우
  function (error) {
    console.log("인터셉트 응답 오류");
    return Promise.reject(error);
  }
);

export default instance;

instance 바꾸기
instance: import해오는 기능들

axios/api.js

import axios from "axios";

const instance = axios.create({
  baseURL: "http://localhost:4000",
});

export default instance;

App.js

import axios from "axios";
import React, { useEffect, useState } from "react";
import api from "./axios/api";

function App() {
  const [todos, setTodos] = useState(null);
  const [input, setInput] = useState({
    title: "",
  });
  const [target, setTarget] = useState("");
  const [content, setContent] = useState("");

  const onSubmitHandler = async () => {
    api.post("/todos", input);
    // setTodos([...todos, input]);
    fetchTodos();
  };
  const onDeleteHandler = async (id) => {
    api.delete(`/todos/${id}`);
    setTodos(
      todos.filter((item) => {
        return item.id !== id;
      })
    );
  };
  const onUpdateHandler = async () => {
    api.patch(`/todos/${target}`, {
      title: content,
    });
    setTodos(
      todos.map((item) => {
        if (item.id == target) {
          return { ...item, title: content };
        } else {
          return item;
        }
      })
    );
  };
  const fetchTodos = async () => {
    const { data } = await api.get("/todos");
    setTodos(data);
  };
  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <div>
      <div>
        {/* 수정 영역 */}
        <input
          type="text"
          placeholder="수정할 id"
          value={target}
          onChange={(event) => {
            setTarget(event.target.value);
          }}
        />
        <input
          type="text"
          placeholder="수정할 title"
          value={content}
          onChange={(event) => {
            setContent(event.target.value);
          }}
        />
        <button onClick={onUpdateHandler}>수정</button>
      </div>
      <div>
        {/* input영역 */}
        <form
          onSubmit={(event) => {
            event.preventDefault();
            onSubmitHandler();
          }}
        >
          <input
            type="text"
            value={input.title}
            onChange={(event) => {
              setInput({
                title: event.target.value,
              });
            }}
          />
          <button>추가</button>
        </form>
      </div>
      <div>
        {/* 데이터 영역 */}
        {todos?.map((item) => {
          return (
            <div key={item.id}>
              {item.id}:{item.title}
              <button onClick={() => onDeleteHandler(item.id)}>삭제</button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

export default로 해오면 꼭 그 이름이 아니더라도 상대경로를 적어 import해 올 수 있다.

profile
Developer ʕ ·ᴥ·ʔ ʕ·ᴥ· ʔ

0개의 댓글