Side-by-Side 폰트 비교 도구를 만든 방법 (그리고 우연히 Browser APIs에 대해 너무 많이 배우게 됐어요)

발행: (2026년 2월 19일 오후 06:24 GMT+9)
9 분 소요
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the post (the paragraphs, headings, etc.) in order to do the translation. Could you please paste the article’s content here (excluding any code blocks or URLs you’d like to keep unchanged)? Once I have the text, I’ll provide the Korean translation while preserving the original formatting and markdown.

아이디어

실제 텍스트로 글꼴이 어떻게 보일지 추측하는 데 지쳐서 FontPreview를 만들었습니다. 디자이너들이 계속 물어보던 것은:

“이 Google Font를 내 컴퓨터에 이미 설치된 글꼴과 비교할 수 있을까요?”

생각보다 쉽지 않다는 것이 밝혀졌습니다.

브라우저는 시스템 글꼴을 탐색하는 것을 별로 원하지 않습니다. 그럴만한 이유가 있죠 — 방문하는 모든 웹사이트가 내 컴퓨터에 설치된 모든 글꼴 목록을 얻는다면 상상해 보세요. 이는 프라이버시 악몽이 됩니다.

하지만 사용자가 허락한다면 이를 가능하게 하는 새로운 API가 있습니다.

로컬 폰트 액세스 API

Local Font Access API 라는 것이 있습니다. 비교적 새롭고 아직 모든 브라우저가 지원하는 것은 아닙니다(특히 Safari). Chrome과 Edge에서는 다음과 같이 사용할 수 있습니다:

const fonts = await window.queryLocalFonts();

이 한 줄의 코드는 사용자의 시스템에 설치된 모든 폰트의 배열을 반환합니다.

중요: 브라우저가 먼저 사용자에게 권한을 요청합니다. “이 사이트가 귀하의 폰트를 확인하려고 합니다.” 라는 팝업이 나타납니다. 사용자가 아니오를 선택하면 아무 것도 얻을 수 없습니다. 이는 좋은 일입니다 — 무작위 사이트가 허가 없이 폰트 목록을 스크래핑하는 것을 원하지 않기 때문입니다.

The Permission Dance

Here’s how I handle the permission request:

function checkSystemFontsPermission() {
  if (!window.queryLocalFonts) {
    showToast('Local Font Access API not supported in this browser');
    useFallbackFonts();
    return;
  }

  // Show the permission modal
  document.getElementById('permissionModal').style.display = 'flex';
}
  • 브라우저가 API를 지원하지 않으면, 인기 있는 시스템 폰트 목록으로 대체합니다. 완벽하진 않지만 전혀 없는 것보다는 낫습니다.
  • 브라우저가 지원한다면, 왜 권한을 요청하는지와 데이터를 어떻게 사용할지 설명하는 모달을 표시합니다 (스포일러: 아무것도 하지 않습니다. 단지 폰트를 드롭다운에 표시할 뿐입니다).

사용자가 “허용”을 클릭하면 무슨 일이 일어나나요

사용자가 Allow를 클릭하면 다음 코드가 실행됩니다:

async function requestFontPermission() {
  try {
    const fonts = await window.queryLocalFonts();

    // Clean up font names (remove "Regular", "Bold", etc.)
    const fontMap = new Map();

    fonts.forEach(f => {
      let name = f.family;
      const suffixes = [' Regular', ' Bold', ' Italic', ' Light', ' Medium'];

      suffixes.forEach(suffix => {
        if (name.endsWith(suffix)) {
          name = name.substring(0, name.length - suffix.length);
        }
      });

      if (!fontMap.has(name)) {
        fontMap.set(name, {
          family: name,
          fullName: f.family,
          style: f.style,
          weight: f.weight
        });
      }
    });

    // Sort and store
    allSystemFonts = Array.from(fontMap.values()).sort((a, b) =>
      a.family.localeCompare(b.family)
    );

    showToast(`Loaded ${allSystemFonts.length} system fonts`);
  } catch (error) {
    if (error.name === 'NotAllowedError') {
      showToast('Permission denied. Using fallback fonts.');
    } else {
      showToast('Error loading fonts. Using fallback.');
    }
    useFallbackFonts();
  }
}

Map은 많은 글꼴이 서로 다른 스타일에 대해 여러 항목(예: “Arial Regular”, “Arial Bold”, “Arial Italic”)으로 반환되기 때문에 중요합니다. 저는 “Arial”을 한 번만 원하므로 Map이 중복을 제거합니다.

내가 예상하지 못한 큰 문제

일단 폰트 목록을 얻었으면, 미리보기에서 실제로 해당 폰트를 사용해야 했습니다.

CSS에서는 간단히 다음과 같이 작성할 수 있습니다:

font-family: 'Arial', sans-serif;

사용자가 Arial을 설치해 두었다면, 정상적으로 작동합니다. 좋습니다.

하지만 저는 사용자가 시스템 폰트와 Google Fonts를 나란히 비교할 수 있게 하고 싶었습니다. 왼쪽에서 Arial, 오른쪽에서 Roboto를 선택하면, Google Fonts에서 Roboto를 로드해야 합니다. 이를 위해 <link> 태그를 동적으로 삽입해야 합니다:

function loadGoogleFontForPanel(fontName, panel) {
  const fontFamily = fontName.replace(/ /g, '+');

  const linkId = `panel-font-${panel}`;
  const oldLink = document.getElementById(linkId);
  if (oldLink) oldLink.remove();

  const link = document.createElement('link');
  link.id = linkId;
  link.rel = 'stylesheet';
  link.href = `https://fonts.googleapis.com/css2?family=${fontFamily}&display=swap`;

  document.head.appendChild(link);
}

각 패널은 독립적으로 자신의 폰트를 로드할 수 있습니다: 왼쪽 패널은 시스템 폰트를 사용하므로 스타일시트가 필요 없고, 오른쪽 패널은 Google Font를 로드하므로 스타일시트가 필요합니다.

여전히 나를 괴롭히는 부분

Google 폰트를 동적으로 로드하면, 사용 가능해지기까지 아주 짧은 지연이 발생합니다. 그 순간 텍스트는 기본 폰트로 표시되었다가 선택한 폰트로 교체됩니다 — 스타일이 적용되지 않은 텍스트가 잠깐 나타나는 현상(FOIT)입니다.

몇 가지 해결책을 시도해봤습니다: 프리로드, font-display: swap, 심지어 폰트가 로드될 때까지 텍스트를 숨기는 방법까지. 모두 어색하게 느껴졌습니다. 결국 그대로 두었습니다; 플래시가 짧고 대부분의 사용자는 눈치채지 못하지만, 저는 매번 눈치챕니다.

내가 다르게 할 것

다시 처음부터 만든다면:

  1. 권한 상태를 캐시합니다. 현재 모달이 “System”을 클릭할 때마다 표시됩니다. 사용자의 선택을 localStorage에 저장하여 반복적으로 귀찮게 하지 않도록 합니다.
  2. 더 나은 오류 처리. 때때로 API가 조용히 실패합니다. 이러한 경우를 포착하고 우아하게 대체하도록 해야 합니다.
  3. Safari용 대체 방안. Safari는 이 API를 전혀 지원하지 않습니다. 인기 있는 폰트의 정적 목록 대신, 보다 견고한 대체 방안을 제공할 수 있습니다(예: 선별된 웹‑안전 폰트 세트 또는 다운로드 가능한 폰트 미리보기 번들).

코드 (원한다면)

전체는 FontPreview에서 확인할 수 있습니다.
소스 보기 — 모두 클라이언트‑사이드이며 백엔드, 추적이 없고 HTML, CSS, JavaScript만 있습니다.

저는 뛰어난 개발자는 아닙니다. 대부분을 부수면서 오류를 구글링하며 배웠습니다. 하지만 작동하고 사람들도 사용하고 있습니다. 그걸로 충분합니다.

Local Font Access API 로 무언가를 만들고 있다면 연락 주세요. 제가 이미 겪은 버그와 같은 것을 겪고 있을 가능성이 높습니다.

Tags: javascript, webdev, tutorial, showdev, api

0 조회
Back to Blog

관련 글

더 보기 »