Clipboard API — 복사-붙여넣기의 모던 접근
클립보드는 복사/붙여넣기라는 가장 기본적인 사용자 인터랙션을 다룹니다. 과거에는
document.execCommand("copy")를 사용했지만, 모던 Clipboard API는 비동기 기반으로 더 안전하고 강력합니다.
텍스트 복사
// 모던 Clipboard API (async)
async function copyText(text) {
try {
await navigator.clipboard.writeText(text);
console.log("복사 성공!");
} catch (err) {
console.error("복사 실패:", err);
}
}
// 사용 — 복사 버튼
copyButton.addEventListener("click", () => {
copyText(codeBlock.textContent);
});
텍스트 붙여넣기
async function pasteText() {
try {
const text = await navigator.clipboard.readText();
console.log("붙여넣은 텍스트:", text);
return text;
} catch (err) {
console.error("붙여넣기 실패:", err);
}
}
이미지 복사/붙여넣기
// 이미지 복사
async function copyImage(imgElement) {
const response = await fetch(imgElement.src);
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
}
// 클립보드에서 이미지 읽기
async function pasteImage() {
const items = await navigator.clipboard.read();
for (const item of items) {
for (const type of item.types) {
if (type.startsWith("image/")) {
const blob = await item.getType(type);
const url = URL.createObjectURL(blob);
const img = document.createElement("img");
img.src = url;
return img;
}
}
}
}
paste 이벤트
document.addEventListener("paste", (e) => {
// 텍스트
const text = e.clipboardData.getData("text/plain");
const html = e.clipboardData.getData("text/html");
// 파일 (스크린샷 등)
const files = [...e.clipboardData.files];
files.forEach((file) => {
if (file.type.startsWith("image/")) {
const url = URL.createObjectURL(file);
console.log("이미지 붙여넣기:", url);
}
});
// 기본 동작 방지 (필요 시)
e.preventDefault();
});
// copy 이벤트 — 복사 내용 커스터마이징
document.addEventListener("copy", (e) => {
const selection = document.getSelection().toString();
// 복사 시 출처 추가
e.clipboardData.setData("text/plain", `${selection}\n\n출처: mysite.com`);
e.preventDefault();
});
복사 버튼 UI 패턴
function createCopyButton(targetElement) {
const button = document.createElement("button");
button.textContent = "복사";
button.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(targetElement.textContent);
button.textContent = "복사됨!";
setTimeout(() => { button.textContent = "복사"; }, 2000);
} catch {
// 폴백: 레거시 방식
const textarea = document.createElement("textarea");
textarea.value = targetElement.textContent;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
textarea.remove();
button.textContent = "복사됨!";
setTimeout(() => { button.textContent = "복사"; }, 2000);
}
});
return button;
}
보안 제약
// 1. HTTPS 필수 (localhost 제외)
// 2. 사용자 제스처 필요 (클릭 이벤트 내에서만)
// 3. readText()는 권한 요청 필요
// 4. 포커스가 없는 탭에서는 동작하지 않음
// 권한 확인
const status = await navigator.permissions.query({
name: "clipboard-read",
});
console.log(status.state); // "granted", "denied", "prompt"
리치 텍스트 복사
// HTML과 일반 텍스트 동시 복사
async function copyRichText(html, plainText) {
const htmlBlob = new Blob([html], { type: "text/html" });
const textBlob = new Blob([plainText], { type: "text/plain" });
await navigator.clipboard.write([
new ClipboardItem({
"text/html": htmlBlob,
"text/plain": textBlob,
}),
]);
}
// 사용
await copyRichText(
"<b>굵은 글씨</b>와 <i>기울임</i>",
"굵은 글씨와 기울임"
);
**기억하기 **: 모던 Clipboard API는
navigator.clipboard.writeText()로 복사,readText()로 읽기입니다. HTTPS와 사용자 제스처가 필요하고, 이미지는ClipboardItem을 사용합니다. 레거시 브라우저 지원이 필요하면document.execCommand("copy")폴백을 추가합니다.
댓글 로딩 중...