React.memo, useMemo, useCallback 역할 및 차이점

2021-08-28 오후 7:49:14
React.js
React.memo, useMemo, useCallback 역할 및 차이점

React.memo

React는 컴포넌트를 렌더링 한 뒤, 이전 렌더된 결과와 비교하여 Dom 업데이트를 결정한다.
상황에 따라 이 과정의 속도를 높이기 위해 React.memo를 사용 한다.

먼저 HOC에 대해 알아보자.
React.memo는 Higher-Order Components(HOC)이다.
(HOC란 컴포넌트를 인자로 받아서 새로운 컴포넌트를 return해주는 구조의 함수)

1. HOC 예시 (Auth에 따른 페이지 권한 검사)

// Auth라는 HOC를 통해 권한에 따라 라우팅 처리 function App() { return ( <Suspense fallback={<div>Loading...</div>}> <NavBar /> <div> <Switch> <Route exact path="/" component={Auth(LandingPage, null)} /> <Route exact path="/login" component={Auth(LoginPage, false)} /> <Route exact path="/register" component={Auth(RegisterPage, false)} /> <Route exact path="/video/upload" component={Auth(VideoUploadPage, true)} /> <Route exact path="/video/:videoId" component={Auth(VideoDetailPage, null)} /> <Route exact path="/subscription" component={Auth(SubscriptionPage, null)} /> </Switch> </div> <Footer /> </Suspense> ); } export default App;
// Auth validation example import React, { useEffect } from 'react'; import { auth } from '../_actions/user_actions'; import { useSelector, useDispatch } from "react-redux"; export default function (SpecificComponent, option, adminRoute = null) { function AuthenticationCheck(props) { let user = useSelector(state => state.user); const dispatch = useDispatch(); useEffect(() => { //To know my current status, send Auth request dispatch(auth()).then(response => { //Not Loggined in Status if (!response.payload.isAuth) { if (option) { props.history.push('/login') } //Loggined in Status } else { //supposed to be Admin page, but not admin person wants to go inside if (adminRoute && !response.payload.isAdmin) { props.history.push('/') } //Logged in Status, but Try to go into log in page else { if (option === false) { props.history.push('/') } } } }) }, []) return ( <SpecificComponent {...props} user={user} /> ) } return AuthenticationCheck }

일반적인 Component는 props를 넘겨받아 UI에 활용하는 반면, HOC는 리액트의 API가 아니라 리액트가 컴포넌트를 구성하는데 있어서의 일종의 패턴이라고 보면된다.

2. React.memo의 사용법

// example const MyComponent = React.memo((props) => { return (/*컴포넌트 렌더링 코드*/)} );

하나의 컴포넌트가 똑같은 props를 넘겨 받았을 때 같은 결과를 렌더링 하고 있다면 React.memo를 사용하여 불필요한 컴포넌트 렌더링을 방지 할 수 있다.
React.memo를 사용할 경우 이전과 같은 props가 들어올때는 렌더링 과정을 스킵하고 가장 최근에 렌더링된 결과를 재사용 한다.

React.memo는 넘겨받은 props의 변경 여부만을 체크한다. 하지만 컴포넌트 내부에서 useState같은 훅을 사용 하고 있는 경우에는 상태가 변경 되면 리렌더링 된다.

넘겨받은 props의 변경 여부는 shallow compare로 비교 되므로, object의 경우 같은 값을 참조 하고 있는지를 비교한다.
(불변성을 고려해야함)

이 비교방식을 커스텀 하고 싶은 경우 아래와 같이 React.memo의 두번째 인자로 넣어주면 된다.

function MyComponent(props) { /* 컴포넌트 로직 */ } function areEqual(prevProps, nextProps) { /* 전달되는 nextProps가 prevProps와 같다면 true를 반환, 같지 않다면 false를 반환해 준다. */ } export default React.memo(MyComponent, areEqual);


useMemo

useMemo는 메모이즈된 값을 return하는 hook이다.
useMemo는 이전 값을 기억해두었다가 조건에 따라 재활용하여 성능을 최적화 하는 용도로 사용된다.


1. useMemo 사용법

아래 예시를 살펴보자.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

인자로 함수와 Dependencies를 넘겨 받는다. 이 때, 2번째 인자로 넘겨준 의존 인자 중에 하나라도 값이 변경되면 1번째 인자의 함수를 재실행한다. 이를 통해 매 렌더링 할때마다 소요되는 불필요한 계산을 피할 수 있다. 만약 Dependencies 인자를 전달하지 않는다면 매번 새롭게 계산하여 return 한다.

2. 유의 사항

모든 함수를 useMemo로 감싸게 되면 이 또한 리소스 낭비가 될 수 있으므로,
퍼포먼스 최적화가 필요한 연상량이 많은 곳에 사용하는 것이 좋다.



useCallback

useCallback은 리액트의 렌더링 성능을 위해서 제공되는 Hook이다.
컴포넌트가 렌더링 될 때마다 내부적으로 사용된 함수가 새롭게 생성되는 경우,
자식 컴포넌트에 Prop으로 새로 생성된 함수가 넘겨지게 되면 불필요한 리렌더링이 일어날 수 있다.

1. useCallback 사용법

다음은 useCallback 훅이 필요한 예시이다.

import React, {useSatate} from 'react'; import {saveToServer} from './api'; import UserEdit from './UserEdit'; function Profile(){ const [name, setName] = useState(''); const [age, setAge] = useState(0); return ( <div> <p>{`name is ${name}`}</p> <p>{`age is ${age}`}</p> <UserEdit onSave={() => saveToServer(name, age)} setName={setName} setAge={setAge} /> </div> ); }

Profile 컴포넌트가 렌더링 될 때마다 UserEdit 컴포넌트의 onSave 속성값으로 새로운 함수가 전달된다.
UserEdit 컴포넌트에서 React.memo를 사용해도 전달된 Prop이 항상 바뀌므로 불필요한 렌더링이 발생한다.
onSave의 속성값은 name이나 age값이 변경되지 않으면 항상 같아야 한다.

이 같은 문제를 다음과 같이 useCallback 을 사용하여 방지 할 수 있다.

function Profile(){ const [name, setName] = useState(''); const [age, setAge] = useState(0); const onSave = useCallback(() => saveToServer(name, age), [name, age]); return ( <div> <p>{`name is ${name}`}</p> <p>{`age is ${age}`}</p> <UserEdit onSave={onSave} setName={setName} setAge={setAge} /> </div> ); }

useMemo와 마찬가지로 1번째 인자로 함수, 2번째 인자로 Dependencies를 전달한다.
전달된 의존성 인자가 바뀌지 않으면 이전에 생성한 함수가 재사용 된다.

즉, name과 age값이 변경되지 않으면,
UserEdit 컴포넌트의 onSave 속성값으로 항상 같은 함수가 전달되어 리렌더링을 방지 할 수 있다.


정리

1. 공통점

  • React.memo, useMemo, useCallback은 모두 불필요한 렌더링 또는 연산을 제어하는 용도로 성능 최적화에 그 목적이 있다.

2. 차이점

  • React.memo는 HOC이고, useMemo와 useCallback은 hook이다.
  • React.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만, useMemo는 hook이기 때문에 함수형 컴포넌트 안에서만 사용 가능하다.
  • useMemo는 함수의 연산량이 많을때 이전 결과값을 재사용하는 목적이고, useCallback은 함수가 재생성 되는것을 방지하기 위한 목적이다.

User Profile Icon
LKHcoding
Front-End Developer