로컬 글꼴로 고급 서체 사용

Local Font Access API를 사용하여 로컬에 설치된 사용자의 글꼴에 액세스하고 글꼴에 관한 하위 수준 세부정보를 가져오는 방법을 알아봅니다.

웹 안전 글꼴

웹 개발을 오랫동안 해왔다면 웹 안전 글꼴이라는 것을 기억하실 겁니다. 이러한 글꼴은 가장 많이 사용되는 운영체제(Windows, macOS, 가장 일반적인 Linux 배포판, Android, iOS)의 거의 모든 인스턴스에서 사용할 수 있는 것으로 알려져 있습니다. 2000년대 초 Microsoft는 '웹용 TrueType core Font for the Web'이라는 이니셔티브도 선보였습니다. 이 글꼴은 '이 글꼴이 지정된 웹사이트를 방문할 때마다 사이트 디자이너가 의도한 대로 페이지가 표시됩니다'라는 목표를 가지고 이 글꼴을 무료로 다운로드할 수 있게 했습니다. 예, 여기에는 Comic Sans MS에 설정된 사이트가 포함됩니다. 다음은 기존 웹 안전 글꼴 스택이며 sans-serif 글꼴의 궁극적인 대체는 다음과 같습니다.

body {
  font-family: Helvetica, Arial, sans-serif;
}

웹 글꼴

웹에서 안전한 글꼴이 정말 중요했던 시절은 이미 지났습니다. 현재 웹 글꼴이 있으며, 그중 일부는 노출된 다양한 축의 값을 변경하여 추가로 조정할 수 있는 가변 글꼴입니다. 다운로드할 글꼴 파일을 지정하는 CSS 시작 부분에 @font-face 블록을 선언하여 웹 글꼴을 사용할 수 있습니다.

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

그런 다음 평소와 같이 font-family를 지정하여 맞춤 웹 글꼴을 사용할 수 있습니다.

body {
  font-family: 'FlamboyantSansSerif';
}

지문 벡터로 사용되는 로컬 글꼴

대부분의 웹 글꼴은 웹에서 시작됩니다. 하지만 흥미로운 사실은 @font-face 선언의 src 속성(url() 함수 제외)도 local() 함수를 허용한다는 것입니다. 이렇게 하면 맞춤 글꼴을 로컬로 로드할 수 있습니다. 사용자가 운영체제에 FlamboyantSansSerif를 설치한 경우에는 다운로드되는 대신 로컬 사본이 사용됩니다.

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

이 접근 방식은 대역폭을 절약할 수 있는 효과적인 대체 메커니즘을 제공합니다. 불행하게도 인터넷에서는 좋은 것을 가질 수 없습니다. local() 함수의 문제는 브라우저 디지털 지문 수집에 악용될 수 있다는 것입니다. 사용자가 설치한 글꼴 목록이 꽤 쉽게 알아볼 수 있습니다. 많은 회사에서 직원의 노트북에 설치되는 자체 회사 글꼴을 가지고 있습니다. 예를 들어 Google에는 Google Sans라는 회사 글꼴이 있습니다.

Google Sans 글꼴의 미리보기를 보여주는 macOS Font Book 앱
Google 직원의 노트북에 설치된 Google Sans 글꼴

공격자는 Google Sans와 같이 알려진 회사 글꼴이 많이 있는지 테스트하여 어떤 회사가 근무하는지 확인할 수 있습니다. 공격자는 이 글꼴에 설정된 텍스트를 캔버스에 렌더링하고 글리프를 측정합니다. 글리프가 알려진 회사 글꼴 모양과 일치하면 공격자에게 맞습니다. 글리프가 일치하지 않으면 공격자는 회사 글꼴이 설치되지 않았으므로 기본 대체 글꼴이 사용되었음을 알게 됩니다. 이 문제와 다른 브라우저 디지털 지문 공격에 관해 자세히 알아보려면 Laperdix 외 다수가 실시한 설문조사 논문을 참고하세요.

회사 글꼴은 분리되어 있지만 설치된 글꼴 목록만으로도 식별할 수 있습니다. 이러한 공격 벡터의 상황이 너무 나빠져서 최근에 WebKit팀은 ''사용 가능한 글꼴 목록에 운영체제에 제공되는 웹 글꼴과 글꼴만 포함하고 사용자가 설치한 글꼴은 포함하지 않습니다'결정했습니다. 다음은 로컬 글꼴에 대한 액세스 권한 부여에 관한 도움말입니다.

로컬 글꼴 액세스 API

이 기사의 시작 부분에서 부정적인 분위기가 느껴졌을 수 있습니다. 정말로 좋은 것을 갖지 못할 수 있을까요? 아니야. 우리는 그렇게 할 수 있다고 생각하며, 아마도 모든 것이 절망적이지 않은 것은 아닙니다. 하지만 먼저 여러분이 궁금해하실 만한 질문에 답해 드리겠습니다.

웹 글꼴이 있는데 로컬 글꼴 액세스 API가 필요한 이유는 무엇인가요?

지금까지 전문가 수준의 디자인 및 그래픽 도구는 웹에서 제공하기 어려웠습니다. 한 가지 문제는 디자이너가 로컬에 설치한 전문적으로 구성되고 힌트가 포함된 다양한 글꼴에 액세스하여 사용할 수 없다는 것입니다. 웹 글꼴을 사용하면 일부 게시 사용 사례가 가능하지만 래스터라이저가 글리프 윤곽선을 렌더링하는 데 사용하는 벡터 글리프 모양 및 글꼴 테이블에 관해 프로그래매틱 방식으로 액세스할 수는 없습니다. 마찬가지로 웹 글꼴의 바이너리 데이터에 액세스할 방법도 없습니다.

  • 디자인 도구는 자체 OpenType 레이아웃을 구현하기 위해 글꼴 바이트에 액세스해야 하며, 글리프 모양에서 벡터 필터나 변환을 실행하는 것과 같은 작업을 위해 하위 수준에 연결할 수 있도록 합니다.
  • 개발자가 웹에 가져오는 애플리케이션에 기존 글꼴 스택을 보유하고 있을 수 있습니다. 이러한 스택을 사용하려면 일반적으로 웹 글꼴에서 제공하지 않는 글꼴 데이터에 직접 액세스해야 합니다.
  • 일부 글꼴은 웹을 통한 전송에 라이선스가 부여되지 않을 수 있습니다. 예를 들어 Linotype은 데스크톱용만 포함하는 일부 글꼴에 대한 라이선스를 보유하고 있습니다.

Local Font Access API는 이러한 문제를 해결하기 위한 시도입니다. 다음 두 부분으로 구성됩니다.

  • font enumeration API: 사용자가 사용 가능한 전체 시스템 글꼴 모음에 액세스할 수 있는 권한을 부여할 수 있습니다.
  • 각 열거 결과에서 전체 글꼴 데이터를 포함하는 하위 수준 (바이트 기반) SFNT 컨테이너 액세스를 요청하는 기능

브라우저 지원

브라우저 지원

  • 103
  • 103
  • x
  • x

소스

Local Font Access API 사용 방법

기능 감지

Local Font Access API가 지원되는지 확인하려면 다음을 사용하세요.

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

로컬 글꼴 열거

로컬에 설치된 글꼴 목록을 가져오려면 window.queryLocalFonts()를 호출해야 합니다. 처음에는 사용자가 승인하거나 거부할 수 있는 권한 메시지가 트리거됩니다. 사용자가 로컬 글꼴의 쿼리 실행을 승인하면 브라우저에서 루프로 처리할 수 있는 글꼴 데이터가 포함된 배열을 반환합니다. 각 글꼴은 family 속성(예: "Comic Sans MS"), fullName (예: "Comic Sans MS"), postscriptName (예: "ComicSansMS"), style (예: "Regular") 속성이 있는 FontData 객체로 표현됩니다.

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

글꼴의 하위 집합에만 관심이 있는 경우 postscriptNames 매개변수를 추가하여 PostScript 이름을 기준으로 글꼴을 필터링할 수도 있습니다.

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

SFNT 데이터 액세스

전체 SFNT 액세스는 FontData 객체의 blob() 메서드를 통해 사용할 수 있습니다. SFNT는 PostScript, TrueType, OpenType, WOFF (Web Open Font Format) 글꼴 등과 같은 다른 글꼴을 포함할 수 있는 글꼴 파일 형식입니다.

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

데모

아래 데모에서 Local Font Access API의 작동 방식을 확인할 수 있습니다. 소스 코드도 확인하세요. 데모에서는 로컬 글꼴 선택 도구를 구현하는 <font-select>라는 맞춤 요소를 보여줍니다.

개인정보 보호 고려사항

"local-fonts" 권한은 지문 인식이 뛰어난 노출 영역을 제공하는 것으로 보입니다. 그러나 브라우저는 원하는 모든 항목을 자유롭게 반환할 수 있습니다. 예를 들어 익명성 중심 브라우저는 브라우저에 내장된 기본 글꼴 집합만 제공하도록 선택할 수 있습니다. 마찬가지로 브라우저는 디스크에 표시되는 테이블 데이터를 그대로 제공할 필요가 없습니다.

가능한 경우 Local Font Access API는 언급된 사용 사례를 사용 설정하는 데 필요한 정보만 정확하게 노출하도록 설계되었습니다. 시스템 API는 설치된 글꼴 목록을 무작위 또는 정렬된 순서가 아니라 글꼴 설치 순서대로 생성할 수 있습니다. 이러한 시스템 API에서 제공하는 설치된 글꼴 목록을 정확히 반환하면 지문 인식에 사용될 수 있는 추가 데이터가 노출될 수 있으며, 사용 설정하려는 사용 사례는 이 순서를 유지함으로써 지원되지 않습니다. 따라서 이 API를 사용하려면 반환된 데이터가 반환되기 전에 정렬되어야 합니다.

보안 및 권한

Chrome팀은 사용자 제어, 투명성, 인체공학 등 강력한 웹 플랫폼 기능에 대한 액세스 제어에 정의된 핵심 원칙을 사용하여 Local Font Access API를 설계하고 구현했습니다.

사용자 제어

사용자 글꼴에 대한 액세스는 전적으로 사용자가 제어할 수 있으며 권한 레지스트리에 나열된 "local-fonts" 권한이 부여되지 않으면 허용되지 않습니다.

투명성

사이트에 사용자의 로컬 글꼴에 대한 액세스 권한이 부여되었는지 여부는 사이트 정보 시트에 표시됩니다.

권한 지속성

"local-fonts" 권한은 페이지를 새로고침해도 유지됩니다. 사이트 정보 시트를 통해 취소할 수 있습니다.

의견

Chrome팀은 Local Font Access API 사용 경험에 관한 의견을 듣고자 합니다.

API 설계에 대해 알려주세요.

API에서 예상한 대로 작동하지 않는 부분이 있나요? 아니면 아이디어를 구현하는 데 필요한 메서드나 속성이 누락되었나요? 보안 모델에 대한 질문이나 의견이 있으신가요? 관련 GitHub 저장소에서 사양 문제를 제출하거나 기존 문제에 대한 의견을 추가하세요.

구현 관련 문제 신고

Chrome 구현에서 버그를 발견하셨나요? 아니면 구현이 사양과 다른가요? new.crbug.com에서 버그를 신고합니다. 가능한 한 많은 세부정보와 간단한 재현 안내를 포함하고 구성요소 상자에 Blink>Storage>FontAccess를 입력합니다. Glitch는 쉽고 빠른 재현을 공유하는 데 효과적입니다.

API 지원 표시

Local Font Access API를 사용할 계획인가요? 공개 지원은 Chrome팀이 기능의 우선순위를 정하는 데 도움이 되며 다른 브라우저 공급업체에 이러한 기능을 지원하는 것이 얼마나 중요한지 보여줍니다.

해시태그 #LocalFontAccess를 사용하여 @ChromiumDev로 트윗을 보내고 사용 위치와 방법을 알려주세요.

감사의 말

Local Font Access API 사양은 Emil A. Eklund, 알렉스 러셀, 조슈아 벨, 올리비에 입통이 있습니다. 이 문서는 Joe Medley, Dominik Röttsches, Olivier Yiptong이 검토했습니다. Unsplash브렛 조던의 히어로 이미지