책정리/모던 리액트 Deep Dive

모던 리액트 Deep Dive 2.1 - 2.2 :: JSX와 리액트 파이버

뽀글보리 2024. 2. 27. 21:58
반응형

2.1 JSX란?

JSX는 리액트가 등장하면서 페이스북에서 소개한 새로운 구문이지만, 반드시 리액트에서만 사용하라는 법은 없다. JSX는 XML과 같은 내장형 구문이며, 리액트에 종속적이지 않은 독자적인 문법이다.

JSX는 반드시 트랜스파일러를 거쳐야 비로소 자바스크립트 런타임이 이해할 수 있는 의미있는 자바스크립트 코드로 변환된다.

 

JSX의 설계목적

다양한 트랜스파일러에서 다양한 속성을 가진 트리 구조를 토큰화해 ECMAScript로 변환한다.

 

2.1.1 JSX의 정의

JSX를 구성하는 가장 기본 요소

  1. JSXOpeningElement: <JSXElement JSXAttributes(options)>
  2. JSXClosingElement: <JSXElement />
  3. JSXSelfClosingElement: <JSXElement JSXAttributes(optional) />
  4. JSXFragment: <>JSXChildren(optional)</>

JSX 식별자로 가능한 것

  • $, _로 시작 가능, 숫자로 시작 불가능
  • JSXIdentifier:JSXIdentifier의 조합. : 를 통해 서로 다른 식별자를 이어주는것도 가능하다.
  • SXIdentifier.JSXIdentifier 의 조합. 즉 .을 통해 서로 다른 식별자를 이어준다.

2.1.3 JSX는 어떻게 자바스크립트에서 변환될까?

babel/puglin-transform-react-jsx 플러그인은 JSX 구문을 자바스크립트가 이해할 수 있는 형태로 변환한다.

var ComponentA = React.createElement(
  'div',
  null,
  React.createElement(
		'span',
		 null,
		 'hello world'
  )
)

babel playground 참고

 

2.1.4 정리

  • JSX는 사실 꼭 리액트에서만 사용하는 문법이 아니라 Preact, SolidJS, Nano JSX 등 다양한 라이브러리에서도 사용하고 있다.
  • JSX 내부에 문법이 많아질수록 복잡성이 증대하면서 코드 가독성을 해칠 수 있다.
  • 어떨 때에는 createElement를 사용해 직접 컴포넌트를 구성하는 편이 더 효율적일 수 있다.

2.2 가상 DOM과 리액트 파이버

2.2.1 DOM과 브라우저 렌더링 과정

브라우저 렌더링 과정

  1. 브라우저가 사용자가 요청한 주소에 대한 HTML 파일을 다운로드한다.
  2. 브라우저 렌더링 엔진은 HTML 파일을 파싱해 DOM 노드로 구성된 트리(DOM)를 만든다.
  3. 2번 과정에서 CSS 파일을 만나면 해당 CSS 파일도 다운로드 한다.
  4. 브라우저 렌더링 엔진은 CSS를 파싱하여 CSSOM 트리를 만든다.
  5. 2번에서 만든 DOM 노드를 순회한다. (이때, 사용자 눈에 보이는 노드만 방문하기에 display: none과 같이 보이지 않는 요소는 제외하고 조금이라도 빠르게 한다.)
  6. 5번에서의 순회된 노드를 대상으로 CSSOM 방문 & 스타일 정보를 노드에 적용한다.
    1. 레이아웃(layout & reflow): 노드가 브라우저 화면의 어느 좌표에 정확히 나타나야 하는 지 계산하는 과정
    2. 페인팅(painting): 레이아웃 단계를 거친 노드에 색과 같은 실제 유효한 모습을 그리는 과정

2.2.2 가상 DOM의 탄생 배경

웹페이지가 변경되는 여러가지 상황

  • 특정 요소의 색상이 변경되는 경우 ⇒ 페인팅만 다시 일어난다.
  • 요소의 사이즈가 변경되는 경우 ⇒ 요소의 위치와 크기를 재계산하기 위해 레이아웃이 일어나고 필연적으로 리페인팅이 발생한다.
  • 많은 자식 요소를 가지는 요소의 변경이 일어나는 경우 ⇒ 모든 하위 자식 요소가 모두 변경이 일어나야 한다.

가상 DOM이 필요한 이유

개발자 입장에서, 사용자의 인터랙션에 따라 DOM의 모든 변경 사항을 추적하는 것은 개발자 입장에서 너무나 수고스러운 일이다. vanilla JS를 사용한다면, dom id 및 속성을 모두 파악해야 한다.

 

가상 DOM은 웹페이지가 표시해야할 DOM을 일단 메모리에 저장하고 리액트가 실제 변경에 대한 준비가 완료됐을 때 실제 브라우저의 DOM에 반영한다. DOM 계산을 브라우저가 아닌 메모리에서 계산하는 과정을 한 번 거치게 된다면 실제로는 여러 번 발생했을 렌더링 과정을 최소화할 수 있다.

 

가상 DOM이 항상 빠르지 않다.

무조건 빠른 것이 아니라 대부분의 상황에서 웬만한 애플리케이션을 만들 수 있을 정도로 충분히 빠르다.

2.2.3 가상 DOM을 위한 아키텍처, 리액트 파이버

리액트 파이버는 리액트에서 관리하는 자바스크립트 객체로, 가상 DOM과 실제 DOM을 비교하여 변경 사항을 수집하고, 둘 사이를 비교하여 화면에 렌더링을 요청한다.

 

⇒ 가상 DOM과 렌더링 과정 최적화를 가능하게 해준다.

 

참고: https://www.youtube.com/watch?v=ZCuYPiUIONs

 

✅ 리액트는 ❌ 가상 DOM ❌이 아닌 ⭕️ Value UI ⭕️, 값을 가지고 있는 UI를 관리하는 라이브러리다. UI를 문자열, 숫자, 배열과 같은 값으로 관리하고 변수에 값을 보관하고, 코드 흐름에 따라 관리하고 표현한다.

 

비동기로 일어나는 파이버 작업들

과거에는 스택을 통해 동기적으로 작업이 이루어졌다. 그러나 지금은 비동기적으로 구성되어있다.

하나의 작업 단위를 처리하고 → finishedWork() 작업 마무리 → 커밋하여 변경사항을 만든다.

  1. 하나의 작업 단위를 처리한다.
    • 렌더 단계에서 리액트는 사용자에게 노출되지 않는 모든 비동기 작업을 수행한다.
    • 파이버의 작업, 우선순위를 지정하거나 중지시키거나 버리는 등의 작업이 일어난다.
      • 에니메이션과 같이 우선순위가 높은 작업은 가능한 한 빠르게 처리한다.
  2. 커밋 단계에서는 앞서 언급한 것처럼 DOM에 실제 변경 사항을 반영하기 위한 작업. commitWork()가 실행되는데, 이 과정은 앞서와 다르게 동기식으로 일어나고 중단될 수도 없다.
    • 이러한 업데이트 작업들은 우선순위가 높은 다른 업데이트가 오면 중단될수도 있다. 따라서 최적의 순위로 작업을 완료할 수 있게끔 만든다.

✅ 커밋 단계에서 수행되는 더블 버퍼링, 새로운 workInProgress 트리를 빌드하고, 최종적으로 렌더링되어 반영이 완료되면 current가 workInProgress로 변경된다.

✅ 트리를 업데이트 하는 일은 계속 일어나기 때문에, 가급적 객체를 새롭게 만들기 보다는 기존의 파이버 객체를 재활용하여 내부 속성값만 바꾸는 형태로 트리를 업데이트한다.

2.2.4 파이버와 가상 DOM

  • 파이버는 리액트 컴포넌트에 대한 정보를 1:1로 갖고 있다.
  • 불완전하게 표시될 수 있으므로, 최종적인 결과물만 실제 브라우저 DOM에 적용한다.

✅  만약 이러한 도움 없이 개발자가 직업 DOM을 수동으로 하나하나 변경해야 한다면 어떤 값이 바뀌었는지, 또 그 값에 따라 어떠한 값이 변경됐고 이와 관련된 것들이 무엇이었는지 파악하기가 매우 어려웠을 것인데 이러한 문제들을 효율적인 메커니즘으로 해결한것이 바로 리액트의 핵심

반응형