티스토리 뷰

TIL/React

[리액트 React] 최적화 (메모이제이션)

개춘기뽀삐 2024. 1. 26. 20:36

 

최적화 종류

 

리렌더링 조건

1. 컴포넌트의 state가 바뀌었을 때
2. 컴포넌트가 내려받은 props 가 변경되었을 때
3. 부모 컴포넌트가 리렌더링되면 자식도 모두 리렌더링 된다

 

컴포넌트란? 

  • 어떤 기능을 모아서 독립적으로 사용하는 함수로, 기능의 집합 같은 것이다
  • 컴포넌트 내부에는 컴포넌트에 관한 것들로만 채워야 한다(독립적인 사용과 협업 시 혼선을 주지 않기 위해)
  • 컴포넌트 내부 함수들은 컴포넌트의 기능을 위한 메서드와 같은 것이다.
  • 반복적인 함수는 컴포넌트를 분리하여 관리하고, 하나의 컴포넌트만 사용하는 함수는 내부에 선언하여 사용된다.
  • 스타일의 경우, 전역으로 선언하여 다른 컴포넌트들이 접근할 수 있게 하거나, 스타일을 위한 컴포넌트를 생성하여 관리하는 것이 제일 효율적이다.

React.memo

  • 캐싱 - 메모리에 임시 저장
  • 컴포넌트를 캐싱한다,
  • 컴포넌트를 메모리에 저장해 두고 필요할 때 갖다 쓰는 것 그러면 부모 컴포넌트의 state 변경으로 props가 변경되지 않는 한 자식 컴포넌트는 리렌더링 하지 않는다 (컴포넌트 임시 저장소)

사용 방법

  1. App 컴포넌트의 이벤트를 작동시키면 BOX 컴포넌트도 같이 리렌더링 된다
  2. 리렌더링 하지 않아도 되는 컴포넌트 export를 React.memo로 감싸주면 App 컴포넌트 이벤트에 영향받지 않게 된다
function App() {
  console.log("App 컴포넌트가 렌더링되었습니다!");
  const [count, setCount] = useState(0);

  // 1증가
  const onPlusBtn = function () {
    return setCount(count + 1);
  };

  // 1감소
  const onMinusBtn = () => {
    setCount(count - 1);
  };

  return (
    <>
      <h3>카운드 예제입니다</h3>
      <p>현재 카운트 : {count}</p>
      <button onClick={onPlusBtn}>+</button>
      <button onClick={onMinusBtn}>-</button>
      <div style={{ display: "flex", marginTop: "10px" }}>
        <Box1 />
        <Box2 />
        <Box3 />
      </div>
    </>
  );
}
function Box1({ initCount }) {
  console.log("Box1 컴포넌트가 렌더링 되었습니다");

  return (
    <div style={style}>
      <button
        onClick={() => {
          initCount();
        }}
      >
        초기화
      </button>
    </div>
  );
}

// 부모 컴포넌트의 state가 변경돼 props가 변하지 않는 한 자식 컴포넌트는 리렌더링 되지 않는다.
// memo - 컴포넌트 캐싱(컴포넌트를 메모리에 저장하여 필요할때 사용한다)
export default React.memo(Box1);

 



useCallback

  • 캐싱 - 메모리에 임시 저장
  • 함수를 캐싱한다, 함수를 useCallback으로 감싸주게 되면 App 컴포넌트가 렌더링 될 때 함수 그대로 메모리에 저장하여 의존성배열[ ] 부분의 값이 변하지 않는 한 초기화되지 않는다(빈 값도 마찬가지)
  • 메모리에 initCount 함수를 저장하여 최적화하는 것 (임시 저장소)

 

useCallback , useEffect 차이

  • useCallback은 컴포넌트 최적화를 위해 함수를 메모리에 저장하는데 목적이 있기 때문에 의존성 배열의 값이 변경되지 않으면 해당 함수는 app컴포넌트가 리렌더링 돼도 초기화되지 않는다, (최초 렌더링 시 그대로) 함수 임시 저장소 느낌
  • useEffect는 컴포넌트 렌더링 시 내부 로직 실행에 목적이 있다. 렌더링 시 무조건 실행 또는 의존성 배열의 값이 변경될 때마다 실행

사용 방법

  1. initCount를 useCallback 없이 실행시키면 React.memo hook을 사용했음에도, app컴포넌트의 state가 변경되면서 리렌더링 될 때마다 함수를 초기화시킨다
  2. 때문에 box1으로 전달하는 props가 바뀌었다고 판단하여 box1도 같이 리렌더링 된다 이것을 막기 위해 useCallback을 사용한다.
  3. useCallback으로 initCount를 감싸면 App 컴포넌트가 렌더링 될 때 initCount함수는 메모리에 그대로 저장되어 [ ]에 값이 변하지 않는 한 초기화되지 않는다(빈 값도 마찬가지)
  4. useCallback으로 app컴포넌트의 렌더링 시점(count=0)에 저장되었기 때문에 값은 변하지 않는다, 하지만 count를 [ ]에 넣어줄 경우, 값이 변할 때마다 리렌더링 된다
  // count를 초기화 하는 함수
  
  const initCount = useCallback(() => {
    console.log(`${count} 에서 0 으로 변경되었습니다`);
    setCount(0);
  }, [count]);

 


useMemo

  • 캐싱- 메모리에 임시 저장
  • useMemo : 값을 캐싱한다, 함수가 반환하는 값 자체를 메모리에 저장한다
  • 저장 값은 변수, 배열, 객체 등 데이터를 저장하여 사용하도록 한다
  • 무거운 작업(엄청 많은 양의 데이터를 반복해야 할 경우) 사용

 

사용 방법_1

  1. HeavyComponent에 많은 데이터를 반복하는 함수가 있다
  2. 컴포넌트가 렌더링 될 때마다, 함수가 초기화되어 다시 실행되고 많은 양의 데이터를 한꺼번에 처리하려 하다 보니, 렌더링 시간이 지연된다
  3. useMemo로 데이터(값)를 감싸주면 컴포넌트의 최초 렌더링 시 해당 값이 메모리에 저장되므로 [의존성 배열] 이 변하지 않으면 함수가 초기화되지 않아 속도가 안정적 이게 된다
// heavy work > 엄청 무거운 작업
function App() {
  return (
    <>
      <nav style={{ backgroundColor: "yellow", margin: "30px" }}>
        네비게이션 바
      </nav>
      <HeavyComponent />
      <footer style={{ backgroundColor: "green", margin: "30px" }}>
        푸터 영역입니다.
      </footer>
    </>
  );
}
function HeavyComponent() {
  const [count, setCount] = useState(0);

  const heavyWork = function () {
    for (let i = 0; i < 2000000000; i++) {}
    return 100;
  };

  // const value = heavyWork(); // useMemo 사용 전
  const value = useMemo(() => heavyWork(), []);
  console.log("value는 ", value);
  return (
    <>
      <p>나는 엄청 무거운 컴포넌트야</p>
      <button onClick={() => setCount(count + 1)}>
        누르면 아래 count가 올라가요!
      </button>
      <br />
      {count}
    </>
  );
}

 

 

사용 방법_2

  1.   useEffect는 컴포넌트가 렌더링 되거나 의존성 배열에 담긴 값이 변경될 때 실행된다, 근데 setUselessCount state가 변경될 때도 작동된다. 원래 둘은 아무 상관이 없기 때문에 작동하지 않아야 한다.
  2. 원인은, setUselessCount state가 변경될 때마다 컴포넌트가 리렌더링하기때문에 렌더링 시 실행되는 useEffect도 영향을 받는 것. 불변성의 법칙과 같다
  3. 컴포넌트 내부 함수들은 객체와 같음, 주소를 참조한다 리렌더링 될때마다 새로운 메모리에 할당하기 때문에 주소값이 달라진다
  4. me 객체를 useMemo에 담아 렌더링 시 데이터에 저장하고 생사 토글이 작동할 때만 useMemo가 갱신되도록 하면 아래 토글의 영향을 받지 않게 된다.
function ObjectComponent() {
  const [isAlive, setIsAlive] = useState(true);
  const [uselessCount, setUselessCount] = useState(0);

  const me = useMemo(() => {
    return {
      name: "Ted Chang",
      age: 21,
      isAlive: isAlive ? "생존" : "사망",
    };
  }, [isAlive]);

  useEffect(() => {
    console.log("생존여부가 바뀔 때만 호출해주세요!");
  }, [me]);

  return (
    <>
      <div>
        내 이름은 {me.name}이구, 나이는 {me.age}야!
      </div>
      <br />
      <div>
        <button
          onClick={() => {
            setIsAlive(!isAlive);
          }}
        >
          누르면 살았다가 죽었다가 해요
        </button>
        <br />
        생존여부 : {me.isAlive}
      </div>
      <hr />
      필요없는 숫자 영역이에요!
      <br />
      {uselessCount}
      <br />
      <button
        onClick={() => {
          setUselessCount(uselessCount + 1);
        }}
      >
        누르면 숫자가 올라가요
      </button>
    </>
  );
}
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함