코드 블록 클릭 복사

발행: (2025년 12월 10일 오전 05:51 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

저는 백엔드에 무게를 둔 풀스택 개발자이며, 긴 하루 동안 JavaScript 코드를 고친 뒤 내 사이트의 코드 블록에 “클립보드에 복사” 버튼을 추가하기로 했습니다. 첫 번째 버전은 ClipboardJS 라이브러리를 사용했지만, 곧 네이티브 Clipboard API만으로도 같은 결과를 훨씬 적은 오버헤드로 구현할 수 있다는 것을 깨달았습니다.

ClipboardJS를 사용한 원래 구현

각 코드 블록에 버튼 추가하기

const containers = document.querySelectorAll(`.${containerClass}`);

containers.forEach(container => {
  const button = document.createElement('button');
  button.className = buttonClass;
  button.innerHTML = icons.faCopyRegular;
  container.prepend(button);
});

ClipboardJS 인스턴스 생성하기

const clipboard = new ClipboardJS(`.${buttonClass}`, {
  target: function (trigger) {
    return trigger.nextElementSibling;
  }
});

복사가 성공했을 때 처리하기

clipboard.on('success', (e) => {
  if (e.action === 'copy') {
    const originalIcon = e.trigger.innerHTML;
    e.trigger.innerHTML = icons.faCheck;

    setTimeout(() => {
      e.trigger.innerHTML = originalIcon;
      e.clearSelection();
    }, iconChangeTimeout);
  }
});

오류 처리하기

clipboard.on('error', (e) => {
  console.error('ClipboardJS Error:', e.action, e.trigger);

  const originalIcon = e.trigger.innerHTML;
  e.trigger.innerHTML = icons.faBomb; // e.g., a cross or “times” icon

  setTimeout(() => {
    e.trigger.innerHTML = originalIcon;
  }, iconChangeTimeout);
});

기능적으로는 문제 없었지만, 몇 줄의 텍스트를 복사하기 위해 전체 ClipboardJS 라이브러리(및 그 의존성)를 로드해야 한다는 단점이 있었습니다.

네이티브 Clipboard API로 리팩터링

네이티브 API는 프로미스를 사용하므로 구현을 더 간결하게 만들 수 있습니다.

버튼 추가 (변경 없음)

const containers = document.querySelectorAll(`.${containerClass}`);

containers.forEach(container => {
  const button = document.createElement('button');
  button.className = buttonClass;
  button.innerHTML = icons.faCopyRegular;
  container.prepend(button);
});
containers.forEach(container => {
  const button = container.querySelector(`.${buttonClass}`);
  button.addEventListener('click', function () {
    const text = this.nextElementSibling.textContent;
    window.navigator.clipboard.writeText(text)
      .then(() => {
        const originalIcon = this.innerHTML;
        this.innerHTML = icons.faCheck;

        setTimeout(() => {
          this.innerHTML = originalIcon;
        }, iconChangeTimeout);
      })
      .catch((e) => {
        console.error('Error copying to clipboard:', e);

        const originalIcon = this.innerHTML;
        this.innerHTML = icons.faBomb;

        setTimeout(() => {
          this.innerHTML = originalIcon;
        }, iconChangeTimeout);
      });
  });
});

이 버전은 외부 의존성을 제거하고 원래 동작을 그대로 유지합니다: 성공 시 체크 표시, 실패 시 폭탄 아이콘을 보여주고 짧은 타임아웃 후 원래 복사 아이콘으로 복원합니다.

최종 구현

// icons.js should export the required SVG strings, e.g.:
// export const faCopyRegular = '';
// export const faCheck = '';
// export const faBomb = '';
import * as icons from './icons.js';

function addCopyToClipboardButtons() {
  const iconChangeTimeout = 1300;
  const containers = document.querySelectorAll('.highlight');

  containers.forEach(container => {
    const button = document.createElement('button');
    button.className = 'copy-button';
    button.innerHTML = icons.faCopyRegular;
    container.prepend(button);

    button.addEventListener('click', function () {
      const originalIcon = this.innerHTML;
      const text = this.nextElementSibling.textContent;

      window.navigator.clipboard.writeText(text)
        .then(() => {
          this.innerHTML = icons.faCheck;
          setTimeout(() => {
            this.innerHTML = originalIcon;
          }, iconChangeTimeout);
        })
        .catch(() => {
          console.error('Error copying to clipboard');
          this.innerHTML = icons.faBomb;
          setTimeout(() => {
            this.innerHTML = originalIcon;
          }, iconChangeTimeout);
        });
    });
  });
}

export { addCopyToClipboardButtons };

이 함수는:

  1. .highlight 클래스를 가진 모든 요소(코드 블록 컨테이너)를 찾습니다.
  2. 복사 아이콘이 들어간 버튼을 앞에 삽입합니다.
  3. 클릭 시 형제 코드 요소의 텍스트를 읽어 navigator.clipboard를 통해 시스템 클립보드에 기록합니다.
  4. 성공 시 체크 아이콘, 실패 시 폭탄 아이콘을 표시하고, iconChangeTimeout 밀리초 후 원래 복사 아이콘으로 복원합니다.

결론

ClipboardJS에서 네이티브 Clipboard API로 전환하면 번들 크기가 감소하고 불필요한 의존성이 사라지면서도 사용자 경험을 그대로 유지할 수 있습니다. 최종 스크립트는 가볍고 유지보수가 쉬우며, Clipboard API를 지원하는 모든 최신 브라우저에서 동작합니다.

Back to Blog

관련 글

더 보기 »