클립보드 액세스 차단 해제

텍스트와 이미지에 대한 더 안전하고 차단 해제된 클립보드 액세스

시스템 클립보드에 액세스하는 기존의 방법은 document.execCommand() 클립보드 상호작용입니다. 이 절단 및 자르기 방법은 널리 지원되지만 붙여넣기로 인해 비용이 발생함: 클립보드 액세스가 동기식이었으며 DOM에 쓸 수 있습니다.

텍스트가 작은 경우에는 괜찮지만, 문제가 발생했습니다. 시간이 오래 걸리는 정리 또는 콘텐츠를 안전하게 붙여넣으려면 이미지 디코딩이 필요할 수 있습니다. 브라우저 붙여넣은 문서에서 연결된 리소스를 로드하거나 인라인해야 할 수 있습니다. 그러면 디스크나 네트워크에서 기다리는 동안 페이지를 차단할 수 있습니다 권한 추가 상상하기 요청할 때 브라우저가 해당 페이지를 차단하도록 클립보드 액세스 이와 동시에, 로컬 컴퓨터에 소프트웨어를 클립보드 상호작용의 document.execCommand()가 느슨하게 정의되고 다양함 차이가 있습니다.

비동기 클립보드 API 잘 정의된 권한 모델을 제공하여 이러한 문제를 해결할 수 있습니다. 차단할 수 있습니다. Async Clipboard API는 텍스트와 이미지 처리로 제한됩니다. 대부분의 브라우저에서 지원되지만, 지원되는 유형은 다를 수 있습니다. 브라우저를 주의 깊게 살펴보고 호환성 개요를 읽어보세요.

복사: 클립보드에 데이터 쓰기

writeText()

텍스트를 클립보드에 복사하려면 writeText()를 호출합니다. 이 API는 비동기식이면 writeText() 함수는 전달된 텍스트가 복사되었는지 여부에 따라 거부됩니다.

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
}

브라우저 지원

  • 66
  • 79
  • 63
  • 13.1

소스

write()

실제로 writeText()는 일반 write()의 편의 메서드일 뿐입니다. 메서드를 사용하면 이미지를 클립보드에 복사할 수도 있습니다. writeText()의 경우처럼요. 비동기식이며 프로미스를 반환합니다.

클립보드에 이미지를 쓰려면 이미지가 blob 이를 위한 한 가지 방법은 fetch()를 사용하여 서버에 이미지를 요청한 다음 blob() 있습니다.

서버에서 이미지를 요청하는 것은 다양한 이유가 있을 수 있습니다 다행히 이미지를 캔버스에 그리고 캔버스에 전화 걸기 toBlob() 메서드를 사용하여 축소하도록 요청합니다.

다음으로 ClipboardItem 객체의 배열을 매개변수로 write()에 전달합니다. 메서드를 사용하여 축소하도록 요청합니다. 현재는 한 번에 하나의 이미지만 전달할 수 있지만 여러 이미지를 지원할 예정입니다. ClipboardItem는 이미지의 MIME 유형을 키로, blob을 값으로 설정합니다. blob용 fetch() 또는 canvas.toBlob()에서 가져온 객체(blob.type 속성) 이미지의 올바른 MIME 유형을 자동으로 포함합니다.

try {
  const imgURL = '/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem({
      // The key is determined dynamically based on the blob's type.
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}

또는 ClipboardItem 객체에 프로미스를 작성할 수 있습니다. 이 패턴의 경우 데이터의 MIME 유형을 미리 알아야 합니다.

try {
  const imgURL = '/images/generic/file.png';
  await navigator.clipboard.write([
    new ClipboardItem({
      // Set the key beforehand and write a promise as the value.
      'image/png': fetch(imgURL).then(response => response.blob()),
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}

브라우저 지원

  • 66
  • 79
  • 127
  • 13.1

소스

복사 이벤트

사용자가 클립보드 복사를 시작하는 경우 preventDefault()를 호출하지 않는 경우 일정 copy 에는 항목이 이미 올바른 형식으로 되어 있는 clipboardData 속성이 포함됩니다. 자체 로직을 구현하려면 preventDefault()를 호출하여 기본 동작을 방지하고 직접 구현하는 것이 좋습니다. 이 경우 clipboardData는 비어 있습니다. 텍스트와 이미지가 포함된 페이지를 생각해 보세요. 사용자가 모두 선택하고 클립보드 복사를 시작하면 커스텀 솔루션은 텍스트를 삭제하고 복사하겠습니다 아래 코드 샘플과 같이 이 작업을 수행할 수 있습니다. 이 예에서 다루지 않은 것은 이전 버전으로 돌아가는 방법입니다. Clipboard API가 지원되지 않는 경우

<!-- The image we want on the clipboard. -->
<img src="kitten.webp" alt="Cute kitten.">
<!-- Some text we're not interested in. -->
<p>Lorem ipsum</p>
document.addEventListener("copy", async (e) => {
  // Prevent the default behavior.
  e.preventDefault();
  try {
    // Prepare an array for the clipboard items.
    let clipboardItems = [];
    // Assume `blob` is the blob representation of `kitten.webp`.
    clipboardItems.push(
      new ClipboardItem({
        [blob.type]: blob,
      })
    );
    await navigator.clipboard.write(clipboardItems);
    console.log("Image copied, text ignored.");
  } catch (err) {
    console.error(err.name, err.message);
  }
});

copy 이벤트의 경우:

브라우저 지원

  • 1
  • 12
  • 22
  • 3

소스

ClipboardItem:

브라우저 지원

  • 76
  • 79
  • 127
  • 13.1

소스

붙여넣기: 클립보드에서 데이터 읽기

readText()

클립보드에서 텍스트를 읽으려면 navigator.clipboard.readText()를 호출하고 기다립니다. 다음 프라미스를 해결합니다.

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Pasted content: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
}

브라우저 지원

  • 66
  • 79
  • 125
  • 13.1

소스

read()

navigator.clipboard.read() 메서드도 비동기식이며 다음을 반환합니다. 약속하지 않죠. 클립보드에서 이미지를 읽으려면 ClipboardItem 객체에 대해 반복해야 할 수 있습니다.

ClipboardItem는 콘텐츠를 다양한 유형으로 보유할 수 있으므로 다음을 실행해야 합니다. for...of 루프를 사용하여 유형 목록을 다시 반복합니다. 각 유형에 대해 현재 유형을 인수로 사용하여 getType() 메서드를 호출하여 해당 blob을 참조하세요. 이전과 마찬가지로 이 코드는 이미지에 연결되지 않으며 다른 향후 파일 형식과 호환됩니다.

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        const blob = await clipboardItem.getType(type);
        console.log(URL.createObjectURL(blob));
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
}

브라우저 지원

  • 66
  • 79
  • 127
  • 13.1

소스

붙여넣은 파일 사용하기

키보드 단축키를 사용하면 ctrl+cctrl+v를 차례로 누름 Chromium은 아래와 같이 클립보드에 읽기 전용 파일을 노출합니다. 사용자가 운영체제의 기본 붙여넣기 단축키를 누르면 트리거됩니다. 또는 사용자가 브라우저의 메뉴 바에서 수정을 클릭한 다음 붙여넣기를 클릭하면 언제든지 붙여넣을 수 있습니다. 추가 배관 코드가 필요하지 않습니다.

document.addEventListener("paste", async e => {
  e.preventDefault();
  if (!e.clipboardData.files.length) {
    return;
  }
  const file = e.clipboardData.files[0];
  // Read the file's contents, assuming it's a text file.
  // There is no way to write back to it.
  console.log(await file.text());
});

브라우저 지원

  • 3
  • 12
  • 3.6
  • 4

소스

붙여넣기 이벤트

앞서 언급했듯이, Clipboard API와 호환되는 이벤트를 도입할 계획이 있습니다. 지금은 기존 paste 이벤트를 사용할 수 있습니다. 새로운 비동기 메서드를 사용합니다. copy 이벤트와 마찬가지로 preventDefault()를 호출하는 것을 잊지 마세요.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const text = await navigator.clipboard.readText();
  console.log('Pasted text: ', text);
});

브라우저 지원

  • 1
  • 12
  • 22
  • 3

소스

여러 MIME 유형 처리

대부분의 구현에서는 한 번의 컷을 위해 클립보드에 여러 데이터 형식을 저장합니다. 복사 작업을 수행할 수 있습니다 여기에는 두 가지 이유가 있습니다. 앱 개발자는 사용자가 텍스트나 이미지를 복사하려는 앱의 기능을 알 방법이 없을 때 많은 애플리케이션이 구조화된 데이터를 일반 텍스트로 붙여넣는 것을 지원합니다. 일반적으로 수정 메뉴 항목(예: 붙여넣기 및 일치 스타일 또는 서식 없이 붙여넣기를 선택합니다.

다음 예는 그 방법을 보여줍니다. 이 예에서는 fetch()를 사용하여 사용할 수도 있지만 <canvas> 또는 File System Access API를 참고하세요.

async function copy() {
  const image = await fetch('kitten.png').then(response => response.blob());
  const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
  const item = new ClipboardItem({
    'text/plain': text,
    'image/png': image
  });
  await navigator.clipboard.write([item]);
}

보안 및 권한

클립보드 액세스는 항상 브라우저의 보안 문제를 야기했습니다. 제외 페이지가 모든 종류의 악성 콘텐츠를 은밀하게 복사할 수 있습니다. 붙여넣으면 치명적인 결과를 초래할 수 있습니다. rm -rf / 또는 감압 폭탄 이미지 클립보드에 복사하겠습니다.

<ph type="x-smartling-placeholder">
</ph> 사용자에게 클립보드 권한을 요청하는 브라우저 메시지 <ph type="x-smartling-placeholder">
</ph> Clipboard API 권한 메시지

웹페이지에 클립보드 액세스 권한을 무제한으로 부여하는 기능이 훨씬 더 많습니다. 있습니다. 사용자는 일상적으로 비밀번호 및 비밀번호와 같은 민감한 정보를 클립보드에 개인 정보가 저장되어 있지 않아도 모든 페이지에서 읽을 수 있습니다. 사용자 지식을 제공합니다

많은 새 API와 마찬가지로, Clipboard API는 HTTPS를 사용합니다. 악용을 방지하기 위해 클립보드 액세스가 다음 경우에만 허용됩니다. 확인할 수 있습니다 활성 탭의 페이지는 작업 없이 클립보드에 쓸 수 있습니다. 클립보드에서 읽기는 항상 권한을 부여했는지 확인합니다.

복사하여 붙여넣기 권한이 Permissions API 다음과 같은 경우 clipboard-write 권한이 페이지에 자동으로 부여됩니다. 확인할 수 있습니다 clipboard-read 권한을 요청해야 합니다. 다음 작업을 할 수 있습니다. 할 수 있습니다. 다음은 후자를 보여주는 코드입니다.

const queryOpts = { name: 'clipboard-read', allowWithoutGesture: false };
const permissionStatus = await navigator.permissions.query(queryOpts);
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);

// Listen for changes to the permission state
permissionStatus.onchange = () => {
  console.log(permissionStatus.state);
};

또한 컷 또는 자르기를 호출하는 데 사용자 동작이 필요한지 여부도 제어할 수 있습니다. allowWithoutGesture 옵션을 사용하여 붙여넣기 이 값의 기본값 브라우저에 따라 다르므로 항상 포함해야 합니다.

여기에서 Clipboard API의 비동기 특성이 실제로 유용합니다. 클립보드 데이터를 읽거나 쓰려고 하면 자동으로 아직 부여되지 않은 경우 권한을 요청해야 합니다. API는 프라미스 기반이므로 이는 완전히 투명하며 사용자가 클립보드 권한을 거부하면 페이지가 적절하게 응답할 수 있도록 거부할 것입니다.

브라우저는 페이지가 활성 탭일 때만 클립보드 액세스를 허용하기 때문에 여기 있는 일부 예제를 개발자 도구 자체가 활성 탭이기 때문입니다. 한 가지 트릭: 지연 setTimeout()을(를) 사용하여 클립보드를 연결한 다음 페이지 안쪽을 빠르게 클릭하여 함수가 호출되기 전에 포커스를 설정하세요.

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

권한 정책 통합

iframe에서 API를 사용하려면 권한 정책 이 메커니즘은 특정 포드의 운영이 가능한 애플리케이션을 선택적으로 사용 설정하고 다양한 브라우저 기능 및 API가 사용 중지됩니다. 구체적으로 또는 앱의 필요에 따라 clipboard-read 또는 clipboard-write 둘 다로 지정할 수 있습니다.

<iframe
    src="index.html"
    allow="clipboard-read; clipboard-write"
>
</iframe>

특성 감지

모든 브라우저를 지원하면서 Async Clipboard API를 사용하려면 다음을 테스트하세요. navigator.clipboard하고 이전 메서드로 돌아갑니다. 예를 들어 다른 브라우저를 포함하도록 붙여넣기를 구현할 수 있습니다.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  let text;
  if (navigator.clipboard) {
    text = await navigator.clipboard.readText();
  }
  else {
    text = e.clipboardData.getData('text/plain');
  }
  console.log('Got pasted text: ', text);
});

그게 전부가 아닙니다. Async Clipboard API 이전에는 웹브라우저에서 서로 다른 복사하여 붙여넣기 구현 방법을 사용하는 경우 대부분의 브라우저에서 브라우저의 자체 복사 및 붙여넣기는 document.execCommand('copy')document.execCommand('paste') 텍스트가 DOM에 없는 문자열인 경우 DOM 및 선택됨:

button.addEventListener('click', (e) => {
  const input = document.createElement('input');
  input.style.display = 'none';
  document.body.appendChild(input);
  input.value = text;
  input.focus();
  input.select();
  const result = document.execCommand('copy');
  if (result === 'unsuccessful') {
    console.error('Failed to copy text.');
  }
  input.remove();
});

데모

아래 데모에서 Async Clipboard API를 사용해 볼 수 있습니다. Glitch에서 텍스트 데모를 또는 이미지 데모를 실험해 봅니다.

첫 번째 예는 텍스트를 클립보드 안팎으로 이동하는 방법을 보여줍니다.

이미지에 API를 사용해 보려면 이 데모를 사용하세요. PNG만 지원된다는 사실을 기억하세요. 그리고 일부 브라우저에서만 지원됩니다.

감사의 말씀

비동기 클립보드 API는 Darwin에 의해 구현되었습니다. HuangGary 카츠마르치크 또한 다윈은 데모를 제공했습니다. 도움을 주신 Kyarik님과 다시 한번 이 도움말의 일부를 검토합니다.

Markus Winkler의 히어로 이미지 스플래시 해제.