1. 무엇인가?
React Portal은 React 컴포넌트를 기존 부모 DOM 계층이 아닌, DOM 트리의 임의의 위치(예: 루트나 특정 엘리먼트)에 렌더링할 수 있게 해주는 기능입니다.
즉, 계층 구조와 무관하게 원하는 위치에 컴포넌트를 그릴 수 있습니다.
2. 왜 사용하는가?
Portal은 부모 DOM 계층의 스타일/레이아웃/기능 한계를 극복하기 위해 사용합니다.
- 레이아웃/스타일링 이슈 해결(예: 모달, 드롭다운, 툴팁 등)
- → 부모 요소의 overflow: hidden, z-index, position: relative 등 스타일 속성 영향을 받지 않고, 원하는 레이어(예: 화면 맨 위)에 노출 가능
- 접근성 개선
- → 모달 등을 DOM 트리 최상단에 렌더링함으로써, 스크린리더나 키보드 네비게이션 등 보조기기가 올바르게 포커스를 감지
- 글로벌 이벤트/UI 독립성→ 여러 부모 계층에 얽매이지 않으므로, 컴포넌트 재사용성과 이동성도 높아짐
- → ESC 키로 닫기, 스크롤 락 등 전역 이벤트가 필요한 UI에 유리
3. 어떻게 사용하는가?
기본 문법
import { createPortal } from 'react-dom';
createPortal(child, container)
- child: 렌더링할 React 노드(JSX)
- container: 실제로 삽입할 DOM 노드 (예: document.body, document.getElementById('modal-root'))
예시 코드
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
);
}
4. Portal의 작동 원리
| 구분 | 설명 |
|---|---|
| 물리적 DOM | Portal로 렌더링한 컴포넌트는 지정한 container(DOM 노드)에 실제로 추가됨 |
| React 트리 | 논리적으로는 기존 부모 컴포넌트의 자식이므로, Context, 이벤트 버블링 등에 영향 없음 |
| 이벤트 버블링 | React 합성 이벤트는 DOM 위치와 관계없이 React 트리 기준으로 버블링 |
| Context | Portal 내부에서도 부모 Context를 그대로 사용할 수 있음 |
5. Portal 주요 사용 사례
- 모달/다이얼로그: z-index, overflow, 포커스 관리 이슈 해결
- 드롭다운/툴팁/컨텍스트 메뉴: overflow: hidden 등에 잘리지 않도록 처리
- 토스트/글로벌 알림: 언제 어디서든 UI 위에 자연스럽게 띄우기
6. 실전 팁 및 주의사항
1. 모달 전용 컨테이너(div) 사용
- 여러 모달이 중첩될 수 있으면
<div id="modal-root"></div>등 별도 컨테이너에 모아 렌더링하는 것이 좋음- 스타일, z-index, 애니메이션 관리가 쉬움
- “마지막 띄운 모달이 제일 위”를 자연스럽게 보장
- 포커스 트랩, 스크린리더 안내 등 전역 처리가 쉬움
- 여러 div에 분산하면 z-index 충돌 등 관리가 어려움
2. 접근성(a11y) 보완을 위한 추가 구현 필요
- 포커스 트랩(Focus Trap)
- 모달 내에서만 탭키 포커스가 이동하도록 제한하는 기능
- 모달이 열릴 때,
- 첫 번째 focusable 요소로 포커스 이동
- Tab키를 누르면 마지막에서 다시 처음으로 이동
- 배경으로 포커스가 빠지지 않게 해야 함
- 접근성(a11y)에서 필수, 라이브러리(focus-trap-react 등) 사용 권장
- 스크롤 락(Scroll Lock)
- 모달, 오버레이가 떠 있는 동안 body의 스크롤을 막는 기능
- 모달이 열릴 때:
document.body.style.overflow = 'hidden';
-
-
- 모달이 닫힐 때:
-
document.body.style.overflow = '';
7. 정리
- Portal은 React 트리와 물리적 DOM 구조를 분리하여, 스타일/접근성/UI 요구를 모두 만족시키는 실전 필수 기술
- 단순 렌더링만으로는 부족하며, 포커스 트랩/스크롤 락/접근성 보완 등 추가 구현이 반드시 필요
- 여러 포탈 UI가 있을 땐, 전용 컨테이너(div)를 적극 활용해서 관리의 일관성을 높일 것
'Web Development > Next.js' 카테고리의 다른 글
| [RxJS] 3편. RxJS로 CRUD 구현하기 – 그리고 절차형처럼 느껴지는 부분 (0) | 2025.03.23 |
|---|---|
| [RxJS] 2편. React에서 RxJS로 상태관리를 한다는 것은? (1) | 2025.03.23 |
| [RxJS] 1편. RxJS와 선언형 프로그래밍의 관계 (1) | 2025.03.23 |
| React에서 useEffect를 꼭 써야 할까? — 오히려 안 써도 되는 경우가 더 많다 (0) | 2025.03.23 |
| React를 쓰면서도 몰랐던 선언형 프로그래밍 이야기 (0) | 2025.03.23 |