React Portal로 모달 z-index 문제 해결하기

문제 상황

재택근무로 전환된 후 레거시 대시보드 프로젝트를 리팩토링하던 중, 모달 컴포넌트가 특정 페이지에서 다른 요소에 가려지는 문제를 발견했다. 기존 코드는 모달을 일반 컴포넌트처럼 렌더링하고 있었고, 부모 요소의 z-indexoverflow: 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 접근 시 체크 로직 추가 필요
  • 여러 모달이 중첩될 경우 관리 전략 필요
React Portal로 모달 z-index 문제 해결하기