치춘짱베리굿나이스

axios - instance 사용하기 본문

ClientSide/라이브러리

axios - instance 사용하기

치춘 2023. 4. 18. 15:25

Axios instance

트랜센던스를 하면서 axios 호출부를 싹 개편할 일이 있었는데 팀원이 Axios instance에 관해 공부하고 트센에 도입하는 게 좋을 것 같다는 의견을 주었다 (gosu)

근데 이전 프로젝트는 fetch 를 썼었고 Axios 는 정말 오랜만에 쓰는 데다가 instance 기능은 안 써봤기 때문에 정리할 필요가 좀 생겼음

Axios 관련해서도 한번 글로 쭉 엮고 가야하는데 귀찮으즘이 아주…

instance 쓰기 전의 axios 함수 구성

axios Wrappers

export async function axiosGet<Type>(uri: string): Promise<Type> {
    return axios
        .get<Type>(`${process.env.REACT_APP_SERVER}${uri}`)
        .then(({data}) => data)
        .catch(throwApiError);
}

export async function axiosPost<bodyObjType, resType>(uri: string, reqData: bodyObjType): Promise<resType> {
  return axios
    .post<resType>(`${process.env.REACT_APP_SERVER}${uri}`, reqData)
    .then(({ data }) => data)
    .catch(throwApiError);
}

export async function axiosPut<bodyObjType>(uri: string, reqData: bodyObjType): Promise<void> {
  return axios.put(`${process.env.REACT_APP_SERVER}${uri}`, reqData);
}

export async function axiosDelete(uri: string): Promise<void> {
  return axios.delete(`${process.env.REACT_APP_SERVER}${uri}`);
}

axios 함수들을 한번 wrap 한 wrapper 함수들을 만들었다

모든 API Call 함수들이 thencatch 에서의 로직이 중복되고, baseUrl (process.env.REACT_APP_SERVER 로 환경변수 처리되어 있다) 이 동일하기 때문에 중복되는 코드를 줄이기 위해 작성했다

서버에서 응답받는 데이터의 타입이 매번 다르기 때문에 제네릭 타입으로 처리했다

 

export async function getFtCallbackCode(code: string) {
    return axiosGet<FtProfileType>(`${API.FT_AUTH_CALLBACK}?code=${code}`);
}

export async function postRegister(nickname: string, avatar: string): Promise<UserInfoType> {
  return axiosPost<BodyObjType, UserInfoType>(API.REGISTER, { nickname, avatar });
}

사용할 때는 이렇게 응답의 타입을 지정하고 uri와 기타 body에 들어갈 값들을 인자로 넣어주면 된다

이전에는 axios.get, then, catch 의 세 줄 코드를 매번 적어넣었어야 했는데 하나의 함수로 합쳐놓으니 훨씬 간결하고 보기가 편해졌다

withCredentials

axios.defaults.withCredentials = true;

쿠키를 받을 때 CORS 이슈를 해결하기 위해 withCredentials 를 매번 true 로 설정해줬었는데, axios.defaults 에 설정을 등록해 두면 이후의 모든 axios call 들에 해당 설정이 적용된 채로 동작했다

다만 위 라인이 최상단 index.ts 에 존재했기 때문에 컴포넌트와 전혀 관련없는 라인이 뜬금없이 들어있는 느낌을 지울 수 없었다

사실 누가 만들어 놓은 게 있었음

사실 axios wrapper 를 만들지 않아도!! withCredentials를 최상단 index.ts 에서 설정하지 않아도!!

axios 에서 자체적으로 “사용자 정의 설정을 구성해둔 인스턴스를 만들어서 API Call 에 사용할 수 있는 기능” 을 지원해주고 있었다

지금부터 위의 코드들을 axios instance 를 구성하여 개선해보도록 하겠다

axios instance

위에서 내가 작성한 axios wrapper 처럼, 중복될 만한 설정값들을 인스턴스에 사전 정의하고, 이 인스턴스를 API Call에 사용하는 기법이다

인스턴스 생성하기

const instance = axios.create( { 설정값들 (키 - 값) } );

axios.create 메서드를 호출하면 인스턴스가 뚝딱

인자로는 단일 객체를 받는데, 이 객체 안에 각종 설정들이 들어간다

앞으로 API Call 시에 사용할 기본 설정들 (baseURL, withCredentials, timeout 설정 등) 을 이 객체에 넣으면 된다

 

const axiosInstance = axios.create({
  baseURL: process.env.REACT_APP_SERVER,
  withCredentials: true,
  timeout: 10000,
});

메서드와 url, 요청에 사용할 데이터는 그때그때 달라지기 때문에, Credentials 설정과 타임아웃만 설정한 간단한 인스턴스를 생성해 주었다

인스턴스 사용하기

export async function axiosGet<ResType>(uri: string, params?: URLSearchParams | string): Promise<ResType> {
  return axiosInstance
    .get<ResType>(uri, {
      params,
    })
    .then(({ data }) => data)
    .catch(throwApiError);
}

export async function axiosPost<BodyObjType, ResType = void>(
  uri: string,
  reqData?: BodyObjType,
  options?: RawAxiosRequestConfig<BodyObjType>,
): Promise<ResType> {
  return axiosInstance
    .post<ResType>(uri, reqData, options)
    .then(({ data }) => data)
    .catch(throwApiError);
}

export async function axiosPut<BodyObjType, ResType = void>(uri: string, reqData?: BodyObjType): Promise<ResType> {
  return axiosInstance
    .put<ResType>(uri, reqData)
    .then(({ data }) => data)
    .catch(throwApiError);
}

export async function axiosDelete<BodyObjType, ResType = void>(uri: string, reqData?: BodyObjType): Promise<ResType> {
  return axiosInstance
    .delete<ResType>(uri, { data: reqData })
    .then(({ data }) => data)
    .catch(throwApiError);
}

이 인스턴스를 메서드별로 한번 더 감싼 axiosGet, axiosPost, axiosPut, axiosDelete 를 만들어 주었다

인스턴스는 export 하지 않고 함수들만 export 하므로, 한 번 캡슐화를 거쳤다고 볼 수도 있겠다

메서드별로 필요한 값이 전부 다르기 때문에 부득이하게 분리해 주었다

  • GET은 별다른 특별한 설정값이나 인자가 필요하지 않다
  • POST는 파일 업로드 기능이 필요해 때에 따라 옵션을 재조정할 수 있도록 인자를 추가로 받았다
  • PUTGET처럼 간단하지만, 요청에 데이터가 필요하기 때문에 reqData를 받을 수 있도록 하였다
  • ft_transcendence 백엔드 스펙에서 Deletedata를 객체로 한 번 감싼 형태의 요청을 받는다

또한 인자의 타입이나 반환되는 객체의 타입이 항상 다르므로 제네릭 타입을 사용해서 함수를 사용하는 곳에서 타입을 지정해줄 수 있도록 했다

 

export async function getAllUserList(): Promise<UserInfoType[]> {
  return axiosGet<UserInfoType[]>(API.USER);
}

사용할 땐 이렇게 간단하게 함수를 호출하는 것으로 axios 인스턴스를 이용한 통신을 할 수 있다

번외

아래에서는 axios 로 요청을 보낼 때 설정할 수 있는 설정값들을 알아본다

https://axios-http.com/kr/docs/req_config ← 공식문서에도 잘 나와 있긴 하다

인스턴스 설정 값: 요청 API 경로와 메서드 설정

instance.get( <여기에 들어가는 게 url> );
// 해당 위치에 url을 넣어주면 [baseURL]/[url] 위치로 요청을 쏜다
  • baseURL
    • 요청 시에 사용하는 기본 URL이다
    • 전체 url의 앞에 해당하는 부분이다
  • url
    • 요청 시에 사용하는 서버 URL이다
    • API를 구분할 때 사용하는, 전체 url에서의 뒤쪽에 해당하는 부분이다
    • API Call 시에는 필수로 지정해줘야 하나, 인스턴스에서 설정할 필요는 없고 요청을 보낼 때마다 그때그때 인자로 넣어 주면 된다
  • method
    • 요청 시에 사용할 메서드를 지정하는 부분이다
    • 이때 메서드는 멤버 함수라는 뜻의 메서드가 아니라 API Method (GET, POST, …) 임에 주의
    • 마찬가지로 인스턴스에서 설정할 필요는 없고, 요청을 보낼 때마다 설정해주는 것으로 충분
    • 기본값은 GET (get) 이다

인스턴스 설정 값: 요청 시 전달할 데이터 처리

  • data
    • body 에 들어갈 데이터이다
    • body에 데이터를 실어 보내는 메서드인 POST, PUT, PATCH, DELETE 에만 적용된다
    • 값은 통상적으로 body의 형태로 사용되는 타입이어야 한다
      • string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
      • Buffer, Stream : Node 전용
      • FormData, File, Blob: 브라우저 전용
    • transformRequest 가 설정되어 있다면, (어차피 해당 콜백 함수로 데이터를 가공하므로) 위의 타입을 지킬 필요가 없다
  • transformRequest
    • 요청의 body 에 들어갈 데이터를 서버 전송 직전에 가공해 주는 콜백 함수를 지정할 수 있다
    • body에 데이터를 실어 보내는 메서드인 POST, PUT, PATCH, DELETE 시에만 적용된다
    • 콜백 함수의 반환값은 Buffer, ArrayBuffer, FormData, Stream, string 등 통상적으로 body의 형태로 사용되는 타입이어야 한다
  • params
    • URL 패러미터 (쿼리스트링) 로 들어가는 데이터들을 지정할 수 있다
    • 객체 형태 (key - value) 또는 URLSearchParams 형태를 띄어야 한다
    • null, undefined 는 URL에 렌더링하지 않고, 알아서 걸러준다고 한다
    • 쿼리 스트링의 주의사항과 일맥상통하게, 보안적으로 위험한 데이터는 넣지 말자
  • paramsSerializer
    • 위의 params 값을 시리얼라이즈할 때 사용하는 함수를 지정할 수 있다

인스턴스 설정 값: 요청 시 사용할 헤더 및 보안 설정

  • headers
    • 헤더에 들어가는 값들을 조정할 수 있다
    • 여기에 객체를 지정함으로써 사용자 지정 헤더 (커스텀 헤더) 를 만들 수 있다는 뜻
  • xsrfCookieName
    • xsrf 토큰 값으로 사용할 쿠키의 이름이다
    • 기본값은 XSRF-TOKEN
  • xsrfHeaderName
    • xsrf 토큰 값을 전달하는 헤더의 이름이다
    • 기본값은 X-XSRF-TOKEN
  • auth
    • HTTP 프로토콜을 통한 Basic 인증을 구현할 때 사용된다
    • 헤더에 Authorization 값이 설정되며, 기존에 설정된 Authorization 값은 덮어씌워진다
    • Basic 인증만 사용 가능하며, JWT 등 그 외의 인증 방식은 사용자 지정 헤더로 따로 구현해야 한다
  • withCredentials
    • 서로 다른 도메인의 사이트 간 통신을 할 때, 요청에 자격 증명 (credentials) 을 함께 보낼 지 결정하는 옵션이다
    • Authorization 속성을 이용하여 CORS (교차 출처 리소스 공유) 밴 이슈를 해결하거나, 쿠키를 저장하고자 할 때 true 로 설정하여야 한다

인스턴스 설정 값: 응답 처리

  • transformResponse
    • 응답을 받았을 때 다음 then / catch 체인으로 넘어가기 전 해당 응답을 가공해 주는 콜백 함수를 지정할 수 있다
  • responseType
    • 응답 데이터의 유형을 정의할 수 있다
    • 기본값은 json으로, 그 외에 arrayBuffer, document, text, stream 등 서버 측과 합의 하에 데이터 형식을 지정해주면 된다
  • validateStatus
    • 특정 응답 코드에 대해 then 으로 보낼 지 (Promise resolved) catch로 보낼 지 (Promise rejected) 결정하는 콜백 함수를 지정한다
    • 콜백 함수의 반환값은 반드시 boolean이어야 한다
    • 반환값이 true 면 resolved, false면 rejected
    • 기본값은 () ⇒ {return status >= 200 && status < 300;}
  • timeout
    • 요청을 쏘아보냈을 때, 일정 시간이 지나도 응답이 돌아오지 않을 경우 시간 초과 처리된다
    • 이 때의 시간 제한을 설정할 수 있으며, 시간 초과되면 요청이 중단된다
    • 0으로 설정할 경우, 시간 제한이 사라지고 응답을 무한정 기다리게 된다
  • ~~timeoutErrorMessage~~
    • 타임아웃 걸렸을 때 반환할 메시지를 정의하는 옵션인데… 버그가 좀 있는 것 같다

인스턴스 설정 값: 그 외

  • onUploadProgress
    • 업로드 진행 이벤트를 처리하는 콜백 함수를 지정한다
    • 브라우저에서만 사용한다
  • onDownloadProgress
    • 다운로드 진행 이벤트를 처리하는 콜백 함수를 지정한다
    • 브라우저에서만 사용한다
  • responseEncoding
    • 응답 데이터를 디코딩할 때 사용할 인코딩 유형이다
    • 기본값은 utf-8 (utf8) 이다
    • Node.js 에서만 사용한다
  • maxRate
    • http 어댑터에서 사용하는 업로드 / 다운로드 속도 제한이다
    • Node.js 에서만 사용한다
  • maxBodyLength
    • http 요청 컨텐츠의 최대 크기를 지정한다
    • Node.js 에서만 사용한다
  • maxRedirects
    • 최대 리디렉션 횟수를 지정한다
    • Node.js에서만 사용한다
    • 기본값은 5
  • beforeRedirect
    • 리디렉션 전에 취할 행동을 정의한다
    • maxRedirects 가 0일 경우, beforeRedirect는 사용되지 않는다
  • decompress
    • 응답의 body 를 자동으로 압축할 것인지 지정한다
    • Node.js 에서만 사용한다
    • 기본값은 true
  • maxContentLength
    • http 응답 컨텐츠의 최대 크기를 지정한다
  • socketPath
    • Node.js에서 사용할 UNIX 소켓 경로를 정의한다
    • socketPathProxy 둘 중 하나만 지정할 수 있으며, 둘 다 지정되면 socketPath 설정이 우선된다
    • 기본값은 null
  • httpAgent, httpsAgent
    • Node.js에서 http / https 요청을 수행할 때 사용할 에이전트를 지정한다
  • proxy
    • 프록시 서버의 호스트명, 포트, 프로토콜을 지정한다
  • adapter
    • 테스트 코드를 작성할 때 사용되며, 미들웨어처럼 특정 응답이 들어오면 그에 맞는 핸들링을 할 수 있도록 설정할 수 있다
  • env
    • 페이로드를 serialize 할 FormData 클래스를 정의한다
    • 객체 안에 FormData 를 감싸서 넣으면 된다 ({ Formdata: … })
  • formSerializer
    • 폼 데이터를 가공하는 옵션
  • signal
    • Axios 요청을 AbortController 를 통해 취소할 때, 해당 AbortController 를 정의한다
  • ~~cancelToken~~
    • 요청을 취소하는 데에 사용할 취소 토큰을 지정한다
    • deprecated
  • ~~transitional~~
    • 호환성을 위한 옵션으로, 차후 버전에서 사라질 가능성이 있음
  • ~~insecureHTTPParser~~
    • 잘못된 HTTP 헤더를 가진 요청을 허용하는 insecure HTTP Parser를 어느 때에 사용할 것인지 정의하는 옵션이다
    • insecure parser의 사용은 최대한 피하는 것이 좋다고 한다

참고자료

https://axios-http.com/kr/docs/req_config

'ClientSide > 라이브러리' 카테고리의 다른 글

Socket.io로 간단한 소켓 통신  (0) 2023.07.22
Storybook, sass 붙이기 + 전역변수 사용하기  (0) 2023.06.28
Cypress로 첫 E2E 테스트 수행하기  (0) 2023.04.18
웹팩과 웹팩 설정하기  (0) 2022.09.18
commander  (0) 2022.08.02
Comments