React 17 업그레이드 후 이벤트 위임 변경 이슈
문제 상황
레거시 jQuery 코드와 React가 혼재된 프로젝트를 React 17로 업그레이드했다. 배포 후 특정 모달에서 클릭 이벤트가 제대로 동작하지 않는 버그 리포트가 들어왔다.
원인 분석
React 16까지는 모든 이벤트를 document에 위임했지만, React 17부터는 ReactDOM.render가 호출된 root DOM 노드에 위임한다.
기존 코드에서 jQuery로 document에 직접 등록한 이벤트 리스너가 있었고, e.stopPropagation()으로 이벤트 전파를 막고 있었다.
// legacy.js
$(document).on('click', '.modal-overlay', function(e) {
e.stopPropagation();
closeModal();
});
React 16에서는 두 리스너 모두 document에 있어서 순서대로 실행됐지만, React 17에서는 root 노드에서 먼저 처리되고 document까지 버블링되면서 타이밍이 꼬였다.
해결 방법
jQuery 이벤트 리스너를 root 노드에 직접 등록하도록 수정했다.
const rootElement = document.getElementById('root');
$(rootElement).on('click', '.modal-overlay', function(e) {
e.stopPropagation();
closeModal();
});
또는 React 쪽 코드를 수정해서 e.nativeEvent.stopImmediatePropagation()을 사용하는 방법도 있었다.
마이그레이션 가이드
React 17은 breaking change가 거의 없다고 알려졌지만, 이벤트 시스템 변경은 레거시 코드와 통합된 환경에서 주의가 필요하다. 특히 document 레벨 이벤트를 직접 다루는 코드가 있다면 반드시 테스트해야 한다.
점진적 마이그레이션을 지원하기 위한 변경이지만, 오히려 혼재 환경에서 문제가 생길 수 있다는 점이 아이러니했다.