동시성(Concurrency)이라는 무기

222025년 09월 06일3

Suspense를 데이터 로딩의 핵심 도구로 재정의한 것은 위대한 첫걸음이었다. 하지만 Suspense가 마법처럼 동작하기 위해서는, 그 배후에서 모든 것을 가능하게 해주는 더 근본적인 엔진이 필요했다. 그 엔진의 이름은 바로 ‘동시성(Concurrency)’이었다.

과거의 React는 ‘블로킹(Blocking)’ 렌더링 방식으로 동작했다. 렌더링이 한번 시작되면, 그 작업이 끝날 때까지 React는 다른 어떤 일도 할 수 없었다. 마치 외길 터널에 들어선 자동차처럼, 중간에 멈추거나 다른 차에게 길을 양보하는 것이 불가능했다. 만약 거대한 컴포넌트 트리를 렌더링하는 데 100밀리초가 걸린다면, 그 100밀리초 동안 사용자의 클릭이나 키보드 입력은 모두 무시되었다. 이것이 바로 UI 버벅임, 즉 ‘프레임 드롭(Frame Drop)’의 주된 원인이었다.

세바스찬 마크바게는 이 문제를 해결하기 위해 수년간 동시성 렌더러 개발에 매달려왔다. React 18에서 실험적으로 도입되었지만, 그 진정한 힘은 서버 컴포넌트와 Suspense가 결합된 React 19 시대에 비로소 드러날 참이었다.

“동시성이란, React가 여러 상태 업데이트를 동시에 처리할 수 있는 능력을 의미합니다.”

세바스찬은 팀원들에게 동시성의 핵심 개념을 다시 한번 설명했다.

“더 중요한 것은, 렌더링을 ‘중단’하고 ‘재개’할 수 있다는 점입니다. 외길 터널이 아니라, 차선을 변경하며 비상 차량에게 길을 터줄 수 있는 4차선 고속도로가 되는 거죠.”

이것이 어떻게 Suspense와 연결되는가?

Suspense로 감싸인 컴포넌트가 데이터 로딩을 위해 렌더링을 ‘일시 중단’시켰다고 가정해보자.

  • 블로킹 렌더러 하에서는: React는 그저 데이터가 올 때까지 멍하니 기다려야 한다. 다른 어떤 작업도 할 수 없다.
  • 동시성 렌더러 하에서는: React는 데이터 로딩을 기다리는 동안, 그 렌더링 작업을 잠시 옆으로 치워둔다. 그리고 그 사이에 더 긴급한 작업, 예를 들어 사용자의 키보드 입력 같은 것이 들어오면 그 작업을 먼저 처리한다. 사용자는 자신의 입력이 즉각적으로 화면에 반영되는 것을 보며, 앱이 멈추지 않았다고 느끼게 된다.

동시성은 React에게 ‘우선순위’를 판단하는 지능을 부여했다.

모든 상태 업데이트는 더 이상 평등하지 않았다. 타이핑처럼 즉각적인 피드백이 중요한 업데이트는 ‘긴급(Urgent)’으로, 화면 전환처럼 약간의 지연이 허용되는 업데이트는 ‘전환(Transition)’으로 분류될 수 있었다.

Suspense가 데이터 로딩이라는 ‘기다림’을 선언적으로 표현하는 방법이라면, 동시성은 그 기다리는 시간 동안에도 앱의 생명력을 유지시켜주는 심장 박동과도 같습니다.” 로렌 탄이 덧붙였다.

이 두 가지가 결합되자, 놀라운 사용자 경험이 가능해졌다.

사용자가 페이지를 전환하는 버튼을 클릭한다. 다음 페이지는 무거운 데이터를 로드해야 해서 Suspense에 의해 렌더링이 잠시 중단된다. 하지만 동시성 덕분에 앱은 멈추지 않는다. 로딩 스피너는 부드럽게 돌아가고, 사용자가 혹시 다른 버튼을 누르더라도 즉각 반응한다. 데이터가 모두 준비되면, React는 중단했던 페이지 전환 렌더링을 매끄럽게 재개한다.

더 이상 개발자는 복잡한 setTimeout이나 requestAnimationFrame을 써가며 렌더링 시점을 조율할 필요가 없었다. 그저 React에게 ‘이 업데이트는 덜 중요하다’고 알려주기만 하면, 동시성 렌더러가 모든 것을 알아서 처리해 주었다.

동시성은 눈에 보이지 않는 무기였다. 하지만 이 강력한 무기가 있었기에, Suspense는 단순한 로딩 상태 관리 도구를 넘어, 끊김 없는 사용자 경험을 창조하는 React 19의 핵심 철학으로 거듭날 수 있었다.