Automatic Batching
상태 업데이트(setState)를 하나로 통합해서 배치처리를 한 후 리렌더링을 진행합니다.
→ 리렌더링 관련 성능 개선
v17 에서는: 이벤트 핸들러 내부에서 발생하는 상태 업데이트 시 fetch()등 과 같은 콜백을 받아 처리하는 메소드가 존재할 경우에는 Automatic Batching이 처리되지 않았습니다.
// v17 & v18: 2가지 상태 업데이트가 이루어졌지만 1번의 리렌더링 발생
const onClick = () => {
setNumber((prev) => prev + 1);
setBoolean((prev) => !prev);
};
---
// v17: 2번의 리렌더링 발생
// v18: 1번의 리렌더링 발생
const onClick = () => {
// fetch()를 활용해서 콜백함수 내부에서 여러개의 상태 업데이트
fetch("<https://jsonplaceholder.typicode.com/posts/1>").then((response) => {
setNumber((prev) => prev + 1);
setBoolean((prev) => !prev);
});
};
결론적으로 Automatic Batching이 버전에 따라 다음과 같이 적용됩니다.
v17
- 콜스택에서 적용됨
- 테스크큐에서 적용되지 않음
v18
- 콜스택 & 테스크큐 모두 적용됨
- ❗️v18에서도 아래 경우에는 적용되지 않습니다.
// 콜스택과 테스크큐에서 상태 업데이트를 각각 진행할 경우 2번의 리렌더링이 발생
const onClick = () => {
setNumber((prev) => prev + 1);
fetch("<https://jsonplaceholder.typicode.com/posts/1>").then((response) => {
setBoolean((prev) => !prev);
});
};
Automatic Batching을 적용시키고 싶지 않다면
- react-dom의 flushSync()를 활용하여 Automatic Batching 기능을 off 할 수 있습니다.
const onClick = () => {
// flushSync() 활용
flushSync(() => {
setNumber((prev) => prev + 1);
});
flushSync(() => {
setBoolean((prev) => !prev);
});
};
Concurrent Feature(동시성 기능)
v18부터 기존 React에서 추구하고 있는 Concurrent Mode를 ‘기능'으로 지원합니다.
→ 자바스크립트는 싱글 스레드기반 언어이므로, React에서도 UI 렌더링 도중에 일어나는 모든 작업은 차단됩니다.
이를 Concurrent Mode를 사용해 여러 작업을 동시에 처리할 수 있도록 기능들을 확대하고 있었습니다.
- 작업들을 작은 단위로 나눈 후 작업들 간의 우선순위를 정합니다.
- 정해진 우선순위에 따라 작업을 수행합니다.
- 실제로는 동시에 작업이 수행되지는 않지만 작업 간의 전환이 매우 빠르기 때문에 동시에 수행되는 것처럼 보입니다.
→ Concurrent Mode를 적용하려면 다음과 같은 방법을 사용합니다.
- useLayoutEffect를 사용하여 컴포넌트의 렌더링이 완료된 후에도 실행시켜 리렌더링 될 때까지 컴포넌트의 이전 렌더링 결과를 유지하게 합니다.
- useState를 사용하여 컴포넌트의 렌더링 상태를 추적(isLoading: true/false)합니다. 컴포넌트의 렌더링 상태를 추적하여, 렌더링이 완료될 때까지 컴포넌트의 이전 렌더링 결과를 유지하게 합니다.
createRoot
기존 v17의 render()가 아닌createRoot()를 사용합니다.
// v17
ReactDOM.**render**(<App />, document.getElementById("root"));
// v18
const container = document.getElementById("root");
const root = **createRoot**(container);
root.render(<App />);
startTransition
디바운스 / 쓰로틀링 / setTimeout 등의 기능을 대체합니다.
(v18 이전에는 디바운스, 쓰로틀링, setTimeout으로 우선순위가 낮은 업데이트를 제어했습니다.)
- 자바스크립트의 setTimeout 동작방식과 달리 테스크큐를 활용하지 않으며 동기적으로 즉시 실행합니다.
- useTransition
- isPending: state 변경 직후에도 UI를 리렌더링 하지 않고 UI를 잠시 유지하는 상태입니다.
- startTransition: 우선순위가 높은 상태 업데이트가 발생할 경우 내부에 선언한 상태 업데이트는 중단되고 이후에 해당 상태 업데이트가 발생합니다.
const [isPending, startTransition] = useTransition({
priority: 0.5
});
const [boolean, setBoolean] = useState();
const onClick = () => {
startTransition(() => {
setBoolean((prev) => !prev);
});
};
Suspense와 SSR
기존 React에서의 SSR 적용은 waterfall 방식을 사용하고 있었습니다.
- React 코드를 서버에서 실행합니다.
- React 코드가 HTML을 생성합니다.
- HTML을 클라이언트에게 전송합니다.
- 클라이언트는 HTML을 렌더링합니다.
- HTML에 자바스크립트가 Hydrate 됩니다.
v18 부터는 독립적으로 각각 렌더링이 가능한 기능이 추가 되었습니다.
- 기존의 createRoot 대신 hyrateRoot 사용
HTML Streaming
- HTML을 서버에서 클라이언트로 전송할 때, 전체 HTML을 한 번에 전송하지 않고, 부분적으로 전송합니다.
- pipeToNodeWritable()를 활용해 HTML코드를 작은 청크로 나눈 후 보내줄 수 있습니다.
*기존의 React는 renderToString()을 사용했습니다.
Selective hydrating
- <Suspense> 를 활용하여 해당 컴포넌트가 아직 렌더링되지 않아도 다른 컴포넌트들이 hydration을 시작할 수 있습니다.

- hydration 시 fallback component를 지정할 수 있습니다.(fallback 속성)
- hydration의 우선순위를 정할 수 있습니다.(preload 속성)
<>
<Suspense fallback={<Spinner />} preload={true}>
<Component1 />
</Suspense>
<Suspense fallback={<Spinner />} preload={false}>
<Component2 />
</Suspense>
</>
- 사용자가 <Component1 />의 hydration이 완료되기 전 <Component2 />의 hydrationTrigger(클릭, 스크롤, 포커스 등)를 발생시킨다면 React는 <Component2 />의 우선순위를 높여 먼저 hydration을 진행합니다.
React Server Component(RSC)
- 지정한 컴포넌트를 클라이언트가 아닌 서버에서 렌더링합니다.
- .client.jsx, .server.jsx, .jsx 3개의 파일로 구성됩니다.
- 서버 컴포넌트는 번들에 포함되지 않기 때문에 번들 사이즈가 감소합니다.
- API 호출을 통해 여러 데이터를 불러올 필요 없이 DB 접근 / 파일 시스템 등을 접근할 수 있습니다.
- 기존 lazy loading 방식을 자동으로 지원합니다.
// v17
const OldPhotoRenderer = React.lazy(() => import('./OldPhotoRenderer.js'));
const NewPhotoRenderer = React.lazy(() => import('./NewPhotoRenderer.js'));
// v18
import OldPhotoRenderer from './OldPhotoRenderer.client.js';
import NewPhotoRenderer from './NewPhotoRenderer.client.js';
기타: New Hooks
useId
- 난수 ID를 생성하는 Hook 입니다.
- 클라이언트와 서버간의 hydration의 불일치를 피하면서 유니크 아이디를 생성 기능을 제공
- 공식문서
useSyncExternalStore
- 동시성 기능을 사용할 때 전역 상태 관리 라이브러리의 상태가 업데이트 되지 않을 경우 강제로 업데이트를 발생시키는 Hook입니다.
- *기존의 useMutableSource hook에서 변경되었습니다.
- 공식문서
useDeferredValue
- 트리에서 급하지 않은 부분의 재랜더링을 지연할 수 있는 기능을 지원하는 Hook 입니다.
- 디바운스와 비슷하지만 고정된 지연시간이 없고 렌더링이 반영되는 시점에 지연 렌더링을 시도합니다.
- 공식문서
useInsertionEffect
- CSS-in-JS 라이브러리를 활용할 때 스타일 삽입 성능 문제를 해결할 수 있는 Hook 입니다.
- Dom이 한번 mutate된 이후 실행되지만 layout effect가 발생하기 전 새 레이아웃을 한번 읽을 수 있기 때문에 사전에 계산할 수 있는 기회가 주어집니다.
- 기존 useLayoutEffect와 비슷하지만 다른점은 DOM 노드에 대한 참조에 접근할 수 있게 됩니다.
- 공식문서
[참고자료]
'React' 카테고리의 다른 글
| useState vs useRef vs 일반 JS 초기화 (0) | 2024.04.15 |
|---|---|
| React DOM Element, ComponentElement (0) | 2024.04.15 |
| 검색 기능 구현 시 고려사항(API 호출 시점, debouncing) (0) | 2024.04.15 |
| Redux → SWR(2): SWR로 Redux 대체 가능한가? (0) | 2024.04.13 |
| Redux → SWR(1): Redux의 한계 (1) | 2024.04.13 |