목차
· useState의 state를 이용한 로딩과 Suspense fallback 로딩의 차이
· tanstack useQuery의 isLoading를 이용한 로딩과 useSuspenseQuery를 이용한 Suspense fallback 로딩의 차이
useState의 state를 이용한 로딩과 Suspense fallback 로딩의 차이
useState의 state를 이용한 로딩 방식
Suspense와 기존 로딩 상태 관리 방식은 useEffect와 loading state는 로딩 상태를 관리하는 방식에서 근본적인 차이가 있어요. 기존 방식에서는 데이터를 불러올 때 useEffect 훅을 사용하고, 로딩 상태를 관리하기 위해 loading이라는 별도의 상태 변수를 만들어야 해요. 예를 들어 데이터를 불러오는 동안엔 loading을 true로 설정하고, 데이터가 다 불러온다면 false로 바꾸는 형식으로 말이죠. 그래서 조건에 따라 로딩 화면을 보여주는 식으로 작동을 해요. 이 방식은 간단한 상황에서는 충분히 유효하지만, 여러 개의 비동기 데이터를 다룰 때에는 조건부 렌더링 로직이 복잡해질 수 있습니다.
Suspense fallback 로딩 방식
반면 Suspense는 로딩 중인 컴포넌트를 직접 렌더링하지 않고, Suspense의 컴포넌트의 fallback 속성으로 로딩 화면을 정의하게끔 해요. 데이터를 기다리는 동안에는 fallback으로 정의된 UI만 보여주고, 데이터가 모두 준비되면 Suspense에 감싸진 컴포넌트를 자연스럽게 표시합니다. 이렇게 로딩 상태를 선언적으로 관리할 수 있기 때문에, 전체적인 코드가 단순해지고 유지보수도 쉬워진다는 장점을 가지고 있어요.
Suspense는 여러 개의 Suspense 컴포넌트를 중첩하거나 트리 구조로 사용할 경우, 각 Suspense가 독립적으로 로딩 상태를 관리하기 때문에 데이터 준비 시점이 다룰 수 있어요. 그 결과 로딩 화면인 fallback이 여러 번 표시되거나 비일관적인 UI 경험이 발생할 수 있습니다. 이를 적절히 제어하기 위해서는 트리의 구조와 데이터 로딩 흐름을 신중하게 설계해야해요. 또한 Suspense는 Promise 기반의 비동기 작업만 지원을 해요. 따라서 일반적인 fetch 요청에 바로 적용할 수 있는 것이 아니라, 이를 위해 추가적인 라이브러리를 사용하거나 Suspsense와 호환되는 형태로 Promise를 관리해야합니다.
tanstack useQuery의 isLoading를 이용한 로딩과
useSuspenseQuery를 이용한 Suspense fallback 로딩의 차이
Suspense를 사용하기 위해 대표적으로 최근에 tanstack을 이용한 방법을 채택하여 사용을 하고 있어요. tanstack에서 Suspense를 이용하기 위해 useSuspenseQuery를 사용한 방법으로 Suspense를 손쉽게 사용할 수 있는데, tanstack에서의 useQuery 또한 isLoading이라는 값을 반환함으로서, 로딩 화면에 대해 제어를 할 수 있어요. 그렇다면, tanstack의 useQuery를 이용한 isLoading과, useSuspenseQuery를 Suspense fallback 방식에는 어떤 차이가 있을까요?
tanstack useQuery의 isLoading 방식
먼저 tanstack의 isLoading은 tanstack-query에서 제공하는 상태 기반 접근 방식이에요, 해당 상태값들을 이용하게 된다면 로딩 상태를 로딩 · 성공 · 에러 상태를 명시적으로 확인하고 UI를 분기처리하는데 있어 코드 흐름이 명확해진다는 특징을 가지고 있어요. Suspense를 사용하지 않고 React 17 이하에서 사용이 가능하며, 데이터 패칭이 필요한 컴포넌트에 커스텀하게 로딩 컴포넌트를 쉽게 렌더링할 수 있는 장점을 가지고 있습니다. 다만 매 데이터 패칭에 관련된 로직마다 수동으로 로딩 상태와 더불어 관련 인터페이스를 제어해야하기 때문에 코드가 다소 장황해질 수 있다는 단점을 가지고 있어요.
useSuspenseQuery를 이용한 Suspense fallback 로딩 방식
useSuspenseQuery를 이용한 Suspense Fallback 로딩의 경우에는 Suspense를 통해 로딩 상태를 선언적으로 처리할 수 있다는 특징을 가지고 있어요. children 안에서 일어나는 모든 로딩 상태 관리에 대한 부분을 React의 Suspense에 위임하여 코드가 간결해진다는 특징을 가지고 있고, 데이터 로딩 실패 시에 에러는 ErrorBoundary와 통합이 가능해요. 또한, 중첩된 컴포넌트에서도 Suspense를 활용하여 로딩 상태를 자연스럽게 처리가 가능합니다. 다만, Suspense를 지원하는 React 18 이상에서만 사용이 가능하며, Suspense를 사용하면 모든 로딩 상태가 단일 fallback으로 처리되기 때문에 세부적인 로딩 상태 관리가 어려울 수 있어요.
이를 테면 아래와 같은 상황을 예로 들어볼게요. 아래 코드에서 TemporaryComponent에 우리가 사용하는 컴포넌트들의 집합이 있다고 가정을 했을 때, 데이터 패칭에 대한 loading 또는 pending이 걸릴 경우에 children을 렌더링하는 것이 아닌 fallback 안에 있는 컴포넌트를 렌더링 시키고, 로딩이 마친 후에야 children의 컴포넌트들을 렌더링시키는 과정으로 동작이 되어요.
import React, { Suspense } from "react";
import { useSuspenseQuery } from "@tanstack/react-query";
function TemporaryComponent() {
const data = useSuspenseQuery(["temporary"], fetchData);
return <div> This is Test Component </div>;
};
function App() {
return (
<div>
<header> This is header section </header>
<Suspense fallback={ <div> Loading... </div> }>
<TemporaryComponent/>
</Suspense/>
</div>
)
};
블러된 로딩 화면을 fallback으로 지정을 했다면, 블러된 로딩화면과 블러 뒤에 데이터의 미리보기를 보여주고 싶어도 children과 fallback 화면을 동시에 보여줄 수 없기 때문에 이럴 경우에는 tanstack의 isLoading을 활용한 로딩으로 세부적으로 관리를 해야해요. Suspense boundary 내부에서 데이터가 로딩 중일 때, children은 렌더링이 되지 않아요, 대신 fallback에 지정된 컴포넌트가 화면에 표시되고, 데이터가 로딩되면 children이 렌더링 되는 상태로 동작 과정이 이루어져요.
실제로 프로젝트에서도 tanstack의 isLoading을 통해 블러된 로딩화면으로 데이터를 흐릿하게 미리 노출되는 형식으로 로딩 화면을 제어했었어요, 하지만 위에서 이야기했던 특징처럼 데이터 패칭에 관련된 로직 별로 세부적으로 다루다보니 그만큼 로직에 대한 관리 리소스도 많이 든다는 단점을 겪었습니다. 다만, Suspense의 fallback을 이용했을 때는 로딩의 책임을 React에게 넘기다보니, 로딩에 대한 세부적인 관리 리소스가 상대적으로 덜했어요. 하지만 블러된 로딩화면 뒤에는 흰색 배경이 나오는 것처럼 상황에 맞게 방식에 대한 선택을 해야할 필요성이 있고, 어떤 것이 사용자에게 명확한 로딩을 제공할 수 있는지에 대한 부분을 고민할 필요가 있을 것 같습니다.
댓글