JS

HTTP 요청하기 & 에러 처리 (+ Axios)

2024. 9. 5. 14:37

HTTP 요청 방식은 크게 두 가지가 있다.

  • XMLHttpRequest
    : 오래된 브라우저에서도 사용 가능
    : 에러 처리 코드 포함, 코드가 직관적
    : 프로미스 코드는 별도로 추가해줘야 함

  • fetch API
    : 최신 문법인 fetch API를 이용해서 위의 'sendHttpRequest' 함수를 보다 간략하게 작성할 수 있다.
    : fetch API 자체가 프로미스를 리턴하므로 별도 프로미스 선언을 하거나 적용할 필요 없다.
    : 브라우저 호환성이 떨어진다. (ex: IE)

  • Axios
    : HTTP 통신을 편하게 할 수 있는 JS 라이브러리인데, 아래에 써놓았으니 일단 기초인 위 두 방식부터 알고 가자.

 

JS (XMLHttpRequest ver.)

const listElement = document.querySelector(".posts");
const postTemplate = document.getElementById("single-post");
const form = document.querySelector("#new-post form");
const fetchButton = document.querySelector("#available-posts button");

// HTTP 요청 보내기 boilerplate
function sendHttpRequest(method, url, data) {
  const promise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.responseType = "json";

    // xhr.onerror에서는 일부 요청 에러만 처리해주기 때문에, onload에서 추가로 별도 에러 처리 설정.
    xhr.onload = function () {
      if (xhr.status >= 200 && xhr.status < 300) {
        // 서버 요청 정상 응답
        resolve(xhr.response);
      } else {
        // 에러 처리
        console.log(xhr.response);
        reject(new Error("Something went wrong!"));
      }
    };

    // XMLHttpRequests.onerror() : 네트워크 오류(요청 전송 실패) 또는 요청 시간 초과 시에만 작동. 요청에는 성공했으나 주소가 틀려서 실패하는 경우(응답 반환O)에는 작동 X.
    xhr.onerror = function () {
      reject(new Error("Failed to send request!"));
    };

    xhr.send(JSON.stringify(data)); // for "POST". 인자로 받은 data를 JSON으로 변환.
  });

  return promise;
}

// promise ver
// function fetchPosts() {
//   sendHttpRequest("GET", 'https://jsonplaceholder.typicode.com/posts').then(responseData => {
//     const listOfPosts = responseData;
//     for (const post of listOfPosts) {
//       const postEl = document.importNode(postTemplate.content, true);
//       postEl.querySelector('h2').textContent = post.title.toUpperCase();
//       postEl.querySelector('p').textContent = post.body;
//       listElement.append(postEl);
//     }
//   })
// }

// async await ver
async function fetchPosts() {
  const responseData = await sendHttpRequest(
    "GET",
    "https://jsonplaceholder.typicode.com/posts"
  );
  const listOfPosts = responseData;
  for (const post of listOfPosts) {
    const postEl = document.importNode(postTemplate.content, true);
    postEl.querySelector("h2").textContent = post.title.toUpperCase();
    postEl.querySelector("p").textContent = post.body;
    postEl.querySelector("li").id = post.id;
    listElement.append(postEl);
  }
}

// "POST" 요청
async function createPost(title, content) {
  const userId = Math.random();
  const post = {
    title: title,
    body: content,
    userId: userId,
  };

  sendHttpRequest("POST", "https://jsonplaceholder.typicode.com/posts", post);
}

// FETCH 요청
fetchButton.addEventListener("click", fetchPosts);
form.addEventListener("submit", (e) => {
  e.preventDefault();
  let enteredTitle = form.querySelector("#title").value;
  let enteredContent = form.querySelector("#content").value;
  createPost(enteredTitle, enteredContent);
});

// DELETE 요청
listElement.addEventListener("click", function (e) {
  let postId = e.target.closest("li").id;
  if (e.target.textContent == "DELETE") {
    sendHttpRequest(
      "DELETE",
      `https://jsonplaceholder.typicode.com/posts/${postId}`
    );
    e.target.closest("li").remove();
  }
});

 

JS (fetch API ver.)

function sendHttpRequest(method, url, data) {
  return fetch(url, { method: method, body: JSON.stringify(data) }).then(
    (response) => {
      // XHR 방식의 onload에서 에러 처리하는 부분
      if (response.status >= 200 && response.status < 300) {
      	return response.json(); // json으로 변환 안 하면 iterable한 객체로 인식 못함
      } else {
      	return response.json().then(err => {
        	console.log(err);
            throw new Error('Something went wrong! - Server Side');
        })
      }
  })
  // XHR 방식의 onerror에서 에러 처리하는 부분
  .catch(error => {
  	console.log(error);
    throw new Error('Something went wrong!');
  }); 
}

// fetchPosts()에 try catch 구문 추가
async function fetchPosts() {
  try {
    const responseData = await sendHttpRequest(
        "GET",
        "https://jsonplaceholder.typicode.com/posts"
      );
      const listOfPosts = responseData;
      for (const post of listOfPosts) {
        const postEl = document.importNode(postTemplate.content, true);
        postEl.querySelector("h2").textContent = post.title.toUpperCase();
        postEl.querySelector("p").textContent = post.body;
        postEl.querySelector("li").id = post.id;
        listElement.append(postEl);
    }
  } catch {
  	alert(error.message);
  }
}

*then 안에서 또 then 체인이 일어나는 현상은 바람직하지 못하지만, 일단 fetch API에서는 이런 방식으로 에러를 처리한다고 한다.

 

 

HTML

<template id="single-post">
  <li class="post-item">
    <h2></h2>
    <p></p>
    <button>DELETE</button>
  </li>
</template>
<section id="new-post">
  <form>
    <div class="form-control">
      <label for="title">Title</label>
      <input type="text" id="title" />
    </div>
    <div class="form-control">
      <label for="content">Content</label>
      <textarea rows="3" id="content"></textarea>
    </div>
    <button type="submit">ADD</button>
  </form>
</section>
<section id="available-posts">
  <button>FETCH POSTS</button>
  <ul class="posts"></ul>
</section>

 

 

  •  요청 '헤더' 추가 작업
    : 일부 API에서는 전송하는 데이터의 유형을 설명해야 하거나, 추가적인 인증 데이터를 필요로 하는 경우가 있다. 이때 헤더가 전송되는 요청에 첨부할 수 있는 메타데이터 역할을 한다.
// fetch API 헤더 추가 예시
return fetch(url, {
    method: method,
    body: JSON.stringify(data),
    headers: { "Content-Type": "application/json" },	// 헤더 추가
  }).then((response) => {
    return response.json(); 
});
// XMLHttpRequest 헤더 추가 예시
const xhr = new XMLHttpRequest();
xhr.setRequestHeader("Content-Type", "application/json");

 

 

Axios.js

  • Axios란?
    : Promise API를 활용한 비동기 통신 js 라이브러리.

  • 왜 사용하는가?
    - HTTP와 통신할 때, XMLHttpRequest 방식은 투박하고(onload에서 따로 error를 또 처리해줘야 됨) fetch API는 에러 처리가 복잡하기 때문. => 프로젝트가 커질수록 코드가 더욱 복잡해지기 때문에 간편하게 axios 라이브러리 사용.
    - 에러 처리도 알아서 잘 해준다. 기존의 두 방식은 http 요청 url을 잘못 입력한 경우를 에러로 인식하지 못했는데, axios는 에러로 인식해줘서 별도로 조건을 걸어주지 않아도 된다.

위 코드에 Axios를 적용하면 위에서 썼던 'sendHttpRequest' 함수는 더 이상 필요 없어진다.

아래는 fetchPosts()가 sendHttpRequest() 대신 axios로 HTTP 요청을 보내는 예시이다.

async function fetchPosts() {
  try {
    const response = await axios.get(  // sendHttpRequest()를 axios.get(url)으로 대체
      'https://jsonplaceholder.typicode.com/posts'
    );
    console.log(response);
    
    const listOfPosts = response.data;
    for (const post of listOfPosts) {
      const postEl = document.importNode(postTemplate.content, true);
      postEl.querySelector('h2').textContent = post.title.toUpperCase();
      postEl.querySelector('p').textContent = post.body;
      postEl.querySelector('li').id = post.id;
      listElement.append(postEl);
    }
  } catch (error) {
    // 요청 주소 에러를 포함한 모든 에러를 여기서 다 처리해줌
    alert(error.message);
    console.log(error.response);
  }
}

 

이를 통해 fetch된 post를 콘솔로 확인해보면, json으로 변환하지 않았는데도 알아서 객체 데이터가 js에서 사용할 수 있는 스냅샷으로 변환되어있는 것을 확인할 수 있다.

 

POST 요청도 axios.post로 대체할 수 있다.

이때, 헤더 구조를 따로 추가하지 않아도 axios에서 알아서 넣어주니 참 좋다.

// "POST" 요청
async function createPost(title, content) {
  const userId = Math.random();
  const post = {
    title: title,
    body: content,
    userId: userId,
  };

  axios.post("https://jsonplaceholder.typicode.com/posts", post);
}

 

DELETE 요청도 마찬가지로 axios 방식으로 바꿔보자.

// DELETE 요청
listElement.addEventListener("click", function (e) {
  let postId = e.target.closest("li").id;
  if (e.target.textContent == "DELETE") {
    axios.delete(`https://jsonplaceholder.typicode.com/posts/${postId}`);
    e.target.closest("li").remove();
  }
});

'JS' 카테고리의 다른 글

번들링을 하는 이유?  (0) 2024.09.06
모듈화 정리  (0) 2024.09.06
HTTP 통신을 해보자  (0) 2024.08.30
프로미스 번외  (0) 2024.08.29
콜백 지옥, 프로미스, Async & Await  (0) 2024.08.29
'JS' 카테고리의 다른 글
  • 번들링을 하는 이유?
  • 모듈화 정리
  • HTTP 통신을 해보자
  • 프로미스 번외
쥬피썬더의노예
쥬피썬더의노예
오히려 좋아
  • 쥬피썬더의노예
    d.log
    쥬피썬더의노예
    글쓰기 관리
    • 분류 전체보기 (112)
      • JS (37)
      • TS (3)
      • WEB (10)
      • React.js (20)
      • Next.js (4)
      • tanstack query (2)
      • Node.js (2)
      • HTML (5)
      • CSS (13)
      • CS (1)
      • 에이전트 (1)
      • Git (4)
      • JAVA (0)
      • SQL (0)
      • db (0)
      • GSAP (0)
      • 자료구조 (1)
      • 알고리즘 (0)
      • ✨회고 (5)
      • 포꾸 (0)
      • 인터뷰 (0)
      • 개발일지 (0)
      • 일기 (1)
      • etc (3)
      • 정처기 실기 (0)
        • C (0)
        • Java (0)
        • Python (0)
      • fonts (0)
      • articles (0)
      • 도서 (0)
  • 인기 글

  • 태그

    클로저
    Next.js
    TypeScript
    React.JS
    자바스크립트
    상태 관리
    HTML
    안티그래비티
    슬라이딩 윈도우
    조합 패턴
    CSR
    Til
    WEB
    useState
    css
    유효성 검사
    javascript
    아키텍처
    SSR
    zustand
    React
    리팩토링
    GIT
    SSG
    useEffect
    리액트
    React Query
    state
    프론트엔드
    폼
  • 최근 글

  • 전체
    오늘
    어제
  • hELLO· Designed By정상우.v4.10.3
쥬피썬더의노예
HTTP 요청하기 & 에러 처리 (+ Axios)
상단으로

티스토리툴바