해당 시리즈는 제 다른글과 비교했을때 특히나 "메모"의 성격이 짙습니다.
그럼에도 최대한 정제해서 작성해보려 노력하겠습니다. 잘 부탁드립니다
해당 시리즈는 원문의 버전인 v16.12.0 버전 함수형 컴포넌트와 브라우저 환경을 기준으로 진행합니다.
하지만 현 시점에서 추가되거나 변경된 정보들을 추가해서 보완해볼 계획입니다.
react는 굉장히 많이 사용해 보았지만 과연 이를 "잘" 사용했다고 말할 수 있을까 싶었습니다.
컴포넌트를 만들줄은 알며,
여차저차 해가며 여러 프로젝트를 수행하기도 하며,
심지어 월급을 받고 일하기도 했었지만
여전히 react가 VDOM을 사용해서 어떤 이점이 있는지 모르며,
Fiber 객체가 VDOM의 어떤 영향을 주고 있으며,
어떤 상황에서 Reconciliation 일어나며,
hook은 컴포넌트와 어떻게 어떤 방식으로 연결되어 있는지,
아직도 모르기도 합니다.
해당 시리즈의 흐름은 React 톺아보기 를 따라가려 합니다.
하지만 제가 학습하면서 바로 이해하지 못해 머뭇거리던 순간들을 공유하고 기록하려합니다.
그럼 시작해보겠습니다.
React 라이브러리에서 가장 핵심이 되는 패키지 입니다.
일반적으로 프로젝트에서 사용될때는 다음과 같이 사용됩니다.
import {useState} from "react"
https://github.com/facebook/react/blob/main/packages/react/package.json (하지만 해당 사진은 원문의 버전인 16.12.0과는 다른 현재의 package.json입니다. 하지만 설명을 위해 추가했습니다.)
해당 package.json을 확인해보면, 정말 다른 모듈들에 대한 의존성이 극히 적다는것을 확인할 수 있습니다. (loose-envify는 React 페키지에서 process.env.NODE_ENV 등과 같은 환경변수를 관리하기 위해서 사용되었습니다.)
이는 react가 정말 책임분리가 완벽하게 되어 있음을 의미합니다.
React 라이브러리의 react 패키지는 "UI 컴포넌트의 생성"과 "상태관리"에만 집중합니다.
특정 플랫폼(웹-모바일)에 대한 렌더링(해당 시리즈에서는 특별하게 정의하지만 여기서는 일반적인 정의에 가까운 "브라우저 렌더링"을 의미입니다) 은 다른 패키지 react-dom, react-native 가 수행합니다.
이를 통해서 각기 다른 패키지가 특정 역할에 집중할 수 있도록 합니다.
// react/packages/react/index.js
export {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
act as unstable_act,
Children,
Component,
Fragment,
Profiler,
PureComponent,
StrictMode,
Suspense,
cloneElement,
createContext,
createElement,
createFactory,
createRef,
createServerContext,
use,
forwardRef,
isValidElement,
lazy,
memo,
cache,
... ,
useId,
useCallback,
useContext,
useDebugValue,
useDeferredValue,
useEffect,
experimental_useEffectEvent,
useImperativeHandle,
useInsertionEffect,
useLayoutEffect,
useMemo,
useOptimistic,
useSyncExternalStore,
useReducer,
useRef,
useState,
useTransition,
version,
} from './src/React';
일반적으로 사용하던 createElement, useState등이 이에 포함된것을 확인할 수 있습니다.
react-native, react-router-dom 과 같이 react의 render phase를 모두 지나 실질적인
DOM tree에 적용되도록 하는 페키지 입니다.
react는 유저의 event를 내부적으로 호스트 이벤트 래핑(wrapping) 하여 사용합니다.
호스트 이벤트 래핑(wrapping)은 React에서 네이티브 브라우저 이벤트를 SyntheticEvent 객체로 감싸는 과정을 말합니다.
예를 들어, 사용자가 웹 페이지에서 버튼을 클릭할 때, 브라우저는 클릭 이벤트를 생성합니다. React는 이 네이티브 클릭 이벤트를 감싸는 SyntheticEvent 객체를 생성하여 React 컴포넌트의 이벤트 핸들러에 전달합니다. 이렇게 함으로써, React는 다양한 브라우저에서 발생하는 이벤트들을 일관되게 처리할 수 있으며, 개발자는 브라우저 간의 차이점을 신경 쓰지 않고 이벤트를 처리할 수 있습니다.
React의 이벤트 풀링(event pooling)은 성능 최적화를 위해 이벤트 객체를 재사용하는 방식입니다. 예를 들어, 사용자가 버튼을 클릭하면 React는 이벤트를 처리하고, 이벤트 객체를 '풀(pool)'에 반환합니다. 다음 이벤트가 발생하면, 이 풀에 저장된 이전 객체가 재사용되어 새로운 이벤트 정보로 업데이트됩니다. 이 방식은 메모리 사용을 줄이고, 애플리케이션의 성능을 향상시키는 데 도움을 줍니다. React는 이벤트 처리 후에 이벤트 객체의 모든 속성을 null로 설정하여 객체를 초기화하고, 필요할 때 다시 사용할 준비를 합니다.
React의 이벤트 풀링은 성능을 최적화하기 위한 것이며, 일반적인 사용에서는 문제가 되지 않습니다. 이벤트 처리가 끝난 후 React는 이벤트 객체를 풀에 반환하기 전에 모든 속성을 null로 설정하여 초기화합니다. 이는 메모리 사용을 줄이기 위함이며, 각 이벤트 처리가 독립적으로 이루어지도록 합니다. 새 이벤트가 발생하면, 풀에서 가져온 객체는 새로운 이벤트 데이터로 업데이트됩니다. 따라서, 이벤트가 연속적으로 발생하더라도 각 이벤트는 독립적인 데이터를 가지고 처리됩니다. 이벤트 객체의 재사용은 React의 성능 향상을 위한 중요한 기능입니다. 하지만 React 이벤트 폴링에는 문제가 있습니다.
+) 하지만 본 글에서 다룰 react 버전인 16.12.0v 에서는 해당 문제에 대한 논의는 "아직" 진행하지 않도록 하겠습니다. 17버전에서 react pooling이 제거 되었으며, 이에 대한 글은 추후 작성하도록 하겠습니다.
++) 현재 버전을 고려하여 이 단락에서는 "react는 event를 특별한 방식으로 다루고 있으며, SyntheticEvent라는 객체로 event가 wrapping 되어 있구나" 정도로 이해해도 좋을것 같습니다.
리액트를 처음부터 배워보자. — 06. 합성 이벤트와 Event Pooling
React v17.0 Release
리액트는 여러 가지 이유로 작업을 비동기로 실행시켜야 합니다. 예를 들어서 사용자의 인터랙션이 필요한 작업들, 사용자가 버튼을 클릭해서 새로운 페이지로 이동해 데이터를 불러와야 하는 경우, 데이터를 불러오는 작업을 수행후 이를 React 는 비동기 적으로 작업을 수행해야만 합니다. 이러한 작업은 Task란 이름으로 우선순위에 따라 스케줄링 됩니다.
예를 들어, 사용자의 입력 처리와 같은 긴급 작업은 데이터 로딩 같은 백그라운드 작업보다 높은 우선순위를 가집니다. 이를 통해 중요한 작업을 먼저 처리하여 앱의 반응성을 유지합니다
Task를 실행하기에 가장 적기인 때를 알고 있는 것이 scheduler이며 리액트는 비동기 실행의 책임을 전문가인 scheduler에게 위임합니다. 이 패키지는 호스트 환경에 의존적입니다.
'호스트 환경에 의존적'이라는 표현은 React Scheduler가 실행되는 환경(예: 웹 브라우저, 서버, 네이티브 모바일 환경 등)의 특성에 따라 다르게 작동할 수 있다는 것을 의미합니다. 다양한 환경에서 최적의 성능을 발휘하기 위해 Scheduler는 해당 환경의 특징과 가능성을 고려하여 작업을 스케줄링합니다.
react의 핵심기능을 내포하고 있습니다. 16버전 이후로 react가 버전업을 하면서 fiver archtecture를 도입하게 되었습니다!
react-fiber 에 정의 되어있는, FiberNode 입니다. 현 시점에선 해당 패키지가 중요한 역할을 수행함을인지하고만 넘어가도 괜찮습니다.
본 글의 원문에서는 다음과 같은 용어를 정의합니다.
용어 정의의 방식은 크게 두 가지로 구분할 수 있습니다.
사회적으로 약속된 정의와 특정 상황에서 임시적으로 명칭하는 방식입니다. 해당 시리즈에서는 후자의 방식으로서 정의되며, 누군가와 공유해야 하는 상황에서 해당 정의는 혼동을 줄 수 있습니다. 오로지 해당 시리즈에서 React를 이해하는 방식으로만 사용되길 바랍니다.
해당 시리즈에서 원문은 React 톺아보기을 총칭하며,
본문은 해당 시리즈를 의미합니다.
React에서 기본적으로 제공하는 컴포넌트로, 예를 들어 React.Fragment가 있습니다. (+ React.memo , React.StrictMode 등) Fragment는 DOM에 별도의 노드를 추가하지 않고 여러 자식들을 그룹화할 때 사용됩니다.
import React, { Fragment } from 'react';
function Example() {
return (
<Fragment>
<ChildA />
<ChildB />
</Fragment>
);
}
특정 플랫폼에 속한 기본 HTML 또는 Naitve 컴포넌트를 말합니다. 예를 들어 웹에서는 <div>
, <span>
과 같은 HTML 요소가 여기에 해당합니다.
function Example() {
return <div>Hello, world!</div>;
}
Naitve 컴포넌트란 React Native와 같은 플랫폼에서 사용되는 컴포넌트를 말합니다. 이 컴포넌트들은 특정 모바일 운영 체제에 맞춰진 UI 요소로, 예를 들어 View, Text, Button 등이 있습니다.
개발자가 직접 정의하는 컴포넌트로, 특정 기능이나 스타일을 가진 컴포넌트를 만들 수 있습니다.
function CustomButton({ label }) {
return <button className="custom-button">{label}</button>;
}
일반적으로 컴포넌트 렌더링이라한다면, 해당 컴포넌트가 호출되는 시점부터 브라우저에 그려지는 모든 단계를 떠올립니다. 하지만 React의 동작을 세부적으로 파악하기 위해 좀 더 세분화 합니다.
컴포넌트 호출은 reconciler에서 합니다. 그 후 VDOM 재조정 작업이 들어가고 renderer를 이용하여 DOM에 마운트합니다.
컴포넌트 호출과 DOM 삽입은 별개입니다. 그리고 리액트에서는 DOM 삽입과 화면에 그려지는 것 또한 별개로 다루고 있습니다.
앞으로 렌더링은 컴포넌트가 호출되어 자식을 반환하고 VDOM에 적용하는 일련의 과정을 일컫는다고 하겠습니다. DOM 작업은 제외 입니다. 컴포넌트 호출은 그저 함수 ‘호출’로만 생각하세요. ‘React element를 반환한다.’ 그 이상 그 이하도 아닙니다.
또한 컴포넌트를 DOM에 삽입하는 것을 마운트, 브라우저가 화면에 그리는 걸 페인트라 정의하겠습니다.
페인트가 브라우저 렌더링 과정의 특수한 한 단계를 지칭하기 때문에 혼동을 줄 수 있습니다만, 원문에서는 브라우저가 그리는 동작 모두를 일컷는다고 이해하는것이 좋습니다. 해당 과정이 궁금하다면 제가 작성한 브라우저가 그리는 법을 참고하시기를 추천합니다.
즉 React의 컴포넌트의 모든 과정을 이렇게 해석할 수 있습니다.
컴포넌트 렌더링(컴포넌트가 호출되어 자식을 반환하고 VDOM에 적용) -> Dom 삽입(마운트) -> 브라우저가 화면에 그림(paint)
virtual Dom 을 의미
리액트는 더블 버퍼링 형태로 VDOM을 설계했습니다.
current는 마운트(React의 컴포넌트가 호출이 종료된 후 Dom에 적용)가 끝난 트리입니다.
리액트는 더블 버퍼링 형태로 VDOM을 설계했습니다.
workInProgress는 업데이트가 적용 중인 트리입니다.
컴포넌트의 정보를 담은 모델 객체입니다. 컴포넌트가 반환하는 것은 JSX가 아닌 이 React element입니다. JSX는 단지 개발자가 쓰기 편한 문법적 표현으로, 실제 React 애플리케이션에서는 ReactElement가 중요한 역할을 합니다.
VDOM의 노드 객체입니다. 이 객체는 React element를 VDOM에 올리기 위해 확장한 객체입니다. fiber를 통해 컴포넌트의 상태, 훅, 라이프 사이클 등 대부분이 관리됩니다.
원문인 React 톺아보기 - 01. Preview의 글을 바탕으로 첫 글에 대해 주석을 담아 학습을 진행해 보았습니다.
개발 공부를 하다보면 앞선 누군가들의 글들이 등불이 되어 길을 밝혀주곤 합니다.
저에겐 해당 글이 빛이 되어 주었습니다.
한번 더 원작자인 @goidle님께 해당 글을 작성할 수 있게 허락해주신것에 감사의 인사를 전합니다😊