천진난만 코딩 스토리
2023.03.03) 항해 26일차 (3-React Hooks 최적화) 본문
오히려 리액트 훅만 볼 때 보다 최적화를 위한 훅이 더 잘 와 닿았다.
콘솔을 여러번 찍어보며 보니 더 와닿을 수 있었다.
이것도 마찬가지로 많이 사용해볼 수 있으려나 모르겠다.....
2) React Hooks - 최적화
① 리-렌더링의 발생 조건과 최적화
❶ 리-렌더링의 발생 조건
- 컴포넌트에서 state가 바뀌었을 때
- 컴포넌트가 내려받은 props가 변경되었을 때
- 부모 컴포넌트가 리-렌더링 된 경우 자식 컴포넌트는 모두
❷ 최적화
리액트에서 리렌더링이 자주 일어난다는 것은 좋지 않다.
비용이 발생하는 것은 최대한 줄여야 하는 작업을 최적화(Optimization)이라고 한다.
- 최적화하는 대표적인 방법
- memo(React.memo) : 컴포넌트를 캐싱
- useCallback : 함수를 캐싱
- useMemo : 값을 캐싱
② React.memo
부모 컴포넌트가 리렌더링 되면 자식컴포넌트는 모두 리렌더링 된다.
자식 컴포넌트의 입장에서는 변경사항이 없지만 불필요하게 렌더링 될 수 있는데, 이 부분을 돕는 도구가 바로 React.memo이다.
React.memo를 이용해서 컴포넌트를 메모리에 저장해두고 필요할 때 갖다 쓸 수 있다.
이렇게 하면 부모 컴포넌트의 state의 변경으로 인해 props가 변경이 일어나지 않는 한 컴포넌트는 리렌더링 되지 않는다.
이것을 컴포넌트 memoization 이라고 합니다.
export default React.memo(컴포넌트이름);
③ useCallback
useCallback은 인자로 들어오는 함수 자체를 기억(메모이제이션)한다.
- React.memo를 통해서 하위 컴포넌트를 메모이제이션을 했는데도 리렌더링이 되는 이유
- 함수형 컴포넌트를 사용하기 때문이고, App.jsx가 리렌더링 되면서 코드가 다시 만들어지기 때문이다.
- 자바스크립트에서는 함수도 객체의 한 종류이다.
- 모양은 같더라도 다시 만들어지면 그 주솟값이 달라지고 이에 따라 하위 컴포넌트는 props가 변경됐다고 인식한다.
그렇기 때문에, 이 함수를 메모리 공간에 저장해놓고, 특정 조건이 아닌 경우엔 변경되지 않도록 해야한다.
// 변경 전
const initCount = () => {
setCount(0);
};
// 변경 후
const initCount = useCallback(() => {
setCount(0);
}, []);
④ useMemo
동일한 값을 반환하는 함수를 계속 호출해야 하면 필요없는 렌더링을 하게 된다.
맨 처음 해당 값을 반환할 때 그 값을 특별한 곳(메모리)에 저장하다면 필요할 때 마다 다시 함수를 호출해서 계산하는게 아니라,
이미 저장한 값을 단순히 꺼내와서 쓸 수 있다.
// as-is
const value = 반환할_함수();
// to-be
const value = useMemo(()=> {
return 반환할_함수()
}, [dependencyArray]);
dependency Array의 값이 변경 될 때만 반환할 함수()가 호출된다.
그 외의 경우에는 memoization 해놨던 값을 가져오기만 한다.
import React, { useEffect, useState } from "react";
function ObjectComponent() {
const [isAlive, setIsAlive] = useState(true);
const [uselessCount, setUselessCount] = useState(0);
const me = {
isAlive: isAlive ? "생존" : "사망",
};
useEffect(() => {
console.log("생존여부가 바뀔 때만 호출해주세요!");
}, [me]);
return (
<>
<div>
<button
onClick={() => {
setIsAlive(!isAlive);
}}
>
누르면 살았다가 죽었다가 해요
</button>
<br />
생존여부 : {me.isAlive}
</div>
{uselessCount}
<br />
<button
onClick={() => {
setUselessCount(uselessCount + 1);
}}
>
누르면 숫자가 올라가요
</button>
</>
);
}
export default ObjectComponent;
useEffect hook을 이용해서 me의 정보가 바뀌었을 때만 발동되게끔 dependency array를 넣어놨지만,
엉뚱하게도 count를 증가하는button을 눌러보면 계속 log가 찍힌다.
이 부분은 불변성과 관련이 깊다.
위 예제에서 버튼이 선택돼서 uselessCount state가 바뀌게 되면
→ 리렌더링이 되고, 컴포넌트 함수가 새로 호출된다
→ me 객체도 다시 할당한다(이 때, 다른 메모리 주소값을 할당받는다)
→ useEffect의 dependency array에 의해 me 객체가 바뀌었는지 확인해봐야 하는데, 이전 것과 모양은 같은데 주소가 다르다.
→ 리액트 입장에서는 me가 바뀌었구나 인식하고 useEffect 내부 로직이 호출된다.
const me = useMemo(() => {
return {
name: "Ted Chang",
age: 21,
isAlive: isAlive ? "생존" : "사망",
};
}, [isAlive]);
useMemo()만 이렇게 써주면, uselessCount가 아무리 증가돼도 영향이 없게 된다.
- 주의해야 할 사항
- useMemo를 남발하게 되면 별도의 메모리 확보를 너무나 많이 하게 되기 때문에 오히려 성능이 악화될 수 있다.
- 필요할 때만 써야 한다.
'TIL(Today I Learned)' 카테고리의 다른 글
2023.03.04) 항해 27일차 (2-React Router Dom) (1) | 2023.03.04 |
---|---|
2023.03.04) 항해 27일차 (1-Redux) (0) | 2023.03.04 |
2023.03.03) 항해 26일차 (2-React Hooks) (0) | 2023.03.03 |
2023.03.03) 항해 26일차 (1-CSS-in-JS,전역스타일링) (0) | 2023.03.03 |
2023.03.02) 항해 25일차 (0) | 2023.03.02 |