새로운 웹뷰 프로젝트를 셋업하면서 “어? 폰트가 뭔가 다른데?”라는 의문에서 시작된 긴 여정의 기록입니다. Next.js에서는 완벽했던 폰트가 Vite 기반 프로젝트로 넘어오니 미묘하게 달라 보였고, 이를 해결하는 과정에서 브라우저의 폰트 렌더링, CSS 최적화, 그리고 UI 라이브러리의 숨겨진 동작까지 깊이 이해할 수 있었습니다.
🔍 문제 발견: “왜 폰트가 다르지?”
증상

- 피그마

- 폰트 적용 전

- 폰트 적용 후
피그마 디자인과 실제 화면을 비교해보니 미묘한 차이가 있었습니다.
특히 이상했던 점들:
- Next.js 프로젝트에서는 정상적으로 보였던 폰트
- 개발 서버에 폰트를 적용하지 않았는데도 “프리텐다드”처럼 보임
- 로컬 폰트를 적용하니 오히려 더 두꺼워짐
- input placeholder만 다른 폰트로 보임
🕵️ 원인 분석 1: 유령 폰트의 정체
시스템 폰트가 적용되고 있었다
첫 번째 반전. 우리가 의도하지 않은 폰트가 적용되고 있었습니다.
케이스 1: Pretendard가 설치된 컴퓨터
/* 브라우저가 시스템 폰트를 찾아서 적용 */
font-family: "Pretendard", sans-serif;
/* → 시스템의 Pretendard 사용 ✅ */
케이스 2: Pretendard가 없는 컴퓨터
font-family: "Pretendard", sans-serif;
/* → sans-serif 폴백 폰트 사용 (애플 SD 산돌고딕 Neo 등) */
/* 이게 Pretendard와 비슷해서 착각할 수 있었습니다 */
🕵️ 원인 분석 2: Medium 파일의 함정
로컬 폰트를 적용했더니 오히려 문제가 더 커졌습니다.
// 기존 코드
export const Pretendard = localFont({
src: "/fonts/Pretendard-Medium.woff2",
variable: "--font-pretendard",
});
Medium 파일의 한계

기존에 설치된 폰트는 미디움 형태였습니다
variable이 아닌 미디움인 이유는 경량화와 디자인시스템상 사용하지 않아서의 영향이 있는것 같았습니다.
Pretendard-Medium.woff2가 지원하는 범위는 아래와 같습니다.
font-weight: 600~900: 정상 작동font-weight: 100~500: 미지원
발생한 문제들
1. 얇은 폰트(100-500)가 적용되지 않음
2. 굵은 폰트(600-900)가 과도하게 두꺼워짐
브라우저는 요청한 굵기의 폰트 파일이 없으면, 기존 폰트를 “억지로 굵게” 만듭니다. 이 과정에서 폰트가 뭉개지고 디자이너가 의도한 것과 달라집니다.
🕵️ 원인 분석 3: Chakra UI의 숨겨진 설정
Next.js에서는 정상이었는데 왜 Vite에서만 다를까? 범인은 Chakra UI였습니다.
font-smoothing의 영향
Chakra UI 내부 코드를 보니:
const preflightCss: Record<string, CssProperties> = {
[scope || "html"]: {
lineHeight: 1.5,
"--font-fallback":
"ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'",
WebkitTextSizeAdjust: "100%",
WebkitFontSmoothing: "antialiased",
MozOsxFontSmoothing: "grayscale",
...
}
이 설정이 폰트를 부드럽게 렌더링하면서 미묘한 차이를 만들었습니다.
우리 프로젝트는 웹뷰의 경우 테일윈드를 사용하게 되었고 기존 웹 프로젝트는 차크라 ui를 사용하고 있다보니 나온 차이점이었습니다.
Heading 컴포넌트의 독자적인 폰트 설정
더 큰 문제는 이거였습니다:
// Chakra UI의 Heading 컴포넌트
export const headingRecipe = defineRecipe({
className: "chakra-heading",
base: {
fontFamily: "heading",
fontWeight: "semibold",
},
...
}
전역 CSS로 body { font-family: "Pretendard" }를 설정해도, Chakra의 <Heading> 컴포넌트는 무시합니다!
/* Heading에는 적용 안 됨 */
body {
font-family: "Pretendard";
}
/* ✅ Chakra Config에서 heading에 영억도 설정해야 했습니다 */

다른 폰트로 테스트 해본 결과 해딩을 사용한 폰트는 모두 적용이 안되었었습니다.
💡 Variable Font
모든 문제의 근본 원인은 고정 웨이트 폰트 파일과 차크라 ui로 인한 설정 미스입니다.
Variable Font
하나의 파일에 모든 굵기(100-900)를 담은 폰트입니다.
💡 Chakra UI 올바르게 설정하기
전역 CSS만으로는 부족합니다. Chakra UI는 자체 테마 시스템이 있습니다.
올바른 설정 방법
// chakra.config.ts
import { createSystem, defaultConfig } from "@chakra-ui/react";
const config = createSystem({
theme: {
tokens: {
fonts: {
body: "Pretendard", // 일반 텍스트
heading: "Pretendard", // 제목 (Heading 컴포넌트)
mono: "monospace", // 코드
}
}
}
});
export default config;
🎬 마치며
“폰트 하나 바꾸는 게 뭐가 어려워?”라고 생각했던 과거의 나를 되돌아봅니다. 이번 경험을 통해 브라우저의 폰트 렌더링 메커니즘, UI 라이브러리의 내부 동작, 그리고 웹 최적화에 대해 깊이 배울 수 있었습니다.
Variable Font는 단순히 파일 크기를 줄이는 것 이상의 가치가 있습니다. 디자인 일관성, 성능 최적화, 그리고 개발 편의성까지 모두 개선됩니다.
혹시 여러분도 “왜 폰트가 다르지?”라는 의문을 가지고 계신다면, 이 글이 도움이 되길 바랍니다.
Happy coding! 🚀