-
useMemo를 사용해야 할 필요성이 있는 경우는 크게 두가지 경우가 있다.
1. 연산이 큰 작업이 반복되어서 호출 될 때
2. Referential equility가 발생할 경우에.
먼저 첫번째 경우를 살펴보자.
위와 같은 예제에서 input의 값을 증가시키면 그 두배가 되는 값을 화면에 렌더링 해주는데 약 0.5초의 시간이 걸린다.
slowFunction에서 반복을 100000000만큼 돈후에 그 값을 두배로 해주기 때문이다. 여기까지는 이해가 쉽다.
하지만 여기에서 changeTheme버튼을 누르면 어떻게 될까? 마찬가지로 0.5초의 시간이 흐른후 화면의 theme색깔이 바뀐다. 왜그럴까?
이는 리엑트에서 state를 업데이트 하면 컴포넌트 전체를 다 리렌더링 하기 때문이다.
즉, 시간이 걸리는 작업인 slowFunction이 MemoEx컴포넌트를 불러올 때(numberState가 변할때, darkState가 변한때 혹은 다른 State가 변할 때 마다 전체가 다시 랜더링 됌) 마다 불필요 하게 계속 실행이 된다는 말
(slowFunction get called every single time that we render MemoEx component)
이럴때 useMemo를 사용하면 값을 캐쉬에(메모리)에 저장하고 이를 그대로 불러오기만 하면 되서 연산을 다시 계산하지 않아도 된다. 이를 Memoization이라고 한다.
우리의 예제에서는 slowFunction의 인풋이 변하지 않는한 그 값은 고정되어있으므로 memoization한 값을 연산없이 그대로 불러와서 쓰기만 하면 된다.
사용법은 다음과 같다
useMemo(()=>{}, [])
콜백함수는 실행할 함수가 들어가고 , 디펜던시 배열을 설정해줄 수 있다.
우리의 예제에서는 slowFunction이 number의 값에 의존적이므로
const doubleNumber = useMemo(()=>{ return slowFunction(number)}, [number])
이런식으로 사용할 수 있다.
이렇게 하면 0.5초의 딜레이가 사라지고 바로바로 화면에 렌더링 되는 모습을 확인 할 수 있다.
즉, number에 변화가 있으면 slowFunction을 실행시키고
number에 변화가 없으면 시간이 오래걸리는 연산인 slowFunction을 건너뛰고 캐싱 해논 결과값을 사용한다.
그러나 useMemo의 경우 이 값을 메모리 내부에 저장해 놓기 때문에 메모리 공간을 필요로 한다. 메모리를 차지하므로 프로그램의 모든 곳에서 메모를 쓴다면 쓸때없이 차지하고 있는 메모리가 증가하여 성능이슈가 발생할 가능성이 높아진다. 결국 메모를 쓰는것이 prefomance benefit이 있을 경우에 적절하게 쓰는 것이 중요하다.
자 이제 두번 째 경우를 살펴보자.
이경우는 자바스크립트의 참조형 타입에서 나타나는 경우이다.
위의 예제에서
const themeStyles = {backgroundColor: dark ? 'black' : 'white',color: dark ? 'white' : 'black',};const themeStyles2 = {backgroundColor: dark ? 'black' : 'white',color: dark ? 'white' : 'black',};가 있을 때 themStyle === themStyles2라는 연산을 해보면 false가 나온다.이는 js에서 두개의 다른 객체를 참조하고 있기 때문에 일어나는 일이다.이런 현상은 함수가 재 렌더링 될때에도 똑같이 일어난다.
처음에 있던 themStyles와 state의 변화가 있어서 재 렌더링 된 후의 themStyles는 값에 전혀 변화가 없더라도 메모리상에 다른 부분에 저장되어있다. 즉 다른 객체라는 소리다.
useEffect(() => {console.log('Theme Changed');}, [themeStyles]);이런식으로 useEffect를 사용해보면
Change Theme을 눌렀을 때 뿐만 아니라, 숫자의 입력을 바꾸더라고 Them Changed가 실행되는 것을 볼 수 있다.
숫자의 변경을 주면 number state가 변하고, 이는 컴포넌트 전체를 다시 랜더링 하게 된다. themeStyle또한 재 랜더링이 일어나고, 이는 다른 객체이므로 (메모리상 참조하는 위치값이 다름) js는 themeStyle이 변했다고 판단하고, useEffect를 실행하게 되는 것이다.
이런 상황에서도 useMemo를 사용할 수 있다.
const themeStyles = useMemo(() => {return {backgroundColor: dark ? 'black' : 'white',color: dark ? 'white' : 'black',};}, [dark]);Memoaization 안에 객체를 래핑 해놓았기 때문에 [dark]라는 값이 변하지 않으면 래핑된 객체(theneStyles)를 reupdate하지 않는다. 정확하게 그 전의 참조값(reference)와 동일 한 값을 얻어 오므로 재 랜더링이 일어 나지 않는다.참고로 []디펜던시 배열을 빈배열로 설정 할 경우에는 컴포넌트가 렌더링 될 때 만들었던 함수를 계속해서 재사용하게 된다. 어떠한 상태값에 의존적이지 않는 경우에만 사용할 수 있다.
'React' 카테고리의 다른 글
useCallback (0) 2022.02.25 setState()심화 (0) 2022.01.17 UseEffect (0) 2022.01.13 Router (0) 2022.01.09 react router useNavigate() (0) 2022.01.05