useCallback과 useMemo의 차이를 아시나요? - 제대로 답하자 면접질문

타락한스벨트전도사·2024년 10월 4일
35

리액트 훅을 얼마나 잘 이해하고 계신가요? 이번 포스트에서는 useRef와 useReducer를 통해 리액트의 내부 동작을 좀 더 깊이 들여다보고자 합니다. 면접 질문에서도 자주 나오는 useCallback과 useMemo의 차이점을 짚어보며, 리액트의 숨겨진 비밀을 함께 파헤쳐볼까요? 🤔

useCallback과 useMemo의 차이

프론트엔드 개발자라면 한 번쯤 들어본 질문이죠: "useCallback과 useMemo의 차이는 무엇일까요?" 저는 면접 중에 이런 질문을 여러 번 받아봤어요. 😅 보통 "useCallback은 함수를 기억하고, useMemo는 값을 기억한다"고 간단히 대답할 수 있지만, 실상 이 둘의 차이는 그렇게 크지 않답니다. 왜냐하면 자바스크립트의 일급 함수 특성 덕분에 함수 역시 값으로 취급되기 때문이죠.

useMemo(() => {
  return 원하는 함수;
}, [deps]);

위 코드처럼 useMemo로 함수를 반환하면 사실상 useCallback과 큰 차이가 없어요. 이런 점에서 면접관이라면 이렇게 질문할 수도 있을 것 같아요: "useReducer로 useState를 구현해보세요" 또는 "useRef로 useMemo를 구현해보세요"라고요. 이런 질문이 의미가 있는 이유는, 실제 리액트 내부의 구현 방식이 이와 크게 다르지 않기 때문이에요! 😮

리액트 훅의 진짜 모습

모든 리액트 훅 - useRef, useMemo, useState - 이들은 결국 리액트의 내부 상태인 fiber에 붙어 있는 hook.memoizedState를 반환하는 과정일 뿐입니다. setStatedispatch 역시 리액트의 reconciler가 반환하는 형태에 지나지 않아요. 이런 구조를 이해하면, 훅의 동작 방식을 더욱 명확하게 이해할 수 있습니다.

저 역시 리액트를 처음 배웠을 때는 useRef가 특별한 훅이라고 생각했어요. 마치 특정 DOM에 직접 접근하기 위한 용도로만 사용되는 듯 보였죠. 하지만 알고 보니, 이 훅 역시 그저 값을 저장하는 역할을 하는 도구였다는 사실에 놀랐습니다. 🤯

import { useReducer, type SetStateAction } from 'react'

function useState<S>(initialState: S | (() => S)): [S, (action: SetStateAction<S>) => void] {
  const [state, dispatch] = useReducer(
    (state: S, action: SetStateAction<S>): S => 
      typeof action === 'function' ? (action as (prevState: S) => S)(state) : action,
    typeof initialState === 'function' ? (initialState as () => S)() : initialState
  );
  
  return [state, dispatch];
}

import { useRef } from 'react';

function useMemo<T>(factory: () => T, deps: React.DependencyList): T {
  const ref = useRef<{ value: T; deps: React.DependencyList | undefined }>({
    value: undefined as T,
    deps: undefined,
  });

  if (!ref.current.deps || !shallowEqual(deps, ref.current.deps)) {
    ref.current.value = factory();
    ref.current.deps = deps;
  }

  return ref.current.value;
}

function useCallback<T extends (...args: any[]) => any>(
  callback: T,
  deps: React.DependencyList
): T {
  return useMemo(() => callback, deps);
}

function shallowEqual(a: React.DependencyList, b: React.DependencyList) {
  return a.length === b.length && a.every((dep, i) => Object.is(dep, b[i]));
}

shallow와 deep의 차이

useCallback과 useMemo의 의존성 배열(deps)을 비교할 때는 shallow 비교만 수행된다는 사실을 알고 계셨나요? 만약 이를 deepEqual로 바꿔 비교한다면, 어떨까요? 그렇다면 useDeepMemo 같은 새로운 훅을 만들 수 있을 겁니다. 🚀 이를 통해 더 깊은 수준의 동등성 검사를 사용해 최적화를 도모할 수 있겠죠.

나도 리액트를 만들 수 있을까?

리액트의 원리를 이해하는 것은 어렵게 느껴질 수 있지만, 실제로 이를 간단하게 구현해볼 수 있는 기회도 많습니다. 리액트를 직접 만들어보는 것은 리액트의 내부 동작을 이해하는 데 큰 도움이 됩니다. 최근에 재미있게 읽은 몇 가지 포스트들을 공유합니다:

또한, 유튜브 채널 '가장 쉬운 웹개발'의 '리액트 까보기' 시리즈도 리액트의 동작을 이해하는 데 큰 도움이 됩니다. 저 역시 이 시리즈를 통해 리액트를 처음 접했답니다! 😊

리액트를 깊이 이해하는 즐거움

리액트의 내부 동작을 이해하면, 더 효율적이고 강력한 코드를 작성할 수 있습니다. 여러분은 어떻게 생각하시나요? 리액트의 어떤 부분이 가장 흥미롭나요? 댓글로 여러분의 생각을 들려주세요! 💬

리액트 훅과 그 내부 동작에 대해 더 알아가면서, 복잡해 보이는 질문도 점점 익숙해지고, 코드 역시 한층 더 자신감 있게 작성할 수 있을 거예요. #React #FrontendDevelopment #JavaScriptTips #CodingInterview #ReactHooks

profile
기부하면 코드 드려요

1개의 댓글

comment-user-thumbnail
2025년 8월 18일

Holy Banya offers an impressive range of Sauna Whisks crafted from natural birch, oak, and linden branches. Each whisk is handmade, preserving traditional banya practices while boosting circulation, detoxifying skin, and soothing the mind. The website is easy to navigate, shipping is fast across the US, and customer support is very responsive.

답글 달기