리액트 훅 살펴보기 - useMemo, useCallback, useRef, useContext

shooting star·2024년 7월 11일

리액트 훅 살펴보기 - useMemo, useCallback, useRef, useContext

리액트는 다양한 훅(Hook)을 제공하여 상태 관리와 최적화를 쉽게 할 수 있습니다. 이번 블로그에서는 useMemo, useCallback, useRef, useContext에 대해 살펴보고 각 훅이 어떻게 사용되는지 예제를 통해 알아보겠습니다.

useMemo

useMemo는 비용이 큰 연산의 결과를 저장해두고, 이를 재사용하여 성능을 최적화하는 훅입니다. 흔히 리액트에서 최적화가 필요할 때 가장 먼저 언급되는 훅입니다.

사용법

첫 번째 인수로는 값을 반환하는 생성 함수를, 두 번째 인수로는 해당 함수가 의존하는 값의 배열을 전달합니다. 리렌더링이 발생했을 때 의존성 배열의 값이 변경되지 않았다면 함수를 재실행하지 않고 이전에 기억해둔 값을 반환합니다. 만약 의존성 배열의 값이 변경되었다면 첫 번째 인수의 함수를 실행한 후 그 값을 반환하고, 다시 기억해둡니다.

예제

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

const ExpensiveComponent = ({ num }) => {
  const computeExpensiveValue = (num) => {
    console.log("Computing...");
    return num * 2;
  };

  const expensiveValue = useMemo(() => computeExpensiveValue(num), [num]);

  return <div>{expensiveValue}</div>;
};

const App = () => {
  const [num, setNum] = useState(1);

  return (
    <div>
      <input type="number" value={num} onChange={(e) => setNum(Number(e.target.value))} />
      <ExpensiveComponent num={num} />
    </div>
  );
};

export default App;

useCallback

useMemo가 값을 기억했다면 useCallback은 인수로 넘겨받은 콜백 함수를 기억합니다. 특정 함수를 새로 만들지 않고 재사용할 수 있도록 합니다.

사용법

첫 번째 인수로 함수를, 두 번째 인수로 의존성 배열을 넣으면, 의존성 배열이 변경되지 않는 한 함수를 재생성하지 않습니다.

예제

import React, { useCallback, useState } from 'react';

const Button = React.memo(({ onClick }) => {
  console.log("Button rendered");
  return <button onClick={onClick}>Click me</button>;
});

const App = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);

  return (
    <div>
      <Button onClick={handleClick} />
      <p>Count: {count}</p>
    </div>
  );
};

export default App;

useRef

useRef는 컴포넌트 내부에서 렌더링이 일어나도 변경 가능한 상태 값을 저장합니다. 그러나 useState와 달리 값이 변해도 렌더링을 발생시키지 않습니다.

사용법

useRef는 반환값인 객체 내부의 current로 값에 접근하거나 변경할 수 있습니다.

예제

import React, { useRef } from 'react';

const FocusInput = () => {
  const inputRef = useRef(null);

  const handleFocus = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
};

export default FocusInput;

useContext

리액트 애플리케이션은 부모 컴포넌트와 자식 컴포넌트로 이루어진 트리 구조를 갖고 있습니다. 부모가 가진 데이터를 자식에서도 사용하려면 보통 props로 데이터를 전달합니다. 하지만 이렇게 하면 코드가 복잡해질 수 있습니다. 이를 해결하기 위해 등장한 개념이 context입니다.

사용법

useContext는 상위 컴포넌트에서 만들어진 context를 함수 컴포넌트에서 사용할 수 있도록 해줍니다. useContext를 사용하면 상위 컴포넌트 어딘가에서 선언된 <Context.Provider />에서 제공한 값을 사용할 수 있습니다.

예제

import React, { createContext, useContext, useState } from 'react';

const CountContext = createContext();

const Counter = () => {
  const count = useContext(CountContext);
  return <div>Count: {count}</div>;
};

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={count}>
      <Counter />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </CountContext.Provider>
  );
};

export default App;

이처럼 useMemo, useCallback, useRef, useContext는 각각의 목적에 맞게 사용하여 성능을 최적화하고 코드의 가독성을 높일 수 있습니다.

0개의 댓글