본문 바로가기

React

[React] useMemo 사용법 및 예제, 변수 재사용 - react hook

안녕하세요. 오늘은 useMemo를 활용하여 렌더링 시 변수를 재 사용함으로써 app의 성능을 향상 시킬 수 있는 방법을 알아보도록 하겠습니다.

 

먼저 예제 코드를 통해 문제 발생을 시켜보겠습니다.

 

index.js

import styles from '@/styles/Home.module.css'
import React, { useEffect, useRef, useState } from 'react'
import HorizontalScroll from '../components/HorizontalScroll/HorizontalScroll'

export default function Home() {
  const [input, setInput] = useState(0)
  const onInputChange= (e)=>{
    setInput(e.target.value)
  }

  return (
    <main >
        <div>
          몇개를 생성할까요?
        </div>
        <input onChange={onInputChange}/>
        <button onClick={()=>pushArr()}>생성하기</button>
              <div>
              <p>==================================</p>
            <HorizontalScroll length={input}>
            </HorizontalScroll>
        </div>
      </main>
  )
}

메인 컴포넌트에서는 input을 통해 생성시킬 리스트의 숫자를 입력받고 이 숫자를 자식 컴포넌트로 props를 통해 전달해 줍니다.

 

HorizontalScroll.js

import styles from './HorizontalScroll.module.css'
import React, { useRef, useState, useMemo } from 'react'

export default function HorizontalScroll({ length}) {  
  const scrollRef = useRef(null);;
  const [input, setInput] = useState("");
  
  const createList = (number) =>{
    let result = []
    for(let i = 0; i < number; i++) result.push(`${i+1}번`)
    console.log("리스트가 생성되었습니다.")
    return(result)
  }
  
  const list = createList(length)
  return (
      <main >
      <div>
          state 변경하면?
          <input onChange={onInputChange}/>
        </div>

            <div className={styles.list}>
              {
                list.map((item, index) => (
                  <div className={styles.item} key={index}
                    onClick={() => {
                      console.log(`${index}번 클릭입니다 created by ${input}`)
                    }}>
                    <p>{item}</p>
                  </div>
                ))
              }
            </div>
      </main>
  )
}

 

자식 컴포넌트에서는 전달받은 숫자만큼 리스트를 생성하여 출력을 해줍니다.

 

실행화면

state가 바뀌면 리스트 생성 함수가 실행 된다

 

 

1. 문제

얼핏 잘 작동하는 것처럼 보이지만 length값은 그대로인데 전혀 상관없는 state가 변경되면 그때마다 리스트를 생성해주는 function이 호출되는 것을 볼 수 있습니다. 이는 복잡한 function 일 수록 app의 성능을 저하시킬 수 있는 요인으로 작용합니다.

 

 

2. 원인

원인은 함수형 컴포넌트와 state의 특징에 있습니다. 

react 에서 state는 특정 값이 변경될때 해당 컴포넌트를 re-rendering 시키기 위해 사용됩니다. 하지만 함수형 컴포넌트는 결국 함수이기 때문에 컴포넌트 함수가 호출 될때마다 내부 변수가 초기화 되는 과정을 거칩니다.

이러한 과정은 예제 코드에서

const list = createList(length)

이 부분을 반복적으로 실행하게 됩니다. createList 함수가 무거운 함수 일수록 성능에 악영향을 주게 됩니다.

 

 

3. 해결 방법

위 예제 코드에서는 이 문제를 해결할 수 있는 여러 방법이 있겠지만 오늘은 이중에서 useMemo라는 react hook을 소개하도록 하겠습니다.

 

 

4. useMemo란?

useMemo에서 memo는 memoization을 의미합니다. memoization은 기존에 수행했던 연산의 결과값을 저장해 두었다가 연산에 같은 input이 들어왔을 때 해당 이전에 기억해두었던 연산을 재활용 하는 기법입니다.

useMemo는 첫번째 인자로 콜백 함수를 받고, 두번째 인자로 의존성 배열을 받습니다. 의존성 배열에 속해있는 변수들에 대한 콜백함수의 연산값을 memoization 해두고 사용 하겠다는 뜻입니다.

 

 

5. 해결

//const list = createList(length) 아래 코드로 변경
const list = useMemo(() => {
  createList(length);
}, [length])

위와 같이 createList함수를 useMemo의 콜백함수로 전달 해주고 의존성 배열에 length를 넣어 줍니다. 이러면 length의 값이 변하지 않는 한 createList 함수는 호출 되지 않을 것입니다. 다시말해 다른 state의 값이 변하여 컴포넌트가 리렌더링 되더라도 createList 함수는 다시 호출되지 않을 것입니다. 여기서 주의 할 점은 만약 콜백 함수에 여러 인자들이 사용된다면 의존성 배열에 해당 인자들을 모두 넣어줘야 한다는 것입니다.

 

 

6. 해결 결과 실행 화면

state가 바뀌어도 리스트 생성 함수는 바뀌지 않는다.

 

 

이상으로 useMemo에 대해서 알아보았습니다. useMemo가 분명 app 성능 향상에 도움을 주는것은 사실이지만 memoization이란 결국 연산 결과값을 어딘가에 저장해두고 사용하는 것이므로 저장에 대한 리소스를 사용하게 됩니다. 그러니 현명한 활용이 필요 할 것입니다.