웹 애플리케이션에서 버튼 클릭 한 번으로 텍스트를 복사하는 기능을 구현할 때 과거 사용하던 document.execCommand('copy')가 동작하지 않거나 콘솔에 경고 메시지가 출력되는 현상을 경험해봤을 것이다. 다만 대부분의 개발자들은 대체 기술인 Clipboard API의 비동기 특성이나 보안 권한 조건을 제대로 모른 채 기존 구형 코드를 그대로 방치하는 경우가 많다. 이번에는 JavaScript Clipboard API가 정확히 무엇인지, 왜 도입해야 하는지, 실무에서 안전하게 구현하는 방법까지 완벽하게 정리해서 소개하겠다.
과거에는 웹 페이지에서 텍스트나 이미지를 클립보드에 복사하기 위해 숨겨진 <textarea> 요소를 동적으로 생성한 후 document.execCommand('copy') 명령을 실행하는 방식을 사용했다. 그러나 이 방식은 동기적으로 작동하여 브라우저 메인 스레드를 블로킹하고, 사용자의 의도와 상관없이 클립보드를 조작할 수 있는 보안 취약점이 존재하여 최신 웹 표준에서 공식 폐기(Deprecated)되었다.
Clipboard API는 Promise 기반의 비동기 방식으로 동작하며, 사용자의 시스템 클립보드에 안전하게 접근할 수 있도록 설계된 최신 브라우저 표준 API다. 단순 텍스트뿐만 아니라 이미지와 같은 다양한 바이너리 데이터도 손쉽게 다룰 수 있고, 메인 스레드를 방해하지 않아 쾌적한 사용자 경험을 제공한다.
| 비교 항목 | document.execCommand | Clipboard API |
|---|---|---|
| 동작 방식 | 동기식 (메인 스레드 블로킹) | 비동기식 (Promise 기반) |
| 지원 데이터 | 단순 텍스트 중심 | 텍스트, 이미지, Blob 등 확장 데이터 |
| 보안 및 권한 | 취약함 (제한 없음) | HTTPS 필수 및 명시적 사용자 권한 검증 |
| 웹 표준 상태 | Deprecated (사용 지양) | W3C 공식 웹 표준 규격 |
Clipboard API는 global navigator 객체의 clipboard 속성(navigator.clipboard)을 통해 접근한다. 실무에서 주로 사용하는 핵심 메서드는 다음과 같다.
navigator.clipboard.writeText(string) 메서드는 인자로 전달된 문자열을 클립보드에 저장한다. 반대로 navigator.clipboard.readText() 메서드는 현재 클립보드에 담긴 텍스트를 읽어온다. 두 메서드 모두 Promise를 반환하므로 async/await 문법을 사용하면 코드가 매우 명확해진다.
이미지 파일이나 HTML 태그 등 복합적인 데이터를 처리할 때는 write()와 read() 메서드를 활용한다. 이 메서드들은 ClipboardItem 객체를 생성하여 데이터의 MIME 타입과 Blob 객체를 매핑하는 방식으로 작동한다.
실무 환경에서 사용자 클릭 이벤트와 연동하여 텍스트를 안전하게 복사하는 코드를 비교해보자.
✗ 잘못된 예제 (구형 execCommand 사용 및 동기 처리)
// 구형 방식으로 인한 블로킹 발생 및 브라우저 경고 출력
function copyOldStyle(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy'); // 보안 및 최신 표준 위배
document.body.removeChild(textarea);
alert('복사되었습니다.');
}✓ 올바른 예제 (Clipboard API 기반 async/await 및 에러 핸들링)
// Clipboard API를 활용한 비동기 복사 모듈
async function copyToClipboard(text) {
if (!navigator.clipboard) {
console.error('Clipboard API를 지원하지 않는 브라우저이거나 비보안(HTTP) 환경입니다.');
return false;
}
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
console.error('클립보드 복사 실패:', err);
return false;
}
}
// 버튼 클릭 이벤트 바인딩
document.getElementById('copyBtn').addEventListener('click', async () => {
const isSuccess = await copyToClipboard('REDINFO 블로그 실무 가이드 텍스트');
if (isSuccess) {
alert('클립보드에 성공적으로 복사되었습니다.');
}
});실행 결과 / 출력값
버튼 클릭 시 화면 멈춤 현상 없이 깔끔하게 지정한 문자열이 클립보드에 저장되며, 성공 알림창이 정상적으로 표시된다.
개발 시 가장 자주 겪는 첫 번째 문제는 보안 컨텍스트(Secure Context) 요구사항이다. Clipboard API는 개인정보 보호를 위해 HTTPS 환경 또는 로컬 개발 환경(localhost)에서만 활성화된다. 일반 HTTP 도메인에서 실행할 경우 navigator.clipboard 객체는 undefined를 반환한다.
두 번째 주의사항은 사용자 제스처(User Gesture) 조건이다. 스팸 스크립트가 사용자의 승인 없이 클립보드를 오남용하는 것을 막기 위해, 브라우저는 click, keydown 같은 사용자 직접 이벤트 발생 내부에서 호출된 경우에만 클립보드 쓰기를 허용한다.
✗ 잘못된 예제 (사용자 동작 없는 자동 실행)
// 페이지 로드 시 임의로 실행 (NotAllowedError 발생)
window.addEventListener('DOMContentLoaded', async () => {
// DOMException: Document is not focused. 에러 발생
await navigator.clipboard.writeText('자동 복사 시도');
});✓ 올바른 예제 (보안 컨텍스트 검증 및 사용자 이벤트 연동)
async function safeCopyHandler(targetText) {
// HTTPS 환경 및 지원 여부 동시 검증
if (window.isSecureContext && navigator.clipboard) {
await navigator.clipboard.writeText(targetText);
} else {
// 비보안 환경에 대한 예외 대처
console.warn('보안 연결(HTTPS) 환경에서만 클립보드 복사 기능이 작동합니다.');
}
}
JavaScript Clipboard API는 오래된 execCommand 방식을 대체하고 웹 서비스의 보안성과 응답성을 획기적으로 개선해주는 표준 도구다. 비동기식 코드 작성과 보안 컨텍스트 준수라는 작은 개발 습관이 모여서 안정적인 사용자 경험이라는 큰 효과를 만든다는 점을 잊지 말자. 이 글의 3단계 실전 코드와 4단계 예외 처리 패턴을 참고해 프로젝트의 복사 로직을 검토하면, 모든 브라우저에서 에러 없는 클립보드 제어 기능을 구현할 수 있을 것이다.