ํฌ๋กฌ ์ต์คํ ์ (Chrome Extension)์ ๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ(Javascript)๋ก ๋์ด์๊ธฐ๋๋ฌธ์ ์น๊ฐ๋ฐ์ ํด๋ณธ ์ฌ๋์ด๋ผ๋ฉด ๊ทธ๋ฆฌ ํฐ ๋ ธ๋ ฅ์ ๋ค์ด์ง ์๊ณ ๋ ๊ฐ๋ฐ์ ์์ ํ ์ ์๋ค. ํ์ง๋ง ๊ตฌ๊ธ์์ ์ต์คํ ์ ์ ํตํ ๋ณด์์ด์๊ฐ ์๊ธฐ๋ ๊ฒ์ ๋ง๊ธฐ ์ํด ๋ค์ํ ๋ฐฉ์์ผ๋ก ์ต์คํ ์ ์ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋๋ ์ปจํ ์คํธ(context)๋ค์ ์ธ๋ถํ ์์ผ๋์๊ธฐ ๋๋ฌธ์, ์ด๋ฌํ ์ธ๋ถํ๋ ์ปจํ ์คํธ์์ ๊ฐ๊ฐ ์ด๋ค ์์ ์ด ์ํ๊ฐ๋ฅํ์ง, ์ด๋ป๊ฒ ๋ถ๋ฆฌ๋ ์คํฌ๋ฆฝํธ ์ปจํ ์คํธ๊ฐ์ ํต์ ์ ํ ์ ์๋์ง๋ฅผ ์ดํดํ๋ ๊ฒ์ด ์๊ฐ๋ณด๋ค ์ด๋ ต๋ค.
๋ฐ๋ผ์ ๋ณธ ๊ธ์์๋ ์ด ๋ถ๋ถ์ ๊ฐ๋ ์ ๋ํด์ ์์ธํ ๋น๊ต ์ค๋ช ํ๊ณ , ๊ฐ๊ฐ์ ์คํฌ๋ฆฝํธ๋ค์ด ์ ๋์ํ๋๋ก ๋ฏธ๋ฆฌ ์ค์ ๋ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ ํ๋ก์ ํธ๋ฅผ ์๊ฐํ ์์ ์ด๋ค. ๋์ ํฌ๋กฌ ์ต์คํ ์ ์์ ์ฌ์ฉํ๋ ์ธ๋ถ์ ์ธ API๋ค์ด๋ manifest๊ด๋ จ ์ธ๋ถ ์ค์ ๋ค์ ๊ตฌ๊ธ์์ ์ ๊ณตํ๋ ๋ฌธ์์๋ ์ ๋์์๊ณ , ๋ค์ํ sample project์๋ ์ผ์ด์ค๋ณ๋ก ์ ๋์์๊ธฐ๋๋ฌธ์ ๋ฐ๋ก ์ ๋ฆฌํ์ง๋ ์์ ์์ ์ด๋ค.
ํฌ๋กฌ ์ต์คํ ์ ๊ณผ ๊ทธ์ ๊ด๋ จ๋ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋๋ ์ปจํ ์คํธ์ ๋ฐ๋ผ์ page, background, content, injected script 4๊ฐ์ง๋ก ๊ตฌ๋ถ์ ํ ์ ์๋ค. ๊ฐ๊ฐ์ ์ปจํ ์คํธ๊ฐ ์ด๋ป๊ฒ ๋ค๋ฅธ์ง ์ข ๋ ์์ธํ ์ดํด๋ณด๋๋ก ํ์.
chrome.* ๋ชจ๋ API์ ์ ๊ทผ ๊ฐ๋ฅํ์ฌ ์ฌ์ฉ์๊ฐ ๋ณด๊ณ ์๋ ํ์ด์ง์ ์ปจํ ์คํธ์์ ์คํ๋๋ ์คํฌ๋ฆฝํธ
chrome.runtime.* API์ ์ ๊ทผ ๊ฐ๋ฅ์์ ์ค๋ช ํ๋ฏ์ด, ๋ค๋ฅธ ์ปจํ ์คํธ์ script๋ค๊ณผ๋ ์ง์ ์ ์ธ ์ ๊ทผ์ด ๋งํ์๊ธฐ๋๋ฌธ์ ๋ฉ์์ง ํจ์ฑ ๋ฐฉ์์ ํตํด ํต์ ์ ํด์ผํ๋ค. ์ผ์ด์ค๋ณ๋ก ์กฐ๊ธ์ฉ API๊ฐ ๋ค๋ฅด์ง๋ง ์๋ฆฌ๋ ๋์ผํ๋ค.
chrome.runtime.onMessagechrome.runtime.sendMessagewindow.postMessagevar port = chrome.runtime.connect(); port.postMessage์ต์คํ ์ ๋ด๋ถ์ ํ์ผ์ ๋ก๋ํ ๊ฒฝ์ฐ ๋ค์ API ๋ฅผ ํตํด์ ์ก์ธ์ค ๊ฐ๋ฅ
chrome.runtime.getURL(path)Content Scripts ๋ฅผ ๋ก๋ํ๋ ๋๊ฐ์ง ๋ฐฉ์
chrome.tabs.executeScript({ code: '...'})chrome.tabs.executeScript({ file: 'file.js'})manifestํ์ผ์ 'content_scripts' ์น์ ์ ์ถ๊ฐ
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
],
๋๋ฒ๊น ๋ฐฉ๋ฒ
page script์ ์ปจํ ์คํธ์์ ์คํ๋๋ ์ฝ๋๊ฐ ํ์ํ๋ค๋ฉด, content script์ ์ปจํ ์คํธ๋ก๋ถํฐ script๋ฅผ injectํด์ผํ๋ค.
inject๋ script์ ๊ฒฝ์ฐ page script ์ปจํ
์คํธ์์ ์คํ๋๋ฏ๋ก chrome.* ์ ๋ชจ๋ API์๋ํด์๋ ์ ๊ทผ ๋ถ๊ฐ
๋ฐฉ๋ฒ 1: ํ์ผ ๋ก๋
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
manifest์ web_accessible_resources ํญ๋ชฉ์ ์๋์ ๊ฐ์ด ์ถ๊ฐํ๋ฉด, ์ต์คํ
์
ํจํค์ง์ ํฌํจ๋ ํ์ผ์ ๋ก๋ํ๋ ๊ฒ์ด ๊ฐ๋ฅํด์ง๋ค. ๊ตฌ๊ธ ๋ฌธ์ ์ฐธ๊ณ
web_accessible_resources": ["script.js"]
CSP (Content Security Policy)๊ฐ ์ค์ ๋ ์น์ฌ์ดํธ์์๋ ๋ฏธ๋ฆฌ ์ค์ ๋ domain์ ๋ํด์๋ง remote script๋ฅผ load๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ํจ๋ถ๋ก ์๋ฌด ์คํฌ๋ฆฝํธ๋ ๋ก๋ํ๋๊ฒ์ด ๋ถ๊ฐ๋ฅ ํ๋ฏ๋ก, injectํ ์คํฌ๋ฆฝํธ๋ ๋๋๋ก ์ต์คํ ์ ํจํค์ง ์์ ํฌํจ์ํค๋ ๊ฒ์ด ์์ ํ๋ค.
๋ฐฉ๋ฒ2: ์ธ๋ผ์ธ(inline) ๋ฐฉ์
var actualCode = '(' + function() {
// All code is executed in a local scope.
// For example, the following does NOT overwrite the global `alert` method
var alert = null;
// To overwrite a global variable, prefix `window`:
window.alert = null;
} + ')();';
var script = document.createElement('script');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
script.remove();
์์ ์ดํด๋ณธ ๊ฐ๊ฐ์ ์ปจํ ์คํธ๋ฅผ ์ผ๋์ ๋๊ณ , ์ต์คํ ์ ์ ๊ฐ๋ฐํ ๋ ๊ธฐ๋ฅ์๋ฐ๋ผ ์ด๋ค ์ปจํ ์คํธ์์ ์ฝ๋๋ฅผ ์์ฑํ ์ง ์ ํ๊ณ ์์ํ๋ฉด ์ข ๋ ํท๊ฐ๋ฆฐ๋ค. ์๋ฅผ๋ค์ด ํญ๊ด๋ จ ์ํ์ ๋ณด๋ฅผ ์ป๊ณ ์ถ๋ค๋ฉด Chrome Application๊ณผ ๊ณผ๋ จ๋ API๋ฅผ ์ฌ์ฉํด์ผ ํ๊ธฐ๋๋ฌธ์ background script๋ฅผ ์์ฑํด์ผํ๊ณ , ํ์ฌ๋ณด๊ณ ์๋ ํ์ด์ง์ ์๋ก์ด ๋ฒํผ์ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด content script๋ฅผ ์์ฑํด์ผํ๋ ์์ด๋ค.
Plain Javascript๋ง์ผ๋ก๋ ํฌ๋กฌ ์ต์คํ ์ ์ ๊ฐ๋ฐํ๋๋ฐ ์๋ฌด ๋ฌธ์ ๊ฐ ์์ง๋ง, React๋ Vuejs ๋ฑ์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ฉด UI๊ฐ ์๋ Chrome Extension์ ๋ง๋๋๋ฐ ํ๊ฒฐ ์์ฐ์ฑ์ด ๋์์ง๋ค.
๋ณด์ผ๋ฌํ๋ ์ดํธ (boilerplate)
์ค์ ์ ์ฉ ์ํ
์ฐธ๊ณ : Extension Reloader ์ต์คํ ์