Ajax & Axios for React

jiny·2022년 9월 3일
1

React

목록 보기
5/9
post-thumbnail

목차

  • Ajax
  • 기존 XHR(XML HTTP Request)의 문제점
  • Axios
  • Axios와 Fetch의 차이점

Ajax

Ajax 소개

Asynchronous JavaScript And XML

  • JavaScript를 사용한 비동기 통신, 클라이언트 - 서버 간 데이터를 주고 받는 기술
  • Ajax를 통해 JS에서 비동기 HTTP 통신(response & request를 비동기로 다룰 수 있다는 것)을 가능하게 함

ex

네이버에 검색창에 원하는 검색어를 입력하는 경우

  • 네이버 검색창에 입력하는 경우 입력할 때 마다 어떠한 쿼리로 서버에 요청하는 것을 알 수 있음

만약 Ajax가 없었다면?

  • 입력 할 때 마다 서버 - 클라이언트간 통신하게 됨 -> 동기적으로 통신
  • 동기적으로 통신하는 경우 통신 할 때마다 브라우저가 재 실행되게 됨
  • 즉, 사용자가 검색어를 입력할 때마다 브라우저가 다시 재 로딩

Ajax가 가능하게 한 것

  • 서버 - 클라이언트 간 통신을 비 동기적으로 할 수 있게 함으로써 브라우저의 재 로딩이 발생하지 않음 -> UX의 향상

순수 Ajax의 문제점과 JQuery

순수 Ajax를 이용한 코드

function reqListener (e) {
    console.log(e.currentTarget.response);
}

const oReq = new XMLHttpRequest();
const serverAddress = "https://jsonplaceholder.typicode.com/posts";

oReq.addEventListener("load", reqListener);
oReq.open("GET", serverAddress);
oReq.send();

Ajax의 문제점

  • Ajax라는 기술은 2005년 부터 쓰여온 기술이며, 정립된 시기는 1999년
  • 코드가 정말 매우 지저분(위 코드 참고) 했기 때문에 보완이 필요했음

JQuery로 구현한 Ajax

const serverAddress = 'https://jsonplaceholder.typicode.com/posts';

$.ajax({
    url: ,
    type: 'GET',
    success: function onData (data) {
        console.log(data);
    },
    error: function onError (error) {
        console.error(error);
    }
});

JQuery

  • JQuery에서 Ajax를 편리하게 사용하도록 정립하면서 JQuery가 급부상
  • JQuery를 사용하여 Ajax를 구현할 경우 브라우저에 구애 받지 않고 동일한 코드로 작업이 구현이 가능
  • 또한 순수 Ajax로는 구현이 어려웠던 응답이 성공했을 경우, 실패했을 경우를 나눠서 처리가 가능

XHR와 XHR의 문제점

XHR(XML HTTP Request)

  • AJAX 요청을 생성하는 JavaScript API
  • XHR의 메서드로 브라우저와 서버간의 네트워크 요청을 전송할 수 있음
  • mozilla에서 언급한 XHR의 설명
  • 기존 Ajax는 XHR를 활용하여 클라이언트 - 서버간 요청 & 응답을 비동기로 처리

XHR의 문제점

  • 잘 설계된 API가 아님
  • 요청의 상태나 변경을 구독하려면 Event를 등록해서 변경사항을 받아야 함
  • 요청의 성공, 실패 여부나 상태에 따라 처리하는 로직이 들어가기 좋지 않음

해결

  • HTTP 요청에 최적화 되있으면서 잘 추상화 된 api들을 사용 (axios, fetch)

Axios

Axios 소개

Axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트 입니다. 동일한 코드베이스로 브라우저와 node.js에서 실행할 수 있습니다. 서버 사이드에서는 네이티브 node.js의 http 모듈을 사용하고, 클라이언트(브라우저)에서는 XMLHttpRequests를 사용합니다.

  • Axios 공식 문서에서 Axios를 설명하는 내용
  • 비동기로 HTTP 통신을 가능케 하며, 리턴 값을 Promise 객체로 하기 때문에 Response 객체 다루기가 편함
  • 자바스크립트 서드 파티 라이브러리로써, CDN이나 패키지 매니저(npm, yarn)을 통해 설치

install & setting

npm

npm i axios

yarn

yarn add axios
  • npm이나 yarn에서 다음과 같은 명령어로 쉽게 설치가 가능

CDN

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  • CDN은 HTML이나 브라우저에 다음과 같은 태그를 추가

setting

api.ts

import axios from "axios";
  • 특정 모듈에서 axios를 import하면 쉽게 사용이 가능하다.

Syntax

단축 메서드를 사용하지 않는 경우

axios(url, {
	method : "get", // 다른 옵션도 가능(post, put, delete, etc.)
    headers : {},
    data : {},
})
  • 첫 번째 인자로는 요청 보낼 url를 설정
  • 두 번째 인자로 요청과 함께 보낼 객체(config) 설정
  • 객체에는 method, headers, data 등 많은 옵션들이 존재

단축 메서드를 사용하는 경우

axios.get(url,{ //설정옵션 })
  • 단축 메서드를 사용하여 좀 더 간편하게 설정도 가능

요청 메서드 종류

axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
  • axios는 다양한 요청 메서드를 제공

요청 config

{
  // `url`은 요청에 사용될 서버 URL입니다.
  url: '/user',

  // `method`는 요청을 생성할때 사용되는 메소드입니다.
  method: 'get', // 기본값

  // `url`이 절대값이 아닌 경우 `baseURL`은 URL 앞에 붙습니다.
  // 상대적인 URL을 인스턴스 메서드에 전달하려면 `baseURL`을 설정하는 것은 편리합니다.
  baseURL: 'https://some-domain.com/api',


  // `transformRequest`는 요청 데이터를 서버로 전송하기 전에 변경할 수 있게 해줍니다.
  // 이것은 'PUT', 'POST', 'PATCH', 'DELETE' 메소드에서만 적용됩니다.
  // 마지막 함수는 Buffer, ArrayBuffer, FormData 또는 Stream의 인스턴스 또는 문자열을 반환해야 합니다.
  // 헤더 객체를 수정할 수 있습니다.
  transformRequest: [function (data, headers) {
    // 데이터를 변환하려는 작업 수행

    return data;
  }],

  // `transformResponse`는 응답 데이터가 then/catch로 전달되기 전에 변경할 수 있게 해줍니다.
  transformResponse: [function (data) {
    // 데이터를 변환하려는 작업 수행

    return data;
  }],

  // `headers`는 사용자 지정 헤더입니다.
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params`은 요청과 함께 전송되는 URL 파라미터입니다.
  // 반드시 일반 객체나 URLSearchParams 객체여야 합니다.
  // 참고: null이나 undefined는 URL에 렌더링되지 않습니다.
  params: {
    ID: 12345
  },

  // `paramsSerializer`는 `params`의 시리얼라이즈를 담당하는 옵션 함수입니다.
  // (예: https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data`는 요청 바디로 전송될 데이터입니다.  
  // 'PUT', 'POST', 'PATCH', 'DELETE' 메소드에서만 적용 가능합니다.
  // `transformRequest`가 설정되지 않은 경우 다음 타입 중 하나여야 합니다.
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 브라우저 전용: FormData, File, Blob
  // - Node 전용: Stream, Buffer
  data: {
    firstName: 'Fred'
  },

  // 바디로 전송하는 데이터의 대안 문법
  // POST 메소드
  // 키가 아닌 값만 전송됩니다.
  data: 'Country=Brasil&City=Belo Horizonte',

  // `timeout`은 요청이 시간 초과되기 전의 시간(밀리초)을 지정합니다.
  // 요청이 `timeout`보다 오래 걸리면 요청이 중단됩니다.
  timeout: 1000, // 기본값은 `0` (타임아웃 없음)

  // `withCredentials`은 자격 증명을 사용하여 사이트 간 액세스 제어 요청을 해야 하는지 여부를 나타냅니다.
  withCredentials: false, // 기본값

  // `adapter`'은 커스텀 핸들링 요청을 처리할 수 있어 테스트가 쉬워집니다.
  // 유효한 Promise 응답을 반환해야 합니다. (lib/adapters/README.md 참고)
  adapter: function (config) {
    /* ... */
  },

  // `auth`는 HTTP Basic 인증이 사용되며, 자격 증명을 제공합니다.
  // `auth`를 사용하면, `Authorization` 헤더가 설정되어 `headers`를 사용하여 설정한 기존의 `Authorization` 사용자 지정 헤더를 덮어씁니다.
  // 이 파라미터를 통해 HTTP Basic 인증만 구성할 수 있음을 참고하세요.
  // Bearer 토큰 등의 경우 `Authorization` 사용자 지정 헤더를 대신 사용합니다.
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType`은 서버에서 받는 데이터의 타입입니다.
  // 옵션: 'arraybuffer', 'document', 'json', 'text', 'stream'
  // 브라우저 전용: 'blob'
  responseType: 'json', // 기본값

  // `responseEncoding`은 응답 디코딩에 사용할 인코딩입니다.
  // Node.js 전용
  // 참고: 클라이언트 사이드 요청 또는 `responseType`이 'stream'이면 무시합니다.
  responseEncoding: 'utf8', // 기본값

  // `xsrfCookieName`은 xsrf 토큰 값으로 사용할 쿠키의 이름입니다.
  xsrfCookieName: 'XSRF-TOKEN', // 기본값

  // `xsrfHeaderName`은 xsrf 토큰 값을 운반하는 HTTP 헤더의 이름입니다.
  xsrfHeaderName: 'X-XSRF-TOKEN', // 기본값

  // `onUploadProgress`는 업로드 진행 이벤트를 처리합니다.
  // 브라우저 전용
  onUploadProgress: function (progressEvent) {
    // 업로드 진행 이벤트 작업 수행
  },

  // `onDownloadProgress`는 다운로드로드 진행 이벤트를 처리합니다.
  // 브라우저 전용
  onDownloadProgress: function (progressEvent) {
    // 다운로드 진행 이벤트 작업 수행
  },

  // `maxContentLength`는 node.js에서 허용되는 http 응답 콘텐츠의 최대 크기를 바이트 단위로 정의합니다.
  maxContentLength: 2000,

  // `maxBodyLength`는 허용되는 http 요청 콘텐츠의 최대 크기를 바이트 단위로 정의합니다.
  // Node.js 전용
  maxBodyLength: 2000,

  // `validateStatus`는 지정된 HTTP 응답 상태 코드에 대한 Promise를 이행할지 또는 거부할지 여부를 정의합니다. 
  // 만약 `validateStatus`가 true를 반환하면(또는 'null' 또는 'undefined'으로 설정) Promise는 이행됩니다.
  // 그렇지 않으면, 그 Promise는 거부될 것이다.
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 기본값
  },

  // `maxRedirects`는 node.js에서 리디렉션 최대값을 정의합니다.
  // 0으로 설정하면 리디렉션되지 않습니다.
  maxRedirects: 5, // 기본값

  // `socketPath`는 node.js에서 사용될 UNIX 소켓을 정의합니다.
  // 예: '/var/run/docker.sock' 도커 데몬에 요청을 보냅니다.
  // 오직 `socketPath` 또는 `proxy`만 지정할 수 있습니다.
  // 둘 다 지정되면 `socketPath`가 사용됩니다.
  socketPath: null, // 기본값

  // `httpAgent`와 `httpsAgent`는 각각 node.js에서 http 및 https 요청을 수행할 때 사용할 사용자 지정 에이전트를 정의합니다.
  // 이렇게 하면 기본적으로 활성화되지 않은 `keepAlive`와 같은 옵션을 추가할 수 있습니다.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy`는 프록시 서버의 호스트이름, 포트, 프로토콜을 정의합니다.
  // 기존의 `http_proxy`와 `https_proxy` 환경 변수를 사용하여
  // 프록시를 정의할 수도 있습니다.
  // 프록시 구성에 환경 변수를 사용하는 경우, 'no_proxy' 환경 변수를 
  // 쉼표로 구분된 프록시가 되지 않는 도메인 목록으로 정의할 수도 있습니다.
  // `false`를 사용하면 프록시를 사용하지 않고, 환경 변수를 무시합니다.
  // `auth`는 프록시에 연결하는데 HTTP Basic auth를 사용해야 함을 나타내며, 
  // 자격 증명을 제공합니다. 그러면 `Proxy-Authorization` 헤더가 설정되고,
  // `headers`를 사용하여 기존의 `Proxy-Authorization` 사용자 지정 해더를 덮어씁니다.
  // 만약 프록시 서버가 HTTPS를 사용한다면, 프로토콜을 반드시 `https`로 설정해야 합니다.
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken`은 요청을 취소하는 데 사용할 수 있는 취소 토큰을 지정합니다.
  // (자세한 내용은 요청 취소 섹션 참조)
  cancelToken: new CancelToken(function (cancel) {
  }),

  // `decompress`는 응답 바디의 자동 압축 해제 여부를 나타냅니다.
  //  `true`로 설정하면, 압축 해제된 모든 응답에서 'content-encoding' 헤더도 제거됩니다.
  // Node.js 전용 (XHR은 압축 해제할 수 없습니다)
  decompress: true // 기본값

}
  • axios는 다양한 요청 config를 제공
  • 해당 config 옵션들을 활용하여 axios의 2번째 인자를 구성하거나 axios 인스턴스에 활용 가능

Axios 응답 config

{
  // `data`는 서버가 제공하는 응답입니다.
  data: {},

  // `status`는 HTTP 상태 코드입니다.
  status: 200,

  // `statusText`는 HTTP 상태 메시지입니다.
  statusText: 'OK',

  // `headers`는 HTTP 헤더입니다.
  // 모든 헤더 이름은 소문자이며, 괄호 표기법을 사용하여 접근할 수 있습니다.
  // 예시: `response.headers['content-type']`
  headers: {},

  // `config`는 요청을 위해 `Axios`가 제공하는 구성입니다.
  config: {},

  // `request`는 이번 응답으로 생성된 요청입니다.
  // 이것은 node.js에서 마지막 ClientRequest 인스턴스 입니다.
  // 브라우저에서는 XMLHttpRequest입니다.
  request: {}
}
  • axios는 응답 config 또한 다양하게 제공

기타

axios({
  method: "get",
  url: url,
  headers: {},
  data: {},
});
  • config 안에 url까지 포함 시킬수도 있음
axios(url);
  • 만약 단축메서드도 옵션도 활용하지 않는다면 기본적으로 GET 요청을 보냄

데이터 처리

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

axios.get(url)
.then(res => {
	console.log(res.data);
})
  • axios를 사용 시 응답 데이터를 기본적으로 JSON 타입으로 사용이 가능
  • 즉, fetch 처럼 json 처리를 따로 할 필요가 없이 좀 더 편하게 사용이 가능
  • 응답 데이터는 응답 객체의 data 프로퍼티에서 사용이 가능
axios.get(url, {
  responseType: "json", // options: 'arraybuffer', 'document', 'blob', 'text', 'stream'
});
  • 다음과 같은 옵션을 통해 응답 타입을 지정하여 기본 JSON 데이터 타입을 재정의 가능

POST 요청을 보내는 경우

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

const todo = {
  title: "A new todo",
  completed: false,
};

axios
  .post(url, {
    data: todo,
  })
  .then(console.log);
  • axios는 fetch와 다르게 data를 보낼 때 기본적으로 content-type을 application/json으로 설정하기 때문에 따로 JSON 직렬화 처리를 할 필요가 없음

에러 처리

  • axios는 모두 이행 되거나 거부된 promise를 반환
  • Promise가 거부 되면 .catch()를 사용하여 에러 처리가 가능
const url = "https://jsonplaceholder.typicode.com/todos";

axios
  .get(url)
  .then((response) => console.log(response.data))
  .catch((err) => {
    console.log(err.message);
  });
.catch((err) => {
// 에러 처리
if (err.response) {
// 요청이 이루어졌고 서버가 응답했을 경우

    const { status, config } = err.response;

    if (status === 404) {
      console.log(`${config.url} not found`);
    }
    if (status === 500) {
      console.log("Server error");
    }

  } else if (err.request) {
    // 요청이 이루어졌으나 서버에서 응답이 없었을 경우
    console.log("Error", err.message);
  } else {
    // 그 외 다른 에러
    console.log("Error", err.message);
  }
});
  • 이런 식으로 http status code에 따른 에러 핸들링이 가능

응답 시간 초과

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

axios
  .get(url, {
    timeout: 4000, // 기본 설정은 '0' (타임아웃 없음)
  })
  .then((response) => console.log(response.data))
  .catch((err) => {
    console.log(err.message);
  });
  • http 요청이 시간 초과될 경우 axios는 timeout 속성을 설정 객체에 추가하여 요청이 종료 될 때까지의 시간을 밀리초로 지정 가능

인스턴스 설정

import axios from "axios";

export const client = axios.create({
    baseURL : "http://localhost:4000",
    withCredentials : true,
})
  • custom config와 create 메서드를 통해 axios 인스턴스 생성이 가능

store.ts

import { atom, selector } from "recoil";
import { client } from "./api/api";

export const refreshSelector = selector({
    key : 'refresh/user',
    get : async ({get}) => {
        let user = get(userInfo);
        await client.get('/api/refresh')
        .then( async (res) => {
            const data = res.data as IToken;
            client.defaults.headers.common["Authorization"] = `Bearer ${data.accessToken}`
            await client.get("/api/user")
            .then(res => {
                let data = res.data as IResponse
                user = data["email"];
            })
            .catch(err => {
                console.log(err);
            })
        });
        return user
    }
})
  • 실제로 axios 인스턴스를 활용한 예시

인터셉터

  • then이나 catch로 처리되기 전 요청과 응답을 가로챌 수 있음
// 요청 인터셉터 추가하기
axios.interceptors.request.use(function (config) {
    // 요청이 전달되기 전에 작업 수행
    return config;
  }, function (error) {
    // 요청 오류가 있는 작업 수행
    return Promise.reject(error);
  });

// 응답 인터셉터 추가하기
axios.interceptors.response.use(function (response) {
    // 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
    // 응답 데이터가 있는 작업 수행
    return response;
  }, function (error) {
    // 2xx 외의 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
    // 응답 오류가 있는 작업 수행
    return Promise.reject(error);
  });
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
  • eject를 활용하여 필요할 때 인터셉터 제거도 가능
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
  • 다음과 같이 커스텀 인스턴스에서도 인터셉터 추가가 가능하다.

Axios - Fetch 차이점

  • 다음과 같은 차이점이 존재한다.

레퍼런스

eunbinn - [번역] 입문자를 위한 Axios vs Fetch

https://velog.io/@eunbinn/Axios-vs-Fetch#%EC%84%B1%EB%8A%A5

kysung95 - [개발상식] Ajax와 Axios 그리고 fetch

https://velog.io/@kysung95/%EA%B0%9C%EB%B0%9C%EC%83%81%EC%8B%9D-Ajax%EC%99%80-Axios-%EA%B7%B8%EB%A6%AC%EA%B3%A0-fetch#%EA%B2%B0%EB%A1%A0

tastestar - Axios

https://velog.io/@tastestar/Axios#%EC%B6%9C%EC%B2%98

Axios 공식문서

https://axios-http.com/

0개의 댓글