반응형의 세 기둥 중 첫째가 유연한 격자라는 걸 알고도, 나는 한동안 칸의 폭을 정확한 픽셀로 못 박는 버릇을 못 버렸다. 숫자로 딱 떨어지게 잡아두면 마음이 놓였기 때문이다. 그런데 화면이 조금만 좁아져도 칸이 삐져나가고 옆으로 밀려나는 걸 겪으면서, 폭은 숫자가 아니라 비율로 잡아야 한다는 걸 몸으로 배웠다.
이번 편은 그 유연한 격자를 만드는 법에 관한 것이다. 비율을 분배하는 fr 단위, 미디어 쿼리 없이 반응형을 완성하는 auto-fit과 minmax, 그리고 좁은 화면에서도 넘치지 않게 지키는 안전장치를 내 경험과 함께 다룬다. 격자가 유연해지면 반응형의 몸통 절반이 세워진다.
1. 고정 폭이 부르는 문제
내가 초반에 자주 한 실수는 세 칸짜리 배치를 만들 때 각 칸의 폭을 특정 픽셀로 고정한 것이다. 넓은 화면에서는 세 칸이 나란히 잘 놓였지만, 화면이 좁아지면 그 폭의 합이 화면을 넘어서면서 마지막 칸이 아래로 떨어지거나 옆으로 삐져나갔다.
고정 폭의 근본 문제는 공간이 줄어도 칸이 줄지 않는다는 데 있다. 화면은 좁아지는데 칸은 자기 폭을 고집하니, 결국 어딘가에서 넘칠 수밖에 없다. 나는 이 넘침을 막으려고 여러 브레이크포인트에서 폭을 일일이 다시 지정했는데, 그럴수록 코드만 불어났다.
이 방식은 사이 간격을 다룰 때 더 골치 아프다. 칸 사이에 간격을 두려면 그 간격까지 폭 계산에 넣어야 하는데, 화면 폭에 따라 이 계산이 계속 어긋난다. 비율로 잡으면 필요 없는 이 복잡한 셈을 나는 고정 폭 시절에 매번 손으로 했다.
그래서 유연한 격자의 첫걸음은 폭을 절대적인 숫자에서 상대적인 비율로 옮기는 것이다. 각 칸이 남은 공간의 몇 분의 몇을 차지할지 정하면, 공간이 줄어드는 만큼 칸도 함께 줄어든다. 화면이 어떤 폭이든 칸들이 그 폭을 나눠 갖는 구조가 된다.
비율로 잡으면 넘침 걱정이 크게 준다. 칸들의 비율 합이 전체 공간을 넘지 않는 한, 화면이 아무리 좁아져도 각 칸이 알아서 작아진다. 나는 이 원리를 이해한 뒤로 세 칸 배치에 브레이크포인트를 거의 걸지 않게 되었다.
물론 무한정 줄어들게 두면 칸이 너무 좁아져 내용이 뭉개진다. 그래서 최소 폭을 함께 정해야 하는데, 이 지점에서 다음에 다룰 도구들이 등장한다. 유연함과 최소 보장을 함께 다루는 것이 격자 설계의 핵심이다.
2. fr 단위로 공간을 나누기
격자에서 비율을 다루는 가장 깔끔한 도구가 fr 단위다. 이 단위는 남은 공간을 몇 등분해서 나눌지를 나타낸다. 세 칸을 똑같이 나누고 싶으면 각 칸에 일 fr씩 주면 되고, 한 칸을 두 배로 넓히고 싶으면 그 칸에 이 fr을 주면 된다.
fr의 큰 장점은 간격 계산이 저절로 맞는다는 점이다. 칸 사이 간격을 gap으로 지정하면, fr은 그 간격을 뺀 나머지 공간을 나눈다. 백분율로 폭을 잡을 때처럼 간격만큼을 손으로 빼는 번거로운 계산이 필요 없다.
나는 이 점 때문에 백분율보다 fr을 선호하게 되었다. 예전에는 세 칸에 간격을 두려고 폭을 삼십 퍼센트쯤으로 어림잡아 넣고 어긋난 여백을 눈으로 맞췄는데, fr로 바꾼 뒤로는 그냥 일 fr씩 주고 gap만 지정하면 끝이었다.
fr은 격자의 행과 열 모두에 쓸 수 있다. 열의 폭 비율뿐 아니라 행의 높이 비율도 이 단위로 나눌 수 있어서, 이차원 배치를 다룰 때 특히 강력하다. 나는 헤더와 본문과 푸터로 이루어진 페이지 골격을 fr로 잡아 세로 비율까지 유연하게 다룬다.
여기에 고정 폭과 비율을 섞을 수도 있다. 사이드바는 이백 픽셀로 고정하고 본문은 일 fr로 두면, 사이드바는 폭을 유지하면서 본문만 남은 공간을 채운다. 나는 이 조합을 자주 쓰는데, 고정이 필요한 부분과 유연해야 할 부분을 한 줄에서 함께 다룰 수 있어 편하다.
결국 fr은 격자에게 남은 공간을 어떻게 나눌지 알려주는 언어다. 절대적인 숫자 대신 상대적인 몫으로 말하니, 화면 폭이 바뀌어도 그 몫의 비율만 지켜진다. 나는 이 단위를 익힌 것이 유연한 격자를 만드는 가장 큰 도약이었다고 느낀다.
3. 미디어 쿼리 없는 반응형 격자
유연한 격자의 백미는 미디어 쿼리를 한 개도 쓰지 않고 반응형 카드 배치를 완성하는 것이다. 그 핵심 도구가 repeat와 auto-fit, 그리고 minmax의 조합이다. 이 한 줄을 처음 봤을 때 나는 그동안 짠 수많은 브레이크포인트가 허무해질 정도로 감탄했다.
원리는 이렇다. 각 칸에 최소 폭과 최대 폭의 범위를 minmax로 주고, 그 칸을 auto-fit으로 화면 폭이 허락하는 만큼 자동으로 채우게 하는 것이다. 화면이 넓으면 칸이 여러 개 들어가고, 좁아지면 들어갈 수 있는 개수만큼만 줄로 접힌다.
예를 들어 각 칸에 이백오십 픽셀에서 일 fr 사이의 범위를 주면, 넓은 화면에서는 여러 칸이 나란히 놓이고 좁은 화면에서는 한두 칸으로 줄어든다. 개수 전환을 위한 미디어 쿼리를 하나도 쓰지 않았는데 반응형이 완성되는 것이다.
여기서 auto-fit과 auto-fill의 차이를 알아두면 좋다. auto-fit은 항목이 적을 때 빈 트랙을 접어서 있는 칸을 넓게 늘리고, auto-fill은 빈 트랙을 그대로 유지해서 빈자리를 남긴다. 나는 대개 칸을 꽉 채우고 싶어서 auto-fit을 쓰지만, 격자 자리를 고정하고 싶을 때는 auto-fill을 택한다.
이 한 줄의 위력은 유지보수에서 드러난다. 카드 개수가 몇 개든, 화면 폭이 얼마든, 이 규칙 하나가 알아서 배치를 정한다. 나는 카드 목록을 다룰 때 거의 항상 이 패턴을 먼저 시도하고, 정말 특별한 배치가 필요할 때만 미디어 쿼리를 더한다.
다만 이 패턴이 만능은 아니다. 각 칸의 내용이 크게 다르거나 정교한 정렬이 필요하면 별도의 손질이 든다. 그래도 대부분의 균일한 카드 나열에는 이 조합이 가장 적은 코드로 가장 견고한 반응형을 만들어준다고 나는 자신 있게 말한다.
4. 좁은 화면에서 넘치지 않게
유연한 격자에도 함정이 있다. 각 칸에 최소 폭을 주었는데 화면이 그 최소 폭보다도 좁아지면, 칸이 화면을 넘어서면서 가로 스크롤이 생긴다. 나는 이백오십 픽셀 최소 폭을 준 카드가 아주 좁은 휴대폰에서 넘치는 걸 발견하고 당황한 적이 있다.
이 문제를 푸는 열쇠가 최소값 자체를 다시 유연하게 만드는 것이다. 최소 폭을 고정 픽셀과 백 퍼센트 중 작은 값으로 지정하면, 화면이 그 픽셀보다 좁을 때는 백 퍼센트가 채택되어 칸이 화면 폭에 맞춰진다. 넘칠 일이 사라지는 것이다.
즉 최소 폭에 min 함수를 겹쳐 쓰는 것이다. 이렇게 하면 넓은 화면에서는 원하는 최소 폭이 지켜지고, 그 최소 폭보다 좁은 초소형 화면에서는 칸이 화면을 넘지 않고 한 줄을 꽉 채운다. 나는 이 작은 안전장치 덕분에 좁은 화면 넘침 걱정을 크게 덜었다.
격자 자식이 안 줄어드는 고전 버그도 알아둘 만하다. 격자나 유연 상자의 자식은 기본적으로 내용보다 작아지지 않으려는 성질이 있어서, 긴 내용이 든 칸이 줄지 않고 넘칠 때가 있다. 나는 이럴 때 그 자식에 최소 폭을 영으로 지정해 문제를 푼다.
이 최소 폭 영은 처음 봤을 때 이해가 안 갔지만, 원리를 알고 나니 명쾌했다. 자식이 내용 폭 아래로 줄어드는 것을 허용해야 부모 격자가 자식을 제 몫만큼 줄일 수 있다. 이 한 줄을 몰라서 넘침의 원인을 엉뚱한 곳에서 찾던 시절이 나에게도 있었다.
이런 안전장치들은 평소에는 보이지 않다가 극단적인 화면에서 진가를 발휘한다. 나는 격자를 짤 때 넓은 화면에서 예쁜 것에 만족하지 않고, 가장 좁은 화면까지 줄여보며 넘침이 없는지 반드시 확인한다. 유연함은 양쪽 끝에서 모두 지켜져야 진짜 유연함이다.
5. 유연 상자와 격자를 나눠 쓰기
격자를 다루다 보면 유연 상자와 격자 중 무엇을 쓸지 고민하게 된다. 나는 이 둘을 경쟁 상대가 아니라 쓰임이 다른 도구로 본다. 대략 유연 상자는 한 방향의 흐름에, 격자는 행과 열이 있는 이차원 배치에 어울린다.
유연 상자는 한 줄로 늘어놓고 남은 공간을 나누거나, 줄바꿈을 허용해 흐르게 하는 데 강하다. 툴바처럼 요소들을 한 줄에 정렬하고 간격을 조절하는 상황이 대표적이다. 나는 방향이 하나로 정해진 배치에는 유연 상자를 먼저 떠올린다.
격자는 행과 열을 동시에 다뤄야 할 때 빛난다. 카드가 여러 줄로 쌓이고 각 줄의 칸이 서로 맞아떨어져야 하는 배치라면, 격자가 훨씬 정돈된 결과를 준다. 나는 카드 목록이나 페이지 골격처럼 이차원 구조에는 격자를 택한다.
둘을 함께 쓰기도 한다. 큰 골격은 격자로 잡고, 그 안의 한 칸 내부는 유연 상자로 요소들을 늘어놓는 식이다. 도구를 하나만 고집할 필요 없이, 각 층위에 맞는 것을 쓰면 코드가 자연스러워진다. 나는 이 조합을 실무에서 가장 많이 쓴다.
여기에 컨테이너 쿼리를 더하면 부품의 유연함이 한 단계 올라간다. 같은 카드가 담긴 칸의 폭에 따라 격자 구조를 바꾸게 하면, 넓은 자리에서는 가로로 좁은 자리에서는 세로로 스스로 배치를 바꾼다. 진짜 재사용 가능한 격자 부품이 되는 것이다.
정리하면 유연한 격자는 폭을 픽셀에서 fr 같은 비율로 옮기는 데서 시작하고, auto-fit과 minmax 조합으로 미디어 쿼리 없는 반응형을 완성하며, min 함수와 최소 폭 영으로 좁은 화면 넘침을 막는다. 유연 상자와 격자를 층위에 맞게 나눠 쓰면 배치가 깔끔해진다. 다음 편에서는 이 격자 위에 놓일 이미지를 화면에 맞게 다루는 법을 살핀다.