타이포그래피를 다루면서 글자 크기를 정할 때마다 나는 어떤 단위를 써야 할지 매번 망설였다. 처음에는 무조건 픽셀로 박아 넣었는데, 사용자가 브라우저 글자 크기를 키워도 내 사이트만 꿈쩍 안 하는 걸 보고 뭔가 잘못됐다는 걸 알았다. 크기를 어떤 단위로 적느냐는 단순한 취향이 아니라, 화면이 얼마나 유연하게 반응하느냐를 결정하는 문제였다.


이번 편은 CSS에서 길이를 표현하는 단위들을 정리한다. 고정된 절대 단위와 무언가에 비례하는 상대 단위의 차이, 부모를 기준으로 하는 단위와 루트를 기준으로 하는 단위의 구분, 화면 크기에 반응하는 뷰포트 단위, 그리고 값의 범위를 유연하게 묶어주는 함수까지 다룬다. 이 단위들을 손에 쥐면 화면이 어떤 크기에서도 무너지지 않게 짤 수 있다.

1. 절대 단위와 상대 단위의 갈림길

CSS의 단위는 크게 절대 단위와 상대 단위로 나뉜다. 절대 단위는 어떤 상황에서도 같은 크기를 유지하는 고정값이고, 상대 단위는 다른 무언가를 기준으로 계산되는 비례값이다. 이 둘의 성격을 이해하는 것이 단위 선택의 출발점이다. 나는 오랫동안 이 구분을 모르고 그냥 눈에 익은 단위를 반복해서 쓰다가, 반응형을 만들면서 비로소 차이의 무게를 실감했다.


가장 흔히 쓰는 절대 단위는 픽셀이다. 픽셀은 값을 적은 그대로 화면에 고정되므로 예측이 쉽고 직관적이다. 테두리 두께나 아주 세밀한 간격처럼 정확히 그 크기여야 하는 곳에는 픽셀이 제격이다. 나는 얇은 구분선이나 그림자의 미세한 값처럼, 화면 크기에 따라 변할 이유가 없는 값에는 지금도 픽셀을 쓴다.


문제는 글자 크기다. 글자 크기를 픽셀로 못 박으면, 사용자가 브라우저 설정에서 기본 글자 크기를 키워도 그 값이 반영되지 않는다. 시력이 약한 사용자가 글자를 키우려 해도 내 사이트만 요지부동인 것이다. 나는 이 사실을 알고 나서, 글자 크기와 그에 딸린 간격을 픽셀에서 상대 단위로 옮기는 작업을 했다.


상대 단위는 기준이 되는 값이 변하면 함께 변한다. 그래서 한 곳의 기준값만 조절해도 그에 비례하는 모든 값이 따라 움직인다. 이 연쇄적인 반응이 상대 단위의 힘이다. 나는 상대 단위를 제대로 쓰기 시작하면서, 화면 전체의 크기감을 값 하나로 조율하는 감각을 익혔다. 절대 단위로는 얻을 수 없는 유연함이었다.


다만 상대 단위는 기준이 무엇이냐에 따라 결과가 달라지므로, 그 기준을 정확히 알아야 한다. 기준을 착각하면 예상과 다른 크기가 나와 당황하게 된다. 나도 처음에는 어떤 단위가 무엇을 기준으로 삼는지 헷갈려 크기가 배로 커지거나 절반으로 줄어드는 일을 겪었다. 단위마다 기준이 다르다는 점을 몸에 익히는 것이 먼저다.


정리하면 절대 단위는 고정이 필요한 세밀한 값에, 상대 단위는 유연함이 필요한 크기와 간격에 쓴다. 이 원칙 하나만 세워도 단위 선택의 절반은 해결된다. 나는 이제 값을 적을 때마다 이건 고정되어야 하는가 아니면 반응해야 하는가를 먼저 자문한다. 그 답이 어떤 단위를 쓸지를 자연스럽게 알려준다.

2. em과 rem, 기준의 차이

대표적인 상대 단위 두 가지가 emrem이다. 이름은 비슷하지만 기준이 완전히 다르다. em은 그 요소에 적용된 글자 크기를 기준으로 하고, rem은 문서의 최상위 요소, 즉 루트의 글자 크기를 기준으로 한다. 이 한 글자 차이가 실무에서 만드는 결과의 차이는 생각보다 크다.


em은 자기 자신 혹은 부모의 글자 크기를 따르므로 맥락에 따라 값이 달라진다. 부모의 글자가 크면 같은 em 값도 커지고, 부모가 작으면 작아진다. 나는 아이콘과 글자를 함께 배치할 때 아이콘 크기를 em으로 주는 걸 좋아한다. 글자 크기가 바뀌면 아이콘도 비례해서 따라오니, 둘의 균형이 자동으로 유지되기 때문이다.


하지만 em에는 함정이 있다. 중첩된 요소에서 em을 연달아 쓰면 값이 곱해지며 눈덩이처럼 불어난다. 목록 안에 목록이 들어가고 각 단계마다 글자 크기를 em으로 키우면, 깊이 들어갈수록 글자가 걷잡을 수 없이 커진다. 나는 이 누적 효과를 모르고 중첩 목록의 글자가 거대해지는 걸 보고 한참을 헤맸다.


rem은 이 누적 문제를 깔끔하게 피한다. 언제나 루트의 글자 크기만 바라보므로, 아무리 깊이 중첩되어도 기준이 흔들리지 않는다. 그래서 나는 요소의 크기나 간격을 정할 때 대부분 rem을 기본으로 삼는다. 예측이 쉽고, 루트 값 하나만 바꾸면 사이트 전체의 크기감이 일관되게 조정되기 때문이다.


루트 글자 크기는 대개 브라우저 기본값을 존중하는 것이 좋다. 사용자가 접근성을 위해 기본 글자 크기를 조정했다면, rem은 그 설정을 그대로 반영해 크기를 키운다. 나는 루트에 픽셀로 고정값을 박아 이 연결을 끊어버리는 실수를 했다가, 사용자 설정을 무시하는 사이트가 된 걸 알고 되돌렸다. rem의 접근성 이점은 이 연결에서 나온다.


실무 감각으로 정리하면, 크기와 간격의 뼈대는 rem으로 잡아 일관성과 접근성을 확보하고, 부모에 비례해야 자연스러운 국소적인 값에만 em을 쓴다. 나는 이 역할 분담을 정한 뒤로 단위 때문에 크기가 꼬이는 일이 거의 사라졌다. 두 단위의 기준 차이만 확실히 알면 각자를 제자리에 배치할 수 있다.

3. 뷰포트 단위와 모바일의 함정

화면 자체의 크기에 반응해야 할 때는 뷰포트 단위를 쓴다. 뷰포트 너비의 백분율을 뜻하는 단위와 높이의 백분율을 뜻하는 단위가 있어서, 화면이 넓어지거나 좁아지면 값도 그에 맞춰 늘고 준다. 나는 화면 전체를 덮는 영역이나, 화면 폭에 비례해 커져야 하는 큰 제목에 이 단위를 즐겨 쓴다. 창 크기에 딱 붙어 반응하는 감각이 좋다.


뷰포트 너비 단위는 창의 가로 크기를 백 등분한 값이다. 이걸 쓰면 화면이 넓을 때 자동으로 커지고 좁을 때 작아지는 요소를 만들 수 있다. 다만 이 단위만으로 글자 크기를 정하면 아주 좁은 화면에서 글자가 지나치게 작아지거나, 넓은 화면에서 터무니없이 커지는 문제가 생긴다. 나는 뷰포트 단위를 단독으로 쓰기보다 뒤에서 다룰 함수와 묶어 범위를 제한한다.


뷰포트 높이 단위에는 오랫동안 악명 높은 함정이 있었다. 모바일 브라우저는 주소창이 나타났다 사라졌다 하면서 화면의 실제 높이가 계속 변한다. 그런데 높이 단위가 이 변화를 제대로 따라가지 못해, 화면을 꽉 채우려던 영역이 주소창에 가려지거나 넘쳐버리는 일이 잦았다. 나도 모바일에서 첫 화면이 어긋나는 걸 보고 원인을 찾느라 애를 먹었다.


이 문제를 풀기 위해 새로운 높이 단위들이 표준에 들어왔다. 주소창이 보일 때의 작은 높이, 주소창이 숨었을 때의 큰 높이, 그리고 상황에 따라 동적으로 변하는 높이를 각각 가리키는 단위가 생긴 것이다. 나는 화면을 꽉 채우는 영역에는 동적으로 반응하는 높이 단위를 써서, 주소창이 어떻게 움직이든 영역이 자연스럽게 맞물리게 한다.


동적 높이 단위는 주소창의 등장과 퇴장에 맞춰 값이 실시간으로 조정된다. 덕분에 모바일에서 첫 화면을 정확히 한 화면으로 채우는 오랜 숙제가 비교적 쉽게 풀렸다. 나는 이 단위를 알기 전까지 자바스크립트로 실제 높이를 재서 값을 채워 넣는 번거로운 방법을 썼는데, 이제는 단위 하나로 해결한다. 표준이 실무의 고통을 덜어준 좋은 사례다.


뷰포트 단위는 강력하지만 남용하면 통제하기 어려워진다. 모든 크기를 화면에 비례시키면, 특정 화면에서는 멋지지만 다른 화면에서는 균형이 무너진다. 나는 뷰포트 단위를 화면 전체를 다루는 큰 구조나 배경에 한정하고, 세부 요소의 크기는 rem으로 안정시킨다. 화면에 반응하는 것과 안정적으로 고정하는 것 사이의 균형이 핵심이다.

4. 퍼센트와 ch, 그리고 접근성

백분율 단위는 기준이 무엇이냐에 따라 의미가 미묘하게 달라지는 까다로운 단위다. 너비의 백분율은 부모의 너비를, 높이의 백분율은 부모의 높이를 기준으로 한다. 그런데 여백이나 안쪽 여백에 준 백분율은 세로 방향이라도 부모의 너비를 기준으로 삼는다는 예외가 있다. 나는 이 예외를 모르고 세로 여백을 백분율로 줬다가 예상 밖의 값이 나와 당황했다.


백분율은 부모에 완전히 종속되므로, 부모의 크기가 명확할 때 가장 잘 작동한다. 부모의 크기가 내용에 따라 유동적이면 백분율의 결과도 예측하기 어려워진다. 나는 그리드나 플렉스 같은 레이아웃 도구가 등장한 뒤로 너비를 백분율로 직접 나누는 일이 줄었다. 그런 도구들이 비율 분배를 더 안전하게 처리해 주기 때문이다.


글자 폭을 기준으로 하는 단위도 있다. 이 단위는 특정 문자의 너비를 기준으로 삼아, 한 줄에 들어갈 글자 수를 겨냥할 수 있게 해준다. 나는 본문 영역의 최대 폭을 이 단위로 지정한다. 한 줄에 담기는 글자 수를 적당한 범위로 제한하면 읽기가 눈에 띄게 편해지기 때문이다. 화면이 아무리 넓어도 본문은 읽기 좋은 폭을 유지한다.


글자 폭 단위는 정확히 그 글자 수를 보장하지는 않는다. 글꼴마다 글자의 실제 너비가 다르고, 특히 한글은 기준 문자와 폭이 다르기 때문이다. 그래서 나는 이 단위를 대략적인 가늠자로 쓴다. 정확한 글자 수를 세려는 게 아니라, 본문 폭이 지나치게 넓어지지 않게 상한을 두는 용도로 접근하면 실망할 일이 없다.


단위 선택은 접근성과 직결된다. 글자 크기를 상대 단위로 두면 사용자의 확대 설정을 존중하지만, 절대 단위로 못 박으면 그 설정을 무시한다. 나는 접근성을 진지하게 여기기 시작하면서, 글자와 그에 연동되는 간격을 모두 상대 단위로 옮겼다. 화면을 확대해도 배치가 무너지지 않고 함께 커지는 사이트가 훨씬 배려 깊게 느껴진다.


결국 어떤 단위든 사용자가 크기를 조정할 자유를 빼앗지 않는 방향이 옳다. 나는 디자인의 정밀함과 사용자의 통제권 사이에서 갈등할 때, 통제권 쪽에 무게를 둔다. 몇 픽셀의 정확함보다 누구나 편히 읽을 수 있는 유연함이 더 값지다. 단위는 이 가치 판단이 코드로 드러나는 지점이다.

5. calc와 clamp로 유연하게 묶기

단위 하나만으로 부족할 때는 값을 계산하는 함수를 쓴다. 서로 다른 단위를 섞어 계산하게 해주는 함수 덕분에, 전체 너비에서 고정된 여백을 뺀 나머지처럼 이전에는 까다로웠던 표현을 한 줄로 적을 수 있다. 나는 이 계산 함수를 알고 나서 레이아웃을 짜는 방식이 한결 자유로워졌다. 상대값과 절대값을 한 식 안에서 섞을 수 있다는 게 큰 무기였다.


이 함수의 진짜 가치는 이질적인 단위를 함께 쓸 수 있다는 데 있다. 백분율에서 고정 픽셀을 빼거나, 뷰포트 단위에 rem을 더하는 식의 계산이 가능하다. 나는 사이드바의 고정 폭을 제외한 나머지를 본문에 주고 싶을 때 이 방식을 쓴다. 유동적인 값과 고정된 값을 한데 묶어야 하는 상황에서 특히 요긴하다.


값의 범위를 제한하는 함수도 있다. 이 함수는 최솟값, 선호값, 최댓값 세 가지를 받아, 선호값이 범위를 벗어나지 않도록 붙잡아 준다. 나는 앞서 뷰포트 단위로 글자 크기를 정할 때 생기던 문제를 이 함수로 해결한다. 화면에 비례해 글자가 커지되, 너무 작아지지도 너무 커지지도 않게 아래위로 울타리를 치는 것이다.


이 범위 제한 함수는 반응형 타이포그래피의 핵심 도구가 되었다. 예전에는 화면 크기 구간마다 글자 크기를 따로 지정해야 했는데, 이제는 함수 하나로 최소와 최대 사이를 부드럽게 오가게 만들 수 있다. 나는 큰 제목의 크기를 이 함수로 정해서, 좁은 화면부터 넓은 화면까지 구간 설정 없이 매끄럽게 반응하도록 한다. 코드가 줄고 결과는 더 자연스럽다.


최솟값만 고르거나 최댓값만 고르는 함수도 따로 있다. 여러 값 중 가장 작은 것이나 가장 큰 것을 택하게 해서, 상한이나 하한만 걸고 싶을 때 쓴다. 나는 요소의 너비를 정할 때 화면 폭과 고정 상한 중 작은 쪽을 택하게 해서, 좁은 화면에서는 화면에 꽉 차고 넓은 화면에서는 일정 폭을 넘지 않게 한다. 조건을 값 하나로 표현하는 깔끔한 방법이다.


정리하면 단위는 고정과 유연 사이에서 고르는 선택이고, 여러 단위를 계산 함수로 묶으면 표현력이 폭발적으로 늘어난다. 나는 상대 단위로 뼈대를 세우고, 계산과 범위 제한 함수로 화면 크기에 매끄럽게 반응시키는 방식을 기본으로 삼는다. 이 감각이 있으면 어떤 화면에서도 무너지지 않는 유연한 값을 짤 수 있다. 다음 편에서는 이 유연함을 화면 크기별로 조율하는 반응형의 기초, 미디어 쿼리를 다룬다.