홈으로 돌아가기

Manifest V3에서 크래시 없이 URL 추적기 정리

Chrome 확장 프로그램 Manifest V3에서 트래커로부터 URL 정리 구현 상세 분석. 검색 엔진 whitelist, transitionType 필터링, content script를 통한 Clipboard API 솔루션 설명. 작동하는 코드와 예제 제공.

Manifest V3: 브라우저를 망가뜨리지 않고 링크 정리
Advertisement 728x90

Manifest V3에서 URL 추적 매개변수 제거하기: 흔한 실수 피하는 법

브라우저 확장 프로그램 개발자들은 종종 URL에서 UTM 태그, fbclid, gclid 같은 추적 매개변수를 제거해야 할 때가 있습니다. 정규 표현식을 사용한 간단한 구현은 기본적인 경우에는 작동하지만, Manifest V3에서는 검색 엔진을 망가뜨리거나, 무한 리디렉션을 일으키고, XHR 요청을 방해하는 등 실패로 이어집니다. 실제 아키텍처 예시와 함께 주요 문제점과 해결책을 살펴보겠습니다.

검색 엔진을 위한 허용 목록은 필요한 매개변수가 제거되는 것을 방지합니다. Google, Yandex 등은 소스와 미디어를 필터(예: 이미지, 동영상)에 사용합니다. 예외 없이 제거하면 검색 기능이 작동하지 않습니다.

const WHITELIST = [
  'google.com', 'bing.com', 'yandex.ru', 'duckduckgo.com' 
];

async function cleanUrl(url) {
  const urlObj = new URL(url);
  if (WHITELIST.some(site => urlObj.hostname.includes(site))) {
    return url; // 검색 엔진은 그대로 둡니다
  }
  // ... 제거 로직
}

Manifest V3에서의 네비게이션: 전환 필터링

Manifest V3는 요청 가로채기를 제한합니다. declarativeNetRequest는 매개변수를 동적으로 파싱하지 않으므로 webNavigation.onBeforeNavigate를 사용합니다. chrome.tabs.update를 통한 간단한 리디렉션은 문제를 일으킵니다:

Google AdInline article slot
  • 폼의 POST 요청이 깨집니다.
  • JSON을 위한 XHR/Fetch 요청이 실패합니다.
  • 리디렉션이 이벤트를 반복적으로 트리거하여 루프를 만듭니다.

해결책은 엄격한 transitionType 확인입니다. 'link'(링크 클릭)만 처리하고, 타이핑, auto_subframe, form_submit은 무시합니다.

chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
  const result = await chrome.storage.sync.get(['autoClean']);
  if (!result.autoClean) return;

  if (details.transitionType !== 'link') return;

  const cleanedUrl = await cleanUrl(details.url);

  if (details.url !== cleanedUrl) {
    chrome.tabs.update(details.tabId, { url: cleanedUrl });
  }
}, {
  url: [
    {hostContains: '.'},
    {schemes: ['http', 'https']}
  ]
});

hostContains: '.' 필터는 new URL()이 예측 불가능하게 작동하는 chrome-extension:// 스킴을 제외합니다.

컨텍스트 메뉴와 클립보드 API

수동 링크 제거를 위해 컨텍스트 메뉴를 추가하세요. Manifest V3 서비스 워커에서는 클립보드 접근이 차단됩니다—콘텐츠 스크립트에 위임하세요.

Google AdInline article slot

background.js에서:

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "cleanCopy" && info.linkUrl) {
    chrome.tabs.sendMessage(tab.id, {
      action: "cleanAndCopy",
      url: info.linkUrl
    });
  }
});

콘텐츠 스크립트는 토스트 알림과 함께 Async Clipboard API를 사용합니다:

function cleanAndCopyLink(url) {
  chrome.runtime.sendMessage({ action: "cleanLink", url: url }, (response) => {
    if (response && response.cleanedUrl) {
      navigator.clipboard.writeText(response.cleanedUrl).then(() => {
        showNotification(`링크 제거 후 복사 완료!\n제거된 추적기: ${response.trackerCount}`);
      });
    }
  });
}

function showNotification(message) {
  let toast = document.getElementById('zerotail-toast');
  if (!toast) {
    toast = document.createElement('div');
    toast.id = 'zerotail-toast';
    toast.style.cssText = `
      position: fixed; bottom: 20px; right: 20px;
      background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
      color: white; padding: 12px 20px; border-radius: 8px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 999999;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      max-width: 320px; transition: opacity 0.3s ease;
    `;
    document.body.appendChild(toast);
  }
  toast.textContent = message;
  toast.style.opacity = '1';
  setTimeout(() => { toast.style.opacity = '0'; }, 3000);
}

스타일링은 Material Design을 모방합니다: 그라데이션 배경, 그림자, 부드러운 페이드.

Google AdInline article slot

핵심 요점

  • 도메인 허용 목록: 매개변수가 기능에 중요한 검색 엔진(google.com, yandex.ru)을 제외합니다.
  • transitionType === 'link': 폼, XHR, URL 입력과의 간섭을 방지합니다.
  • 스킴 필터: hostContains: '.' + http/https로 extension:// 오류를 피합니다.
  • 클립보드 위임: Manifest V3를 위해 서비스 워커 → 콘텐츠 스크립트.
  • 토스트 UI: 알림 없이 미니멀한 알림, 제거된 추적기 수 표시.

— Editorial Team

Advertisement 728x90

다음 읽기