[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] Portals & Modal

minji·2025년 9월 8일
post-thumbnail

"포탈타고 원하는 곳으로 자유롭게 이동하자!"


포탈(Portal)이란?

| 부모 컴포넌트의 DOM 계층구조 바깥에 있는 DOM노드로 자식 컴포넌트를 렌더링 하는 방법

위에 적힌 포탈의 의미를보면 바로 이해하기가 어렵다.
즉, 포탈은 DOM 노드의 물리적 배치만 변경하고, 그 외에는 리액트 컴포넌트 노드처럼 동작하게 하는 방법인 것이다.

나라는 사람은 우리집 소속으로 있지만, 포탈을 타고 몸만 집밖으로 나가서 노는 느낌으로 이해를 해봤다. 🤔

→ 이벤트 실행시, DOM트리가 아닌 React트리를 기준으로 전파가 이루어진다.

// 형태
createPortal(children, domNode, key?)

매개변수

  • children: JSX의 일부(<div />, <SomeComponent />), <Fragment>(<>...</>), 문자열, 숫자 등 리액트로 렌더링할 수 있는 모든 것을 의미한다.
  • domNode: 자바스크립트에서 다뤘던 document.getElementById() 로 가져오는 포탈로 렌더링할 실제 DOM노드 위치를 의미한다.

단, 노드는 이미 존재하고 있어야한다. 리렌더링이 발생하면서 다른 DOM 노드위치가 전달되면 포털은 "새로운 곳으로 가야하는구나!" 라고 생각해서 기존 렌더링은 지우고 새로운 곳에 재생성을 진행하기 때문이다.

  • (optional)key: key로 사용할 고유한 문자열 또는 숫자를 의미한다.

반환값
JSX를 포함하거나 리액트 컴포넌트를 반환할 수 있는 React 노드를 반환한다.

그럼 포탈은 어떤 경우에서 사용될까?

웹사이트를 이용하다보면 자주 보이는 모달, 토스트(toast), 드롭다운 이 대표적인 예시이다.
이처럼 화면 위에 독립적으로 나타나야하는 UI를 구현할 때 포탈은 유용하게 쓰인다.

기존 컴포넌트 트리 구조나 스타일에 영향을 받지않고, 원하는 위치와 계층에 정확히 표시되어야 할 때! 그때가 바로 포털의 마법을 부릴 순간인 것이다. 🪄✨

포털을 어떻게 사용하는지 예시로 살짝쿵 살펴보자.

index.html

<div id="alert-portal"></div>

alert-portal.jsx (컴포넌트 파일)

import { createPortal } from 'react-dom'

function AlertPortal({children}) {
  if (typeof document === 'undefined') return null // SSR 환경보호차원
  
  const portalRoot = document.getElementById('alert-portal') // 렌더링 될 위치 결정
  if (!portalRoot) return null
  
  return createPortal(children /* AlertPortal함수의 매개변수로, 렌더링할 대상 */, portalRoot /* 렌더링될 위치 */)

✋🏻 여기서 잠깐! react 와 react-dom 은 무슨 차이일까?
파일내에서 필요기능을 import 할 때 from 뒤에 react 가 있을 때도 있고, reacdt-dom이 있을 때도 있다. 둘은 어떤 차이가 있길래 createPortal을 불러올 땐 react-dom 에서 불러오는 걸까?

  • react : 해당 라이브러리는 사용자 인터페이스(UI) 를 구성하는 핵심 기능인 JSX, Component 등을 제공함 ⇒ 설계도 및 규칙 정의 역할
  • react-dom : 해당 라이브러리는 리액트 컴포넌트를 실제 웹페이지(DOM)에 연결하고 렌더링 하는 기능을 제공함 ⇒ 설계도를 기반으로한 구현 및 출력 역할
    createPortal은 컴포넌트를 렌더링 하는 기능을 하므로 react-dom 에서 import 해오는 것이다.

포탈과 키보드 포커스 관리

포탈을 이용하여 UI를 구현하는 예시 중 모달키보드 포커스 관리에 대해서 살펴보자.

모달이 띄워졌을 때, 마우스로 필요한 곳에 이동하고 닫기버튼을 눌러 창을 빠져나올 수 있다.
하지만 마우스가 작동하지 않을때 혹시 귀찮으니까 키보드로 이동을 해야할 때가 있다.
이 때, 포커스 트랩이 매우 중요하다.

🔎 포커스 트랩이란?

  • Tab 키를 누르면 탭 이동이 가능한 다음 요소로 초점이동되고, 마지막 요소에서 Tab 키를 누르면 내부에서 첫번째로 탭 이동이 가능한 요소로 초점이동이 되어야 한다.
  • Shift + Tab 키를 누르면 탭 이동이 가능한 이전 요소로 초점이동되고, 첫번째 요소에서 Shift + Tab 키를 누르면 내부에서 마지막으로 탭 이동이 가능한 요소로 초점이동이 되어야 한다.
  • ESC 키를 누르면 창이 닫히고 창을 연(트리거 역할) 요소로 초점이동이 되어야 한다.

포탈(portal)을 이용할 때는 키보드 포커스 관리에 신경쓰자!

✋🏻 여기서 잠깐! 모달(modal) 과 다이얼로그(dialog)에 대해서 정리해보자.

비슷해보이는 두 용어의 차이에 대해서 정리해보고 넘어갈 필요가 있다. (처음엔 나도 헷갈렸다..)

모달(modal)

현재 화면위에 나타나서 사용자의 시선을 집중시키는 창 또는 레이어를 의미한다.

모달이 나타나면 사용자는 해당 모달에서 요구/필요하는 작업을 진행하며, 모달을 닫기전까지는 뒷배경의 다른 UI 요소들과 상호작용할 수 없다.

⇒ 일시적으로 메인 흐름을 중단시키고 모달이라는 특정 작업에만 집중하도록 만든다.

다이얼로그(dialog)

사용자에게 정보를 제공하거나 확인/취소와 같은 결정을 내리도록 요청하는 팝업창을 의미한다.

일반적으로 사용자와 시스템간의 소통창구 역할을하며, 다이얼로그는 모달일 수도 있고, 비모달일 수도 있다. (두가지 모두를 가지는 욕심쟁이)

  • 모달 속성을 가질 경우 : 경고창, 확인창 등
  • 비모달 속성을 가질 경우 : 도움말 창, 채팅창 등

⇒ 모달이 다이얼로그의 상위 개념이라고 볼 수 있다.


🔗 참고자료
https://ko.react.dev/reference/react-dom/createPortal
https://ko.legacy.reactjs.org/docs/portals.html
https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element
https://developer.mozilla.org/ko/docs/Web/HTML/Reference/Elements/dialog
https://caniuse.com/mdn-css_properties_transition-behavior
https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement
https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop
https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/dialog/
https://ko.legacy.reactjs.org/docs/accessibility.html#programmatically-managing-focus
https://developer.mozilla.org/en-US/docs/Web/Accessibility/Guides/Keyboard-navigable_JavaScript_widgets
https://allyjs.io/data-tables/focusable.html
https://focus-trap.github.io/focus-trap/

profile
FE Developer .mjs

0개의 댓글