치춘짱베리굿나이스
axios - instance 사용하기 본문
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 함수들이 then
과 catch
에서의 로직이 중복되고, 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
는 파일 업로드 기능이 필요해 때에 따라 옵션을 재조정할 수 있도록 인자를 추가로 받았다PUT
은GET
처럼 간단하지만, 요청에 데이터가 필요하기 때문에reqData
를 받을 수 있도록 하였다- ft_transcendence 백엔드 스펙에서
Delete
는data
를 객체로 한 번 감싼 형태의 요청을 받는다
또한 인자의 타입이나 반환되는 객체의 타입이 항상 다르므로 제네릭 타입을 사용해서 함수를 사용하는 곳에서 타입을 지정해줄 수 있도록 했다
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 소켓 경로를 정의한다
socketPath
와Proxy
둘 중 하나만 지정할 수 있으며, 둘 다 지정되면socketPath
설정이 우선된다- 기본값은
null
httpAgent
,httpsAgent
- Node.js에서 http / https 요청을 수행할 때 사용할 에이전트를 지정한다
proxy
- 프록시 서버의 호스트명, 포트, 프로토콜을 지정한다
adapter
- 테스트 코드를 작성할 때 사용되며, 미들웨어처럼 특정 응답이 들어오면 그에 맞는 핸들링을 할 수 있도록 설정할 수 있다
env
- 페이로드를 serialize 할
FormData
클래스를 정의한다 - 객체 안에
FormData
를 감싸서 넣으면 된다 ({ Formdata: … }
)
- 페이로드를 serialize 할
formSerializer
- 폼 데이터를 가공하는 옵션
signal
- Axios 요청을
AbortController
를 통해 취소할 때, 해당AbortController
를 정의한다
- Axios 요청을
~~cancelToken~~
- 요청을 취소하는 데에 사용할 취소 토큰을 지정한다
- deprecated
~~transitional~~
- 호환성을 위한 옵션으로, 차후 버전에서 사라질 가능성이 있음
~~insecureHTTPParser~~
- 잘못된 HTTP 헤더를 가진 요청을 허용하는 insecure HTTP Parser를 어느 때에 사용할 것인지 정의하는 옵션이다
- insecure parser의 사용은 최대한 피하는 것이 좋다고 한다
참고자료
'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 |