
링크를 잘 저장하고, 다시 꺼내보는 습관을 만들고 싶었습니다.
매번 Notion에 붙여넣는 것도 번거롭고, 북마크는 어느새 정리가 안 되더라고요.
“그냥, 브라우저 안에서 바로 정리할 수 있으면 좋지 않을까?”
사실 크롬 익스텐션을 만들게 된 가장 큰 이유는,
매번 링크를 복사해서 링크 드라퍼 사이트에 들어가 붙여넣는 일조차 번거롭게 느껴졌기 때문입니다.
그래서 브라우저에서 버튼 한 번으로 바로 저장할 수 있는 기능이 필요했습니다.
이렇게 시작된 링크 드라퍼 크롬 익스텐션 개발기.
이번 글에서는 아이디어 구상부터 초기 세팅, 구조 설계, OG 정보 추출, 쿠키 인증 처리까지 실제로 구현한 내용을 정리해보려 합니다.
익스텐션 개발이 처음이라, 익숙한 스택으로 시작하고 싶었습니다.
그래서 선택한 조합은:
src/
├── background/ # 백그라운드 서비스 워커
├── content/ # Content Script
├── popup/ # 툴바 팝업 UI
├── assets/ # 정적 자원
├── manifest.json # 크롬 익스텐션 설정
{
"manifest_version": 3,
"name": "Link Dropper",
"version": "1.0",
"permissions": ["tabs", "storage", "bookmarks"],
"host_permissions": ["<all_urls>"],
"action": {
"default_popup": "popup/index.html"
},
"background": {
"service_worker": "background/index.ts"
}
}
| 속성명 | 설명 |
|---|---|
manifest_version | 매니페스트 버전. 크롬에서는 현재 3을 권장하고 있으며, service_worker를 사용해야 함 |
name | 익스텐션의 이름. 크롬 웹스토어나 익스텐션 관리 화면에 표시됨 |
version | 버전 정보. 숫자 혹은 문자열로 설정하며, 업데이트에 사용됨 |
permissions | 크롬에서 제공하는 API 권한 목록 (tabs, storage, bookmarks 등) |
host_permissions | 외부 URL 도메인 접근 권한. <all_urls>는 모든 도메인 허용 |
action.default_popup | 브라우저 툴바 클릭 시 열리는 팝업 HTML 경로 |
background.service_worker | 백그라운드에서 실행될 JS/TS 파일. MV3에서는 background script 대신 service_worker만 지원됨 |
팝업 UI는 Mantine UI를 사용했습니다.
좁은 화면에 알맞은 컴포넌트를 빠르게 조립할 수 있어서 굉장히 유용했어요.
팝업에는 다음 기능들을 넣었습니다:
단순 URL만 저장하면 보기 불편해서, OG 데이터를 직접 추출했습니다.
탭 내부에서 실행되는 Content Script에서 아래 방식으로 처리했어요:
const metaTags = document.querySelectorAll('meta[property^="og:"]');
const ogInfo: Record<string, string> = {};
metaTags.forEach((meta) => {
const prop = meta.getAttribute("property") ?? "";
const content = meta.getAttribute("content") ?? "";
ogInfo[prop] = content;
});
대부분 잘 작동하지만, 예외적인 사이트도 많아서 fallback 처리를 꽤 많이 추가했습니다.
chrome.bookmarks.getTree() API를 활용해 북마크 폴더 트리를 가져왔습니다.
그리고 그 중 폴더만 필터링해서 드롭다운에 표시했습니다.
chrome.bookmarks.getTree((tree) => {
const folders = extractFolders(tree);
setFolderOptions(folders);
});
초기에는 nested 구조로 보여주려다, UI가 너무 복잡해져서 평면 구조로 정리했어요.
Zustand로 상태를 관리하면서도, 일부 정보는 크롬의 storage.local API에 저장했습니다.
대표적으로는:
chrome.storage.local.set({ lastUsedFolder: "folder-id" });
chrome.storage.local.get("lastUsedFolder", ({ lastUsedFolder }) => {
console.log(lastUsedFolder);
});
비동기 API라 Promise 기반 유틸을 만들면 사용하기 더 편합니다.
익스텐션에서 로그인 세션을 유지하려면, 서버의 session_token 쿠키를 읽어야 했습니다.
httpOnly 쿠키라 JS에서 읽을 수 없기 때문에, chrome.cookies.get() API를 사용했습니다.
chrome.cookies.get({ url: "http://localhost:4000", name: "session_token" }, (cookie) => {
if (cookie) {
chrome.storage.local.set({ session_token: cookie.value });
}
});
그리고 fetch 요청 시 Authorization 헤더에 붙여서 사용합니다:
chrome.storage.local.get("session_token", async ({ session_token }) => {
const res = await fetch("http://localhost:4000/api/me", {
headers: {
Authorization: `Bearer ${session_token}`,
},
});
});
🔒 주의:
content script에서는chrome.cookiesAPI를 사용할 수 없습니다. 반드시background또는popup에서 호출해야 합니다.
MV3 기반 크롬 익스텐션은
처음엔 간단해 보여도 막상 해보면 고려할 게 꽤 많습니다.
생각보다 “브라우저가 내 앱이 되는 구조”를 만드는 데 많은 시행착오가 있었지만,
익스텐션이라는 도구의 잠재력을 다시 느끼게 되는 시간이었습니다.
“이건 꼭 막혀본다” — 크롬 익스텐션 실전 API 연동과 CORS 지옥 탈출기
2편에서는 외부 API 연동, CORS 문제, 쿠키 인증 처리, 환경변수 관리, 새 탭 열기 등
실전에서 꼭 부딪히는 이슈들 위주로 정리해보려 합니다.
읽어주셔서 감사합니다 🙌
궁금한 점은 댓글로 남겨주세요!
저희는 현재 링크 드라퍼의 베타 버전을 운영 중입니다.
이번 크롬 익스텐션도 포함되어 있으며, 여러분의 피드백이 서비스 개선에 큰 힘이 됩니다.
편하게 사용해보시고, 좋았던 점이나 아쉬웠던 점을 알려주세요!