React 렌더링
1. 렌더링 이란?
React 애플리케이션은 컴포넌트라고 불리는 작은 조각들로 구성됩니다. 각 컴포넌트는 상태(State)와 속성(Properties)을 기반으로 UI를 렌더링하게 된다. 렌더링은 상태나 속성이 변경될 때 해당 컴포넌트를 갱신하고, 변경된 내용을 화면에 반영하는 프로세스를 포함한다.
함수
function MyComponent() {
return (
<div id="myDiv" className="container">
<h1>Hello, Virtual DOM!</h1>
<p>This is a virtual DOM example.</p>
</div>
);
}
- 리액트 함수 문법으로 컴포넌트를 작성을 한다
VirtualDOM(객체)
const virtualDOM = {
type: 'div',
props: {
id: 'myDiv',
className: 'container',
children: [
{
type: 'h1',
props: {
children: 'Hello, Virtual DOM!'
}
},
{
type: 'p',
props: {
children: 'This is a virtual DOM example.'
}
}
]
}
};
- 컴포넌트 기반으로 가상 돔의 객체가 생성이 된다
RealDOM
<div id="myDiv" class="container">
<h1>Hello, Virtual DOM!</h1>
<p>This is a virtual DOM example.</p>
</div>
- VirtualDOM 기반으로 HTML 이 생성하게 된다
정리하면 리액트에서는 함수를 만들면 VirtualDOM 렌더링이 되고 RealDOM 에 Commit 이 된다
그리고 개발자는 함수(컴포넌트)만 작성하면 리액트가 알아서 작성해준다
2. 리-렌더링
React Fiber
React Fiber는 React v16부터 도입된 새로운 조화(reconciliation) 알고리즘을 기반으로 하는 리액트의 내부 동작 원리 가상 DOM 트리를 효율적으로 조작하고 관리하기 위한 데이터 구조이다.
Fiber의 핵심적인 요소는 "Fiber Node"이다
Fiber Node는 가상 DOM 요소를 나타내는 자바스크립트 객체로서, React 엘리먼트에 대응하며 컴포넌트 트리를 표현한다.
각 Fiber Node는 해당 요소의 타입, 속성, 상태 등을 포함하고 있다.
Fiber Node의 구조는 다양한 정보를 포함하는데, 주요 속성은 다음과 같다
- Key: 요소의 키를 나타낸다. 키는 리스트 아이템을 식별하는 데 사용.
- Props: 요소의 속성(props)을 나타낸다. 컴포넌트에 전달된 속성들이 포함.
- State: 컴포넌트의 상태를 나타낸다. 컴포넌트의 내부 상태 정보가 여기에 저장.
- Parent, Child, Sibling: 컴포넌트 트리에서 부모, 자식, 형제 Fiber Node를 가리키는 포인터
React Fiber는 이러한 Fiber Node들을 조합하여 가상 DOM을 구성하고, 변경 사항을 효율적으로 처리하여 실제 DOM에 반영한다. Fiber의 도입으로 비동기적인 작업과 우선순위 기반의 렌더링이 가능해져서, 앱의 성능을 향상시키고 사용자 경험을 개선할 수 있게 되었다.
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return <button onClick={handleClick}>You pressed me {count} times</button>;
}
useState 안에 fiberNode 상태를 저장하게 된다 우리는 현재 count 의 상태값을 저장하고 버튼이 클릭시 count 숫자 값이 올라가면서 Re-render 하게 된다.
3. 리-렌더링 최적화
만약에 컴포넌트가 많거나 리-렌더링 하는 컴포넌트가 무거울때는 어떻게 해야될까?
리액트는 상태가 변경되면 전체를 리-렌더링 하게 된다 이러한 작업은 리액트 입장에서 너무 부담스러운 작업이다.
React Memo 를 사용하여 렌더링될때 memo 된 부분은 재사용할수 있게 된다.
하지만 memo 를 사용하게 된다고해서 이전에 있는것을 재사용하는것은 아니다.
동일한 값을 주었을때는 기존에 있는것을 사용하지만 다른값이 주어질때는 리-렌더링하게 된다
No useMemo
import React, { useState } from "react";
const NoMemoExample = () => {
const [a, setA] = useState(5);
const [b, setB] = useState(10);
// a * b의 결과를 계산합니다.
const calculateResult = () => {
console.log("Calculating result...");
return a * b;
};
const result = calculateResult();
return (
<div>
<p>
Result of {a} * {b} is {result}
</p>
<button onClick={() => setA(a + 1)}>Increment A</button>
<button onClick={() => setB(b + 1)}>Increment B</button>
</div>
);
};
export default NoMemoExample;
NoMemoExample 함수를 실행할때 calculateResult 함수안에 객체는 새롭게 생성이 되고 계속 생기는 객체의 참조는 서로 달라지게 되면서 리액트는 다르다고 판단하게 된다
useMemo
import React, { useState, useMemo } from "react";
const MemoExample = () => {
const [a, setA] = useState(5);
const [b, setB] = useState(10);
// a * b의 결과를 계산하고, a 또는 b가 변경될 때에만 재계산합니다.
const result = useMemo(() => {
console.log("Calculating result...");
return a * b;
}, [a, b]);
return (
<div>
<p>
Result of {a} * {b} is {result}
</p>
<button onClick={() => setA(a + 1)}>Increment A</button>
<button onClick={() => setB(b + 1)}>Increment B</button>
</div>
);
};
export default MemoExample;
위 예제에서 result는 useMemo를 사용하여 계산된 값입니다. a나 b가 변경될 때만 결과를 다시 계산하게 된다.
useCallback 도 비슷하다.
No useCallback
import React, { useState } from "react";
const NoCallbackExample = () => {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={incrementCount}>Increment Count</button>
</div>
);
};
export default NoCallbackExample;
fiberNode 에 값을 참조하기 때문에 호출할때마다 incrementCount 함수의 값은 다르다고 생각해서 리-렌더링하게 된다
useCallback
import React, { useState, useCallback } from "react";
const CallbackExample = () => {
const [count, setCount] = useState(0);
// 콜백 함수를 생성하고 해당 함수는 count를 증가시킵니다.
const incrementCount = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={incrementCount}>Increment Count</button>
</div>
);
};
export default CallbackExample;
참고자료
'Front-End > React' 카테고리의 다른 글
리액트 컴포넌트 패턴: 프레젠테이션 vs 컨테이너 (0) | 2024.02.19 |
---|---|
React Router v5에서 v6로의 업그레이드: API 단순화와 새로운 기능 소개 (2) | 2024.02.15 |
useState() 리액트 상태 관리 마스터하기 (0) | 2024.02.13 |
React 동작 과정 (0) | 2024.01.08 |
React18 변경한 부분 (0) | 2023.12.01 |