C.W.K.
Stream
Lesson 04 of 06 · published

Hello ClipDeck — 첫 동작하는 popup

~14 min · clipdeck, popup, chrome.tabs, first-working-demo, hands-on

Level 0Extension 입덕
0 XP0/54 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Popup 은 HTML 이야 — 그게 트릭. 'Chrome 만의 뭔가' 라고 생각하지 말고 chrome.* 접근권 가진 작은 webpage 라고 생각하기 시작하면, 나머지 extension API 도 더 이상 이질감 없어."

Manifest 에서 동작하는 popup 까지

Lesson 2 의 manifest 가 action block 을 선언했어:

"action": {
  "default_popup": "popup.html",
  "default_title": "ClipDeck"
}

default_popup field 가 약속이야 — toolbar icon 클릭하면 Chrome 이 그 이름의 HTML 파일을 popup window 안에 렌더. Chrome 은 그 파일 내용에 무관심해, 유효한 HTML page 이기만 하면 — "Hello world" 정적 문자열도 가능, React bundle 도 가능, 또는 — 여기서 만들 — active tab 읽어서 title 보여주는 얇은 shell.

이 lesson 은 clipdeck/ 에 두 새 파일 저장: popup.html / popup.js. Reload, 클릭, ClipDeck 이 처음으로 뭔가 해.

popup.html — 최소 skeleton

Popup 은 평범한 HTML 문서야, 알아둘 두 가지만:

  • Popup window 는 content 에 자동 크기 조정, 약 800×600 까지. body 에 명시적 width 박아서 안정화.
  • MV3 는 inline JavaScript 금지 (Content Security Policy 통해). Script 는 별도 파일에 두고 <script src="popup.js"> 로 참조. <script>alert()</script> 같은 inline script 안 돼, inline onclick handler 도 안 돼. 모든 MV2-시대 튜토리얼 reader 가 딱 한 번씩 이걸로 걸려.

popup.js — Active tab 읽기

chrome.tabs.query 가 "지금 user 가 보고 있는 tab 이 뭐야?" 물어보는 API. MV3 에선 Promise 반환 (또는 callback — 둘 다 됨). Filter property 셋이 중요:

  • active: true — 자기 window 안에서 focus 가짐
  • currentWindow: true — 지금 이 window, background window 아님
  • 결과는 항상 배열 — 보통 length 1, 근데 MV3 가 보장하지는 않아. tabs[0]?.title 같은 식 또는 default 값 있는 array destructuring 으로 방어.

Popup script 가 하는 일은 작아: query / title 읽기 / 렌더. 그게 popup.js 본문 전체.

첫 클릭

popup.htmlpopup.jsclipdeck/ 에 저장한 다음:

  1. chrome://extensions 열기.
  2. ClipDeck 카드의 reload (↻) 버튼 클릭.
  3. Toolbar 의 ClipDeck 아이콘 클릭.

작은 popup window 가 열려. Script 가 돌아. 그 tab 의 page title 이 popup 에 떠. Tab 바꾸고 아이콘 다시 클릭 — 새 tab 의 title 표시. chrome.tabs.query 가 popup 열릴 때마다 상태 read — 캐싱 없음, stale data 없음, 그 순간 Chrome UI 가 아는 거 그대로.

이 작은 데모가 왜 중요해

이게 모든 미래 ClipDeck 기능이 따를 형태:

  1. Manifest 가 surface 선언 (popup / side panel / content script / options page).
  2. 그 surface 의 HTML load.
  3. 그 surface 안의 JavaScript 가 chrome.* API 호출 — Chrome 상태 read 또는 변경.
  4. UI update.

Track 2 가 background service worker 추가 (UI 없는 surface). Track 3 가 content script 추가 (host page 안의 UI 존재). Track 4 가 popup 을 더 풍부한 side panel 로 교체. Track 5 가 popup 에 selection capture + storage save 를 wire. 다 위 4 단계 패턴 위에 쌓는 거.

Popup 은 chrome.* 접근권 가진 HTML. 트릭은 "Chrome 만의 뭔가" 가 아니라 — 추가 초능력 가진 작은 webpage. 다른 모든 surface (side panel / options / content script) 도 같은 원리.
Popup 이 처음으로 현재 page title 렌더하는 거 보면, 반응은 어린아이가 첫 alert() 봤을 때랑 똑같아 — "잠깐, 브라우저가 진짜 내 말 듣네?" 그 느낌 잡고 있어. browser extension 이 존재하는 이유 전부. Pippa Chrome Embed v0.1 이 그 같은 놀라움을 household scale 로 늘린 거 — 같은 surprise, 그저 hardcoded query 대신 cwkPippa 의 brain 통과해서 흐를 뿐.

Track 1 의 조기 회고

Popup 이 동작하면, ClipDeck 은 "disk 위의 파일" 에서 "동작하는 Chrome extension" 으로 선을 넘은 거야. Lesson 5 와 6 이 security model 과 gotchas 다루지만 — ClipDeck 자체는 이 시점부터 살아 있어. 매 미래 track 이 한 조각씩 더해. 이 데모의 작음에 속지 마: 여기서 dev loop 가 닫혀.

Code

clipdeck/popup.html — external script reference 가진 최소 HTML·html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>ClipDeck</title>
  <style>
    body {
      font-family: -apple-system, system-ui, sans-serif;
      font-size: 13px;
      width: 240px;
      padding: 12px;
      margin: 0;
    }
    h1 { font-size: 14px; margin: 0 0 8px 0; }
    .title { color: #555; word-break: break-word; }
    .empty { color: #999; font-style: italic; }
  </style>
</head>
<body>
  <h1>ClipDeck</h1>
  <div id="page-title" class="empty">Reading current tab…</div>
  <script src="popup.js"></script>
</body>
</html>
clipdeck/popup.js — chrome.tabs.query 가 active tab 읽음·javascript
// clipdeck/popup.js
// Runs every time the popup opens.
// Reads the active tab in the current window, renders its title.

async function showCurrentTabTitle() {
  const [activeTab] = await chrome.tabs.query({
    active: true,
    currentWindow: true,
  });

  const titleEl = document.getElementById("page-title");
  if (activeTab?.title) {
    titleEl.textContent = activeTab.title;
    titleEl.classList.remove("empty");
    titleEl.classList.add("title");
  } else {
    titleEl.textContent = "(no active tab — open a real page and try again)";
  }
}

showCurrentTabTitle();
MV3 CSP gotcha — inline script 차단, 외부 파일만 허용·html
<!-- Rejected by MV3 Content Security Policy: -->
<script>alert("hi")</script>

<!-- Allowed — external file reference only: -->
<script src="popup.js"></script>

External links

Exercise

위 코드로 clipdeck/popup.html / clipdeck/popup.js 만들어. chrome://extensions 의 ClipDeck 카드 reload. Toolbar 아이콘 클릭. Popup 이 현재 tab 의 title 보여줘야 함 — 어떤 tab 에서 테스트했고 어떤 title 떴는지 메모. 그 다음 다른 tab 두 개 (아무 site 나) 바꿔서 다시 테스트. 매번 title 업데이트 됐어? 됐으면 ClipDeck 이 chrome.tabs.query 를 성공적으로 처음 쓴 거야. 안 됐으면 Errors panel 이 이유 알려줘 — 보통 popup.js typo, <script src> path 안 맞음, 또는 inline JS 썼다면 CSP 위반.
Hint
Popup 이 비어 보이거나 "Reading current tab…" 만 보이면, popup 자체에 DevTools 열어: popup 안에서 우클릭 → Inspect. Popup 이 자기 DevTools window 갖고 — popup.js 로그랑 error 가 거기 떠. tabs[0] 이 undefined 인 경우 보통 popup 이 chrome://extensions 자체에서 열린 거 (평범한 active tab 없음). google.com 같은 평범한 page 에서 테스트.

Progress

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

댓글 0

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

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