useMemo()

jh_leitmotif·2021년 10월 8일
0

Frontend 개인 공부

목록 보기
4/24
post-thumbnail

🧐 개요

useEffect, useState, useRef, useDispatch... 등등

Hook의 재미에 빠지다 약간씩 느린 웹 반응을 해결하기 위해

사용해본 것이 useMemo였습니다.

결론적으론 Navigation Bar를 제외하면 딱히 프로젝트 내에 정적으로 있을 내용은 없다보니

큰 틀을 먼저 렌더링하고 useEffect로 약간 응답이 늦을 수도 있는 것들을 처리했습니다만

useMemo 기능을 간과하면 안되겠다는 생각에 포스트로 정리해봅니다.


📋 What is useMemo?

useMemo는 메모이제이션된 값을 반환합니다.

메모이제이션(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 
이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 
프로그램 실행 속도를 빠르게 하는 기술 from 위키백과

핵심 개념이 될 메모이제이션에 대해 미리 정리해봅니다.


Memoization?

메모이제이션의 흔한 예로는 팩토리얼피보나치 등이 있습니다.

Standard Factorial

def factorial(n):
    if n < 2:
        return 1
    return n * factorial(n - 1)

factorial(5)
factorial(6)

위와 같은 일반적인 팩토리얼 코드에서는 함수를 호출했을 때 매번 모든 계산을 통해 결과가 나옵니다.

5 x 4 x 3 x 2 x 1 이라는 대장정을 거쳤는데

또 다시 6 x 5 x 4 x 3 x 2 x 1, 즉 6 x 5!을 전부 계산해야되는 것이죠.

이를 위해 이전의 값을 미리 저장해두고

필요할 때 꺼내어 사용하는 방식을 메모이제이션이라고 합니다

Memoization Factorial

factorial_memo = {}

def factorial(n):
    if n<2:
        return 1
    if n not in factorial_memo: 
    		# 만약 n 요소가 배열 안에 있다면
            	# 계산을 스킵하고 즉시 배열에 있는 값을 반환해버린다.
        factorial_memo[n] = n * factorial(n-1)
    return factorial_memo[n]

factorial(5)
factorial(6)

주석에 있는 것과 같이, 직전에 있는 값이 배열에 이미 계산되어 넣어져있기 때문에

예를 들어 factorial(3)은 3 x factorial(2)를 하는 것이 아니라

3 x 2 를 하게 되어 두번의 재귀 과정을 스킵하게 됩니다.

한 두번 정도는 그저 가랑비지만, 계속 누적되다보면 흠뻑 젖는 것처럼 늘어나는 연산량을

메모이제이션이 멋지게 해결해주는 것입니다.

React의 경우

간단하게 카운트가 증가되는 예시를 들어보겠습니다.

Standard Code

import React, {useState} from 'react'

const Counter = () =>{
	const [countValue, setCountValue] = useState(0)
    
    const countHandler = () =>{
    	setCountValue(countValue+1)
    }
    
    const tempCalculate = () =>{
    	let sum = 0;
        for (let i=0; i<50000000; i++){
        	sum+=i;
        }
        return sum;
    }
    
    return (
    	<>
        	<button onClick={countHandler}> 카운트 증가! </button>
            	<span>{countValue}</span>
            	<div>
                	{tempCalculate}
                </div>
        </>
    )

버튼을 누를 때마다 countValue 값이 증가합니다.

countValue 값을 웹에 보여주기 때문에 버튼을 누를 때마다 새로 렌더링이 될텐데

tempCalculate라는 어마무시한 값을 반환할 함수가 떡하니 있어 약간의 부하가 분명히 있을 겁니다.

Memoization Code

import React, {useState, useMemo} from 'react'

const Counter = () =>{
	const [countValue, setCountValue] = useState(0)
    
    const countHandler = () =>{
    	setCountValue(countValue+1)
    }
    
    const tempCalculate = useMemo(() =>{
    	let sum = 0;
        for (let i=0; i<50000000; i++){
        	sum+=i;
        }
        return sum;
    }),[])
    
    return (
    	<>
        	<button onClick={countHandler}> 카운트 증가! </button>
            	<span>{countValue}</span>
            	<div>
                	{tempCalculate}
                </div>
        </>
    )

이 경우, 간단히 해당 함수를 useMemo로 감싸주면 됩니다.

tempCalculate 함수는 따로 상태를 건드리지 않으니, 더 업데이트될 값은 없을테고

반환된 값은 메모리에 잘 저장되어 countValue가 업데이트될 때 연산되지 않고

꺼내어 보여주기만 하게 됩니다.

useMemo의 파라미터

useMemo(function(~),[deps])

첫 번째 파라미터는 메모이제이션될 함수를 넣습니다.

두 번째 파라미터는 의존성 배열입니다.
예를 들어 deps state의 변화가 있으면 연산을 진행하고, 없으면 이전 연산된 값을 사용합니다.


😡 성급한 최적화

규칙 1. 하지마라.
규칙 2 (전문가만 해당함). 아직은 하지 마라. 최적화가 필요하다는 것을 100% 확신하지 못한다면 하지 마라.

  • M.A.Jackson [Jackson75]

사용법과 관련해 이것저것 알아보다가, 사용하느니만 못할 수 있다는 포스트들이 많았습니다.

사실 따져보면 그것도 그럴 것이, 이미 컴퓨터의 연산 속도가 매우 빨라지기도 했고

웹이든 모바일 환경이든, 사용자들의 선택에 따라 update되는 요소들이 너무나도 많습니다.

그래도 위와 같이 어렵게 계산된 값을 웹 페이지에 보여줘야 된다라고 해도

const calculate = useMemo(()=>{
	let sum = 0
        for (let i = 0; i<10; i++){
            sum+=i;
        }
        return sum
}

이렇게 굳이 코드를 길게 빼기 보단

const initialCalculate = 45

계산기던... 간단한 스크립트를 짜던 미리 계산한 값을 변수로 선언해두면 될 일입니다.

결국 코드가 많아질 수록 더 많은 비용을 감내해야되므로

무분별한 최적화는 오히려 어플리케이션의 성능을 낮출 수 있다는 것이 결론입니다.


🎯 마무리

되돌아보면 useMemo를 써야하나? 라고 고민했던 것은

적절히 useEffect hook을 몇몇 상태에 따라 분배하여 렌더링을 제어하는 것만으로 해결되었습니다.

다만 간단한 문제가 아닌, 어쩔 수 없는 과한 연산을 실시간으로 처리해야하는 문제가 생길 수 있으니

useMemo()에 대한 개념은 꼭 빠삭하게 정리해두자라는 생각입니다.

한편으론 'hook...hook...hook...' 하고 있다가

코드가 균일하지 않게 짜여진 것은 아닐까? 생각이 듭니다.

내일은 프로젝트를 run 했을 때 뜨는 수많은(?) 사소한 경고줄들을 없애버릴 겁니다.


📋 도움이 된 링크들

https://ko.reactjs.org/docs/hooks-reference.html
-> React 공식 문서
https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98
-> 메모이제이션 위키백과
https://rinae.dev/posts/review-when-to-usememo-and-usecallback
https://yujonglee.com/reactrendering.html
-> 개인 블로그

특히 개인 블로그 글에서 많이 생각하며 배웠습니다. 감사합니다.

profile
Define the undefined.

0개의 댓글