본문 바로가기

Backend/Node.js

axios configs 정리

- 다양한 http 라이브러리

 

Node.Js 프로젝트를 진행하다보면 다른 서버와의 http 통신이 필요한 상황은 거의 무조건 오는데,

이때 사용 가능한 라이브러리는 axios, got, bent, node-fetch-npm 등 여러가지가 있습니다.

 

일전에 저도 이 다양한 라이브러리 중 어느것을 사용하는게 좋을지 확인하기 위해 비교 테스트를 진행해 포스팅한 적이 있습니다 (링크)

아무래도 가장 널리 쓰이던 라이브러리는 request 가 아니었을까 생각합니다. 해당 라이브러리에 베이스를 둔 request-promise 또한 많이 쓰였습니다. 하지만 작년 2월 request 라이브러리는 완전히 deprecated 되었고, 사내에서 관리중인 프로젝트에서도 조금씩 라이브러리 교체 작업을 진행해왔습니다.

 

여러 라이브러리를 비교 후 결국 production 레벨에서 선택한건 axios 입니다. 다른 라이브러리들은 너무 무겁거나, 과도하게 기능이 많거나, 너무 기능이 부족했습니다. (axios 도 나름 오래된 라이브러리고 많이 사용되는데, 왜 아직 메인 버전이 0.x 인지는 의문입니다..)

 

주로 request, request-promise 를 axios 로 변경하는 작업을 진행하며, axios config 에도 다양한 옵션이 있다는 점을 알게 되었습니다. 따라서 이번엔 해당 옵션들에 대해 어떤 기능을 하는지 정리하는 포스팅을 해 볼 생각입니다.

 

- axios configs

 

axios({
  url: 'http://localhost:4000/users',
});

 

• url : 요청에 사용될 서버 URL 입니다.

 

axios({
  method: 'get',
});

 

• method : 요청에 사용될 메소드이며, default 값은 get 입니다. 대문자도 가능합니다.

 

axios({
  url: '/users',
  baseURL: 'http://localhost:4000/',
});

 

• baseURL : url 값이 절대 URL 이 아니라면, 그 앞에 설정한 baseURL 이 붙습니다. 로컬에서 개발시엔 별 문제 없지만, production 레벨에선 이 값을 설정하는게 좀 더 편할 수 있습니다.

 

axios({
  data: { id: 1 },
  transformRequest: [function(data, headers) {
    const payload = { ...data, key: 'some-key' };
    return qs.stringify(payload);
  }],
});

 

• transformRequest :  이 기능은 언제 쓸까 싶긴 하지만, 서버에 요청을 보내기 전에 데이터를 변환할 수 있습니다. 배열의 형태로 여러 변환 함수를 넣을 수 있으며, 배열의 마지막 함수는 버퍼의 문자열이나 인스턴스를 반환해야 합니다. (이 기능은 post, put, patch 메소드에만 적용 가능합니다)

위처럼 { id: 1 } 데이터에 key: 'some-key' 라는 값을 추가해 요청을 보낼 수 있습니다.

 

axios({
  transformResponse: [function(data) {
    const tmpData = JSON.parse(data);
    return tmpData.data.map((item, i) => {
      item.index = 1;
      return item;
    });
  }],
}).then(result => {
  console.info(result.data); // [ { id: 1, index: 0 }, { id: 2, index: 1 } ]
});

 

• transformResponse : 이 기능 또한 쓸 일이 있나 싶지만, 서버에서 받은 데이터를 변경해서 then/catch 로 전달합니다.

위처럼 원래 [{ id: 1 }, { id: 2 }] 로 응답받은 데이터를 transformResponse 를 사용해 index 를 추가하여 전달한 예시입니다.

 

axios({
  headers: { 'X-Access-Token': 'access-token', 'Accept-Encoding': 'gzip' },
});

 

• headers : 서버에 전송될 사용자 정의 헤더입니다. gzip 압축을 허용한다는 Accept-Encoding 이나 커스텀 헤더를 보낼 수 있습니다.

 

axios({
  url: 'http://localhost:4000/',
  params: { key: 3 },
});

 

• params : 요청 URL 과 같이 전송될 매개 변수입니다. 위처럼 보낼 경우, http://localhost:4000?key=3 이렇게 요청을 보낸 것과 동일합니다.

 

axios({
  params: { key: [1, 2, 3] },
  paramsSerializer: paramObj => {
    const params = new URLSearchParams();
    for (const key in paramObj) {
        params.append(key, paramObj[key])
    }

    return params.toString();
  },
});

 

• paramsSerializer : 위에서 보낸 params 를 직렬화하는 옵션 함수입니다. 만약 params 에 배열을 전송한다면, 요청을 받는 서버 쪽에선 { 'key[]': [ '1', '2', '3' ] } 의 형태로 받아 'key[]' 를 파싱해야 합니다. 하지만 위처럼 직렬화 함수 설정을 통해 처리한다면 요청을 받는 서버쪽에선 { key: '1,2,3' } 의 형태로 params 데이터를 받을 수 있습니다.

 

axios({
  data: { id: 3 },
});

 

• data : request body 로 전송할 데이터입니다. put, post, patch 메소드에만 적용 가능합니다.

 

axios({
  timeout: 1000,
});

 

• timeout : 요청이 타임아웃되는 ms 를 지정합니다. 기본값은 0 으로 타임아웃이 적용되지 않습니다.

요청이 해당 설정 시간보다 지연될 경우 요청은 중단되며, catch 로 에러가 전달됩니다.

 

axios({
  withCredentials: false,
});

 

• withCredentials : cross-site Access-Control 요청이 필요한 경우 해당 값을 true 로 설정하여 사용합니다.

사용 예시로는 서버와 클라이언트의 도메인이 서로 다르면 쿠키 전송이 되지 않는데, 이런 경우 사용할 수 있습니다.

 

const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');

axios({
  url: 'http://localhost:4000/',
  adapter: config => {
    return new Promise((resolve, reject) => {
      httpAdapter(config).then(response => {
        if (response.status === 200) {
          response.status = 503;
        }
        settle(resolve, reject, response);
      }).catch(reject);
    });
  },
}).catch(err => {
  console.error(err); // Error: Request failed with status code 503 ...
});

 

• adapter : 별도의 커스텀 adapter 를 추가해서 요청에 대해 커스텀 핸들링한 응답을 받을 수 있습니다.

위 예시에선 response status 가 200 일 경우 503 으로 세팅해 응답하도록 adapter 를 추가했고, 그 결과 정상적으로 왔을 응답이 503 에러로 처리되어 catch 에서 찍히는것을 확인할 수 있습니다. (adapter 는 Promise 를 반환해야 합니다)

 

axios({
  auth: {
    username: 'xxx',
    password: 'xxxx',
  },
});

 

• auth : HTTP 기본 인증이 사용되며 credentials 를 제공함을 나타냅니다. 기존의 Authorization 헤더를 덮어쓰는 헤더 설정이 들어가게 됩니다. auth 를 설정하지 않았을 경우와 비교했을 때, 요청 받은 서버쪽에서 그 차이를 아래와 같이 확인할 수 있습니다.

 

auth 를 설정하지 않은 경우
auth 를 설정한 경우

 

axios({
  responseType: 'json',
});

 

• responseType : ['arraybuffer', 'blob', 'document', 'json', 'text', 'stream'] 이 값들 중 서버에서 응답할 데이터 타입을 설정할 수 있습니다. 디폴트는 'json' 입니다.

 

axios({
  responseEncoding: 'utf8',
});

 

• responseEncoding : 응답 디코딩에 사용할 인코딩을 나타내며, 'utf8' 이 디폴트입니다. responseType 이 stream 인 경우엔 해당 설정을 무시합니다.

 

axios({
  xsrfCookieName: 'XSRF-TOKEN',
});

 

• xsrfCookieName : xsrf 토큰 값으로 사용할 쿠키 이름입니다.

 

axios({
  xsrfHeaderName: 'X-XSRF-TOKEN',
});

 

• xsrfHeaderName : xsrf 토큰값을 운반하는 HTTP 헤더 이름입니다.

 

# 위에서 나오는 xsrf 는 csrf (Cross Site Request Forgery) 라고도 하며 사용자의 의도와 관계없이 행해지는 공격 기법인데, 예를 들어 공격자가 심어놓은 스크립트에 의한 것이 있을 수 있고 이를 방어하기 위해 csrf(xsrf) token 이 사용됩니다.

 

axios({
  onUploadProgress: progressEvent => {
    ...
  },
  onDownloadProgress: progressEvent => {
    ...
  },
});

 

• onUploadProgress : 업로드 프로그레스 이벤트를 처리합니다.

• onDownloadProgress : 다운로드 프로그레스 이벤트를 처리합니다.

위 두 옵션은 브라우저에서만 사용되는 옵션이므로 더 파보진 않겠습니다. native progressEvent 를 받아 ui 상에서 처리할 수 있는 코드 추가가 가능한 것으로 보입니다.

 

axios({
  url: 'http://localhost:4000/',
  maxContentLength: 1,
}).catch(err => {
  console.error(err); // Error: maxContentLength size of 1 exceeded
});

 

• maxContentLength : 응답 컨텐츠의 최대 크기를 설정하며, 단위는 Bytes 입니다.

위 예시처럼 maxContentLength 값을 설정하고 요청을 보낼 경우 응답 컨텐츠의 사이즈가 설정값을 넘어 catch 에서 처리되는 것을 확인할 수 있습니다.

 

axios({
  maxBodyLength: 2000,
});

 

• maxBodyLength : http 요청 컨텐츠의 최대 크기를 설정하며, 단위는 Bytes 입니다.

이 옵션은 Node.Js 에서만 사용할 수 있습니다.

 

axios({
  validateStatus: status => {
    // return status >= 200 && status < 300; - default
    return status !== 200;
  },
});

 

• validateStatus : 응답 상태 코드에 대해 true | false 로 promise 를 resolve 하거나 reject 합니다.

기본값으로 axios 는 2xx 대의 응답 코드에 대해선 promise 를 resolve 합니다. 하지만 응답코드 200 외에는 전부 에러로 처리하고 싶다면 위처럼 커스텀 validateStatus 함수를 작성할 수 있습니다.

 

axios({
  maxRedirect: 5,
});

 

• maxRedirect : Node.Js 에서 리다이렉션이 가능한 최대 갯수를 정의하며, 0 으로 설정하면 리다이렉션이 되지 않습니다.

 

axios({
  socketPath: null,
});

 

• socketPath : Node.Js 에서 사용될 UNIX 소켓을 정의합니다. null 이 기본값이며, 예를 들어 '/var/run/docker.sock' 로 설정하면 docker daemon 으로 요청을 보냅니다.

 

axios({
  proxy: {
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'xxx',
      password: 'xxxx',
    },
  },
});

 

• proxy : 프록시 서버를 설정하는 옵션입니다. 옵션 혹은 환경 변수를 무시하고 프록시를 사용하지 않으려면 false 로 설정합니다.

axios 옵션에선 socketPath, proxy 둘 중 하나만 사용할 수 있습니다. 만약 둘 다 지정되면, socketPath 가 사용됩니다.

 

axios({
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),
});

 

• httpAgent, httpsAgent : Node.Js 에서 http, https 요청을 보낼 때 사용할 커스텀 에이전트를 정의할 수 있습니다.

기본적으로 Node.Js 의 http, https 에이전트는 모든 새 요청에 대해 새로운 tcp 연결을 생성합니다. 이러한 새 연결 설정 비용이 들지 않게 하기 위해 { keepAlive: true } 와 같은 옵션으로 기존 연결을 재사용하게끔 설정할 수 있습니다.

 

const axios = require('axios');
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios({
  url: 'http://localhost:4000/',
  cancelToken: source.token,
}).catch(err => {
  console.error(err); // Cancel { message: 'Want to cancel' }
  console.info(axios.isCancel(err)); // true
});

source.cancel('Want to cancel');

 

• cancelToken : axios 의 CancelToken 을 사용해 요청을 취소할 수 있습니다. (자세한 내용 링크)

위 예시처럼 cancelToken 을 넣어 보내면, 요청이 취소되어 reject 로 처리되고 그 에러가 cancel 된 요청인지도 확인할 수 있습니다.

위 코드에서 마지막 라인을 제거하면, 요청은 cancelToken 이 있더라도 취소되지 않습니다.

 

axios({
  decompress: true,
});

 

• decompress : 응답 바디를 자동으로 압축을 풀 것인지를 나타내는 옵션입니다. 기본값은 true 입니다.

이 옵션은 Node.Js 에서만 사용 가능합니다.

 

대부분 몰랐던 옵션들이고, 대체로 사용할 것 같진 않지만 몇몇 유용해 보이는 옵션도 있었습니다.

여기까지 axios configs 에 대한 정리를 마치도록 하겠습니다. (참고 링크)