유연 상자로 한 줄 배치를 자유자재로 다루게 되자, 나는 이제 뭐든 할 수 있다고 자신했다. 그런데 사진첩처럼 행과 열이 반듯하게 맞아떨어지는 격자를 짜려니 유연 상자로는 자꾸 어긋났다. 줄바꿈은 되는데 세로줄이 맞지 않았다. 그때 알았다. 행과 열을 동시에 다루는 도구는 따로 있다는 것을. 그게 그리드였다.


이번 편은 그리드의 첫걸음으로, 행과 열을 정의하는 법을 다룬다. 격자의 틀을 만드는 템플릿 속성, 공간을 비율로 나누는 단위, 반복을 간결하게 표현하는 함수, 그리고 미디어 쿼리 없이 반응형 격자를 만드는 마법 같은 한 줄까지 살펴본다. 이차원 배치의 기초를 여기서 세운다.

1. 그리드는 이차원 배치 도구다

그리드는 유연 상자와 달리 행과 열을 동시에 다루는 이차원 배치 방식이다. 부모에 그리드 배치 값을 주면, 그 안에 눈에 보이지 않는 격자가 생기고 자식들이 그 칸에 배치된다. 유연 상자가 한 방향의 흐름이라면, 그리드는 가로세로가 맞물린 표와 같다. 이 차이가 두 도구의 쓰임을 가른다.


그리드의 가장 큰 장점은 세로줄과 가로줄이 정확히 맞아떨어진다는 것이다. 유연 상자로 여러 줄을 만들면 각 줄의 아이템 폭이 제각각이 되기 쉽지만, 그리드는 열의 폭이 줄에 상관없이 일정하다. 사진첩, 대시보드, 카드 격자처럼 반듯한 정렬이 필요한 곳에서 그리드는 압도적으로 유리하다.


그리드를 이해하는 핵심 개념은 트랙과 선이다. 트랙은 행 하나 또는 열 하나를 가리키고, 선은 그 트랙들을 나누는 경계다. 아이템은 이 선들 사이에 놓인다. 나는 처음에 칸으로만 생각했는데, 선을 기준으로 이해하기 시작하자 아이템을 원하는 위치에 놓는 일이 훨씬 명확해졌다.


그리드는 부모에서 격자의 틀을 정의하고, 자식을 그 틀에 배치하는 두 단계로 나뉜다. 이번 편은 첫 단계, 즉 행과 열의 틀을 정의하는 데 집중한다. 자식을 특정 영역에 배치하는 두 번째 단계는 다음 편에서 다룬다. 우선 격자 자체를 그리는 법을 익히는 것이 순서다.


그리드와 유연 상자는 경쟁 관계가 아니라 역할 분담이다. 나는 페이지의 큰 골격은 그리드로 짜고, 그 안 컴포넌트의 한 줄 배치는 유연 상자로 처리한다. 둘을 겹쳐 쓰는 것도 흔하다. 그리드 칸 안에 유연 상자를 두는 식이다. 이차원과 일차원 도구를 상황에 맞게 조합하는 것이 실무의 감각이다.


그리드는 한때 최신 기술이었지만 이제는 모든 주요 브라우저가 안정적으로 지원한다. 그래서 나는 격자 배치가 필요하면 주저 없이 그리드를 쓴다. 예전에 표나 띄우기로 억지로 짜던 격자들을 이제 그리드가 깔끔하게 대체한다. 이차원 배치에 관한 한 그리드가 정석이다.

2. 행과 열의 틀 정의하기

그리드의 틀은 열 템플릿과 행 템플릿 속성으로 정의한다. 열 템플릿에 값을 나열하면 그 개수만큼 열이 생기고, 각 값이 그 열의 폭이 된다. 행도 마찬가지다. 예를 들어 세 개의 값을 나열하면 세 개의 열이 만들어진다. 나는 이 방식으로 격자의 뼈대를 한눈에 그린다.


열의 폭은 고정값으로도, 유연한 값으로도 지정할 수 있다. 픽셀 같은 절대값을 주면 그 폭이 고정되고, 비율 단위를 주면 남는 공간을 나눠 갖는다. 나는 사이드바처럼 폭이 고정되어야 하는 열엔 절대값을, 본문처럼 늘어나야 하는 열엔 비율 단위를 섞어 쓴다. 고정과 유연을 한 격자에 함께 담을 수 있다.


행은 열과 조금 다르게 동작한다. 명시적으로 행 템플릿을 정하지 않아도, 아이템이 늘어나면 행이 자동으로 생긴다. 그래서 열만 정의하고 행은 콘텐츠에 맡기는 경우가 많다. 나는 카드 격자를 만들 때 열 개수만 정하고, 카드가 몇 줄이 될지는 그리드의 자동 생성에 맡긴다.


이 자동으로 생기는 행의 크기는 별도 속성으로 조절할 수 있다. 자동 행에 최소 높이를 주거나 내용에 맞추게 하는 식이다. 나는 카드 높이를 내용에 맞추되 최소 높이를 보장하고 싶을 때 이 자동 행 속성을 조절한다. 명시적 틀과 자동 생성을 함께 다루는 것이 그리드 활용의 요령이다.


행과 열 사이의 간격은 전용 간격 속성으로 준다. 유연 상자와 마찬가지로 이 속성 하나면 트랙들 사이에 균일한 거리가 생긴다. 가로 간격과 세로 간격을 따로 줄 수도, 한 번에 줄 수도 있다. 나는 격자를 짤 때 여백 대신 이 간격 속성으로 칸 사이 거리를 만든다. 훨씬 깔끔하다.


틀을 정의할 때 나는 항상 이 격자가 어떤 모양이어야 하는지를 먼저 그린다. 몇 개의 열이 필요한지, 각 열이 고정인지 유연인지, 행은 명시할지 자동에 맡길지를 정한다. 이 밑그림이 명확하면 템플릿 속성에 그대로 옮기기만 하면 된다. 격자의 설계는 코드보다 그림에서 시작된다.

3. 비율 단위와 반복 함수

그리드에는 격자 전용의 유연한 단위가 있다. 이 비율 단위는 남는 공간을 비율대로 나눈다. 두 열에 각각 하나의 비율 값을 주면 공간을 절반씩, 한 열에 두 배 값을 주면 그 열이 두 배를 차지한다. 나는 이 단위 덕에 복잡한 계산 없이 열의 비율을 직관적으로 정할 수 있게 되었다.


이 비율 단위는 고정값과 섞어 쓸 때 특히 강력하다. 한 열은 고정 폭을 주고 다른 열에 비율 값을 주면, 고정 열이 자리를 확보한 뒤 남는 공간을 비율 열이 전부 차지한다. 사이드바와 본문의 조합이 이 방식으로 아주 간결하게 표현된다. 나는 이 조합을 페이지 레이아웃의 기본으로 자주 쓴다.


같은 값을 여러 번 반복해야 할 때는 반복 함수를 쓴다. 열 세 개에 같은 비율 값을 주려면, 값을 세 번 쓰는 대신 반복 함수로 개수와 값을 한 번에 지정한다. 열이 열두 개쯤 되면 이 함수의 편리함이 확연해진다. 나는 반복되는 트랙은 거의 항상 이 함수로 표현한다.


트랙 크기에 최소와 최대를 함께 지정하는 함수도 있다. 이 함수는 트랙이 최소값보다 작아지지 않고 최대값보다 커지지 않게 한다. 예를 들어 최소는 일정 폭, 최대는 비율 단위로 주면, 좁을 땐 최소 폭을 지키고 넓을 땐 공간을 채운다. 이 함수가 다음 절에서 볼 반응형 격자의 핵심 재료다.


반복 함수와 최소최대 함수는 함께 쓸 때 진가를 발휘한다. 반복 안에 최소최대를 넣으면, 각 트랙이 유연하게 크기를 조절하면서도 정해진 범위를 벗어나지 않는다. 나는 이 조합으로 다양한 화면에서 무너지지 않는 격자를 만든다. 두 함수의 결합이 그리드를 진짜 반응형 도구로 만든다.


이 단위와 함수들을 익히면 격자를 표현하는 코드가 놀랄 만큼 짧아진다. 예전에 여러 줄이 필요했던 복잡한 배치가 한두 줄로 정리된다. 나는 그리드의 이 간결함에 반해, 이차원 배치라면 거의 반사적으로 그리드를 떠올린다. 짧은 코드로 강력한 배치를 얻는 것이 그리드의 매력이다.

4. 미디어 쿼리 없는 반응형 격자

그리드에는 반응형 격자를 미디어 쿼리 없이 만드는 유명한 한 줄이 있다. 반복 함수에 특별한 키워드와 최소최대 함수를 조합한 것이다. 이 한 줄은 컨테이너 폭에 맞춰 열의 개수를 스스로 조절한다. 넓으면 열이 많아지고, 좁으면 열이 줄어든다. 나는 이 한 줄을 처음 봤을 때 감탄을 금치 못했다.


이 마법의 원리는 이렇다. 각 열에 최소 폭을 정해두고, 컨테이너 안에 그 최소 폭짜리 열이 몇 개나 들어가는지를 브라우저가 계산한다. 그리고 들어갈 수 있는 만큼 열을 만들고, 남는 공간은 비율 단위로 열들이 나눠 갖는다. 화면이 넓어지면 열이 하나 더 들어가고, 좁아지면 하나 빠진다.


이 방식에는 두 가지 채움 키워드가 있고, 둘의 차이를 아는 게 중요하다. 하나는 빈 트랙을 접어 아이템을 늘리고, 다른 하나는 빈 트랙을 그대로 유지한다. 아이템이 몇 개 없을 때 이 차이가 드러난다. 나는 아이템이 공간을 채우길 원하면 접는 쪽을, 자리를 유지하길 원하면 남기는 쪽을 고른다.


이 반응형 격자는 카드 목록에 최적이다. 상품 목록, 사진첩, 글 목록처럼 같은 모양의 카드가 여럿 반복될 때, 이 한 줄이면 모든 화면 크기에 대응한다. 나는 이런 목록을 만들 때 미디어 쿼리를 거의 쓰지 않는다. 그리드의 이 한 줄이 브레이크포인트마다 열 개수를 바꾸던 수고를 통째로 없앤다.


다만 이 방식에도 함정이 있다. 좁은 화면에서 최소 폭이 화면보다 크면 아이템이 넘쳐 가로 스크롤이 생긴다. 나는 이걸 막기 위해 최소 폭에 화면 폭과 비교하는 처리를 더한다. 그러면 아주 좁은 화면에서도 열이 컨테이너를 뚫지 않는다. 이 작은 보완이 반응형 격자를 완성한다.


이 반응형 한 줄은 그리드가 왜 판을 바꿨는지를 가장 잘 보여주는 예다. 미디어 쿼리로 브레이크포인트마다 열을 지정하던 방식과 비교하면, 코드는 짧아지고 대응 범위는 넓어졌다. 나는 이 한 줄을 그리드에서 가장 자주 쓰는 패턴으로 꼽는다. 알아두면 반응형 작업이 훨씬 가벼워진다.

5. 그리드로 페이지 골격 잡기

나는 페이지의 큰 골격을 그리드로 짜는 것을 기본으로 삼는다. 헤더와 본문과 푸터, 혹은 사이드바를 낀 구조를 그리드의 행과 열로 정의하면 레이아웃의 뼈대가 한눈에 잡힌다. 유연 상자로 이런 이차원 골격을 짜려면 여러 감싸개가 필요하지만, 그리드는 한 컨테이너로 끝난다.


골격을 짤 때 나는 먼저 행과 열의 큰 그림을 정한다. 위에 헤더, 가운데에 본문과 곁가지, 아래에 푸터라면, 세 개의 행과 필요한 열을 정의한다. 헤더와 푸터는 폭 전체를 차지하고, 가운데만 열로 나뉜다. 이런 구조가 템플릿 속성으로 명료하게 표현된다.


화면 크기에 따라 골격 자체를 바꿔야 할 때는 미디어 쿼리로 템플릿을 다시 정의한다. 넓은 화면에서는 사이드바와 본문을 두 열로, 좁은 화면에서는 한 열로 쌓는 식이다. 컨테이너의 템플릿만 바꾸면 자식들이 알아서 재배치되므로, 골격 전환이 아주 깔끔하다. 나는 이 방식으로 반응형 레이아웃을 관리한다.


그리드 컨테이너 안의 각 칸에는 다시 다른 배치 도구를 넣을 수 있다. 본문 칸 안에 카드 격자를, 헤더 칸 안에 유연 상자 툴바를 두는 식이다. 큰 틀은 그리드가 잡고, 세부는 각 도구가 맡는 이 계층 구조가 규모 있는 레이아웃을 관리 가능하게 만든다. 나는 이 중첩 구조를 즐겨 활용한다.


골격을 그리드로 짜면 최소 높이를 화면 전체로 주는 기법과도 잘 어울린다. 컨테이너에 화면 높이를 최소로 걸고 본문 행을 유연하게 두면, 내용이 짧아도 푸터가 화면 바닥에 붙는다. 나는 이 조합으로 콘텐츠가 적은 페이지에서도 레이아웃이 허전해지지 않게 만든다. 그리드가 이런 세로 방향 제어에도 강하다.


정리하면 그리드는 행과 열을 동시에 다루는 이차원 배치 도구다. 템플릿 속성으로 틀을 정의하고, 비율 단위와 반복 함수로 간결하게 표현하며, 특별한 한 줄로 미디어 쿼리 없는 반응형 격자를 얻는다. 페이지 골격을 잡는 데도 제격이다. 다음 편에서는 이 격자 안에 아이템을 원하는 영역에 배치하는 법을 다룬다.