[React] 리액트 Concurrent 모드란
Concurrent 모드?
리액트 공식 문서 - Concurrent 모드 에 대해
리액트 공식문서를 보면 Concurrent 모드에 대한 도입이 실험적 단계로 진행되고 있다고 말합니다. 최근 점점 쓰이고 있는 'Suspense 모드' 또한 이런 concurrent 모드와 맥락을 함께합니다. 그렇다면 이 Concurrent 모드는 무엇이며 왜 등장한 것이고, 무슨 이점이 있을까요?
Concurrent, 우리말로 하면 '동시의' 라는 뜻을 가지고 있습니다. Concurrent 모드는 한국말로하면 '동시 체계' 혹은 '동시 방법' 정도가 되겠네요. 컴퓨터 사이언스에 에서 자주나오는 개념인 concurrent / parallel 의 의미 를 담고 있다고 생각합니다. (처음 들어보시는 분은 여기를 보시면 잘 설명되고 있습니다.) 자바스크립트는 다중 코어를 이용해서 병렬적으로 실행시키는 언어는 아니죠. 그렇기에 이벤트 루프라는 방식을 이용해서 이 한계점을 최대한 극복할 수 있는 구조가 쓰였습니다. 자바스크립트의 라이브러리인 React 또한 마찬가지입니다. parallel 모드가 아닌 concurrent 모드는 이런 환경 속에서 최대한 동시성을 추구할 수 있는 방법을 도입하겠다는 의미를 담고있습니다.
동시성?
언뜻 생각하면 이런 의문이 듭니다. 지금도 이미 동시다발적으로 잘 작동하지 않나? 그렇기도 하지만, 그렇지만은 않습니다. DOM 트리가 복잡해질 수록 Jank 현상이나, 반응성 저하 문제가 발생하고, 아직까지 React에서 해결하기 어려웠던 문제였죠. 따라서 많은 frontend 개발자들은 항상 사용자 경험을 높이는데에 온 힘을 쏟고 있고, 렌더링 최적화는 이에 필수적인 요소입니다. 그 예시들을 보시죠.
디바운싱과 쓰로틀링의 필요성?
디바운싱과 쓰로틀링이 무엇인지 모르신다면, 이 포스트를 먼저 참고 바랍니다.
공식문서에서 Concurrent 모드에 대해 논할 때, '인터럽트 가능한 렌더링' 에 대해 얘기합니다. 기존에 사용자의 텍스트 타이핑 따라 UI 가 업데이트 되는 검색을 예시로 들죠. (엔터나 검색버튼을 누르지 않아도, UI가 업데이트 되는 케이스)
이럴 때, 모든 타이핑 때마다, 목록을 업데이트 하는 작업은 버벅거림을 유발하고, 의도하지 않은 과도한 함수 호출을 발생시킵니다. 그래서 일반적으로 이 케이스에 '디바운싱' 이나 '쓰로틀링' 기법을 이용해서 해결하죠! (디바운싱, 쓰로틀링 이란?❓) 하지만, 이 역시 한계가 있습니다. 결국 UI가 렌더링이 시작되면 중간에 중단될 수 없고 이는 결국 텍스트 키 누르는 즉시 업데이트가 불가능함을 의미합니다.
이런 문제를 해결하는 데에 React는 Concurrent 모드라는 방식을 통해 접근합니다. 그 순간에는 UI를 렌더링하는 작업보다는, 텍스트 입력을 업데이트하는 것이 사용자 경험에서 더 좋은 작업이겠죠? Concurrent 모드는, 이런 경우에서 사용자의 텍스트 입력을 paint하는 것을 더 우선순위가 높은 작업으로 취급해 먼저 처리하고, 렌더링은 잠시 인터럽트처리합니다. 이런 방식으로 개발자 입장에서의 디바운싱, 쓰로틀링과 같은 번거로운 기능 구현의 필요성을 줄이는 것입니다.
정리하자면, 렌더링 과정을 더 작은 작업들로 나누고, 스케쥴러를 이용해서 각 작업들에 대해 중요도를 선정, 우선순위를 부여합니다. 우선순위가 높은 작업들을 먼저 처리하고, 최종 결과로 확정하지 않고도 부분적으로 DOM 트리 렌더링이 가능하게끔 말이죠.
로딩 시퀀스
이것도 결국 비슷한 케이스입니다. 일반적으로, 다른 페이지를 넘어가는 데에 있어서 페이지 로드에 생기는 사이 시간을 로딩바나 스피너로 처리를 하곤 하죠. 하지만, 이런 로딩바, 스피너 자체를 렌더링 하는 작업이 페이지를 로드하는 작업에도 악영향을 준다는 점에 주목합니다. 로딩바가 없었으면 더 빨리 페이지를 로드할 수 있었는데, 불필요한 데에 시간을 쏟고 있다는 점이죠.
Concurrent 모드는 이런 좋지 않은 로딩 state를 건너뛰는 방식을 가능하게 합니다. 다른 메모리에서 새로운 화면을 준비하는 작업을 하고, 로딩 state를 렌더링하는 과정이 이에 영향을 주지 않게끔 하는 것이죠.
정리
(리액트 공식문서 中)
CPU 바운드 업데이트(예를 들어 DOM 노드 만들기 및 컴포넌트 코드 실행)의 경우 Concurrency는 더욱 긴급한 업데이트가 이미 시작한 렌더링을 “중단” 할 수 있음을 의미합니다.
IO 바운드 업데이트(예를 들어 네트워크에서 코드나 데이터를 가져오는 것)의 경우 Concurrency는 모든 데이터가 도달하기 전에 React가 메모리에서 렌더링을 시작할 수 있으며 빈 로딩 state표시를 무시할 수 있음을 의미합니다.
Concurrent 모드는 이런 두 가지의 맥락에서 동시성을 매우 향상시켜줍니다.
호버나 텍스트 작업은, 아주 짧은 시간 안에 처리되어야 하지만, 클릭이나 페이지 전환은 조금 더 오래 걸려도 루즈한 누낌을 주지 않습니다. 이와 같은 기존 웹에서 싸였던 UX(사용자 경험)을 바탕으로, React Concurrent 모드는 내부작업들의 우선순위를 결정하고, 이는 최종적으로 사람-컴퓨터 간 상호작용에 대한 연구 결과가 실제 UI와 통합되도록 돕습니다.
또한, concurrent 모드를 사용하는지 여부와 상관없이 개발자의 입장에서는 기존의 컴퍼넌트, 프롭스, 스테이트 등 근본적으로 같은 방식으로 리액트를 사용할 수 있다는 것입니다.
참고