[POB#12] 리액트 성능 최적화

dongwon·2021년 8월 6일
1

원티드-프리온보딩

목록 보기
9/25
post-thumbnail

8월 5일 강의를 이해한 대로 정리한 글입니다.
틀린 내용이 있으면 지적해 주세요 !

React 성능 최적화

❓ 꼭 해야 하나

많이 해봤자 0.001초 개선되기 때문에 정말 중요한 건 아닙니다. 하지만 D3.js, 핀테크와 같이 성능이 중요한 회사는 필요합니다.
하지만 아는 것과 모르는 것은 다르기 때문에 알고 갈 필요는 있습니다.

❓ 리액트 최적화를 언제쯤 해야 할까

리액트 최적화를 처음부터 하려다 보면 효율을 생각해야 하기 때문에 오히려 코드의 가독성이 떨어질 수 있습니다. 따라서 처음부터 최적화를 진행하지 않고 제품의 서비스가 너무 느리다 싶을 때 해도 늦지 않습니다.

🎯 최적화의 주요 목적 : 돔 리렌더링 최소화

리렌더링 막는 방법 4가지

1. Memoiztaion

input, output이 똑같다면, 그대로 다시 쓸 수 있게 하는 기법

기본적으로 컴포넌트의 리렌더링은 state나 props가 변할 때 일어납니다. 하지만 자식 컴포넌트의 state나 props가 변하지 않았는데, 부모 컴포넌트의 state, props가 변해 리렌더링 된다면, 자식 컴포넌트 또한 리렌더링 됩니다. 이것을 막기 위해 Memoiztaion 기법을 사용합니다.

React.PureComponent, React.memo

Memoiztaion 기법을 사용하는 방법으론 React.PureComponent(클래스형)과 React.memo(함수형) 두 가지가 있습니다. 같은 props 인데 리렌더링이 자주 일어날 것 같은 컴포넌트에 적용하면 됩니다.

// 클래스형
class CounterButton extends React.PureComponent { ... }

// 함수형
const CounterButton = ({ color }) => { ... }

export default React.memo(CounterButton);

useMemo hook

useMemomemoizing을 return 하는 hook입니다. 즉 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있습니다. 함수와 의존 값을 받아와 의존 값이 달라질 때만 재연산을 합니다.

// utils/getSum.js
const getSum = (numbers) => {
  return numbers.reducer((a, b) => a + b);
};

// 컴포넌트
const Sum = () => {
  const arr = [1, 2, 3, 4];

  // arr이 변하지 않는 이상 재연산을 하지 않는다.
  const value = useMemo(() => getSum(arr), [arr]);
};

2. Avoid Object/Array Mutation ( 불변성 지키기 )

불변성이 지켜지지 않는다면 객체/배열의 값이 새로워져도 바뀐 것을 감지할 수 없습니다. 즉, 불변성을 지켜야 하는 이유는 이전 Virtual DOM에 있던 내용과 현재 내용을 빠르게 비교해 바뀐 DOM만 실제 DOM에 적용하기 위함입니다.

기존의 값을 직접 수정하지 않으면서 새로운 값을 만들어 낸다.

  • ES6의 spread syntax 이용
   // 기존의 words의 복사 본에, 'marklar'를 추가합니다.
   (O) : words: [...state.words, 'marklar']

   // 기존의 words에 marklar를 추가합니다.
   (X): words: state.words.concat(['marklar'])
  • ES6의 Object.assign 이용
   // colormap을 그대로 가져오고 교체할 부분만 교체
   (O) return Object.assign({}, colormap, { right: "blue" });

   // 객체에 바로 접근하여 교체
   (X) olormap.right = "blue";
  • ES8의 object spread properties 이용
   // colormap 복사 본, 교체할 부분만 교체
   (O) return {...colormap, right: 'blue'};

3. 참조형 데이터를 주의하자.

Javascript 참조형의 특징
배열, 객체, 함수와 같은 타입은 값을 저장할 때 다른 곳에 저장하고 메모리 주소(참조 값)만을 가지고 있습니다. 따라서 직접 값을 바꾸려는 경우 참조 값만 변할 뿐,
원본 값은 변하지 않습니다.

참조형의 특징으로 인해 리렌더링 되는 상황 막기

  1. 이벤트 리스너와 같이 함수를 전달할 때 참조형의 특징으로 매번 리렌더링 됩니다. 이것을 막기 위해 useCallback hook을 사용합니다. useCallback함수의 이전 값을 기억하고 있어 함수 리렌더링을 막을 수 있습니다. 두 번째 인자에 의존성 값을 넣어 조절할 수 있싑니다.
const clickHandler = useCallback(() => {
  alert("clicked!");
}, []);
  1. 만약 props로 객체를 전달한다면, 객체를 미리 정의한 후 상수를 전달합니다. 객체 역시 참조형이기 때문에 객체를 전달할 때마다 리렌더링 됩니다.
// 올바른 방법
const MENU_ITMM = {
  name: "About",
  link: "/about",
};

function TopBanner() {
  return <MenuList menu={MENU_ITEM} />;
}

// 올바르지 않은 방법
function TopBanner() {
  return <MenuList menu={{ name: "About", link: "/about" }} />;
}

4. useEffect 다시 보기

useEffect hook은 두 번째 인자에 의존성 배열을 설정함으로써 리렌더링 되는 조건을 설정할 수 있습니다. 하지만 의존성 배열이 너무 길다면, 오히려 성능에 악영향을 끼칠 수 있습니다. 따라서 줄일 수 있다면 최대한 줄이는 것이 좋습니다.

const [toggle, setToggle] = useState(false);

useEffect(() => {
  setToggle(!toggle);
}, [toggle]);

다음과 같은 방법으로 줄일 수 있습니다.

useEffect(() => {
  setToggle((prev) => !prev);
}, []);
profile
데이원컴퍼니 프론트엔드 개발자입니다.

0개의 댓글