콘텐츠로 이동
Study Noteshadcn/ui

8. 아이콘 · 폰트 리소스

아이콘과 폰트는 파일이라서 토큰과 다르게 느껴지지만, 관리 대상인 건 똑같다. 통일되지 않으면 화면이 어색해지고, 잘못 불러오면 화면이 늦게 뜬다.

이 장에서 처음 나오는 말5개
lucide
shadcn/ui의 기본 아이콘 세트. 계속 추가되는 선(line) 스타일 오픈소스 아이콘 모음.
SVGScalable Vector Graphics
수학적으로 그려지는 이미지 형식. 아무리 확대해도 안 깨지고, CSS로 색을 바꿀 수 있다.
`currentColor`
CSS 키워드. "지금 이 요소의 글자색"을 뜻한다. SVG 아이콘이 주변 글자색을 자동으로 따라오게 만드는 장치.
FOIT / FOUTFlash of Invisible / Unstyled Text
웹폰트를 내려받는 동안 글자가 안 보이거나(FOIT) 다른 폰트로 보였다가 바뀌는(FOUT) 현상.
서브셋Subset
폰트 파일에서 필요한 글자만 잘라낸 것. 한글 폰트를 실용적으로 쓰려면 필수.

아이콘은 저마다 선 굵기, 모서리 처리, 여백 비율이 다르다. 두 세트를 섞으면 개별로는 멀쩡한데 나란히 놓으면 어색하다.

{/* ❌ 섞으면 굵기와 크기가 안 맞는다 */}
import { Search } from 'lucide-react'
import { FaUser } from 'react-icons/fa'
import { MdSettings } from 'react-icons/md'
{/* ✅ 한 세트에서만 */}
import { Search, User, Settings } from 'lucide-react'

세트 선택도 테마 결정이다. 프로젝트당 하나로 정하고 리뷰에서 지킨다.

init이 기본으로 깔아 주는 세트다.

import { Search, Loader2 } from 'lucide-react'
<Search className="size-4" />
<Loader2 className="size-4 animate-spin" />
규칙이유
크기는 size-4(16px) 기본14px 글자 옆에 놓았을 때 균형이 맞는다. 버튼 안 아이콘은 대부분 이것
색은 지정하지 않는다lucide는 stroke="currentColor"라서 글자색을 자동으로 따라온다
aria-hidden장식용 아이콘은 화면낭독기가 읽지 않게 한다
의미 있는 아이콘엔 라벨아이콘만 있는 버튼은 aria-label="검색"이 필요하다
클래스크기어디에
size-312px배지 안, 아주 작은 표시
size-416px기본. 버튼·메뉴·입력창
size-520px카드 헤더, 조금 강조
size-6+24px+빈 화면 안내, 히어로

components.json에 등록하면 CLI가 그 세트로 컴포넌트를 받아 온다.

{ "iconLibrary": "tabler" }

새 프로젝트라면 shadcn/create나 preset에서 고르는 편이 안전하다. 이미 받은 컴포넌트는 import와 아이콘 이름이 남아 있으므로, 설정 필드만 바꾸지 말고 CLI의 현재 migration/preset 흐름으로 diff를 확인한다.

폰트는 언제 어떻게 내려받게 할 것인가가 전부다. 잘못하면 화면이 늦게 뜨거나 덜컹거린다.

HTML 도착부터 웹폰트 요청까지 간 뒤 글자를 숨기면 FOIT, 기본 폰트로 보이면 FOUT이 되는 갈림길

첫 방문처럼 폰트가 캐시에 없을 때 브라우저는 대기 중 글자를 숨기거나 폴백으로 보여 준다. 대부분의 본문에서는 폴백을 먼저 보여 주는 쪽이 낫다 — 글자가 안 보이는 것보다 낫기 때문이다.

@font-face {
font-family: 'Pretendard Variable';
src: url('/fonts/pretendard.woff2') format('woff2');
font-display: swap; /* ← 기본 폰트로 먼저 보여준다 = FOUT 선택 */
}
@theme inline {
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI',
'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif;
}
  • 내려받을 게 없다. FOIT도 FOUT도 없다
  • 기기마다 글꼴이 다르다 — 브랜드 통일이 안 된다
  • 관리도구·내부 시스템이라면 최선의 선택

어느 방법을 쓰든 마지막은 똑같다 — 폰트를 토큰에 매달아 컴포넌트가 이름만 알게 한다.

  1. 폰트를 불러온다 (위 셋 중 하나)

  2. 토큰에 연결한다

    @theme inline {
    --font-sans: 'Pretendard Variable', system-ui, sans-serif;
    --font-mono: ui-monospace, Menlo, monospace;
    }
  3. 바탕에 한 번만 적용한다

    <body className="font-sans antialiased">
  4. 컴포넌트는 아무것도 안 한다

    font-sans는 상속되므로 개별 컴포넌트에 폰트 클래스를 붙일 일이 없다. 코드에 폰트가 필요한 자리에만 font-mono를 준다.

  • 아이콘 세트는 하나로 통일한다. 섞으면 선 굵기와 여백이 안 맞아 어색해진다
  • lucide 아이콘은 currentColor라 글자색을 자동으로 따라온다 — 색 클래스를 붙이지 않는다
  • 크기는 size-4(16px)가 기본, 버튼 안에서는 shrink-0을 같이 준다
  • 웹폰트가 아직 없을 때는 FOIT(안 보임) 또는 폴백 표시 정책이 필요하다. font-display: swap은 폴백을 먼저 보인다
  • 방법 셋 — 시스템 폰트(가장 빠름) · next/font(Next.js 기본) · 동적 서브셋(한글의 강력한 선택지)
  • 어느 방법이든 마지막은 --font-sans 토큰에 연결 + <body>에 한 번 적용