HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤎
프론트엔드 데브코스 5기 교육생
/
🐻
문동욱팀
/
대박사건
대박사건
/
✍🏻
학습 공유
/
🎉
Suspense와 선언적으로 Data fetching처리
🎉

Suspense와 선언적으로 Data fetching처리

작성자
김유경

Suspense란?

Suspense는 아직 렌더링이 준비되지 않은 컴포넌트가 있을때 로딩 화면을 보여주고 로딩이 완료되면 해당 컴포넌트를 보여주는 React에 내장되어 있는 기능입니다.
 

Suspense를 사용하려는 이유

  • 복잡한 비동기 처리를 해결하기 위해
    • ⇒ 로딩시 , 성공시, 실패시를 선언적으로 처리 가능
 

Suspense 도입을 고민하게 된 사례

  1. ReceivedPostListPage.tsx - isLoading, data값의 존재 유무등으로 처리
function ReceivedPostListPage() { const { data, isLoading } = useChannelPost(); return ( <> <PostList type="post" posts={!isLoading && data ? (data as UserPost[]) : []} /> </> ); }
  1. Channel.tsx - 비동기 값을 한번더 비동기(useState)로 묶어서 사용
function Channel() { const [data, setData] = useState({ name: '프룽', posts: [], description: '' }); const [visible, handleModalClick] = useModal(); const [isOpened, setIsOpened] = useState(false); const { channelName } = useParams(); const { data: channelInfo } = useChannelQuery(channelName ?? ''); useEffect(() => { if (channelInfo) setData(channelInfo); console.log(channelInfo); }, [channelInfo]);
 
각 방법의 공통 문제점
  • 받아온 데이터에 관한 처리가 복잡합니다. 지금은 성공시의 화면만 신경써서 data가 undefined일 때만 처리하고 있지만 실패시, 로딩시 화면까지 처리하려 한다면 더욱 복잡해집니다,
 

Suspense 사용시 이점

성공시 처리는 데이터가 undefined 가 아닌 상태를 보장하므로 위와 같은 처리가 필요없고 실패시, 로딩시는 ErrorBoundary, Suspense fallback ui로 선언적으로 나타낼 수 있습니다.
 
 

Suspense 적용

tanstack query의 suspense

tanstack query에서 suspense 기능을 제공해줍니다.
v5부터 suspense=true는 deprecated되었고 대신 useSuspenseQuery, useSuspenseQueries, useSuspenseInfiniteQuery 훅을 제공해줍니다.
notion image
 
notion image
위의 3가지 훅을 사용할 시 타입 레벨에서 data가 undefined 상태가 되지 않습니다.
⇒ undefined 인 상태에서는 Suspense에서의 fullback UI가 렌더링 되기 때문입니다.
 

Suspense 사용

suspense를 사용할 mypage컴포넌트의 상위에 작성해줍니다. fallback에 로딩시 보여줄 컴포넌트를 작성합니다.
import { Suspense } from 'react'; ... <Suspense fallback={<fallbackUi />}> <PostListPage /> </Suspense>
 

tanstack query hook 수정

useQuery → useSuspenseQuery
useQueries→ useSuspenseQueries
로 변경해주면 됩니다.
 
  • ReceivedPostListPage.tsx
function ReceivedPostListPage() { const { data, isLoading } = useChannelPost(); console.log(isLoading, data); return ( <> <PostList type="post" posts={!isLoading && data ? (data as UserPost[]) : []} /> </> ); }
notion image
isLoading이 true가 되어도 data가 undefined인 경우가 있습니다. 때문에 따로 예외처리가 필요했는데 suspense훅을 사용할 경우 undefined가 아닌 타입을 보장해서 불필요한 코드를 줄일 수 있습니다.
 

수정후

  • ReceivedPostListPage.tsx
function ReceivedPostListPage() { const { data } = useChannelPost(); return ( <> <PostList type="post" posts={data} /> </> ); }
  • Channel.tsx
function Channel() { // const [data, setData] = useState({ // name: '프룽', // posts: [], // description: '' // }); const [visible, handleModalClick] = useModal(); const [isOpened, setIsOpened] = useState(false); const { channelName } = useParams(); const { data: channelInfo } = useChannelQuery(channelName ?? ''); // useEffect(() => { // if (channelInfo) setData(channelInfo); // console.log(channelInfo); // }, [channelInfo]);
useChannelQuery를 suspenseQuery로 변경했기때문에 주석처리된 부분을 제거할 수 있습니다.
  • AppRouter.ts
component: ( <Suspense fallback={<CommentSkeleton />}> <CommentListPage /> </Suspense> )
⚠ Suspense로 감싸주지 않으면 로딩되는 동안 보여줄 페이지가 없어서 오류가 발생하므로 반드시 감싸줘야 합니다.
 

적용 화면

notion image
 

ErrorBoundary

suspense의 fullback은 로딩중일때의 ui를 그리고 실패시의 ui는 ErrorBoundary에서 처리 가능합니다.
간편한 에러처리를 위해 react-error-boundary 라이브러리를 설치해서 사용했습니다.
  • App.tsx
{userRoutes.mypage.map((route, idx) => ( <Route path={route.path} element={ <QueryErrorBoundary> <AuthMiddleware> <>{route.component}</> </AuthMiddleware> </QueryErrorBoundary> } key={idx} ></Route> ))}
function QueryErrorBoundary({ children }: ErrorFullback) { const { reset } = useQueryErrorResetBoundary(); const location = useLocation(); return ( <ErrorBoundary onReset={reset} fallbackRender={({ resetErrorBoundary }) => ( <Style.Container> {mypagePathToSkeletonType[location.pathname] === 'post' ? ( <PostSkeleton /> ) : null} {mypagePathToSkeletonType[location.pathname] === 'comment' ? ( <CommentSkeleton /> ) : null} {mypagePathToSkeletonType[location.pathname] === 'follow' ? ( <FollowSkeleton /> ) : null} <div className="info-container"> <Style.InfoWrap> <span>⚠ 문제가 발생했습니다</span> <button className="button" onClick={() => { resetErrorBoundary(); }}> 다시 시도하기 </button> </Style.InfoWrap> </div> </Style.Container> )}> {children} </ErrorBoundary> ); }
각 pathname마다 다른 화면을 보여주기 위해 QueryErrorBoundary컴포넌트를 만들어 사용했습니다.
 

적용 화면

 
notion image
 
이렇게 하면 로딩중 컴포넌트, 성공시 컴포넌트, 실패시 컴포넌트를 선언적으로 각각 처리할 수 있습니다.
 

 
 
💬
설명이 부족했다면 언제든 물어봐 주세요. 그리고 틀린 게 있거나 다른 더 좋은 방법이 있다면 언제든 공유해주시면 바로 반영하겠습니다!!
 
[참고자료]
  • tanstack query suspense
Suspense | TanStack Query Docs
React Query can also be used with React's Suspense for Data Fetching API's. For this, we have dedicated hooks: useSuspenseQuery
Suspense | TanStack Query Docs
https://tanstack.com/query/latest/docs/react/guides/suspense
GitHub - ssi02014/react-query-tutorial: 😃 TanStack Query(aka. react query) 에서 자주 사용되는 개념 정리
😃 TanStack Query(aka. react query) 에서 자주 사용되는 개념 정리 - GitHub - ssi02014/react-query-tutorial: 😃 TanStack Query(aka. react query) 에서 자주 사용되는 개념 정리
GitHub - ssi02014/react-query-tutorial: 😃  TanStack Query(aka. react query) 에서 자주 사용되는 개념 정리
https://github.com/ssi02014/react-query-tutorial?tab=readme-ov-file#suspense
GitHub - ssi02014/react-query-tutorial: 😃  TanStack Query(aka. react query) 에서 자주 사용되는 개념 정리
 
  • suspense
토스ㅣSLASH 21 - 프론트엔드 웹 서비스에서 우아하게 비동기 처리하기
API를 호출하거나 네이티브 앱과 통신할 때 프론트엔드 웹 서비스에서는 반드시 비동기 작업이 일어나게 됩니다. 일상처럼 다루고 있지만 정작 UI에서 다루기 힘든 비동기 프로그래밍. React Suspense를 이용하여 우아하게 처리하는 이론과 실전 적용법을 공유합니다. 박서진 / 토스 Frontend Developer 토스 개발자 컨퍼런스 SLASH에 대한 자세한 정보는 👉🏻 https://toss.im/slash-21 컨퍼런스 및 세션에 대한 문의는 👉🏻 slash@toss.im #토스 #개발자컨퍼런스 #SLASH
토스ㅣSLASH 21 - 프론트엔드 웹 서비스에서 우아하게 비동기 처리하기
https://www.youtube.com/watch?v=FvRtoViujGg&t=992s
토스ㅣSLASH 21 - 프론트엔드 웹 서비스에서 우아하게 비동기 처리하기
 
  • errory boundary
React의 Error Boundary를 이용하여 효과적으로 에러 처리하기 | 카카오엔터테인먼트 FE 기술블로그
카카오페이지에 적용된 에러 처리 방식에 대해 공유합니다
React의 Error Boundary를 이용하여 효과적으로 에러 처리하기 | 카카오엔터테인먼트 FE 기술블로그
https://fe-developers.kakaoent.com/2022/221110-error-boundary/
React의 Error Boundary를 이용하여 효과적으로 에러 처리하기 | 카카오엔터테인먼트 FE 기술블로그