성능 최적화를 위하여 연산된 값을 useMemo
라는 hook 을 사용하여 재사용하는 방법을 알아보도록 하자.
App 컴포넌트에서 다음과 같이 countActiveUsers
라는 함수를 만들어서 active
값이 true
인 사용자의 수를 세어서 화면에 렌더링이 되도록 했다.
import React, {useState, useEffect, useRef, useMemo} from 'react';
import CreateUser from "./Components/CreateUser";
import UserList from "./Components/UserList";
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
...
const count = countActiveUsers(users);
return(
<div>
<CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate} onUpdate={onUpdate}/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} onModify={onModify}/>
<div>활성사용자 수: {count}</div>
</div>
)
}
이 상태에서는 성능적 문제가 하나 발생한다. 바로 input 의 값을 바꿀때에도 countActiveUsers
함수가 호출된다는 것이다.
활성 사용자 수를 세는건, users 에 변화가 있을 때만 세면된다. 하지만 현재는 input 값이 바뀔 때에도 컴포넌트가 리렌더링 되므로 이렇게 불필요할때에도 함수가 호출되어 자원이 낭비되고 있다.
+ input 값을 바꿀 때 countActiveUsers 함수가 호출되는 이유
예를 들어 계정명을 입력한다고 하자. CreateUser 컴포넌트의 username 의 속성(props)가 바뀌게 되는데, 이 값은 원래 App 컴포넌트의 state이다. 결국 input에 입력하는 행위는 App 컴포넌트의 state를 바꾸는 행위이다. 따라서 App의 state가 변경되었으니 App 컴포넌트가 다시 렌더링되면서 App 컴포넌트 내에 있는 count 선언문 const count = countActiveUsers(users);
이 실행되고, 따라서 countActiveUsers
함수가 호출되는 것이다.
이러한 상황에서는 useMemo
라는 hook 을 사용하여 성능을 최적화 할 수 있다. Memo 는 "memoized" 를 의미하는데, 이는 이전에 계산한 값을 재사용한다는 의미를 가지고 있다.
이 부분을
const count = countActiveUsers(users);
이렇게 바꾸면 된다.
const count = useMemo(() => countActiveUsers(users), [users]);
useMemo
의 첫 번째 인자에는 어떻게 연산할지 정의하는 함수를 넣어주면 되고 두 번째 인자에는 deps 배열을 넣어주면 된다. 이 배열안에 넣은 내용이 바뀌면, 우리가 등록한 함수를 호출해서 값을 연산해주고, 만약에 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 된다.
처음에 useMemo
를 알았을 때 useEffect
와 차이점이 뭐지? 라고 생각했다. 그래서 useEffect
로 구현하려고 했었다.
var count = countActiveUsers(users);
useEffect(()=>{
count = countActiveUsers(users);
},[users])
첫 번째 시도에서 useEffect
을 이용해 users의 값이 바뀔때만 count의 값이 변경되도록 했으나, 결국 App컴포넌트가 렌더링 될때 count 선언문도 같이 실행되기 때문에 소용없다는 걸 알았다.
useEffect(()=>{
const count = countActiveUsers(users);
},[users])
두 번째 시도에서는 useEffect
안에 count변수를 정의해서 App컴포넌트가 렌더링될때 선언이 실행되지 않도록 했다. 하지만 결국 useEffect
에서 정의된 지역변수이기 때문에 return 문에서 count값을 사용할 수 없었다.
따라서 결국 useMemo
를 사용할 수 밖에 없었지만 이론적인 명쾌한 답을 얻지는 못하였다. 조금 더 알아본 후 관련 내용을 추가하도록 하겠다.