이번 18에 추가된 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
다.
리액트가 데이터를 패치를 하는 방법은 세 가지가 있다.
흔히 알고 있는 컴포넌트 렌더링 후 useEffect를 비동기로 사용하여 데이터를 가져오는 법이다.
js 모듈을 가져오고 비동기로 데이터를 호출하고 데이터가 가져오고 이미지를 호출하는 식으로 처리가 된다.
useEffect를 통해 화면을 그리는 필요한 데이터를 조회 후 화면을 렌더링 한다.
데이터를 먼저 조회하고 그 후에 js 모듈을 가져온다. 이 두 방식은 워터폴 방식이라 화면이 그려지기까지 느릴 수 밖에 없다
위 두 방식을 개선한 방법이 render as you fetch
다. 이 방식은 말그대로 렌더와 패치를 동시에 하는 것이다.
위의 방식으로 하면 데이터, 이미지, js 모듈을 한 번에 가져올 수 있다.
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을 보여준다.
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