Suspense

김동하·2023년 3월 19일
2

react

목록 보기
25/31

들어가며

이번 18에 추가된 suspense이 그렇게 핫하다고 하는데, 무엇이 그렇게 핫한지 알아보장!

Suspense

위처럼 무언가 정보를 fetching해서 UI에 보여주는 앱이 있다고 가정하자. PokemonInfo는 부모로부터 데이터를 받는다.

아주 나이브하게 생각할 수 있는 것은 전역 스크립트가 실행되면 fetch를 하고 데이터를 어딘가에 할당하고 필요한 컴포넌트에 데이터를 주입하는 것이다(useEffect 이후 useState로)

이렇게 전역에서 fetching을 하고 패칭한 데이터를 할당한다.

PokemonInfo 컴포넌트가 렌더할 때 promise 객체를 던지게 하는 것이다. 그럼 리렌더할 때 resovled가 되어 data 받아온다.

이렇게 되면 아래와 같은 에러가 나온다. 즉, promise로 인해 한 번 suspended 되어서 fallback UI가 필요하다는 것이다.

react에서 제공하는 Suspense를 추가하자

fallback UI를 잠시 보여주고 제대로 데이터를 바인딩하는 걸 볼 수 있다.

즉, 스크립트가 load되면 전역에 선언된 fetch 함수가 비동기로 실행된다.

앱이 렌더되고 PokemonInfo 컴포넌트가 렌더될 때 if문에 걸리고 데이터가 없으니 promise 객체를 던진다.

그러면 suspense가 promise를 잡고 fallback UI를 보여준다.

아까 실행했던 promise가 resolved 되면 PokemonInfo 가 리렌더되면서 UI가 나온다.

이것이 Suspense다.

Fetch

리액트가 데이터를 패치를 하는 방법은 세 가지가 있다.

Fetch-on-render

흔히 알고 있는 컴포넌트 렌더링 후 useEffect를 비동기로 사용하여 데이터를 가져오는 법이다.

js 모듈을 가져오고 비동기로 데이터를 호출하고 데이터가 가져오고 이미지를 호출하는 식으로 처리가 된다.

Fetch-then-render

useEffect를 통해 화면을 그리는 필요한 데이터를 조회 후 화면을 렌더링 한다.

데이터를 먼저 조회하고 그 후에 js 모듈을 가져온다. 이 두 방식은 워터폴 방식이라 화면이 그려지기까지 느릴 수 밖에 없다

Render as You Fetch

위 두 방식을 개선한 방법이 render as you fetch다. 이 방식은 말그대로 렌더와 패치를 동시에 하는 것이다.

위의 방식으로 하면 데이터, 이미지, js 모듈을 한 번에 가져올 수 있다.

Suspense와 useTransition

render as you fetch를 알아보기 전에 suspense와 useTrasition에 대해 간단히 알아보자. 왜냐면 두 조합을 기존의 loading UI를 향상시킬 수 있다.

예제

위와 같은 예제가 있다. 1. 포켓몬 이름이 적힌 버튼을 누르거나, 2. 포켓폰 이름 입력 후 제출 버튼을 누르면 api를 호출한다.

즉, 버튼을 누를 때마다 정상 UI -> 로딩 -> 정상 UI를 거치게 된다.

문제는 아니지만 번거롭게 로딩 UI를 계속 보여주고 싶지 않다면 즉, 정상 UI -> 정상 UI로 넘어가고 싶다면 suspesne와 useTransition 조합을 사용해봐도 좋다.

** suspense의 fallback으로 준 PokemonInfoFallback에서 로딩 UI를 띄우고 데이터가 정상 패치되면 리렌더링한다.

먼저 Suspense로 UI 렌더링 하는 컴포넌트를 감싼다.

useTransition는 상태 변화의 우선순위를 정할 수 있는 hooks인데, startTransition() 은 우선순위가 낮은 콜백함수를 받는다.

비동기 fetch 부분을 startTransition()로 실행하면 끝이다.

isPending은 false가 기본값이고 패칭이 일어나면 true로 변하고, 패칭 resolved 후 state 변화가 리렌더링이되면 다시 false로 변한다.

이렇게 코드를 수정하고 다시 패칭을 해보면

즉, startTransition() 내에서 업데이트 된 상태로 인해 렌더링이 중단되면 Suspense는 fallback 대신 이전 UI를 보여준다. 이로서 로딩 UI 없이 화면 전환이 가능한 것이다.

** startTransition의 delay보다 패칭이 늦어질 경우, 늦어진 만큼만 fallback을 보여준다.

UI 향상

isPending을 활용하여 UI 향상도 가능하다. isPending에 따라 opacity를 조절하면

이렇게 자연스러운 변화가 가능하다. 마음이 편안해진다... 후...

** 마운트엔 Supense의 fallback이 무조건 보여진다.

** 패칭이 빠르게 일어나는 경우, opacity로 인해 깜빡이는 것처럼 보일 수도 있는데, 이는 transition-delay를 이용해서 자연스럽게 해결이 가능하다. (kent 선생님이 이 부분을 설명하면서 앵귤러 팀에서 일할 때, flash 이슈가 있어서 이렇게 해결했다고 그때를 회상하는데 살짝 눈가가 촉촉해지신 것 같았다)

참고 -

https://velog.io/@xiniha/React-Suspense-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

https://epicreact.dev/render-as-you-fetch/

profile
프론트엔드 개발

0개의 댓글