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로의 전환은 좋은 중간 단계였다.