Invoker Commands API란?

2025년 12월, 웹 개발의 판도를 바꿀 새 HTML 표준이 모든 주요 브라우저에서 Baseline 상태를 획득했습니다. 바로 Invoker Commands API입니다.

기존에는 버튼 클릭으로 팝오버(popover)를 열거나 다이얼로그(dialog)를 띄우려면 반드시 JavaScript 이벤트 리스너를 작성해야 했습니다. Invoker Commands API는 이 과정을 HTML 속성 두 개만으로 대체합니다. JavaScript를 한 줄도 작성하지 않고 완전히 동작하는 인터랙티브 UI를 만들 수 있게 된 것입니다.

브라우저 지원 현황 (2026년 4월 기준)

Invoker Commands API는 Baseline 2025로 지정되어 모든 최신 주요 브라우저에서 안정적으로 사용할 수 있습니다.

  • Chrome / Edge: 135 버전부터 지원
  • Firefox: 144 버전부터 지원
  • Safari: 26.2 버전부터 지원 (Baseline 완성)
  • Opera: 120 버전부터 지원

사파리 26.2가 롤아웃을 완료하면서 크로스 브라우저 지원이 완전히 확립되었습니다. 별도의 폴리필 없이 프로덕션에서 사용 가능한 시점입니다.

핵심 속성: commandfor와 command

API의 핵심은 <button> 요소에 추가된 두 가지 새 HTML 속성입니다.

commandfor 속성

버튼을 명령 호출자(invoker)로 변환합니다. 제어하고 싶은 대상 요소의 id 값을 지정합니다.

<button commandfor="target-element">버튼</button>

command 속성

commandfor로 지정된 요소에 수행할 동작을 지정합니다. 내장 명령어와 커스텀 명령어를 모두 지원합니다.

<button commandfor="mypopover" command="toggle-popover">팝오버 열기/닫기</button>

내장 명령어 목록

현재 사양에서 제공하는 내장 명령어는 다음과 같습니다.

Popover 관련 명령어

명령어 대상 동작
toggle-popover popover 속성이 있는 요소 팝오버 열기/닫기 토글
show-popover popover 속성이 있는 요소 팝오버 표시
hide-popover popover 속성이 있는 요소 팝오버 숨김

Dialog 관련 명령어

명령어 대상 동작
show-modal <dialog> 요소 모달 다이얼로그 표시
close <dialog> 요소 다이얼로그 닫기

실전 예제 1: Popover 제어 (JavaScript 없음)

가장 기본적인 사용 예입니다. popover 속성과 commandfor / command만으로 완전히 동작하는 팝오버를 구현합니다.

<!-- 팝오버를 열고 닫는 버튼 -->
<button commandfor="info-popover" command="toggle-popover">
  정보 보기
</button>

<!-- 팝오버 콘텐츠 -->
<div id="info-popover" popover>
  <h3>추가 정보</h3>
  <p>여기에 팝오버 내용을 작성합니다.</p>
  <!-- 팝오버 내부에서도 닫기 버튼 가능 -->
  <button commandfor="info-popover" command="hide-popover">
    닫기
  </button>
</div>

popover 속성만 설정하면 기본적으로 popover="auto"가 적용됩니다. 팝오버 외부를 클릭하면 자동으로 닫히는 동작도 포함됩니다.

실전 예제 2: Dialog 모달 제어 (JavaScript 없음)

기존에는 dialog.showModal()을 JavaScript로 호출해야 했지만, 이제 HTML만으로 모달을 띄울 수 있습니다.

<!-- 모달을 여는 버튼 -->
<button commandfor="confirm-dialog" command="show-modal">
  확인 창 열기
</button>

<!-- 모달 다이얼로그 -->
<dialog id="confirm-dialog">
  <h2>정말 삭제하시겠습니까?</h2>
  <p>이 작업은 되돌릴 수 없습니다.</p>
  <div>
    <button commandfor="confirm-dialog" command="close">
      취소
    </button>
    <button commandfor="confirm-dialog" command="close">
      확인
    </button>
  </div>
</dialog>

다이얼로그에서 close 명령어를 쓰면 dialog.close()를 호출한 것과 동일하게 동작합니다.

실전 예제 3: 여러 버튼으로 하나의 팝오버 제어

하나의 대상 요소를 여러 버튼이 각기 다른 명령으로 제어하는 것도 가능합니다.

<button commandfor="notification" command="show-popover">
  알림 열기
</button>
<button commandfor="notification" command="hide-popover">
  알림 닫기
</button>

<div id="notification" popover="manual">
  <p>새 메시지가 도착했습니다.</p>
</div>

popover="manual"로 설정하면 외부 클릭에 의한 자동 닫힘 없이, 명시적 명령으로만 열고 닫을 수 있습니다.

고급: 커스텀 명령어로 Web Components 확장

내장 명령어 외에도 -- 접두사를 붙인 커스텀 명령어를 정의할 수 있습니다. CSS 커스텀 속성과 동일한 네이밍 규칙입니다. 커스텀 명령어는 JavaScript command 이벤트를 통해 처리합니다.

<!-- 커스텀 명령어를 가진 버튼들 -->
<button commandfor="product-image" command="--zoom-in">
  확대
</button>
<button commandfor="product-image" command="--zoom-out">
  축소
</button>
<button commandfor="product-image" command="--reset">
  초기화
</button>

<img id="product-image" src="product.jpg" alt="제품 이미지" />
const img = document.getElementById("product-image");
let scale = 1;

img.addEventListener("command", (event) => {
  switch (event.command) {
    case "--zoom-in":
      scale = Math.min(scale + 0.2, 3);
      break;
    case "--zoom-out":
      scale = Math.max(scale - 0.2, 0.5);
      break;
    case "--reset":
      scale = 1;
      break;
  }
  img.style.transform = `scale(${scale})`;
});

커스텀 명령어는 Web Components와 결합할 때 특히 강력합니다. 컴포넌트를 사용하는 측에서 JavaScript 없이 선언적으로 컴포넌트를 제어할 수 있는 API를 제공할 수 있습니다.

JavaScript API: 프로그래매틱 접근

HTML 속성 외에 JavaScript에서 직접 접근하는 프로퍼티도 추가되었습니다.

const btn = document.querySelector("button");

// commandForElement: 제어 대상 요소 읽기/쓰기
console.log(btn.commandForElement); // HTMLElement 반환
btn.commandForElement = document.getElementById("other-target");

// command: 명령어 읽기/쓰기
console.log(btn.command); // "toggle-popover" 등
btn.command = "show-modal";

// command 이벤트 수신 (대상 요소에서 발생)
document.getElementById("mypopover").addEventListener("command", (e) => {
  console.log("실행된 명령어:", e.command);
  console.log("호출한 버튼:", e.source); // invoker 버튼 참조
});

기존 방식과 비교

Invoker Commands API 도입 전후의 코드를 비교해 봅니다.

기존 방식 (JavaScript 필요)

<button id="open-btn">모달 열기</button>
<dialog id="my-dialog">
  <button id="close-btn">닫기</button>
  내용
</dialog>

<script>
  document.getElementById("open-btn").addEventListener("click", () => {
    document.getElementById("my-dialog").showModal();
  });
  document.getElementById("close-btn").addEventListener("click", () => {
    document.getElementById("my-dialog").close();
  });
</script>

Invoker Commands API 방식 (HTML만)

<button commandfor="my-dialog" command="show-modal">모달 열기</button>
<dialog id="my-dialog">
  <button commandfor="my-dialog" command="close">닫기</button>
  내용
</dialog>

JavaScript 이벤트 리스너 4줄이 HTML 속성 2개로 대체됩니다. 코드량 감소뿐 아니라 페이지 인터랙티비티(INP)도 향상됩니다. JavaScript가 파싱되기 전에도 버튼이 즉시 반응하기 때문입니다.

접근성(Accessibility) 측면

Invoker Commands API는 접근성 면에서도 장점이 있습니다.

  • 키보드 사용자: 스페이스바와 Enter 키로 자동 트리거됩니다.
  • 스크린 리더: <button> 요소를 사용하므로 보조 기술과 자연스럽게 호환됩니다.
  • 포커스 관리: show-modal 명령어는 다이얼로그에 포커스를 이동시키는 기본 동작을 포함합니다.
  • Escape 키: popoverdialog의 기본 Escape 닫기 동작이 그대로 유지됩니다.

주의 사항 및 한계

  • command 속성은 반드시 commandfor와 함께 사용해야 합니다. 단독으로는 아무 효과가 없습니다.
  • commandfor의 값은 같은 문서 내 요소의 id여야 합니다. 크로스 도큐먼트(iframe 등)는 지원하지 않습니다.
  • 커스텀 명령어는 반드시 --로 시작해야 합니다. 그렇지 않으면 브라우저가 무시합니다.
  • 구형 브라우저 대응이 필요한 경우, JavaScript 폴리필을 병행 적용하거나 점진적 향상(progressive enhancement) 전략을 사용해야 합니다.

정리

Invoker Commands API는 웹 플랫폼이 오랜 시간 JavaScript에 위임했던 인터랙션 패턴을 HTML 자체로 흡수하는 흐름의 연장선에 있습니다. Popover API, <dialog> 요소와 결합하면 순수 HTML만으로 접근성 높고 성능 좋은 팝업, 모달, 툴팁, 알림 UI를 구현할 수 있습니다.

Baseline 2025 달성으로 사용 시기는 바로 지금입니다. 신규 프로젝트라면 JavaScript 이벤트 리스너 대신 commandforcommand 속성을 우선 검토해 보세요.