Logo

React에서 원격 API 호출하기

React 앱을 개발하다보면 네트워크를 통해 REST API나 GraphQL API를 호출하여 원격에 있는 데이터를 가져와야 하는 일이 빈번하게 생기는데요. 이번 포스팅에서는 React 훅 함수를 이용해서 원격 API를 깔끔하게 호출하는 방법에 대해서 알아보겠습니다.

useEffect()로 원격 데이터 가져오기

보통 원격 API를 호출하는 작업은 컴포넌트 랜더링을 막지않기 위해서 비동기로 처리하기 경우가 대부분인데요. 이러한 Side Effect 처리를 위해 React에서는 useEffect 훅 함수를 제공하고 있습니다.

예를 들어, 원격에 있는 특정 사용자의 정보를 가져와 보여주는 간단한 프로필 컴포넌트를 작성해보겠습니다. 인터넷에 공개되어 있는 REST API 서비스를 호출하기 위해서 window.fetch() 함수를 이용하였습니다.

window.fetch() 함수에 대한 자세한 설명은 관련 포스팅를 참고바랍니다.

import React, { useState, useEffect } from "react";

function Profile({ userId }) {
  const [loading, setLoading] = useState(true);
  const [user, setUser] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    window
      .fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
      .then((res) => res.json())
      .then((user) => {
        setUser(user);
        setLoading(false);
      })
      .catch((error) => {
        setError(error);
        setLoading(false);
      });
  }, [userId]);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <ul>
      <li>id: {user.id}</li>
      <li>email: {user.email}</li>
      <li>name: {user.name}</li>
      <li>phone: {user.phone}</li>
      <li>website: {user.website}</li>
    </ul>
  );
}

위 컴포넌트를 브라우저에 랜더링해보면 화면에 잠깐 loading... 메시지가 나왔다가, 주어진 userId prop값에 부합하는 사용자 정보를 보여줄 것입니다. 또한, 어떤 이유로든 REST API의 호출이 실패하는 경우에는 브라우저 화면에 Error!가 표시될 것입니다.

React의 useEffect() 훅 함수에 대한 자세한 설명은 관련 포스팅를 참고바랍니다.

커스텀 훅 함수로 추상화 시키기

위 컴포넌트를 보면 상태 관리와 원격 API 호출을 위한 코드가 상당 부분을 차지하는 것을 알 수 있는데요. 이 부분을 커스텀 훅 함수로 빼내서 추상화 시키면 컴포넌트의 코드를 좀 더 간결해질 것입니다.

// useFetchUser.js

import { useState, useEffect } from "react";

function useFetchUser(userId) {
  const [loading, setLoading] = useState(true);
  const [user, setUser] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    window
      .fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
      .then((res) => res.json())
      .then((user) => {
        setUser(user);
        setLoading(false);
      })
      .catch((error) => {
        setError(error);
        setLoading(false);
      });
  }, [userId]);

  return { loading, user, error };
}

export default useFetchUser;
// ProfileWithUseFetchUser.jsx

import React from "react";
import useFetchUser from "./useFetchUser";

function Profile({ userId }) {
  const { loading, user, error } = useFetchUser(userId);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <ul>
      <li>id: {user.id}</li>
      <li>email: {user.email}</li>
      <li>name: {user.name}</li>
      <li>phone: {user.phone}</li>
      <li>website: {user.website}</li>
    </ul>
  );
}

전체 코드의 양에서는 기존과 크게 달라지지는 않았지만, 복잡한 컴포넌트의 경우 이렇게 리팩토링을 하면 코드를 유지보수하기가 쉬워집니다.

커스텀 훅 함수를 일반화 시키기

원격 API를 호출하는 것은 웹 애플리케이션에서는 빈번한 작업이므로 다른 컴포넌트에서도 필요한 코드일 것입니다. 다른 컨포넌트가 뽑아낸 커스텀 훅 함수를 사용하기 수월하도록 코드를 좀 더 일반화 시켜보겠습니다.

// useFetch.js

import { useState, useEffect } from "react";

function useFetch(url) {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    window
      .fetch(url)
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error);
        setLoading(false);
      });
  }, [url]);

  return { loading, data, error };
}

export default useFetch;
// ProfileWithUseFetch.jsx

import React from "react";
import useFetch from "./useFetch";

function Profile({ userId }) {
  const {
    loading,
    data: user,
    error,
  } = useFetch(`https://jsonplaceholder.typicode.com/users/${userId}`);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <ul>
      <li>id: {user.id}</li>
      <li>email: {user.email}</li>
      <li>name: {user.name}</li>
      <li>phone: {user.phone}</li>
      <li>website: {user.website}</li>
    </ul>
  );
}

export default Profile;

자, 이렇게 일반화된 훅 함수 useFetch()를 원격 API 호출이 필요한 다른 컴포넌트에도 적용해볼까요? 특정 사용자가 작성한 글 목록을 REST API를 호출하여 보여주는 간단한 컴포넌트를 작성해보겠습니다.

// Posts.jsx

import React from "react";
import useFetch from "./useFetch";

function Profile({ userId }) {
  const {
    loading,
    data: posts,
    error,
  } = useFetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <ul>
      {posts.map(({ id, title }) => (
        <li key={id}>{title}</li>
      ))}
    </ul>
  );
}

전체 코드

제가 포스팅에서 작성한 전체 코드는 아래에서 직접 확인하고 실행해볼 수 있습니다.

마치면서

이상으로 React의 useEffect() 함수룰 이용하여 어떻게 원격에 있는 데이터를 가져올 수 있는지 알아보고, 재사용이 가능한 커스텀 훅 함수를 작성해 보았습니다. 본 포스팅에서는 GET 방식의 REST API 호출만 다루었지만, window.fetch() 함수의 두 번째 옵션 인자를 통해서 다른 방식(POST, PUT, DELETE…)의 호출도 가능할 것입니다. 이 부분은 비슷한 전략을 사용하셔서 여러분이 스스로 코드를 개선해나갈 수 있으실 거라 믿고 여기서 줄이겠습니다.