본문 바로가기
C.W.K.
Stream
Lesson 02 of 06 · published

Side panel 등록 — Manifest + chrome.sidePanel

~12 min · side-panel, manifest, chrome.sidePanel, setOptions, user-gesture

Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Manifest에 선언. SW에서 manipulate. User gesture 아래에서 open. 세 문장, 한 lesson 안의 세 lesson — 그리고 어느 하나라도 건너뛰면 API가 조용히 동작 거부하는 세 가지 실제 방법."

Step 1 — Manifest에 선언

두 field가 함께 default panel 등록:

  • side_panel.default_path — panel 열릴 때 Chrome이 load 할 extension 안 HTML file. Panel 존재 자체에 필수.
  • permissions"sidePanel" — SW에서 chrome.sidePanel.* 호출 필수. 그게 없으면 API 자체가 undefined.

이것만 있어도 Chrome의 side-panel 목록에는 뜨긴 해. user가 직접 골라서 열 수 있고. 다만 그 외에는 아무것도 연결이 안 돼 있어.

Step 2 — SW에서 behavior 구성

chrome.sidePanel API가 실제로 쓸 세 method 노출:

  • chrome.sidePanel.setOptions({ tabId?, path, enabled }) — panel HTML과 enabled 상태 설정, 선택적으로 tab 별. 한 tab target 하려면 tabId 전달. global default 면 생략.
  • chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: boolean }) — toolbar icon click이 popup 대신 panel 열지 toggle. Icon click의 popup과 mutually exclusive: click이 panel 열거나 popup 열거나.
  • chrome.sidePanel.open({ tabId? | windowId? }) — programmatic panel 열기. User-gesture handler (action click, keyboard command, context menu)에서 호출해야 함, 아니면 reject.

셋 다 SW에 살아. Panel 자체는 자기 visibility 구성 안 함 — cross-tab perspective 가진 SW에 그 권한이 있어.

User-gesture 규칙

chrome.sidePanel.open()가 엄격한 거. Timer, alarm, webRequest event, user gesture에서 안 온 content-script 메시지에서 호출하면 Side panel can only be opened by a user gesture로 reject. 정당한 trigger:

  • chrome.action.onClicked — toolbar icon click. Manifest에 default_popup 없을 때만 fire.
  • chrome.commands.onCommand — manifest commands block에 선언된 keyboard shortcut.
  • chrome.contextMenus.onClicked — right-click menu item.
  • 실제 user click에 응답하는 popup 이나 panel에서 온 메시지일 때의 chrome.runtime.onMessage — Chrome이 그걸 gesture-derived event로 다룸.

이건 보안의 밑바닥 규칙이야. extension이 user가 아무것도 안 했는데 멋대로 panel을 들이밀 수는 없다는 거지. 그러니 존중해 줘. timeout을 걸어서 user가 누른 척 흉내 내려고 하지 말고.

Global-first 패턴

모든 tab이 같은 panel을 쓰는 ClipDeck v1은 global option 하나면 충분해:

  1. Manifest가 default pathsidePanel permission을 선언해.
  2. SW가 chrome.sidePanel.setOptions({ path: 'panel.html', enabled: true })를 global로 한 번 걸어.
  3. URL에 따라 panel을 끄거나 다른 path를 줄 제품 요구가 생길 때만 tabId와 tab lifecycle listener를 더해.

Popup 공존 선택

ClipDeck은 popup과 panel 둘 다 ship. Toolbar icon이 default로 뭘 열지 골라야 함:

  • action.default_popup: "popup.html"을 그대로 두고 openPanelOnActionClick: false로 하면 → toolbar를 누르면 popup이 뜨고, panel은 자기 토글이나 버튼으로만 열려.
  • default_popup 제거 AND openPanelOnActionClick: true 설정 → toolbar click이 panel 열고. popup은 icon에서 닿을 수 없음 (키보드나 다른 flow로 여전히 열 수는 있음).
  • 둘 다 유지: 불가. Chrome이 매 click에 선택 강제.

ClipDeck 현재 선택: popup을 default로 유지 (더 빨리 load, "reset / quick stats" 버튼 가짐), popup에 user-gesture handler에서 chrome.sidePanel.open() 부르는 "Open clip list" 버튼 추가.

manifest가 경로를 적고, SW가 tab 별 설정과 동작을 잡고, open()은 user가 뭔가를 누른 직후에만 먹혀. 이 셋 중 하나라도 빠지면 panel이 아예 없거나, 있어도 안 뜨거나, 엉뚱한 에러를 뱉으면서 거절당해.
Lifecycle listener를 억지로 만들지 마. Global panel에는 global option 하나면 돼. 원하는 option이 active tab이나 load가 끝난 URL에 실제로 의존할 때만 tabs.onActivatedtabs.onUpdated를 써.

Code

background.js — global panel default 하나·javascript
// background.js — one global panel default
chrome.runtime.onInstalled.addListener(async () => {
  await chrome.sidePanel.setOptions({
    path: "panel.html",
    enabled: true,
  });
  // The popup keeps the toolbar click; it opens the panel from its button.
  await chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false });
});
popup.js — 실제 user click에서 side panel 열기·javascript
// popup.js — Open Panel 버튼 (popup 안에 살아, 그게 user gesture)
async function openSidePanel() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  if (!tab) return;
  // 이 handler 안에서 sidePanel.open 호출 OK — popup click 이
  // user gesture, Chrome 이 이 호출로 그걸 carry.
  await chrome.sidePanel.open({ tabId: tab.id });
  // Popup 이 보통 이 후 auto-close; 예상된 동작.
}

document.getElementById("openPanelBtn").addEventListener("click", openSidePanel);
background.js — 키보드 command가 panel 열기·javascript
// background.js — panel 여는 keyboard command 도 bind
chrome.commands.onCommand.addListener(async (command) => {
  if (command !== "open-panel") return;
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  if (!tab?.id) return;
  await chrome.sidePanel.open({ tabId: tab.id });
});

// Manifest commands block 에 save-clip 옆에 추가:
//   "open-panel": {
//     "suggested_key": { "default": "Ctrl+Shift+P", "mac": "Command+Shift+P" },
//     "description": "Open the ClipDeck side panel"
//   }

External links

Exercise

Global background.js setup과 popup button handler를 넣어. Reload하고 popup의 Open Clip List를 눌러 native side panel이 열리는지 봐. 두 번째 user-gesture path가 필요할 때만 optional keyboard command를 더해. 그다음 per-tab setOptions가 정당한 경우를 설명해.
Hint
sidePanel.open은 click 또는 command handler에서 바로 불러. Global option을 반복하려고 onActivated/onUpdated를 넣지 말고 URL-dependent behavior가 있을 때만 더해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.