app/root.tsx에 favicon <link> 태그를 그대로 붙여넣었는데 Remix가 중복을 제거하거나 조용히 누락시키는 현상을 보신 적이 있다면, 올바른 패턴은 links export입니다. 이 위치는 Remix v2가 HTML link descriptor를 정의하기를 기대하는 정확한 곳이며, 프레임워크가 이를 자동으로 document head로 렌더링해 줍니다. 전체 API 명세는 공식 Remix route links 문서를 참고해 주세요.
사전 요구사항
작동하는 Remix v2 프로젝트와 favicon 파일 묶음이 필요합니다. 모던 셋업을 모두 구성하려면 다음 파일을 준비하세요.
/public/favicon.svg— 벡터 소스, 다크 모드 지원/public/favicon.ico— 구형 브라우저용 레거시 폴백/public/apple-touch-icon.png— iOS 홈 화면용 180×180/public/site.webmanifest— PWA 설치 동작용
public/에 넣은 모든 파일은 사이트 루트에서 그대로 서빙되므로, /public/favicon.svg는 별도의 라우팅 작업 없이 https://yoursite.com/favicon.svg로 해석됩니다.
Step 1: root.tsx에 links export 구성하기
links export는 <link> descriptor 배열을 돌려주는 함수입니다. Remix는 각 descriptor를 document head의 실제 HTML 태그로 매핑합니다. 타입 안전성을 위해 @remix-run/node(또는 런타임별 패키지)에서 LinksFunction을 import해 사용하세요.
import type { LinksFunction } from '@remix-run/node';
export const links: LinksFunction = () => [
{ rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
{ rel: 'alternate icon', href: '/favicon.ico', type: 'image/x-icon' },
];
rel 값은 HTML 명세와 1:1로 대응합니다. 사용 가능한 전체 어휘는 whatwg의 rel=icon 정의를 참고하세요. 브라우저는 현재 컨텍스트에 매칭되는 첫 번째 icon를 선택하므로, 선명한 SVG를 ICO 폴백보다 먼저 배치하세요.
Step 2: Apple Touch 및 manifest 항목 추가하기
Remix는 리소스 종류와 무관하게 모든 descriptor를 동일한 방식으로 처리합니다. 같은 배열에 차곡차곡 쌓아 두면 됩니다.
export const links: LinksFunction = () => [
{ rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
{ rel: 'alternate icon', href: '/favicon.ico', type: 'image/x-icon' },
{ rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' },
{ rel: 'manifest', href: '/site.webmanifest' },
];
sizes 속성은 정보 제공용이며, 브라우저가 각 렌더링 컨텍스트에 가장 잘 맞는 icon를 고를 때 참고합니다. Remix export를 넘어선 favicon link 태그 패턴은 favicon HTML 코드 예제에서 확인할 수 있습니다.
Step 3: 다크 모드 SVG 깔끔하게 배포하기
브라우저는 icon link 태그의 media 속성도 그대로 인지합니다. 두 개의 SVG를 배포하고 사용자의 color scheme에 따라 브라우저가 알맞은 파일을 선택하도록 할 수 있습니다. 두 번째 파일을 /public/favicon-dark.svg로 저장한 뒤, 두 descriptor를 모두 추가하세요.
{
rel: 'icon',
href: '/favicon.svg',
type: 'image/svg+xml',
media: '(prefers-color-scheme: light)',
},
{
rel: 'icon',
href: '/favicon-dark.svg',
type: 'image/svg+xml',
media: '(prefers-color-scheme: dark)',
},
HTML 명세는 icon link에서 media descriptor를 명시적으로 지원하므로, 이 패턴은 hack이 아닌 정식 first-class 패턴입니다. 두 SVG가 컬러 스킴에 맞춰 정상적으로 교체되도록 만드는 SVG 측 설정은 favicon 다크 모드 가이드에서 media query 문법을 자세히 다룹니다.
Step 4: icon 업데이트 시 cache-bust 처리하기
브라우저는 favicon.ico를 매우 적극적으로 캐싱하며, Remix는 기본적으로 public/ 자산에 content hash를 붙이지 않습니다. 새 디자인을 배포할 때는 버전 쿼리 스트링을 덧붙이세요.
{ rel: 'icon', href: '/favicon.svg?v=2', type: 'image/svg+xml' }
icon 릴리스마다 v 값을 올려주세요. URL에 묶인 캐시가 갱신되어, 사용자는 새 탭 그래픽을 즉시 보게 됩니다.
자주 발생하는 실수
Remix links export로 icon을 연결할 때는 다음 사항에 주의하세요.
- raw JSX에 태그를 직접 넣기.
root.tsx의<head>안에 작성된 태그는 렌더링되지만, Remix의 dedup 로직과 SSR 순서 때문에 조용히 누락될 수 있습니다. export가 문서화된 정식 경로입니다. type을 누락하기. MIME 힌트가 없으면 Safari가 가끔 잘못된 형식을 선택합니다.image/svg+xml과image/x-icon이 안전한 기본값입니다.shortcut icon사용하기. 이 레거시 속성은 HTML 명세에 포함되지 않으며, 모던 브라우저는 이를 무시합니다. 대신alternate icon을 사용하세요.- manifest
rel을 빠뜨리기. PWA 설치는 icon link가 아니라site.webmanifest를 읽습니다. 같은links배열에서 함께 참조하세요. - 배포 후 icon이 stale 상태로 남기. 브라우저 캐시는 배포 시점과 무관하게 유지됩니다. icon 업데이트 시에는 반드시 cache-bust 처리를 하세요.
마무리
Remix links export는 favicon HTML 태그를 연결하기에 가장 깔끔한 위치입니다. 타입 안전성, 예측 가능한 SSR 출력, 그리고 icon 묶음을 업데이트할 때 단 한 파일만 수정하면 되는 단순함을 모두 얻을 수 있습니다. Mzu favicondl의 선명한 벡터 소스와 결합하고 cache-bust 쿼리를 함께 배포하면, 모든 브라우저 탭에서 완성도 높은 Remix 앱을 보여줄 수 있습니다.