리액트의 내부 동작을 알아보기 위해 JSer.dev님의 React Internals Deep Dive 블로그를 기반으로 Deep Dive를 시작해보려고한다.
해당 시리즈에서는 블로그 원문을 단순 번역하는게 아니라 내가 학습한것들을 기반으로 배운것과 시도해본것들을 기록 할 것이다.
혼자만 알아보는 막글을 쓰고싶지는 않아서 공개된 장소에 글을 작성하지만 혹여나 이 글을 읽게된다면 글에서 제공되는 정보가 틀릴 수 있으니 검증은 따로 충분히 해주셨으면 좋겠다. (물론 나 자신도 정보에 대한 검증을 위해 자료도 같이 첨부하며 정리 할 예정이다)
리액트의 내부 동작을 알아보기 위해 실제 리액트의 사용자(개발자)가 break point를 설정하고 디버깅을 할 수 있는 방법에 대해 알아보자.
break point를 지정하는 방법은 크게 2가지가 있다.
Javascript의 키워드(예약어)인 debugger 사용
function App() {
debugger;
const [count, setCount] = useState(0);
useEffect(() => {
debugger;
setCount(prevCount => prevCount + 1);
}, []);
return <div>currentCount: {count}</div>;
}
브라우저의 개발자 도구(Chrome 기준)에서 특정 DOM 요소의 하위 트리가 변경되었을때 break point를 만들 수 있다.
아래 사진처럼 특정 요소의 하위 트리가 변경되었을때 브레이크 포인트를 만든다면 하위에 어떠한 요소 하나라도 업데이트 된다면 브레이크 포인트로 작동해야한다.
일단 위에서 살펴본 방법으로 디버깅을 해보자. 개념적으로 먼저 살펴보기 위해 원문 블로그에서 제공하고 있는 데모 페이지를 활용 할 것이다.
a. 데모 페이지에 기본적으로 아래와 같은 break point가 debugger 키워드를 사용하여 지정되어 있다.
function App() {
const [count, setCount] = useState(1);
debugger;
useEffect(() => {
debugger;
setCount((count) => count + 1);
}, []);
return <button>{count}</button>;
}
ReactDOM.createRoot(document.getElementById("container")).render(<App />);
b. 개발자 도구를 연 후 id가 container인 노드에 break point를 걸어준다.
break point를 설정한 후 개발자 도구를 열고 새로고침을 하게되면 break point마다 코드의 실행이 멈추며 디버깅을 할 수 있다.
이때 콜스택을 첫번째 break point인 App function의 debugger 구문에서 코드 실행이 중단된다. 이때 콜스택이 어떻게 쌓였는지 살펴보자. 콜 스택이기 때문에 아래에 있는 요소가 더 먼저 실행된 함수라고 생각하면 된다.
위 콜스택에서 React가 렌더링을 어떻게 수행하는지에 대해 중요 몇가지 기능을 위주로 살펴보자. 지금은 단순히 용어를 보는것만으로는 이해가 되지 않으니 이정도의 흐름을 가지고 있구나 정도로 읽고 넘어가보자.
ReactDOMRoot.render
: 보통의 경우 React를 실행할때의 entry point인 index 파일에서 실행하는 render 함수이다. 여기까지는 개발자가 직접 코드단으로 수정할 수 있는 레벨이다.scheduleUpdateOnFiber
: React에게 렌더링 할 위치를 알려주는 함수이다. 초기 렌더링시에는 루트에서 호출된다.ensureRootIsScheduled
: 예약되어 있는 performConcurrentWorkOnRoot
가 있는지 확인하는 함수이다. 해당 동작은 중요한 역할을 한다고 하는데 React Scheduler 섹션에서 좀 더 자세히 알아보자.scheduleCallback
: React Scheduler의 일부이고 postMessage
에 의해 호출되는 비동기 함수라고 한다. 지금으로썬 이해하기 쉽지 않아보이니 이 또한 React Scheduler에서 자세히 알아보자.workLoop
: 브라우저의 event loop
처럼 React의 반응성 요소들을 지속적으로 처리하기 위한 동작이다. 이 개념도 React Scheduler에서 자세히 알아보자.performConcurrentWorkOnRoot
: 예약되어있는 작업이 실행되는 구분이며 이때 구성 요소가 실제로 렌더링 된다.