
리액트로 개발을 할 땐 크롬 확장 프로그램 React Devloper Tools를 설치하는 것이 좋다.

설치 후 개발자 도구에서 아래와 같이 Highlight updates when components render에 체크 설정을 해두면 화면에서 컴포넌트가 렌더될 때마다 그 컴포넌트 테두리를 빛나게 표시해준다. 이것으로 불필요하게 리렌더링 되는 컴포넌트를 확인할 수 있다.

변경될 필요가 없는 것들은 리렌더링 되지 않고 그대로 유지시킨다면 불필요한 리렌더링을 줄일 수 있을 것이다. useState 등 useHook들의 값은 화면이 리렌더링 되어도 다른 곳에 저장되어있다. useMemo와 useCallback은 그것을 도와주는 hooks이다.
useMemo는 변수를 기억한다. 만약 복잡한 계산을 해야하는 로직이 있는데 이것을 계속 리렌더링한다면 큰 손해일 것이다.
import { useMemo } from "react";
export default function MemoPage() {
const memo = useMemo(() => {
const aaa = Math.random();
}, []); // [] : 의존성 배열
return (
<div>{memo}</div>
)
}
위의 코드에서는 memo 라는 변수에 useMemo 훅을 이용해 aaa의 값을 처음 한 번의 계산 값을 저장해둬서 화면을 새로고침해도 새로운 난수가 나오지 않는다.
일반적으로 아주 복잡한 계산 외에는
useMemo를 많이 사용하지 않는다.
useCallback은 함수를 기억하는 hook이다.
import { useCallback } from "react";
export default function MemoPage() {
const countLet = 0;
const [countState, setCountState] = useState(0);
// countLet을 1씩 올리는 함수
const onClickCountLet = useCallback(() => {
console.log(countLet + 1);
count += 1;
}, []);
// countState를 1씩 올리는 함수
const onClickCountState = useCallback(() => {
setCountState((prev) => prev + 1);
}, []);
return (
<div>countLet : {countLet}</div>
<div>countState : {countState}</div>
)
}
위의 onClickCountLet 함수는 함수를 기억해 리렌더링 되지 않으면서 countLet 값이 1씩 증가해 콘솔에는 찍힌다. 하지만 화면의 값은 변하지 않는다.
onClickCountState 함수는 countState를 1씩 증가시킨다. 화면의 리렌더링은 일어나지만 함수를 새로 그리지는 않으면서 state를 증가시킬 수 있다.
이 함수에서 prev 값을 쓰지않고 단순히 countState를 1씩 증가시킨다면 state를 기억하기 때문에 1만 찍히게 된다.
✚ useMemo를 이용해 useCallback을 만들 수도 있다.
const myUseCallback = useMemo(() => {
return () => {
setCountState((prev) => prev + 1);
};
}, []);
의존성 배열의 인자가 1~2개보다 많아질 땐 그냥 리렌더를 하는 것이 유지보수에 더 좋다. 성능이 조금 좋아지는 것보다는 유지보수에 편리한 것이 더 낫기 때문!
import MemoPage from "./memopage";
export default function ParentPage = () => {
return (
<MemoPage />
)
}
import { memo } from "react";
function MemoPage = () => {
return (
//
)
}
export default memo(MemoPage);
위의 MemoPage는 memo로 감싸져있다. memo로 싸여있는 컴포넌트는 리렌더링 되지 않는다.
하지만 memo로 싸여있는 컴포넌트도 props나 key값이 변경되면 memo가 깨지고 리렌더링된다.
import MemoPage from "./memopage";
import { v4 as uuidv4 } from "uuid";
import { useState } from "react";
export default function ParentPage = () => {
const [char, setChar] = useState("오늘의 날씨는 흐림");
const onClickChange = () => {
setChar("오늘의 날씨는 맑음.");
}
return (
<>
{char.split("").map((el) => (
<MemoPage key={uuidv4()} el={el} />
))}
<button onClick={onClickChange}>체인지!</button>
</>
)
}
import { memo } from "react";
function MemoPage = (props) => {
return (
<span>{props.el}</span>
)
}
export default memo(MemoPage);
위의 코드에서 버튼을 클릭하면 char 값이 '흐림'에서 '맑음'으로 변경된다. '오늘의'와 '날씨는'은 동일해서 변경되지 않는다. 하지만 key값으로 uuid가 쓰여 모두 재렌더링된다. key값이 변경되지 않았다고 가정했을 때에는 변경된 값('맑음')이 props로 넘어간 컴포넌트만 리렌더링된다.
uuid는 불필요한 리렌더링을 초래하므로 필요한 상황에서만 주의해서 사용해야 한다.
'내가 만드는 서비스를 이용하는 사람들의 컴퓨터는 대부분 느리다'라는 생각을 갖고 개발해야한다.
최대한 간단하게 코드를 짜고 리팩토링하고 최적화해야한다 !