0Pricing
Frontend Academy · 강의

모달과 툴팁을 위한 포털

ReactDOM.createPortal을 사용해 자식 컴포넌트를 다른 DOM 노드에 렌더링하고, overflow:hidden 컨테이너를 벗어나는 모달과 툴팁을 구현합니다.

모달과 툴팁을 위한 포털은(는) CoddyKit의 무료 Frontend Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Frontend Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Frontend Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

컨테이너 탈출 문제

모달과 툴팁은 DOM의 최상위에서 렌더링되어 부모의 overflow:hidden, z-index 스태킹, 변환으로 생성된 컨텍스트를 벗어나야 합니다. 해결책은 포털입니다.

createPortal API

createPortal(children, container)는 children을 container에(어떤 DOM 노드든 가능) 렌더링하면서, 이벤트와 Context를 위해 React 컴포넌트의 논리적인 자식으로는 계속 유지합니다.

import { createPortal } from 'react-dom';

function Modal({ children, onClose }) {
  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

포털 컨테이너

일반적인 대상은 document.body, index.html에 있는 전용 #modal-root div, 또는 필요할 때 생성하는 ref 참조 컨테이너입니다.

<!-- index.html -->
<div id="root"></div>
<div id="modal-root"></div>

// component:
const modalRoot = document.getElementById('modal-root');
return createPortal(<div>...</div>, modalRoot);

그냥 인라인으로 렌더링하면 안 되는 이유

부모에 overflow: hidden이 있으면 자식 모달이 잘립니다. 부모에 transform: scale이 있으면 모달의 고정 위치가 부모를 기준으로 정해집니다. 포털은 이 모든 문제를 피합니다.

이벤트는 여전히 버블링됩니다

DOM의 다른 위치에서 렌더링되더라도 React 이벤트는 DOM 트리가 아니라 컴포넌트 트리를 따라 버블링됩니다. 따라서 포털 안에서 발생한 클릭도 상위 React 컴포넌트의 onClick 처리기로 버블링됩니다.

오버레이 클릭으로 닫기

모달 콘텐츠에서 이벤트 전파를 중지하여 내부를 클릭해도 모달이 닫히지 않게 하십시오.

function Modal({ onClose, children }) {
  return createPortal(
    <div className="backdrop" onClick={onClose}>
      <div className="content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

Escape 키로 닫기

모달이 열려 있는 동안 키보드 입력 리스너를 추가하고, 마운트 해제 시 제거하십시오.

useEffect(() => {
  const onKey = (e) => { if (e.key === 'Escape') onClose(); };
  window.addEventListener('keydown', onKey);
  return () => window.removeEventListener('keydown', onKey);
}, [onClose]);

포털에서 포커스 관리하기

모달이 열리면 포커스를 내부로 이동하고, 닫힐 때까지 포커스를 가둔 다음, 모달을 연 요소로 포커스를 복원하십시오. 이 기능을 다시 구현하지 않도록 focus-trap-react 같은 라이브러리를 사용하십시오.

네이티브 dialog 요소

HTML의 <dialog>는 모달 위치 지정, 배경 오버레이, 포커스 가두기, Escape 키 닫기를 기본으로 처리합니다. 대부분의 모달에서는 포털보다 더 나은 출발점입니다.

function Modal({ open, onClose, children }) {
  const ref = useRef(null);
  useEffect(() => {
    if (open) ref.current?.showModal();
    else ref.current?.close();
  }, [open]);
  return (
    <dialog ref={ref} onClose={onClose}>
      {children}
    </dialog>
  );
}

툴팁과 팝오버

툴팁은 기준 요소를 기준으로 위치가 정해집니다. 포털을 사용하면 스태킹 컨텍스트를 벗어날 수 있습니다. 충돌 감지, 뒤집기, 위치 지정을 위해 floating-ui와 함께 사용하십시오.

import { useFloating, autoUpdate } from '@floating-ui/react';

function Tooltip({ children, content }) {
  const { refs, floatingStyles } = useFloating({ whileElementsMounted: autoUpdate });
  const [open, setOpen] = useState(false);
  return (
    <>
      <button ref={refs.setReference} onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
        {children}
      </button>
      {open && createPortal(
        <div ref={refs.setFloating} style={floatingStyles}>{content}</div>,
        document.body
      )}
    </>
  );
}

SSR 주의 사항

서버에는 document가 존재하지 않습니다. 클라이언트에서만 렌더링하거나(typeof window을 확인), Next.js의 dynamic을 ssr:false와 함께 사용하십시오.

접근성: aria-modal

모달 컨테이너에는 role="dialog", aria-modal="true", 그리고 모달의 제목을 가리키는 aria-labelledby가 있어야 합니다. 또는 네이티브 <dialog>를 사용하십시오.

빠른 확인

컴포넌트 트리 안에 모달을 인라인으로 렌더링하는 대신 모달에 createPortal을 사용하는 이유는 무엇입니까?

복습: 포털

createPortal(children, container)은 React 트리의 논리적 구조는 유지하면서 DOM의 다른 위치에 렌더링합니다. overflow, transform, z-index에서 벗어나야 하는 문제를 해결합니다. 이벤트는 여전히 React 트리를 따라 버블링됩니다. Escape 키 닫기와 포커스 관리를 추가하십시오. 네이티브 <dialog>는 모달에 필요한 기능 대부분을 처리합니다. 툴팁에는 floating-ui를 함께 사용하십시오. SSR(typeof window 확인)을 잊지 마십시오.

자주 묻는 질문

“모달과 툴팁을 위한 포털” 강의는 무료인가요?

네 — “모달과 툴팁을 위한 포털” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Frontend Academy 강의 전체를 잠금 해제할 수 있습니다. Frontend Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“모달과 툴팁을 위한 포털”에서 뭘 배우나요?

ReactDOM.createPortal을 사용해 자식 컴포넌트를 다른 DOM 노드에 렌더링하고, overflow:hidden 컨테이너를 벗어나는 모달과 툴팁을 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 Frontend Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Frontend Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Frontend Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“모달과 툴팁을 위한 포털” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Frontend Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Frontend Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Context를 사용한 복합 컴포넌트
  2. 렌더 프롭과 HOC 패턴
  3. 모달과 툴팁을 위한 포털
  4. 오류 경계
← Frontend Academy(으)로 돌아가기