브라우저 안에 내 앱을 심었다 — 첫 크롬 익스텐션 개발기

LinkDropper·2025년 5월 2일
post-thumbnail

링크를 잘 저장하고, 다시 꺼내보는 습관을 만들고 싶었습니다.
매번 Notion에 붙여넣는 것도 번거롭고, 북마크는 어느새 정리가 안 되더라고요.

“그냥, 브라우저 안에서 바로 정리할 수 있으면 좋지 않을까?”

사실 크롬 익스텐션을 만들게 된 가장 큰 이유는,
매번 링크를 복사해서 링크 드라퍼 사이트에 들어가 붙여넣는 일조차 번거롭게 느껴졌기 때문입니다.
그래서 브라우저에서 버튼 한 번으로 바로 저장할 수 있는 기능이 필요했습니다.

이렇게 시작된 링크 드라퍼 크롬 익스텐션 개발기.
이번 글에서는 아이디어 구상부터 초기 세팅, 구조 설계, OG 정보 추출, 쿠키 인증 처리까지 실제로 구현한 내용을 정리해보려 합니다.


🧱 개발 환경 구성 — React + Vite + TypeScript + MV3

익스텐션 개발이 처음이라, 익숙한 스택으로 시작하고 싶었습니다.
그래서 선택한 조합은:

  • React + Vite (빠른 개발)
  • TypeScript (정적 타입 체크)
  • Manifest V3 (Chrome 권장 버전)

📁 디렉토리 구조

src/
├── background/      # 백그라운드 서비스 워커
├── content/         # Content Script
├── popup/           # 툴바 팝업 UI
├── assets/          # 정적 자원
├── manifest.json    # 크롬 익스텐션 설정

🧾 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는 Mantine UI를 사용했습니다.
좁은 화면에 알맞은 컴포넌트를 빠르게 조립할 수 있어서 굉장히 유용했어요.

팝업에는 다음 기능들을 넣었습니다:

  • 현재 탭의 링크 정보 (제목 + 이미지) 보여주기
  • 저장 버튼
  • 폴더 선택 드롭다운

🔍 Open Graph 정보 추출

단순 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가 너무 복잡해져서 평면 구조로 정리했어요.


💾 크롬 스토리지(local storage) 활용

Zustand로 상태를 관리하면서도, 일부 정보는 크롬의 storage.local API에 저장했습니다.
대표적으로는:

  • 최근 선택한 폴더
  • 세션 토큰 (인증 처리용)
chrome.storage.local.set({ lastUsedFolder: "folder-id" });

chrome.storage.local.get("lastUsedFolder", ({ lastUsedFolder }) => {
  console.log(lastUsedFolder);
});

비동기 API라 Promise 기반 유틸을 만들면 사용하기 더 편합니다.


🍪 세션 쿠키 가져오기 (httpOnly 대응)

익스텐션에서 로그인 세션을 유지하려면, 서버의 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.cookies API를 사용할 수 없습니다. 반드시 background 또는 popup에서 호출해야 합니다.


✍️ 마무리하며

MV3 기반 크롬 익스텐션은
처음엔 간단해 보여도 막상 해보면 고려할 게 꽤 많습니다.

  • 퍼미션
  • 쿠키 인증
  • 북마크 트리
  • OG 데이터 추출
  • 스토리지 상태 관리

생각보다 “브라우저가 내 앱이 되는 구조”를 만드는 데 많은 시행착오가 있었지만,
익스텐션이라는 도구의 잠재력을 다시 느끼게 되는 시간이었습니다.


📌 다음 편 예고

“이건 꼭 막혀본다” — 크롬 익스텐션 실전 API 연동과 CORS 지옥 탈출기

2편에서는 외부 API 연동, CORS 문제, 쿠키 인증 처리, 환경변수 관리, 새 탭 열기 등
실전에서 꼭 부딪히는 이슈들 위주로 정리해보려 합니다.

읽어주셔서 감사합니다 🙌
궁금한 점은 댓글로 남겨주세요!


🚀 링크 드라퍼 베타 테스트에 참여해보세요

저희는 현재 링크 드라퍼의 베타 버전을 운영 중입니다.
이번 크롬 익스텐션도 포함되어 있으며, 여러분의 피드백이 서비스 개선에 큰 힘이 됩니다.

🔗 링크 드라퍼 베타 체험하러 가기

편하게 사용해보시고, 좋았던 점이나 아쉬웠던 점을 알려주세요!

profile
“기록하는 습관을 도구로 만들다 — 두 개발자의 링크 드라퍼 구축기”

0개의 댓글