TIL #28 | 메모이제이션을 통한 성능 최적화 / 비동기 처리 방식

kibi·2023년 11월 17일
1

TIL (Today I Learned)

목록 보기
27/83

메모이제이션을 통한 성능 최적화

함수가 동일한 계산을 반복해야 할 때 이전에 계산한 값을 메모리에 저장해서 동일한 입력이 다시 발생할 때 캐시된 결과를 반환한다. 동일한 계산의 반복 수행을 제거하여 프로그램의 속도를 높이는 최적화 기술이다.

"불필요한 리렌더링을 막아주기 위해 계산된 값을 메모리에 저장해주는 방법!"

컴포넌트간 함수를 내려줬을 때
자식컴포넌트는 변경되지 않아도 부모컴포넌트가 렌더링 될때마다 자동으로 리렌더링 된다 -> 매우 비효율적
=> 이런 부분을 해결하기 위해 캐싱 처리를 해야한다.

대표적인 캐싱 처리 방법
1) React.memo
2) useMemo
3) useCallback


React.memo

컴포넌트가 동일한 props로 동일한 결과를 렌더링한다면 React.memo를 호출하고 결과를 메모이징 하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있다.

React는 먼저 컴포넌트를 렌더링 한 뒤 이전에 렌더링 된 결과와 비교하여 DOM 업데이트를 결정한다. 만약 결과가 이전과 다르다면, React는 DOM을 업데이트 한다.

React.memo()로 둘러 쌓여있으면 React는 컴포넌트를 렌더링하고 결과를 메모이징한다. 그리고 다음 렌더링이 일어날 때 렌더링하는 컴포넌트의 props가 같아면 React는 메모이징된 내용을 재사용한다.

React.memo 예제

부모 컴포넌트의 카운트를 동작시키면 자식 컴포넌트 Box1, 2, 3 들이 자동으로 리렌더링 된다.
![[스크린샷 2023-11-17 오후 2.20.15.png]]

React.memo로 함수명을 감싸준다.

  • 초기 렌더링 되었을 때 정보를 기억한다.
export default React.memo(Box1);
export default React.memo(Box2);
export default React.memo(Box3);

부모 컴포넌트가 리렌더링 되어도 자식컴포넌트가 리렌더링 되지 않는다.
![[스크린샷 2023-11-17 오후 2.24.48.png]]

만약 부모 컴포넌트와 자식 컴포넌트가 props로 연결되어 있다면 React.memo()를 사용했어도 부모컴포넌트의 state가 바뀔 때 자식 컴포넌트도 영향을 받아 다시 리렌더링 된다.

React.memo의 얕은 비교

React.memo는 props 및 props의 객체를 비교할 때 얕은 비교를 한다.

함수를 넘겨줬을 경우, 넘겨준 함수가 동작하는게 아니더라도 자식 컴포넌트가리렌더링된다.

참조형 변수는 별도의 주소값을 참조한다
(배열, 객체, 함수 등은 매우 큰 값이기 때문이다. )

결국 부모 컴포넌트가 리렌더링 될때 함수의 메모리 주소가 계속 바뀌기 때문에 props로 함수를 받은 자식 컴포넌트가 자동으로 리렌더링 된다.

=> useCallback 사용해서 문제를 해결하자


useCallback을 이용한 함수 최적화

메모이제이션된 함수를 반환하는 함수

useCallback()을 사용하연 초기 렌더링 됐을 때의 메모리 주소값을 기억하게 되기 때문에 리렌더링 될 때마다 메모리 주소가 변하지 않는다.
메모리 주소가 변하지 않으면 자식 컴포넌트에 props로 넘겨준 함수에도 변화가 없기 때문에 자식 컴포넌트가 리렌더링 되지 않는다.

  const initCount = useCallback(() => {
    setCount(0);
  }, []);

useMemo

component내 compute 함수가 만약 복잡한 연산을 수행한다면 결과 값을 리턴하는데 오랜 시간이 걸리게 된다.. 이럴 시에 컴포넌트가 계속 리렌더링 된다면 연산을 계속 수행하는데 오랜 시간이 걸려서 성능에 안좋은 영향을 미치게 된다.
이런 현상을 해결해주기 위해 useMemo를 사용할 수 있다.
compute 함수에 넘겨주는 a, b값이 이전과 동일하다면 컴포넌트가 리렌더링 되더라도 연산을 다시 하지 않고 이전 렌더링 때 저장해두었던 값을 재사용한다.


비동기 처리

jsonplaceholder
https://jsonplaceholder.typicode.com/
비동기 처리 실습을 위해 위 홈페이지에서 api를 받아와 사용했다.

axios 패키지 설치
yarn add axios

promise를 이용한 비동기 처리

function App() {
  const [list, setList] = useState([]);

  useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/todos")
      .then((response) => setList(response.data))
      .catch((error) => console.log(error));
  }, []);

async, await를 이용한 비동기 처리

  useEffect(() => {
    const fetchData = async () => {
      const { data } = await axios.get(
        "https://jsonplaceholder.typicode.com/todos"
      );

      setList(data);
    };
    fetchData();
  }, []);

로딩 및 에러 처리

  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    setIsLoading(true);
    setIsError(false);
    const fetchData = async () => {
      try {
        const { data } = await axios.get(
          "https://jsonplaceholder.typicode.com/todos"
        );
        setIsLoading(false);
        setIsError(false);
        setList(data);
      } catch (error) {
        setIsLoading(false);
        setIsError(true);
        alert(error);
      }
    };
    fetchData();
  }, []);

  if (isLoading === true) return <div>로딩중...</div>;
  if (isError === true) return <div>에러가 발생했습니다.</div>;

0개의 댓글