React

[React] Suspense 란 ?

공쥬쥬 2022. 10. 27. 15:27

React suspense 란 ?

  • 컴포넌트가 읽어야하는 데이터가 아직 준비 되지 않았다고 리액트에게 알려주는 새로운 매커니즘이다 !
  • 아직 랜더링이 준비되지 않은 컴포넌트가 있을 때, 로딩화면을 보여주고, 로딩이 완료되면 해당 컴포넌트를 보여주는 React 내장 기능이다.
  • 이는 컴포넌트의 랜더링을 어떤 작업이 끝날 때 까지 잠시 중단 시키고 다른 컴포넌트를 먼저 랜더링 할 수 있다.

 

 

suspense 를 사용하면, component Lazy loading 이나 Data Fetching 등 비동기처리를 할 때 응답을 기다리는 동안 fallback 속성으로 넘긴 컴포넌트(예를 들면 loading Spinner 등 )를 대신보여주고, 그 사이에 우선순위가 높은 다른 UI를 먼저 랜더링 할 수 있다. 

 

function ProfilePage() {
  return (
    <Suspense fallback={<h1>Loading profile...</h1>}>
      <ProfileDetails />
      <Suspense fallback={<h1>Loading posts...</h1>}>
        <ProfileTimeline />
      </Suspense>
    </Suspense>
  );
}

리액트 공식 문서의 예제를 보면 suspense 에 fallback으로 <h1>태그로 응답을 기다리는 동안 대신 보여줄 UI를 설정해주었다.

 

 

 

suspense는 React 17의 핵심기능 중 하나인 동시성과 관련있으며 선언형 라이브러리로서의 React가 에러/ 비동기/정상상태를 조금 더 선언적으로 표현하는데 도움을 준다.

 

suspense 사용 이전에는 컴포넌트 내에서 데이터를 가져오는 부분, loading 처리하는 부분, error처리하는 부분까지 모두 하나의 컴포넌트에서 처리를 했다. 즉, 명령적인 방법으로 확인을 해야한다.

 

하지만 suspense를 사용하면 로딩 처리 하는 부분에 대한 책임과, 데이터가 로딩되었는지를 판단하는 책임을 suspense에게 맡기고 정상적으로 데이터 로딩 되었을 때의 UI만 선언할 수 있다. 

 

 

 

아래의 예제는 suspense를 사용하기 이전, 컴포넌트 내에서 loading을 처리하는 부분이다.

function ProfilePage() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser().then(u => setUser(u));
  }, []);

  if (user === null) {
    return <p>Loading profile...</p>;
  }
  return (
    <>
      <h1>{user.name}</h1>
      <ProfileTimeline />
    </>
  );
}

 

 

suspense는 다음과 같은 원리로 작동한다.

  1. render method에서 캐시로부터 값 읽기를 시도한다.
  2. value가 캐시되어 있으면, 정상적으로 render한다.
  3. value가 캐시되어 있지 않으면, 캐시는 promise를 throw한다.
  4. promise가 resolve되면 React는 promise를 throw한 곳으로 부터 재시작한다.

 

즉, 데이터가 로딩 중일때 컴포넌트는 suspense에게 promise를 throw한다. 

데이터가 로딩되고 나면 suspenseSMS fallback UI 보여주는 것을 멈추고 다시 정상적인 컴포넌트를 로딩한다는 것이다.

 

 

 

 

이노베이션 캠프 파이널 프로젝트(zzw)에서 data를 받아오기 전 로딩페이지를 구현했다.

모든 페이지에 spinner를 적용했어야했는데,

React Query를 사용하여 loading부분을 아래와 같이 설정하여 구현했다.

실제 서비스가 배포되고 나서 피드백을 받아 수정을 한 부분이여서 생각보다 손이 많이 갔던 작업이었기에 suspense를 사용했다면 조금 더 쉽게 부모컴포넌트 내에서 쉽게 할 수 있었지 않을까... 하는 아쉬움이 든다.

예제를 조금 더 찾아보고 혼자서 라도 리팩토링을 해볼 예정이다 !

mutate를 사용하여 데이터를 post해줌

 

 

참고

https://17.reactjs.org/docs/concurrent-mode-suspense.html#what-is-suspense-exactly

https://blog.mathpresso.com/conceptual-model-of-react-suspense-a7454273f82e