다음과 같은 상황을 겪어보신적이 있으실까요?
쿠키는 도메인에 종속적이기 때문에 example.com
에 name=ming
이런 쿠키가 심기면 dev.exmaple.com
과 같은 서브도메인에서는 이 쿠키를 받아주지만 test.com
과 같은 다른 도메인에서는 이 쿠키를 받을 수 없습니다.
가끔 운영에서의 도메인과 개발에서의 도메인이 아예 다르게 되거나 각종 이유로 도메인이 다른 곳에 name=ming
쿠키를 심고 싶어질때가 있죠.
그것을 위해서 만든 chrome extension 개발기를 오늘 얘기해보려고 합니다.
기존에는 bookmarklet
을 이용하여 북마크를 클릭하면 document.cookie
를 조작하는식으로 간단하게 개발할 예정이었습니다.
하지만 기존의 cookie는 httpOnly
옵션이 붙어서 내려오기때문에 document.cookie를 통해서 조회를 할 수가 없었는데요.
반면에 chrome extension의 chrome.cookies
를 이용하면 httpOnly의 쿠키까지 조회를 할 수 있어 이 방법을 채택하였습니다.
chrome extension은 크게 3가지로 popup, background, content_script로 나눠집니다.
popup은 말 그대로 우리가 크롬에있는 아이콘을 클릭하면 팝업이 열리게 되죠? 그 ui에 대한것을 담당하는것이 popup입니다.
보통 Popup.js에서 특정 버튼을 클릭하면 sendMessage
를 통하여 event를 emit하고 background나 content_script에서 받도록 많이 이용을 합니다.
background는 service-worker라고도 불리는 요소입니다.
특징으로는 크롬 익스텐션이 켜질때 실행되며 여러개의 탭이 존재하더라도 단 한번만 초기화되는 녀석입니다. 주로 extension내에서 이벤트를 관리할때 이용합니다.
이는 익스텐션 내부에 존재하는 실제 외부의 dom을 건드리거나 할수는 없습니다.
content_script는 외부 웹페이지에서 실행되는 스크립트입니다.
예를 들어 크롬 익스텐션을 https://example.com
에서 켰다면 이 페이지의 dom에 접근하거나, storage정보같이 페이지의 정보를 가져올 수 있습니다.
이 3개에 대해서 간단하게 안 상태로 어떻게 chrome extension을 만들었는지 알아보도록 하죠.
가장 먼저 알아볼 것은 manifest.json
입니다. chrome extension에서의 설정 파일이라고 생각하시면 됩니다.
각각의 manifest 속성을 설명하기에는 너무 많기에 주로 이용하는것에 대한것만 설명할 예정입니다. 나머지 속성들은 다음 docs에서 확인하시면 됩니다.
https://developer.chrome.com/docs/extensions/mv3/manifest/
필수로 적어야되는 필드입니다.
name은 이 익스텐션의 이름, version은 익스텐션의 버전, manifetst_version은 현재 3까지 존재하며 말그대로 manifetst.json의 버전을 의미합니다.
위에 올려드린 docs는 3버전에 대한 것입니다.
다음은 permission 필드, 즉 권한에 관한 내용입니다.
chrome extesion에서는 사용자의 크롬에 얼마나 접근이 가능하게 할지 설정하는 permission tab이 존재합니다.
https://developer.chrome.com/docs/extensions/mv3/declare_permissions/#permissions
역시 tabs에는 여러가지 종류가 있는데요. 이번에는 cookie를 다룰때 이용하게 된 permission들을 알아보겠습니다.
"permissions": [
"cookies",
"storage",
"tabs",
],
chrome.tabs
를 위한 tabs
,
chrome extesion내의 storage
이용을 위한 storage
,
chrome.cookies
를 이용하기 위한 cookies
이렇게 3개를 이용하였습니다. 추후에 각각 어떻게 사용됬는지는 popup, background, content_script 영역에서 다룰 예정입니다.
말그대로 chrome extension에서 사용될 icon을 의미합니다. 프로프트 지니
를 예시로 보면 chatgpt icon이 있는것을 확인할 수 있죠.
"icons": {
"16": "images/16.png",
"32": "images/32.png",
"48": "images/48.png",
"128": "images/128.png"
},
말그래돌 host에 대한 권한을 설정하는겁니다.
특정한 도메인을 지정하는게 아니라 매치 패턴을 이용하여 정의를 할 수 있습니다. 이렇게 설정하면 매치패턴에 해당되는 도메인만 크롬익스텐션을 사용할 수 있도록 설정하는겁니다.
"host_permissions": [
"*://developer.mozilla.org/*",
"*://*.example.com/*"
]
위에서 설명했던 background에 대한 파일을 정의하는곳입니다.
"background": {
"service_worker": "./background.js",
},
다음과 같이 실제 background파일이 있는곳을 경로로 잡아주시면 됩니다.
이 크롬익스텐션에 대한 단축키를 설정할 수 있습니다.
저는 크롬익스텐션을 열기위한 단축키로 command + I
를 이용중입니다.
"commands": {
"_execute_action": {
"suggested_key": {
"default": "Ctrl+I",
"mac": "Command+I"
}
}
},
"content_scripts": [
{
"matches": ["https//*.example.com"],
"js": ["./content_script.js"]
}
]
역시 위에서 봤던 content_scripts입니다.
content_scripts
는 외부 웹페이지에서 실행하는 스크립트이기에 어떤 웹페이지에서만 스크립트를 실행할지 matches
옵션을 통해서 설정할 수 있습니다.
js
는 위의 background와 같이 content_script.js
의 파일경로를 적어주시면 됩니다.
일단 쿠키 옮기기를 어떤 방식으로 설계했는지 부터 보시죠.
popup에서는 간단하게 그 쿠키에 대한 html
,css
로 ui를 잡고 js에서 content_script, background와 연결되는 로직을 작성해주었습니다.
위의 설계와 함께 보면 popup에서는 먼저 초기화가 되자마자 background
에 메시지를 보냅니다.
chrome.runtime.sendMessage
를 이용하면 background에 메시지를 보내면 popupOpen: true
를 보내 background
에서 로직을 처리하도록 진행합니다. 그런 후 응답을 받으면 날짜, 도메인을 수정하여 cookieValue에 저장해줍니다.
let cookieValue = null;
let originCookieValue = null;
chrome.runtime.sendMessage({popupOpen: true}, (res) => {
chrome.storage.sync.get("cookie", ({cookie}) => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
originCookieValue = cookie ? cookie : null;
cookieValue = cookie ? `a=${cookie.value};expires=${tomorrow.toUTCString()};domain=.test.com` : null; // 도메인 변경하여 설정
})
})
$loginButton.addEventListener('click', () => {
if(cookieValue) {
chrome.tabs.query({}, tabs => {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, {cookie: cookieValue});
});
});
alert('성공적으로 등록되었습니다. 새로고침을 진행해주세요.');
window.close();
} else {
alert('쿠키가 존재하지 않습니다.');
window.close();
}
})
chrome.tabs.query({}, tabs => {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, {cookie: cookieValue});
});
});
// content_script에 메시지를 보내는 방식입니다.
클릭을 할시 content_script
를 향해 sendMessage를 해줍니다. 이때 날짜, 도메인에 변경된 cookieValue
를 content_script에 보내줍니다.
$logoutButton.addEventListener('click', () => {
chrome.tabs.query({}, tabs => {
tabs.forEach(tab => {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const expiredCookieValue = `a=${originCookieValue.value};expires=${yesterday.toUTCString()};domain=.test.com`
chrome.tabs.sendMessage(tab.id, {cookie: expiredCookieValue});
});
alert('성공적으로 로그아웃 되었습니다. 새로고침을 진행해주세요.');
window.close();
});
로그인버튼 클릭했을시와 비교하면 content_script
에 cookieValue를 보내는것은 동일하지만 로그아웃시에는 어제의 날짜를 담아 보내, 바로 expire시키도록 설정합니다.
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
if(message.popupOpen) {
chrome.cookies.get({url: 'https://example.com', name: "a"}, (cookie) => {
if(cookie) {
chrome.storage.sync.set({ cookie: cookie });
} else {
chrome.storage.sync.set({cookie: null});
}
});
}
});
popup에서 보낸것을 chrome.runtime.onMessage.addListener
를 통하여 이벤트를 받을 수 있습니다.
message에서 popupOpen의 true값을 읽고 chrome.cookies
를 이용하여 기존에는 가지고오지 못했던 httpOnly
가 적용된 쿠키까지 가지고 올 수 있게 됩니다.
그런 후 다시 popup에서 cookie값을 이용하기위해 chrome의 storage에 저장합니다. (이는 chrome extension의 storage입니다.)
chrome.runtime.onMessage.addListener(msgObj => {
document.cookie = msgObj.cookie;
});
놀랍게도 이게 전부입니다.. content_script에서는 이제 우리가 옮겨줄 페이지 *.test.com
에 쿠키를 심어주는 스크립트가 존재합니다.
background와 동일하게 chrome.runtime.onMessage.addListener
로 받고 document.cookie
를 해주시면 됩니다.
chrome extension을 배포하려면 인증도 받아야하고 이것저것 할게 많습니다..
하지만 우리는 이것을 크롬 웹스토어 올려서 이용할것이 아니라 개발 생산성
증진을 위해서 이용하는것이기에 이렇게 할 필요가 없습니다.
그러면 어떤 방법으로 할 수 있을까요?
chrome extension에는 개발자 모드가 있습니다. 밑의 사진에 우측 상단을 보시면 확인할 수 있습니다. chrome에서 확장프로그램 관리를 클릭시 다음과 같이 들어갈 수 있습니다.
그후에 압축해제한 확장 프로그램을 로드합니다.
를 클릭하고 만들어 놓은 extension을 넣어주면 크롬 웹스토어에서 다운로드없이 사용을 할 수 있습니다.
이제는 이 익스텐션을 이용하고 싶은 모두에게 이용할 수 있도록 해야겠죠?
프로젝트를 tag로 관리하면 버전별로 source code를 zip파일로 받을 수 있기때문에 이를 압축해제하여 chrome extension developer mode로 이용할 수 있습니다.
실제로 chrome extension에서 제공하는 여러 api를 통해서 개발생산성을 향상시킬 수 있는 여러가지 툴들을 만들 수 있습니다.
popup, background, content_script의 역할과 공식문서의 api만 잘 봐도 문제없이 개발이 가능하기에 한번쯤 만들어보시면 좋을것 같습니다.
세션도 다뤄주실수 있나요 .. ?