Contents
see List왜 Popover API를 검토해야 하나
웹 서비스에서 알림 메뉴, 사용자 메뉴, 필터 패널, 더보기 액션처럼 버튼을 눌렀을 때 잠시 떠 있는 UI는 매우 자주 등장합니다. 과거에는 이런 기능을 만들기 위해 클릭 이벤트, 외부 영역 클릭 감지, Escape 키 처리, z-index 조정, 포커스 이동, 스크롤 위치 계산을 모두 직접 작성하는 경우가 많았습니다. 작은 메뉴 하나를 위해 코드가 길어지고, 여러 컴포넌트가 동시에 열릴 때 닫힘 규칙이 어긋나거나 모바일 브라우저에서 위치가 틀어지는 문제가 생기기 쉬웠습니다.
HTML Popover API는 이런 떠 있는 UI를 브라우저가 이해할 수 있는 방식으로 선언하게 해 줍니다. MDN 기준으로 popover 속성은 2024년부터 주요 최신 브라우저에서 사용할 수 있는 Baseline 기능으로 안내되고 있으며, 버튼의 popovertarget 속성과 대상 요소의 popover 속성만으로 기본 열기, 닫기, Escape 키 닫기, 바깥 영역 클릭 닫기 동작을 얻을 수 있습니다. 여기에 2026년 Baseline으로 안내되는 CSS anchor() 계열 기능을 함께 쓰면 메뉴 위치를 자바스크립트 계산 없이 버튼 기준으로 배치할 수 있습니다.
적합한 사용 범위
Popover는 사용자 입력을 완전히 막는 모달 대화상자보다 가벼운 UI에 적합합니다. 예를 들어 계정 메뉴, 정렬 옵션, 알림 목록, 색상 선택 패널, 테이블 행 액션 메뉴처럼 본문 위에 잠깐 나타났다가 사용자가 다른 곳을 누르면 닫혀도 되는 화면에 잘 맞습니다. 반대로 결제 확인, 삭제 확인, 필수 입력처럼 사용자가 반드시 선택해야 하는 흐름은 dialog 요소와 모달 동작을 쓰는 편이 더 명확합니다.
- 사용자 흐름을 막지 않는 메뉴나 보조 패널에는 popover를 우선 검토합니다.
- 확인, 취소, 필수 선택처럼 작업을 강제해야 하면 dialog를 사용합니다.
- 복잡한 위치 계산이 필요하면 CSS anchor positioning을 적용하되, 구형 브라우저에는 기본 위치 fallback을 둡니다.
- div에 popover만 붙이면 자동으로 메뉴 역할이 생기는 것은 아니므로 role, aria-label, 버튼 텍스트를 함께 설계합니다.
기본 구현 예제
아래 예제는 회원 관리 화면에서 행마다 더보기 메뉴를 띄우는 구조입니다. 핵심은 버튼의 popovertarget 값과 메뉴의 id를 맞추는 것입니다. popover="auto"는 기본값에 가까운 동작으로, 사용자가 바깥을 누르거나 Escape 키를 누르면 닫힙니다. CSS에서는 anchor-name으로 버튼을 기준점으로 지정하고, 메뉴에는 position-anchor와 anchor()를 사용해 버튼 아래에 붙입니다.
<div class="user-row">
<span>[email protected]</span>
<button class="more-button" popovertarget="user-menu-1" aria-label="사용자 작업 열기">
더보기
</button>
<div id="user-menu-1" class="action-menu" popover="auto" role="menu" aria-label="사용자 작업">
<button type="button" role="menuitem">권한 변경</button>
<button type="button" role="menuitem">비밀번호 재설정</button>
<button type="button" role="menuitem">접속 차단</button>
</div>
</div>
.more-button {
anchor-name: --user-menu-button;
}
.action-menu {
position: absolute;
position-anchor: --user-menu-button;
top: calc(anchor(bottom) + 8px);
left: anchor(right);
transform: translateX(-100%);
min-width: 180px;
padding: 8px;
border: 1px solid #d6d8df;
border-radius: 8px;
background: #fff;
box-shadow: 0 12px 28px rgba(20, 24, 35, 0.14);
}
@supports not (top: anchor(bottom)) {
.action-menu {
inset: auto 16px 16px auto;
}
}
.action-menu button {
display: block;
width: 100%;
padding: 9px 10px;
border: 0;
background: transparent;
text-align: left;
}
.action-menu button:hover,
.action-menu button:focus-visible {
background: #f3f5f9;
outline: none;
}운영 코드에서 주의할 점
반복 목록에서 같은 anchor-name을 여러 번 쓰면 브라우저가 의도하지 않은 기준점을 잡을 수 있습니다. 실무에서는 컴포넌트 범위가 명확한 구조를 만들거나, 행마다 별도의 이름을 만들거나, anchor-scope 지원 범위를 확인해 충돌을 막는 방식이 필요합니다. 특히 테이블, 가상 스크롤 목록, 검색 결과 카드처럼 같은 메뉴가 여러 번 반복되는 화면에서는 테스트 데이터 1개만 보고 끝내지 말고 최소 20개 이상 렌더링한 상태에서 위치를 확인해야 합니다.
접근성도 별도로 점검해야 합니다. popover 속성은 뜨는 동작을 제공하지만, 메뉴의 의미까지 모두 대신 정해 주지는 않습니다. 메뉴라면 role="menu"와 role="menuitem"을 고려하고, 단순한 정보 패널이라면 제목과 aria-labelledby를 연결하는 편이 좋습니다. 버튼 텍스트가 아이콘만 있는 경우에는 aria-label을 반드시 넣어야 합니다. 키보드 사용자는 Tab, Shift+Tab, Escape로 메뉴를 열고 닫을 수 있어야 하며, hover에만 의존한 메뉴는 피해야 합니다.
서버에서 렌더링하는 화면이라면 id 중복도 흔한 장애 원인입니다. popovertarget은 id를 참조하므로 목록 안에서 동일한 id가 반복되면 첫 번째 메뉴만 열리거나 엉뚱한 메뉴가 열릴 수 있습니다. 데이터의 기본키나 반복 인덱스를 이용해 고유 id를 만들고, 프론트엔드 프레임워크에서는 컴포넌트 재사용 시 id 생성 규칙을 공통 유틸로 묶어 두는 것이 좋습니다.
점진적 적용 전략
기존 프로젝트에서 Floating UI, Popper.js, 자체 overlay manager를 이미 사용하고 있다면 한 번에 제거하지 말고 새로 만드는 작은 액션 메뉴부터 Popover API를 적용하는 것이 안전합니다. 먼저 브라우저 지원 대상, 접근성 요구, 디자인 시스템의 메뉴 스타일을 확인합니다. 다음으로 공통 MenuButton 컴포넌트를 만들고, 열기와 닫기 상태를 굳이 애플리케이션 상태로 보관하지 않아도 되는 화면에 적용합니다. 외부 클릭 닫기와 Escape 닫기를 직접 구현하던 코드는 줄이고, 서버 상태 변경이나 분석 이벤트처럼 실제 업무 로직에 필요한 JavaScript만 남깁니다.
구형 브라우저 지원이 필요한 서비스에서는 @supports로 anchor positioning만 fallback 처리해도 많은 경우 충분합니다. Popover 자체가 필요한 최소 브라우저 범위에 들어오지 않는다면 기존 메뉴 컴포넌트를 유지하고, 최신 브라우저 전용 관리자 화면이나 내부 도구부터 적용하는 방식이 현실적입니다. 중요한 것은 기능 사용 여부보다 코드의 책임을 나누는 것입니다. 브라우저가 안정적으로 처리하는 열기, 닫기, 최상단 레이어 동작은 HTML에 맡기고, 비즈니스 이벤트와 데이터 변경은 애플리케이션 코드에 남겨야 유지보수가 쉬워집니다.
적용 전 체크리스트
- 메뉴가 모달처럼 사용자를 막아야 하는 흐름인지, 가벼운 보조 UI인지 먼저 구분합니다.
- popovertarget과 popover 요소의 id가 목록 안에서 중복되지 않는지 확인합니다.
- 아이콘 버튼에는 aria-label을 넣고, 메뉴 목적을 role 또는 aria-labelledby로 설명합니다.
- 키보드로 열기, 항목 이동, 닫기, 다음 요소 이동이 자연스러운지 직접 테스트합니다.
- anchor positioning을 쓴다면 @supports fallback을 작성하고 모바일 폭에서 메뉴가 화면 밖으로 나가지 않는지 확인합니다.
- 기존 JavaScript overlay 코드에서 닫힘 처리, z-index 처리, 위치 계산을 어느 범위까지 줄일 수 있는지 작은 컴포넌트부터 검증합니다.