본문 바로가기
Web/React

[FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 3️⃣ - 미래 <React의 Concurrent Rendering>

by 서상혁 2023. 1. 28.

들어가며

https://programming119.tistory.com/276

 

[FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 2️⃣ - 현재 < SPA와 CSR, SSR에 대해>

들어가며 1️⃣ - 과거 [FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 1️⃣ - 웹 서비스의 역사

programming119.tistory.com

2부에서는 가장 보편화된 렌더링방식들에 대해 알아봤습니다.

 

3부에서는 앞으로의 렌더링 방식이 어떻게 발전해 나갈 것 같은지, 2023 트렌드에 최대한 맞춰서 말해보려고 합니다.!

 

React 18 과 Concurrent Rendering

 

출처 :&nbsp;https://baek.dev/post/33/

현대 웹에 대해 얘기할 때 FE의 깡패 React를 빼놓고 얘기할 수가 없습니다. 끝없는 성장과 발전으로 FE 라이브러리 1위자리를 차지하고 이제는 탄탄한 성벽을 세우는 중이죠. 오늘은 React 의 관점에서 새롭게 시도중인 렌더링 방식에 대해 얘기할 생각입니다.

 

https://www.youtube.com/watch?v=ByBPyMBTzM0&list=PLPxbbTqCLbGE5AihOSExAa4wUM-P42EIJ&index=15 

 

리액트는 꽤나 오래 전부터 기존 react의 렌더링 방식에 대해 의문을 가지고, 새로운 방식을 제시하였습니다. 그 방식은 처음에는 asynchronous rendering으로 불리워졌으며, 결국 concurrent mode 라는 이름을 가지게 되었죠. 위 영상은 2018 리액트 컨퍼런스에서 concurrent feature에 대해 설명하는 영상인데요, 이 때는 아직 hooks도 보편화되지 않았던 시기랍니다. 그 이후 컨퍼런스들에서도 concurrent 에 대한 얘기는 빼놓지 않고 꾸준히 지속해왔었으니, 얼마나 오랫동안 리액트가 새로운 렌더링 방식에 대해 준비해온지 체감할 수 있죠... 😮

 

 

https://programming119.tistory.com/242

 

[React] 리액트 Concurrent 모드란

Concurrent 모드? 리액트 공식 문서 - Concurrent 모드 에 대해 리액트 공식문서를 보면 Concurrent 모드에 대한 도입이 실험적 단계로 진행되고 있다고 말합니다. 최근 점점 쓰이고 있는 'Suspense 모드' 또

programming119.tistory.com

 

저도 React Concurrent 모드에 대해 간략히 소개한적이 있었습니다! 저 글을 쓸 때만 하더라도 concurrent mode는 실험적 단계에 불과했는데요, (1년 반 전이네요 벌써..)  어느덧 리액트 18도 공식적으로 지원하는 버전이 되었습니다. 물론 v18 부터는 변경사항이 크기 때문에 아직 실무에 직접적으로 사용하는 곳은 많지는 않은 것으로 알아요. 그렇기 때문에 곧 현재이자 미래인 것이죠!

 

정식 도입된 Concurrent

사실 Concurrent mode는 이제 deprecated 된 명명 방식입니다. v18 부터 정식도입된 Concurrent feature는 이제 특별한 mode가 아니라, 하나의 정식화된 방식으로 자리잡을 예정이라는 뜻이죠.

 

 

그래서 Concurrent  가 렌더링을 어떻게 개선하는데?

 

Blocking rendering 문제

 

사실 리액트는 UI 렌더링하는 순간에 다른 작업들을 전부 차단합니다. (정확히 말하자면 여태까지의 리액트는 그렇습니다). 물론 그 시간이 너무 찰나이기 때문에, 사용자가 체감할만한 상황이 자주 나오지는 않죠.  React 가 DOM 작업을 하는동안 다른 작업은 모두 Stuck됐습니다. 이를 Blocking mode 라고 부릅니다. Concurrent mode와 반대의 의미죠.

 

리액트에서는 이를 설명하는 좋은 예시로 사용자 텍스트 입력에 나오는 자동완성을 듭니다. 사용자가 텍스트를 입력하면, input 창에는 바로 텍스트가 입력됩니다. 헌데, 텍스트 입력과 텍스트 창의 텍스트 paint가 과연 동시에 이루어질까요? 텍스트 입력에 따라 부가적으로 실행되어야하는 작업이 클 때는 얘기가 달라집니다. 백번 설명하는 것보다 예시한번 보는게 200배는 깔끔할 것 같아요.

 

https://codesandbox.io/s/blocking-mode-forked-vdmisv

 

blocking-mode (forked) - CodeSandbox

blocking-mode (forked) using react, react-dom, react-scripts

codesandbox.io

출처 : https://tecoble.techcourse.co.kr/post/2021-07-24-concurrent-mode/

 

텍스트 입력에 따라 바뀌어야하는 결과들을 렌더링하느라, 정작 사용자가 가장 체감하기 쉬운 텍스트 input창은 멈춰있게 됩니다. 사용자 경험은 자연스럽게 하락하겠죠. 이런 문제를 해결하기 위해 concurrent mode가 등장하게 됩니다.

 

 

Rendering interruptible

 

위에 첨부한 React Concurrent 모드 포스팅에서도 말했듯이, 리액트 개발자들은 이 부분의 문제를 개선하는 데에 집중했습니다. 

 

사실 위 예시에서 진짜 중요한 부분은 아래의 텍스트 결과값 렌더링이 아니라 사용자 입력창의 페인팅 입니다.

중요도 : 사용자 입력창 페인팅  >  텍스트 결과값 페인팅 

이처럼 Concurrent Feature 의 핵심은 priority(중요도) 와 중단 가능(Interruptible) 입니다. 우선순위를 설정해, 기존 렌더링 과정이 우선순위가 낮다면 중단시키고,  더 중요한 부분을 concurrent하게 렌더링하겠다는 뜻이죠. 그래서 concurrent 모드는 Rendering interruptible (중단가능) 하다고 표현합니다.

 

출처 - https://sweaty-concurrent-react.netlify.app/

어마어마한 렌더링 계산 작업이 필요하지만, 텍스트 input창에 영향을 주지는 않는 concurrent mode.

아래 예시를 통해 직접 확인해볼 수 있어요.

https://sweaty-concurrent-react.netlify.app/

 

위 예시 출처는 https://dawchihliou.github.io/articles/stress-testing-concurrent-features-in-react-18 인데 이 게시글에서 더욱더 자세한 정보를 얻을 수 있습니다.

 

 

React Fiber

렌더링을 interruptible하게 할 수 있는 요인에는 React Fiber이 있는데요, React Fiber를 간단하게 말하자면 리액트가 새로 구성한 reconcliation 알고리즘입니다. reconcliation이 뭔지 모르겠다구요? 아래 글을 읽어보시고 오면 됩니다~

 

https://programming119.tistory.com/240

 

[React] 리액트 reconciliation(재조정) 이란? / 리액트 key

Virtual DOM 리액트에 있어서 reconciliation 의 개념을 이해하려면, 버츄얼 돔의 이해가 필요합니다. Virtual DOM에 대해서는 간단하게만 설명하고 넘어가겠습니다. 이 동영상 은 VDOM을 직관적으로 이해하

programming119.tistory.com

 

 

Fiber는 Reconciler를 재구현한다.

- UI에서 모든 업데이트를 즉시 적용할 필요는 없다. 모든 업데이트를 즉시 적용하는 것은 낭비일뿐만 아니라 프레임을 떨어트리고 사용자 경험을 감소시킬 수 있다.
- 다른 유형의 업데이트들은 우선순위가 다르다. 예를 들어, 애니메이션 업데이트는 데이터 저장소의 업데이트보다 빠르게 완료돼야 한다.
- push 기반 접근법은 앱(개발자 당신)에게 스케줄 일정을 어떻게 결정해야 할지 요구한다. 반면, pull 기반 접근법은 프레임워크(리액트)가 똑똑하게 당신을 대신하여 그러한 결정을 내린다.

 

UI 렌더링에 최적화되도록 Call Stack의 동작을 사용자 지정할 수 있다면 좋지 않을까?
Call Stack과 stack 프레임을 조작할 수 있다면 좋지 않을까?

그것이 Fiber의 목적이다. Fiber는 리액트 컴포넌트에 특화된 stack의 재구성이다.
하나의 Fiber를 virtaul stack frame으로 간주할 수 있다.

stack을 재구성함으로써 얻는 이점은 stack frame을 메모리에 보관하고 원하는 경우 언제든지 실행할 수 있다는 것이다.
이는 스케줄링 목표를 달성하는 데 매우 중요하다.

 

 

리액트 Fiber의 핵심 내용들을 가져와보았습니다. 여기 https://github.com/acdlite/react-fiber-architecture 더 자세한 fiber 문서가 있으며, 위 내용은 아래 번역본을 올려주신 marco 님의 블로그에서 퍼왔습니다.

 

https://velog.io/@jangws/React-Fiber

 

React Fiber 얕게 알아보기

리액트 화이바....? 어디선가 계속 언급되던 React Fiber를 살펴봐야겠다고 마음을 먹었다. react-fiber-architecture 문서를 읽다가 이해도 잘 안되고 금방 까먹을 것 같아 일단 대충 번역했고, 이외에 다

velog.io

 

 

아래는 Fiber 동작과정을 예시 코드와 함께 간단하게 풀어서 설명해준 좋은 글입니다!

 

원리 : https://betterprogramming.pub/how-does-reacts-interruptible-updates-work-5340bcaadb1a

 

How Does React’s Interruptible Updates work?

Why doesn’t Vue use the fiber architecture?

betterprogramming.pub

 

 

사실 이 뿐만아니라, 리액트는 내부 깁숙한 곳에 우선순위 큐, 다중 버퍼링과 같이 더 정교한 알고리즘을 자체적으로 구현하고 있다고 하네요. 구체적인 구현을 몰라도 유저들은 기본적인 API의 사용방법만 알면 이를 가져다가 활용만 잘 하면 된다~ 라고 말하고 있습니다. (암요 암요)

 

 

 

 

Concurrent rendering 적용

 

그렇다면 concurrent rendering을 어떻게 직접 사용할 수 있을까요?

 

리액트 18부터, concurrent 렌더링 방식을 적용 가능한 훅을 제공합니다.

 

useTransition

https://beta.reactjs.org/reference/react/useTransition

 

useTransition

A JavaScript library for building user interfaces

beta.reactjs.org

 

useTransition이라는 훅을 이용해서 상태 변화의 우선순위를 적용할 수 있는데요,

 

const [isPending, startTransition] = useTransition();

위와 같은 형태로 사용이 됩니다.

useTransition 으로 생성한 startTransition 함수로 상태변화의 지연이 가능하구요,

isPending은 작업이 지연되고 있음을 나타내는 불린값입니다.

 

const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');

function selectTab(nextTab) {
  startTransition(() => {
    setTab(nextTab);
  });
}

 

startTransition 콜백 안에 넣은 상태변화들은, 다른 더 중요한 업데이트(렌더링)가 있다면 지연되게 됩니다. 

지연되는 동안 isPending 값은 true 가 되겠죠?

 

아래 startTransition 공식문서를 통해 자세한 내용과 예시를 확인해볼 수 있습니다!

 

https://beta.reactjs.org/reference/react/startTransition

 

startTransition

A JavaScript library for building user interfaces

beta.reactjs.org

 

 

useDeferredValue

또 하나의 훅으로는 useDeferredValue가 있는데요,

 

https://beta.reactjs.org/reference/react/useDeferredValue

 

useDeferredValue

A JavaScript library for building user interfaces

beta.reactjs.org

 

useDeferredValue 의 훅으로 state를 감싸주게 되면, 그 state 와 같지만 조금은 다른 값을 가지게 됩니다.

기존의 state는 변화되었다면 그 즉시 바로바로 리렌더링 대상이 되지만, useDeferredValue 로부터 생성된 값은 리렌더링을 일으킬 때 interruptible 하다는게 특징입니다!  즉, useDeferredValue 로부터 생성된 값에 변화가 일어나서 렌더링이 필요한 상황에서 다른 state도 변화된 상황이라면 그 렌더링이 우선시되고,  useDeferredValue 의 값은 미뤄지게 되죠.

 

말로만 들어서는 감이 잘 안오시죠.. 사용 예시가 나온 좋은 글이 있어 첨부합니다.

https://vroomfan.tistory.com/45

 

React 18 useDeferredValue로 성능 최적화하기

프로젝트: tracking map 키워드: react v18, useDeferredValue, interruptible rendering, rendering blocking 상황 react 18에서 새로 나온 useDeferredValue hook을 사용해보고 장점을 알아본다. 실제 프로젝트에 적용하여 렌더

vroomfan.tistory.com

 

이런 concurrent Rendering을 지원하는 훅들 덕분에 디바운싱이나 쓰로틀링을 임의로 구현하지 않아도, 렌더링에 따른 성능 저하를 막을 수 있습니다!

 


마치며

 

 

React의 Concurrent rendering 을 탐구해보았습니다. 탐구라고하기에는 깊지도, 그렇다고 너무 얕지도 않았던 것 같습니다.

Concurrent rendering의 핵심은, 병렬성과 중단가능한(interruptible) 렌더링 을 꼽을 수 있겠네요. 리액트에 한정적인 내용일 수 있지만, 기존 리액트에는 없던 새로운 접근 방식이라 자세히 다뤄보고 싶었습니다.

 

아직 저도 사용해본적이 없고 새로 배워나가고 있는 부분인지라 틀린 점이 있을 수도 있고 다속 미숙할 수 있다는 점 양해바랍니다 ☺️

 

 

 


이전 화

 

[FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 2️⃣ - 현재 < SPA와 CSR, SSR에 대해>

 

[FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 2️⃣ - 현재 < SPA와 CSR, SSR에 대해>

들어가며 1️⃣ - 과거 [FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 1️⃣ - 웹 서비스의 역사

programming119.tistory.com

 

 

 

 

출처

 

 

- https://www.grapecity.com/blogs/experimental-react-concurrent-mode-is-coming

- https://velog.io/@jay/Concurrent-React

- https://dawchihliou.github.io/articles/stress-testing-concurrent-features-in-react-18 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90

댓글