React 17에서 이벤트 위임 변경사항과 마이그레이션

문제 상황

레거시 jQuery 기반 어드민과 React 기반 대시보드가 공존하는 프로젝트를 진행 중이었다. React 17로 업그레이드 후 일부 모달에서 외부 클릭 감지가 제대로 동작하지 않는 버그가 발생했다.

원인 분석

React 16까지는 모든 이벤트를 document에 위임했다. React 17부터는 ReactDOM.render가 호출된 root DOM 노드에 이벤트를 위임하도록 변경되었다.

// React 16
<div onClick={handler}> // 실제론 document.addEventListener

// React 17
const root = document.getElementById('root');
ReactDOM.render(<App />, root); // root.addEventListener

이 변경으로 여러 React 버전이 공존하거나, jQuery 등 다른 라이브러리와 함께 사용할 때 이벤트 충돌이 줄어들었다.

해결 방법

외부 클릭 감지 로직에서 e.stopPropagation()을 호출하던 부분을 수정했다. React 17부터는 이벤트가 실제 DOM 트리를 따라 전파되므로, 네이티브 이벤트와의 상호작용을 고려해야 했다.

const useOutsideClick = (ref, handler) => {
  useEffect(() => {
    const listener = (e) => {
      if (!ref.current || ref.current.contains(e.target)) {
        return;
      }
      handler(e);
    };
    
    // React root가 아닌 document에 직접 등록
    document.addEventListener('mousedown', listener);
    return () => document.removeEventListener('mousedown', listener);
  }, [ref, handler]);
};

마이그레이션 팁

  • 대부분의 경우 코드 변경 없이 동작한다
  • e.stopPropagation() 사용처는 검토 필요
  • 레거시 코드와 혼용 시 이벤트 전파 테스트 필수
  • onScroll, onFocus 등은 여전히 버블링되지 않음

React 18 RC가 나온 지금, 17로의 전환은 좋은 중간 단계였다.

React 17에서 이벤트 위임 변경사항과 마이그레이션