일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- CSS
- Fetch
- 추상화
- 리액트
- 객체지향프로그래밍
- REACT
- 클래스
- Props
- 패스트캠퍼스
- 불변성
- 웹개발
- 투두앱만들기
- JavaScript
- 부트캠프
- typeScript
- 상속
- frontend
- 노마드코더
- webdevelopment
- 타입스크립트
- OOP
- js
- 프론트엔드
- Hooks
- 캡슐화
- 자바스크립트
- 자바스트립트
- 논리연산자
- github
- Zustand
- Today
- Total
connecting dots
React | React Profiler로 성능 측정하기, React memo를 이용한 성능 최적화, 얕은 비교(shallow equal) 본문
React | React Profiler로 성능 측정하기, React memo를 이용한 성능 최적화, 얕은 비교(shallow equal)
dearsuhyun 2024. 8. 2. 12:55React Profiler로 성능 측정하기
App.js
A.js
모든 요소를 하나의 컴포넌트에
코드 설명
posts 배열을 순회하며 각 post 객체를 기반으로 리스트 항목(<li>)을 생성
{posts.map((post) => {
return (
<li key={post.id}>
<p>{post.title}</p>
</li>
);
})}
1. posts 배열:
• posts는 객체 배열입니다. 각 객체는 블로그 포스트와 같은 데이터 항목을 나타내며, id와 title 속성을 가집니다.
2. map 메서드:
• map 메서드는 배열의 각 요소를 순회하며 콜백 함수에서 반환된 결과로 새로운 배열을 만듭니다.
• posts.map((post) => { ... })는 posts 배열의 각 요소에 대해 콜백 함수를 실행합니다.
3. 콜백 함수:
• post는 posts 배열의 각 요소를 나타냅니다.
• 콜백 함수의 반환값은 새로운 배열의 요소가 됩니다. 여기서 반환값은 <li> 태그로 구성된 JSX 요소입니다.
4. <li key={post.id}>:
• <li> 태그는 리스트 항목을 나타냅니다.
• key 속성은 React에서 각 리스트 항목을 고유하게 식별하기 위해 필요합니다. 이는 React가 리스트를 효율적으로 업데이트하는 데 도움을 줍니다.
• post.id는 각 post 객체의 고유 식별자입니다.
5. <p>{post.title}</p>:
• <p> 태그는 각 포스트의 제목을 감싸고 있습니다.
• {post.title}은 post 객체의 title 속성을 JSX 내부에 삽입하여 제목을 표시합니다.
B.js
컴포넌트를 작게 쪼개 만든 버전
React Developer Tool --> google extension으로 다운 받아서 성능 측정 가능 !
--> 'h'를 눌렀을 때 리렌더링 속도를 비교했을 때 A가 더 빠름
B 컴포넌트의 문제점
현재 앱에서 B 컴포넌트는 B, List, Listitem, Message 컴포넌트로 나누어져 있음
이렇게 나눠준 이유는 재사용성을 위해서 뿐만 아니라 각 컴포넌트의 렌더링 최적화를 위해서를 이기도 함
예를 들어 input애서 글을 타이핑을 할 때 원해는 Message 컴포넌트와 그 state를 값을 가지고 있는 app 컴포넌트만
렌더링이 되어야 하는데 현재는 상관이 없는 다른 부분까지 렌더링이 되고 있음
React Memo 적용으로 문제 해결
원하는 컴포넌트를 React memo로 감싸주면 됨 !
React memo란 ?
리액트는 먼저 컴포넌트를 렌더링한 뒤, 이전에 렌더링 된 결과와 비교하여 DOM 업데이트를 결정함
만약 렌더링 결과가 이전과 다르다면 --> 리액트는 DOM을 업데이트함
이 과정에서 만약 컴포넌트가 React.memo()로 둘러쌓여 있다면, 리액트는 컴포넌트를 렌더링하고 결과를 메모이징(memoizing)함
그리고 다음 렌더링이 일어날 때 렌더링하는 컴포넌트의 props가 같다면 --> 리액트는 메모이징된 내용을 재사용 함 !
메모이제이션(memoization)이란 ?
주어진 입력값에 대한 결과를 저장함으로써 같은 입력값에 대해 함수가 한 번만 실행되는 것을 보장하는 것
(props 같으면 굳이 다시 만들 필요 없으니까 !)
React.memo가 props를 비교하는 방법은 ?
props 혹은 props의 객체를 비교할 때 얕은 비교를 함 !
얕은 비교와 React.memo의 연관성
React.memo는 기본적으로 얕은 비교를 사용하여 props가 이전과 동일한 경우 컴포넌트를 리렌더링하지 않습니다. 얕은 비교는 props 객체의 최상위 속성들만 비교하며, 중첩된 객체나 배열의 내부 속성들은 비교하지 않습니다. 따라서, props 객체의 최상위 속성 값이 변경되지 않으면 컴포넌트는 리렌더링되지 않습니다.
cf. props 객체의 최상위 속성들이란 ?
props 객체의 직접적인 자식 속성들을 의미함. 즉, 객체가 가지고 있는 1차 속성들로, 중첩된 객체나 배열의 내부 속성이 아닌 최상위에 위치한 속성들 !const props = { name: "Alice", age: 25, address: { city: "Wonderland", zip: "12345" }, hobbies: ["reading", "gaming"] };
• 최상위 속성들:
• name: “Alice”
• age: 25
• address: { city: “Wonderland”, zip: “12345” } (객체 전체가 최상위 속성)
• hobbies: [“reading”, “gaming”] (배열 전체가 최상위 속성)
• 중첩된 속성들 (최상위 속성에 포함된 속성들):
• address.city: “Wonderland”
• address.zip: “12345”
• hobbies[0]: “reading”
• hobbies[1]: “gaming”
const prevProps = { name: "Alice", age: 25, address: { city: "Wonderland", zip: "12345" }, hobbies: ["reading", "gaming"] }; const nextProps = { name: "Alice", age: 25, address: { city: "Wonderland", zip: "12345" }, hobbies: ["reading", "gaming"] }; const arePropsEqual = (prev, next) => { return ( prev.name === next.name && prev.age === next.age && prev.address === next.address && prev.hobbies === next.hobbies ); }; console.log(arePropsEqual(prevProps, nextProps)); // false
• name과 age는 원시 타입(문자열, 숫자)로서 값 자체를 비교합니다.
• address와 hobbies는 객체와 배열이므로 참조를 비교합니다.
• 비록 prevProps와 nextProps의 address와 hobbies가 동일한 내용을 가지고 있지만, 서로 다른 객체이므로 참조 비교(메모리의 위치)에서는 false를 반환합니다.
-->
prevProps.address와 nextProps.address는 내용은 같지만, 각각 다른 메모리 위치에 저장되어 있는 별개의 객체 !
따라서 참조 비교를 하면 서로 다른 메모리 위치를 가리키고 있으므로 false가 반환됨
쉬운 비유
• 참조 비교를 마치 두 사람의 집 주소를 비교하는 것이라고 생각해보세요.
• 두 집의 구조가 완전히 똑같더라도, 서로 다른 위치에 있다면 주소는 다르죠?
• 여기서 집 구조가 객체의 내용이고, 집 주소가 객체의 참조(메모리 위치)입니다.
결론
• 최상위 속성들: props 객체의 직접적인 자식 속성들입니다. 예를 들어, props.name, props.age, props.address, props.hobbies 등이 최상위 속성입니다.
• 중첩된 속성들: 최상위 속성 내부에 있는 속성들입니다. 예를 들어, props.address.city 또는 props.hobbies[0] 등입니다.
• 얕은 비교: 객체의 최상위 속성들만 비교합니다. 객체나 배열의 경우 참조를 비교합니다.
--> default 비교방식은 전체를 비교하는 건데 이 방식을 수정하고 싶다면 ?
==> React.memo()의 두 번째 매개변수로 비교함수를 넣어주면 됨
얕은 비교(shallow equal)
숫자, 문자열 등 원시자료형은 값을 비교하는 반면
객체, 배열 등 참조자료형은 값 또는 속성을 비교하지 않고, 참조되는 위치를 비교함
cf. 깊은 비교
1) object depth가 깊지 않은 경우: json.stringify() 사용
2) object depth가 깊은 경우: lodash 라이브러리의 isEqual() 사용
const obj1 = {a:1, b:2}
const obj2 = {a:1, b:2}
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)) // true
얕은 비교를 사용하는 상황 | 깊은 비교를 사용하는 상황 |
1) React.memo()에서 props를 비교할 때 2) 리액트 컴포넌트가 리 렌더링을 하기 전, - state 변경이 있을 때 - 부모 컴포넌트가 렌더링 될 때 |
1) state 변경이 있을 때 2) 부모 컴포넌트가 렌더링 될 때 3) 새로운 props가 들어올 때 4) shouldComponentUpdate에서 true가 반환될 때 5) forceUpdate가 실행될 때 |
객체의 깊이(object depth)
객체 내부에 중첩된 객체가 얼마나 깊이 위치해 있는지를 나타내는 개념
즉, 객체 안에 객체가 있고, 그 안에 또 객체가 있는 경우.
객체의 깊이가 깊다는 것은 이러한 중첩된 구조가 여러 층으로 구성되어 있다는 것을 의미함
1. 얕은 깊이:
• 객체가 1~2단계 정도의 중첩을 가지고 있는 경우.
• JSON.stringify()와 같은 간단한 방법으로 깊은 비교가 가능한 경우.
• 예: { a: 1, b: { c: 2 } } (깊이 2)
2. 깊은 깊이:
• 객체가 3단계 이상 중첩되어 있는 경우.
• JSON.stringify()를 사용하면 성능이 저하되거나, 비교의 정확도가 떨어질 수 있는 경우.
• lodash의 isEqual() 같은 라이브러리를 사용하여 깊은 비교가 필요한 경우.
• 예: { a: 1, b: { c: { d: { e: 5 } } } } (깊이 4)
깊은 비교 (Deep Comparison)
깊은 비교는 객체의 최상위 속성뿐만 아니라 중첩된 객체나 배열의 모든 속성까지 재귀적으로 비교하는 방식입니다. 객체의 내용이 동일하면 참조가 달라도 true를 반환합니다. 즉, 객체 내부의 모든 값이 동일한지 확인하는 방식입니다.
왜 깊은 비교가 필요한가?
깊은 비교는 객체의 구조가 깊고, 내용이 동일한지를 확인해야 할 때 필요합니다. 얕은 비교는 참조를 비교하기 때문에 중첩된 객체나 배열의 실제 내용을 비교하지 않습니다. 따라서, 내용이 동일한지 확인하려면 깊은 비교가 필요합니다.
React.memo()를 지양해야 하는 상황
props가 자주 변하는 컴포넌트는 메모이제이션의 이점을 얻기 힘듦
Reace.memo()는 리렌더링을 막기 위한 도구라기 보다는 성능 개선의 도구 !
리액트에서는 성능 개선을 위한 하나의 도구로 메모이제이션을 사용함
대부분의 상황에서 리액트는 메모이징 된 컴포넌트의 리 렌더링을 피할 수 있지만,
렌더링을 막기 위해 메모이제이션에 너무 의존하면 안 됨 (버그 유발 가능성 존재)
==> 리액트에서 렌더링 성능 최적화를 위해서는 컴포넌트를 분리하여 React.memo를 사용하면 됨
또한 React.memo 사용은 항상 좋은 것은 아니니까 profiler를 이용하여 성능상 이점이 있는지 확인 후 사용하는 것을 추천
'Online Class > DevCamp' 카테고리의 다른 글
React Project | Postcss란 ? (0) | 2024.08.04 |
---|---|
React Project | vite 란 ?, Create React App과 Vite 비교 (0) | 2024.08.04 |
Typescript | 함수 오버로딩, 접근 제어자, 제네릭(Generics) (0) | 2024.07.29 |
React | React 불변성 (0) | 2024.07.26 |
React | 구조분해할당(Destructuring) (0) | 2024.07.26 |