useState Hook과 클로저

ggong·2021년 5월 22일
30

전에 기술면접에서 "React hook에서 클로저가 어떻게 쓰이는지 설명해보세요" 라는 질문을 받은 적이 있다. 나름 리액트를 오래 썼다고 생각했는데, 클로저에 대해서도 방금 설명했는데!! 둘의 연관성은 한번도 생각해보지 않아서 당황했다. 그때의 북흐러움을 기억하면서 정리하는 useState의 원리와 클로저.

1. React Hook에서의 useState

React에서는 상태가 변경되면 변경되었음을 컴포넌트에 알려주고, 변경된 상태를 반영해서 컴포넌트가 리렌더링된다. 기존 클래스형 컴포넌트에서는 state로 컴포넌트 내부 상태를 정의하고 setState를 사용해 이 로직을 구현했다.

반면 React Hook에서는 useState라는 Hook을 통해 컴포넌트 내의 상태를 관리한다. useState는 초기값(initialValue)를 받아서 [상태, 상태를 변경하는 함수] 형태의 배열을 반환한다. 비구조화 할당을 통해 아래 형태로 사용한다.

const [state, setState] = useState(initialValue)

함수형 컴포넌트에서 이전 상태와 현 상태의 변경이 있는지를 감지하기 위해서는 함수가 실행되었을 때 이전 상태에 대한 정보를 가지고 있어야 한다. React는 이 과정에서 클로저를 사용한다.

2. useState의 작동 방식

useState를 실행했을 때 받아오는 값은 react 모듈 내부로 거슬러 올라가 찾을 수 있다.
node_modules/react/cjs/react.development.js 안으로 들어가보자.

useState는 intialState를 인자로 받는 함수로 선언되어 있다. 함수를 보면 resolveDispatcher라는 또다른 함수를 통해 리턴된 dispatcher의 useState 메소드에 initialState를 전달한 결과를 리턴하고 있다.

resolveDispatcher 함수로 타고 들어가보면, 다시 ReactCurrentDispatcher 안의 current 값을 리턴하고 있다.
ReactCurrentDispatcher까지 들어가본다.

그냥 전역에 선언된 current라는 값을 담은 변수이다.

다시 말해 useState는 외부에 선언된 상태값에 접근해서 이전 상태를 가져오고, 변경된 상태값을 관리하고 있다. 함수형 컴포넌트도 결국 함수이기 때문에, 클로저를 통해 선언되는 시점에 접근 가능했던 외부 상태값에 계속 접근할 수 있는 것이다. 함수형 컴포넌트에서 상태값을 변경하면 외부의 값이 변경되고, 리렌더링(=함수 재호출)을 통해 새로운 값을 받아오게 된다.

useState와 비슷한 역할을 하는 코드는 쉽게 구현할 수 있다.

let _value;

export useState(initialValue){
  if (_value === 'undefined') {
    _value = initialValue;
  }
  const setValue = newValue => {
    _value = newValue;
  }
  
  return [_value, setValue];
}

useState 밖에 선언된 변수 _value가 있다. useState() 함수에서는 초기값(initialValue)를 받아 만약 기존 _value 값이 없으면 초기값으로 세팅한다. setValue 함수는 받아오는 값으로 전역 _value를 업데이트한다. 그리고 _valuesetValue 함수를 배열 형태로 반환한다. useState() 함수가 어디에서 실행되었건, 클로저를 통해 _value 값에 접근할 수 있다.

이렇게 React hook에서는 useState를 통해 생성한 상태를 접근하고 유지하기 위해서 useState 바깥쪽에 state를 저장한다. 이 state들은 선언된 컴포넌트를 유일하게 구별할 수 있는 키로 접근할 수 있으며 배열 형식으로 저장된다. useState 안에서 선언되는 상태들은 이 배열에 순서대로 저장된다.

3. hook을 조건문 내에서 사용하면 안되는 이유

리액트 홈페이지에서 보면 '컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장되어야 한다'는 내용이 있다.

위에서 설명한대로 컴포넌트의 상태값들은 컴포넌트를 키로 하는 배열에 순서대로 저장되기 때문에, hook을 조건문이나 일반 javascript 함수 안에서 사용하게 된다면, 맨 처음 함수가 실행되었을 때 저장되었던 순서와 맞지 않게 되어 잘못된 상태를 참조하게 될 수 있다. 그래서 반복문이나 조건문 안에서 Hook을 사용하려고 하면 아래와 같은 에러가 난다.

'React Hook은 컴포넌트가 렌더링 될 때마다 정확히 같은 순서대로 호출되어야 한다'는 뜻이다.

4. 정리

React에서 함수형 컴포넌트의 상태관리를 위해서는 컴포넌트 외부에 저장된 값을 사용하며, 클로저를 통해 해당 값에 접근해 상태를 비교하고 변경한다. useState는 컴포넌트 내부에서 값을 변경시키는 것이 아니라, 외부에 있는 값을 변경시키기 때문에 상태가 변경된 직후 컴포넌트가 가진 값은 이전의 값을 그대로 참조한다. 각 컴포넌트의 상태 정보는 배열 형태로 저장되기 때문에 상태를 변화시키는 hook을 조건문이나 반복문 안에서 사용하면 잘못된 순서의 값을 참조하게 될 수 있다.



참고 :
[React] 클로저와 useState Hooks
(https://yeoulcoding.tistory.com/149#recentEntries)
React 톺아보기 - 03. Hooks_1
(https://goidle.github.io/react/in-depth-react-hooks_1/)

profile
파닥파닥 FE 개발자의 기록용 블로그

1개의 댓글

comment-user-thumbnail
2021년 12월 24일

오 저도 대답못했었는데 ㅋㅋㅋㅋㅋ 감사합니다

답글 달기