
폰트가 계단처럼 뚝뚝 바뀌는 걸 보고 난 뒤
브라우저 창을 천천히 드래그해본 적 있으신가요? 저는 예전에 클라이언트 검수 중 태블릿 구간에서 글자가 갑자기 커 보인다는 피드백을 받고 처음 그 동작을 해봤습니다. 폰트가 계단처럼 뚝뚝 바뀌는 걸 보는 순간, 이건 구조적으로 뭔가 잘못됐다는 생각이 들었습니다. clamp()는 그 문제를 해결해준 함수입니다.
여기서 미리 한 가지 짚고 싶습니다. clamp()가 좋은 도구라는 건 분명한데, 이 함수를 소개하는 글들 중 상당수가 "미디어쿼리를 대체한다"는 표현을 쓰는 경우가 있습니다. 이 말이 반은 맞고 반은 오해를 부릅니다. 이 글에서 그 경계를 분명히 하려 합니다.
미디어쿼리로 폰트를 관리하던 시절의 한계
일반적으로 반응형 웹을 구현할 때 미디어쿼리를 쓰면 된다고 알려져 있습니다. 미디어쿼리란 특정 화면 너비 구간을 지정해두고, 그 구간에 해당할 때만 스타일을 적용하는 CSS 기법입니다. 저도 오랫동안 세 개의 브레이크포인트를 기준으로 폰트 크기를 각각 따로 선언하는 방식을 써왔습니다. 브레이크포인트란 반응형 디자인에서 레이아웃이 전환되는 기준 화면 너비를 뜻합니다.
그런데 이 방식에는 직접 겪어보고 나서야 인식한 구조적 결함이 있습니다. 화면은 픽셀 단위로 연속적으로 늘어나는데, 폰트는 세 개의 구간 안에서만 고정된 값을 유지하다가 경계를 넘는 순간 갑자기 바뀝니다. 개발자 도구를 열고 뷰포트 너비를 천천히 조정해보면 그 순간이 꽤 어색하게 느껴집니다. 뷰포트란 브라우저에서 실제로 콘텐츠가 표시되는 영역을 의미합니다.
클라이언트로부터 태블릿에서 제목이 갑자기 커 보인다는 피드백을 받은 후, 저는 이 문제가 단순한 수치 조정이 아니라 접근 방식 자체의 한계라는 걸 인정해야 했습니다. 브레이크포인트를 늘리는 것도 방법이지만, 그건 구간을 잘게 쪼개는 것일 뿐 연속성 문제를 해결하지는 못합니다. 화면 너비가 연속이면 폰트 크기도 연속적으로 반응해야 자연스럽습니다.
clamp() 함수로 전환했을 때 실제로 달랐던 것
clamp()를 처음 적용해봤을 때 솔직히 이건 예상 밖이었습니다. 개발자 도구의 반응형 뷰에서 창 크기를 드래그하는데, 폰트가 끊기지 않고 화면 너비에 따라 연속적으로 부드럽게 커졌습니다. 그 동작 하나만으로도 이전 방식으로 돌아가기 싫어졌습니다.
clamp()는 세 개의 인자를 받습니다. 최솟값, 유동값, 최댓값 순서입니다. 유동값 자리에는 주로 vw(viewport width) 단위나 calc()를 사용합니다. vw란 뷰포트 너비의 1%에 해당하는 단위로, 화면이 넓어질수록 자동으로 값이 커집니다. 최솟값 아래로는 줄어들지 않고, 최댓값 위로는 커지지 않으면서 그 사이에서 화면 너비에 비례해 자동 조정됩니다.
제가 경험한 또 다른 차이는 텍스트 위계(type hierarchy) 관리였습니다. 텍스트 위계란 h1, h2, 본문, 캡션처럼 요소별 폰트 크기 간의 상대적 비율 관계를 말합니다. 처음에 모든 요소에 동일한 비율의 clamp()를 적용했더니, 화면이 커졌을 때 제목과 본문 크기 차이가 거의 없어지는 문제가 생겼습니다. 요소별로 vw 계수를 달리 설정하고 나서야 큰 화면에서 제목은 드라마틱하게 커지고 본문은 안정적으로 유지되는 균형이 잡혔습니다. 이 부분은 처음 도입할 때 놓치기 쉬운 포인트입니다.
이 실수를 돌아보면서 생각하게 된 건, "유동 폰트를 적용한다"는 것은 단일 요소의 크기를 조정하는 문제가 아니라 전체 타이포그래피 시스템을 설계하는 문제라는 겁니다. 하나의 요소만 따로 떼어서 최적화하면 나머지와의 관계가 무너집니다.
접근성과 calc(rem + vw) 조합이 중요한 이유
접근성 검수에서 px만 쓴 폰트가 지적받은 경험이 있습니다. 그 이후로 저는 clamp()의 유동값에 calc(rem + vw) 조합을 기본으로 쓰고 있습니다. rem이란 루트 요소의 폰트 크기를 기준으로 하는 단위로, 사용자가 브라우저 기본 폰트 크기를 변경했을 때 그 설정을 그대로 반영합니다.
웹 접근성(web accessibility) 기준에서 px 단위는 사용자의 브라우저 폰트 설정을 무시하기 때문에 문제가 됩니다. 웹 접근성이란 시각 장애, 저시력 등 다양한 사용자 환경에서도 콘텐츠를 동등하게 이용할 수 있도록 보장하는 개념입니다. W3C의 웹 콘텐츠 접근성 지침(WCAG)은 텍스트 크기 조정이 가능해야 한다는 기준을 명시하고 있으며, rem 기반 단위가 이를 충족하는 방법 중 하나로 권장됩니다.
clamp()에 rem을 포함시키면 사용자가 브라우저 설정에서 기본 폰트를 크게 키웠을 때 전체 텍스트가 그에 맞게 같이 커집니다. px만 썼을 때는 이 설정이 무시됩니다. 이 차이는 코드를 보는 것만으로는 와닿지 않습니다. 실제로 브라우저 설정에서 폰트 크기를 최대로 올린 뒤 페이지를 열어보면, px 폰트와 rem 폰트의 차이를 한눈에 알 수 있습니다. 한 번이라도 직접 해보시길 권합니다.
실제로 쓸 때 놓치기 쉬운 함정들
clamp()가 우아한 해결책이라는 건 분명합니다. 그런데 두 번째 인자의 유동 공식을 직관적으로 계산하기가 쉽지 않습니다. 특정 화면 너비에서 얼마로 렌더링되는지 머릿속으로 바로 나오지 않습니다. 이 부분은 Fluid Type Scale Calculator 같은 도구를 함께 쓰는 게 현실적인 방법입니다.
여기서 한 가지 더 말하고 싶은 게 있습니다. 계산 도구에 지나치게 의존하면 실제로 어떤 값이 왜 그렇게 나오는지 이해하지 못한 채 숫자만 복사해오는 상황이 생깁니다. 저도 처음엔 그랬는데, 시간이 지날수록 "이 유동값이 어떤 범위에서 어떤 속도로 커지는가"를 눈으로 가늠할 수 있어야 수정 작업이 훨씬 빨라진다는 걸 느꼈습니다. 도구는 학습을 돕는 용도로 써야지, 이해를 우회하는 용도로 쓰면 안 됩니다.
"clamp()를 쓰면 미디어쿼리가 필요 없다"는 말을 가끔 보는데, 이건 폰트 크기에 한정된 이야기입니다. 레이아웃이 바뀌거나 이미지 처리가 필요한 구간에서는 미디어쿼리가 여전히 제 역할을 합니다. 이 부분을 오해하고 전체 반응형 설계에서 미디어쿼리를 걷어내려 하면 예상 못 한 곳에서 레이아웃이 무너집니다. clamp()는 미디어쿼리와 경쟁하는 도구가 아니라 보완하는 도구입니다. 이 구분을 처음부터 명확히 해두는 게 중요합니다.
clamp()를 한 번 쓰기 시작하면 폰트 관련 미디어쿼리가 눈에 띄게 줄어드는 건 사실입니다. 저는 그것만으로도 충분히 가치 있는 전환이었다고 봅니다. 처음 도입하신다면 Fluid Type Scale Calculator로 먼저 값의 범위를 시각적으로 확인해본 뒤, 요소별로 하나씩 적용해보는 방식을 권합니다. 브라우저 창을 직접 드래그하면서 폰트가 자연스럽게 따라오는 걸 확인하는 순간, 다시 미디어쿼리로 돌아가고 싶은 마음은 사라질 겁니다.
참고:
MDN Web Docs — clamp()
Fluid Type Scale Calculator
web.dev — Fluid typography
CSS-Tricks — Fluid Typography