본문 바로가기

# DevNote

HTTP 메세지와 연관지어 Axios 사용법 이해하기

Axios란?

Axios는 자바스크립트 위에서 동작하는 HTTP(S) Request Library입니다. 자바스크립트 위에서 돌아간다는 것은 프론트엔드백엔드에서도 사용할 수 있다는 것이므로, 한 번 사용법을 익혀두면 HTTP 요청에 대해서는 더 이상 고민할 것이 없어집니다. 다만, 사용법을 아는 것사용법을 이해하는 것은 차이가 꽤 크죠. 여기서는 HTTP 메세지 구조와 연관지어 Axios의 사용법을 이해하여 보겠습니다.


Request

요청

HTTP 요청 메세지의 첫 번째, 두 번째 라인은 다음 정보를 포함하고 있어야 합니다.

  • 요청 메서드
  • 요청 경로명
  • 호스트 주소

GET :

GET /robots.txt HTTP/1.1 
Host: naver.com
await Axios({
    method: "GET",
    url: "https://naver.com/robots.txt",
});

POST :

POST / HTTP/1.1 
Host: comic.naver.com
await Axios({
    method: "POST",
    url: "https://comic.naver.com",
});

파라미터

부가적인 요청 데이터를 전달할 수 있게 도와줍니다. 경로명 뒷 부분에서 ?으로 시작하여 &으로 분리되고 = 기호로 Key-Value가 연결된 형태입니다. 영어 알파벳이 아닌 문자들은 Percent Character로 인코딩됩니다.

https://search.naver.com/search.naver?query=안녕&ie=utf8
GET /search.naver?query=%EC%95%88%EB%85%95&ie=utf8 HTTP/1.1 
Host: search.naver.com

Axios에서 params 객체를 명시적으로 전달하여, 추가적인 파라미터를 설정할 수 있습니다.

await Axios({
    method: "GET",
    url: "https://search.naver.com/search.naver",
    params: {
        ie: "utf8",
        query: "안녕",
    },
});

헤더

부가적인 연결 데이터를 전달할 수 있게 도와줍니다. 두 번째 줄부터 개행으로 분리되고 :기호로 Key-Value가 연결된 형태입니다. 예를 들어, 다음과 같은 관리자 페이지를 가정해봅시다.

http://localhost:4000/admin

관리자 페이지 이하에서 발생하는 모든 커넥션은 권한 인증이 필수적으로 요구되는데, 이러한 역할을 하는 추가적인 키값(여기서는 Access-Token)을 헤더에 넣어 관리할 수 있습니다.

GET /admin HTTP/1.1 
Host: localhost:4000
Access-Token: 54321

Axios에서 headers 객체를 명시적으로 전달하여, 추가적인 헤더를 설정할 수 있습니다.

await Axios({
    method: "GET",
    url: "https://search.naver.com/search.naver",
    headers: {
        "Access-Token" : 54321
    },
});

헤더를 사용하지 않고 전부 파라미터에 넣어버리면 되지 않느냐는 질문이 있을 수 있는데, 관례적으로 파라미터어떤 데이터를 원하는지에 대한 정보이고, 관례적으로 헤더어떤 연결을 수립할 것인지에 대한 부가적인 정보입니다. 물론 이것은 관례적인 것이라 강제성은 없습니다.


바디

요청 메시지에 큰 데이터를 담아 전달할 수 있습니다. 요청 메세지의 하단부에 정의되며 Content-Type 헤더값에 따라 해석하는 방법이 다르기 때문에, 송신측과 수신측이 서로 동일한 컨텐츠 타입을 사용해야 합니다. 여기서는 x-www-form-urlencodedform-data 타입에 대해서만 설명합니다.


x-www-form-urlencoded

풀네임은 application/x-www-form-urlencoded입니다. &으로 분리되고 = 기호로 Key-Value가 연결된 형태입니다. 영어 알파벳이 아닌 문자들은 Percent Character로 인코딩됩니다. 바디 분자열의 크기를 Content-Length 헤더에 명시해야 합니다.

POST / HTTP/1.1
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom

Axios에서 data 객체를 명시적으로 전달하여, 바디를 설정할 수 있습니다.

await axios({
    method: "POST",
    url: `foo.com`,
    headers: {
        "Content-Type" : "application/x-www-form-urlencoded",
        // "Content-Length"은 자동으로 계산되어 삽입된다.
    },
    data: {
        say:"hi",
        to:"mom"
    },
});

form-data

풀네임은 multipart/form-data입니다. boundary로 구분되는 Key-Value 튜플 입니다. 각 Value문자열 또는 파일을 가르킬 수 있습니다. 파일 업로드와 같은 상황에서 주로 사용됩니다.

POST /test.html HTTP/1.1 
Host: example.org 
Content-Type: multipart/form-data;boundary="--xxx" 

--xxx 
Content-Disposition: form-data; name="field1" 

value1 
--xxx 
Content-Disposition: form-data; name="field2"; filename="example.txt" 

value2
--xxx

data 객체를 명시적으로 전달하여 바디를 설정하는 것은 같습니다. 하지만 데이터를 직접 생성하는 것 보다는 FormData 라이브러리를 사용하는 것이 좋습니다.

$ npm install form-data
//
// FormData 생성
const formData = new FormData();
formData.append("text", STRING_OBJECT);
formData.append("file", READ_STREAM_OBJECT);

//
// 
await axios({
    method: "POST",
    url: `https://www.tistory.com/apis/post/attach`,
    headers: {
        //
        // form-data headers with boundary
        ...formData.getHeaders(),
    },
    data: formData,
});

파일을 업로드하기 위해서는 Read Stream을 인자로 전달해야 합니다. 로컬 파일을 업로드하려면 fs을 사용하고, 인터넷에 있는 파일을 업로드하려면 Axios의 반환형식을 stream으로 하여 데이터를 읽어오면 됩니다.

const filePath = "https://www.naver.com/favicon.ico";
const formData = new FormData();
let readStream;
if (filePath.startsWith("http")) {
    //
    // 인터넷에 있는 파일을 업로드
    readStream = (await axios({
        method: "GET",
        url: filePath,
        responseType: "stream",
    })).data
} else {
    //
    // 로컬에 있는 파일을 업로드
    readStream = fs.createReadStream(filePath);
}
formData.append("uploadedfile", readStream);

Response

반환형식 변경

AxiosResponse Message를 읽어 특정한 포맷으로 변환해줍니다. responseType 옵션을 지정하면 어떤 포맷으로 변환할지 설정할 수 있습니다.

export type ResponseType = 
  | 'arraybuffer' 
  | 'blob' 
  | 'document' 
  | 'json' 
  | 'text' 
  | 'stream'
await axios({
        method: "GET",
        url: "https://naver.com",
        responseType: "stream",
    })
).data;

리다이렉트 방지

기본적으로 Axios는 리다이렉트 상태코드인 302를 만나면, 다시 리다이렉트된 주소로 요청을 시도합니다. 이것을 막으려면 maxRedirects를 0으로 설정하면 됩니다.

await Axios({
    method: "GET",
    url: "...",

    //
    // 리다이렉트 방지
    maxRedirects: 0,
});

유효 상태코드 변경

기본적으로 Axios는 오류 상태코드인 4xx, 5xx, ...를 만나면 에러를 발생시킵니다. 하지만 에러가 발생하는게 당연한 상황인 경우가 있을겁니다. 유효한 상태코드를 변경하려면 validateStatus(stat)를 설정하면 됩니다. 이 함수가 false일 때만 에러가 발생합니다.

await Axios({
    method: "GET",
    url: "...",
    maxRedirects: 0,
    validateStatus: function (stat: number) {
        return stat === 302;
    },
});

쿠키값 가져오기

서버가 클라이언트 측에 쿠키를 설정하기 위해서는 응답 메세지 헤더set-cookie를 설정해야 합니다.

Set-Cookie: x=1; HttpOnly; Path=/
Set-Cookie: y=2; HttpOnly; Path=/
Set-Cookie: z=3; HttpOnly; Path=/

이것을 역이용하면 응답 메세지 헤더에서 서버가 설정하고자 하는 쿠키 목록들을 가져올 수 있습니다.

const res = await Axios({
    ...
});

const cookies : string[] = res["headers"]["set-cookie"];
// cookies[0] = "x=1; HttpOnly; Path=/"
// cookies[1] = "y=2; HttpOnly; Path=/"
// cookies[2] = "z=3; HttpOnly; Path=/"