프론트엔드 개발자라면 성능 최적화에 신경 쓸 필요가 있다.
리액트는 성능 최적화를 위해 다양한 Hook을 제공한다.
그 중 useMemo
에 대해 알아보자.
리액트에서 컴포넌트가 리렌더링 되면 그 안에 선언된 연산도 다시 계산하게 된다.
useMemo
는 Memoization이라는 뜻으로, 연산 결과를 재사용한다.
즉, 한 번 연산한 결과를 기억하고, 다시 동일한 연산이 필요할 때 기억한 데이터를 반환하는 것이다.
useMemo
는 useMemo(연산 정의 함수, deps 배열)
로 이루어져 있다. Object.is
비교를 사용해 각 deps를 이전 값과 비교한다. import { useMemo } from 'react';
function TodoList({ todos, tab }) {
// 컴포넌트 최상단에 위치
const visibleTodos = useMemo(
() => filterTodos(todos, tab), // 계산 함수
[todos, tab] // deps 배열
);
// ...
}
간단한 Todo List 기능에서 useMemo
을 사용해보자.
import React, { useState } from 'react';
function App() {
const [todo, setTodo] = useState('');
const [todoList, setTodoList] = useState([]);
const onChange = (e) => {
const { value } = e.target;
setTodo(value);
};
const onSubmit = (e) => {
e.preventDefault();
setTodoList([...todoList, todo]);
setTodo('');
};
const getCount = (list) => {
console.log('What is the number of todo lists?');
return list.length;
};
const count = getCount(todoList);
return (
<>
<h1>Todo List</h1>
<form onSubmit={onSubmit}>
<input
type='text'
value={todo}
onChange={onChange}
placeholder='Enter your to-do'
/>
<button type='submit'>Save</button>
</form>
<div>Total: {count}</div>
<ul>
{todoList.map((todo) => (
<li key={`${todo}`}>{todo}</li>
))}
</ul>
</>
);
}
export default App;
위 코드를 직접 사용해보면 막힘 없이 작동하는 것을 볼 수 있다.
하지만 개발자도구를 열고 todo를 입력하면 다음과 같이 getCount
가 계속 호출되는 것을 볼 수 있다.
키보드를 입력할 때마다 렌더링이 되기 때문에 쓸데 없는 getCount
가 불러와지는 것이다.
todo를 저장할 때만 getCount
를 호출하고 싶다면 다음과 같이 수정하면 된다.
const count = useMemo(() => getCount(todoList), [todoList]);
todoList에 값이 들어올 때, 즉 todoList 값이 바뀔 때만 getCount
를 호출한다.
무분별하게 사용하면 오히려 성능에 무리가 갈 수 있다.
계산값을 저장하여 재사용하기 때문에 리소스가 낭비될 수 있다.
아래와 같은 상황에서는 useMemo
사용을 지양하는 것이 좋다.