본문으로 건너뛰기

의존성 배열을 사용하는 훅

useEffect, useLayoutEffect, useMemo, useCallback은 모두 의존성 배열을 사용하지만 역할은 서로 다릅니다.

Hook주요 역할실행 또는 재생성 시점
useEffect렌더링 이후 Effect 실행의존성이 변경될 때
useLayoutEffect화면이 그려지기 전에 Effect 실행의존성이 변경될 때
useMemo계산된 값 기억의존성이 변경될 때 다시 계산
useCallback함수 기억의존성이 변경될 때 다시 생성

의존성 배열과 얕은 비교

의존성 배열에는 Effect나 메모이제이션 대상이 다시 실행될 조건을 작성합니다.

type DependencyList = ReadonlyArray<any>;

useEffect는 의존성 배열의 변경 여부를 얕은 비교로 판단합니다.

객체나 배열을 의존성으로 전달하면 내부 값이 같더라도 원하지 않는 Effect 실행이 반복될 수 있습니다.

const Label: React.FC<LabelProps> = ({ value }) => {
useEffect(() => {
// value.name과 value.id를 사용한다.
}, [value]);

return null;
};

이 경우 객체 전체보다 실제로 사용하는 값을 의존성 배열에 작성할 수 있습니다.

const Label: React.FC<LabelProps> = ({ value }) => {
const { id, name } = value;

useEffect(() => {
// name과 id를 사용한다.
}, [id, name]);

return null;
};

이 특징은 useMemouseCallback에도 동일하게 적용됩니다.

useEffect

useEffect는 컴포넌트가 렌더링된 이후 실행할 작업을 정의합니다.

function useEffect(
effect: EffectCallback,
deps?: DependencyList,
): void;

type EffectCallback = () => void | Destructor;

Effect 콜백은 아무것도 반환하지 않거나 cleanup 함수를 반환해야 합니다.

따라서 Promise를 반환하는 async 함수를 직접 전달할 수 없습니다.

useEffect(async () => {
await fetchData();
}, []);

cleanup 함수

cleanup 함수는 컴포넌트가 해제되거나 Effect가 다시 실행되기 전에 기존 작업을 정리합니다.

useEffect(() => {
// Effect 실행

return () => {
// cleanup 실행
};
}, [value]);
Effect 실행
→ 의존성 변경
→ 이전 cleanup 실행
→ 새로운 Effect 실행

경쟁 상태처럼 실행 순서나 타이밍에 따라 결과가 달라질 수 있는 상황에서도 기존 작업을 정리하는 데 사용할 수 있습니다.

useLayoutEffect

useLayoutEffectuseEffect와 역할은 비슷하지만 실행 시점이 다릅니다.

function useLayoutEffect(
effect: EffectCallback,
deps?: DependencyList,
): void;

useEffect는 화면 렌더링이 완료된 이후 실행되고, useLayoutEffect는 컴포넌트가 렌더링된 직후 브라우저가 화면을 그리기 전에 실행됩니다.

const [name, setName] = useState('');

useLayoutEffect(() => {
setName('배달이');
}, []);

return <div>{`안녕하세요, ${name}님!`}</div>;

화면이 그려지기 전에 상태 변경을 처리하므로 첫 렌더링의 빈 값이 잠깐 보이는 현상을 방지할 수 있습니다.

useMemo

useMemo는 이전에 계산한 값을 기억합니다.

function useMemo<T>(
factory: () => T,
deps: DependencyList | undefined,
): T;

의존성이 변경되지 않으면 기존 값을 반환하고, 변경되면 factory를 다시 실행해 새로운 값을 반환합니다.

useCallback

useCallback은 이전에 생성한 함수를 기억합니다.

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

의존성이 변경되지 않으면 기존 함수를 반환하고, 변경되면 새로운 함수를 반환합니다.

구분useMemouseCallback
기억하는 대상계산된 값함수
반환 값factory의 실행 결과전달한 함수

두 훅을 과도하게 사용한다고 컴포넌트의 성능 향상이 보장되는 것은 아닙니다.

정리

  • 의존성 배열은 값의 변경 여부를 얕은 비교로 판단합니다.
  • useEffect는 렌더링 이후 실행되며 cleanup 함수를 반환할 수 있습니다.
  • useLayoutEffect는 브라우저가 화면을 그리기 전에 실행됩니다.
  • useMemo는 계산된 값을 기억합니다.
  • useCallback은 함수를 기억합니다.

예상 질문

useEffect에 async 함수를 직접 전달할 수 없는 이유는 무엇인가요?

async 함수는 Promise를 반환하지만, Effect 콜백은 아무것도 반환하지 않거나 cleanup 함수를 반환해야 하기 때문입니다.

useEffect와 useLayoutEffect는 어떻게 다른가요?

useEffect는 화면 렌더링 이후 실행되고, useLayoutEffect는 브라우저가 화면을 그리기 전에 실행됩니다.

useMemo와 useCallback은 어떻게 다른가요?

useMemo는 계산된 값을 기억하고, useCallback은 함수를 기억합니다.