본문 바로가기
WEB/깊게 공부하기

React가 이벤트를 관리하는 방식 - 2

by IT황구 2023. 6. 18.
728x90
반응형

https://rbals0445.tistory.com/138

 

React가 이벤트를 관리하는 방식 - 1

올해가 가기 전 리액트를 소스코드 레벨에서 분석해보는게 목표였습니다. 목표를 간단하게 잡고 분석을 해보자 했지만, 생각보다 모르는게 너무 많더라구요. ㅎㅎ 거의 2~3주 동안 시간이 나면

rbals0445.tistory.com

 

React에서 사전에 어떻게 native event와 매핑이 되고, 실제로 등륵은 어떻게 이루어지는지 알아보았습니다.

 

이번 글에서는 어떻게 이벤트가 실행되는지 알아보겠습니다.

 

화면에 모든 컴포넌트들이 렌더링 되고, 특정 엘리먼트를 클릭하면 이벤트는 어떻게 실행 될까요??

 

 

이벤트 실행시키기

 

 

 

item 1 을 클릭해서 'click' event를 발생시킨 경우를 예로 들어보겠습니다. 코드는 아래와 같습니다.

function MyButton() {
  const onClick = (e, test) => {
    console.log(e, test);
  };

  return (
    <ul>
      <li onClick={onClick}>Item 1</li>
    </ul>
  );
}

 

 

확인해야 할 포인트는 2가지 입니다.

  • 1편에서 다뤘던 target.addEventListner('click', listener) 를 등록했을때 'click'에 해당하는 listener는 discreteEvent 입니다.
  • 엘리먼트에서 어떤 함수를 실행시킬지? 를 찾는 함수는 getListener 함수입니다.

 

 

  • 엘리먼트를 클릭해서 dispatchDiscreteEvent가 호출 -> getListener 를 통해 어떤 함수를 실행시킬지 선택하는 과정은 Chrome Debugger의 Call stack 을 통해서 확인할 수 있습니다. 

 

 

  • dispatchDiscreteEvent -> dispatchEvent -> .... -> getListener 까지 이동한것을 확인할 수 있습니다.
  • 제가 1편에서 미리 써뒀던 내용입니다. 결국 dispatchEvent는 항상 불리게 되어있습니다.

 

 

getListener 를 보기에 앞서 React는 내부적으로 속성을 정의해서 여러 값들을 기억하고 있습니다. 평소에 엘리먼트에 넘기는 props도 그 중 하나입니다.

 

 

getListener는 다음과 같습니다.

 

 

  • click 이벤트에 대한 listener를 찾는 과정입니다. 사진상 1번 부분의 props는 위의 사진에 있었던 internalProps 에서 가져왔습니다.
  • 사진의 2번 에서는 props에서 registrationName(onClick) 을 통해서 onClick에 등록되어 있는 함수만 가져왔습니다.
    • 여기서 의문이 생깁니다. 'click' 이벤트에 대한 응답인데 언제 onClick 으로 변경된것이지?

 

 

  • topLevelEventsToReactNames 는 1편에서 react-dom.js 가 실행되자마자 사전에 매핑해두었던 Map 입니다. 이것을 이용합니다.
  • extractEventsdispatchEvent -> getListeners 사이에 호출되는 함수입니다. 그 사이의 함수에서 변환되는것을 알 수 있습니다. 

 

이제 onClick에 달아뒀던 함수를 listener 라는 변수에 담아서 리턴하게 됩니다.

 

반환받은 listener는 바로 실행시키지 않습니다. dispatchQueue 라는곳에 담고, nativeEvent를 React 전용 Synthetic Event로 변환해주는 과정이 남아있습니다.

 

  • 합성 이벤트를 통해 모든 브라우저에서 동일한 인터페이스를 보장해줍니다. (firefox, IE에 문제가 많은데, 합성 이벤트는 이것들도 해결해줍니다)

 

 

함수 획득 -> 합성 이벤트 생성 -> dispatchQueue 에 push 

  • 위의 과정을 모두 거친 후 processDispatch -> .... -> executeDispatch -> invokeGuardedCallback~~ 의 함수를 거쳐 최종적으로 실행되게 됩니다.
  • 위의 과정 또한 Chrome Debugger를 통해 검증할 수 있습니다. 

 

실행하기 전 executeDispatch 를 잠시 확인해야 합니다.

 

 

이 함수에서 보내는 인자 4가지를 확인해보면, 어떤 이벤트, 어떤함수 그리고 '합성 이벤트' 를 보내는것을 확인할 수 있습니다.

 

이제 실제로 실행시키는 코드를 확인하겠습니다.

 

 

Call Stack에 있는 함수는 Dev 함수라서 이름은 다르지만, 결국 왼쪽처럼 func.apply(context, funcArgs) 를 통해서 함수를 실행시킵니다.

 

이때 func는 getListner에서 가져온 listener(함수) 입니다.

그리고 funcArgs 를 주목해야 합니다. 여기서 slice를 통해서 (SyntheticEvent) 만 남게되고, 이것이 func의 인자로 넘어가게 됩니다.

 

즉, onClick에 e를 자연스럽게 이곳에서 바인딩 해주고 있다는것을 알 수 있습니다.

 

 

Production code에서 확인해보았습니다. funcArgs 에 해당하는 'm' 안에 nativeEvent가 아닌 SyntheticEvent 가 들어가있는것을 확인할 수 있었습니다.

 

 

이제 위의 코드에서 왜 'e'를 출력하면 합성이벤트가 나오고, test를 출력하면 undefined 가 나오는지 알 수 있게되었습니다.

e만 바인딩을 해주고 있었기 때문입니다.

 

여기까지가 이벤트 실행의 끝 입니다. func.apply 를 마치면 이벤트 핸들러가 작동됩니다. 

 

 

정리

 

이렇게 React가 이벤트를 사전매핑 -> 등록 -> 실행까지 하는 과정을 모두 살펴보았습니다.

합성 이벤트가 이벤트가 실행되기 직전에 만들어져서 들어간다는것도 확인할 수 있었습니다.

 

리액트는 이벤트 버블링을 이용해서 실제로 엘리먼트의 이벤트 핸들러들을 모두 등록하지 않고도, root level에서 처리하는것을 볼 수 있었습니다.

 

따라서 우리가 이벤트 위임으로 처리해야하나? 고민하지 않더라도, 이미 React에서 버블링을 통해서 효율적으로 처리하는것을 알 수 있었습니다.

 

하지만, 누군가는 아직도 이벤트 버블링 내가 하면 더 좋은거 아닌가? prop도 따로 안넘겨도 될텐데.. 굳이? 라는 생각을 가진 분들이 계실겁니다.

 

이러한 내용에 대해서 안된다! 라고 할 수 있는 이유들에 대해 적어보도록 하겠습니다. 

 

감사합니다.

 

 

 

 

 

728x90
반응형