Google에서 기술적인 것을 검색하고 각 결과의 사이트 이름 옆에 있는 작은 아이콘을 보세요. 그것이 사이트의 파비콘입니다. Google은 2019년에 모바일 검색 결과에 파비콘을 표시하기 시작했고, 지금은 결과의 사이트 이름 옆에 표시합니다(Google 검색 센터). 알아볼 수 있는 파비콘이 없는 사이트는 방치된 것처럼 보이고, 선명하고 브랜드다운 파비콘이 있으면 공들인 것처럼 보입니다. 이 작은 마크는 사용자가 클릭할지 정하는 자리에서 도메인 옆에 나타납니다.
이 작은 마크는 브라우저 탭, 즐겨찾기 바, OS 작업 표시줄, iOS 홈 화면, Android PWA 설치, RSS 리더, 바로가기 타일을 횡단하며 살아남는 몇 안 되는 시각 요소 중 하나입니다. 각 자리는 조금씩 다른 파일을 원합니다 — 크기가 다르고, 가끔 형식이 다르고, 가끔 자기만의 manifest 항목을 요구합니다. 이 글은 모던 favicon 세트가 실제로 어떻게 생겼는지, 왜 단일 favicon.ico로는 더 이상 충분하지 않은지, 런타임에 각 파일이 어떤 역할을 맡는지, 그리고 ZeroTool 생성기가 어떻게 바이트를 업로드하지 않고 브라우저 안에서 패키지를 만드는지를 다룹니다.
모던 브라우저가 실제로 요청하는 파일
브라우저가 어떤 파일을 요청하는지는 페이지의 <link> 태그와 사용자의 동작에 달려 있습니다. 자기 사이트에서 DevTools를 열고 새로고침한 다음 Network 패널을 보세요. 데스크톱 브라우저는 보통 탭용으로 고른 아이콘 하나만 요청합니다. 진입점은 셋입니다:
| 요청 | 사용처 | 비고 |
|---|---|---|
/favicon.ico | 페이지에 아이콘 선언이 없을 때 브라우저가 이 경로로 대체한다 | 여러 크기를 담는 컨테이너. 루트 경로만 시도하는 도구와 크롤러를 위해 남겨 둔다 |
<link rel="icon">의 href가 가리키는 이미지 | 탭, 즐겨찾기, 방문 기록 | 브라우저가 sizes와 type으로 후보 중에서 고른다. HTML 표준은 선택을 브라우저에 맡긴다 |
<link rel="apple-touch-icon">의 href가 가리키는 이미지 | iOS Safari 「홈 화면에 추가」 | 표준 크기 180×180 |
Android Chrome에서 PWA로 설치하면 그림이 더 커집니다:
| 요청 | 사용처 |
|---|---|
/site.webmanifest (또는 manifest.json) | Chrome, Edge, Brave, Samsung Internet — PWA를 설치할 수 있는 어디든 |
manifest의 icons 배열에 선언된 192×192와 512×512 PNG | Android 홈 화면 아이콘, 스플래시 화면, Android 공유 시트 |
아이콘을 purpose: "maskable"로 지정하면 Android가 런처에 따라 원형이나 둥근 사각형 마스크를 씌워 런처 그리드에서 네이티브 앱처럼 보이게 합니다. maskable 아이콘은 보이는 부분이 안전 영역(아이콘 크기의 40%를 반지름으로 하는 원, 각 변에 약 10% 여백) 안에 있어야 하며, 벗어나면 마스크에 잘립니다(web.dev). web.dev는 한 파일에 "any maskable"을 쓰지 말라고 권합니다. 마스크용 여백을 둔 아이콘은 그대로 표시되는 곳에서 작아 보이기 때문입니다. any용과 maskable용은 파일을 나누세요.
그리고 긴 꼬리가 있습니다. Windows 8·10의 고정 사이트 타일은 browserconfig.xml을 읽지만, Windows 11에는 라이브 타일이 없습니다. 예전 Safari는 고정 탭을 <link rel="mask-icon">으로 선언한 단색 SVG로 그렸습니다. 예전 Android Chrome은 홈 화면 바로가기에 apple-touch-icon을 썼습니다. 일부 생성기가 20개가 넘는 파일을 내놓는 이유입니다.
왜 단일 favicon.ico로는 충분하지 않은가
역사적 기본은 ICO 하나를 사이트 루트에 두고 브라우저가 알아서 하게 두는 것이었습니다. 모니터가 1280×1024이 한계이고 탭이 16픽셀 정사각형이던 시절에는 그것으로 충분했습니다. 두 가지가 그 균형을 깨뜨렸습니다:
- Retina 디스플레이. 브라우저가 2× 디스플레이에서 16×16 ICO를 32 논리 픽셀로 확대해 표시하면 흐릿해집니다.
<link rel="icon" type="image/png" sizes="32x32">태그가 디바이스 픽셀 비율에 맞는 정확한 리소스를 선택하게 합니다. - 탭 너머의 운영체제 표면들. iOS 홈 화면 아이콘, Android 홈 화면 아이콘, PWA 스플래시 화면은 각각 세트에서 특정 크기를 가져가며, 어느 것도 16픽셀 ICO를 쓰지 않습니다. 필요한 것은 180, 192, 512와 무엇이 무엇인지 알려 주는 manifest입니다.
해결은 모던 세트입니다: 호환을 위한 작은 ICO, 모던 <link rel="icon"> 체인용 PNG 몇 가지 크기, iOS용 apple-touch-icon, 그리고 PWA 아이콘을 담은 webmanifest.
전체 파일 목록과 런타임 역할
ZeroTool 생성기가 출력하는 파일과 런타임에 누가 사용하는지 정리:
| 파일 | 크기 | 사용처 |
|---|---|---|
favicon.ico | 16+32+48 멀티 사이즈 | 브라우저 탭: 2026-10-02 Chromium 152(2× 화면)에서 이 도구의 HTML 스니펫을 넣은 페이지는 이 파일만 요청했습니다. 아이콘 선언이 없는 페이지에서도 루트의 이 파일을 요청합니다 |
favicon-16.png | 16×16 | HTML 스니펫에서 선언. ICO 링크가 함께 있으면 위 실측에서 Chromium은 요청하지 않음 |
favicon-32.png | 32×32 | 위와 같음. ICO 없이 PNG만 선언하면 Chromium은 2× 화면에서 이 파일을 요청 |
favicon-48.png | 48×48 | HTML 스니펫에서 선언. Google 검색은 48×48보다 큰 아이콘을 권장 |
favicon-64.png | 64×64 | 스니펫에서 참조하지 않음. 필요하면 <link> 추가 |
favicon-96.png | 96×96 | 스니펫에서 참조하지 않음 |
favicon-128.png | 128×128 | 스니펫에서 참조하지 않음 |
apple-touch-icon.png | 180×180 | iOS Safari 「홈 화면에 추가」 |
android-chrome-192.png | 192×192 | PWA 설치 후 Android 홈 화면(manifest 아이콘) |
android-chrome-512.png | 512×512 | Android 스플래시 화면 등 큰 표시 영역 |
site.webmanifest | text | 사이트 이름, start_url, display, 테마 색상, 192/512 PNG를 선언(purpose: "any"; 패키지에 maskable 아이콘은 없음, 위 참고) |
11개 파일입니다. 전체 크기는 소스에 따라 다르며, 평면적인 이모지와 단순한 SVG는 사진보다 훨씬 작게 압축됩니다.
ICO 형식의 동작 방식
favicon.ico는 세트에서 가장 묘한 파일입니다. PNG보다 먼저 태어났기 때문이죠 — Windows 3.0에서 태어났고 원래 Device-Independent Bitmap (DIB / BMP) 프레임을 운반했습니다. 오늘날 대부분의 생성기는 PNG 압축 항목을 출력하며, Windows Vista 이후의 셸, IE 11, 그 후 모든 브라우저가 네이티브로 지원합니다. PNG 압축 항목은 더 작고, 알파를 보존하며, 형식도 직관적입니다.
ICO 파일의 구조는 이렇습니다:
ICONDIR (6 바이트)
├── reserved (2 바이트, 영)
├── type (2 바이트, 1 = ICO, 2 = CUR)
└── count (2 바이트)
ICONDIRENTRY × count (각 16 바이트)
├── width (1 바이트, 0은 256을 의미)
├── height (1 바이트, 0은 256을 의미)
├── colorCount (1 바이트, 32-bit에선 영)
├── reserved (1 바이트, 영)
├── colorPlanes (2 바이트, 1)
├── bitsPerPixel (2 바이트, 32)
├── imageSize (4 바이트)
└── imageOffset (4 바이트 — ICO 파일 내 절대 오프셋)
(이미지 페이로드는 디렉터리 뒤에 연결)
PNG 항목의 경우, imageSize는 완전한 PNG 파일(자체 IHDR / IDAT / IEND 청크 포함)의 크기이고 imageOffset은 그 PNG의 첫 바이트를 가리킵니다. ICONDIRENTRY의 너비와 높이는 PNG 자체의 너비와 높이에 맞추세요. 읽는 쪽은 디코딩하기 전에 디렉터리를 보고 항목을 고릅니다.
ZeroTool 생성기는 16, 32, 48의 PNG를 ICO에 담고 그보다 큰 크기는 넣지 않습니다. 더 많은 픽셀이 필요한 브라우저는 <link rel="icon">의 PNG, apple-touch-icon, manifest에서 가져갑니다. 이 ICO를 Windows 데스크톱 아이콘(256×256까지 쓰임)으로도 쓰려면 그 크기를 포함한 ICO를 따로 만드세요.
ZIP 컨테이너의 동작 방식
브라우저는 11개 다운로드를 발생시키지 않고 한 번의 클릭으로 11개 파일을 사용자에게 전달할 방법이 필요해서, 생성기는 그것들을 ZIP으로 묶습니다. ZIP은 모든 운영체제가 서드파티 도구 없이 열 수 있어 수십 년간 기본 컨테이너였습니다. 내부는 계층 구조입니다: 각각 페이로드가 따라오는 local file header의 시퀀스, 그다음 모든 항목을 나열하는 「central directory」, 마지막으로 리더에게 central directory 시작 위치를 알려주는 「end-of-central-directory」 레코드.
각 항목의 페이로드 인코딩 방법은 둘입니다: STORED (압축 안 함) 또는 DEFLATE. PNG는 이미 압축되어 있어 DEFLATE를 다시 걸어도 거의 줄지 않습니다. ZeroTool 생성기는 모든 항목에 STORED를 쓰고 DEFLATE 구현을 페이지에 싣지 않습니다.
CRC-32는 ZIP 사양에서 건너뛸 수 없는 유일한 부분입니다. 모든 항목은 비압축 바이트의 32-bit CRC를 가져야 합니다. 생성기는 스크립트 시작 시 256개 항목 룩업 테이블을 미리 계산하고, 파일별로 바이트를 한 번 훑습니다. 파일 이름은 General Purpose Bit Field의 비트 11에서 UTF-8로 표시되어, macOS Finder, Windows Explorer, 7-Zip에서 비 ASCII 이름이 깨지지 않고 읽힙니다.
Canvas 파이프라인
각 PNG 그리기는 같은 5단계를 따릅니다:
// 한 타깃 크기에 대한 의사코드
const canvas = createCanvas(size, size);
const ctx = canvas.getContext('2d');
if (shape !== 'square') {
drawShapePath(ctx, shape, size);
ctx.clip(); // 둥근 / 원형의 알파 마스크
}
if (bgMode === 'color') {
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, size, size);
}
const inner = size * (1 - padding * 2);
drawSourceCentered(ctx, source, size, inner); // emoji / text / image / svg
const pngArrayBuffer = await canvasToPng(canvas);
canvas.toBlob('image/png')이 무거운 일을 합니다 — 리샘플링, 알파 인코딩. 출력은 IHDR, 하나 이상의 IDAT 청크, IEND를 갖춘 완전한 PNG입니다. IDAT 안의 zlib 압축 수준은 브라우저가 정하므로 파일 크기는 브라우저마다 조금씩 다릅니다.
알아 둘 실패 모드 셋:
- canvas 오염. canvas가 오염되면
toBlob이SecurityError를 던집니다. 생성기는 이를 잡아서 SVG 안의 외부 리소스를 먼저 인라인하라고 안내합니다. - 컬러 이모지 글꼴 없음. 컬러 이모지 글꼴이 없는 Linux 환경에서는 canvas가 대체 글리프(흔히 빈 상자)를 그리고, 그 상자가 모든 PNG에 들어갑니다. 생성기는 이를 감지하지 않으니 미리보기를 확인하거나 이미지 / SVG 입력으로 바꾸세요.
- 큰 이미지의 메모리 압력. 생성기는 9개 크기를 하나씩 그리고 각
toBlob을 await하며, 9개 canvas를 동시에 들고 있지 않습니다.
SVG, 이모지, 그리고 크로스 플랫폼 일관성 문제
가장 미묘한 출력 차이는 컬러 이모지 글꼴에서 옵니다. macOS는 Apple Color Emoji, Windows는 Segoe UI Emoji, Android와 많은 Linux 데스크톱은 Noto Color Emoji를 탑재하며, 같은 코드 포인트(예: 🚀)도 글꼴마다 그리는 방식이 다릅니다.
macOS에서 favicon을 생성한 뒤 누군가 Windows에서 사이트를 방문해도, 그들은 여전히 당신이 생성한 favicon을 봅니다 — 바이트는 이미 PNG에 구워졌습니다. Canvas 렌더러는 개발자의 머신에서 그려진 모습 그대로 이모지를 캡처해 그 픽셀을 모든 방문자에게 배달합니다. 그러므로 한 머신을 골라 한 번 생성하면, 그 후 모든 방문자에게 결과는 일관됩니다. 일관성이 정말 문제 되는 곳은 개발자 자신의 반복 작업입니다: 6개월 뒤 다른 OS에서 재생성하면 약간 다른 favicon이 만들어집니다.
머신 간 픽셀 동일성을 원한다면 Image 또는 SVG 입력으로 전환하세요. SVG 벡터 마크는 OS 글꼴 스택이 아니라 SVG 자체에 따라 결정론적으로 비트맵화됩니다. 단, SVG는 자기 완결적이어야 합니다 — 원격 리소스를 참조하는 <image href> 불가, 외부 <use> 조각 불가, 원격 @font-face URL 불가. 붙여 넣기 전에 모든 외부 의존을 인라인 처리하세요.
HTML 스니펫이 해 주는 일
생성기는 다음 스니펫을 출력하며, 이를 <head> 안에 둡니다:
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon-48.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="theme-color" content="#ffffff">
몇 가지 미묘한 점:
- HTML 표준은 브라우저가
sizes와type을 보고 모든<link rel="icon">후보 중에서 고르도록 하며, 우선순위는 정하지 않습니다. ICO를 맨 앞에 두는 것은 관례이지 필수가 아닙니다. <meta name="theme-color">는 모바일 Safari와 Chrome의 주소창과, 설치된 PWA의 타이틀 바 색상을 제어합니다. favicon은 아니지만 같은 head 섹션에 거주하고 브랜드 색상 결정을 공유하기에 favicon 세트와 함께 다닙니다.- 파일 이름
site.webmanifest는 관습이며, 많은 튜토리얼은manifest.json을 씁니다. 둘 다 동작합니다. 프로젝트 다른 부분이 쓰는 쪽에 맞추는 게 편합니다. - iOS Safari는 홈 화면에
apple-touch-icon이 있으면 그것을 씁니다. Android Chrome은 설치된 웹 앱에 manifest 아이콘을 씁니다.
다른 생성기와 비교한 ZeroTool
RealFaviconGenerator는 오래된 대표 도구로, 플랫폼별 옵션이 더 많고 기존 사이트의 파비콘 설정을 점검하는 기능도 있습니다. 그런 옵션이 필요하면 쓰세요.
favicon.io는 생각이 ZeroTool과 가장 가깝습니다. 입력이 단순하고(이모지, 텍스트, 이미지) webmanifest를 포함한 작은 패키지를 내놓습니다.
ZeroTool 생성기는 11개 파일, webmanifest, 복사해 붙일 HTML 스니펫을 내놓으며, 모든 것을 브라우저에서 그리고 소스 이미지를 업로드하지 않습니다.
배포 전 체크리스트
패키지를 배포하기 전, 다음을 점검하세요:
- 11개 파일을 사이트 루트에 두세요. 아이콘 선언을 찾지 못한 브라우저와 크롤러는 루트의
/favicon.ico를 요청하고, HTML 스니펫과 manifest도 루트 경로를 씁니다. - HTML 스니펫을
<head>안에 붙여 넣으세요. PNG와 apple-touch-icon은 페이지가<link>로 선언할 때만 쓰입니다. - 생성기에 사이트 이름을 입력하세요.
site.webmanifest의name(짧은 이름은short_name)으로 들어갑니다. 비워 두면 생성기는 두 필드를 쓰지 않으며, Chrome이 사이트를 앱으로 설치하라고 안내하지 않습니다. - DevTools의 Application 패널에서 테스트하세요. Application → Manifest에서 파싱된 manifest, 해석된 아이콘, 오류를 볼 수 있습니다. 「Show only the minimum safe area for maskable icons」로 maskable 잘림을 미리 볼 수 있습니다.
- iOS 경로를 확인하세요. 실제 iPhone(또는 iOS 시뮬레이터)에서 「홈 화면에 추가」를 해 보세요. iOS는 모서리를 직접 둥글게 하고 투명도를 유지하지 않으므로 apple-touch-icon에는 불투명한 배경을 주세요.
- Google 검색의 파비콘을 확인하세요. Google은 정사각형에 8×8 이상을 요구하고 48×48보다 큰 크기를 권장합니다(Google 검색 센터). 홈페이지를 다시 크롤링한 뒤에 반영되며, Search Console에는 파비콘 보고서가 없습니다.
- 브랜드가 바뀌면 다시 만드세요. favicon 세트를 일회성 자산이 아니라 빌드 산출물로 다루세요. 소스 파일(생성기에 넣은 SVG 또는 고해상도 PNG)을 버전 관리에 두고, 마크나 테마 색이 바뀔 때 같은 소스에서 다시 생성하세요.
개인정보 메모
전체 파이프라인이 브라우저 안에서 동작하므로 소스 이미지의 어떤 버전도 기기를 떠나지 않습니다. 생각보다 중요한 점입니다. 출시 전 제품의 파비콘을 만들 때 업로드 방식의 생성기를 쓰면 브랜드 마크를 그 서비스에 넘기게 됩니다. ZeroTool은 이미지를 로컬에 둡니다. DevTools → Network를 열고 Generate를 누르면 이미지를 실은 요청은 없고, 분석 이벤트는 도구 이름과 동작만 기록합니다.
더 읽기
- Apple의 「Configuring web content」 가이드: apple-touch-icon 사이징
- W3C Web App Manifest 사양 — manifest icon
purpose값의 사실 출처 - web.dev: PWA의 Maskable 아이콘
- Google Search Central: 검색 결과에 표시할 favicon 정의
- PKWARE ZIP file format specification (APPNOTE.TXT) — 전체 ZIP 레이아웃을 읽고 싶은 이를 위해
ZeroTool 관련 도구: SVG → PNG 변환기, WebP 변환기, 이미지 Base64 변환기, QR 코드 생성기.