코드 블록 클릭 복사
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);
});
navigator.clipboard.writeText 사용하기
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 };
이 함수는:
.highlight클래스를 가진 모든 요소(코드 블록 컨테이너)를 찾습니다.- 복사 아이콘이 들어간 버튼을 앞에 삽입합니다.
- 클릭 시 형제 코드 요소의 텍스트를 읽어
navigator.clipboard를 통해 시스템 클립보드에 기록합니다. - 성공 시 체크 아이콘, 실패 시 폭탄 아이콘을 표시하고,
iconChangeTimeout밀리초 후 원래 복사 아이콘으로 복원합니다.
결론
ClipboardJS에서 네이티브 Clipboard API로 전환하면 번들 크기가 감소하고 불필요한 의존성이 사라지면서도 사용자 경험을 그대로 유지할 수 있습니다. 최종 스크립트는 가볍고 유지보수가 쉬우며, Clipboard API를 지원하는 모든 최신 브라우저에서 동작합니다.