효율적인 HTTP 요청 처리: 다양한 HTTP 요청 라이브러리 분석

신재욱·2024년 8월 8일
post-thumbnail

HTTP 요청 라이브러리란 무엇인가?

HTTP 요청 라이브러리는 웹 애플리케이션이 서버와 통신할 때 사용되는 도구로, HTTP 프로토콜을 통해 데이터를 주고받는 작업을 간편하게 처리할 수 있도록 도와준다. 이러한 라이브러리는 HTTP 메서드(GET, POST, PUT, DELETE 등)를 사용하여 서버로 요청을 보내고, 서버로부터 응답을 받아오는 기능을 제공한다. 주로 프론트엔드와 백엔드 간의 데이터 통신을 위해 사용된다.




왜 HTTP 요청 라이브러리가 필요한가?

  • 비동기 통신의 편리성: HTTP 요청 라이브러리는 비동기 통신을 쉽게 구현할 수 있도록 도와준다. 이를 통해 사용자가 웹 애플리케이션을 사용하는 동안 서버와 통신하는 동안에도 인터페이스가 응답성을 유지할 수 있게 된다.

  • 코드 간결화: HTTP 요청 라이브러리를 사용하면 복잡한 XMLHttpRequest 객체를 직접 다루지 않아도 된다. 간단한 함수 호출로 HTTP 요청을 처리할 수 있어 코드가 훨씬 간결하고 가독성이 높아진다.

  • 에러 처리: 라이브러리는 HTTP 요청 중 발생할 수 있는 다양한 에러를 효과적으로 처리할 수 있는 메커니즘을 제공한다. 이를 통해 개발자는 더 안정적인 애플리케이션을 개발할 수 있다.

  • 다양한 기능 지원: 요청 및 응답 인터셉터, 자동 JSON 데이터 변환, 타임아웃 설정, 요청 취소 등의 기능을 제공하여 개발자가 필요로 하는 다양한 요구사항을 쉽게 구현할 수 있다.




JavaScript 주요 HTTP 요청 라이브러리 소개 및 비교

1. Axios

Axios는 2016년에 출시된 Promise 기반의 HTTP 클라이언트로, 브라우저와 Node.js 환경 모두에서 사용할 수 있다. 요청 및 응답 인터셉터, 자동 JSON 데이터 변환, XSRF 보호 등 다양한 기능을 제공했다.

주요 기능:

  • 모든 요청 방법 지원 (GET, POST, PUT, DELETE 등)
  • 요청과 응답에 대한 인터셉터 기능 제공
  • JSON 데이터 자동 변환
  • Node.js에서 http 모듈을 기반으로 작동
  • 브라우저에서의 XSRF 보호 지원

장점:

  • 요청 및 응답 인터셉터 지원으로 다양한 전처리, 후처리 가능
  • 자동 JSON 데이터 변환
  • HTTP 요청 취소 기능
  • XSRF 보호 기능
  • 브라우저와 Node.js 모두에서 사용 가능

단점:

  • Fetch API에 비해 상대적으로 무겁다
  • 최신 브라우저 기능과의 호환성 문제 발생 가능
axios.get('https://api.example.com/data')
  .then(response => console.log(response.data))
  .catch(error => console.error('Error:', error));

2. Fetch API

Fetch API는 2015년에 출시된 브라우저 내장 API로, 별도의 설치 없이 사용할 수 있다. Promise 기반으로 동작하며, 사용법이 간단하지만 기본 기능 외의 고급 기능은 추가 구현이 필요했다.

주요 기능:

  • 모든 요청 방법 지원 (GET, POST, PUT, DELETE 등)
  • JSON 데이터 처리 용이
  • 스트림 처리 가능
  • 네트워크 요청에 대한 풍부한 설정 옵션 제공

장점:

  • 브라우저 내장 API로 추가 설치 불필요
  • Promise 기반으로 비동기 처리 간편
  • 최신 브라우저와의 호환성 우수

단점:

  • 요청 및 응답 인터셉터 미지원
  • JSON 자동 변환 미지원(수동으로 처리 필요)
  • 요청 취소 기능 부족 (AbortController 필요)
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

3. React Query

React Query는 2020년에 출시된 React 애플리케이션에서 서버 상태를 관리하기 위한 라이브러리다. 데이터 페칭, 캐싱, 동기화 등을 쉽게 처리할 수 있으며, HTTP 요청 자체보다는 데이터 관리와 상태 동기화에 중점을 두었다.

주요 기능:

  • 데이터 페칭 및 캐싱: 데이터를 효율적으로 페칭하고 캐싱하여 성능 최적화
  • 자동 리페치: 데이터 변경 시 자동으로 리페치
  • 쿼리 무효화 및 재페치: 데이터가 변경될 때 쿼리를 무효화하고 재페치
  • 배치 업데이트: 여러 쿼리의 결과를 일괄 업데이트
  • Pagination 및 Infinite Query: 페이지네이션 및 무한 스크롤 지원
  • Devtools 지원: React Query Devtools를 통해 쿼리 상태를 시각적으로 디버깅

장점:

  • 데이터 페칭, 캐싱, 동기화 등을 간편하게 처리
  • 서버 상태 관리 최적화
  • 자동 리페치(데이터가 변경될 때 자동으로 다시 가져오기)
  • 매우 직관적인 API

단점:

  • React 환경에서만 사용 가능
  • 기본 HTTP 클라이언트는 Fetch API (다른 클라이언트 사용하려면 추가 설정 필요)
  • 상대적으로 러닝 커브가 있음
import { useQuery } from 'react-query';

function App() {
  const { data, error, isLoading } = useQuery('fetchData', () =>
    fetch('https://api.example.com/data').then(res => res.json())
  );

  if (isLoading) return <span>Loading...</span>;
  if (error) return <span>Error: {error.message}</span>;

  return <div>{JSON.stringify(data)}</div>;
}



Axios

1. 요청 및 응답 인터셉터 (Interceptors)

Axios는 요청과 응답을 가로채서 처리할 수 있는 인터셉터 기능을 제공한다. 이를 통해 요청을 보내기 전에, 응답을 받기 전에 특정 작업을 수행할 수 있다. 예를 들어, 요청에 공통 헤더를 추가하거나 응답 에러를 처리하는 데 유용하다.

요청 인터셉터

axios.interceptors.request.use(
  function (config) {
    // 요청이 전달되기 전에 수행할 작업
    config.headers.Authorization = `Bearer ${token}`;
    return config;
  },
  function (error) {
    // 요청 오류가 발생했을 때 수행할 작업
    return Promise.reject(error);
  }
);

응답 인터셉터

axios.interceptors.response.use(
  function (response) {
    // 응답 데이터 가공
    return response;
  },
  function (error) {
    // 응답 오류가 발생했을 때 수행할 작업
    if (error.response.status === 401) {
      // 예를 들어, 인증 오류 처리
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

2. Axios 인스턴스

Axios는 기본 설정을 가진 인스턴스를 생성하여 여러 곳에서 재사용할 수 있다. 이를 통해 일관된 설정으로 여러 요청을 보낼 수 있으며, 각 인스턴스마다 다른 설정을 가질 수 있다.

Axios 인스턴스 생성

const apiClient = axios.create({
  baseURL: 'https://example.com/api',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' }
});

const handlePutRequest = async () => {
  setIsLoading(true);
  const requestData = {};
  try {
    const response = await apiClient.put(
      `/resource`,
      requestData
    );
    alert('완료되었습니다.');
    setReleasePatch(response);
  } catch (error) {
    console.error('POST 요청 중 오류 발생:', error);
    alert('등록을 실패했습니다.');
  }
  setIsLoading(false);
};

3. 취소 토큰 (Cancel Tokens)

Axios는 요청을 취소할 수 있는 기능을 제공한다. 이를 통해 불필요한 요청을 중단하거나 사용자가 페이지를 이동할 때 미완료된 요청을 취소할 수 있다.

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

const handlePutRequest = async () => {
  setIsLoading(true);
  const requestData = {};
  try {
    const response = await axios.put(
      `https://example.com/api/resource`,
      requestData,
      { cancelToken: source.token }
    );
    alert('등록을 완료했습니다.');
    setReleasePatch(response);
  } catch (error) {
    if (axios.isCancel(error)) {
     alert('등록을 취소했습니다.');
    } else {
      console.error('POST 요청 중 오류 발생:', error);
      alert('등록을 실패했습니다.');
    }
  }
  setIsLoading(false);
};

// 요청 취소 예시
source.cancel('Operation canceled by the user.');

4. 응답 데이터 변환

응답 데이터를 변환하는 방법을 정의하여 서버에서 받은 데이터를 원하는 형식으로 가공할 수 있다.

const handlePutRequest = async () => {
  setIsLoading(true);
  const requestData = {};
  try {
    const response = await axios.put(
      `https://example.com/api/resource`,
      requestData,
      {
        transformResponse: [function (data) {
          // JSON 문자열을 객체로 변환
          let parsedData = JSON.parse(data);
          // 데이터 변환 작업 수행
          parsedData.customField = 'Custom Value';
          return parsedData;
        }]
      }
    );
    alert('완료되었습니다.');
    setReleasePatch(response);
  } catch (error) {
    console.error('POST 요청 중 오류 발생:', error);
    alert('등록을 실패했습니다.');
  }
  setIsLoading(false);
};

5. 디버깅과 로깅

Axios는 요청과 응답에 대한 디버깅과 로깅을 지원한다. 이를 통해 요청이 어떻게 이루어졌는지, 응답이 어떻게 돌아왔는지 쉽게 추적할 수 있다.

axios.interceptors.request.use(request => {
  console.log('Starting Request', request);
  return request;
});

axios.interceptors.response.use(response => {
  console.log('Response:', response);
  return response;
});

const handlePutRequest = async () => {
  setIsLoading(true);
  const requestData = {};
  try {
    const response = await axios.put(
      `https://example.com/api/resource`,
      requestData
    );
    alert('완료되었습니다.');
    setReleasePatch(response);
  } catch (error) {
    console.error('POST 요청 중 오류 발생:', error);
    alert('등록을 실패했습니다.');
  }
  setIsLoading(false);
};

6. 보안 및 인증

CSRF 보호

Axios는 브라우저 환경에서 CSRF 공격을 방지하는 기능을 기본으로 지원한다.

CSRF(Cross-Site Request Forgery) : 사용자가 인식하지 못한 상태에서 원치 않는 요청을 서버에 보내는 공격

CSRF 방지 기본 설정

  • CSRF 보호를 위해 서버는 보통 CSRF 토큰을 발급하고, 이를 클라이언트가 각 요청마다 포함하도록 요구
  • 이 토큰은 클라이언트의 쿠키에 저장되며, 각 요청 헤더에 포함되어 서버로 전송
axios.defaults.xsrfCookieName = 'CSRF-TOKEN';
axios.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';



Fetch API

1. Request 및 Response 스트리밍

Fetch API는 스트리밍을 지원하여 대규모 데이터 처리에 유리하다. Fetch API를 사용하면 응답 본문을 스트림으로 처리할 수 있어 데이터를 점진적으로 읽고 처리할 수 있다.

Request 스트리밍

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue(new TextEncoder().encode("Hello, "));
    controller.enqueue(new TextEncoder().encode("world!"));
    controller.close();
  }
});

fetch('https://example.com/api', {
  method: 'POST',
  body: stream
}).then(response => {
  console.log('Request completed');
});

Response 스트리밍

fetch('https://example.com/largefile')
  .then(response => {
    const reader = response.body.getReader();
    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              controller.close();
              return;
            }
            controller.enqueue(value);
            push();
          });
        }
        push();
      }
    });
  })
  .then(stream => new Response(stream))
  .then(response => response.text())
  .then(text => {
    console.log(text);
  });

2. AbortController

Fetch 요청을 취소하기 위해 AbortController를 사용할 수 있다. 이는 긴 시간 동안 실행되는 요청을 중단하거나 사용자가 페이지를 이동할 때 사용된다.

const controller = new AbortController();
const signal = controller.signal;

fetch('https://example.com/longtask', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// 요청을 취소하려면:
controller.abort();

3. Cache 모드 설정

Fetch API는 요청의 캐싱 동작을 제어할 수 있는 cache 옵션을 제공한다. 이를 통해 브라우저의 캐시 동작을 세밀하게 조정할 수 있다.

fetch('https://example.com/api', { cache: 'no-store' })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

캐시 모드

  • default: 기본 브라우저 캐싱 동작
  • no-store: 캐시를 사용하지 않고, 항상 네트워크 요청을 수행
  • reload: 브라우저 캐시를 무시하고 네트워크 요청을 수행하지만, 응답은 캐시에 저장
  • no-cache: 항상 네트워크 요청을 수행하지만, ETag를 사용하여 조건부 요청을 수행
  • force-cache: 캐시에 저장된 응답이 있으면 사용하고, 그렇지 않으면 네트워크 요청
  • only-if-cached: 캐시에 저장된 응답만 사용하고, 네트워크 요청을 수행하지 않음

4. Redirect 모드 설정

Fetch API는 요청이 리디렉션을 어떻게 처리할지 제어할 수 있는 redirect 옵션을 제공한다. 이를 통해 리디렉션 동작을 세밀하게 제어할 수 있다.

fetch('https://example.com/api', { redirect: 'manual' })
  .then(response => {
    if (response.type === 'opaqueredirect') {
      console.log('Redirect detected');
    }
    return response;
  })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Fetch error:', error));

리디렉션 모드

  • follow: 기본 설정으로, 리디렉션을 자동으로 따름
  • manual: 리디렉션을 따르지 않고, 리디렉션 응답을 그대로 반환
  • error: 리디렉션이 발생하면 오류를 발생시킴

5. Request와 Response 클론

Fetch API의 Request와 Response 객체는 한 번만 소비될 수 있다. 하지만 clone() 메서드를 사용하면 동일한 데이터를 여러 번 처리할 수 있다.

fetch('https://example.com/api')
  .then(response => {
    const responseClone = response.clone();
    response.json().then(data => console.log('Original response:', data));
    responseClone.text().then(text => console.log('Cloned response:', text));
  })
  .catch(error => console.error('Fetch error:', error));



React Query

1. 데이터 페칭 (Data Fetching)

React Query는 데이터를 쉽게 페칭할 수 있도록 도와준다. useQuery 훅을 사용하면 간단하게 비동기 요청을 관리할 수 있다.

import { useQuery } from 'react-query';

// 서버에서 사용자 데이터를 가져오는 비동기 함수
const fetchUser = async () => {
  const response = await fetch('/api/user');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

function UserProfile() {
  // useQuery 훅을 사용하여 데이터 페칭
  const { data, error, isLoading } = useQuery('user', fetchUser);

  // 데이터 로딩 중일 때 표시할 내용
  if (isLoading) {
    return <span>Loading...</span>;
  }

  // 에러 발생 시 표시할 내용
  if (error) {
    return <span>Error: {error.message}</span>;
  }

  // 데이터 로드 완료 후 표시할 내용
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

export default UserProfile;

2. Mutation

Mutation은 서버에 데이터를 변경하는 작업을 처리한다. 주로 POST, PUT, DELETE 요청에 사용된다.

import { useMutation, useQueryClient } from 'react-query';

function AddTodo() {
  const queryClient = useQueryClient();
  // useMutation 훅을 사용하여 데이터 변이 처리
  const mutation = useMutation(newTodo => {
    return fetch('/api/todo', {
      method: 'POST',
      body: JSON.stringify(newTodo),
    });
  }, {
    onSuccess: () => {
      // 변이가 성공하면 todos 쿼리를 무효화하여 다시 페칭
      queryClient.invalidateQueries('todos');
    },
  });

  return (
    <button onClick={() => mutation.mutate({ id: Date.now(), text: 'New Todo' })}>
      Add Todo
    </button>
  );
}

3. Query Invalidation

쿼리 무효화(Query Invalidation)는 특정 데이터를 갱신하기 위해 기존 데이터를 무효화할 때 사용된다. 이는 데이터가 변경된 후 자동으로 다시 페칭되도록 한다.

import { useQuery, useQueryClient } from 'react-query';

const fetchTodos = async () => {
  const response = await fetch('/api/todos');
  return response.json();
};

function TodoList() {
  const queryClient = useQueryClient();
  const { data: todos } = useQuery('todos', fetchTodos);

  const handleAddTodo = async () => {
    await fetch('/api/add-todo', { method: 'POST' });
    // todos 쿼리를 무효화하여 다시 페칭
    queryClient.invalidateQueries('todos');
  };

  return (
    <div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
      <button onClick={handleAddTodo}>Add Todo</button>
    </div>
  );
}

4. Optimistic Updates

Optimistic updates는 서버 응답을 기다리지 않고 UI를 즉시 업데이트하는 기능, 사용자가 즉각적인 피드백을 받을 수 있도록 하여 사용자 경험을 향상시킨다.

import { useMutation, useQueryClient } from 'react-query';

function Todos() {
  const queryClient = useQueryClient();
  const mutation = useMutation(newTodo => {
    return fetch('/api/todo', {
      method: 'POST',
      body: JSON.stringify(newTodo),
    });
  }, {
    onMutate: async newTodo => {
      await queryClient.cancelQueries('todos');

      const previousTodos = queryClient.getQueryData('todos');
      // 낙관적 업데이트로 UI를 즉시 업데이트
      queryClient.setQueryData('todos', old => [...old, newTodo]);

      return { previousTodos };
    },
    onError: (err, newTodo, context) => {
      // 에러 발생 시 이전 데이터로 롤백
      queryClient.setQueryData('todos', context.previousTodos);
    },
    onSettled: () => {
      // 변이 완료 후 todos 쿼리를 무효화하여 다시 페칭
      queryClient.invalidateQueries('todos');
    },
  });

  return (
    <div>
      <button onClick={() => mutation.mutate({ id: Date.now(), text: 'New Todo' })}>
        Add Todo
      </button>
    </div>
  );
}

5. Infinite Queries

React Query는 무한 스크롤 및 페이징을 지원한다. 이를 통해 사용자는 데이터를 부분적으로 로드하고, 스크롤이 끝에 도달할 때 추가 데이터를 로드할 수 있다.

import { useInfiniteQuery } from 'react-query';

const fetchProjects = async ({ pageParam = 0 }) => {
  const response = await fetch(`/api/projects?cursor=${pageParam}`);
  return response.json();
};

function Projects() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
  } = useInfiniteQuery('projects', fetchProjects, {
    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
  });

  return (
    <div>
      {data.pages.map((page, i) => (
        <Fragment key={i}>
          {page.projects.map(project => (
            <p key={project.id}>{project.name}</p>
          ))}
        </Fragment>
      ))}
      <button
        onClick={() => fetchNextPage()}
        disabled={!hasNextPage}
      >
        Load More
      </button>
    </div>
  );
}

6. Pre-fetching

React Query는 데이터가 필요하기 전에 미리 데이터를 가져오는 기능을 제공한다. 이는 사용자가 특정 페이지로 이동하기 전에 데이터를 미리 로드하여 성능을 향상시킬 수 있다.

import { useQueryClient } from 'react-query';

function App() {
  const queryClient = useQueryClient();

  const handleMouseEnter = () => {
    queryClient.prefetchQuery('todos', fetchTodos);
  };

  return (
    <div>
      <button onMouseEnter={handleMouseEnter}>
        Pre-fetch Todos
      </button>
    </div>
  );
}

7. Query Batching

React Query는 기본적으로 여러 쿼리를 하나의 네트워크 요청으로 배치할 수 있다. 성능 최적화에 도움이 된다.

import { QueryClient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      refetchOnMount: false,
      refetchOnReconnect: false,
      retry: false,
      staleTime: 1000 * 60 * 5,
    },
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Todos />
    </QueryClientProvider>
  );
}

8. Request와 Response 스트리밍

React Query는 Fetch API를 사용하여 스트리밍 데이터를 처리할 수 있다. 이를 통해 대규모 데이터 처리에 유리한 스트리밍을 사용할 수 있다.

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue(new TextEncoder().encode("Hello, "));
    controller.enqueue(new TextEncoder().encode("world!"));
    controller.close();
  }
});

fetch('https://example.com/api', {
  method: 'POST',
  body: stream
}).then(response => {
  console.log('Request completed');
});

fetch('https://example.com/largefile')
  .then(response => {
    const reader = response.body.getReader();
    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              controller.close();
              return;
            }
            controller.enqueue(value);
            push();
          });
        }
        push();
      }
    });
  })
  .then(stream => new Response(stream))
  .then(response => response.text())
  .then(text => {
    console.log(text);
  });



관련 라이브러리

1. axios-retry

  • Axios 요청이 실패했을 때 자동으로 재시도하도록 설정할 수 있는 라이브러리
  • 네트워크 오류나 서버 과부하 등으로 인해 일시적으로 요청이 실패할 경우 요청이 자동으로 재시도되어 사용자 경험 개선
npm install axios-retry
import axios from 'axios';
import axiosRetry from 'axios-retry';

// axios-retry 설정
axiosRetry(axios, { retries: 3 });

axios.get('https://api.example.com/data')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });

2. axios-mock-adapter

  • Axios 요청을 모킹(mocking)할 수 있는 라이브러리
  • 테스트 환경에서 네트워크 요청을 모킹하여 실제 서버와의 통신 없이 테스트를 수행할 수 있다.
npm install axios-mock-adapter
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

// MockAdapter 설정
const mock = new MockAdapter(axios);

// 모킹된 응답 설정
mock.onGet('/api/data').reply(200, {
  data: 'mocked data'
});

// 실제 요청 테스트
axios.get('/api/data')
  .then(response => {
    console.log(response.data); // 'mocked data'
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });

3. axios-auth-refresh

  • Axios 요청에 대한 인증 토큰 갱신을 자동으로 처리하는 라이브러리
  • 액세스 토큰이 만료된 경우 자동으로 갱신하고 요청을 재시도한다.
npm install axios-auth-refresh

사용전

import axios from 'axios';

// 기본 Axios 설정
const instance = axios.create({
  baseURL: 'https://api.example.com',
  headers: {
    'Content-Type': 'application/json'
  }
});

// 토큰 갱신 함수
const refreshAuthLogic = failedRequest => instance.post('/auth/refresh-token', {
  token: localStorage.getItem('refreshToken')
}).then(tokenRefreshResponse => {
  // 새로운 액세스 토큰을 로컬 저장소에 저장
  localStorage.setItem('accessToken', tokenRefreshResponse.data.accessToken);
  // 실패한 요청의 헤더를 갱신된 토큰으로 업데이트
  failedRequest.response.config.headers['Authorization'] = 'Bearer ' + tokenRefreshResponse.data.accessToken;
  return Promise.resolve();
});

// 요청 인터셉터
instance.interceptors.request.use(config => {
  // 요청을 보내기 전에 로컬 저장소에서 액세스 토큰을 가져와 헤더에 추가
  const token = localStorage.getItem('accessToken');
  if (token) {
    config.headers['Authorization'] = 'Bearer ' + token;
  }
  return config;
}, error => {
  return Promise.reject(error);
});

// 응답 인터셉터
instance.interceptors.response.use(response => {
  return response;
}, error => {
  const originalRequest = error.config;
  // 401 에러 처리
  if (error.response && error.response.status === 401) {
    // 만료된 토큰이므로 리프레시 토큰으로 갱신 시도
    return refreshAuthLogic(originalRequest)
      .then(() => {
        originalRequest.headers['Authorization'] = 'Bearer ' + localStorage.getItem('accessToken');
        return axios(originalRequest);
      });
  }
  return Promise.reject(error);
});

export default instance;

사용후

  • createAuthRefreshInterceptor401 응답을 감지하고 refreshAuthLogic 함수를 호출하여 토큰을 갱신
import axios from 'axios';
import createAuthRefreshInterceptor from 'axios-auth-refresh';

// 기본 Axios 설정
const instance = axios.create({
  baseURL: 'https://api.example.com',
  headers: {
    'Content-Type': 'application/json'
  }
});

// 토큰 갱신 함수
const refreshAuthLogic = failedRequest => instance.post('/auth/refresh-token', {
  token: localStorage.getItem('refreshToken')
}).then(tokenRefreshResponse => {
  // 새로운 액세스 토큰을 로컬 저장소에 저장
  localStorage.setItem('accessToken', tokenRefreshResponse.data.accessToken);
  // 실패한 요청의 헤더를 갱신된 토큰으로 업데이트
  failedRequest.response.config.headers['Authorization'] = 'Bearer ' + tokenRefreshResponse.data.accessToken;
  return Promise.resolve();
});

// 인터셉터 설정
createAuthRefreshInterceptor(instance, refreshAuthLogic);

// 요청 인터셉터
instance.interceptors.request.use(config => {
  // 요청을 보내기 전에 로컬 저장소에서 액세스 토큰을 가져와 헤더에 추가
  const token = localStorage.getItem('accessToken');
  if (token) {
    config.headers['Authorization'] = 'Bearer ' + token;
  }
  return config;
}, error => {
  return Promise.reject(error);
});

export default instance;

4. axios-middleware

  • Axios 요청과 응답에 대해 중간 처리를 할 수 있는 미들웨어를 추가할 수 있는 라이브러리
  • 요청과 응답을 쉽게 중간에서 처리할 수 있어, 로깅이나 공통 처리를 구현하는 데 유용
npm install axios-middleware
import axios from 'axios';
import { AxiosMiddleware, getDefaultMiddleware } from 'axios-middleware';

// Axios 인스턴스 생성
const client = axios.create();
const middleware = new AxiosMiddleware(client, getDefaultMiddleware());

// 미들웨어 추가
middleware.use({
  onRequest(config) {
    console.log('Sending request to', config.url);
    return config;
  },
  onResponse(response) {
    console.log('Received response from', response.config.url);
    return response;
  },
  onError(error) {
    console.error('Error:', error);
    return Promise.reject(error);
  }
});

// 요청 보내기
client.get('https://api.example.com/data')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });



결론

HTTP 요청 라이브러리는 프론트엔드 개발자가 서버와의 통신을 효율적으로 관리할 수 있도록 돕는 필수 도구다. 블로그에서 다룬 Axios, Fetch API, React Query와 같은 라이브러리는 각각 고유의 장점과 기능을 제공하여 다양한 요구사항을 충족시킬 수 있다.

Axios는 다양한 기능과 강력한 인터셉터 시스템을 제공하고, 브라우저와 Node.js 환경 모두에서 사용할 수 있다.
Fetch API는 브라우저 내장 API로, 간단하고 가볍지만, 고급 기능이 부족할 수 있다.
React Query는 서버 상태 관리와 데이터 동기화에 특화된 강력한 라이브러리로, React 애플리케이션에서의 사용에 최적화되어 있다.

각 라이브러리의 장점과 단점을 이해하고, 프로젝트의 요구사항에 맞는 라이브러리를 선택하는 것이 중요하다. 이를 통해 더욱 효율적이고 유지보수하기 쉬운 애플리케이션을 개발할 수 있다. HTTP 요청 라이브러리를 적절히 활용하면 데이터 통신의 복잡성을 줄이고, 더 나은 사용자 경험을 제공할 수 있다.




참고 자료

Axios 공식 문서
Fetch API 문서 - MDN Web Docs
MDN Web Docs - HTTP Redirections
JavaScript Promises

profile
3년차 개발자

0개의 댓글