font-face CSS 작성법, woff2 순서부터

웹폰트 파일을 내려받아 사이트에 적용하려면 @font-face 규칙을 작성해야 하는데, src 안에 woff2·woff·ttf를 어떤 순서로 나열해야 하는지, font-family에 따옴표가 왜 필요한지가 자주 헷갈립니다. 이 순서와 문법은 임의로 정해진 관행이 아니라 브라우저가 CSS를 읽는 방식 자체에서 나온 규칙입니다. 그 동작 원리를 코드 구조 중심으로 하나씩 짚습니다.
요약 ① src 목록은 브라우저가 위에서부터 읽다가 지원하는 첫 포맷에서 멈추기 때문에 woff2 → woff → truetype 순서가 고정됩니다. ② font-family 이름은 공백·예약어 충돌을 피하기 위해 따옴표로 감싸는 것이 안전합니다. ③ font-display: swap은 폰트가 로드되기 전 대체 글꼴로 먼저 텍스트를 보여주고 로드가 끝나면 지정한 폰트로 교체합니다.
웹폰트 파일 3개를 받고 나면 여기서 막힙니다
폰트 파일을 웹용으로 변환해주는 사이트나 다운로드 도구를 쓰면 보통 .woff2, .woff, .ttf 세 개가 함께 나옵니다. 브라우저마다 지원 포맷이 달라 하나만 넣지 않고 세 개를 모두 CSS에 연결해야 합니다.
문제는 여기서부터입니다. 검색해서 나오는 예제마다 src 안의 순서가 제각각이고, 어떤 자료는 ttf를 맨 위에 두기도 합니다. 순서를 바꿔 써도 브라우저가 에러를 내지 않고 조용히 폰트를 로드하기 때문에, 눈으로는 무엇이 잘못됐는지 바로 확인하기 어렵습니다.
font-family에 따옴표를 붙여야 하는지, font-weight를 실제 파일과 다르게 써도 되는지도 마찬가지로 애매합니다. 결과는 대부분 "적용된 것 같기도 하고 원래 폰트가 나오는 것 같기도 한" 애매한 화면으로 남습니다.
도구 없이 @font-face 코드를 직접 쓰는 방법
@font-face 규칙은 폰트 이름을 선언하고, 그 이름에 연결할 파일 목록(src)과 굵기·스타일 정보를 붙이는 구조입니다. 기본 형태는 다음과 같습니다.
@font-face {
font-family: "Pretendard";
src: url("/fonts/pretendard.woff2") format("woff2"),
url("/fonts/pretendard.woff") format("woff"),
url("/fonts/pretendard.ttf") format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
이 구조에서 순서와 문법이 왜 이렇게 되어야 하는지를 하나씩 봅니다.
src 순서가 woff2 → woff → truetype로 고정되는 이유
src 속성에는 콤마로 구분된 url()·format() 쌍을 여러 개 나열할 수 있습니다. 브라우저는 이 목록을 위에서부터 순서대로 읽으면서, 자신이 지원하는 첫 번째 포맷을 찾으면 그 파일만 내려받고 나머지는 무시합니다.
그래서 나열 순서가 곧 우선순위입니다. WOFF2는 압축률이 높아 파일 용량이 가장 작고, WOFF는 그보다 오래됐지만 더 넓게 지원되며, TTF(TrueType)는 가장 오래된 폴백 포맷입니다. woff2를 맨 위에 둬야 이를 지원하는 브라우저가 가장 가벼운 파일을 받고, 지원하지 않는 환경만 다음 줄로 넘어가 woff를, 그마저 안 되면 ttf를 받습니다.
순서를 반대로 써서 ttf를 맨 위에 두면, ttf도 대부분 브라우저가 지원하는 포맷이라 목록 첫 줄에서 바로 멈춰버립니다. 결과적으로 woff2를 지원하는 최신 브라우저까지 더 무거운 ttf 파일을 그대로 받게 됩니다. 브라우저 입장에서는 오류가 아니라 지정된 순서를 그대로 따른 것이라 콘솔에 경고도 뜨지 않습니다.
font-family에 따옴표를 쓰는 이유
font-family 값은 CSS에서 두 가지 방식으로 쓸 수 있습니다. 따옴표 없이 식별자(identifier)로 쓰거나, 따옴표로 감싼 문자열(string)로 쓰는 방식입니다.
식별자로 쓸 경우 공백이 있는 이름은 여러 단어로 쪼개져 해석될 위험이 있고, 숫자로 시작하는 이름이나 sans-serif·inherit 같은 CSS 예약어와 겹치는 이름은 파싱 오류로 이어질 수 있습니다. 따옴표로 감싸면 이런 문제 없이 이름 전체가 하나의 문자열로 정확히 전달됩니다.
그래서 폰트 이름에 띄어쓰기가 없더라도 따옴표를 붙이는 습관이 안전합니다. 위 예시에서 "Pretendard"를 따옴표로 감싼 것도 이 때문입니다.
font-display: swap이 렌더링에서 실제로 하는 일
font-display는 웹폰트 파일이 아직 다운로드되지 않은 짧은 시간 동안 브라우저가 텍스트를 어떻게 보여줄지 정하는 속성입니다. swap 값을 주면 브라우저는 폰트를 기다리지 않고 즉시 대체 글꼴(폴백 폰트)로 텍스트를 먼저 그립니다.
그 뒤 지정한 웹폰트 파일이 로드를 마치면, 이미 그려진 텍스트를 그 폰트로 교체합니다. font-display를 지정하지 않으면 브라우저 기본 동작에 따라 폰트가 로드될 때까지 텍스트가 보이지 않는 대기 구간이 생길 수 있는데, swap은 이 대기 구간을 사실상 없애 텍스트가 항상 먼저 보이도록 만드는 값입니다.
다만 폴백 폰트와 실제 폰트의 글자 폭이 다르면 교체되는 순간 줄바꿈이나 배치가 살짝 움직이는 것을 볼 수 있습니다. 텍스트가 늦게 나타나는 것보다 즉시 보이는 편이 나을 때 swap을 씁니다.
src 순서, 따옴표, font-display까지 구조를 알아도 폰트 이름과 파일 경로 세 개를 매번 손으로 맞춰 쓰는 일은 번거롭습니다. 이 과정을 저희 도구로 바로 확인할 수 있습니다.
폰트 이름과 파일 경로만 입력하면 순서가 맞는 @font-face 코드가 실시간으로 만들어집니다. 서버 전송 없이 브라우저에서만 처리됩니다.

실제로 폰트가 안 보이는 세 가지 실수
문법 오류 없이 코드가 그대로 작동하는 것처럼 보여도 폰트가 적용되지 않는 경우 대부분 아래 세 가지 중 하나입니다.
| 실수 | 무엇이 벌어지는가 |
|---|---|
| src 순서를 반대로 씀 (ttf를 맨 위) | 최신 브라우저도 목록 첫 줄에서 멈춰 더 무거운 ttf를 그대로 받음 |
| format() 이름이 실제 파일 확장자와 다름 | 브라우저가 힌트만 보고 포맷을 판단해 해당 줄을 건너뛰거나 파일을 읽지 못함 |
| font-weight·font-style이 실제 파일과 다름 | 선언한 굵기·스타일로 텍스트를 찾을 때 이 폰트가 매칭되지 않아 폴백 폰트나 합성 볼드가 대신 쓰임 |
format() 이름은 브라우저가 파일을 열어보기 전에 먼저 참고하는 힌트입니다. 실제로는 .woff2 파일인데 format("woff")라고 써두면, 브라우저는 이 줄을 지원하지 않는 포맷으로 판단해 건너뛰고 다음 줄로 넘어갈 수 있습니다. 파일 자체는 멀쩡한데 코드의 이름표가 틀려 로드되지 않는 경우입니다.
font-weight도 마찬가지입니다. 브라우저는 파일을 열어 실제 굵기를 확인하지 않고 CSS에 적힌 숫자를 그대로 믿습니다. Bold 파일을 올려두고 font-weight: 400으로 선언하면, font-weight: 700을 요청하는 텍스트에는 이 폰트가 연결되지 않고 폴백 폰트나 브라우저가 임의로 합성한 볼드가 대신 쓰입니다.
정리
- src 순서는 관행이 아니라 브라우저가 지원하는 첫 포맷에서 멈추는 동작 원리 때문에 woff2 → woff → truetype로 고정됩니다.
- font-family는 공백·예약어 충돌을 막기 위해 따옴표로 감싸는 것이 안전합니다.
- font-display: swap은 폰트 로드를 기다리지 않고 대체 글꼴로 먼저 보여준 뒤 교체하는 방식입니다.
- format() 값은 실제 파일 확장자와, font-weight·font-style은 실제 파일의 굵기·스타일과 정확히 일치해야 폰트가 제대로 연결됩니다.
- 이 네 가지만 지키면 별도 도구 없이도 정확한 @font-face 코드를 직접 작성할 수 있습니다.
폰트 이름과 파일 경로만 입력하면 순서가 맞는 @font-face 코드가 실시간으로 만들어집니다. 서버 전송 없이 브라우저에서만 처리됩니다.
자주 묻는 질문
woff2 파일 하나만 넣어도 되나요?
지원 대상을 최신 브라우저로 좁힌다면 가능합니다. 다만 구형 브라우저까지 고려한다면 woff를 폴백으로 함께 넣는 것이 안전하며, ttf는 아주 오래된 환경까지 지원해야 할 때만 추가로 넣는 포맷입니다.
format() 안의 문자열은 파일명 그대로 써야 하나요?
아닙니다. format()에는 CSS 스펙에서 정해진 값(woff2, woff, truetype, opentype 등)을 씁니다. 다만 그 값이 실제 파일의 포맷과 일치해야 합니다. .ttf 파일이면 format("truetype"), .otf 파일이면 format("opentype")을 씁니다.
굵기가 다른 폰트 파일 여러 개는 어떻게 선언하나요?
같은 font-family 이름으로 굵기가 다른 파일마다 @font-face 규칙을 각각 따로 선언합니다. Regular 파일은 font-weight: 400, Bold 파일은 font-weight: 700으로 선언해두면 브라우저가 실제 사용된 font-weight 값에 맞는 규칙을 골라 씁니다.
font-display에 swap 말고 다른 값도 있나요?
auto, block, swap, fallback, optional 다섯 가지 값이 있습니다. block은 폰트를 짧게 기다렸다가 텍스트를 그리고, swap은 기다리지 않고 대체 글꼴로 먼저 그린 뒤 교체합니다. 텍스트를 최대한 빨리 보여주고 싶다면 swap을, 폰트가 로드된 뒤 갑자기 바뀌는 모습 자체를 줄이고 싶다면 block을 검토합니다.
