클로저는 자바스크립트에서 기본이 되는 아주 중요한 개념이다.

기술 면접에서 1순위로 자주 물어보는 개념이기 때문에, 나도 기술 면접을 준비하며 외웠던 기억이 난다.

하지만, 실무에서 클로저가 어떻게 쓰이는지 잘 이해하지 못했고, 이를 실제로 어떻게 응용해야 할지 고민하던 시기가 있었다. 이번에 클로저 개념을 더 심도 있게 공부해보려 한다.

문제

customHook에 함수를 파라미터로 넘겨줄 때, useCustom 컴포넌트가 언마운트되는 상황에서 해당 함수가 실행될 때, 함수 내부의 state 값이 어떻게 출력되는지 살펴보자.

import useCustom from './hooks/useCustom'
import { useState } from 'react'

export default function Home() {
  const [state, setState] = useState(0)

  useCustom(() => console.log('inner func state:', state))

  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <div>{state}</div>
      <button onClick={() => setState(state + 1)}>+</button>
    </div>
  )
}
import React, { useEffect } from 'react'

const useCustom = (func: Function, state: any) => {
  console.log('params state:', state)
  useEffect(() => {
    return () => func()
  }, [])

  return <div>useCustom</div>
}

export default useCustom

코드 설명

  1. 초기 렌더링 시 state는 0으로 설정된다.
  2. useCustom 훅 내부의 console.log에 출력되는 state 값은 초기 값인 0이다.
  3. 이때 useCustom에서 넘겨준 func 함수도 클로저로 인해 state가 0인 상태를 기억하게 된다. 이후 useCustom 컴포넌트가 언마운트될 때 해당 함수가 실행되지만, 그때의 state는 0으로 남아 있다.
  4. 의존성 배열이 빈 배열이기 때문에 useEffect는 한 번만 실행되며, state가 바뀌더라도 재실행되지 않는다.
  5. 만약 최신 state 값을 사용하고 싶다면, useEffect의 의존성 배열에 state를 추가해 주어야 한다. 그렇지 않으면 클로저로 인해 이전 상태를 참조하게 된다.