TIL 231102 - useState Hook 파헤치기

송용승·2023년 11월 2일
3

TIL

목록 보기
10/29
post-thumbnail

Today I Learned

이번주에 들어 React 기초 수업을 듣고 있다. 분명 전에 만져보고 나름대로 미니 프로젝트도 한 적이 있는데 왜이리 생경한건지 모를 일이지만, 그래도 아주 낯설지는 않아서 쏙쏙 흡수하며 배워가고 있다.

그래서 그런걸까? 더 깊이 보고싶어졌다. 특히 useState 훅을.

오늘 포스팅은 useState Hook을 파헤치려다 약간의 힌트만 얻고 일단 포기한 경험을 다룹니다.

배경

윗 단락에서 이야기했듯, 최근 리액트 강의를 듣고 있다. 당연한 수순으로 JSX 문법을 배우고 props 전달을 거쳐 명령형 프로그래밍, 생명주기를 살짝 맛보고 지금은 useState 사용법을 배우고 있는 동시에 챌린지반 두번째 강의를 들었는데, 어제의 포스팅 주제인 패키지 매니저에 익숙해지기 위해 직접 패키지를 만들어서 npm에 배포해보는 내용이었다. 수업을 마무리지으며 튜터님께서 말씀하시길 -

"여러분 오늘 수업은 npm, yarn과 패키지 매니저에 친숙해져보기 위한거고 너무 깊게 파고들지 마세요. 여기 node_modules 디렉토리는 그냥 쳐다보지를 마세요."

- 라고 하셨다. 강의가 끝나고 자리로 돌아와 오늘 공부한 내용들을 정리해보는데 useState 가 계속 눈에 밟혔고, 어차피 포스팅 할 내용인데 조금 알아볼까 하다가 어느덧

이런 화면을 보고있는 나를 발견했다. 접근을 금하신 node_modules 내의 react 폴더를 열어 useState 를 검색해본 것이다. 이제 저 파일들을 한참을 들여다 본 결과를 하나씩 나열해보자. 아마 내공이 좀 있으신 분들에게는 그저 당연한 이야기일지도 모른다.

useState는 함수다

당연한 이야기인지도 모르겠지만, 발밑만 보고 걷는 사람은 본인이 어느 길을 가고있는지 모른다. JS를 익힌지도 얼마 되지 않아 React를 들이마시고 있는 나의 입장에서는 useState Hook 이라는 게 그냥 쓰라니까 쓰는 거고 리렌더링을 일으킬 수 있는 도구일 뿐이다. 하지만 물론, useState 는 함수다.

내 CRA 프로젝트 디렉토리 안에서 useState 를 사용하는 부분에 커서를 가져다대고 option + click 하면 index.d.ts 파일이 열리고 그 안에서 useState 의 함수 정의부를 볼 수 있다. 내용은 아래와 같다.

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>]

위의 코드는 TypeScript 로 작성된 useState 의 설명이다. 이걸 내가 아는 선에서 해석해보면...

  1. useState 는 제네릭 함수로, 하나의 타입 매개변수 S를 받으며, 이는 useState를 사용하여 관리하려는 state 변수의 타입을 나타낸다.

  2. 이 제네릭 함수는 파라미터 한 개로 initialState 를 받으며, 이 파라미터는 S 타입이거나 S 를 리턴하는 함수이고 이를 통해 state 의 초기값을 제공한다. 이 때 사용자는 직접 S 타입의 초기값을 지정하거나 초기값을 반환하는 함수를 제공할 수 있다.

  3. 이 함수는 두 개의 요소를 가진 배열을 반환하며, 이게 바로 일반적인 용례의 [state, setState] 에 구조 분해 할당되는 배열이다. 배열의 첫 번째 요소는 S 타입으로 현재 상태 값을 나타내고 두 번째 요소는 Dispatch<SetStateAction<S>> 타입으로 상태 값을 업데이트 하는 데에 사용되는 함수이다.

음... 대부분 우리가 일반적으로 useState 를 처음 접할때 배워 아는 내용이다. 다만, 저 Dispatch<SetStateAction<S>> 라는 부분이 뭔지 당최 알 수가 없다. dispatch는 보내다, 파견하다 같은 뜻의 단어인데... 'state 를 변경하는 함수를 React의 어느 내부 로직으로부터 보내온다는 이야기' 정도로밖에 추측이 안 된다.

dispatch 라는 알 수 없는 것은 node_modules/react/cjs 디렉토리 내부의 react.development.js 파일 안에서도 찾아볼 수 있다.

function useState(initialState) {
  var dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}
// ...
function resolveDispatcher() {
  var dispatcher = ReactCurrentDispatcher.current;

  {
    if (dispatcher === null) {
      error('error message');
    }
  } 
  return dispatcher;
}
// ...
var ReactCurrentDispatcher = {
    /**
     * @internal
     * @type {ReactComponent}
     */
  current: null
};

여기서는 dispatcher 라고 불리는데, react.development.js 파일 내부에서 65번이나 등장한다. 지금으로서는 이 객체로 추정되는 dispatcher

  1. Object다.
  2. 겉에 드러나지 않는, React 내부에서 동작하는 존재일 것이다.
  3. React에서 제공하는 여러 hook들에서 아주 아주 중요한 역할을 할 것이다. (65번이나!!!)

일 것으로 생각된다. 자세한 건 더 찾아보거나, 튜터님께 여쭤보는 것이 좋겠다...

useState 와 클로저

강의를 듣고, useState 를 이래저래 사용해보다가 문득 이상한 점이 느껴졌다. useState 를 사용하여 받아오는 state 변경 함수(이하 setState)는 어떻게 항상 state 값을 알고 있지? 내가 setState 에게 제공하는 건 state어떻게 바꾸는지에 대한 방식(또는 함수) 뿐인데?

방금의 이상한 점을 조금 더 풀어서 이야기 하면 다음과 같다.

useState 함수가 반환하는 setState 는 그 함수 내부에서 state 를 주어진 방식에 맞게 변경하고 실행 컨텍스트 스택에서 사라질텐데, 다음 이 함수를 호출할 때에는 어떻게 state 의 값을 알고 변경하는걸까?

React 도 결국 자바스크립트 라이브러리고, 자바스크립트 엔진 안에서(?) 동작한다. 실행 컨텍스트 스택에서 사라진 후에도 값을 참조하는 것은 클로저로 구현가능한 것인데, useState 는 클로저를 이용하는 건가? 그렇다면 React 는 어떤 맥락 하에서 동작하는건가? ....
...
...
여기서 내 뇌가 활동을 멈췄다. 지금 내가 아는 것으로는 더이상 설명할 수가 없다. 그렇기 때문에 포스팅의 서두에 '일단'포기한 경험이라고 한 것이다. 어쨌든 오늘 알게된 내용을 정리해보면 아래와 같다.

React hook들은 dispatcher 라는 내부적으로 중요한 무언가와 모두가 깊게 얽혀있고, 몇 번이고 호출되는 setState 함수는 클로저를 이용해 state 의 값을 알 수 있는 것이다.

dispatcher 나 리액트 훅과 클로저에 대해 더 알게되면 후속 포스팅을 하겠다. 오늘은 이만 끝!

profile
웹 프론트엔드 개발을 익히고 있습니다.

0개의 댓글