많은 사이트에서 버튼을 클릭하면 정해진 텍스트를 클립보드로 카피할 수 있는 기능을 사용하고 있습니다.
copy-gif.gif

클립보드에 데이터를 복사하는 방법은 execCommand API 를 사용하거나 ClipboardAPI 를 사용하는 두 가지 방법이 있습니다. ClipboardAPI는 비교적 최신 스펙으로, 아직 지원되지 않는 브라우저가 많습니다.

image.png

이 글에서는 execCommand API를 사용한 클립보드 복사 방법과, safari ios환경에서의 트러블슈팅을 다룹니다.

execCommand를 이용한 복사

버튼을 클릭하여 클립보드로 복사하는 스크립트의 순서는 다음과 같습니다.

  1. 텍스트 값을 가진 <textarea> 혹은 <input> 엘리먼트를 임시로 생성하여 문서에 추가
  2. 텍스트 영역을 선택하여 execCommand("copy") 메소드를 사용해 클립보드로 복사
  3. 마지막으로 해당 엘리먼트를 제거
<button class="copy">clipboard copy</button>
document.querySelector(".copy").addEventListener("click", function(){
  var tempElem = document.createElement('textarea');
  tempElem.value = 'I am copied text!';  
  document.body.appendChild(tempElem);

  tempElem.select();
  document.execCommand("copy");
  document.body.removeChild(tempElem);
});

https://stackblitz.com/edit/simple-copy-clipboard

select() 메소드는 textarea 나 input과 같은 form 엘리먼트에서만 사용할 수 있습니다.

하지만 exeCommand() 메소드를 사용하여 copy 동작을 수행할 때, 브라우저 환경과 버전에 따라 보안 정책과 구현 방법이 다르기 때문에 특정 환경에서는 지원되지 않을 수도 있으며 동작에 선행되는 조건이 필요하기도 합니다.

가령, 위에서 구현한 클립보드 복사 코드는 현재 IOS safari에서 동작하지 않습니다. 모바일 사파리 브라우저는 execCommand의 copy 기능을 수행하기 위해서 유효한 범위의 선택 영역이 필요합니다.

textarea 엘리먼트의 select() 와 함께 setSelectionRange 메소드로 선택 영역을 직접 지정해주면 ios 사파리에서 동작합니다. setSelectionRange의 인자로 시작 위치와 마지막 위치(텍스트를 포함할만한 큰 값)를 지정해주면 이를 유효한 범위로 인식하여 execCommand('copy') 동작이 정상 수행됩니다.

하지만 이 코드도 완벽한 해결책은 아니며, os 버전이나 키보드 설정 환경에 따라 동작하지 않을 수도 있습니다. 가령, 엘리먼트 focus()가 필요하거나, contentEditable, readonly 옵션을 변경해주거나, 생성한 textarea의 스타일을 변경하기와 같은 꼼수가 필요합니다. 참고

  function copyIOS(string){
  var textarea = document.createElement('textarea');
  textarea.value = string;

  document.body.appendChild(textarea);
  textarea.select();
  textarea.setSelectionRange(0, 9999);  // 추가

  document.execCommand('copy');
  document.body.removeChild(textarea);
}

Selection

추가로 선택 영역에 대해 잠깐 알아보겠습니다.

selection은 유저가 선택한 영역의 범위를 나타내는 객체입니다. 마우스나 키보드로 선택할 수 있으며, 아래 이미지에서 파란 색으로 표시된 영역입니다.

현재 선택 된 영역의 정보는 window.getSelection() 을 통해 얻을 수 있습니다.
image.png

다음은 selection의 주요 개념들입니다.

  • 앵커 anchor
    선택 영역selection의 첫 지점을 앵커라고 합니다. 키보드나 마우스로 문서를 드래그해서 선택 영역을 생성할 때 문서를 클릭하는 첫 부분입니다. 유저가 마우스를 움직여 선택 영역의 범위를 바꾸더라도 첫 지점의 위치는 변하지 않기 때문에 앵커라고 부릅니다.
  • 포커스 focus
    포커스는 선택 영역의 끝 점을 말합니다. 마우스로 선택 영역을 만들었을 때 마우스 버튼에서 손을 떼는 지점입니다. 마우스나 키보드로 선택 영역을 바꿀 때 포커스도 같이 변경됩니다.

  • 레인지 range
    레인지는 문서의 인접한 부분을 말합니다. 레인지는 노드 부분이나 노드 전체를 포함할 수 있으며, 가령 텍스트 노드의 일부가 될 수 있습니다. 기본적으로 한 번에 단일한 범위를 선택할 수 있지만, 유저가 컨트롤 키 등을 누른 채로 여러 범위를 선택할 수도 있습니다. range 객체는 selection에서 얻을 수 있으며, DOM을 통해 동적으로 추가하거나 생성할 수 있습니다.

다음은 range와 selection을 활용해서 윈도우 전체의 선택 영역을 초기화하고 textarea의 유효한 범위를 지정하는 코드입니다.

var textarea = document.createElement('textarea');
textArea.value = text;

var range = document.createRange();
range.selectNodeContents(textArea);

var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);

textArea.setSelectionRange(0, 999999);

clipboard.js 라이브러리

클립보드 복사 기능을 지원하는 라이브러리인 clipboard.js 는 폭 넓은 브라우저를 지원하며, 복사가 완료된 것을 확인할 수 있는 툴팁을 제공합니다. 이 라이브러리 역시 Selection 과 execCommand API에 의존하고 있습니다.

가능하면 clipboard.js 라이브러리 사용을 추천하지만, 요구 조건에 따라 직접 구현이 필요한 경우가 있어 관련 내용을 정리해 보았습니다.

Ref