본문 바로가기

front-end 국비과정 학습일지

React의 동작 과정, React.memo개념정리 및 useCallback, useMemo와 함깨 사용

리엑트가 동작하는 과정 

리엑트는 상태(state)가 변경되면 변경된 사항을 react-Dom에게 전달하고 
react-Dom 즉 가상의 DOM과 브라우저의 실제 DOM을 비교하여 변경 된 부분을
브라우저의 실제 DOM에 반영한다 

변경사항이 생기면 컴포넌트는 재평가를 위해 컴포넌트가 리렌더링 되는데
만약 변경사항이 많다면 문제가 없지만 소규모의 변경사항이 곳곳에서 생기고 그때마다 DOM전체를 리렌더링 해야한다면 메모리 자원소모가 굉장히 클것이다

즉 
변경사항을 반영하기위해 브라우저의 실제DOM을 리렌더링하는것과 
변경사항을 반영하기위해 작은 함수하나를 리렌더링하는것은 성능측면에서 큰차이를 보인다

react-dom은 DOM전체를 리렌더링 하는대신 변경사항이 있는 컴포넌트만 리렌더링하여 성능적인 부분에 문제를 해결하고,
가상돔과 실제돔을 비교하여 수정된부분만 실제돔에 반영하여 변경사항을 즉각 브라우저에 반영한다



React.memo

1 React.memo는 함수형 컴포넌트에서만 사용 가능하다

2 React.memo의 원리
메모이제이션(memoization) 이란? 
함수와 같은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장하는 행위

컴포넌트는 함수이고 React.memo를 사용한 컴포넌트는 처음 랜더링 후에 메모리에 값을 저장한다 
상태 즉 state가 바뀌면 컴포넌트가 리렌더링되고 자식컴포넌트가 존재한다면 자식컴포넌트까지 리렌더링되는데 

React.memo를 사용한 컴포넌트는 리렌더링에 기준을 상태(state)의 변경사항이 아니라 props에 변경사항으로 만들고
메모이제이션 된 값을 사용 함으로써 불필요한 리렌더링을 만들어 준다 

3 왜 사용하는가?
react.memo는 결국 최적화를 위해사용한다 특정 컴포넌트를 메모이제이션 함으로써 불필요한 리렌더링을 방지하기 때문이다

4 주의사항 
메모이제이션이란 결국 메모리에 컴포넌트라는 함수를 계산한 결과를 저장하는 행위이기 때문에 메모리 자원이 소모된다
때문에 남용시 메모리에 저장되는 내용이 많아짐으로 최적화 측면에서 사용하지 않은것보다 않좋은 결과를 발생시킬 수 있다
 
5 props가 객체 또는 함수일 경우
props가 객체 또는 함수일 경우 참조형 데이터이기때문에 부모컴포넌트가 리렌더링되면 부모 컴포넌트에서 전달하는 참조형 데이터의 메모리 주소가 바뀌어 props가 변경된것으로 인식한다 때문에 객체일경우 useMemo를 ,함수일 경우 useCallback을 이용해 메모리에 메모이제이션 해놓으면 참조형 데이터의 메모리주소가 메모리에 저장되어 
부모컴포넌트가 갱신될때 props로 전달되는 참조형 데이터의 메모리 주소는 변하지 않기때문에 자식 컴포넌트의 리렌더링 현상을 방지 할 수 있다

6 결론
React.memo는 컴포넌트가 리렌더링될때 자식컴포넌트까지 같이 리랜더링 되는 현상을 방지하기 위해 사용된다 

React.memo는 주로 내려오는 props가 원시데이터일때 사용하고 내려오는 props가 (함수,객체)일때  useCallback은 내려오는 props가 참조형데이터(함수)일때 uesMemo는 내려오는 props가 참조형데이터(객체)일때 React.memo와 함깨 사용한다


또한 useCallback은 최적화를 위한 용도가 아니더라도 사용빈도가 높은 hook이므로 반드시 기억하자 

ex)react.memo사용시 props가 원시형 데이터인경우

//부모컴포넌트
import React, { useState } from 'react';
import styled from 'styled-components';
import Test from '~/components/Test';
const App = () => {
  const [change, setChange] = useState(false);

  const btnHandler = () => {
    setChange(!change);
  };

  return (
    <StyledApp>
      {change && <div>test</div>}
      <Test title="props" description="react.memo Test" kim="kim" />
      <button onClick={btnHandler}>testBTN</button>
      {/* 컴포넌트에 리렌더링을 발생시키고자 만든 코드 */}
    </StyledApp>
  );
};

const StyledApp = styled.div``;

export default App;

//자식 컴포넌트
import React from 'react';
import styled from 'styled-components';

const Test = ({ title, description, kim }) => {
  return (
    <StyledTest>
      <div>{title}</div>
      <div>{description}</div>
      <div>{kim}</div>
    </StyledTest>
  );
};
const StyledTest = styled.div`
  color: red;
`;

export default React.memo(Test);




ex2 react.memo사용시 props가 참조형 데이터인경우

//부모컴포넌트
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import styled from 'styled-components';
import Test from '~/components/Test';
const App = () => {
  const [parentAge, setParentAge] = useState(0);
  const [test1, setTest1] = useState({
    name: 'kim',
    age: 26,
  });

  const conveyTest1 = useMemo(() => {
    return test1;
  }, []);
  //react.memo사용시 전달되는 props가 객체일때 리렌더링 되는 현상을 방지하기 위한 코드
  // useMemo의 2번인자는 변경사항을 감시할 데이터를 넣는다 2번인자의 데이터가 변경되면 메모리제이션된 함수를 리렌더링함

  const parentAgeHandler = () => {
    setParentAge(parentAge + 1);
  };
  // parentAge의 값을 변경시켜 부모컴포넌트를 리렌더링 시키기위한 함수

  const callbackTest = useCallback(() => {
    return console.log('this is useCallback');
  }, []);
  // 함수또한 객체이자 참조형 데이터이기 때문에 useCallback으로 랩핑하여 메모리주소의 변화 방지

  console.log('부모컴포넌트 렌더링!');
  //리렌더링 확인 코드
  return (
    <StyledApp>
      <div>부모컴포넌트 age:{parentAge}</div>
      <button onClick={parentAgeHandler}>change parentAge!</button>
      <Test conveyTest1={conveyTest1} callbackTest={callbackTest} />
    </StyledApp>
  );
};

const StyledApp = styled.div`
  margin-top: 30px;
`;

export default App;
//자식 컴포넌트
import React, { useEffect } from 'react';
import styled from 'styled-components';

const Test = ({ conveyTest1, callbackTest }) => {
  console.log('자식컴포넌트 랜더링');
  return (
    <StyledTest>
      <p>{conveyTest1.name}</p>
      <p>{conveyTest1.age}</p>
      <button onClick={callbackTest}>test2 증가!</button>
    </StyledTest>
  );
};
const StyledTest = styled.div`
  color: red;
`;

export default React.memo(Test);