개념,기능 정리

memoization

rhdaud2 2024. 5. 29. 20:44

 

 

 

memoization , custom hook 가 없어도 기능은 잘 되어야한다.

최적화가 없이 작동이 안 된다면, 잘못 작성한 코드일 가능성이 높다

-최적화는 마지막에 진행

 

 

memoization 

불필요한 리렌더링 막기

 

*캐싱 : 자주 사용하는 데이터를 저장해두고, 필요할때 빠르게 접근할 수 있게 하기 위해 사용

 

 

웹개발에서의 사용 예 : 

- 첫 렌더링 시 불러온 데이터를 브라우저에 캐싱해두고, 필요할 때 재사용( 브라우저 캐싱)

- 캐싱은 자주 사용하는 데이터를 더 빠르게 접근할 수 있는 위치에 저장한다

 

 

react 공식 문서 - useContext

https://react-ko.dev/reference/react/useContext

 

useContext – React

The library for web and native user interfaces

react-ko.dev

 

 

 

1. memo(컴포넌트 캐싱)

 

부모 컴포넌트 리렌더링 시 props 변화 없는 자식 컴포넌트도 렌더링 될 경우, 그 때를 막기 위해 주로 사용한다.

- 필요한 경우에만 사용. (memoization도 결국 메모리를 차지하는 것이기때문에, 불필요하게 사용하면 메모리 낭비)

 

props 값이 변하지 않는다는 전제 하에 적용

 

 

사용법 1

 

component의 함수 부분 감싸기

import { memo } from "react";

const List = memo(({ items }) => {
  console.log("List component rendered");
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
});

export default List;

 

사용법 2

 

export 할 때에 감싸기(이 방식이 더 편리하고 좋아보인다)

import { memo } from "react";

const List = ({ items }) => {
  console.log("List component rendered");
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
};

export default memo(List);

 

 

 

 

 

 

2. useMemo(값을 캐싱, 함수 캐싱도 가능)

 

계산 비용이 많이 드는 함수를 rendering path 내에서 사용해야할 때

 

계산 시간이 1ms 이상이면 계산 비용이 비싸다고 판단

- console.time(),console.timeEnd() 로 작동 시간 확인 가능 -괄호 속에 같은 이름을 문자열로 넣어 함수를감싸줘야한다)

- for, filter, map 등의 가동 시간이 1ms가 넘어가면 적용 - react에서 권장하는 방식)

 

ex. 반복문 

conse doExpensiveWork = useMemo(()=>{}

=> 첫 렌더링시에만 작동시켜 값을 담고, 다시 실행시키지 않는다

 

ex.

const App = () => {
  const [input, setInput] = useState("");
  const [items, setItems] = useState(["Item 1", "Item 2", "Item 3"]);

  const handleInputChange = (event) => {
    setInput(event.target.value);
  };

  const addItem = () => {
    setItems((prevItems) => [...prevItems, input]);
    setInput("");
  };

  //items가 변경될 때만 해당 함수 실행
  const filteredItems = useMemo(() => {
    return items.filter((item) => item.toLocaleLowerCase().includes("item"));
  }, [items]);

  return (
    <div>
      <h1>Item List</h1>
      <input type="text" value={input} onChange={handleInputChange} />
      <button onClick={addItem}>Add Item</button>
      <List items={filteredItems} />
    </div>
  );
};

 

 

 

3. useCallback(함수 자체를 캐싱)

- useMemo 사용으로도 되지만, 가독성 등을 높이기 위해 이해하고있어야한다

custom hook에 함수가 있을 경우 useCallback으로 감싸주는 걸 리액트 공식 문서에서 권장함

 

 

 

custom hook : 

 

https://ko.react.dev/learn/reusing-logic-with-custom-hooks

 

커스텀 훅 이름은 use 로 시작

재사용하는 커스텀 훅도 useCallback으로 감싸준다

(리액트 권장 내용)

 

 

사용 이유

 

- 비지니스 로직 재사용

- 컴포넌트 가독성 향상

- 유지보수성 향상

 

 

 

사용 예시

 

//custom hook

// TODO: 이곳에 커스텀훅 작성하세요.

import { useEffect, useState } from "react";

function useFetch() {
  const [title, setTitle] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
        console.log("response:", response);
        if (!response.ok) {
          //응답 실패시,작동 멈추고 콘솔에 "Network 오류"
          throw new Error("Network 오류");
        }
        const result = await response.json();
        setTitle(result.title);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <h1>Loading...</h1>;
  if (error) return <h1>Error: {error.message}</h1>;

  return { title };
}

export default useFetch;

 

 

//App.jsx

import React from "react";
import useFetch from "./hooks/useFetch";

const App = () => {
  const { title } = useFetch();

  return (
    <div>
      <h1>Data Fetching Example</h1>
      <p>{title}</p>
    </div>
  );
};

export default App;