내가 직접 솔루션을 만들게 만든 클립보드 문제

발행: (2026년 1월 14일 오전 12:54 GMT+9)
10 min read
원문: Dev.to

Source: Dev.to

클립보드를 또 잃어버렸어요. 복잡한 SQL 쿼리를 복사하고 다른 탭으로 전환한 뒤 다른 것을 복사했더니 그 쿼리가 사라졌습니다. 기억을 되살려 다시 작성해야 했죠.

이런 일이 계속되었습니다. 원격 근무자로서 노트북과 데스크톱을 계속 오가며 작업했는데, 한 장치에서 무언가를 복사하고 다른 장치로 전환하면 사라졌습니다. 몇 가지 클립보드 관리자를 시도했지만 내 작업 흐름에 맞지 않았습니다.

그래서 직접 만들었습니다. 아래에는 제가 배운 점, 마주친 도전 과제, 그리고 다르게 할 점을 정리했습니다.

고충

  • 기기 간 손실 – 노트북에서 코드 스니펫을 복사하고 데스크톱으로 전환하면 사라짐.
  • 우연한 덮어쓰기 – 이메일 템플릿을 복사하고 탭을 바꾼 뒤 다른 것을 복사하면 템플릿을 잃음.
  • 히스토리 스크롤 – 매일 사용하는 항목을 찾기 위해 50개의 클립보드 항목을 스크롤해야 함.

시도해 본 도구

도구부족했던 이유
기본 OS 클립보드 관리자기기 간 동기화가 없음.
클라우드 클립보드 도구너무 느리거나 번거롭고 단계가 많음.
브라우저 확장 프로그램모든 것을 저장하지만 실제로 사용하는 것을 우선순위에 두지 않음.

대부분의 확장 프로그램은 히스토리만 저장했습니다. 매일 붙여넣는 이메일 서명을 찾기 위해 수십 개의 항목을 스크롤해야 했습니다. 목적에 맞지 않았습니다.

나의 솔루션: 맞춤형 브라우저 확장 프로그램

특징

  • Chrome과 Edge 브라우저 간에 클립보드 기록을 동기화합니다.
  • 내가 가장 많이 붙여넣는 내용을 학습하고 해당 항목을 우선순위에 둡니다.
  • 빠른 접근을 위한 오른쪽 클릭 컨텍스트 메뉴를 제공합니다.
  • Google Docs, Reddit, Notion 등 복잡한 사이트에서도 작동합니다.

핵심 차이점 – 클릭한 것만이 아니라 실제로 붙여넣은 것을 추적합니다. 자주 붙여넣는 항목은 자동으로 상단으로 이동합니다.

Source:

기술 심층 분석

Clipboard API의 특이점

// This works in some contexts, not others
navigator.clipboard.readText().then(text => {
  // But what if the page doesn't have focus?
  // What if it's a content script?
});

문제점

  • HTTPS 또는 localhost가 필요합니다.
  • 콘텐츠 스크립트가 클립보드에 직접 접근하지 못할 때가 있습니다.
  • 권한 처리 방식이 상황에 따라 다릅니다.

해결책 – 콘텐츠 스크립트와 백그라운드 서비스 워커 간에 메시지 전달을 사용하고, 대체 방안을 마련합니다.

서비스 워커 수명 주기

// This state gets lost when the service worker dies
let clipboardHistory = [];

// Had to persist everything
chrome.storage.local.set({ clipboardHistory });

도전 과제

  • 서비스 워커는 언제든지 종료될 수 있습니다.
  • 콘텐츠‑스크립트 주입 시점 문제가 발생합니다.
  • CSP 제한으로 일부 접근 방식이 차단됩니다.

해결책 – 상태를 chrome.storage.local에 저장하고, 시작 시 재초기화를 처리합니다.

복잡한 사이트에 붙여넣기

방법설명신뢰도
Modernelement.dispatchEvent(new ClipboardEvent('paste'))일부 상황에서 동작하지만, 모든 상황에서는 아닙니다.
Deprecateddocument.execCommand('paste')때때로 유일한 옵션이 됩니다.
Direct DOMelement.textContent = clipboardText간단한 사이트에서 동작합니다.

통합 접근 방식

async function pasteText(element, text) {
  // Try modern approach first
  try {
    element.focus();
    await navigator.clipboard.writeText(text);
    element.dispatchEvent(new ClipboardEvent('paste'));
    return true;
  } catch (e) {
    // Fallback to execCommand
    try {
      element.focus();
      document.execCommand('insertText', false, text);
      return true;
    } catch (e2) {
      // Last resort: direct manipulation
      element.textContent = text;
      return true;
    }
  }
}

실시간 동기화 vs. 폴링

폴링 (피하고 싶었던 방식)

setInterval(async () => {
  const latest = await fetchLatestClipboard();
  // Check for changes...
}, 5000); // Too slow, or too resource‑intensive

해결책: 실시간 구독 (Supabase 예시)

const subscription = supabase
  .channel('clipboard-changes')
  .on('postgres_changes', {
    event: 'INSERT',
    schema: 'public',
    table: 'clipboard_items'
  }, payload => {
    // Handle new item instantly
    updateLocalClipboard(payload.new);
  })
  .subscribe();

도전 과제

  • 연결 끊김을 처리해야 합니다.
  • 여러 기기에서 동일한 항목이 업데이트될 때 충돌을 해결해야 합니다.
  • 구독 수명 주기를 관리해야 합니다.

브라우저 간 호환성

브라우저API 네임스페이스
Chromechrome.*
Firefoxbrowser.*
Edgechrome.* (조금 다르게 동작)

추상화 레이어

const browserAPI = {
  storage: {
    local: {
      get: key => {
        if (typeof chrome !== 'undefined' && chrome.storage) {
          return new Promise(resolve => {
            chrome.storage.local.get(key, resolve);
          });
        } else if (typeof browser !== 'undefined') {
          return browser.storage.local.get(key);
        }
      },
      set: items => {
        if (typeof chrome !== 'undefined' && chrome.storage) {
          return new Promise(resolve => {
            chrome.storage.local.set(items, resolve);
          });
        } else if (typeof browser !== 'undefined') {
          return browser.storage.local.set(items);
        }
      },
      // ...similar for remove, clear, etc.
    }
  }
};

배운 교훈

  • 모든 것을 지속하세요 – 서비스 워커가 언제든 죽을 수 있다고 가정하세요.
  • 단순하게 시작하세요 – 로컬 전용 저장소로 MVP를 만들고, 이후 동기화를 추가하세요.
  • 실제 사용량을 추적하세요 – 클릭만이 아니라 붙여넣기 이벤트도 모니터링하세요.
  • 모든 대상 브라우저에서 테스트하세요 – Chrome, Firefox, Edge는 각각 다르게 동작합니다.
  • 충돌 해결을 일찍 계획하세요 – 나중에 큰 고통 포인트가 됩니다.
  • 시작부터 견고한 오류 처리를 추가하세요 – 재시도, 대체 로직 등을 포함합니다.
  • 테스트 인프라를 일찍 구축하세요 – 단위 + 통합 테스트가 시간을 절약합니다.
  • 사용자 피드백을 빨리 받으세요 – 너무 오래 고립된 상태로 개발하는 것을 피하세요.

아직 풀고 있는 열린 질문들

  1. 클립보드 권한 – 브라우저 간 확장 프로그램에서 이를 어떻게 신뢰성 있게 처리하나요?
  2. 붙여넣기 호환성 – 복잡한 사이트(Google Docs, Notion, CodePen 등)에서 더 나은 접근법은?
  3. 실시간 동기화 아키텍처 – 최소 지연 및 충돌로 클립보드 데이터를 동기화하는 가장 견고한 방법은?
  4. Manifest V3 서비스 워커 수명 주기 – 상태를 지속하고 재시작을 처리하기 위한 모범 사례는?

이러한 문제들을 해결한 경험이 있다면, 여러분의 생각을 듣고 싶습니다!

모범 사례?

브라우저 간 확장 프로그램을 효율적으로 테스트하는 방법은?

비슷한 것을 만들었거나 아이디어가 있다면, 알려 주세요.

몇 달 동안 사용해 본 결과, 잘 작동하고 있습니다. 클립보드 내용을 거의 잃지 않으며, 자주 사용하는 항목이 빠르게 나타납니다. 완벽하진 않지만 제 워크플로에 맞습니다.

사용해 보고 싶다면 ChromeEdge에서 사용할 수 있습니다. 더 중요한 것은, 여러분이:

  • 비슷한 문제를 어떻게 해결했는지
  • 브라우저 확장 프로그램에서 겪은 도전 과제는 무엇인지
  • 어떤 기능이 이 도구를 실제로 유용하게 만들지

알려 주시면 좋겠습니다.

아직 진행 중이며, 진행하면서 배우고 있습니다.

작성자: Anurag G.
LinkedIn:

Back to Blog

관련 글

더 보기 »