[React] React Portal 을 사용해서 모달 만들기

임홍원·2023년 12월 19일
2
post-thumbnail

React Portal

Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공한다.

React Portal을 왜 사용하는지?

React는 부모 컴포넌트가 렌더링 되면서 자식 컴포넌트가 렌더링되는 트리 구조로 되어있다.
이러한 트리 구조가 불편함을 줄 때가 있고, 부모 - 자식 관계를 유지하지만 독립적인 위치에서 렌더링 할 때 더욱 편한 경우가 있다. 또한 스타일링을 통해 z-index와 같은 후 처리를 해주어야 한다.

또한 기본적으로 모달은 모든 페이지위에 오버레이 된다. 하지만 스크린리더가 렌더링되는 HTML코드를 해석할 때 모달이라는 존재를 인식할 수 없게 된다.
또한 의미적이나 구조적인 관점에서 모달이 모든 영역 위에 깔린 것인지 알지 못한다.

이러한 상황에서 Portal을 통해 독립적인 구조와 부모 - 자식 관계를 유지할 수 있다.

React Portal 사용법

import ReactDOM from 'react-dom';

const element = document.getElementById('modal') as HTMLElement;
ReactDOM.createPortal(children, element);

createPortal이 정의되어 있는 ReactDOM을 불러온다.

첫 번째 인자(child)는 엘리먼트, 문자열, 혹은 fragment와 같은 어떤 종류이든 렌더링할 수 있는 React 자식이다.
두 번째 인자(container)는 DOM 엘리먼트이다.

React Portal로 Modal 구현하기

// public/index.html
 <body>
    <div id="root"></div>
    <div id="modal"></div>
  </body>

Portal을 구현 할 트리의 부모 컴포넌트를 어디로 정할지 설정한다.
위 코드에선 최상단 요소인 root의 형제관계로 modal을 넣어주었다.

Portal 만들기

// ModalPortal.tsx
import { ReactNode } from 'react';
import ReactDOM from 'react-dom';

interface Props {
	children: ReactNode;
}

const ModalPortal = ({ children }: Props) => {
	const element = document.getElementById('modal') as HTMLElement;
	return ReactDOM.createPortal(children, element);
};

export default ModalPortal;
// Modal.tsx
import styles from '../styles/Modal.module.css';

interface Props {
	onClose: () => void;
}

const Modal = ({ onClose }: Props) => {
	return (
		<div>
			<div className={styles['modal-overlay']} onClick={onClose}></div>
			<div className={styles['modal-content-container']}>
				<div className={styles['modal-content']}>
					<h1>모달테스트</h1>
					<button
						onClick={onClose}
						className={styles['modal-close-button']}
						type='button'
					>
						닫기
					</button>
				</div>
			</div>
		</div>
	);
};

export default Modal;

Event Handler

// App.tsx
import { useState } from 'react';
import Modal from './components/Modal';
import ModalPortal from './components/ModalPortal';

const App = () => {
	const [modalOpen, setModalOpen] = useState<boolean>();

	const showModal = () => {
		setModalOpen(true);
	};

    const closeModal = () => {
        setModalOpen(false);
    }
	return (
		<div>
			<button onClick={showModal}>모달 열기</button>
			{modalOpen && (
				<ModalPortal>
					<Modal onClose={closeModal}/>
				</ModalPortal>
			)}
		</div>
	);
};

export default App;

Portal 시키고자 하는 요소를 <ModalPortal> 컴포넌트로 감싸 준다.

또한 이벤트 핸들러를 달아주어 검은색 배경과 모달의 버튼 클릭시 모달이 닫히게 하였다.

0개의 댓글