DataTransfer API를 사용한 장벽 해소

사용자가 브라우저 창 외부에서 데이터를 공유할 수 있도록 합니다.

HTML5 Drag and Drop API클립보드 이벤트의 일부인 DataTransfer API에 관해 들어보셨을 수도 있습니다. 소스와 수신 대상 간에 데이터를 전송하는 데 사용할 수 있습니다.

브라우저 지원

  • Chrome: 3.
  • Edge: 12.
  • Firefox: 3.5
  • Safari: 4.

소스

드래그 앤 드롭 및 복사 붙여넣기 상호작용은 페이지 내에서 A에서 B로 간단한 텍스트를 전송하는 상호작용에 자주 사용됩니다. 하지만 이러한 상호작용을 사용하여 브라우저 창을 넘어설 수 있다는 사실은 종종 간과됩니다.

브라우저의 기본 제공 드래그 앤 드롭과 복사 붙여넣기 상호작용은 웹이든 다른 애플리케이션이든 다른 애플리케이션과 통신할 수 있으며 출처에 연결되지 않습니다. 이 API는 데이터가 전송되는 위치에 따라 서로 다른 동작을 가진 여러 데이터 항목을 지원합니다. 웹 애플리케이션은 수신 이벤트를 리슨할 때 전송된 데이터를 주고받을 수 있습니다.

이 기능은 데스크톱 웹 애플리케이션의 공유 및 상호 운용성에 대한 생각을 바꿀 수 있습니다. 애플리케이션 간에 데이터를 전송할 때 더 이상 밀접하게 결합된 통합에 의존할 필요가 없습니다. 대신 사용자에게 데이터를 원하는 곳으로 전송할 수 있는 전체 제어 권한을 부여할 수 있습니다.

DataTransfer API로 가능한 상호작용의 예시입니다. (동영상에 소리가 포함되지 않음)

데이터 전송

시작하려면 드래그 앤 드롭 또는 복사 붙여넣기를 구현해야 합니다. 아래 예에서는 드래그 앤 드롭 상호작용을 보여주지만 복사 붙여넣기 프로세스도 비슷합니다. 드래그 앤 드롭 API에 익숙하지 않다면 HTML5 드래그 앤 드롭을 설명하는 유용한 도움말을 참고하세요.

MIME 유형 키 데이터를 제공하면 외부 애플리케이션과 자유롭게 상호작용할 수 있습니다. 대부분의 WYSIWYG 편집기, 텍스트 편집기, 브라우저는 아래 예시에서 사용된 '원시' mime 유형에 응답합니다.

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', 'Foo bar');
  event.dataTransfer.setData('text/html', '<h1>Foo bar</h1>');
  event.dataTransfer.setData('text/uri-list', 'https://example.com');
});

event.dataTransfer 속성을 확인합니다. 그러면 DataTransfer 인스턴스가 반환됩니다. 이 객체는 다른 이름의 속성에서 반환되는 경우도 있습니다.

데이터 전송을 수신하는 방법은 제공하는 방법과 거의 동일합니다. 수신 이벤트(drop 또는 paste)를 수신 대기하고 키를 읽습니다. 요소 위로 드래그할 때 브라우저는 데이터의 type 키에만 액세스할 수 있습니다. 데이터 자체는 게시 후에만 액세스할 수 있습니다.

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  console.log(event.dataTransfer.types);
  // Without this, the drop event won't fire.
  event.preventDefault();
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  // Log all the transferred data items to the console.
  for (let type of event.dataTransfer.types) {
    console.log({ type, data: event.dataTransfer.getData(type) });
  }
  event.preventDefault();
});

애플리케이션 전반에서 널리 지원되는 세 가지 MIME 유형은 다음과 같습니다.

  • text/html: contentEditable 요소 및 Google Docs, Microsoft Word 등의 리치 텍스트 (WYSIWYG) 편집기에서 HTML 페이로드를 렌더링합니다.
  • text/plain: 입력 요소의 값, 코드 편집기의 콘텐츠, text/html의 대체 값을 설정합니다.
  • text/uri-list: URL 표시줄이나 브라우저 페이지에 드롭하면 URL로 이동합니다. 디렉터리 또는 데스크톱에 드롭하면 URL 바로가기가 생성됩니다.

WYSIWYG 편집기에서 text/html를 광범위하게 채택하여 매우 유용합니다. HTML 문서에서와 마찬가지로 데이터 URL 또는 공개적으로 액세스할 수 있는 URL을 사용하여 리소스를 삽입할 수 있습니다. 이는 Google Docs와 같은 편집기로 시각화를 내보낼 때 유용합니다 (예: 캔버스에서).

const redPixel = 'data:image/gif;base64,R0lGODdhAQABAPAAAP8AAAAAACwAAAAAAQABAAACAkQBADs=';
const html = '<img src="' + redPixel + '" width="100" height="100" alt="" />';
event.dataTransfer.setData('text/html', html);

복사하여 붙여넣기를 사용한 전송

복사-붙여넣기 상호작용과 함께 DataTransfer API를 사용하는 방법은 다음과 같습니다. DataTransfer 객체는 클립보드 이벤트의 clipboardData 속성에서 반환됩니다.

// Listen to copy-paste events on the document.
document.addEventListener('copy', (event) => {
  const copySource = document.querySelector('#copySource');
  // Only copy when the `activeElement` (i.e., focused element) is,
  // or is within, the `copySource` element.
  if (copySource.contains(document.activeElement)) {
    event.clipboardData.setData('text/plain', 'Foo bar');
    event.preventDefault();
  }
});

document.addEventListener('paste', (event) => {
  const pasteTarget = document.querySelector('#pasteTarget');
  if (pasteTarget.contains(document.activeElement)) {
    const data = event.clipboardData.getData('text/plain');
    console.log(data);
  }
});

맞춤 데이터 형식

원시 MIME 유형으로 제한되지 않으며 어떤 키든 사용하여 전송된 데이터를 식별할 수 있습니다. 이는 애플리케이션 내에서 교차 브라우저 상호작용에 유용할 수 있습니다. 아래와 같이 JSON.stringify()JSON.parse() 함수를 사용하여 더 복잡한 데이터를 전송할 수 있습니다.

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  const data = { foo: 'bar' };
  event.dataTransfer.setData('my-custom-type', JSON.stringify(data));
});

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  // Only allow dropping when our custom data is available.
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
  }
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
    const dataString = event.dataTransfer.getData('my-custom-type');
    const data = JSON.parse(dataString);
    console.log(data);
  }
});

웹 연결

맞춤 형식은 개발자가 제어하는 애플리케이션 간의 통신에 적합하지만, 개발자가 사용하는 형식이 아닌 애플리케이션으로 데이터를 전송할 때는 사용자를 제한합니다. 웹에서 서드 파티 애플리케이션과 연결하려면 범용 데이터 형식이 필요합니다.

JSON-LD (연결된 데이터) 표준이 여기에 적합합니다. 가볍고 JavaScript에서 읽고 쓰기 쉽습니다. Schema.org에는 사용할 수 있는 사전 정의된 유형이 많이 포함되어 있으며 맞춤 스키마 정의도 사용할 수 있습니다.

const data = {
  '@context': 'https://schema.org',
  '@type': 'ImageObject',
  contentLocation: 'Venice, Italy',
  contentUrl: 'venice.jpg',
  datePublished: '2010-08-08',
  description: 'I took this picture during our honey moon.',
  name: 'Canal in Venice',
};
event.dataTransfer.setData('application/ld+json', JSON.stringify(data));

Schema.org 유형을 사용할 때는 일반적인 Thing 유형으로 시작하거나 Event, Person, MediaObject, Place와 같이 사용 사례에 더 가까운 유형을 사용하거나 필요한 경우 MedicalEntity와 같이 매우 구체적인 유형을 사용할 수 있습니다. TypeScript를 사용하는 경우 schema-dts 유형 정의의 인터페이스 정의를 사용할 수 있습니다.

JSON-LD 데이터를 전송 및 수신하면 더 연결되고 개방된 웹을 지원할 수 있습니다. 동일한 언어를 사용하는 애플리케이션을 사용하면 외부 애플리케이션과의 심층 통합을 만들 수 있습니다. 복잡한 API 통합이 필요하지 않습니다. 필요한 모든 정보가 전송된 데이터에 포함되어 있습니다.

캘린더의 일정을 좋아하는 할 일 앱에 공유하고, 이메일에 가상 파일을 첨부하고, 연락처를 공유하는 등 제한 없이 (웹) 애플리케이션 간에 데이터를 전송할 수 있는 모든 가능성을 생각해 보세요. 좋을 것 같네요. 먼저 시작해 보세요. 🙌

문제

현재 DataTransfer API를 사용할 수 있지만 통합하기 전에 알아두어야 할 몇 가지 사항이 있습니다.

브라우저 호환성

데스크톱 브라우저는 모두 위에 설명된 기법을 잘 지원하지만 휴대기기는 그렇지 않습니다. 이 기법은 모든 주요 브라우저 (Chrome, Edge, Firefox, Safari)와 운영체제 (Android, ChromeOS, iOS, macOS, Ubuntu Linux, Windows)에서 테스트되었지만 안타깝게도 Android와 iOS는 테스트를 통과하지 못했습니다. 브라우저가 계속 개발되고 있지만 현재 이 기법은 데스크톱 브라우저로만 제한됩니다.

발견 가능성

드래그 앤 드롭과 복사 붙여넣기는 데스크톱 컴퓨터에서 작업할 때의 시스템 수준 상호작용으로, 40년이 넘은 첫 번째 GUI에 그 뿌리를 두고 있습니다. 파일을 정리하는 데 이러한 상호작용을 사용한 횟수를 생각해 보세요. 이는 아직 웹에서 흔하지 않습니다.

사용자에게 이 새로운 상호작용에 관해 안내하고, 특히 지금까지 컴퓨터 경험이 모바일 기기에만 국한된 사용자를 위해 이를 인식할 수 있는 UX 패턴을 고안해야 합니다.

접근성

드래그 앤 드롭은 접근성이 높지 않지만 DataTransfer API는 복사 붙여넣기에서도 작동합니다. 복사-붙여넣기 이벤트를 수신 대기해야 합니다. 추가 작업이 별로 필요하지 않으며 추가하면 사용자에게 큰 도움이 됩니다.

보안 및 개인 정보 보호

이 기법을 사용할 때는 몇 가지 보안 및 개인 정보 보호 고려사항을 알고 있어야 합니다.

  • 클립보드 데이터는 사용자 기기의 다른 애플리케이션에서 사용할 수 있습니다.
  • 드래그하는 웹 애플리케이션은 데이터가 아닌 유형 키에 액세스할 수 있습니다. 데이터는 드롭 또는 붙여넣기 시점에만 사용할 수 있습니다.
  • 수신된 데이터는 다른 사용자 입력과 마찬가지로 취급해야 합니다. 사용하기 전에 정리하고 유효성을 검사하세요.

Transmat 도우미 라이브러리 시작하기

애플리케이션에서 DataTransfer API를 사용해 보고 싶으신가요? GitHub의 Transmat 라이브러리를 살펴보세요. 이 오픈소스 라이브러리는 브라우저 차이를 조정하고, JSON-LD 유틸리티를 제공하며, 드롭 영역 강조 표시를 위한 전송 이벤트에 응답하는 관찰자를 포함하고, 기존 드래그 앤 드롭 구현 간에 데이터 전송 작업을 통합할 수 있도록 지원합니다.

import { Transmat, TransmatObserver, addListeners } from 'transmat';

// Send data on drag/copy.
addListeners(myElement, 'transmit', (event) => {
  const transmat = new Transmat(event);
  transmat.setData({
    'text/plain': 'Foobar',
    'application/json': { foo: 'bar' },
  });
});

// Receive data on drop/paste.
addListeners(myElement, 'receive', (event) => {
  const transmat = new Transmat(event);
  if (transmat.hasType('application/json') && transmat.accept()) {
    const data = JSON.parse(transmat.getData('application/json'));
  }
});

// Observe transfer events and highlight drop areas.
const obs = new TransmatObserver((entries) => {
  for (const entry of entries) {
    const transmat = new Transmat(entry.event);
    if (transmat.hasMimeType('application/json')) {
      entry.target.classList.toggle('drag-over', entry.isTarget);
      entry.target.classList.toggle('drag-active', entry.isActive);
    }
  }
});
obs.observe(myElement);

감사의 말씀

UnsplashLuba Ertel님 제공 히어로 이미지