useCallback vs. useMemo

Keun·2022년 6월 27일
0

반드시 알아야 하는~

리액트 훅에 대해서 저번에 한번 정리를 하기 했으나, 완벽하진 않은것 같다.
특히, 내가 요즘 VS CODE에다가 혼자 사용해보면서 우오 이렇구나를 느끼고 있는데,
usCallback과 useMemo가 헷갈린다. 그리고 useCallback은 내가 심지어 써본 것이기도 한데 이상하게 머릿속에 생각이나지않는다. 내 기억에 이 두개의 차이는 면접에서도 물어본 것 같다. 어찌어찌 대답은 했으나, 딱 들어봤을때 '잘 알지 못하고 일단 외운대로 말하는구나' 라는 인상을 심어줬던 것 같다.

크게 보았을 때의 차이.

usCallback과 useMemo는 메모이제이션 된 값을 반환한다. 차이점은 useCallback은 함수를 메모이제이션하고 useMemo 는 값을 메모이제이션한다.

메모이제이션이란?
위키피디아에서는: "메모이제이션(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다. 동적 계획법의 핵심이 되는 기술이다."

차이를 보기전에 알아야 할 것들.

이 글은 철저히,

https://leego.tistory.com/entry/React-useCallback%EA%B3%BC-useMemo-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

이곳에서 참고했고, 정리했다. 감사드립니다. 예시도 여기서 확인바란다. 굉장히 정리를 기가막히게 잘해놓으셨다.

리액트에서 리렌더링은 굉장히 중요하다. 성능 향상과 저하에 엄청난 영향을 준다.
우선 리렌더링 되는 조건은 (1) state가 변경될 때, (2) 부모컴포넌트로부터 props가 변경될때, (3) 부모 컴포넌트가 리렌더링될때 이렇게 3가지이다.

하지만 이 세가지 조건을 다 따르는 컴포넌트들이 만들어 졌다고 하자. 예를 들어, useState를 사용하여 값 (state)이 버튼이라는 컴포넌트를 누를때마다 바뀌고, 이것을 부모 컴포넌트에서 관리 한다고 하자. 여기서 이 모든 세가지 조건이 발동하여, 어느 부분을 누르든 모든것이 리렌더링 될 것이다. 물론, 가끔가다가 어떤 웹을 보면, 의도적으로 이렇게 만들어서, 동작하는 것도 있긴하지만. 이 세가지를 다 포함하는 컴포넌트를 만들게 된다면, 어딜 누르나 상태변화가 일어나기 때문에, 동작이 하나만 되도록 붙잡아 놓는 것이 좋다. 즉, 리렌더링을 줄여아한다.

React.memo

이름만 봐도 알 수 있듯이, 메모이제이션을 해준다. 특히 이 기능은, 부모컴포넌트로 넘겨받은 props가 같다면 메모이제이션 해둔 렌더링 결과를 가져온다. 메모이제이션한 내용을 재사용하여 렌더링시 가상 DOM에서 달라진 부분을 확인하지 않아 성능상의 이점이 생기게 된다고 한다.

사용법

const MyComponent = React.memo(function MyComponent(props){
렌더링 하는 곳. 
});
function MyComponent(props){
렌더링 하는 곳.
}
export default React.memo(MyComponent, areEqual);

하지만, 이것을 반드시 알아야한다. 리렌더링 될 경우에, 새로운 함수를 계속 생성한다.
React.memo를 이미 사용하여, 메모이제이션되서 리렌더링을 일으키지 않는 컴포넌트가 있다. 그런데, 다른 컴포넌트때문에 리렌더링이 일어났다. 이렇게 될 경우, React.memo가 고장난 것이 아니라, 판단의 오차가 발생 하기 때문이다.

"컴포넌트는 리렌더링 할 때 마다 새로운 함수를 계속 생성하며, React.memo는 부모 컴포넌트로 넘겨받은 props가 변경되었다고 판단하여 계속 리렌더링 하는 것이다."

내가 참고한 블로그에서 글쓴이는 이와같은 예시를 들어주어서 나의 이해를 높여주었다.

object !== object
리렌더링이 발생되면 해당 컴포넌트의 객체들은 다시 생성된다. -> 함수도 객체이다!!!!!!
처음알았음...함수도 객체라는 사실... 감사합니다...
자바스크립트에서 객체는 참조타입으로 완전히 동일한 값을 가지고 있더라도 참조하는 메모리의 주소가 다르면 서로 다른 객체로 취급한다고 한다.

안에 값들이 같더라도 서로 다른 객체를 취급하는 것을 볼 수 있다. wow와 amazing은 같다. 왜냐면 amazing이 wow에서 가져와씩 때문이다. 그러나 wow를 값이 똑같은 새로운 것과 비교했더니 다르다고 한다. 그것은 메모리에 저장된 주소값이 다르기 때문에 그런 것이다.

"컴포넌트는 리렌더링할 때 마다 새로운 함수를 계속 생성하며, React.memo는 부모컴포넌트로 넘겨받는 props가 변경되었다고 판단하여 계속 리렌더링라는 것이다."

이것을 고치기 위해서, useCallback과 useMemo는 여기서 발생하는 불필요한 렌더링과 불필요한 계산을 방지하는 목적으로 설계되었다고한다.

useCallback

사용법

const 콜백사용법 = useCallback(
()=>{
  doSomething(a, b);
}, 
[a, b], // dependencies
);

deps안에 넣어준 값이 바뀔때마다 새로운 객체를 생성해준다.
-> 즉, 한번만 렌더링 될 뿐, 리렌더링은 절대 안된다.

useMemo

사용법

const 메모이제이션훅사용법 = useMemo(()=> computeExpensiveValue(a,b), [a, b]);

예시

function App(){
...
return(
	<div className="App">
    	<div className="num" onClick={()=>{setNumber(number+1)}}>
        	{number}
        </div> 
        <Button onClick={onClick} style={{backgroundColor: 'darkseagreen'}}/>
    </div> 
...);
}

이렇게 만들어줄 경우에, 분명히 리렌더링이 모두에게 발생하고, 렌더링시마다 새로운 객체가 만들어진다. 컴퓨터는 메모리에 새로운 주소값을 계속 넣어주고 있을 것이다.

이럴때 useMemo로 값을 홀드해주는 것이다. 그러면 리렌더링이 안되게 방지시킬수있다.
한마디로 컴퓨터에게 이거 내가 deps에다가 값 넣어주지 않는이상 리렌더링 해주지마. 라고 말을 해주는 것이다.

function App(){
...
const buttonStyle - useMemo(() => ({backgroundColor: 'darkkseagreen'}), []);

return(
	<div className="App">
    	<div className="num" onClick={()=>{setNumber(number+1)}}>
        	{number}
        </div> 
        <Button onClick={onClick} style={{buttonStyle}}/>
    </div> 
...);
}

이럴경우 다시 렌더링이 일어나지 않는다.

deps에는 반드시 의존하는 값이 있으면 넣어줘야한다. 없더라도 반드시 빈 대괄호라도 넣어주는거 잊지말고.

최적화를 위해서는 불필요한 리렌더링을 줄이는 것이 또하나의 관건이다. -> 그래서 useCallback과 useMemo를 알아야한다.

지금 정리는 했는데 써보러 가봐야제~

0개의 댓글