클립보드는 복사/붙여넣기라는 가장 기본적인 사용자 인터랙션을 다룹니다. 과거에는 document.execCommand("copy")를 사용했지만, 모던 Clipboard API는 비동기 기반으로 더 안전하고 강력합니다.

텍스트 복사

JS
// 모던 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);
});

텍스트 붙여넣기

JS
async function pasteText() {
  try {
    const text = await navigator.clipboard.readText();
    console.log("붙여넣은 텍스트:", text);
    return text;
  } catch (err) {
    console.error("붙여넣기 실패:", err);
  }
}

이미지 복사/붙여넣기

JS
// 이미지 복사
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 이벤트

JS
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 패턴

JS
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;
}

보안 제약

JS
// 1. HTTPS 필수 (localhost 제외)
// 2. 사용자 제스처 필요 (클릭 이벤트 내에서만)
// 3. readText()는 권한 요청 필요
// 4. 포커스가 없는 탭에서는 동작하지 않음

// 권한 확인
const status = await navigator.permissions.query({
  name: "clipboard-read",
});
console.log(status.state); // "granted", "denied", "prompt"

리치 텍스트 복사

JS
// 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") 폴백을 추가합니다.

댓글 로딩 중...