state가 바뀌면 바뀐 state로 컴포넌트가 다시 만들어지는데, 다시 만들어지면서 성능에 악영향을 미친다.
컴포넌트가 어떻게 재생성되고, 다시 만들지 않게 하는 방법은 무엇인지 알아보자!
memoization
- 새로 만들 컴포넌트를 메모해놓고, 다음에 다시 만들어야 할 때 새로 만들지 않고 메모해 놓은 것을 가져다 쓴다고 생각하면 된다.
새로 만들어지는 것들
- 컴포넌트 안의 hook을 제외한 나머지는 전부 새롭게 다시 만들어진다.
- 부모가 새로 만들어지면 자식들도 새로 만들어진다.
<뜬금꿀팁>
(index.tsx일 경우 폴더명까지만 적으면 되지만(index 생략 가능) 파일명이 index.tsx가 아닐 경우 폴더명/파일명으로 접속하면 된다.)
http://localhost:3000/32-01-memoization.container
렌더링이 일어나면 hook을 제외한 모든것이 새로 만들어진다.
(state는 useState로 만든 hook이니까 새로 만들어지지 않음)
/* let으로 선언한 변수 */
let countLet = 0;
/* let으로 선언한 변수를 증가시키는 함수 */
const onClickCountLet = () => {
console.log("let : " + Number(countLet + 1)); // 실행될 결과
countLet += 1;
};
/* state로 선언한 변수 */
const [countState, setCountState] = useState(0);
/* state로 선언한 변수를 증가시키는 함수 */
const onClickCountState = () => {
console.log("state : " + Number(countState + 1)); // 실행될 결과
setCountState((prev) => prev + 1);
};
[React Developer Tools]
React Developer Tools의 Profiler: 성능 측정 도구
React Developer Tools를 설치한다.
리액트 앱으로 만들어진 브라우저를 열고 개발자 도구에서 Profiler 탭으로 들어간다.
설정 아이콘 > Highlight updates when components render
체크
렌더링이 일어나는 부분이 화면에 표시된다.
Start profiling을 누르면 녹화를 할 수 있다.
5-1. 렌더링된 부분이 노란색으로 표시되어 나온다.
부모가 렌더링이 되면 자식도 렌더링이 된다는 것을 확인할 수 있다.
렌더링이 일어나지 않게 만들어보자!
부모가 렌더링되어도 자식은 렌더링 되지 않게 만들어준다.
memo도 HOC이다.
자식까지 리렌더링 되는 것은 피할 수 있으면 memo를 이용해서 방지해야 한다.
memo를 쓰고 있어도 전달하고 있는 props가 변경되면 자식 컴포넌트가 리렌더링 된다.
(자식 컴포넌트에서 전달 받은 props를 사용하지 않고 있더라도 변경되면 자식 컴포넌트가 리렌더링된다.
따라서, props를 전달할 때 정말 필요한 것만 전달해야 한다.)
- props가 바뀌면 바뀐다고 해서 memo를 남용하면 안된다.
메모를 한다는 것은 결국엔 어딘가에 저장된다는 것이고, 변경될 때 계속해서 체크해야 하기 때문에 남용을 하면 오히려 퍼포먼스가 더 떨어질 수 있다.
import { memo } from "react";
function ChildrenPage(props: any) {
~~ 자식 컴포넌트 ~~
}
export default memo(ChildrenPage);
const aaa = useMemo(() => Math.random(), [countState]);
import { useMemo } from "react";
const random = Math.random();
console.log("그냥 랜덤 숫자 : " + random);
const useMemoRandom = useMemo(() => Math.random(), []);
console.log("useMemo 랜덤 숫자 : " + useMemoRandom);
👇🏻 사실, 변하지 않는 상수값이라면 useMemo를 쓰지 않고 컴포넌트 밖에 const로 선언하는 것이 편리하다..^^
그럼 언제 써?!
컴포넌트 안에서 써야 하고, 계산을 통해서 만들어지면서 리렌더 될 때 다시 계산될 필요가 없을 때 쓰면 된다.
쓸 일이 별로 없다~
(useMemo로 useCallback 만들기)
리턴하는 값을 함수로 넣어주면 된다.
const onClickCountState = useMemo(() => {
return () => setCountState((prev) => prev + 1);
}, []);
/* 위아래 같음 */
const onClickCountState = useMemo(
() => () => setCountState((prev) => prev + 1),
[]
);
뜬금 꿀팁 2,,
button 태그의 onClick 안에 함수를 바인딩시키지 않고 바로 작성해도 된다.
하지만 이렇게 하면 useCallback은 적용 못하니까 바인딩해서 사용하자~
useCallback( () => { ~~함수~~ }, [] );
const onClickCountState = useCallback(() => {
console.log(countState);
setCountState(countState + 1);
}, []);
/* ❌ useCallback을 잘못 사용한 예 */
const onClickCountState = useCallback(() => {
console.log("state : " + Number(countState + 1)); // 증가된 결과
setCountState(countState + 1);
}, []);
👇🏻
/* 👍🏻 옳은 예 */
const onClickCountState = useCallback(() => {
setCountState((prev) => prev + 1);
}, []);
dependency array에 들어간 값이 변경되면 다시 리렌더링이 되는데,
그 값이 많아지면 언제 새로 만들어지고 만들어지지 않는지 확인이 어려워서 오히려 유지보수가 힘들어질 수 있다.
따라서, dependency array에 들어가는 값이 한 두개일 때 사용하는 것이 좋다.
관리자 사이트처럼 데이터가 많은 경우, 체크박스가 체크될 때 memoization을 하지 않으면 굉장히 버벅이게 된다!
memo만 달아줘도 성능 향상 굿임