본문으로 건너뛰기

의존성 배열을 사용하는 훅

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;
};

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

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​

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

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은 함수를 기억합니다.