오늘 기술면접에서 "React hook에서 클로저의 역할과 어떻게 쓰이는지 설명해보세요" 라는 질문을 받았다.
나름 리액트를 공부했다고 생각했는데, 둘의 연관성은 한번도 생각해보지 않아서 당황했다ㅠㅠ
다음에 같은 질문이 온다면 대답을 해야지 하면서 글을 작성하고 있다.
React에서는 상태가 변경되면, 변경되었음을 컴포넌트에 알려주고, 변경된 상태를 반영해서 컴포넌트가 리렌더링된다.
React Hook에서는 useState라는 Hook을 통해 컴포넌트 내의 상태를 관리하는데, useState는 초기값(initialValue)를 받아서 [상태, 상태를 변경하는 함수] 형태의 배열을 반환한다. (비구조화 할당을 사용)
const [state, setState] = useState(initialValue)
함수형 컴포넌트에서 이전 상태와 현 상태의 변경이 있는지를 감지하기 위해서는 함수가 실행되었을 때 이전 상태에 대한 정보를 가지고 있어야 한다.
React는 이 과정에서 클로저를 사용한다.
useState를 실행했을 때 받아오는 값은 react 모듈 내부로 거슬러 올라가 찾을 수 있다.
node_modules/react/cjs/react.development.js 안으로 들어가보자.
useState는 intialState를 인자로 받는 함수로 선언되어 있다. 함수를 보면 resolveDispatcher라는 또다른 함수를 통해 리턴된 dispatcher의 useState 메소드에 initialState를 전달한 결과를 리턴하고 있다.
resolveDispatcher 함수로 타고 들어가보면, 다시 ReactCurrentDispatcher 안의 current 값을 리턴하고 있다.
ReactCurrentDispatcher까지 들어가본다.
그냥 전역에 선언된 current라는 값을 담은 변수이다.
다시 말해 useState는 외부에 선언된 상태값에 접근해서 이전 상태를 가져오고, 변경된 상태값을 관리하고 있다.
정리
함수형 컴포넌트도 결국 함수이기 때문에, 클로저를 통해 선언되는 시점에 접근 가능했던 외부 상태값에 계속 접근할 수 있는 것이다.
함수형 컴포넌트에서 상태값을 변경하면 외부의 값이 변경되고, 리렌더링(=함수 재호출)을 통해 새로운 값을 받아오게 된다.
리액트 홈페이지에서 보면 '컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장되어야 한다'는 내용이 있다.
위에서 설명한대로 컴포넌트의 상태값들은 컴포넌트를 키로 하는 배열에 순서대로 저장되기 때문에, hook을 조건문이나 일반 javascript 함수 안에서 사용하게 된다면, 맨 처음 함수가 실행되었을 때 저장되었던 순서와 맞지 않게 되어 잘못된 상태를 참조하게 될 수 있다. 그래서 반복문이나 조건문 안에서 Hook을 사용하려고 하면 아래와 같은 에러가 난다.
'React Hook은 컴포넌트가 렌더링 될 때마다 정확히 같은 순서대로 호출되어야 한다'는 뜻이다.
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/)