"Popup이 tooltip 이라면 side panel은 dock 이야. Track 4는 ClipDeck을 user가 페이지를 읽는 내내 열어 둘 수 있는 표면으로 졸업시키는 시간이야 — 책상에 붙여 둔 포스트잇이랑 펼쳐 놓은 노트의 차이지."
이 표면이 뭐냐면
Side panel은 Chrome이 tab 오른쪽에 붙여서 그려 주는, extension이 소유한 HTML 문서야. 눈으로 보면 북마크 바랑 toolbar 오른쪽 가장자리 사이에 자리를 잡아. 기능으로 보면 popup 이랑 똑같은 extension 페이지고 — 같은 chrome.* API를 쓰고, 같은 격리된 자리에 있고, DevTools로 똑같이 들여다볼 수 있어.
결정적인 차이는 수명이야. popup은 user가 다른 데를 누르는 순간 죽어. side panel은 user가 toolbar에서 직접 닫기 전까지 열려 있어. ClipDeck 한테는 이게 커. user가 기사를 읽으면서 clip 목록을 계속 띄워 두고, 저장할 때마다 뭘 다시 열지 않고도 clip을 줄줄이 쌓을 수 있거든.
어쩌다 생겼나
chrome.sidePanel은 2023 중반 Chrome 114에서 정식으로 들어왔어. 그전엔 붙박이 사이드바를 흉내 내는 방법이 content script로 iframe을 밀어 넣는 것뿐이었지. CSP를 빡세게 건 site 에선 툭하면 깨지고, cross-origin 임베드에선 아예 안 되고, view-source 페이지에선 가망이 없었어. 정식 API가 생기면서 페이지 DOM 이랑 전혀 안 부딪히는, Chrome이 관리하는 자리를 받은 거야.
지금 새로 만든다면 Chrome 114를 최소 기준으로 잡으면 돼. Firefox는 모양이 다른 자기 사이드바 API를 갖고 있어. 이 lesson은 Chrome 얘기야. 브라우저를 넘나드는 전략은 Track 8의 packaging 쪽에서 끝에 살짝 다뤄.
user 눈에 보이는 동작 셋
- toolbar icon을 누르면 열려 (
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })로 켜). 제일 찾기 쉬운 길이야. - API를 불러서 열어 — user가 뭔가를 한 직후에만 돼. 우클릭 메뉴 항목이든, 단축키든,
chrome.sidePanel.open()을 부르는 popup 버튼이든. 원하는 단축키에 묶을 수 있어. - tab을 옮겨도 계속 열려 있어. 보이는 내용은 tab 마다 달라질 수 있지만 (다음 lesson에서 per-tab 이랑 global을 다뤄), tab을 바꿨다고 Chrome이 dock을 알아서 닫진 않아.
안에 뭐가 들어가나
side panel은 그냥 panel.html 이랑 딸린 script, style 이야. 원하는 걸 뭐든 그려도 돼 — 순수 DOM 이든, React/Svelte/Solid 앱이든, 직접 띄운 주소를 가리키는 iframe 이든. 다만 이 HTML도 extension의 보안 정책 아래에서 뜨니까, 외부 script 랑 inline event handler는 CSP를 풀어 주지 않으면 막혀 (Lesson 5에서 다뤄).
storage 쓰는 법은 어디서나 똑같아. 뜰 때 chrome.storage.local을 읽고, 실시간 갱신은 chrome.storage.onChanged로. SW 랑 메시지 주고받는 것도 여태 쓰던 chrome.runtime.sendMessage 그대로고.
ClipDeck 한테 panel이 필요한 이유
popup은 앞의 두 track 동안 제 몫을 톡톡히 했어. 작고, 빠르고, 뻔했지. 그런데 clip이 열 개를 넘어가면 popup이 답답해지고, 쉰 개가 넘으면 못 쓸 지경이 돼. side panel은 늘어나도 버텨 — 시각, 출처 URL, 검색, 그리고 Track 7에서 붙일 inline 편집과 삭제까지 담은 창 높이만 한 목록이니까. popup은 그대로 남아 있어. "오늘 clip 몇 개?" 같은 걸 빠르게 보여 주고 "side panel 열기" 버튼을 얹어 두는 자리로.
frame-src는 계약의 절반뿐이야. Extension page가 iframe을 시도할 수 있게 할 뿐, remote response가 X-Frame-Options나 자기 CSP frame-ancestors로 embedding을 거절할 수 있어. 직접 통제하는 embed route를 쓰고 임의 login page를 frame하지 마.