STUDY/[ React ]

왜 리액트는 Hook을 순서 기반으로 설계했는가?

Lim임 2025. 11. 27. 02:56

먼저: React는 “트리 + 재렌더링” 구조

React는 컴포넌트를 일종의 함수(=렌더링 함수) 로 보고,
"함수를 다시 실행해서 UI를 만들고 diffing하는 방식"으로 동작해.

즉:

function Component(props) {
  // 여기서 계산된 결과가 렌더링
}

 

컴포넌트가 재렌더링되면
전체 함수가 다시 실행됨.

그러면 문제가 생김:

"상태(state)를 어디에 저장하지?"
"함수를 다시 실행했으니까 내부 변수는 전부 초기화되는데?"

 

그래서 React는 다음 아이디어를 사용함:

컴포넌트를 렌더링할 때 ‘그 컴포넌트 인스턴스 전용 저장공간’을 따로 만든다.

(이걸 React Fiber 노드라고 해)

여기에 Hook 데이터들이 순서대로 저장됨.


핵심: React는 Hook을 “순서 기반 배열”처럼 저장한다

React는 각 컴포넌트 렌더링마다 Hook을 아래처럼 관리함:

0: useState #1 값
1: useState #2 값
2: useEffect #1 값
3: useMemo #1 값
...

 

"훅을 호출한 순서 = 내부 저장 배열의 인덱스" 인 셈.

이게 되는 이유는 React가 훅을 이렇게 생각:

  • useState → 상태 하나 저장
  • useEffect → effect 하나 저장
  • useMemo → 메모된 값 하나 저장

이걸 어떤 이름이나 식별자로 저장하는 것이 아니라
그냥 “몇 번째 Hook인지”로 구분함.


왜 이렇게 단순한 구조를 선택했을까?

1. 리렌더링이 매우 빠르고 단순해지기 때문

 

React는 컴포넌트가 재렌더링되면 다시 실행되는데,
Hook을 “순서대로 저장”하면 다음처럼 처리됨:

렌더 시작 → Hook index = 0
useState 호출 → stored[0] 반환
useState 호출 → stored[1] 반환
useEffect 호출 → stored[2] 반환
...
렌더 끝

 

순서 유지 = 매우 빠르고 단순.

만약 Hook에 ID를 붙이려 했다면?

  • 각 Hook마다 고유한 키 필요
  • 렌더링마다 ID 매칭해야 함
  • DOM diff처럼 복잡한 알고리즘 필요
  • 성능 뚝 ↓

React 팀은 이걸 거부했음.

“함수형 컴포넌트는 빠르고 단순해야 한다.”

그래서 Hook은 “순서 = identity” 모델로 고정됨.


2. 함수형 컴포넌트가 “상태없는 함수”처럼 보이도록 하기 위해

원래 React 컴포넌트는 이렇게 단순했음:

function Component() {
  return <div />;
}

 

여기에 상태를 추가해야 하는데 클래스처럼 복잡해지면 안 됨.

그래서 React 팀은 생각했지:

“상태를 컴포넌트 밖에서 관리하고,
컴포넌트는 그 상태를 순서대로 사용할 수만 있으면 되지 않을까?”

그래서 나온 구조:

  • 상태 저장은 React가 관리한다
  • 컴포넌트는 단지 “첫 번째 상태”, “두 번째 상태”를 읽을 뿐

너무 단순해서 라이프사이클이나 클래스보다 훨씬 쉽고 예측 가능.


3. 훅 호출 규칙을 강제하여 “예측 가능한 UI” 보장

React는 매 렌더링마다 Hook 호출 순서가 같다는 걸 보장해야

  • 이전 상태 매칭
  • 이전 effect cleanup
  • memo 값 재사용
  • callback identity 일치

같은 걸 전부 안정적으로 수행할 수 있음.

여기서 조금만 순서가 바뀌면?

useState #1 → 갑자기 effect 저장 칸을 참조함
useMemo #1 → 이전 state 값을 참조함

 

완전히 망가져버림.

그래서 React는 규칙을 강제함:

  • ❌ 조건문 안 hook
  • ❌ 반복문 안 hook
  • ❌ 일반 함수 안 hook

이게 전부 “순서가 달라질 수 있는 상황”이기 때문.


4. Fiber 구조가 Hook을 순서 기반으로 저장하도록 설계됨

React의 Fiber 노드는 이렇게 생김:

Fiber {
  memoizedState: Hook Linked List,
  updateQueue: ...
}

React는 훅을 연결 리스트처럼 저장함:

Hook #1 → Hook #2 → Hook #3 → ...

렌더링 때마다 React는 훅을 하나씩 따라가면서:

  • useState면 상태 읽기
  • useEffect면 effect 저장
  • useMemo면 캐싱 확인

이걸 한다.

즉 디테일한 구조는:

  • 각 Hook은 “다음 Hook”을 가리키는 링크드 리스트 구조
  • React는 렌더링할 때마다 첫 번째 Hook부터 순서대로 traverse

그래서 순서가 어긋나면 바로 터짐.

 


 

React가 Hook을 순서 기반으로 만든 이유는:

  1. 렌더링이 빠르고 단순해지기 때문에
    (ID 매칭 같은 복잡한 로직 필요 없음)
  2. 함수형 컴포넌트를 간단하게 유지하기 위해서
  3. Hook 상태/이펙트/메모 값이 정확하게 대응되기 위해서
  4. Fiber 구조가 Hook을 링크드 리스트로 “순서대로” 저장하기 때문
  5. 조건문/반복문이 Hook 순서를 바꾸면 React 전체 흐름이 깨지기 때문