2022년, React 18이 배포되고 리액트에서 내세운 주요 Feature는 'Concurrent Feature'이었다. React의 맥락에 빗대어 생각해보면 '한번에 2개 이상의 동작'을 하는 것을 의미한다. 옛날의 React에서는 Blocking Rendering(차단 렌더링)이라고 하여, 한 번에 하나의 작업만 처리하였고 작업이 시작하면 중단될 수 없었다.
하지만 React 18에서는 Concurrent Mode라는 것을 실험적으로 내었다가, 정식 릴리즈에서는 Concurrent Feature라고 공식적으로 소개되었다. 이전에는 꽉 막힌 일차선 도로를 무한히 달려야 했다면, 이제는 2차선, 3차선 도로를 내어 작업을 병렬적으로 처리할 수 있게 되는 것이다.
특히 React 18의 소개 문서를 살펴보면, React 18의 주요 기능으로 개발자가 생각할 필요가 없는 동시성이라고 말하고 있다. 그동안 당연하다고 생각되었던 것들, 혹은 개발자도 알 수 없었던 내용을 구체화하고 투명하게 제공하여 원하는 사용자 경험에 맞추어 개발할 수 있도록 하겠다는 것이다.
Concurrect React의 핵심 속성은 다음과 같다고 볼 수 있다.
'Batching'이란 React가 성능 향상을 위해 여러 상태의 업데이트를 하나의 렌더링 그룹으로 그룹핑하는 것이다. 마찬가지로 공식 문서에서 예로 든 이슈의 설명을 살펴보자.
function handleClick() {
setCount(c => c + 1); // Does not re-render yet
setFlag(f => !f); // Does not re-render yet
// React will only re-render once at the end (that's batching!)
}
다음과 같이 버튼의 클릭 이벤트에 상태 업데이트를 실행시켜 놓은 경우, 버튼을 클릭할 시 batching이 적용된다. 다음 데모를 통해 직접 확인해보자
https://codesandbox.io/s/spring-water-929i6?file=/src/index.js
실제로 버튼 클릭 시 상태 업데이트는 두개가 일어나지만 콘솔에 로그는 한번만 찍히는 모습을 볼 수 있다. 업데이트를 그룹핑하여 불필요한 리렌더링을 방지하는 것이다.
그렇다면 React 18에서 말하는 Auto Batching은 무엇일까? 이미 batching이 적용되고 있는 것이 아닌가? 🧐
다음 데모를 살펴보자.
https://codesandbox.io/s/trusting-khayyam-cn5ct?file=/src/index.js
해당 예시에서는 버튼을 클릭 할 시 상태 업데이트마다 한번씩, 즉 콘솔에 로그가 두번 찍힌다. 차이점이 무엇일까? 코드를 살펴보면 다음의 차이가 있다.
function handleClick() {
console.log("=== click ===");
fetchSomething().then(() => {
// React 17 and earlier does NOT batch these:
setCount((c) => c + 1); // Causes a re-render
setFlag((f) => !f); // Causes a re-render
});
}
브라우저 이벤트에 걸린 것이 아니라 특정 데이터를 fetch한다거나 할 경우 batching이 적용되지 않는다. 기존의 리액트는 batching을 처리하는 과정에서 일관성이 없는 것이다.
React 18에서 같은 코드를 돌려보았을 때의 결과를 살펴보자.
https://codesandbox.io/s/morning-sun-lgz88?file=/src/index.js
특정 Data를 fetch한 후에 상태 업데이트를 진행했지만, 기대한대로 콘솔에 로그가 한번만 찍히는 것을 볼 수 있다. batching이 적용된 것이다.
React 18의 공식 문서로 다시 돌아가보면 모든 Concurrent React는 Opt-in이라는 설명이 나와있다. React 18의 기능을 사용하려면 Root API를 업데이트해야 하고, 이를 통해 버전이 업그레이드되어도 모든 기능을 점진적으로 사용해 나갈 수 있도록 하는 것이다.
React 18의 Migration guide를 살펴보면, 바뀐 Root API의 가이드를 살펴볼 수 있다.
// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);
// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<App tab="home" />);
이를 통해 React 18의 기능을 사용할 수 있다. Auto Batching 외에도 동시성을 보장하기 위한 React 18의 기능이 꽤 많은데, 앞으로도 살펴볼 필요가 있을 것 같다 🧐