반응형

 

useMemo


useMemo
동일한 값을 리턴하는 함수를 반복적으로 호출해야 한다면 처음값을 계산할 때
그 값을  메모리에 저장해서 필요할때만 계산을 하고, 일반적으로 리렌더링 되는상황에서는

메모리에서 꺼내서 재사용하는 기법 자주 필요한 값을 처음 계산할 때 캐시에 저장을 해두어  
값이 필요할 때마다 다시 계산하지 않고 캐시에서 꺼내서 재사용. 값이 변경되면 다시 저장

 

사용법

구문 * deps == 의존성배열

useMemo(( )=>{
	return value;
},[deps])

콜백함수와 , 배열을 인자로 받아서 사용한다,

콜백함수는 메모이제이션(메모리에저장) 해줄 값을 리턴해주는 함수,

배열은 의존성 배열이라고도 부르는데 배열 안의 값이 업데이트될 때만 콜백함수를 호출해서

메모이제이션 된 값을 업데이트해서 다시 메모이제이션 해준다.

(저장된 값을 불러와서 변경한 후 다시 저장한다는 소리.) ,

의존성 배열이 없으면 처음마운트될 때 값이 메모이제이션되고 필요할 때마다 같은 값을 사용한다

(바뀌었는지 확인할 수 있는 값이 없으니 처음에 저장된 값을 계속 사용.)

 

App.js

import './App.css';
import { useState } from 'react';
import ShowState from './components/ShowState';

function App() {
  const [number,setNumber] = useState(0);
  const [text,setText] = useState("");
  const increaseNumber = ()=>{
    setNumber(number+1)
  }
  const decreaseNumber = ()=>{
    setNumber(number-1)
  }
  const onChange = (e)=>{
    setText(e.target.value)
  }

  return (
    <div className="App">
      <div>
        <button onClick={increaseNumber}>+1</button>
        <button onClick={decreaseNumber}>-1</button>
        <input type="text" placeholder='lastname' value={text} onChange={onChange}/>
      </div>
      <ShowState number={number} text={text}></ShowState>
    </div>
  );
}

export default App;

ShowState.js

import React from 'react';

const getNumber = (number)=>{
    console.log("숫자가 변동되었습니다.");
    return number
}
const getText = (text) =>{
    console.log("글자가 변동되었습니다.")
    return text;
}
const ShowState = ({number,text}) => {
    const showNumber = getNumber(number);
    const showText = getText(text);
    return (
        <div>
            {showNumber}<br/>
            {showText}
        </div>
    );
};
export default ShowState;

숫자를 변경해도 글자가 변동되었다는 문자가 같이 나온다

상태값이 변경될 때 컴포넌트가 리렌더링 되는데 다시 렌더링 되면서 함수를 다시 전부 실행해 줘서이다.

 

+1 버튼을 클릭하면 increaseNumber함수가 실행되고 setNumbernumber상태를 1 증가시켜 준다

상태가 변경되면서 리렌더링 되고 App, ShowState 컴포넌트를 다시 불러온다.

 

ShowState컴포넌트 내부의 코드

const showNumber = getNumber(number);
const showText = getText(text);

getNumber와 getText함수를 다시 실행하기 때문에 콘솔에 로그가 뜨게 된다. 

 

ShowState.js (useMemo사용)

import React, { useMemo } from "react";

const getNumber = (number) => {
  console.log("숫자가 변동되었습니다.");
  return number;
};
const getText = (text) => {
  console.log("글자가 변동되었습니다.");
  return text;
};
const ShowState = ({ number, text }) => {
  const showNumber = useMemo(() => {
    return getNumber(number);
  }, [number]); //number가 변경될때만 getNumber가 다시 실행된다.
  // const showText = useMemo(() => {
  //   return getText(text);
  // }, [text]);
  const showText = getText(text); //상태값이 변경되서 리렌더링 될때마다 getText다시실행행
  return (
    <div>
      {showNumber}
      <br />
      {showText}
    </div>
  );
};

export default ShowState;

useMemo를 사용하게 되면 의존성배열에 있는 값을 메모리에 저장했다가

값에 변화가 있을 때에만 콜백함수를 실행해서 값을 업데이트하고 다시 메모리에 저장해 준다.

위 코드에서처럼 useMemonumber값을 메모리에 저장하고 number값이 변경될 때만 실행된다

input에 글자를 입력하는 경우는 number값에 변화가 없기 때문에 getNumber함수가 실행되지 않는다.

 

반대로 showText값은 아까처럼  어떠한 상태값이 변경되어서 리렌더링 될 때마다

getText함수를 실행시켜서 콘솔에 

"글자가 변경되었습니다"라는 글이 나오게 된다

그렇지만 useMemo를 이렇게 사용하는 것은 별로 좋은 사용법이 아니다.

메모리의 자원을 점유하게 되기 때문에 자원을 낭비하게 되는 것이다.

꼭 필요한 경우에만 사용할 수 있도록 하자!! 

지금 같은 코드는 useEffect로 값이 변경될 때에만 실행시켜 주는 방식으로 충분히 문제를 예방할 수 있다.

 

ShowState.js (useEffect사용)

import React, { useEffect } from "react";

const getNumber = (number) => {
  console.log("숫자가 변동되었습니다.");
  return number;
};
const getText = (text) => {
  console.log("글자가 변동되었습니다.");
  return text;
};
const ShowState = ({ number, text }) => {
  const showNumber = useEffect(
    (number) => {
      return getNumber(number);
    },
    [number]
  );
  const showText = useEffect(
    (text) => {
      return getText(text);
    },
    [text]
  );
  return (
    <div>
      {showNumber}
      <br />
      {showText}
    </div>
  );
};

export default ShowState;

이렇게 메모리를 사용하지 않고도 해결할 수 있기 때문에 useMemo를 사용할 일이 많지는 않지만 

useEffect로 의존배열을 받을 때 객체는 넣어도 구분할 수 없다는 문제점이 있어서 이때는 useMemo를 사용해서 해결해 줄 수 있다.

 

App.js

import "./styles.css";
import React, { useEffect, useState } from "react";

const MemoComponent = () => {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);

  const location = isKorea ? "한국" : "외국";
  //함수가 호출될때마다 항상 새로운객체가 생성됨
  // useEffect -->마운트될때, 리렌러링될때(업데이트) ,언마운트될때
  useEffect(() => {
    console.log("useEffect 호출");
  }, [location]);
  // 마운트될때와 location이변경될때 실행됨
  return (
    <div className='App'>
      <h2>좋아하는 숫자는?</h2>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <h2>이동하실까요</h2>
      <p>나라: {location}</p>
      <button onClick={() => setIsKorea(!isKorea)}>이동</button>
    </div>
  );
};

export default MemoComponent;

아까처럼 useEffect를 사용해서 숫자가 변경될 때에 location 값이 변경될 때에만 "useEffect를 사용 중입니다."라는 코드를 실행하게 해 주었다.

 

 

지금은 의존성 배열에 문자열값이 들어가는데 만약 여기에 객체타입이 들어간다면 어떻게 될까?

App.js

import "./styles.css";
import React, { useEffect, useState } from "react";

export default function App() {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);

  const location = {
    country: isKorea ? "한국" : "외국"
  };

  useEffect(() => {
    console.log("useEffect 호출");
  }, [location]);

  return (
    <div className="App">
      <div>
        <h2>좋아하는 숫자는?</h2>
        <input
          type="number"
          value={number}
          onChange={(e) => setNumber(e.target.value)}
        />
        <h2>이동하실까요</h2>
        <p>나라: {location.country}</p>
        <button onClick={() => setIsKorea(!isKorea)}>이동</button>
      </div>
    </div>
  );
}

location을 객체로 반환받게 만들어줬다

타입이 객체로 바뀐 것뿐인데 숫자를 바꿔도 'useEffect호출'이 콘솔에 뜨는 걸 볼 수 있다.

 

이건 타입에서 생기는 문제인데 string타입은 변수에 값이 바로 담겨있기 때문에 useEffect에서 봤을 때

같은 값이면 실행시키지 않는다.

그러나 객체타입은 값을 바로 가지는 게 아니고 주소값을 가지기 때문에 상태값이 변경되어 리렌더링이 일어나고 location이 다시 실행되어서 같은 모양의 객체를 만들어도 가지고 있는 주소값이 이전과 다르기 때문에 useEffect에서 보기에는 다른 값이라고 여기게 되어서 리렌더링이 일어날 때마다

안의 console.log가 실행되게 된다.

 

이럴 때에는 useMemo를 이용해서 isKorea 가 변경될 때만

location이 실행되어서 country를 받을 수 있게 해 준다.

 

App.js

import "./styles.css";
import React, { useEffect, useMemo, useState } from "react";

export default function App() {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);

  const location = useMemo(() => {
    return {
      country: isKorea ? "한국" : "외국"
    };
  }, [isKorea]);
  useEffect(() => {
    console.log("useEffect 호출");
  }, [location]);

  return (
    <div className="App">
      <div>
        <h2>좋아하는 숫자는?</h2>
        <input
          type="number"
          value={number}
          onChange={(e) => setNumber(e.target.value)}
        />
        <h2>이동하실까요</h2>
        <p>나라: {location.country}</p>
        <button onClick={() => setIsKorea(!isKorea)}>이동</button>
      </div>
    </div>
  );
}

이렇게 써주면 숫자가 변경될 때는 isKorea가 변하지 않기 때문에 {country: isKorea? '한국':'외국'} 구문을 실행하지 않게 되고 useEffect에서는 새로 객체를 만들지 않으니 똑같은 값이기 때문에 useEffect호출이 콘솔 로그되지 않는다.

 

아직 useMemo개념은 언제 써야 할지 잘 모르겠다... 조금 더 hooks에 익숙해지고 나면 잘 쓸 수 있겠지.. 지금 이 내용도 그때 다시 포스팅을 올려서 더 알기 쉬운 포스팅이 되었으면 좋겠다..

반응형

+ Recent posts