Skip to content

Tags

나와 Fetch와 Axios
×
문서 생성 시각 2022-09-21 18:19:15 최근 수정 시각 2022-09-22 09:53:07

나와 Fetch와 Axios

는 그동안 HTTP 요청을 하기 위해 배운대로 Fetch API 를 써왔다.

어떤 튜토리얼에서 Axios를 사용하는 것을 따라해봤고, 다른 개발자 분들도 Axios를 사용하는 걸 지켜봤다. 기회가 생겨 어떤 점이 좋은지 여쭤봤다.

fetch를 사용할 때보다 편리한 점이 있어요.

편리한 점이 어떤 것일까? 이걸 알아내는 것은 내 몫이다.

하지만 지난 여름, 그걸 알아보기도 전에 사용을 먼저 해버렸다.
그래서 조금 늦었지만 둘의 차이를 비교하고, 아니 XMLHttpRequest까지 셋의 차이를 알아본 뒤 사용하려고 한다.

요청하기

XMLHttpRequest

XMLHttpRequest의 HTTP 요청 과정은 다음과 같다.

  1. 새로운 XMLHttpRequest 객체 생성하기
  2. HTTP 메서드와 URL을 전달해 요청을 초기화하기
  3. 요청 구성하기
  4. 요청 전송하기
const request = params => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
const { method = 'GET', url, headers = {}, body } = params
xhr.open(method, url)
setHeaders(xhr, headers)
xhr.send()
})
}

JSON 데이터를 전송하기 위해선 직렬화해줘야 한다.

xhr.send(JSON.stringify(body))

Fetch API

Fetch API는 fetch()Promise 객체를 반환하기 때문에 XMLHttpRequest보다 읽기 쉽다.
XMLHttpRequest와 마찬가지로 전송 데이터는 JSON.stringify를 사용해 직렬화해야 한다.

const request = async params => {
const { method = 'GET', url, headers = {}, body } = params
const config = {
method,
headers: new window.Headers(headers),
}
if (body) {
config.body = JSON.stringify(body) // 직렬화
}
const response = await window.fetch(url, config)
// ...
}

Axios

Axios는 라이브러리로 Fetch API처럼 Promise 기반이다.

세 가지 방법 중 가장 읽기가 쉽다. 요청시 POST 메서드를 이용해서 JavaScript 객체를 전송하면 axios가 알아서 데이터를 문자열로 변경해준다!
전송하고자 하는 데이터는 data 프로퍼티에 할당하면 된다.

const request = async params => {
const { method = 'GET', url, headers = {}, body } = params
const config = {
url,
method,
headers,
data: body,
}
return axios(config)
}

응답받기

XMLHttpRequest

XMLHttpRequest의 응답과정은 다음과 같다.

  1. 요청 완료까지 대기하기
    1. 요청이 성공적으로 끝나면 onload 호출
    2. 요청이 오류로 끝나면 onerror 호출
    3. 요청이 timeout으로 끝나면 ontimeout 콜백 호출
xhr.onerror = () => {
reject(new Error('Timeout Error'))
}
xhr.onload = () => {
resolve(parseResponse(xhr))
}
xhr.ontimeout = () => console.log(`timeout`)

여기서 응답이 성공했을 때, 응답 데이터가 존재하고 (HTTP 상태 코드204가 아닌 경우) 라면, JSON.parse로 역직렬화해야 한다. 그러면 JSON 포맷으로 데이터를 가져올 수 있다.

const parseResponse = xhr => {
const { status, responseText } = xhr
let data
if (status !== 204) {
data = JSON.parse(responseText)
}
return {
status,
data,
}
}

Fetch API

fetch()Promise를 반환한다. 그런데 JSON 데이터 포맷이 아니기 때문에 .json() 메서드를 호출해야 하고, 그러면 JSON 포맷으로 resolve된 또 다른 Promise를 반환한다.

// ...
const response = await window.fetch(url, config)
let data
const { status } = response
if (status !== 204) {
data = await response.json()
}
return {
status
data,
}

Axios

Axios는 응답 데이터를 요청할때처럼 기본적으로 JSON 타입으로 사용이 가능하다. 응답 객체의 data 프로퍼티를 통해 응답 데이터를 얻을 수 있다.

const url = 'https://jsonplaceholder.typicode.com/posts/1'
const config = {
url,
method: 'GET'
}
const response = await axios(config)
console.log(response.data) // 응답 데이터

에러처리

XMLHttpReqeust

XMLHttpRequest 참고

Fetch API

Fetch는 HTTP 에러 응답을 받았다고 해서 Promisereject하지 않는다. 네트워크 장애가 발생한 경우에만 reject한다고 한다. 그래서 .then을 사용해서 수동으로 에러 처리를 해야 한다.

try {
const response = await window
.fetch(url, config)
.then(response => {
if (!response.ok) { // 응답을 확인해서 error 발생시키기
throw new Error(`HTTP Error: ${response.status}`)
}
return response
})
.catch(err => {
throw new Error(err.message)
})
return parseResponse(response)
} catch (err) {
console.log(err)
}

Axios

Axios는 HTTP 상태 코드가 2xx를 넘어가면 모두 reject한다고 한다. 에러 객체에 response 또는 request 프로퍼티가 포함되어있는지 확인해서 상세한 정보를 얻을 수 있다.

const url = 'https://jsonplaceholder.typicode.com/posts/1'
const config = {
url,
method: 'GET'
}
axios(config).then(response => response)
.catch(err => {
const { response, request } = err
if (response) {
const { status, config } = response
if (status === 404) {
console.log(`${config.url} not found`)
}
if (status === 500) {
console.log(`Server Error`)
}
} else if (request) {
console.log(`No Response`, err.message)
} else {
console.log(`Error`, err.message)
}
})

응답 시간 초과와 요청 취소 처리하기

XMLHttpRequest

XMLHttpRequest 객체의 timeout 프로퍼티에 요청이 종료될 때까지의 시간을 밀리초로 지정할 수 있다.

const request = params => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
const { method = 'GET', url, headers = {}, body } = params
xhr.open(method, url)
xhr.timeout = 10
setHeaders(xhr, headers)
xhr.send()
})
}

요청이 정해진 시간 내에 처리되지 않는 경우 ontimeout 콜백이 호출된다.

xhr.ontimeout = () => console.log(`timeout`)

Fetch

Fetch는 요청 취소를 위해 AbortController 인터페이스를 사용할 수 있다.

const url = 'https://jsonplaceholder.typicode.com/posts/1'
const controller = new AbortController()
const signal = controller.signal
setTimeout(() => controller.abort(), 3000)
fetch(url, { signal })
.then((response) => response.json())
.then(console.log)
.catch((err) => {
console.error(err.message)
})

controller 객체 생성 후 controllersignal 객체를 fetch()에 전달한다.
이렇게하면 controller.abort() 메서드가 호출될 때마다 fetch 요청이 종료된다고 한다. 위 코드는 3초 이내에 응답이 없으면 종료될 것이다.

응답을 기다리는 시간을 10으로 설정하고 확인해보니, catch 절에서 다음 에러가 발생하는 것을 확인할 수 있었다.

The operation was aborted.

Axios

Axios는 XMLHttpRequest 처럼 timeout 속성을 요청시 전달하면 설정할 수 있었다.

const url = 'https://jsonplaceholder.typicode.com/posts/1'
const config = {
url,
method: 'GET',
timeout: 4000 // 요청을 기다리는 시간
}
axios(config)

fetch() 처럼 값을 10을 넘겨 테스트해보니, 요청은 했으나 응답이 없는 경우에 timeout of 10ms exceeded라는 에러를 확인할 수 있었다.

axios(config)
.then(response => response)
.catch(err => {
const { response, request } = err
if (response) {
const { status, config } = response
if (status === 404) {
console.log(`${config.url} not found`)
}
if (status === 500) {
console.log(`Server Error`)
}
} else if (request) { // 에러 발생!
console.log(`No Response`, err.message)
} else {
console.log(`Error`, err.message)
}
})

생각

이 글에서 정리하려고 한 것은 XMLHttpRequest, Fetch API, Axios의 차이점이었다. 그리고 Axios가 어떤점에서 편리한 것인지 궁금했다.

확실히 JSON 데이터를 알아서 잘 변환해줘서 편리한 것이 있었고, 에러처리도 다른 두 가지에 비해 더 에러에 대해 상세하게 확인할 수 있다는 걸 알게되었다.

다른 자료를 살펴보니 속도는 편리함과는 반대로 XMLHttpReqeust > Fetch API > Axios 순이었다.

어쨌든 이렇게 비교해보니 앞으로는 Axios를 사용하지 않을까 싶다.

최근에 읽은 [[프레임워크 없는 프론트엔드 개발]]에 나온 팁 중에

라이브러리를 사용할 때는 항상 이에 대한 인터페이스를 생성하라. 필요시 새로운 라이브러리로 쉽게 변경할 수 있다.

라는 팁이 있었다. 여기 코드에서도 앞 부분에 request라는 wrapper 함수를 사용했는데 책에서 참고한 것이다.
언젠가 Axios보다 더 편리하고 프로젝트에 맞는 라이브러리가 나타났을 때, 인터페이스를 사용해 코드를 작성해놨다면 쉽게 변경할 수 있을 것 같다.
이 사실을 잘 기억해둬야 겠다!

참고