웹 기능 Codelab

1. 소개 및 설정

웹 기능

Google은 웹과 네이티브 간의 기능 격차를 해소하고 개발자가 개방형 웹에서 멋진 환경을 쉽게 구축할 수 있도록 지원하고자 합니다. Google은 모든 개발자가 우수한 웹 환경을 만드는 데 필요한 기능에 액세스할 수 있어야 한다고 생각하며, 더 강력한 웹을 위해 노력하고 있습니다.

하지만 파일 시스템 액세스, 유휴 감지와 같은 일부 기능은 네이티브에서는 사용할 수 있지만 웹에서는 사용할 수 없습니다. 이러한 기능이 누락되면 일부 유형의 앱을 웹에서 제공할 수 없거나 유용성이 떨어집니다.

Google은 상호 운용 가능한 설계를 보장하기 위해 설계를 반복하면서 개발자 및 기타 브라우저 공급업체로부터 초기 의견을 수렴하는 동시에 기존 개방형 웹 플랫폼 표준 프로세스를 사용하여 개방적이고 투명한 방식으로 이러한 새로운 기능을 설계하고 개발할 것입니다.

빌드할 항목

이 Codelab에서는 완전히 새로운 웹 API 또는 플래그 뒤에서만 사용할 수 있는 웹 API를 다양하게 살펴봅니다. 따라서 이 Codelab에서는 특정 최종 제품을 빌드하는 대신 API 자체와 이러한 API로 구현할 수 있는 사용 사례에 중점을 둡니다.

학습할 내용

이 Codelab에서는 최첨단 API의 기본 메커니즘을 알아봅니다. 이러한 메커니즘은 아직 확정되지 않았으며 개발자 흐름에 관한 의견을 보내주시면 감사하겠습니다.

필요한 항목

이 Codelab에 소개된 API는 최신 기술을 사용하므로 각 API의 요구사항이 다릅니다. 각 섹션의 시작 부분에 있는 호환성 정보를 주의 깊게 읽어보시기 바랍니다.

Codelab에 접근하는 방법

이 Codelab은 순차적으로 진행하지 않아도 됩니다. 각 섹션은 독립적인 API를 나타내므로 가장 관심 있는 내용을 선택하세요.

2. Badging API

배지 API의 목표는 백그라운드에서 발생하는 일에 사용자의 관심을 유도하는 것입니다. 이 Codelab의 데모를 간단하게 만들기 위해 API를 사용하여 포그라운드에서 발생하는 일에 사용자의 관심을 끌어 보겠습니다. 그런 다음 백그라운드에서 발생하는 일로 정신적 전환을 할 수 있습니다.

Airhorner 설치

이 API가 작동하려면 홈 화면에 설치된 PWA가 필요하므로 첫 번째 단계는 악명 높고 세계적으로 유명한 airhorner.com과 같은 PWA를 설치하는 것입니다. 오른쪽 상단의 설치 버튼을 누르거나 점 3개 메뉴를 사용하여 수동으로 설치합니다.

8b7fa8b3c94c6bdd.png

확인 메시지가 표시되면 설치를 클릭합니다.

98e90422167ac786.png

이제 운영체제의 도크에 새 아이콘이 표시됩니다. 클릭하여 PWA를 실행합니다. 자체 앱 창이 있으며 독립형 모드로 실행됩니다.

배지 설정

이제 PWA가 설치되었으므로 배지에 표시할 숫자 데이터 (배지에는 숫자만 포함될 수 있음)가 필요합니다. The Air Horner에서 간단하게 셀 수 있는 것은 한숨, 경적을 울린 횟수입니다. 설치된 Airhorner 앱으로 경적을 울리고 배지를 확인해 보세요. 경적을 울릴 때마다 1씩 증가합니다.

b5e39de7a1775054.png

그렇다면 어떻게 작동할까요? 기본적으로 코드는 다음과 같습니다.

let hornCounter = 0;
const horn = document.querySelector('.horn');
horn.addEventListener('click', () => {
  navigator.setExperimentalAppBadge(++hornCounter);
});

에어혼을 몇 번 울리고 PWA의 아이콘을 확인합니다. 에어혼이 울릴 때마다 아이콘이 업데이트됩니다. 간단하죠?

eed10c3ffe59999.png

배지 지우기

카운터는 99까지 올라간 후 다시 시작됩니다. 수동으로 재설정할 수도 있습니다. DevTools 콘솔 탭을 열고 아래 줄을 붙여넣은 후 Enter 키를 누릅니다.

navigator.setExperimentalAppBadge(0);

또는 다음 스니펫과 같이 명시적으로 배지를 지워 배지를 없앨 수도 있습니다. 이제 PWA의 아이콘이 처음과 같이 배지 없이 선명하게 표시됩니다.

navigator.clearExperimentalAppBadge();

33eafb314a3a9f30.png

의견

이 API에 관해 어떻게 생각하시나요? 간단한 설문조사에 응답하여 Google을 도와주세요.

이 API를 사용하기에 직관적이었나요?

아니요

예를 실행하셨나요?

아니요

더 추가할 내용이 있으신가요? 누락된 기능이 있었나요? 이 설문조사를 통해 간단한 의견을 보내주세요. 감사합니다.

3. Native File System API

Native File System API를 사용하면 개발자가 사용자의 로컬 기기에 있는 파일과 상호작용하는 강력한 웹 앱을 빌드할 수 있습니다. 사용자가 웹 앱에 액세스 권한을 부여하면 이 API를 통해 웹 앱이 사용자 기기의 파일 및 폴더를 직접 읽거나 변경사항을 저장할 수 있습니다.

파일 읽기

Native File System API의 'Hello, world'는 로컬 파일을 읽고 파일 콘텐츠를 가져오는 것입니다. 일반 .txt 파일을 만들고 텍스트를 입력합니다. 그런 다음 example.com과 같은 보안 사이트 (HTTPS를 통해 제공되는 사이트)로 이동하여 DevTools 콘솔을 엽니다. 콘솔에 아래 코드 스니펫을 붙여넣습니다. Native File System API에는 사용자 동작이 필요하므로 문서에 더블클릭 핸들러를 연결합니다. 나중에 파일 핸들이 필요하므로 전역 변수로 만듭니다.

document.ondblclick = async () => {
  window.handle = await window.chooseFileSystemEntries();
  const file = await handle.getFile();
  document.body.textContent = await file.text();
};

c02679081eb4d538.png

그런 다음 example.com 페이지의 아무 곳이나 더블클릭하면 파일 선택기가 표시됩니다.

d98962600d62d149.png

이전에 만든 .txt 파일을 선택합니다. 그러면 파일 콘텐츠가 example.com의 실제 body 콘텐츠를 대체합니다.

eace3d15bd4f8192.png

파일 저장

다음으로 몇 가지 변경사항을 적용해 보겠습니다. 따라서 아래 코드 스니펫을 붙여넣어 body를 수정 가능하게 만듭니다. 이제 브라우저가 텍스트 편집기인 것처럼 텍스트를 수정할 수 있습니다.

document.body.contentEditable = true;

ca32797417449343.png

이제 이러한 변경사항을 원본 파일에 다시 작성하려고 합니다. 따라서 파일 핸들에 대한 쓰기 권한이 필요하며, 콘솔에 아래 스니펫을 붙여넣어 이 권한을 얻을 수 있습니다. 이번에도 사용자 동작이 필요하므로 이번에는 기본 문서에서 클릭을 기다립니다.

document.onclick = async () => {
  const writer = await handle.createWriter();
  await writer.truncate(0);
  await writer.write(0, document.body.textContent);
  await writer.close();
};

d2729a8f76f43073.png

이제 문서를 더블클릭하지 않고 클릭하면 권한 메시지가 표시됩니다. 권한을 부여하면 파일의 콘텐츠는 이전에 body에서 수정한 내용이 됩니다. 다른 편집기에서 파일을 열어 변경사항을 확인합니다 (또는 문서를 다시 더블클릭하고 파일을 다시 열어 프로세스를 다시 시작합니다).

2eccf61fe4a46109.png

202263abdedae737.png

축하합니다. 방금 세계에서 가장 작은 텍스트 편집기 [citation needed]를 만들었습니다.

의견

이 API에 관해 어떻게 생각하시나요? 간단한 설문조사에 응답하여 Google을 도와주세요.

이 API를 사용하기에 직관적이었나요?

아니요

예를 실행하셨나요?

아니요

더 추가할 내용이 있으신가요? 누락된 기능이 있었나요? 이 설문조사를 통해 간단한 의견을 보내주세요. 감사합니다.

4. Shape Detection API

Shape Detection API는 가속화된 모양 감지기 (예: 사람 얼굴)에 대한 액세스를 제공하며 정지 이미지 또는 실시간 이미지 피드에서 작동합니다. 운영체제에는 Android FaceDetector와 같이 성능이 우수하고 고도로 최적화된 기능 감지기가 있습니다. 모양 감지 API는 이러한 네이티브 구현을 열고 JavaScript 인터페이스 집합을 통해 노출합니다.

현재 지원되는 기능은 FaceDetector 인터페이스를 통한 얼굴 인식, BarcodeDetector 인터페이스를 통한 바코드 감지, TextDetector 인터페이스를 통한 텍스트 감지 (광학 문자 인식)입니다.

얼굴 인식

Shape Detection API의 흥미로운 기능은 얼굴 인식입니다. 테스트하려면 얼굴이 있는 페이지가 필요합니다. 작성자의 얼굴이 표시된 이 페이지를 시작으로 하면 좋습니다. 아래 스크린샷과 같이 표시됩니다. 지원되는 브라우저에서는 얼굴의 경계 상자와 얼굴 특징이 인식됩니다.

Glitch 프로젝트, 특히 script.js 파일을 리믹스하거나 수정하면 이 작업을 수행하는 데 필요한 코드가 얼마나 적은지 확인할 수 있습니다.

f4aa7b77a0a1d1f5.png

저자의 얼굴뿐만 아니라 완전히 동적인 방식으로 작업하려면 비공개 탭이나 게스트 모드에서 얼굴이 가득한 이 Google 검색 결과 페이지로 이동하세요. 이제 해당 페이지에서 아무 곳이나 마우스 오른쪽 버튼으로 클릭한 다음 검사를 클릭하여 Chrome 개발자 도구를 엽니다. 그런 다음 콘솔 탭에 아래 스니펫을 붙여넣습니다. 코드에서 감지된 얼굴을 반투명 빨간색 상자로 강조 표시합니다.

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const faces = await new FaceDetector().detect(img);
    faces.forEach(face => {
      const div = document.createElement('div');
      const box = face.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left + left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      img.before(div);
    });
  } catch(e) {
    console.error(e);
  }
});

DOMException 메시지가 표시되고 일부 이미지가 처리되지 않습니다. 스크롤 없이 볼 수 있는 부분의 이미지는 데이터 URI로 인라인 처리되어 액세스할 수 있지만 스크롤해야 볼 수 있는 부분의 이미지는 CORS를 지원하도록 구성되지 않은 다른 도메인에서 가져오기 때문입니다. 데모를 위해 이 부분은 신경 쓰지 않아도 됩니다.

얼굴 랜드마크 감지

macOS는 얼굴 자체뿐만 아니라 얼굴 랜드마크 감지도 지원합니다. 얼굴 특징점 감지를 테스트하려면 다음 스니펫을 콘솔에 붙여넣습니다. crbug.com/914348로 인해 랜드마크 라인업이 완벽하지는 않지만 이 기능이 어디로 향하고 얼마나 강력한지 확인할 수 있습니다.

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const faces = await new FaceDetector().detect(img);
    faces.forEach(face => {
      const div = document.createElement('div');
      const box = face.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left + left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      img.before(div);

      const landmarkSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      landmarkSVG.style.position = 'absolute';
      landmarkSVG.classList.add('landmarks');
      landmarkSVG.setAttribute('viewBox', `0 0 ${img.width} ${img.height}`);
      landmarkSVG.style.width = `${img.width}px`;
      landmarkSVG.style.height = `${img.height}px`;
      face.landmarks.map((landmark) => {                    
        landmarkSVG.innerHTML += `<polygon class="landmark-${landmark.type}" points="${
        landmark.locations.map((point) => {          
          return `${scaleX * point.x},${scaleY * point.y} `;
        }).join(' ')
      }" /></svg>`;          
      });
      div.before(landmarkSVG);
    });
  } catch(e) {
    console.error(e);
  }
});

바코드 감지

Shape Detection API의 두 번째 기능은 바코드 감지입니다. 이전과 마찬가지로 바코드가 있는 페이지(예: 이 페이지)가 필요합니다. 브라우저에서 열면 다양한 QR 코드가 해독되어 표시됩니다. Glitch 프로젝트, 특히 script.js 파일을 리믹스하거나 수정하여 방법을 확인하세요.

7778003ff472389b.png

더 동적인 이미지를 원한다면 Google 이미지 검색을 다시 사용할 수 있습니다. 이번에는 브라우저에서 비공개 탭이나 게스트 모드로 이 Google 검색 결과 페이지로 이동합니다. 이제 아래 스니펫을 Chrome DevTools 콘솔 탭에 붙여넣습니다. 잠시 후 인식된 바코드에 원시 값과 바코드 유형이 주석으로 표시됩니다.

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const barcodes = await new BarcodeDetector().detect(img);
    barcodes.forEach(barcode => {
      const div = document.createElement('div');
      const box = barcode.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 255, 255, 0.75)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left - left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      div.style.color = 'black';
      div.style.fontSize = '14px';      
      div.textContent = `${barcode.rawValue}`;
      img.before(div);
    });
  } catch(e) {
    console.error(e);
  }
});

텍스트 인식

Shape Detection API의 마지막 기능은 텍스트 감지입니다. 이제 방법을 아실 겁니다. Google 도서 스캔 결과가 포함된 이 페이지와 같이 텍스트가 포함된 이미지가 있는 페이지가 필요합니다. 지원되는 브라우저에서는 인식된 텍스트와 텍스트 구절 주위에 그려진 경계 상자가 표시됩니다. Glitch 프로젝트, 특히 script.js 파일을 리믹스하거나 수정하여 방법을 확인하세요.

ec2be17d1e4d01ba.png

동적으로 테스트하려면 비공개 탭이나 게스트 모드에서 이 검색 결과 페이지로 이동하세요. 이제 아래 스니펫을 Chrome DevTools 콘솔 탭에 붙여넣습니다. 잠시 기다리면 일부 텍스트가 인식됩니다.

document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
  try {
    const texts = await new TextDetector().detect(img);
    texts.forEach(text => {
      const div = document.createElement('div');
      const box = text.boundingBox;
      const computedStyle = getComputedStyle(img);
      const [top, right, bottom, left] = [
        computedStyle.marginTop,
        computedStyle.marginRight,
        computedStyle.marginBottom,
        computedStyle.marginLeft
      ].map(m => parseInt(m, 10));
      const scaleX = img.width / img.naturalWidth;
      const scaleY = img.height / img.naturalHeight;
      div.style.backgroundColor = 'rgba(255, 255, 255, 0.75)';
      div.style.position = 'absolute';
      div.style.top = `${scaleY * box.top + top}px`;
      div.style.left = `${scaleX * box.left - left}px`;
      div.style.width = `${scaleX * box.width}px`;
      div.style.height = `${scaleY * box.height}px`;
      div.style.color = 'black';
      div.style.fontSize = '14px';      
      div.innerHTML = text.rawValue;
      img.before(div);
    });
  } catch(e) {
    console.error(e);
  }
});

의견

이 API에 관해 어떻게 생각하시나요? 간단한 설문조사에 응답하여 Google을 도와주세요.

이 API를 사용하기에 직관적이었나요?

아니요

예를 실행하셨나요?

아니요

더 추가할 내용이 있으신가요? 누락된 기능이 있었나요? 이 설문조사를 통해 간단한 의견을 보내주세요. 감사합니다.

5. Web Share Target API

Web Share Target API를 사용하면 설치된 웹 앱이 기본 운영체제에 공유 타겟으로 등록하여 Web Share API 또는 운영체제 수준 공유 버튼과 같은 시스템 이벤트에서 공유 콘텐츠를 수신할 수 있습니다.

PWA를 설치하여 공유하기

먼저 공유할 PWA가 필요합니다. 이번에는 Airhorner가 작동하지 않지만 Web Share Target 데모 앱이 있습니다. 앱을 기기의 홈 화면에 설치합니다.

925df16a12638bb2.png

PWA에 공유하기

다음으로 Google 포토의 사진과 같이 공유할 항목이 필요합니다. 공유 버튼을 사용하고 스크랩북 PWA를 공유 대상으로 선택합니다.

7216e8bb1be6d6db.png

앱 아이콘을 탭하면 스크랩북 PWA로 바로 이동하며 사진이 바로 표시됩니다.

9016985cb4bb48fe.png

그렇다면 어떻게 작동할까요? 이를 확인하려면 스크랩북 PWA의 웹 앱 매니페스트를 살펴보세요. Web Share Target API가 작동하도록 하는 구성은 매니페스트의 "share_target" 속성에 있으며, 이 속성의 "action" 필드는 "params"에 나열된 매개변수로 장식된 URL을 가리킵니다.

그러면 공유 측에서 이 URL 템플릿을 적절하게 채웁니다 (공유 작업을 통해 지원되거나 개발자가 Web Share API를 사용하여 프로그래매틱 방식으로 제어). 그러면 수신 측에서 매개변수를 추출하여 표시하는 등의 작업을 할 수 있습니다.

{
  "action": "/_share-target",
  "enctype": "multipart/form-data",
  "method": "POST",
  "params": {
    "files": [{
      "name": "media",
      "accept": ["audio/*", "image/*", "video/*"]
    }]
  }
}

의견

이 API에 관해 어떻게 생각하시나요? 간단한 설문조사에 응답하여 Google을 도와주세요.

이 API를 사용하기에 직관적이었나요?

아니요

예를 실행하셨나요?

아니요

더 추가할 내용이 있으신가요? 누락된 기능이 있었나요? 이 설문조사를 통해 간단한 의견을 보내주세요. 감사합니다.

6. Wake Lock API

배터리가 많이 소모되지 않도록 대부분의 기기는 유휴 상태가 되면 빠르게 절전 모드로 전환됩니다. 대부분의 경우 문제가 없지만 일부 애플리케이션은 작업을 완료하기 위해 화면이나 기기를 절전 모드에서 해제해야 합니다. Wake Lock API는 기기에서 화면을 어둡게 하거나 잠그지 못하도록 하거나 기기가 절전 모드로 전환되지 않도록 하는 방법을 제공합니다. 이 기능을 사용하면 지금까지 네이티브 앱이 필요했던 새로운 환경을 구현할 수 있습니다.

화면 보호기 설정

Wake Lock API를 테스트하려면 먼저 기기가 절전 모드로 전환될 수 있는지 확인해야 합니다. 따라서 운영체제의 환경설정 창에서 원하는 화면 보호기를 활성화하고 1분 후에 시작되도록 설정하세요. 기기를 정확히 그 시간 동안 그대로 두어 작동하는지 확인합니다 (힘들다는 것을 알고 있습니다). 아래 스크린샷은 macOS를 보여주지만, 모바일 Android 기기나 지원되는 데스크톱 플랫폼에서도 시도해 볼 수 있습니다.

6f345e8c2b6c22c.png

화면 절전 모드 해제 잠금 설정

이제 화면 보호기가 작동하는 것을 알았으므로 "screen" 유형의 절전 모드 해제 잠금을 사용하여 화면 보호기가 작업을 실행하지 못하도록 합니다. Wake Lock 데모 앱으로 이동하여 활성화

screen Wake Lock 체크박스

12ed15dd94f51d4d.png

이때부터 절전 모드 해제 잠금이 활성화됩니다. 기기를 1분 동안 그대로 두면 화면 보호기가 시작되지 않습니다.

어떻게 작동할까요? 자세한 내용은 Wake Lock 데모 앱의 Glitch 프로젝트로 이동하여 script.js를 확인하세요. 코드의 요지는 아래 스니펫에 있습니다. 새 탭을 열거나 열려 있는 탭을 사용하여 Chrome 개발자 도구 콘솔에 아래 코드를 붙여넣습니다. 창을 클릭하면 정확히 10초 동안 활성 상태인 절전 모드 해제 잠금이 표시되고 (콘솔 로그 참고) 화면 보호기가 시작되지 않습니다.

if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {  
  let wakeLock = null;
  
  const requestWakeLock = async () => {
    try {
      wakeLock = await navigator.wakeLock.request('screen');
      wakeLock.addEventListener('release', () => {        
        console.log('Wake Lock was released');                    
      });
      console.log('Wake Lock is active');      
    } catch (e) {      
      console.error(`${e.name}, ${e.message}`);
    } 
  };

  requestWakeLock();
  window.setTimeout(() => {
    wakeLock.release();
  }, 10 * 1000);
}

621c2654d06a7cce.png

의견

이 API에 관해 어떻게 생각하시나요? 간단한 설문조사에 응답하여 Google을 도와주세요.

이 API를 사용하기에 직관적이었나요?

아니요

예를 실행하셨나요?

아니요

더 추가할 내용이 있으신가요? 누락된 기능이 있었나요? 이 설문조사를 통해 간단한 의견을 보내주세요. 감사합니다.

7. Contact Picker API

Google에서 매우 기대하는 API는 Contact Picker API입니다. 이를 통해 웹 앱이 기기의 기본 연락처 관리자에서 연락처에 액세스할 수 있으므로 웹 앱이 연락처의 이름, 이메일 주소, 전화번호에 액세스할 수 있습니다. 연락처를 하나만 원하는지 아니면 여러 개를 원하는지, 모든 필드를 원하는지 아니면 이름, 이메일 주소, 전화번호의 일부만 원하는지 지정할 수 있습니다.

개인정보 보호 고려사항

선택기가 열리면 공유할 연락처를 선택할 수 있습니다. '모두 선택' 옵션이 없다는 점에 유의하세요. 이는 의도적인 것입니다. 공유는 신중한 결정이 되어야 합니다. 마찬가지로 액세스는 연속적이지 않고 일회성 결정입니다.

연락처 액세스

연락처에 액세스하는 것은 간단한 작업입니다. 선택기가 열리기 전에 원하는 필드 (name, email, telephone 옵션)와 여러 연락처에 액세스할지 아니면 하나의 연락처에만 액세스할지 지정할 수 있습니다. 데모 애플리케이션을 열어 Android 기기에서 이 API를 테스트할 수 있습니다. 소스 코드의 관련 섹션은 기본적으로 아래 스니펫입니다.

getContactsButton.addEventListener('click', async () => {
  const contacts = await navigator.contacts.select(
      ['name', 'email'],
      {multiple: true});
  if (!contacts.length) {
    // No contacts were selected, or picker couldn't be opened.
    return;
  }
  console.log(contacts);
});

de94db2dfb7c67af.png

8. Async Clipboard API

텍스트 복사 및 붙여넣기

지금까지는 이미지를 시스템의 클립보드에 프로그래매틱 방식으로 복사하여 붙여넣을 방법이 없었습니다. 최근에 Async Clipboard API에 이미지 지원이 추가되었습니다.

이제 이미지를 복사하여 붙여넣을 수 있습니다. 새로운 점은 이미지를 클립보드에 쓸 수도 있다는 것입니다. 비동기 클립보드 API는 한동안 텍스트 복사 및 붙여넣기를 지원해 왔습니다. navigator.clipboard.writeText()를 호출하여 텍스트를 클립보드에 복사한 후 나중에 navigator.clipboard.readText()를 호출하여 해당 텍스트를 붙여넣을 수 있습니다.

이미지 복사 및 붙여넣기

이제 이미지를 클립보드에 쓸 수도 있습니다. 이 작업을 수행하려면 이미지 데이터를 blob으로 가져와 클립보드 항목 생성자에 전달해야 합니다. 마지막으로 navigator.clipboard.write()를 호출하여 이 클립보드 항목을 복사할 수 있습니다.

// Copy: Writing image to the clipboard
try {
  const imgURL = 'https://developers.google.com/web/updates/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem(Object.defineProperty({}, blob.type, {
      value: blob,
      enumerable: true
    }))
  ]);
  console.log('Image copied.');
} catch(e) {
  console.error(e, e.message);
}

클립보드에서 이미지를 다시 붙여넣는 작업은 복잡해 보이지만 실제로는 클립보드 항목에서 blob을 다시 가져오는 것으로 구성됩니다. 여러 개가 있을 수 있으므로 원하는 항목이 나올 때까지 루프를 통해 확인해야 합니다. 보안상의 이유로 현재는 PNG 이미지만 지원되지만 향후 더 많은 이미지 형식이 지원될 수 있습니다.

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

데모 앱에서 이 API가 작동하는 모습을 확인할 수 있습니다. 관련 스니펫은 위의 소스 코드에서 가져왔습니다. 권한 없이 이미지를 클립보드에 복사할 수는 있지만 클립보드에서 붙여넣으려면 액세스 권한을 부여해야 합니다.

99f6dbf35ff4f393.png

액세스 권한을 부여한 후 클립보드에서 이미지를 읽어 애플리케이션에 붙여넣을 수 있습니다.

ace5945f4aca70ff.png

9. 축하드립니다.

축하합니다. Codelab을 완료했습니다. 다시 한번 말씀드리지만 대부분의 API는 아직 변경 중이며 활발히 작업 중입니다. 따라서 귀하와 같은 사용자와의 상호작용만이 이러한 API를 올바르게 만드는 데 도움이 되므로 팀에서는 의견을 보내주시면 정말 감사하겠습니다.

기능 방문 페이지도 자주 확인해 보세요. 이 페이지는 최신 상태로 유지되며 Google에서 작업하는 API에 관한 모든 심층 도움말로 연결되는 포인터가 있습니다. 계속해서 멋진 음악을 만들어 주세요!

Tom 및 전체 기능팀 🐡