📮
Web Share API로 공유 기능 쉽게 구현하기

Web Share API를 활용해 네이티브 디바이스의 공유 기능을 웹에서 호출할 수 있습니다.

May 09, 2020


JavaScript


네이티브 다이얼로그 네이티브 다이얼로그를 웹에서도 쓰자! 거 좋은 거 있음 같이 씁시다

최근에 회사에서 진행한 프로젝트에서 Web Share API를 활용하게 될 기회가 있어서, 이에 대해 한 번 짚고 넘어가고자 포스트를 써봅니다.

사실 순수하게 사용하는 방법만 소개하자면 꽤 간단한 글인데, 그것만 설명하면 좀 심심하니까(…) 오늘은 API의 등장 배경과 사용 예시, 주의할 점, 읽을거리 등 디테일한 정보를 알아볼 예정입니다.

이번 포스트를 통해 프로젝트에 Web Share API를 사용해보고자 하시는 분들에게 도움이 되었으면 좋겠습니다.

왜 사용하나요

유저플로우 일반적인 네이티브에서 공유하기를 사용하기 위한 유저 플로우

Web Share API는 사용자가 선택한 임의의 대상과 텍스트, 링크 및 기타 컨텐츠를 공유하기 위해 제안된 API로, 웹 개발자는 웹 어플리케이션 사용자에게 네이티브 환경과 동일한 공유 다이얼로그를 제공할 수 있게 됩니다.

왜 만들어졌을까요

공유 예시 일반적으로 웹에서 덕지덕지 붙어있는 공유 아이콘들

웹 어플리케이션에서 우리는 일반적으로 URL이나 텍스트, 이미지를 다른 어플리케이션에 공유하고 싶을 때가 있습니다. 이런 상황에서 현재 우리가 취할 수 있는 행동은 다음과 같습니다.

  • 페이스북이나 트위터같은 특정 서비스로의 공유 버튼을 사용하기

이 방법은 가장 일반적인 방법이지만 합리적이지 않은 부분이 있습니다. 우선 공유할 대상이 될 플랫폼이 온전히 웹 개발자에 의해 선택되고, 공유할 플랫폼 역시 페이스북이나 트위터 등 몇 개 정도로 제한되기 때문입니다.

  • 대부분의 모바일 브라우저단에서 지원하는 공유 기능을 활용하기

두 번째 방법 역시 몇 가지 문제가 있습니다. 공유 기능을 웹 어플리케이션 안에서 직접 컨트롤할 수 없고, 일반적으로 URL만 공유할 수 있으며, 특정한 경우에는 공유 기능을 사용하지 못 할 수도 있기 때문입니다.

따라서 w3c에서는 웹 사이트 제작자에게 사용자가 선택한 앱에 임의의 콘텐츠를 직접 공유할 수 있는 방법을 제공함으로써 이러한 문제를 해결하는 API를 제안하였습니다. Web Share API는 이렇게 등장하게 되었습니다.

어디에서 쓸 수 있나요

브라우저 지원 범위 MDN에서 확인한 브라우저 별 지원 범위

caniuse.com에서 확인한 지원 범위와 차이가 있었습니다. 삼성 브라우저로 확인해 본 결과 MDN이 더 정확한 결과인듯 하여 MDN 자료를 첨부했습니다.

아직 Web Share API는 실험적 기능이고, 이 때문에 위의 사진에서 볼 수 있듯이 브라우저 지원 범위가 넓은 편은 아닙니다.

하지만 이 API 자체가 네이티브 기기의 공유 기능을 호출하는 용도이기 때문에, 모바일 브라우저로 대상을 좁힌다면 caniuse.com 에 나온 기준으로 약 85%의 커버리지를 확인할 수 있습니다. 따라서 Web Share API를 지원하지 않는 브라우저에 대한 폴백(fallback)을 충분히 마련한다면, 사용해 볼 만한 기능입니다.

어떻게 쓰나요

Web Share API는 share() 메소드는 window.navigator에 포함된 내장 메소드입니다.

window.navigator.share({
  title: '', // 공유될 제목
  text: '', // 공유될 설명
  url: '', // 공유될 URL
  files: [], // 공유할 파일 배열
});

url 프로퍼티에 빈 스트링("")을 넣으면 현재 URL이 자동으로 설정됩니다.

share()의 파라미터로 들어가는 객체는 title, text, url, files 프로퍼티를 가집니다. 각 프로퍼티는 모두 옵셔널하고, filesFile 배열 타입이며 나머지 프로퍼티는 String 타입을 가집니다.

share()Promise 기반으로, 사용자가 공유 액션을 성공적으로 마무리했을 때만 resolve 됩니다. 따라서 try, catch 문을 활용하게 되면 성공과 실패 케이스를 구분할 수 있고, 콜백 액션 역시 별도로 정의할 수 있습니다.

const shareData = {
  files: filesArray, // 파일 배열
  title: '파일 공유하기',
};

if (navigator.canShare && navigator.canShare(shareData)) {
  navigator.share(shareData);
})

파일을 공유하는 것은 조금 다른데, 보안 상의 이유로 공유할 수 있는 파일 타입이 제한되어 있기 때문에 위처럼 canShare() 메소드로 해당 파일 목록이 share() 메소드로 호출이 가능한지를 미리 확인하는 로직이 필요합니다.

TypeScript에서는 3.9 버전 이후부터 지원하며, 이전 버전에서 사용하고자 하는 경우에는 아래와 같이 직접 타입을 선언하여 사용할 수 있습니다.

type ShareData = {
  title?: string;
  text?: string;
  url?: string;
  files?: File[];
};

interface Navigator {
  share?: (data? : ShareData) => Promise<void>;
  canShare?: (data? : ShareData) => boolean;
}

어떻게 활용하나요

아래 코드는 JavaScript에서 이벤트 리스너를 활용해 공유 버튼을 클릭하면 현재 URL을 공유하게 되는 코드입니다. 특정 버튼을 누르면 제 블로그를 공유하는 코드를 만들어볼게요.

shareButton.addEventListener("click", async () => {
  try {
    await navigator.share({
      title: "재그지그의 개발 블로그",
      text: "디자인과 UI, UX에 관심이 많은 주니어 웹 프론트엔드 개발자입니다.",
      url: "https://wormwlrm.github.io",
    });
    console.log("공유 성공");
  } catch (e) {
    console.log("공유 실패");
  }
});

위의 API를 적용하고, 이 버튼을 누르게 되면

카카오톡 예시

이렇게 공유 다이얼로그가 뜨게 되고, 카카오톡으로 공유하게 되면 아래와 같이 나오게 됩니다.

카카오톡 예시 title, text, url 순으로 나오네요

이 곳에서 동작하는 예시를 직접 확인할 수 있습니다.

주의해야 할 점

웹 개발자는 브라우저가 Web Share API를 지원하지 않는 경우에 대한 예외 처리를 해주어야 합니다.

if (typeof navigator.share === "undefined") {
  // 공유하기 버튼을 지원하지 않는 경우에 대한 폴백 처리
  shareButton.hidden = true;
}

그리고 보안 상의 이유로, Web Share API는 HTTPS 환경에서만 사용 가능하며 사용자의 직접적인 액션으로만 호출할 수 있습니다. 즉, onload 같은 콜백 함수에서는 호출할 수 없습니다.

읽을거리

처음에는 공유 기능을 지원하는 API를 만드는 대신 mailto:, tel: 처럼 URL 스키마로 하는 방법은 어떨까 하는 제안도 있었다고 합니다.

<a href="share:?title=Example%20Page&amp;url=https://example.com/page">공유하기</a>

이러한 방법은 몇 가지 이점이 있었는데요,

  • 공유할 대상을 명시적으로 선언할 수 있음
  • 별도의 JavaScript 로직을 필요로 하지 않음

하지만 아래와 같은 몇 가지 현실적인 문제에 의해서 기각되고 맙니다.

  • 일반적인 케이스의 경우 현재 URL을 공유하게 되는데, href 속성값으로 현재 URL을 넣어주는 로직이 필요함
  • 사용자 시스템에서 공유가 지원되는지 확인할 방법이 없음
  • 공유에 성공하거나 실패했을 때 성공 여부에 대한 응답이나 콜백을 처리할 수 없음

이런 문제점은 mailto:tel: 에서도 제기되었는데, URI는 자원(resource)을 구별하기 위한 용도로 사용되어야 하지 특정한 행동(actions)을 유발시키는 용도로 사용하는 것은 URI의 개념을 남용하는 것이라는 의견이 있었다고 하네요.

참고 문서