JSX는 반드시 트랜스파일러를 거쳐야 자바스크립트 런타임이 이해할 수 있는 의미있는 자바스크립트 코드로 변환된다. JSX의 설계 목적은 다양한 트랜스파일러에서 다양한 속성을 가진 트리 구조를 토큰화해 ECMAScript로 변환하는데 초점을 두고 있다.
(JSX 내부에 트리 구조로 표현하고 싶은 다양한 것들을 작성해두고, JSX를 트랜스파일이라는 과정을 거쳐 자바스크립트가 이해할 수 있는 코드로 변환)
JSX는 기본적으로 JSXElement, JSXAttribute, JSXChildren, JSXString 라는 4가지 컴포넌트를 기반으로 구성돼 있다.
JSXElement
JSX를 구성하는 가장 기본요소이다. HTML과 비슷한 역할을 한다.
JSXAttribute
JSXElement에 부여할 수 있는 속성을 의미한다. 단순히 속성을 의미하기 때문에 필수값이 아니고, 존재하지 않아도 에러가 나지 않는다.
JSXChildren
JSXElement의 자식 값을 나타낸다. 속성을 가진 트리 구조를 나타내기 위해 만들어졌기 때문에 JSX로 부모와 자식 관계를 나타낼 수 있으며 그 자식을 JSXChild이라 한다.
JSXString
HTML에서 사용가능한 문자열은 모두 JSXString에서도 가능하다.
JSX는 어떻게 자바스크립트에서 변환될까?
이 방식을 알려면 리액트에서 JSX를 변환하는 @babel/plugin-transform-react-jsx 플러그인을 알아야 한다.
const ComponentA = <A required={true}>Hello world</A>
const ComponentB = <>Hello world</>
const ComponentC = (
<div>
<span>Hello world</span>
</div>
)
'use strict'
var ComponentA = React.createElement(
A,
{
required : true,
},
'Hello world',
)
var ComponentB = React.createElement(React.Fragment, null, 'Hello world')
var ComponentC = React.createElement(
'div',
null,
React.createElement('span', null, 'hello world'),
)
두 결과물에는 다음과 같은 공통점이 있다.
정리
Preact, SolidJs 등 다양한 라이브러리도 JSX를 채용하고 있으며, 이 라이브러리들은 리액트와는 다르게 목적에 따라 사용할 수 있다.
JSX는 자바스크립트 코드 내부에 HTML과 같은 트리 구조를 가진 컴포넌트를 표현할 수 있다는 점에서 각광받고 있다.
가상 DOM이 왜 만들어졌는지, 실제 DOM과는 어떠한 차리가 있는지, 실제 DOM을 조작하는 것보다 빠른지에 대해 알아보자.
DOM은 웹페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 담고 있다.

다음 예시를 통해 위 과정이 어떻게 일어나는지 직접 살펴보자
#text {
background-color: red;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Hello</title>
</head>
<body>
<div style="width:100%">
<div id="text" style="width: 50%;">Hello2</div>
<div>
</body>
</html>
브라우저가 웹페이지를 렌더링 하는 과정은 복잡하고 비용이 든다. 하지만 요즘은 정보를 보여주는데 그치지 않고 사용자의 인터랙션을 통해 다양한 정보를 노출한다.
따라서 렌더링이 완료된 이후에도 사용자의 인터랙션으로 웹페이지가 변경되는 상황 또한 고려해야 한다.
특정 요소의 색상 변경 → 페인팅만 일어난다. (비용이 적게든다.)
사이즈 변경, 요소의 위치나 크기를 재계산 → 레이아웃 + 리페인팅이 일어난다. (비용이 많이 든다.)
또한 DOM 변경이 일어나는 요소가 많은 자식 요소를 가지고 있는 경우 하위 자식 요소도 덩달아 변경돼야 하기 때문에 더 많은 비용을 브라우저+사용자가 지불하게 된다.
이러한 렌더링 작업은 하나의 페이지에서 모든 작업이 일어나는 싱글 페이지 애플리케이션에서 더욱 많아진다. 라우팅이 변경되는 경우 사이드바나 헤더 같은 특정 요소를 제외하고 대부분의 요소를 삭제하고, 삽입하고, 위치를 계산하는 등의 작업을 수행해야 하므로 특히 이러한 과정이 두드러진다.
이러한 문제점을 해결하기 위해 탄생한 것이 바로 가상 DOM이다. 가상 DOM은 웹페이지가 표시해야 할 DOM을 일단 메모리에 저장하고 리액트가 실제 변경에 대한 준비가 완료됐을 때 실제 브라우저의 DOM에 반영한다.
가상 DOM과 렌더링 과정 최적화를 가능하게 해주는 것이 바로 리액트 파이버이다.
리액트 파이버란?
리액트에서 관리하는 평범한 자바스크립트 객체다.
파이버는 파이버 조정자가 관리하는데, 이는 앞서 이야기한 가상 DOM과 실제 DOM을 비교해 변경 사항을 수집하며, 이 둘 사이에 차이가 있으면 변경에 관련된 정보를 가지고 있는 파이버를 기준으로 화면에 렌더링을 요청하는 역할을 한다.
파이버의 목표는 리액트 웹 애플리케이션에서 발생하는 애니메이션, 레이아웃, 사용자 인터랙션에 올바른 결과물을 만드는 반응성 문제를 해결하는 것이다. 이를 위해 파이버는 다음과 같은 일을 할 수 있다.
중요한 것은 이러한 모든 과정이 비동기로 일어난다.
파이버는 단순 자바스크립트 객체로, 하나의 작업 단위로 구성돼 있다. 이러한 작업 단위를 하나씩 처리하고 finishedWork()라는 작업으로 마무리한다. 그리고 이 작업을 커밋해 실제 브라우저 DOM에 가시적인 변경 사항을 만들어 낸다.
파이버는 컴포넌트가 최초로 마운트되는 시점에 생성되어 이후에는 가급적이면 재사용된다.
tag : 파이버는 하나의 element에 하나가 생성되는 1:1관계를 가지고 있다. 1:1로 연결되는 것은 리액트의 컴포넌트일수도, DOM 노드일 수도, 혹은 다른 어떤 것일 수도 있다.

여기서 HostComponent가 바로 웹의 div와 같은 요소를 의미한다.
stateNode : 파이버 자체에 대한 참조 정보를 가지고 있으며, 이 참조를 바탕으로 리액트는 파이버와 관련된 상태에 접근한다.
child, sibling, return : 파이버 간의 관계 개념을 나타내는 속성이다. 파이버도 트리 형식을 갖게 되는데, 이 트리 형식을 구성하는데 필요한 정보가 이 속성 내부에 정의된다. 리액트 컴포넌트 트리와 다른 점은 children이 없다는것. 하나의 child만 존재한다는 점이다.
<ul>
<li>하나</li>
<li>둘</li>
</ul>
const l2 = {
return: ul,
index:1, // 여러 형제들 사이에서 자신의 위치가 몇번째인지 숫자로 표현한다.
}
const l1 = {
sibling: l2,
return: ul,
index:0,
}
const ul = {
// ...
child:li,
}
이렇게 생성된 파이버는 state가 변경되거나 생명주기 메소드가 실행되거나 DOM의 변경이 필요한 시점 등에 실행된다. 작업을 직접 처리하기도 하지만 스케줄링하기도 한다.
리액트 파이버 트리
파이버 트리는 리액트 내부에서 두개가 존재한다.
하나는 현재 모습을 담은 파이버 트리이고, 다른 하나는 작업 중인 상태를 나타내는 workInProgress이다. 리액트 파이버의 작업이 끝나면 단순히 포인터만 변경해 workInProgress 트리를 현재 트리로 바꿔버린다. 이러한 기술을 더블 버퍼링이라고 한다.
더블 버퍼링이란? 보이지 않는 곳에서 그 다음으로 그려야 할 그림을 미리 그린 다음, 이것이 완성되면 현재 상태를 새로운 그림으로 바꾸는 기법이다.

리액트에서도 불완전한 트리를 보여주지 않기 위해 더블 버퍼링 기법을 사용한다.
이러한 더블 버퍼링을 위해 트리가 두개 존재하며, 이 더블 버퍼링은 커밋 단계에서 수행된다.
파이버의 작업 순서
업데이트가 발생하면?
앞서 만든 current 트리가 존재하고, setState로 인한 업데이트 요청을 받아 workInProgress 트리를 다시 빌드하기 시작한다. 이때 파이버를 새로 생성하지 않고 기존 파이버에서 업데이트된 props를 받아 파이버 내부에서 처리한다. 업데이트는 시도때도 없이 일어나고 매번 새로운 파이버 객체를 생성하는 것은 리소스 낭비이기 때문이다.
과거에는 트리 업데이트 과정이 동기적으로 작동했지만, 현재는 우선순위를 할당하는 것이 가능하다고 한다.
리액트 컴포넌트에 대한 정보를 1:1로 가지고 있는 것이 파이버이며, 리액트 아키텍쳐 내부에서 비동기로 이루어진다. 실제 브라우저 구조인 DOM에 반영하는 것은 동기적으로 일어나야 하고, 처리해야 하는 작업을 가상에서, 즉 메모리 상에서 먼저 수행해서 최종적인 결과물만 실제 브라우저 DOM에 적용하는 것이다.
가상 DOM과 파이버는 단순히 브라우저에 DOM을 변경하는 작업보다 빠르다는 이유로만 만들어진 것은 아니다.
만약 이러한 도움 없이 개발자가 직접 DOM을 수동으로 하나하나 변경해야 한다면 어떤 값이 바뀌었는지 파악하기가 매우어려웠을 것이다.
가상 DOM과 리액트의 핵심은 브라우저의 DOM을 더욱 빠르게 그리고 반영하는 것이 아니라 바로 값을 UI에 표현하는 것이다.