
본 컴포넌트는 단일 파일 구조 내에서 **"플로팅 컨텐츠 노출"**과 **"포커스 관리"**라는 역할을 수행하도록 설계되었다. 외부 클릭 감지 및 Esc 키 바인딩을 통해 웹 접근성(A11y)과 사용자 경험을 최적화하였다.
| 파일명 | 유형 | 주요 역할 |
|---|---|---|
| popover.tsx | Client | 트리거 요소에 따른 부유(Floating) 컨텐츠의 표시 및 위치를 관리하는 공통 컴포넌트 |
document.body.style.overflow = "hidden"을 통해 배경 스크롤을 자동으로 차단한다.role="dialog"와 aria-modal="true"를 부여하고, Escape 키 입력 시 onClose 콜백을 호출하여 사용자 이탈을 돕는다.animate-in과 backdrop-blur를 결합하여 고급스러운 등장 효과와 시각적 깊이감을 제공한다.| 속성명 | 타입 | 기본값 | 설명 |
|---|---|---|---|
isOpen |
boolean |
필수 | 팝업의 노출 여부를 제어 |
onClose |
() => void |
필수 | 닫기 버튼, 배경 클릭, Esc 키 입력 시 호출되는 함수 |
children |
ReactNode |
필수 | 팝업 내부에 표시될 컨텐츠 |
trigger |
ReactNode |
- |
팝업을 열기 위한 기준 요소를 팝업과 함께 렌더링할 때 사용 |
align |
`'left' | 'center' | 'right'` |
center |
boolean |
true |
화면 정중앙에 배치할지 여부 |
className |
string |
- |
팝업 컨텐츠 박스의 스타일을 커스텀 |
containerClassName |
string |
- |
trigger를 감싸는 컨테이너의 스타일을 커스텀 |
가장 일반적인 형태로, 외부 상태(useState)에 따라 화면 중앙에 중요한 알림을 띄운다
"use client";
import Popup from "@/src/shared/ui/popup";
import { useState } from "react";
export default function DeleteConfirm() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(true)}>삭제하기</button>
<Popup isOpen={isOpen} onClose={() => setIsOpen(false)}>
<div className="text-center">
<h3 className="text-h3 font-bold mb-2">정말 삭제할까요?</h3>
<p className="text-body1 text-gray-400 mb-6">삭제된 데이터는 복구할 수 없습니다.</p>
<div className="flex gap-2">
<button onClick={() => setIsOpen(false)} className="flex-1 p-3 bg-gray-100 rounded">취소</button>
<button className="flex-1 p-3 bg-red-500 text-white rounded">삭제</button>
</div>
</div>
</Popup>
</>
);
}
trigger 속성을 사용하여 팝업과 버튼을 한 곳에서 관리하며, 하단 정렬 등을 커스텀할 수 있다.
<Popup
isOpen={showPopup}
onClose={() => setShowPopup(false)}
trigger={<span className="icon">🔔 알림 보기</span>}
center={false}
align="right"
className="w-[400px] mt-auto mb-10" // 하단 우측 배치 예시
>
<div className="p-4">새로운 알림이 도착했습니다.</div>
</Popup>