코딩하는라민

[React] 컴포넌트 최적화를 위한 React.memo 본문

Core/React

[React] 컴포넌트 최적화를 위한 React.memo

코딩하는라민 2024. 5. 7. 17:55
728x90
반응형

[React] 컴포넌트 최적화를 위한 React.memo

React.memo 란?

`React.memo` 는 고차컴포넌트(HOC)로 컴포넌트 자체를 `메모이제이션`한다.

컴포넌트의 props 가 바뀌지 않은 경우, 리렌더링을 방지해 컴포넌트의 성능을 향상시킬 수 있다. 컴포넌트의 props 가 변한다면 다시 리렌더링된다.

 

사용방법

사용 방법은 간단하다. 리렌더링을 막고자 하는 컴포넌트를 `memo` 로 감싸주면 된다.

props 가 없는 컴포넌트도 사용이 가능하다.

import { memo } from 'react'

function MyComponent(props) {
  return (
    <>
      <h1>{props.title}</h1>
      <p>{props.content}</p>
    </>
  )
}

export default memo(MyComponent)

 

memo 를 사용하면 부모 컴포넌트가 리렌더링 되더라도 props 의 변화가 없다면 자식 컴포넌트는 리렌더링되지 않는다.

 

메모이제이션

memo 는 컴포넌트 자체를 기억해놨다가 props 가 이전과 같다면 기억해둔 컴포넌트의 결과를 반환한다.

즉, props, state, context 가 변경되지 않았다면 동일한 결과를 반환해야한다.

이를 `메모이제이션` 방식이라고 할 수 있다.

 

memo 를 사용해도 컴포넌트가 리렌더링되는 경우

memo 로 wrapping 된 컴포넌트는 props 가 변경되지 않는 한 리렌더링 되지 않는다.

 

하지만,

전역 상태 관리 도구를 사용하는 경우 state 가 변경될 때마다 해당 컴포넌트가 리렌더링된다.

이는 컴포넌트가 전역 state 에 의존하기 때문이다.

 

이 경우 전역 state 를 자식 컴포넌트의 props 로 내려주고, 자식 컴포넌트는 memo 로 감싸주면 된다.

전역 state 는 그 값이 변경되지 않더라도 해당 state 에 영향받는 컴포넌트를 리렌더링한다. 하지만 자식 컴포넌트로 전역 state 를 props 로 내려준다면, 자식 컴포넌트는  props 가 변경되지 않는 경우에는 리렌더링을 막을 수 있다.

// src/App.tsx
import Children from '@/components/Children'
import { useContext } from 'react'

const App = () => {
  const { count } = useContext(MyCount)
  return (
    <div>
      <h1>Parent Component</h1>
      <Children count ={count} />
    </div>
  )
}

// src/components/Children.tsx
import { memo } from 'react'

const Children = ({ count }) => {
  return (
    <div>
      <span>props: {count}</span>
    </div>
  )
}

export default memo(Children)

 

React.memo 를 사용하지 않아도 되는 경우

정적 컴포넌트

단순한 정적 컴포넌트의 경우에는 memo 를 사용하지 않아도 된다.

정적 컴포넌트란 컴포넌트의 상태가 props 에 의존하지 않고, 항상 동일한 결과를 반환하는 경우를 말한다.

 

렌더링이 적은 컴포넌트

렌더링이 자주 일어나지 않는 컴포넌트의 경우에도 memo 를 사용할 필요가 없다. 렌더링 비용이 크지 않은 경우에는 memo 를 사용해도 큰 차이가 없기 때문이다.

 

상위 컴포넌트에서 최적화한 경우

상위 컴포넌트에서 불필요한 리렌더링을 최적화할 수 있는 경우에도 자식 컴포넌트에 memo 를 사용하지 않아도 될 수 있다. 상위 컴포넌트에서 state 를 메모이제이션해서 props 로 넘겨주면 된다.

 

props 가 계속해서 변경되는 경우

props 가 계속해서 변경되는 경우라면 memo 를 사용해봤자 지속적인 리렌더링이 일어날 것이다. 따라서 이 경우에도 memo 를 사용할 필요가 없다.

오히려 리렌더링이 빈번할 경우 memo 를 사용한다면 불필요한 메모리를 사용할 수 있다.

 

익명 컴포넌트 ESLint 경고

 

memo 를 사용하다 아래와 같은 ESLint 경고 문구가 뜨는 경우가 있다.

const Components = () => {
  ...
}

export default memo(Components) // 💡Fast refresh can't handle anonymous components. Add a name to your export.

 

이는 익명 컴포넌트를 처리할 수 없을 때 발생하는 오류로 `.eslintrc.cjs` 파일을 조금 수정해주면 해결된다.

rules: {
  'react-refresh/only-export-components': "off"
},

 

만약 ESLint 설정을 바꾸지 않고 싶은 경우 아래와 같이 하면 경고는 사라진다.

const BaseComponents = () => {
  ...
}

const Components = memo(BaseComponents)

export default Components

 

마치며

`React.memo` 는 컴포넌트 자체를 메모이제이션한다.

컴포넌트의 props 가 변경되지 않으면 메모이제이션 했던 결과값을 사용하고, props 가 변경되었다면 새로운 값을 반환한다. React.memo 를 사용하지 않아도 되는 경우에 useCallback 으로 해결 가능한 경우가 있다.

 

`useCallback` 은 함수 자체를 메모이제이션하여 동일한 인스턴스를 재사용한다.

주로 자식 컴포넌트에 전달되는 콜백함수가 자주 변경되는 경우 사용한다.

 

React.memo 와 이름이 유사한 `useMemo` 는 함수의 결과값을 메모이제이션하여 이전에 계산된 결과를 재사용한다. 주로 복잡한 계산 결과를 저장할 때 사용한다. memo 와 유사하지만 useMemo 는 hook 이고, memo 는 HOC 라는 차이점이 있다.

 


참고

728x90
반응형