고양이hyebin
axios-retry
January 20, 2024

서비스를 운영하면서 예상치 못한 네트워크 에러로 인해 데이터가 업로드되지 않는 상황이 발생하였다. 사용자가 서비스를 이용하는 도중에 발생하는 에러는 사용자 경험에 직접적인 영향을 미치며, 심지어 서비스의 신뢰성에도 영향을 미칠 수 있다. 그렇기 때문에 이번 사건을 발판으로 에러 처리에 중요성을 다시금 느끼며 공부한 내용을 기록하기로 마음먹었다.

잠깐! 그럼 재시도 요청은 왜 필요할까?

웹 서비스에서는 다양한 이유로 인해 HTTP 요청이 실패할 수 있다. 재시도를 통해 얻을 수 있는 몇 가지 장점은 다음과 같다.

  • 향상된 안정성: 실패한 요청을 자동으로 재시도함으로써 성공적인 데이터 응답의 가능성이 높아지고 애플리케이션의 전반적인 안정성이 향상된다.
  • 향상된 사용자 경험: 백그라운드에서 자동으로 요청을 재시도하면 오류 메시지나 중단 없이 원활한 사용자 경험을 보장할 수 있다.
  • 수동 개입 감소: 자동화된 재시도 메커니즘이 없으면 요청을 수동으로 재시도를 해야한다. 이는 효율적이지도 않고 확장성도 없다.

방법 1. 무작정 코드 짜기

처음엔 무작정 재 요청을 하는 코드를 짰다. retries변수로 에러 시 retries를 증가시켰다. 그리고 1초 후 재 요청을 보냈다. 하지만 보기만 해도 코드는 복잡하고 지저분했다.

.catch((error) => {
    if (
      error.message === "Network Error" ||
      error.code === "ECONNABORTED" ||
      error.response === undefined
    ) {
      let retries = 0;
      const retryRequest = () => {
        retries++;
        if (retries <= 3) {
          console.log(`Network error occurred. Retrying (${ 3 - retries + 1} attempts left)...`);
          setTimeout(() => {
            axios
              .post(
                "https://" + process.env.REACT_APP_SERVER + "/upload", data)
              .then((response) => {...})
              .catch(retryRequest);
          }, 1000);
        } else {
          console.error(
            "Maximum retries reached. Unable to complete the request."
          );
        }
      };
      retryRequest();
    }
})

방법 2. axios-retry

다른 방법을 찾아보다가 axios-retry라는 라이브러리를 발견하였다.

axios-retry는 Axios 라이브러리와 함께 사용되는 HTTP 요청 재시도를 구현하기 위한 라이브러리로, 네트워크 오류 또는 일시적인 서버 문제와 같은 상황에서 자동으로 HTTP 요청을 다시 시도할 수 있도록 도와준다.

설치하기

npm install axios-retry

일단 따라해보자

공식 홈페이지에 나와있는 방법을 일단 따라해봤다.

import axiosRetry from "axios-retry";
 
const client = axios.create({ baseURL: "http://example.com" });
axiosRetry(client, { retries: 3 });
 
client.get("/test").then((result) => {
  result.data;
});

이슈발생 🚨 retry 되지 않음

내가 실수한게 있나 검색을 해보니 실패한 모든 요청에 대해서 재시도 조건을 다음과 같이 지정해야했다.

axiosRetry(axios, {
  retries: 3,
  retryCondition: () => true,
});

스택오버플로우 링크 👈

By default, it retries if it is a network error or a 5xx error on an idempotent request (GET, HEAD, OPTIONS, PUT or DELETE).

axios-retry는 기본적으로 5xx 오류에 대해 재시도하고 다른 오류의 경우 'retryCondition' 옵션을 사용해야 한다.

axios-retry가 적용된 코드

코드는 훨씬 보기 좋고 깔끔해졌다.

import axiosRetry from 'axios-retry';
 
	const retryInterval = 3000;
  const accessToken = sessionStorage.getItem("token");
 
  const client = axios.create({
    baseURL: process.env.REACT_APP_API_SERVER,
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  });
 
  axiosRetry(client, {
    retries: 3,
    retryCondition: () => true,
    retryDelay: () => retryInterval,
  });
 
	await client
      .post("/upload", data)
      .then((response) => {...})
      .catch((error) => {...});

axios-retry를 통해 최대 재시도 횟수, 재시도 간 딜레이, 재시도 조건 등을 쉽게 커스텀 할 수 있었다.

에러와 예외

  • 에러 : 시스템 단계에서 발생. 시스템의 비정상적인 상황이므로 예외처리가 아닌 시스템 환경을 개선해야 한다.
  • 예외 : 프로그램 로직에서 발생. 프로그래머가 작성한 로직에서 예외를 예상하여 구분하고 처리해야 한다.

예측가능한 예외 vs 예측 불가능한 예외

  • 예측 가능한 예외

    • 프로그램에서 당연히 발생 할 수 밖에 없는 상황.    ex) 로그인 실패, 데이터 조회 실패 .. 등
  • 예측 불가능한 예외

    • 에러와 같은 수준의 레벨.     ex) 버그, 시스템의 메모리 문제 .. 등

    • 시스템 환경에서 개선해야 한다.

예외에 대한 생각 정리

프로그램 설계는 참 중요하다. 무작정 개발만 하는 것이 아닌 수많은 예외 상황을 고려하며 설계해야한다. 하지만 예외 상황을 다 예측하기에는 너무 힘든게 사실이다. 회사를 다니면서 기획의 중요성을 참 많이 느꼈다. 기획이 계속 틀어지면서 처음 설계한 프로그램이 꼬이기 시작하고, 그로 인해 없었던 사이드 이펙트 까지 생겨나게 되버린것이다. 하지만 그런 기획까지 예측하며, 잘게 쪼개고 나누는 것이 개발자의 몫인건가.🤔 참 어려운 것 같다. (이런 문제가 많아서 아토믹이 생겨난 것 같기도 하다)

예외 상황은 빈번하게 발생한다. 하지만 예외 상황이 발생한 것 보다 이를 보완하며 고쳐나가는 자세가 더 중요한 것 같다. 분명 작은 예외를 무시할 경우 더 큰 예외 상황이 될 것이다.

예외에 대해 생각하고 또 생각하자.

참고