React Portal로 모달 z-index 문제 해결하기
문제 상황
재택근무로 전환된 후 레거시 대시보드 프로젝트를 리팩토링하던 중, 모달 컴포넌트가 특정 페이지에서 다른 요소에 가려지는 문제를 발견했다. 기존 코드는 모달을 일반 컴포넌트처럼 렌더링하고 있었고, 부모 요소의 z-index와 overflow: hidden 속성 때문에 모달이 제대로 표시되지 않았다.
해결 방법
React 16부터 제공되는 Portal API를 사용해 모달을 document.body 직하위에 렌더링하도록 변경했다.
import { useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';
const Modal = ({ children, onClose }) => {
const elRef = useRef(null);
if (!elRef.current) {
elRef.current = document.createElement('div');
}
useEffect(() => {
const modalRoot = document.body;
modalRoot.appendChild(elRef.current);
return () => {
modalRoot.removeChild(elRef.current);
};
}, []);
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>,
elRef.current
);
};
결과
DOM 트리 구조상 최상위에 렌더링되므로 부모 요소의 CSS 속성에 영향받지 않게 되었다. 별도의 z-index 조정 없이도 모달이 항상 최상단에 표시된다.
추가 고려사항
- 포커스 트랩과 키보드 이벤트(ESC) 처리는 별도로 구현 필요
- SSR 환경에서는
document접근 시 체크 로직 추가 필요 - 여러 모달이 중첩될 경우 관리 전략 필요