📋 JavaScript 실전 패턴

클립보드 복사
(Clipboard Copy)

"복사" 버튼 하나로 코드·링크·텍스트를 바로 클립보드에 저장. navigator.clipboard API로 깔끔하게 구현하고 복사 완료 피드백까지 완성해봐요.

📋 Clipboard API ⚡ 3줄이면 완성 🎨 피드백 애니메이션 📱 모바일 지원
① Concept

Clipboard API란?

브라우저가 제공하는 클립보드 접근 API예요. 버튼 하나로 텍스트를 복사할 수 있어 사용자 경험을 크게 향상시켜요.

💡

핵심 3줄: await navigator.clipboard.writeText(text) — 이게 전부예요. async/await로 감싸고, 완료 후 버튼 텍스트를 바꿔주면 끝이에요.

방법 지원 범위 특징 추천도
document.execCommand('copy') 모든 브라우저 (구형 포함) 텍스트 선택 후 복사. 동기식. 이미 deprecated 됨 구식
navigator.clipboard.writeText() Chrome 66+, Firefox 63+, Safari 13.1+ Promise 기반 비동기. HTTPS 또는 localhost 필요 권장
🔒

HTTPS 필요

navigator.clipboard는 보안 컨텍스트(HTTPS 또는 localhost)에서만 동작해요. HTTP 사이트에서는 document.execCommand 폴백이 필요해요.

⏱️

사용자 행동 내 호출

클립보드 API는 버튼 클릭 같은 사용자 제스처 안에서만 호출할 수 있어요. 페이지 로드 시 자동으로 복사하려 하면 차단돼요.

📱

iOS Safari 주의

iOS Safari 13.4 미만에서는 navigator.clipboard가 없을 수 있어요. try/catch로 에러를 잡고 execCommand로 폴백 처리하세요.

🎨

피드백이 핵심

복사가 됐는지 사용자가 알 수 없으면 불안해요. 버튼 텍스트 변경, 색상 변화, 토스트 알림 등으로 복사 완료를 명확하게 알려주세요.

② Demo

직접 클릭해보세요

3가지 복사 패턴을 직접 체험해보세요. 버튼을 클릭하면 클립보드에 복사돼요.

① 기본 복사 버튼

텍스트를 수정하고 복사 버튼을 눌러보세요. 2초 후 버튼이 원래대로 돌아와요.

✓ 클립보드에 복사됐어요!

② 코드 블록 복사

코드 영역 오른쪽 상단의 copy 버튼을 눌러보세요.

const copyToClipboard = async (text) => {
  try {
    await navigator.clipboard.writeText(text);
    console.log('복사 완료!');
  } catch (err) {
    console.error('복사 실패:', err);
  }
};
✓ 코드가 복사됐어요!

③ 현재 페이지 URL 복사

현재 페이지의 URL을 클립보드에 복사해요. 블로그나 공유 버튼에 유용해요.

✓ URL이 복사됐어요! 어디서든 붙여넣기 하세요.
③ Code

코드 해설

기본 복사 버튼부터 심화 패턴까지 단계별로 살펴봐요.

1 — 기본 복사 버튼 (3줄 핵심)

이것만 알면 80% 완성이에요. async/await로 복사하고 버튼 텍스트를 바꿔요.

script.js
const btn  = document.querySelector('#copyBtn');
const text = '복사할 텍스트를 여기에';

btn.addEventListener('click', async () => {
  await navigator.clipboard.writeText(text);

  // 복사 완료 피드백
  btn.textContent = '복사됨! ✓';
  btn.style.background = '#16a34a';   // 초록색으로 변경

  // 2초 후 원래대로
  setTimeout(() => {
    btn.textContent = '📋 복사';
    btn.style.background = '';
  }, 2000);
});

2 — 입력 필드 내용 복사

input 또는 textarea 값을 복사할 때는 .value를 읽어서 writeText에 넘겨요.

script.js
const input = document.querySelector('#myInput');
const btn   = document.querySelector('#copyBtn');

btn.addEventListener('click', async () => {
  const text = input.value.trim();

  if (!text) {
    alert('복사할 내용이 없어요!');
    return;
  }

  try {
    await navigator.clipboard.writeText(text);
    showFeedback(btn, '복사됨! ✓');
  } catch (err) {
    console.error('복사 실패:', err);
    fallbackCopy(text);   // execCommand 폴백
  }
});

function showFeedback(btn, msg) {
  const original = btn.textContent;
  btn.textContent = msg;
  btn.disabled = true;
  setTimeout(() => {
    btn.textContent = original;
    btn.disabled = false;
  }, 2000);
}

3 — 코드 블록 복사 버튼 (HTML 포함)

index.html + script.js
<!-- HTML -->
<div class="code-area" style="position:relative">
  <pre id="myCode">const x = 42;</pre>
  <button class="copy-code-btn"
          style="position:absolute;top:10px;right:10px"
          onclick="copyCode(this,'myCode')">copy</button>
</div>

// JavaScript
async function copyCode(btn, preId) {
  const code = document.getElementById(preId).innerText;
  await navigator.clipboard.writeText(code);
  btn.textContent = 'copied! ✓';
  btn.style.color = '#a6e3a1';
  setTimeout(() => {
    btn.textContent = 'copy';
    btn.style.color = '';
  }, 2000);
}
④ Advanced

응용 & 예외 처리

실서비스에서 자주 맞닥뜨리는 예외 상황과 UX 향상 팁이에요.

⚠️

iOS Safari 구버전 대응: iOS 13.4 미만에서는 navigator.clipboard가 없을 수 있어요. try/catch 안에서 navigator.clipboard 존재 여부를 확인하고 없으면 document.execCommand('copy') 폴백을 사용하세요.

완전한 폴백 처리 코드

clipboard-utils.js
/**
 * 텍스트를 클립보드에 복사 (폴백 포함)
 * @param {string} text - 복사할 텍스트
 * @returns {Promise<boolean>} 성공 여부
 */
async function copyToClipboard(text) {
  // 최신 브라우저: navigator.clipboard API
  if (navigator.clipboard && window.isSecureContext) {
    try {
      await navigator.clipboard.writeText(text);
      return true;
    } catch (err) {
      console.warn('Clipboard API 실패, 폴백 시도:', err);
    }
  }

  // 폴백: execCommand (구형 브라우저 / HTTP)
  const textarea = document.createElement('textarea');
  textarea.value = text;
  textarea.style.cssText = 'position:fixed;opacity:0;top:-9999px';
  document.body.appendChild(textarea);
  textarea.focus();
  textarea.select();

  try {
    document.execCommand('copy');
    return true;
  } catch (err) {
    console.error('복사 실패:', err);
    return false;
  } finally {
    document.body.removeChild(textarea);
  }
}

// 사용 예시
btn.addEventListener('click', async () => {
  const ok = await copyToClipboard('복사할 텍스트');
  if (ok) {
    showSuccess('복사됐어요! ✓');
  } else {
    showError('복사에 실패했어요. 직접 선택 후 Ctrl+C를 눌러주세요.');
  }
});
🎨

버튼 색상 피드백

복사 완료 시 초록색으로, 실패 시 빨간색으로 버튼 색상을 바꿔 직관적으로 상태를 알려요. 2초 후 원래 색상으로 돌아오게 setTimeout을 사용해요.

🍞

토스트 알림

화면 하단이나 상단에 "복사됐어요!" 토스트를 띄우는 방식도 인기 있어요. 여러 곳의 복사 버튼이 있을 때 중앙 피드백으로 일관성을 줄 수 있어요.

접근성 고려

버튼에 aria-label="코드 복사"를 추가하고, 복사 완료 시 aria-live="polite" 영역에 "복사됐습니다"를 넣으면 스크린 리더 사용자도 피드백을 받을 수 있어요.

⑤ Prompt Tip

AI 프롬프트 팁

복사 버튼 기능을 AI에게 요청할 때 이 프롬프트를 참고해보세요.

✦ 바이브코더 프롬프트

아래 프롬프트를 복사해서 AI에 붙여넣어 보세요.

내 블로그 코드 블록에 복사 버튼을 추가해줘.

요구사항:
- 코드 블록(pre 태그) 오른쪽 상단에 "copy" 버튼 표시
- 클릭하면 해당 코드 내용을 클립보드에 복사
- 복사 성공 시 버튼이 "copied! ✓"로 바뀌고 초록색이 됨
- 2초 후 원래 "copy" 버튼으로 되돌아옴
- navigator.clipboard API 사용 (iOS Safari 폴백 포함)
- 페이지에 코드 블록이 여러 개 있어도 각각 독립적으로 동작
- 순수 Vanilla JS (라이브러리 없이)

현재 내 코드 블록 HTML 구조:
<pre class="code-block"><code>코드 내용</code></pre>
⑥ FAQ

자주 묻는 질문

클립보드 복사 구현 시 자주 맞닥뜨리는 질문들이에요.

navigator.clipboard는 보안 컨텍스트(HTTPS)에서만 동작해요. HTTP로 배포된 사이트에서는 사용할 수 없어요. 해결책은 ① HTTPS로 배포하거나, ② document.execCommand('copy') 폴백을 함께 구현하는 것이에요. 대부분의 호스팅 서비스(Netlify, Vercel, GitHub Pages)는 기본으로 HTTPS를 제공해요.
pre.innerTextpre.textContent의 차이 때문일 수 있어요. innerText는 화면에 렌더링된 텍스트를, textContent는 HTML 태그를 포함한 원본을 반환해요. 코드 하이라이팅을 위해 span 태그를 사용했다면 textContent에는 태그가 포함돼요. innerText를 사용하면 화면에 보이는 텍스트만 복사돼요.
data-copy-target 같은 데이터 속성으로 버튼과 복사 대상을 연결하면 돼요. 예시: <button data-copy-target="code1">복사</button>document.querySelectorAll('[data-copy-target]').forEach(btn => btn.addEventListener('click', ...)). 이벤트 위임 패턴으로 하나의 이벤트 리스너로 모든 버튼을 처리할 수 있어요.