React 18 Suspense를 활용해보자

BangDori·2023년 12월 26일
1

React에 본격적으로 흥미가 생기기 시작하고, React Conference 관련 영상을 보던 중 Suspense를 사용한 스트리밍 서버 렌더링에 대해 알게 되었습니다. 오늘은 Suspense가 무엇인지 그리고 프로젝트에서 Suspense 기능을 어떻게 활용했는지에 대해 포스팅해보겠습니다.

📕 목차
1. Suspense란?
2. React Router v6.14와 함께 사용해보자
3. Suspense vs Await
4. 후기

Suspense란?

Suspense는 컨퍼런스 영상 뿐만 아니라 React 공식 홈페이지에서도 소개가 되어 있으니, 간단하게 소개해보겠습니다.

Suspense는 단순하게 말하면, 컴포넌트가 보여주어야 할 데이터가 아직 준비되지 않은 상태라면 로딩 화면을 보여주고, 로딩이 완료되면 해당 컴포넌트를 보여주는 새로운 기능 입니다.

정말 중요한 것은 이 Suspense가 바로 서버 컴포넌트를 비동기적으로 제공해준다는 것입니다.

간단하게 예시를 하나 들어, 이전의 React와 Suspense가 추가된 React를 소개해보겠습니다. 이전에는 돼지 국밥, 순대 국밥, 소머리국밥을 주문하면 음식점에서는 하나씩 배달하기에는 너무 많은 비용이 부담되어서 모든 메뉴를 다 만든 후에 배달을 진행했습니다. 즉, 모든 컴포넌트가 로딩이 완료된 이후에 사용자에게 전달이 되었습니다.

하지만, React 18에서 Suspense는 다른 방법을 택합니다. 각 컴포넌트를 비동기적으로 보내주어 사용자에게 완료된 것을 먼저 보여주는 방식으로 말이죠. 즉, 돼지 국밥 만들고 돼지 배달, 순대 국밥 만들고 순대 배달, 배달에 드는 비용은? React에서 알아서 최적화 해 줄테니 편하게 사용해라가 되었습니다.

React는 CSR 아닌가요? SSR도 되나요?

CSR? SSR?

React 하면 보통 CSR(Client Side Rendering)을 떠올리겠지만 SSR을 사용할 수 있습니다.

먼저 CSR로만 앱을 생성한다고 생각해 보겠습니다. 사용자는 우선 모든 스크립트 코드를 로딩하고, 데이터를 불러와 컴포넌트들을 렌더링 한 후 인터랙티브한 페이지를 보게 됩니다.

현대 웹, 앱 애플리케이션의 경우 많은 기능을 지원해주기 때문에, 번들 사이즈가 커져 자바스크립트를 로딩하는데 굉장히 많은 시간이 소요되는 경우가 있습니다. 즉, 이는 로딩 시간 이전까지 계속해서 빈 페이지를 보여주어 사용자에게 최악의 경험을 제공해준다는 것을 의미하게 됩니다.

그렇다면 SSR로 앱을 생성한다면 어떻게 될까요? 물론 SSR 이라고 이를 100% 해결해주는 것은 아닙니다. 하지만, SSR은 자바스크립트가 로드 되기 이전에 HTML 파일을 먼저 렌더링 해주기 때문에 이전보다 더 나은 사용자 경험을 제공해줄 수 있습니다.

하지만, SSR만으로는 완전한 웹 사이트를 사용자에게 제공해주기는 어렵다고 보는게 결국 모든 자바스크립트를 로딩해야 하기 때문입니다. 사용자가 HTML을 볼 수는 있지만 사용자에게 인터랙티브한 컨텐츠를 제공할 수 없기 때문입니다. 따라서 SSR 단계에서는 자바스크립트를 로딩한 이후 마지막에 Hydrate 작업을 진행합니다.

Hydration

하이드레이션은 수분을 공급한다는 사전적 의미를 가지고 있는데 '마치 드라이한 컴포넌트에 수분을 공급해줌으로써 인터랙티브한 컴포넌트를 만든다' 라는 뜻으로 해석할 수 있습니다. 즉, 하이드레이션을 통해 인터랙티브가 없는 컴포넌트에 인터랙티브 기능을 채워주는 것입니다.

React Router v6.14와 함께 사용해보자

React 18에서 Suspense는 기존의 문제점을 해결할 수 있는 주요 기능을 제공했습니다. 바로 스트리밍 HTML과 셀렉티브 하이드레이션입니다.

기존의 문제점은 '모든 데이터를 불러오기 전까지 아무 것도 볼 수 없다.' 이였습니다. React에서는 Suspense로 감싼다면 React에서 데이터를 기다릴 필요가 없기 때문에 해결할 수 있다고 말하고 있습니다. 그렇다면 바로 React Router v6.14를 활용하고 있는 프로젝트에 적용해보겠습니다.

const HomePage = () => {
  const { posts } = useLoaderData();

  return (
    <>
      <Home /> 
      <Suspense fallback={<p>Loading...</p>}>
        <PostList posts={posts} />
      </Suspense>
    </>
  );
};

export async function loadPosts() {
  const response = await getPosts();

  if (!response.ok) {
    throw json(
      { message: 'Could not found posts.' },
      { status: response.status },
    );
  }

  const resData = await response.json();
  return defer({
  	posts: resData.posts
  });
}

현재 loadPosts는 React Router v6.14에서 제공해주는 loader 함수입니다. 위 함수는 컴포넌트에 들어가기 이전 데이터를 페칭해주는 역할을 하고 있습니다. loader에 대한 자세한 내용은 React-Router loader에서 확인하실 수 있습니다.

분명 Suspense를 활용했음에도 불구하고 posts가 promise 상태로 에러가 발생하고 있습니다. 아니 Suspense가 비동기적으로 로딩되는 컴포넌트를 관리해준다며,,

해당 문제는 Deffered Data The solution을 통해 해결할 수 있었습니다. fetching 해온 데이터에 Await 컴포넌트를 적용해보겠습니다.

const HomePage = () => {
  const { posts } = useLoaderData();

  return (
    <>
      <Home />
      <Suspense fallback={<p>Loading...</p>}>
        <Await resolve={posts}>
          {(loadedPosts) => <PostList posts={loadedPosts} />}
        </Await>
      </Suspense>
    </>
  );
};

문제가 해결된 것을 확인할 수 있었습니다. 여기서 궁금한 점이 하나 생겼습니다. Suspense와 Await 둘 다 비동기 처리인 것은 알겠는데,, 왜 Suspense로는 비동기 데이터를 처리하지 못하는거지?

공식 문서를 통해 React Router의 loader 함수와 React 18의 Suspense는 다른 목적으로 사용이 된다는 것을 알 수 있었습니다.

실제로 React 공식 문서에서 소개하는 Suspense를 확인해보면, Suspense는 React 자체적으로 지연된 컴포넌트 로딩을 관리하고, 로딩 상태에서 대체 컨텐츠를 표시하는데 사용된다는 것입니다.

즉, Suspense는 비동기적으로 받아온 데이터를 처리하는 것이 아닌 지연된 컴포넌트 로딩을 관리하는 역할을 하는 거구나! 그렇다면 Await는 정확히 무슨 역할이지?

Awaitloader가 무엇인지에 대해 알면 이해할 수 있습니다. React Router에서 loader 함수는 페이지 컴포넌트가 렌더링되기 전에 필요한 데이터를 비동기적으로 로드하는 역할을 합니다. 이후, loader에서 비동기적으로 로드한 데이터를 Awaitresolve props를 이용해 뷰에 렌더링합니다.

후기

오늘은 굉장히 어려운 주제를 가지고 이야기를 해서, 주저리 주저리 흐름을 잡지 못하고 이야기가 왔다갔다 한 것 같습니다...

적용을 하기 이전에 완벽한 이해를 바탕으로 한 것이 아니기에, 100%를 안다고는 자신할 수 없지만 최신 기술을 적용해보는 것은 정말 재미있는 경험이였던 것 같습니다.

프로젝트에 적용하면서 이전에 남발되었던 데이터 loading을 확인하는 코드가 사라지기도 하고, fallback을 따로 만들어서 더 나은 사용자 경험을 제공해주기도 하고 확실히 이전보다 더 나은 코드를 작성할 수 있었습니다.

React 18에 더 많은 관심을 가지고 찾아봐야 할 것 같네요. 긴 글 읽어주셔서 감사합니다.

참고

profile
Happy Day 😊❣️

0개의 댓글