React18 버전에서 가장 크게 강조된 키워드는 동시성이다. 자바스크립트는 싱글스레드 언어이기때문에 한 번에 하나의 작업만 처리할 수 있다. 화면이 오래 멈춰있다면 사용자 경험은 악화되기 때문에 웹 개발에 있어서 큰 걸림돌이 된다. React는 렌더링 블록킹 문제를 해결하기 위해 동시성을 이용하게 되었다.
개념
해당 기능은 상태 업데이트를 함에 있어서 우선순위를 정하는데 도움을 준다. 리액트에서는 상태 업데이트 대상을 두가지로 나누었으며, 이를 통해 transition이 의미하는 바가 무엇인지 파악할 수 있다.
- Urgent updates(높은 우선순위) : 버튼 클릭, 키보드 입력과 같이 직관적으로 보았을 때 업데이트가 즉각적으로 일어나는 것을 기대하는 상태 값들
- Transition updates(낮은 우선순위) : 사용자가 상태 값의 변화에 따른 모든 업데이트가 뷰에 즉각적으로 일어나는 것을 기대하지 않는 것들
- load(로딩) transition, refresh(새로고침) transition
예시
검색 사이트에서 auto complete 기능이나 검색 필터링 기능을 사용한다고 했을때, 검색한 결과값을 이용해 상태를 업데이트하기 위해 아래와 같은 코드를 작성한다.
검색 결과 리스트가 매우 길거나 많지 않더라도 사이트에서 검색 결과 값을 가지고 내부적으로 복잡한 작업을 진행할 수 있다. 유저의 이벤트 값이 약간이라도 달라지더라도 페이지 UI에 큰 변화를 불러 일으킨다. 이 때 발생하는 렉을 최적화할 수 있는 방법이 없다.
// show what was typed
setInputValue(input);
// show results
setSearchQuery(input);
검색을 할 때 사용자는 키보드 입력은 즉시 일어날 것으로 기대하지만, 결과창에 대한 렌더링이 즉시 일어날 것이라고는 기대하지 않는다. startTransition은 이러한 사용자의 심리를 이용한 스펙이다.
import { startTransition } from 'react';
// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});
우선순위에 따른 작업을 분리하여 높은 우선순위의 작업(키보드 입력)은 코드 위쪽으로 두어, 즉각 렌더링에 반영한다. 그리고 낮은 우선순위의 작업(결과창)은 startTransition에 넣어 업데이트를 지연시켜 놓는다. 그러면 키입력이 다 끝난 후에 결과창의 업데이트가 발생하게 된다.
- searchQuery 상태 업데이트 진행 중 inputValue의 상태 업데이트가 발생하게 되면 잠시 중단하고 inputValue 상태 업데이트가 완료되면 searchQuery 상태 업데이트가 완료된다.
- debounce을 활용하지 않고 기기 성능에 따라 최적화가 가능해진다.
- 기존 디바운스 / 쓰로틀링은 setTimeout을 활용해 특정 시간을 무저건 기다려야 했다.
유저에게 transition 업데이트가 백그라운드에서 진행됨을 알려주고 싶을 때는 useTransition 훅을 이용해 <Spinner/>와 같은 UI를 표시해줄 수 있다.
import { useTransition } from 'react';
const [isPending, startTransition] = useTransition();
...
{isPending && <Spinner />}
...
작동 방식
오토컴플리트 구현을 위해 debounce 기능을 사용한다. debounce에는 setTimeout을 사용한다.
- yielding: 렌더링과정을 작게 분할하고 일시중지 할 수 있음(중요한 일을 양보)
- interrupting: 동시성 모드에서 업데이트에 대해 우선순위가 있음
- 이전 결과 건너뛰기: 현재 상태만 방영하도록 중간 상태 반영을 건너 뜀. 필요한 최종 상태만 반영
setTimeout과 어떻게 다른 점
- setTimeout으로 큐에 들어간 작업은 들어간 순서대로 처리되며, 이는 취소될 수 없다.
- 이전 결과 건너뛰기가 불가능하다.
startTransition은 크게 리엑트가 UI 업데이트를 위해 크고 복잡한 일을 함으로 써 대기 시간이 발생하거나 느린 네트워크 환경에서 데이터를 받아오기 위해 기다리는 상황에서 사용한다고 한다.