이전 포스팅 '2.2 크롬 익스텐션 만들어 보기 Part I - 웹 페이지 삽입 스크립트'에서 타겟 URL의 웹 페이지와 함께 동작하는 content 스크립트의 작성 방법과 역할에 대해 살펴보았다.
2024.02.02 - [웹 프로그래밍 고급 주제들] - 2.2 크롬 익스텐션 만들어 보기 Part I - 웹 페이지 삽입 스크립트
2.2 크롬 익스텐션 만들어 보기 Part I - 웹 페이지 삽입 스크립트
이전 포스팅에서 브라우저 익스텐션(크롬 기준)의 기본적인 구성 파일들을 살펴보고 간단한 익스텐션도 만들어 보았다. 그리고 코딩한 익스텐션을 웹 브라우저에 설치하는 방법도 살펴보았다.
youngbyte.tistory.com
이번 포스팅에서는 브라우저 익스텐션의 구성 요소 중 서비스 워커(백그라운드 스크립트)에 대해 살펴보도록 하겠다.
백그라운드 스크립트는 사용자에게 보여지는 인터페이스를 가지고 있지 않다. 웹 브라우저의 익스텐션 프로세스에 의해 관리되며 아래와 같이 브라우저의 서비스 워커 스레드로 확인할 수 있다(크롬 브라우저 기준으로 Chrome 설정 및 제어 > 도구 더보기 > 작업 관리자).
참고로, 이어지는 포스팅에서 언젠가 브라우저의 구조에 대해 정리할 기회를 가져보려한다. 최신 웹 브라우저들은 프로세스들을 관리하는 관리자이고 특히 웹 언어(HTML, JavaScript, WASM) 측면에서는 런타임 환경을 제공하는 가상 머신적인 측면도 가지고 있다.
익스텐션에서 서비스 워커의 역할은 크게 두 가지로 요약할 수 있다. 첫째로 페이지 삽입 스크립트(content 스크립트)와 익스텐션 UI(popup 스크립트)가 필요로하는 서비스(예: 외부 서버로 데이터 요청, 브라우저 저장 공간에 데이터 저장 등)를 제공하는 프록시(proxy, 대리인) 서버 역할을 한다. 둘째로 페이지 삽입 스크립트와 익스텐션 UI 사이에 데이터, 메시지를 중계하는 역할을 수행한다. '2.1 크롬 익스텐션 만들어 보기 Part I - 기본 구조'의 그림을 보면 그 역할을 좀 더 구체적으로 살펴볼 수 있다.
부족한 설명이지만 서비스 워커의 역할에 대해서 어렴풋하게 나마 실마리를 잡았을 것으로 생각한다. 또 다른 익스텐션을 작성하면서 서비스 워커를 어떻게 작성하는지 직접 경험해보자.
살펴볼 예제는 구글에서 공식적으로 제공하는 익스텐션 작성 예제 중 서비스 워커 하나로 구성되어 있는 Focus mode(https://developer.chrome.com/docs/extensions/get-started/tutorial/scripts-activetab) 예제이다. 익스텐션의 전체 구성을 먼저 살펴보면 아래와 같다.
먼저 익스텐션 파일들을 저장할 디렉토리(폴더)를 하나 생성한다. 여기서는 Focus_mode라는 이름으로 생성하였다. 이 디렉토리를 사용하는 에디터로 열고 먼저 익스텐션의 틀을 기술하는 manifest 파일을 아래와 같이 작성한다.
{
"manifest_version": 3
, "name": "Focus Mode"
, "description": "Enable focus mode on Chrome's official Extensions and Chrome Web Store documentation"
, "version": "1.0"
, "icons": {
"16": "images/icon-16.png"
, "32": "images/icon-32.png"
, "48": "images/icon-48.png"
, "128": "images/icon-128.png"
}
, "background": {
"service_worker": "background.js"
}
, "action": {
"default_icon": {
"16": "images/icon-16.png"
, "32": "images/icon-32.png"
, "48": "images/icon-48.png"
, "128": "images/icon-128.png"
}
}
, "permissions": [
"activeTab"
, "scripting"
]
, "commands": {
"_execute_action": {
"suggested_key": {
"default": "Ctrl+B"
, "mac": "Command+B"
}
}
, "custom_cmd1": {
"suggested_key": {
"default": "Ctrl+Shift+Y"
, "mac": "Command+Shift+Y"
},
"description": "Run \"custom cmd 1\" on the current page."
}
}
}
<Focus_mode/manifest.json>
manifest_version, name, description, version, 그리고 icons 필드들은 지난 포스팅들에서 다루었던 두 예제들에서 살펴보았으므로 새롭게 등장하는 내용들에 대해서 아래와 같이 정리하였다.
항목 | 내용 | 필수 여부 |
background | 웹 브라우저의 백그라운드에서 실행될 서비스 워커 스크립트 파일을 지정한다. | Y(서비스 워커 사용 시) |
action | 크롬 익스텐션의 아이콘을 클릭했을 때 실행 대상이 되는 아이콘(default_icon) 등을 지정한다. | N |
permission | 익스텐션 동작에 필요한 권한들을 명시한다. | Y |
commands | 사용자 커맨드를 등록한다. 커맨드 이름과 커맨드 실행 단축키 항목(suggested key), 그리고 커맨드 설명(description) 항목들로 구성된다. | N |
manifest의 내용 중 permission 항목과 commands 항목은 좀 더 살펴볼 내용들이 있다. 먼저 permission 항목에는 브라우저 익스텐션 동작에 필요한 권한들을 기술한다. 이번 예제의 manifest 파일에 기술된 내용들인 activeTab 권한은 브라우저의 탭(tab) 중 현재 탭(active tab)에 익스텐션이 접근할 수 있는 권한이고 scripting 권한은 서비스 워커가 현재 탭의 페이지에 동적으로 HTML, CSS, JavaScript 등의 스크립트를 삽입하는 것을 허용하는 권한이다.
commnds 항목에는 사용자가 익스텐션에 전달할 수 있는 커맨드들이 기술된다. commands 항목의 요소들은 다시 아래와 같은 형태로 기술되어 있다.
커맨드 이름: {
"suggested_key": {
"default": 기본 단축기(윈도우 운영체제)
, "mac": 맥 운영체제에서의 단축기
},
"description": 커맨드 설명
}
예제에 사용된 첫 번째 커맨드인 _execute_action 커맨드는 웹 브라우저에 의해 예약되어 있는 커맨드로서 익스텐션의 아이콘을 클릭했을 때와 같은 메시지를 서비스워커에 전달한다. 이렇게 받은 메시지를 서비스 워커가 어떻게 처리하는지에 대해서는 조금 뒤에 서비스 워커 스크립트를 살펴보면서 설명하도록 하겠다.
두 번째 커맨드 custom_cmd1은 사용자가 자유롭게 설정하고 사용할 수 있는 커맨드이다. 커맨드의 내용을 보면 Ctrl+Shift+Y키를 단축키로 지정하고 있는데 사용자가 지정된 단축키를 입력하면 서비스 워커 스크립트의 chrome.commands API가 호출된다. 호출된 API에서 어떤 처리 과정이 일어나는지 서비스 워커 스크립트를 살펴보자.
예제에서 사용하는 아이콘들은 구글에서 예제를 위해 제공하는 아이콘들을 다운로드 받아서 사용하면 된다. 익스텐션 디렉토리 안에 images 폴더를 만들고 다운로드 받은 아이콘들을 저장한다.
이제 본격적으로 서비스 워커 스크립트(일반적으로 background 스크립트라고 부른다)를 만들어보자. 익스텐션 구성 파일들을 저장하는 디렉토리에 background.js 파일을 만들고 아래 내용들을 입력한다.
chrome.runtime.onInstalled.addListener(() => {
chrome.action.setBadgeText({
text: "OFF"
});
});
const extensions = 'https://developer.chrome.com/docs/extensions';
const webstore = 'https://developer.chrome.com/docs/webstore';
chrome.action.onClicked.addListener(async (tab) => {
if (tab.url.startsWith(extensions) || tab.url.startsWith(webstore)) {
// Retrieve the action badge to check if the extension is 'ON' or 'OFF'
const prevState = await chrome.action.getBadgeText({tabId: tab.id});
// Next state will always be the opposite
const nextState = prevState === 'ON' ? 'OFF' : 'ON';
// Set the action badge to the next state
await chrome.action.setBadgeText({
tabId: tab.id
, text: nextState
});
if (nextState === 'ON') {
// Insert the CSS file when the user turns the extention on
await chrome.scripting.insertCSS({
files: ["focus_mode.css"]
, target: { tabId: tab.id }
});
}
else if (nextState === 'OFF') {
// Remove the CSS file when the user turns the extension off
await chrome.scripting.removeCSS({
files: ["focus_mode.css"]
, target: { tabId: tab.id }
});
}
}
});
chrome.commands.onCommand.addListener((command) => {
console.log(`Command: ${command}`);
});
<Focus_mode/background.js>
가장 첫 줄의 chrome.runtime.onInstalled API는 익스텐션이 처음 설치되었을 때 호출된다. 익스텐션의 아이콘에 'OFF'라는 문자열을 출력(setBadgeText)하도록 하고 있다.
그 다음 등장하는 두 URL들은 백그라운드 스크립트가 동작할 타겟 페이지들을 지정하기 위한 데이터들이다.
chrome.action.onClicked API는 익스텐션의 manifest.json 파일에서 action 항목에 지정한 아이콘들을 클릭했을 때 호출된다. 그 내용을 살펴보면 현재 탭의 URL을 확인(tab.url.startsWith(...))한 후, 현재 익스텐션 아이콘에 표시된 문자열(getBadgeText(...))이 ON인지 OFF인지 확인한다. 다음 상태(nextState)는 이전 상태(prevState)의 반대(ON인 경우 OFF, OFF인 경우 ON)인데 만약 다음 상태(nextState)가 ON이라면 지정한 CSS 파일을 현재 탭의 페이지에 삽입하고(chrome.scripting.insertCSS), 만약 다음 상태가 OFF라면 지정한 CSS 파일을 현재 탭의 페이지에서 제거(removeCSS)하도록 하고 있다. 페이지에 CSS 스크립트를 동적으로 삽입하고 제거할 필요가 있기 때문에 manifest.json 파일의 permission 항목에 scripting 권한을 요구했음을 기억할 것이다. 이 scripting 권한이 여기에 사용된다.
서비스 워커 스크립트의 마지막에 등장하는 onCommand API는 사용자가 지정한 커스텀 커맨드를 위한 것이다. manifest.json 파일의 commands 항목의 custom_cmd1에 지정된 단축키를 사용자가 입력하면 이 onCommandAPI에 기술된 내용이 실행된다. 예제에서는 단순히 커맨드의 이름을 웹 브라우저의 디버그 화면에 출력하도록 하고 있다.
마지막으로 focus_mode.css 파일을 아래와 같이 작성하자.
body > .scaffold > :is(top-nav, navigation-rail, side-nav, footer)
, main > :not(:last-child)
, main > :last-child > navigation-tree
, main .toc-container {
display: none;
}
main > :last-child {
margin-top: min(10vmax, 10rem);
margin-bottom: min(10vmax, 10rem);
}
<Focus_mode/focus_mode.css>
CSS파일의 내용은 페이지에서 특정 영역을 감추고(display: none), 페이지의 main 요소의 마지막 요소(:last-child)의 여백을 조정하는 것이다.
CSS까지 작성하여 익스텐션의 모든 구성 파일들을 갖추었다. 브라우저의 익스텐션 관리자(크롬의 경우 Chrome 맞춤설정 및 제어(브라우저 우상단의 세로점 버튼) > 확장 프로그램 > 확장 프로그램 관리 > 개발자 모드 ON)로 들어가 '압축 해제된 확장 프로그램을 로드합니다.' 버튼을 클릭하여 익스텐션 파일들이 들어있는 디렉토리를 선택하여 업로드하면 된다.
크롬의 확장 프로그램 버튼을 클릭하면 Focus mode 예제의 아이콘을 볼 수 있을 것이다.
이제 익스텐션이 활성화되는(정확히는 익스텐션의 서비스 워커가 적용되는) URL인 아래 페이지를 방문해보자.
https://developer.chrome.com/docs/extensions/get-started/tutorial/scripts-activetab
활성 탭에 스크립트 삽입 | Extensions | Chrome for Developers
현재 페이지의 스타일을 단순화하는 방법을 알아보세요.
developer.chrome.com
이제 익스텐션 아이콘을 클릭하거나 단축키 Ctrl-B를 입력해보자. 아래와 같이 익스텐션 아이콘의 문자열이 ON으로 변경되고 페이지 네비게이터가 사라짐을 볼 수 있다.

익스텐션의 백그라운드 스크립트(서비스 워커)는 브라우저의 백그라운드에서 대기하고 있다가 지정한 사용자 입력을 받으면 깨어나 사용자 입력에 해당하는 코드를 실행시킨다. 여기 예에서와 같이 Ctrl+B를 입력하면 background.js 스크립트의 chrome.action.onClicked API가 호출된다.
익스텐션 관리자 화면으로 들어가( 크롬의 경우 Chrome 맞춤설정 및 제어(브라우저 우상단의 세로점 버튼) > 확장 프로그램 > 확장 프로그램 관리) 뷰 검사 서비스 워커 링크를 클릭하자. 그러면 서비스 워커의 디버그 화면이 나타난다.
이제 Ctrl+Shift+Y를 입력하여 커스텀 커맨드를 입력해보자. 커스텀 커맨드 단축키를 입력하면 서비스 워커 스크립트 background.js의 chrome.commands.onCommand API가 호출되고 해당 API의 리스너 함수에 기술한 내용인 메시지 출력 명령(console.log)이 실행된다. 이와 같은 방식으로 커스텀 커맨드를 지정하고 서비스 워커를 통해 실행시킬 수 있다.
이번 포스팅에서는 아래 내용들에 대해 살펴보았다.
· 서비스 워커의 역할
· 서비스 워커 스크립트(백그라운드 스크립트) 작성 방법
· 서비스 워커의 동작
이전 두 포스팅, 그리고 이번 포스팅까지 세 개의 포스팅에 걸쳐서 크롬 브라우저 익스텐션을 구성하는 기본적인 요소들인 manifest 파일, 익스텐션 UI(popup 스크립트), 페이지 삽입 스크립트(content 스크립트), 그리고 서비스 워커(background 스크립트)에 대해 기본적인 내용들을 예제와 함께 살펴보았다. 이어지는 포스팅에서는 이들 요소들과 요소들 사이의 관계를 다시 정리해보도록 하겠다.
■
'웹 프로그래밍 고급 주제들' 카테고리의 다른 글
3.1. 크롬 익스텐션 만들어보기 Part II - 서비스 워커 심화 (0) | 2024.03.02 |
---|---|
2.4. 크롬 익스텐션 만들어보기 Part I - 구조 및 예제 정리 (0) | 2024.02.20 |
2.2 크롬 익스텐션 만들어 보기 Part I - 웹 페이지 삽입 스크립트 (0) | 2024.02.02 |
2.1 크롬 익스텐션 만들어 보기 Part I - 기본 구조 (0) | 2024.01.30 |
1. 프롤로그 - 크롬 익스텐션 개발, 할 만 한가? (2) | 2024.01.22 |
댓글