이 글은 누구를 위한 것인가
- 디자이너마다 폰트 크기를 다르게 쓰고 있어 일관성이 없는 팀
- "이 텍스트를 얼마나 크게 해야 하나요?"라는 질문이 매번 나오는 환경
- 반응형에서 타이포그래피 크기를 일일이 조정하는 작업이 반복되는 개발팀
타이포그래피 시스템이 없을 때 생기는 문제
"조금 더 크게", "좀 강조해 주세요" — 명확한 스케일이 없으면 폰트 크기가 13px, 14px, 15px, 16px이 혼재하게 된다. 결과는 시각적 위계 혼란, 일관성 없는 읽기 경험, 그리고 코드 전역에 흩어진 매직 넘버들이다.
1. 모듈러 스케일: 수학적 비율로 크기 결정
모듈러 스케일은 기준 크기와 비율(ratio)을 정의해 모든 폰트 크기를 수학적으로 도출한다.
주요 비율
| 비율 이름 | 값 | 느낌 |
|---|---|---|
| Minor Second | 1.067 | 미세한 차이, 본문 중심 |
| Major Second | 1.125 | 부드러운 위계 |
| Minor Third | 1.200 | 균형 잡힌 위계 |
| Major Third | 1.250 | 뚜렷한 구분 |
| Perfect Fourth | 1.333 | 강한 위계, 마케팅 페이지 |
| Golden Ratio | 1.618 | 극적인 대비, 랜딩 페이지 |
대부분의 UI 시스템에서는 Minor Third(1.2) 또는 Major Third(1.25) 가 적합하다.
스케일 계산 예시 (기준 16px, 비율 1.25)
xs: 16 ÷ 1.25² = 10.24px → 10px
sm: 16 ÷ 1.25 = 12.8px → 13px
base: 16px (기준)
lg: 16 × 1.25 = 20px
xl: 16 × 1.25² = 25px
2xl: 16 × 1.25³ = 31.25px → 31px
3xl: 16 × 1.25⁴ = 39px
4xl: 16 × 1.25⁵ = 48.83px → 49px
2. 토큰으로 표현하기
계산된 스케일을 디자인 토큰으로 정의한다.
:root {
/* font-size */
--text-xs: 0.625rem; /* 10px */
--text-sm: 0.8125rem; /* 13px */
--text-base: 1rem; /* 16px */
--text-lg: 1.25rem; /* 20px */
--text-xl: 1.5625rem; /* 25px */
--text-2xl: 1.9375rem; /* 31px */
--text-3xl: 2.4375rem; /* 39px */
--text-4xl: 3.0625rem; /* 49px */
/* line-height */
--leading-tight: 1.25;
--leading-snug: 1.375;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--leading-loose: 2.0;
/* letter-spacing */
--tracking-tight: -0.025em;
--tracking-normal: 0em;
--tracking-wide: 0.025em;
--tracking-wider: 0.05em;
--tracking-widest: 0.1em;
/* font-weight */
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
}
3. 줄 높이와 자간: 자주 놓치는 설정
폰트 크기만 정하고 줄 높이와 자간을 방치하면 가독성이 떨어진다.
줄 높이 가이드라인
| 용도 | 줄 높이 | 이유 |
|---|---|---|
| 제목 (24px+) | 1.1~1.25 | 큰 텍스트는 줄 간격 좁아도 읽힘 |
| 소제목 (18~24px) | 1.25~1.375 | 중간 균형 |
| 본문 (14~18px) | 1.5~1.625 | 긴 문장 가독성 |
| 캡션 (10~13px) | 1.4~1.5 | 작은 텍스트는 약간 넓게 |
| 코드 | 1.5~1.7 | 코드 블록 판독성 |
핵심: 폰트가 클수록 줄 높이를 낮추고, 작을수록 높인다.
자간 가이드라인
| 용도 | 자간 |
|---|---|
| 대형 제목 (32px+) | -0.02em ~ -0.04em (좁게) |
| 소제목 | 0 (기본) |
| 본문 | 0 ~ +0.01em |
| 전체 대문자 (ALL CAPS) | +0.05em ~ +0.1em (넓게) |
| 캡션·레이블 | +0.01em ~ +0.03em |
대문자로만 구성된 레이블은 반드시 자간을 넓혀야 읽기 편하다.
4. 시맨틱 텍스트 스타일
숫자 스케일(text-2xl)을 직접 쓰는 것보다 역할 기반 시맨틱 스타일을 정의하면 일관성이 생긴다.
/* 시맨틱 텍스트 스타일 */
.text-display {
font-size: var(--text-4xl);
font-weight: var(--font-bold);
line-height: var(--leading-tight);
letter-spacing: var(--tracking-tight);
}
.text-heading-1 {
font-size: var(--text-3xl);
font-weight: var(--font-bold);
line-height: 1.2;
letter-spacing: -0.01em;
}
.text-heading-2 {
font-size: var(--text-2xl);
font-weight: var(--font-semibold);
line-height: var(--leading-snug);
}
.text-heading-3 {
font-size: var(--text-xl);
font-weight: var(--font-semibold);
line-height: var(--leading-snug);
}
.text-body-lg {
font-size: var(--text-lg);
font-weight: var(--font-normal);
line-height: var(--leading-relaxed);
}
.text-body {
font-size: var(--text-base);
font-weight: var(--font-normal);
line-height: var(--leading-normal);
}
.text-body-sm {
font-size: var(--text-sm);
font-weight: var(--font-normal);
line-height: var(--leading-normal);
}
.text-caption {
font-size: var(--text-xs);
font-weight: var(--font-normal);
line-height: var(--leading-normal);
letter-spacing: var(--tracking-wide);
}
.text-label {
font-size: var(--text-sm);
font-weight: var(--font-medium);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
}
5. 유체 타이포그래피: 뷰포트에 따라 부드럽게 변화
모바일과 데스크탑에 서로 다른 폰트 크기를 브레이크포인트로 전환하면 크기가 갑자기 바뀐다. 유체 타이포그래피는 뷰포트 너비에 따라 폰트 크기가 연속적으로 변화한다.
/* clamp(최솟값, 선형 함수, 최댓값) */
.text-heading-1 {
font-size: clamp(1.75rem, 3vw + 1rem, 3rem);
/* 뷰포트 320px: ~1.75rem (28px) */
/* 뷰포트 768px: ~2.3rem */
/* 뷰포트 1280px+: 3rem (48px) */
}
계산 공식:
선형 함수 = 기울기vw + 절편rem
기울기 = (최대 크기 - 최소 크기) / (최대 뷰포트 - 최소 뷰포트) × 100
절편 = 최소 크기 - 기울기 × (최소 뷰포트 / 100)
일일이 계산하지 않고 Utopia 같은 도구로 자동 생성할 수 있다.
6. 한국어 타이포그래피 고려사항
한국어는 영어와 다른 특성이 있어 별도 조정이 필요하다.
줄 높이
한국어 글자는 세로 폭이 넓다. 영어 기준 1.5 줄 높이를 그대로 쓰면 답답하게 느껴진다. 한국어 본문은 1.6~1.8 줄 높이가 더 편하다.
폰트 패밀리 스택
:root {
--font-sans:
'Pretendard Variable', /* 한국어 가변 폰트 (권장) */
'Pretendard',
-apple-system,
BlinkMacSystemFont,
system-ui,
'Apple SD Gothic Neo',
'Noto Sans KR',
sans-serif;
}
word-break
한국어는 단어 사이 공백이 없어 긴 단어가 줄바꿈 없이 컨테이너를 넘칠 수 있다.
body {
word-break: keep-all; /* 단어 단위로 줄바꿈 (한국어 권장) */
overflow-wrap: break-word; /* 컨테이너 넘침 방지 폴백 */
}
맺으며
타이포그래피 시스템은 한 번 정해두면 디자이너와 개발자 모두 "이 텍스트 몇 px로 하면 돼요?"라는 질문 없이 시맨틱 스타일 이름만으로 소통할 수 있게 해준다.
모듈러 스케일로 크기를 정하고, 줄 높이와 자간까지 토큰에 포함시키고, 한국어 특성에 맞게 조정하면 충분하다. Figma의 Text Styles에 동일한 이름으로 정의해두면 디자인-코드 간 언어가 일치한다.