virtual dom 이 좋은건가?

dante Yoon·2021년 9월 13일
4

react

목록 보기
2/19
post-thumbnail

글을 시작하며

리엑트 공식 문서의 [Reconciliation]에(https://reactjs.org/docs/reconciliation.html) 대한 설명을 보면 다음과 같이 기술 되어있습니다.

React Provides a declarative API so that you don't have to worry about exactly what changes on every update.
This makes writing applications a lot easier, but it might not be obvious how this is implemented with in React.

저의 리엑트 학습은 개발자로서 세상에서 가장 인기있는 프레임워크를 사용하는 것은 당연하다는 생각에서 시작되었습니다.

시간이 지난 지금, 비즈니스 로직을 구현하는데 있어서 여러 디자인 패턴과 RxJS와 같은 리엑티브 프로그래밍, 함수형 프로그래밍에 대해 살펴보기 시작하며, 여러 프로그래밍 패러다임이 전달해준 편리함과 장점이 리엑트가 전달해준 그것과 상당히 유사하다는 것을 발견했습니다.

누군가가 이제 리엑트를 사용하는 이유를 다시 묻는다면, 스택오버플로우 설문조사에서 항상 1,2위에 링크되는 최신기술이니까에 이러한 답변을 추가할 것 같습니다.
UI 업데이트를 관리하는 것이 편리합니다.

뭐가 편하다는 말씀인가요?

바닐라 자바스크립트를 이용해 UI를 변경하는 것은 다음과 같이 크게 두 가지의 방법이 있습니다.
1. 기존에 있던 DOM을 새로운 DOM으로 대체할 수 있습니다.
2. 기존에 있던 DOM을 그대로 두고 시각적으로 변경되는 부분을 돔을 조작해서 변경합니다.

다음과 같은 수동적인 돔 조작은 전형적인 명령형(Imperative) 프로그래밍 방식입니다.

const element = document.getElementById("search");
element.value = "updated value";
element.focus();

페이지에 많은 인터렉션의 구현이 요구됨에 따라 SPA가 등장했다 라는 역사적 사실에 더해서,
개발자의 입장에서 인터렉션을 쉽게 구현하기 위해서는 객체 지향 프로그래밍, 함수형 프로그래밍 둘 중 어느 프로그래밍 패러다임을 따르든 내부 상태의 변환에 따라 최신 UI 상태를 유지해야 합니다.

어떤 UI를 보여줄 것인가에 대한 표현어떤 상황에서 보여줄 것인가에 대한 조건을 모두 만족하며 프로그래밍 하는 것은 전통적인 명령형(imperative) 프로그래밍 패러다임으로는 굉장히 지루한 일이 될 수 밖에 없습니다.
따라서 리엑트는 개발자가 쉽게 UI 변경을 할 수 있도록 내부 상태 변경에만 집중하고 뷰는 자동으로 최적화하여 업데이트 해줍니다.

jsx를 이용한 선언형(declarative) 프로그래밍을 이용해 UI에 대한 표시와 뷰 로직을 쉽게 분리할 수 있습니다.

React Provides a declarative API so that you don't have to worry about exactly what changes on every update.

앞서 말했듯이, 리엑트는 알아서 뷰 업데이트를 최적화 해줍니다.
리엑트는 최적화에 시간복잡도 O(n)에 해당하는 Diffing Algorithm을 이용하는데요,
이 알고리즘은 다음의 두 가지의 가정을 기반으로 수행됩니다.
1. 두 엘리먼트의 타입이 다르면 트리또한 다르게 만들어집니다.
2. 개발자는 key를 이용해 같은 자식 엘리먼트들을 서로 구분할 수 있게 리엑트에게 알려줄 수 있습니다.

1번에 의해 루트 엘리먼트의 타입이 다르면 dom을 지우고 다시 그립니다.

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

엘리먼트 타입이 같고 내부 속성만 달라진다면 생성된 기존의 엘리먼트 인스턴스와 내부 상태는 그대로 유지하고 virtual dom에 있는 새로운 돔의 props만 업데이트 합니다.
클래스 컴포넌트를 기준으로 render 메소드가 호출되고 내부 컴포넌트에 대하여 다시 diff algorithm이 재귀적으로 수행됩니다.

<div className="before" title="stuff" />

<div className="after" title="stuff" />

문서의 설명은 리엑트가 매우 합리적으로 동작한다는 것을 알려주고 있습니다.
그런데 리엑트의 리렌더링은 UI 변경이 없어도 발생할 수 있습니다.
diff algorithm은 UI 변경점 유무와 무관하게 수행된다는 것입니다.

...

const [needLog, setNeedLog] = useState(false);

useEffect(() => {
  setNeedLog(true);
},[])

return(
  <div>this text would never have been changed</div>
)

공식 문서의 TradeOffs 섹션을 보면 이러한 부분이 언급되어 있습니다.

React could rerender the whole app on every action; the end result would be the same. Just to be clear, rerender in this context means calling render for all components, it doesn’t mean React will unmount and remount them.

문서에는 렌더링 속도에 대한 부분이 나와있지 않습니다.
속도가 빠르다는 말이 나와있지 않습니다.
virtual-dom이 non-virtual-dom 보다 빠르다는 내용은 나와있지 않습니다.
UI적으로 변경되는 부분에 대하서만 돔 트리를 업데이트하기 때문에 virtual-dom이 빠르다는 편견이 생긴 것 같습니다.

그래서 정말 리엑트가 다른 프레임워크랑 비교했을 때 수치적으로 안좋은 결과가 나오고 있을까요?
여러 프레임워크를 비교한 벤치마크 글은 흔히 찾아볼 수 있습니다. 이 중 유명도와 실제 점수를 함께 나타낸 글을 하나 소개합니다.

신기하게도 벤치마크 점수의 랭크에 올라가 있는 프레임워크 목록에는 생소한 이름들로 가득합니다.
svelte를 제외하고 elm, imba, hyperapp, solid.js를 사용해보시거나 들어보신 적이 있나요?

현대의 많은 프레임워크들이 virtual dom을 기반으로 뷰를 업데이트 하고 있습니다.
react, vue 2, angular 2 모두 virtual dom을 사용합니다.

깃허브 스타가 많기에, 채용시장의 수요가 많기에 우리는 익숙하게 들어온 기술들이 최적의 해결안을 제시해준다고 굳게 믿으며 너무나도 당연하게 create-next-app으로 새로운 프로젝트를 시작합니다.

fancy하고 brand new한 라이브러리를 익히기 위해 우리는 많은 시간을 투자하고 새로운 인터넷 강의를 구입하기도 합니다.

그래서 하고자 하는 말씀이 뭔가요?

새로운 환경에 대한 적응과 능동적인 학습은 너무도 훌륭하고 장려받아 마땅한 자세입니다!
다만, 내가 공부하는 기술을 왜 사용하는지, 새로운 기술을 프로젝트에 도입할때 간과하고 있는 점이 없지는 않은지 한번 살펴보는 것도 필요한 자세라고 생각합니다.

하루가 멀다하고 바뀌고 업데이트 되는 신기술들을 익히느라 brand new technology fatigue syndrome (이런 말은 없습니다. 제가 즉석으로 지어낸 단업니다.)에 필요 이상의 에너지를 쓰고 있지는 않은지 한번 되돌아보면 좋을 것 같습니다.

프레임워크는 일시적이나 언어는 지속된다.

profile
성장을 향한 작은 몸부림의 흔적들

0개의 댓글