그동안 백엔드나 서드파티 API에 네트워크 요청을 보낼 때, 나는 fetch
메서드로 HTTP 클라이언트를 사용해서 요청을 전송했었다. 왜? 처음 입문했던게 fetch
니깐. 언젠가 Axios를 배우고 이와 관련된 글을 정리하여 내 머릿속에 넣어야겠다는 생각을 하던 찰나에, 이번 회사 웹 리뉴얼 작업에서 Axios를 사용한다고 해서 글을 작성하며 학습해보고자 한다.
Axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트다. HTTP 클라이언트란, HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 프로토콜인 HTTP 통신에서, 요청(Request)을 보내 서버가 응답(Response)할 수 있도록 하는 단말기(사용자)이다.
HTTP 클라이언트의 다른 예시로는 Fetch API가 있는데, Fetch API는 fetch()
라는 메서드를 제공하며 모던 브라우저에 내장되어 있어 따로 설치할 필요가 없다.
반면에 Axios는, 서드파티 라이브러리로 CDN, npm, 혹은 yarn과 같은 패키지 매니저를 통해 설치하여 프로젝트에 추가해야 하며 브라우저 혹은 node.js 환경에서 실행할 수 있다.
Fetch 와 axios는 모두 Promise 기반의 HTTP 클라이언트이므로 이들을 이용해 네트워크 요청을 하면 이행(resolve) 혹은 거부(reject)할 수 있는 promise가 반환된다.
브라우저를 위해 XMLHttpRequests 생성
node.js를 위해 http 요청 생성
Promise API를 지원
요청 및 응답 인터셉트
요청 및 응답 데이터 변환
요청 취소
JSON 데이터 자동 변환
XSRF를 막기위한 클라이언트 사이드 지원
XMLHttpRequest
XMLHttpRequest
는 서버와 상호작용(동기 또는 비동기식 요청)을 할 때 사용되며 이를 사용하면 페이지의 새로고침 없이도 URL에서 데이터를 가져올 수 있다.
XSRF
CSRF라고도 하며, 쿠키만으로 인증하는 서비스의 취약점을 이용해, 사용자가 모르게 해당 서비스에 특정 명령을 요청하는 공격이다. Axios는 클라이언트 사이드를 지원해 이 공격을 예방할 수 있다.
npm 사용하기
$ npm install axios
yarn 사용하기
$ yarn add axios
const axios = require('axios');
// 지정된 ID를 가진 유저에 대한 요청
axios.get('/user?ID=12345')
.then(function (response) {
// 성공 핸들링
console.log(response);
})
.catch(function (error) {
// 에러 핸들링
console.log(error);
})
.then(function () {
// 항상 실행되는 영역
});
// 선택적으로 위의 요청은 다음과 같이 수행될 수 있다.
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// 항상 실행되는 영역
});
// async/await 사용을 원한다면, 함수 외부에 `async` 키워드를 추가
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 여러 동시 POST 요청 생성
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
Promise.all([getUserAccount(), getUserPermissions()])
.then(function (results) {
const acct = results[0];
const perm = results[1];
});
axios
에 해당 config를 전송하면 요청이 가능하다.
axios(config)
// POST 요청 전송
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
// node.js에서 GET 요청으로 원격 이미지 가져오기
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream'
})
.then(function (response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
다음은 요청을 만드는 데 사용할 수 있는 config 옵션들이다. 오직 url
만 필수이며 method를 지정하지 않으면 GET
방식이 기본값이다.
{
// `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 // 기본값
}
요청에 대한 응답은 아래의 정보를 가지고 있다.
{
// `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 기본값
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
커스텀 인스턴스 기본값
// 인스턴스를 생성할때 config 기본값 설정하기
const instance = axios.create({
baseURL: 'https://api.example.com'
});
// 인스턴스를 만든 후 기본값 변경하기
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 요청이 전송되었고, 서버는 2xx 외의 상태 코드로 응답했습니다.
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// 요청이 전송되었지만, 응답이 수신되지 않았습니다.
// 'error.request'는 브라우저에서 XMLHtpRequest 인스턴스이고,
// node.js에서는 http.ClientRequest 인스턴스입니다.
console.log(error.request);
} else {
// 오류가 발생한 요청을 설정하는 동안 문제가 발생했습니다.
console.log('Error', error.message);
}
console.log(error.config);
});
보통 Axios는 JSON을 사용한다. application/x-www-form-urlencoded
포맷으로 데이터를 전송하려면, 다음 옵션 중 하나를 사용할 수 있다.
브라우저에서는 URLSearchParams
API를 사용할 수 있다.
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
대안으로 qs 라이브러리를 사용하여 데이터를 인코딩할 수 있다.
import qs from 'qs';
const data = { 'bar': 123 };
const options = {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data),
url,
};
axios(options);