useEffect와 Suspense를 활용한 로딩 상태 관리 차이점

하영·2025년 1월 30일

React

목록 보기
21/26

useEffect와 Suspense를 활용한 로딩 상태 관리 차이점

01. useEffect와 useState를 사용한 로딩 상태 관리 ✏️

useEffect를 사용하면 API 요청을 직접 수행하고, useState로 로딩 상태를 명시적으로 관리해야 한다.

✅ 코드 예제

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

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

  useEffect(() => {
    setLoading(true); // 요청 시작 시 로딩 상태 활성화
    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then((response) => response.json())
      .then((json) => {
        setData(json);
        setLoading(false); // 데이터 로딩 완료
      })
      .catch((err) => {
        setError(err);
        setLoading(false); // 오류 발생 시 로딩 종료
      });
  }, []);

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

  return <div>{data.title}</div>;
}

export default DataFetchingComponent;
  • 특징
    • useEffect 안에서 비동기 요청을 실행하고, useState를 통해 로딩 상태 직접 관리
    • loading 상태를 명시적으로 ture → false 로 변경하여 UI 렌더링 흐름을 제어함
    • API 호출이 끝날 때까지 "Loading…" 메시지를 표시함
  • 장점
    • 비교적 단순하고 직관적인 방식
    • 요청을 여러 번 수행하거나 로딩 상태를 세부적으로 제어할 수 있다.
  • 단점
    • 로딩 상태를 직접 관리해야 하므로 코드가 장황해질 수 있다.
    • 여러 개의 비동기 요청이 있다면 관리가 복잡해진다.
    • Suspense 보다 렌더링이 효율적이지 못하다. (React가 자동으로 최적화해주지 않음)

02. Suspense를 사용한 로딩 상태 관리 ✏️

Suspense는 리액트의 동시성 기능(Concurrent Features)과 함께 사용되며, 비동기 데이터를 요청하는 컴포넌트가 로딩 상태일 때 자동으로 UI를 관리할 수 있다.

React.lazy() 또는 React Query와 함께 자주 사용된다.

✅ Suspense + React.lazy()

import React, { Suspense, lazy } from "react";

const LazyComponent = lazy(() => import("./LazyComponent"));

function App() {
  return (
    <Suspense fallback={<p>Loading component...</p>}>
      <LazyComponent />
    </Suspense>
  );
}

export default App;
  • lazy()를 사용하여 LazyComponent를 동적으로 로드한다.
  • 로드되는 동안 fallback UI(로딩 메시지)를 자동으로 표시합니다.

✅ Suspense + 데이터 로딩 - React Query 활용

import React, { Suspense } from "react";
import { useSuspenseQuery } from "@tanstack/react-query";

function FetchDataComponent() {
  const { data } = useSuspenseQuery({
    queryKey: ["post"],
    queryFn: async () => {
      const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
      return response.json();
    },
  });

  return <div>{data.title}</div>;
}

function App() {
  return (
    <Suspense fallback={<p>Loading data...</p>}>
      <FetchDataComponent />
    </Suspense>
  );
}

export default App;
  • 특징
    • Suspense가 자동으로 로딩 상태를 관리해주기 때문에 별도로 useState를 사용할 필요가 없다.
    • 로딩 상태, 데이터 상태를 일관되게 유지하여 코드가 더 간결해진다.
    • React Query, Relay 같은 데이터 라이브러리와 결합하여 사용하면 효율적인 데이터 관리가 가능
  • 장점
    • 로딩 상태를 자동으로 처리하므로, 별도의 useState 상태 관리가 필요하지 않음
    • 비동기 컴포넌트가 많을 때 최적화가 용이함
    • UI가 더 부드럽게 업데이트 되며 최적의 사용자 경험을 제공함
  • 단점
    • 일반적인 fetch API 와는 바로 사용할 수 없고 React Query, Relay 같은 라이브러리와 함께 사용해야함
    • SSR 환경에서는 제대로 동작하지 않는다.

03. useEffect vs Suspense 비교 🔍

비교 항목useEffectSuspense
로딩 상태 관리useState로 수동 관리Suspense가 자동 관리
코드 가독성로딩 상태를 직접 처리해야 하므로 코드가 길어짐더 간결한 코드 작성 가능
사용 라이브러리기본 React + fetchReact.lazy(), React Query 등과 함께 사용
SSR 지원가능제한적 (React 18에서 개선되었으나 주의 필요)
성능 최적화기본적인 수준동시성 기능과 결합 시 더 부드러운 렌더링
유지보수 용이성로직이 여러 곳에 분산될 수 있음로딩 UI가 컴포넌트 트리에서 중앙 집중화됨

04. Suspense의 단점 ⚠️

4-1. 일반적인 fetch API와 바로 사용할 수 없음

  • Suspense는 기본적으로 Promise를 반환하는 함수와 함께 사용해야함
  • 일반적인 fetch API 와는 바로 사용할 수 없기 때문에 비동기 데이터 로딩을 위해 React Query, Relay, SWR 등과 함께 사용한다.
  • 또는 fetch를 사용하려면 직접 Promise 래핑 로직을 구현한다.

4-2. 서버 사이드 렌더링(SSR)과의 호환성 문제

  • Suspense는 클라이언트 렌더링(CSR) 환경에서 최적화되어 있으며, SSR에서는 기본적으로 동작하지 않는다.
  • React 18에서 Suspense for SSR이 도입되었지만, 복잡한 설정이 필요하며 완전한 기능을 보장하지 않는다.
  • React 18의 Suspense for SSR을 사용하거나, Next.js와 같은 프레임워크의 기능을 활용하여 해결한다.
  • 서버에서 데이터를 미리 패칭하여 클라이언트로 전달하는 방식(예: getServerSideProps, getStaticProps)을 사용한다.

4-3. Error Boundary 사용한 오류 처리

  • Suspense 자체에는 오류 처리 메커니즘이 없다.
  • 비동기 로딩 중 발생한 오류를 처리하려면 Error Boundary를 함께 사용해야 한다.
    <ErrorBoundary fallback={<ErrorComponent />}>
      <Suspense fallback={<LoadingComponent />}>
        <DataFetchingComponent />
      </Suspense>
    </ErrorBoundary>

4-4. 중첩된 Suspense의 관리가 어려움

  • 여러 개의 Suspense가 중첩된 경우, 로딩 상태 및 오류 처리가 복잡해질 수 있음
  • Suspense에 대한 fallback UI를 개별적으로 관리해야 함
  • 중첩된 Suspense를 피하거나, 상위 Suspense에서 로딩 상태를 일관되게 관리해야한다.
  • SuspenseList를 사용해 다중 Suspense 컴포넌트를 제어 가능하다.

5. 언제 useEffect와 Suspense를 선택해야 할까? 🤔

  • 단순한 데이터 로딩 : useEffect 사용이 적합 (ex: API 요청 후 수동으로 상태 관리)

  • 복잡한 데이터 요청, 대량의 비동기 처리 :Suspense 사용이 유리 (ex: React Query와 함께 데이터 캐싱 및 관리)

  • 렌더링 성능 최적화 필요 : Suspense가 더 좋은 선택


📚 참고자료

profile
왕쪼랩 탈출 목표자의 코딩 공부기록

0개의 댓글