본문 바로가기
  • 삽질하는 자의 블로그
React/React-Basic

7. 리액트의 최적화 With React.memo(), useCallback(), useMemo()

by 이게뭐당가 2022. 12. 14.

서론. 리액트가 작동하는 방식

 

리액트는 오로지 "컴포넌트" 에만 초점이 맞추어져 있다.

 

1) 컴포넌트 끼리 props 를 넘긴다던가 [ props 전달 ]

2) 컴포넌트의 State 를 변화시킨다던가 [ internal-component State ]

3) 컴포넌트를 Context 를 사용해 State 를 변화시킨다던가 [ wide-data State]

 

이런 것들이 변화되면, 리액트는 [ 컴포넌트를 재평가, 재실행 ] 하게된다.

또한 그 과정에, 화면에 표시해야 할 것들이 있다면, 리액트는 React-DOM 에 그것을 알린다.

 

리액트DOM

 

리액트 DOM은 Real DOM 과는 달리 가상 DOM 이다.

리액트로부터 받은 "스냅샷"을 현재의 "스냅샷" 과 비교해, [ 다른 요소들만을 비교해 추가, 삭제, 변경 ] 한다.

즉, 리액트 DOM (가상 DOM) 은, 전체를 re-render 하지 않고, 필요한 부분만을 추가, 삭제하므로, Real DOM 조작에 비해,

굉장히 빠르고, 효율적인 메모리 사용을 한다.

 

그 과정에서

 

부모의 컴포넌트가 재평가, 재실행 된다면, 그들의 하위 컴포넌트인 자식 컴포넌트들은, "전부 재평가, 재시작 된다"

이미 자식 컴포넌트는, 부모의 컴포넌트의 일부이기 때문이다.

 

그렇다면, 변경이 되지 않는 자식들을 재실행 하는것은, 효율적인가?

 

그렇지 않다. 그러므로 리액트는

[상태를 자식에게 props 로 넘기고, 만약 그 props 의 값이 변화하지 않는다면] 

자식을 재실행 하지 않는, [ 최적화 기능] 을 가지고 있다.

 

1. 최적화의 방법 : React.memo()

[자식 컴포넌트의 export 를 할때, React.memo() 를 사용한다]

그렇게되면, React.memo() 는 props 의 변화가 있을때에만, re-render 된다.

 

<자식 컴포넌트>

	import React from "react"

        function Demo(props){
                ...
        }
        export default React.memo(Demo)

 

2. 그렇다면 왜? 전부 React.memo() 를 사용하여, 자식 컴포넌트를 만들지 않는가?

 

props 를 비교하는 비용이 발생하기 때문이다.

 

React.memo() 를 사용하면

props 를 메모리에 담아두고(저장), 부모의 컴포넌트가 재평가될때, 메모리 안의 기존 props와 현재의 props 를 비교하여,

그것을 판단하기 때문에,

[저장, 비교] 하는 성능비용과, [컴포넌트를 받아 re-render] 하는 비용을 비교했을때,

우리는 더 나은 쪽을 선택할 필요가 있다.

 

3. 그렇다면 언제 사용할까?

 

대체적으로, 

1. 자식 컴포넌트가 많은 하위 자식들을 거느렸을때,

2. 또한 그 자식 컴포넌트로 가는 props 가, 잘 변화하지 않을때

사용한다.

 

      A   - AB - AC - AD - AF ...
      
          - BA - BB - BC ...
          
          - CA - CB ...

부모인 A 로부터, 파생되는 자식들이다.

 

만약 AB 로 가는 props 의 변화가 거의 없고, AB 의 하위 컴포넌트가 수십개 일 경우,

A의 재평가가, AB 에게 아무런 영향이 없을 경우

우리는 AB를 React.memo() 를 사용하여, 최적화를 진행할 것이다.

 

4. 기본적인 React.memo() 는 "원시값의 props 를 비교하는데에만 적합하다"

 

자바스크립트를 생각해보자

 

"hi" == "hi"  // true
3 == 3 // true

[1,2,3] == [1,2,3]		//?

위의 정답은? false 이다

 

자바스크립트의 데이터는 원시값 혹은 참조값 으로 저장된다.

참조값은 "배열, 객체, 함수(함수는 객체이므로)" 이다.

 

또한 참조값은 메모리의 주소를 "참조" 하여, 우리에게 보여진다.

 

변수에 원시값을 저장하면, 확보된 메모리에는 [실제 값이 저장되며], 다른 변수에 넣으면, 원시값이 그대로 복사되어 온다,

 

그러나, 참조값은, 확보된 메모리 공간에, [ 값을 참조하는 참조값 ]이 저장되어,  이는 그 [메모리 주소] 를 의미한다.

 

실제 데이터에 접근하는 것이 아니므로, 데이터 변경이 가능하다.

 

이 의미는 [1,2,3] 을 만들고, [1,2,3] 을 다시 만들면, "둘의 메모리 주소(참조값)" 이 다르므로, 둘은 서로 다른 값이 된다.

 

"hi" : "hi" 그 자체로 저장된다.

[1,2,3]  : 사실 그저 주소로 저장된다.

 

그렇다는 것은

 

         
        <부모>
            function App(){
                function funA(){
                }
                retrun(
                    ...
                    <DemoA props={false}/>      // 원시값 props
                    <DemoB props={funA}/>       // 참조값인 funA 함수
                )
            }
        
        <자식 A>
                
            import React from "react"

            function DemoA(props){
                    ...
            }
            export default React.memo(Demo)

        <자식 B>

            import React from "react"

            function DemoB(props){
                    ...
            }
            export default React.memo(DemoB)


    ==> 이 경우에서는, "자식 B" 는 "re-render" 된다. "심지어 함수에서 나온 값, 식이 전부 동일함에도"

 

5. [함수] 를 위한 최적화 방법 useCallback Hook

 

useCallback 의 사용 방법

 

함수를 useCallback 으로 감싸고, 의존성을 넣어준다.

        const functionA = useCallback( functionA(){
            ...
        },[])

 

useCallback 은 마치 한 변수에 저장된 것 처럼, [ 결과 값이 같다면 ], 이 함수객체는, 마칭 상속 받는 것 처럼, 새로운 메모리 주소에, 새로운 함수를 생성하지 않는다.

 

이는, useCallback 으로 감싸진 함수는, [ 값이 같다면, 정말로 같은 함수 ] 임이 된다.

 

* 의존성은 복잡하다. 더 공부해야한다.

 

6. [ 배열 ] 을 위한 최적화 방법 useMemo Hook

 

 useMemo의 사용 방법

 

함수형 으로, 호출해 배열을 감싸고, 의존성을 넣어준다.

const ArrayA = useMemo(()=>{
    	return [1,2,3,4,5]
    },[])

useCallback 처럼 [ 값이 같다면, 정말로 같은 배열 ] 이 된다.

 

* 의존성은 복잡하다. 더 공부해야한다.

 

최종적으로

1. 원시값의 props를 넘겨받은 자식은 React.memo 를 사용하여, "하위 컴포넌트(자식) 측에서"  export 하면 된다.

2. 함수값의 props 를 넘길 부모는, 최적화를 하려면, [useCallback Hook] 을 사용하여, 함수를 만들고,

    자식에게 넘긴다. 자식은, React.memo 를 사용하여, export 한다.

2. 함수값의 props 를 넘길 부모는, 최적화를 하려면, [useMemoHook] 을 사용하여, 배열를 만들고,

    자식에게 넘긴다. 자식은, React.memo 를 사용하여, export 한다.

댓글