리액트를 사용함에 있어서 여러가지 performance 측면의 개선 방법에 대한 글들이 많습니다.
그 중에서 많이 사용되고 있는 React memo에 대한 이야기입니다.
리액트 메모를 사용하기 전에 체크해볼 수 있는 몇 가지 사항들은 다음과 같습니다.
- production 빌드를 실행 중인지 확인합니다. (개발 빌드는 매우 느립니다.)
- 상태를 렌더링 트리에서 불필요하게 상속받고 있는지 확인합니다.
- React DevTools Profiler를 실행해서 리렌더링 되는 부분을 확인해보세요. (Highlight 옵션을 통해서 확인 가능)
만약에 비용이 비싸다면, 컴포넌트 자체를 memo()로 감쌉니다.
그리고 필요한 함수에 useMemo도 추가합니다.
그 외에도, 심각하게 느린 컴포넌트 환경은 많습니다.
그 때, memo를 성급하게 사용하기 전에 정리할 수 있는 부분들이 있습니다.
다음과 같이 비용이 비싼 render tree가 있다고 가정 합니다.
import { useState } from "react";
export const App = () => {
const [text, setText] = useState("");
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<SlowComponent />
</div>
);
};
const SlowComponent = () => {
const t1 = performance.now();
while (performance.now() - t1 < 300) {}
const t2 = performance.now();
console.log(t2 - t1 + " milliseconds");
return <p>아주 느린 컴포넌트</p>;
};
이 때 SlowComponent에 memo()를 사용해도 되겠지만, 사전 해결방법 두 가지를 제시합니다.
1. 상태를 아래로 내리기
렌더링 되는 트리를 살펴보면, 일부의 코드인 input만이 text와 연관되어있다는 사실을 알 수 있습니다.
그 부분을 <Input /> 컴포넌트로 추출하고, useState 연산을 자식 컴포넌트로 내립니다.
이제 text가 변경되면 Input 컴포넌트만 리렌더링 됩니다.
import { Input } from "./Input";
export const App = () => {
return (
<div>
<Input />
<SlowComponent />
</div>
);
};
const SlowComponent = () => {
const t1 = performance.now();
while (performance.now() - t1 < 300) {}
const t2 = performance.now();
console.log(t2 - t1 + " milliseconds");
return <p>아주 느린 컴포넌트</p>;
};
2. 그렇다면 상태값을 부모에 적용할 때는 어떻게?
위의 방법은 아래에서 모든 컴포넌트의 상태가 결정될 때 유용하지만, 부모에 그 상태를 다시 할당해야할 경우에는 사용하기 힘듭니다.
그럴 때는, 컨텐츠 자체를 끌어올려서 사용하는 방법이 있습니다. children을 사용해서 유용하게 만들어보시죠.
import { useState } from "react";
export const App = () => {
return (
<ContentLiftUp>
<SlowComponent />
</ContentLiftUp>
);
};
const SlowComponent = () => {
const t1 = performance.now();
while (performance.now() - t1 < 300) {}
const t2 = performance.now();
console.log(t2 - t1 + " milliseconds");
return <p>아주 느린 컴포넌트</p>;
};
const ContentLiftUp = ({ children }) => {
const [text, setText] = useState("");
return (
<>
<div className="text_box">{text}</div>
<input value={text} onChange={(e) => setText(e.target.value)} />
{children}
</>
);
};
이제 ContentLiftUp의 관심사를 가지고 있는 컴포넌트 조각이 있고, 그 상태에서 children으로 비용이 비싼 컴포넌트를 감싸고 있어서, 함수 내의 연산이 서로 영향을 주지 않을 수 있게 되었습니다.
위의 방법은 여러 컴포넌트 이점을 가집니다.
서버 컴포넌트를 Hydrate 할 수 있는 준비가 되었다면, ContentLiftUp 컴포넌트는 children을 서버로부터 받을 수도 있습니다.
최상위의 React 상태 업데이트 되는 부분 조차도 클라이언트에서 이 부분을 건너 뛰면서 작용할 것입니다.
memo로도 이와 같은 일을 할 수는 없습니다.
그렇게 했음에도 충분하지 않을 경우에만 memo를 사용하는 것이 더 효율적일 것입니다 : )
리액트 합성 모델에 대해서 더욱 더 공부하면서, 많은 것들을 배워나가요!
출처 : https://overreacted.io/before-you-memo/
'SPA > REACT' 카테고리의 다른 글
React의 미래: Create React App(CRA) 대신 Vite를 사용하자 (0) | 2023.04.15 |
---|---|
다크모드를 위해서 Context API보다 CSS 변수를 활용하세요 (0) | 2022.10.30 |
transition에 대한 물리엔진 패키지 - react-spring (0) | 2022.08.21 |
리플로우가 일어나는 환경에서 React Suspense 사용해보기 (0) | 2022.01.18 |
[React] useState의 초깃값 지연 (0) | 2021.01.01 |
댓글