[React] 서스펜스와 리액트쿼리를 활용하여 로딩화면 구현하기 (Feat. Skeleton UI 💀)
개요 🔔
리액트 서스펜스(React Suspense)
는,
앱에서 비동기적으로 데이터를 처리하고 관리하기 위해 사용하는 리액트의 새로운 기능이다.
서스펜스는 React 16
에서 처음 선보이고,
React 18
에서 정식으로 추가 된 기능이라고 한다.
Why Suspense ❓
사용자에게 구현한 웹 페이지를 보여주기 위한 UI/UX 개선사항으로 스켈레톤 UI 를 접하고,
이를 구현하기 위해
삼항연산자를 통해 LoadingComponent
를 불러오는 형태로 진행했다.
하지만,
서스펜스를 사용하면 코드가 훨씬 직관적으로 리팩토링이 되며,
코드 스플리팅을 통해 웹 성능(최적화) 에도 유의미한 영향을 끼칠 수 있었다.
서스펜스의 사용 방법은 아래와 같다.
레이아웃을 담당하는 컴포넌트 하위에
서스펜스를 사용하여 비동기 데이터 로딩을 처리하는 로직이 담겨있다.
<Comment />
컴포넌트를 비동기적으로 로딩하며,
로딩 중에는 <Spinner />
컴포넌트가 표시된다.
fallback
옵션은 서스펜스 컴포넌트에서 사용되는 옵션 중 하나로,
코드 스플리팅이나 데이터 로딩을 처리할 때
로딩 중에 보여줄 컴포넌트나 요소를 저장하는데 사용된다.
즉, 서스펜스 컴포넌트는 비동기적으로 로딩되는 컴포넌트를 기다리며
로딩중일 때 사용자에게 어떤 내용을 보여줄지fallback
을 통해 정의한다.
React-Query 🌸
React-Query
에는 서스펜스와 유사한 isLoading
이 있다.
isLoading
은, useQuery
훅을 사용할 때 발생하는 상태 중 하나로
데이터를 비동기적으로 가져오는 동안의 로딩 상태를 나타낸다.
useQuery
훅을 사용하면, 데이터를 가져오는 동안 제공하는 주요 상태가 있다.
- data
- isLoading
- error
isLoading
의 경우, 데이터를 가져오는 동안 true
로 설정되며
데이터를 모두 불러오거나, 에러가 발생하면 상태가 false
로 변경된다.
import { useQuery } from "react-query";
function MyComponent() {
const { data, isLoading, error } = useQuery("myData", fetchDataFunction);
if (isLoading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
// 여기에서 data를 사용하여 UI를 렌더링합니다.
return <div>{data}</div>;
}
이 코드는 useQuery
로 데이터를 가져오는 중인지 여부를 isLoading
을 통해 확인하고,
해당 상태가 true
라면, 로딩 메세지가 출력된다.
에러가 발생한 경우도 동일 !
데이터가 성공적으로 잘 불러와졌다면 data
객체를 출력한다.
Suspense vs isLoading ❔
두 방법 모두 비동기적으로 데이터를 가져오고 있는 동안 처리하는 기능을 담당하고있다.
- Suspense
import React, { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
const MyComponent = () => {
return (
<Suspense fallback={<LoadingSpinner />}>
<LazyComponent />
</Suspense>
);
};
React
의 Suspense
경우 비동기 데이터를 로딩하는 동안 대기할 때 사용한다.
코드에서 사용된 것처럼,
lazy
로딩을 통해 컴포넌트를 import
하여 대기하고,
이를 fallback
옵션에서 컴포넌트를 호출하여 Suspense
중 보여질 컴포넌트를 명시하고 있다.
- isLoading
import { useQuery } from "react-query";
const MyComponent = () => {
const { data, isLoading, error } = useQuery("myQueryKey", fetchDataFunction);
if (isLoading) {
return <LoadingSpinner />;
}
if (error) {
return <ErrorComponent />;
}
return <DataComponent data={data} />;
};
React-Query
가 제공하는 useQuery
는,
비동기 데이터를 가져오고 있는동안 isLoading
이라는 상태를 제공한다.
이 상태는 데이터가 로딩 중 인지의 여부를 boolean
으로 뱉어낸다.
위 예시 참조 !!
두 개념 모두 비슷하지만 정리하자면,
React-Query
의 isLoading
은
데이터가 로딩 중 인지 여부를 확인하는데 사용되고,
React
의 Suspense
는,
비동기 작업 중 대기하는 동안 특정 컴포넌트나 리소스를 지정하는데 사용된다.
해당 코드는 두 기법을 사용한 간단한 예시이다.
import { Suspense, useState, useEffect } from "react";
const MyComponent = () => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
try {
const response = await fetch("https://api.example.com/data");
const result = await response.json();
setData(result);
setIsLoading(false);
} catch (error) {
console.error("Error fetching data:", error);
setIsLoading(false);
}
};
return (
<div>
<Suspense fallback={<LoadingSpinner />}>
<AsyncComponent data={data} />
</Suspense>
{isLoading && <LoadingSpinner />}
</div>
);
};
이 코드에서 Suspense
는, AsyncComponent
가 데이터 로딩이 완료 될 때 까지 기다리도록 하고,
isLoading
은, 데이터가 로딩 중인지의 여부를 관리한다.
즉,
React-Query
에서 제공하는isLoading
은 👇
비동기 데이터를 가져오고 있는 동안isLoading
을 사용하며,
이 속성은 데이터가 로딩 중인지 여부를 나타낼때 사용 !!!React
에서 제공하는Suspense
는 👇
비동기 데이터를 로딩하는 동안 대기할 때 사용하며,React.lazy
와 함께 사용되어 코드 스플리팅과 같은 비동기 작업에서 컴포넌트를 기다리게 한다.Suspense
를 사용하여 코드 스플리팅된 컴포넌트나 데이터를 로딩하는 컴포넌트를 대기할 때 사용한다.
차이점 🎭
얼핏 보면 두 기능은 비동기 데이터를 불러오기 이전의 작업을 처리하는 용도로 사용되어,
다소 비슷하다고 느껴질 수 있지만, 두 기능은 서로 다른 용도를 가진 기능들이다.
- isLoading
React-Query
의 isLoading
- 👩🦰 목적
비동기 데이터를 가져오는 동안의 데이터 로딩 상태를 관리하고,
해당 상태에 따라 UI 를 업데이트 하는 데 사용. - 🧒 활용
useQuery
훅을 사용하여 데이터를 비동기로 가져올 때 사용하며,
isLoading
속성을 통해 데이터가 로딩 중인지의 여부를 확인할 수 있다.
- Suspense
- 👨 목적
비동기 작업 중, 대기하는 동안 특정 컴포넌트나 발생하는 리소스를 처리하는 데 사용.
코드 스플리팅과 함께 사용하여 필요한 컴포넌트를 필요한 때만 렌더링 하는데 목적을 둠. - 👴 활용
React.lazy
와 함께 사용하여, 코드 스플리팅된 컴포넌트를 비동기적으로 불러오거나,
React.Suspense
컴포넌트를 통해 대기중에 특정 컴포넌트를 렌더링 할 때 사용.
이렇게 두 기능의 차이를 다시 한 번 정리해 봤는데,
역시 비슷해 보인다..😫
하지만,
두 기능은 각각 데이터 로딩과 데이터 처리에 관련하여 다른 측면에 중점을 둔 기능들 이므로
엄연히 다른 기능(목적)을 갖고 있다고 말할 수 있다.
오늘도 새로운 지식 잘 먹었다 !
Reference 🌊
https://www.loginradius.com/blog/engineering/react-code-splitting-with-lazy-suspense/
https://www.daleseo.com/react-suspense/
https://yiyb-blog.vercel.app/posts/react-query-loading-state
https://stackoverflow.com/questions/75401477/reasons-for-using-isloading-or-isfetching-in-react-query
https://programmerplum.tistory.com/179
https://velog.io/@bnb8419/Suspense%EC%99%80-lazy-Loading#lazy-loading%EC%9D%B4%EB%9E%80
https://lasbe.tistory.com/160#google_vignette