두 번째 규칙: 함수형 컴포넌트에서만 호출하라

342025년 09월 18일4

첫 번째 규칙이 훅의 ‘언제(When)’와 ‘어떻게(How)’를 정의했다면, 두 번째 규칙은 훅의 ‘어디서(Where)’를 정의해야 했다.

댄은 화이트보드에 두 번째 규칙을 적었다.

규칙 2: 오직 리액트 함수형 컴포넌트(React Function Components) 내에서만 훅을 호출해야 합니다.

그는 잠시 말을 멈추고, 이 규칙의 의미를 명확히 하기 위해 부연 설명을 덧붙였다.
“물론, 우리가 만든 커스텀 훅 안에서도 훅을 호출할 수 있습니다. 즉, 훅을 호출할 수 있는 곳은 단 두 군데뿐입니다. 리액트 함수형 컴포넌트와, 다른 커스텀 훅.”

이 규칙은 얼핏 보면 너무나 당연해 보였다.
훅은 컴포넌트의 상태와 생명주기를 다루기 위해 만들어졌으니, 당연히 컴포넌트 안에서 사용해야 하지 않겠는가?

하지만 소피는 이 규칙이 왜 필요한지, 그 기술적인 배경을 명확히 할 필요가 있다고 생각했다.
“만약 일반 자바스크립트 함수에서 useState를 호출하면 정확히 어떤 일이 일어나죠? 개발자들이 그 이유를 알아야 합니다.”

그녀의 질문에, 댄은 useState의 내부 구현 로직을 다시 상기시켰다.

// --- 가상의 React Core ---
let currentlyRenderingComponent = null; // 현재 렌더링 중인 컴포넌트를 가리키는 포인터

function useState(initialValue) {
  // 훅이 호출되는 순간, 리액트는 '현재 렌더링 중인 컴포넌트'가
  // 누구인지 알아야만 한다.
  if (currentlyRenderingComponent === null) {
    throw new Error("훅은 리액트 함수 컴포넌트 내에서만 호출되어야 합니다.");
  }
  
  // 'currentlyRenderingComponent'의 상태 저장소에 접근한다.
  const componentState = getStateFor(currentlyRenderingComponent);
  // ... (상태를 읽고, 업데이트 함수를 반환하는 로직)
}

“보시다시피, 훅이 제대로 작동하려면 전제 조건이 있습니다.”
댄이 설명했다.
“리액트는 useState가 호출되는 바로 그 순간, ‘지금 어떤 컴포넌트를 위해 이 상태를 관리해야 하는가?’를 알아야만 합니다. 우리는 currentlyRenderingComponent라는 내부 포인터를 통해 이 정보를 관리하죠.”

리액트가 함수형 컴포넌트의 렌더링을 시작할 때, 이 포인터는 해당 컴포넌트를 가리키도록 설정된다. 그리고 렌더링이 끝나면 다시 null로 초기화된다.
훅은 오직 이 포인터가 특정 컴포넌트를 가리키고 있는, 짧은 렌더링 시간 동안에만 호출될 수 있었다. 이 시간적, 공간적 맥락을 ‘렌더링 컨텍스트’라고 불렀다.

“만약 개발자가 일반 자바스크립트 함수에서 useState를 호출한다면,”
댄이 말을 이었다.
“그때 currentlyRenderingComponent 포인터는 null일 겁니다. 리액트는 이 상태가 누구의 것인지 알 길이 없으니, 에러를 던질 수밖에 없습니다.”

잘못된 코드:

// 일반 자바스크립트 함수
function utilityFunction() {
  // ERROR: 리액트 렌더링 컨텍스트 밖에서 훅을 호출!
  const [value, setValue] = useState(0); 
}

이 설명으로 모든 것이 명확해졌다.
두 번째 규칙은 임의적인 제약이 아니었다. 훅이 자신의 ‘주인’이 누구인지를 알기 위한, 지극히 기술적이고 필연적인 요구사항이었다.

이 두 개의 규칙이 확립되면서, 훅의 세계에는 질서가 잡혔다.

  1. 최상위에서만 호출하라: 상태 배열의 인덱스를 보존하기 위해.
  2. 함수형 컴포넌트나 커스텀 훅에서만 호출하라: 상태가 어떤 컴포넌트에 속하는지 리액트가 알 수 있게 하기 위해.

이 두 개의 기둥은 훅이라는 새로운 구조물을 안전하게 지탱했다.
이제 개발자들은 이 두 규칙만 지킨다면, 훅의 강력한 힘을 마음껏 활용할 수 있었다.

하지만 팀은 여기서 멈추지 않았다.
규칙을 만드는 것만으로는 부족했다. 인간은 실수를 하는 존재라는 것을, 그들은 너무나도 잘 알고 있었다. 이제 그 실수를 미연에 방지해 줄 파수꾼을 만들 차례였다.