TIL | React (비동기 통신 Axios와 fetch, axios interceptor ...)

·2023년 7월 5일

TIL # WIL

목록 보기
20/65

23.07.05

1. 비동기 통신 - Axios

1-1. Axios

Axios : node.js와 브라우저를 위한 Promise 기반 http 클라이언트 => http를 이용해서 서버와 통신하기 위해 사용하는 패키지 …?

즉, 간단하게 말하면 클라이언트는 서버와 통신해야하는데 이때 서버와 통신하기 위해 사용하는 패키지가 Axios로, Axios는 http 클라이언트임 !

yarn add axios 패키지 설치 필요
json-server 설치 필요

+) 기본적으로 리액트와 db.json은 다른 포트를 사용하고 있으므로 완전히 다른 공간에 있다고 생각해야 함 !

json 서버 공식 문서
참고 링크

  • 우리는 json 서버에 요청할 때, 위 사진처럼 path 방법으로 요청하면 됨 !
  • 근데 더 깊은 속성에 접근하기 위해선(ex, filter와 같은 검색) query 방법을 사용하면 됨 !

 
서버 통신을 한다는 것 = ‘비동기’
비동기 통신 = 제어권이 나한테 없음 => 즉, 서버의 상태를 우리는 기다려야 함

// db.json
{
  "todos": [
    {
    // 이때 id를 "1"이라고 문자형으로 적어줘서 id가 숫자 순서대로 나오는게 아니라 랜덤 id로 출력되었음 ...
      "id": 1,
      "title": "react"
    },
    {
      "id": 2,
      "title": "node"
    },
    {
      "id": 3,
      "title": "spring"
    }
  ]
}

1-2. GET 조회 🔆

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4001/todos");
    // await을 써주지 않으면 response는 Promise의 <pending>으로 찍힘
    // => 응답을 받기 전 콘솔창이 먼저 찍히기 때문에 ...
    console.log("data", data);
    setTodos(data);
  };

1-3. POST 추가 🔆

// submit을 눌렀을 때 POST하므로 onSubmitHandler 또한 async 함수, async 함수는 항상 인자가 필요
const onSubmitHandler = async () => {
  axios.post("http://localhost:4001/todos", inputValue);
  // post를 하고나서 setTodos를 통해 state를 추가해주고 나면 DB에는 쌓이지만,
  // state에서는 자동으로 id값이 갱신되지 않기 때문에 새로고침 하고나서야 갱신됨
  // setTodos([...todos, inputValue]);

  // 그래서 차라리 다시 fetchTodos를 통해 데이터를 다시 조회하는게 나음
  fetchTodos();
};

1-4. DELETE 삭제

const onDeleteButtonClickHandler = async (id) => {
  axios.delete(`http://localhost:4001/todos/${id}`);
  setTodos(
    todos.filter((item) => {
      return item.id !== item;
    })
  );
};

1-5. PATCH 수정

// targetId를 통해 삭제할 데이터를 찾고 그 id 안에 title을 새로 입력한 contents로 바꿔주는 것
const onUpdateButtonClickHandler = async () => {
  axios.patch(`http://localhost:4001/todos/${targetId}`, {
    title: contents,
  });

2. axios와 fetch의 비교

fetch는 axios와 비슷함
그런데 fetch는 자바스크립트 내장 라이브러리로 따로 npm이나 yarn을 통해 패키지 설치할 필요가 없음
그렇지만 fetch는 미지원 브라우저 존재, 개발자에게 불친절한 response, axios에 비해 부족한 기능 등 이러한 단점들이 있어ax서 ios 사용할 예정

크게 아래 2가지 case에서 차이를 보자면

  1. 데이터 읽어오기
fetch(url) 
.then((response) => response.json()) 
.then(console.log);

fetch를 사용해 데이터를 읽어오면 fetch().then()을 한 상태여도 JSOP 포맷의 응답이 아니기 때문에 response.json()을 한번 더 해줘야 해서 fetch로 데이터를 요청하는 경우 두 개의 .then()이 필요

const url = "https://jsonplaceholder.typicode.com/todos"; 
axios.get(url).then((response) => console.log(response.data));

반면, axios를 사용해 데이터를 읽어오면 response가 기본적으로 JSOPN 포맷이어서, response.data로만 사용하면 됨

  1. 에러 처리
const url = "https://jsonplaceholder.typicode.com/todos";

axios
  .get(url)
  .then((response) => console.log(response.data))
  .catch((err) => {
	if (err.response) {
		// 여기서 error handling 가능 !!!!!!!!!!!!!!
	}   
  });

axios.get()요청이 반환하는 Promise 객체(= 응답, response)가 갖고있는 상태코드가 2xx의 범위를 넘어가면 거부함
=> 따라서, 바로 catch() 부분을 통해 error handling이 가능
=> 즉, 각 상태코드에 맞게 무슨 문제가 있는지 알 수 있고 error handling도 가능 !

const url = "https://jsonplaceholder.typicode.com/todos";

fetch(url)
  .then((response) => {
    if (!response.ok) {
      throw new Error(
        `This is an HTTP error: The status is ${response.status}`
      );
    }
    return response.json();
  })
  .then(console.log)
  .catch((err) => {
    console.log(err.message);
  });

fetch의 경우, catch()가 발생하는 경우는 오직 네트워크 장애 케이스
=> 따라서 개발자가 일일히 then() 안에 모든 케이스에 대한 HTTP 에러 처리를 해야함


3. axios interceptor

intercept : 가로채다
interceptor : 가로채는 것 (?)

예를 들어 호출하는 서버 포트가 변경되었을 때, 만약 리액트 앱이 300개라면 …? 300개를 일일이 찾아서 변경해줘야 함 => 인적 리소스 낭비

axios interceptor : 즉, 클라이언트와 서버 사이의 axios interceptor는 요청(request)과 응답(response)의 흐름을 가로채서 코드에 관여를 하는 것 !!!

  1. 요청(request)이 처리되기 전( = http request가 서버에 전달되기 전)
  2. 응답(response)의 then(=성공) 또는 catch(=실패)가 처리되기 전

axios interceptor를 사용하면 아래 이러한 작업들을 한꺼번에 처리할 수 있음

  • 요청 헤더 추가
  • 인증 관리
  • 로그 관련 로직 삽입
  • 에러 핸들링

3-1. .env

.env (environment) : 환경변수 관리로 API key, port, DB 등 민감한 정보를 환경변수에 담아 관리하는 방법

env 주의사항

  1. root 폴더에 생성해야 함
  2. 변수명은 반드시 'REACTAPP'으로 시작해야 함
  3. .gitignore에 env를 등록해야 함
  4. 환경변수를 불러오는 것은 import 없이 'process.env.REACTAPP변수명'으로 불러오면 됨
    (만약 env의 값을 변경하여 적용하고 싶다면, 변경 후 server를 다시 실행시켜주어야 정상적으로 적용됨)

=> 즉, .env 파일을 이용해서 REACT_APP_SERVER_URL=http://localhost:4001 이런식으로 서버 URL 변수를 만들어줌

그리고 이제 App.jsx에서는 process.env.REACT_APP_SERVER_URL 이런 방식을 통해 URL에 접근할 수 있음

3-2. axios 가공

axios 폴더와 api.js 파일에서 axios를 가공해서 사용 !
=> axios.create()를 통해 새로운 instance를 만들어서 사용하면 됨, 그리고 create() 인자에는 객체가 들어감

가공하고 나서부터는 import api from "./axios/api"; 이런 식으로 import해서 api를 사용하여 접근하면 됨

 
instance.interceptors.request.use()

instance.interceptors.request.use(
  // 요청을 보내기 전 수행되는 함수
  // 인자로 항상 config를 받음
  function (config) {
    console.log("인터셉터 요청 성공 !");
    return config;
  },

  // 오류 요청을 보내기 전 수행되는 함수
  function (error) {
    console.log("인터셉터 요청 오류 !");
    // error 객체를 인자로 전달 ...
    return Promise.reject(error);
  }
);

 
instance.interceptors.response.use()

instance.interceptors.response.use(
  // 응답을 내보내기 전 수행되는 함수
  function (response) {
    console.log("인터셉터 응답 성공 !!");
    return response;
  },

  // 오류 응답을 내보내기 전 수행되는 함수
  function (error) {
    console.log("인터셉터 응답 오류 !!");
    return Promise.reject(error);
  }
);

실패 시켜보기

// timeout 프로퍼티
// 0.001초 동안 response가 안오면 무조건 오류를 낼 거야
timeout: 1, // message : "timeout of 1ms exceeded"

더 적용할 수 있는 부분

  • 요청 시, content-type 적용
  • token 등 인증 관련 로직 적용
  • 서버 응답 코드에 대한 오류 처리(controller)
  • 통신시작 및 종료에 대한 전역 상태를 관리하여 spinner, progress bar 등 구현 가능
    사실, axios interceptor를 통해 통신의 중간과정에서 개발자의 머릿속에 있는 모든 걸 구현 가능

0개의 댓글