그리드로 격자의 틀을 그리는 법을 익히자, 나는 자연스럽게 다음 벽에 부딪혔다. 아이템을 그 격자 안 원하는 자리에 놓고, 어떤 아이템은 두 칸을 차지하게 만들고 싶었다. 자동으로 채워지는 순서에만 맡길 수는 없었다. 아이템을 격자 위에 의도대로 배치하는 법, 그것이 그리드의 진짜 힘이 발휘되는 지점이었다.
이번 편은 그리드 아이템을 격자에 배치하는 법을 다룬다. 선 번호로 위치를 지정하는 법, 여러 칸에 걸치게 하는 법, 그리고 영역에 이름을 붙여 시각적으로 배치하는 방식까지 살펴본다. 여기에 아이템을 칸 안에서 정렬하는 법과 최신 표준인 서브그리드까지 더해 이차원 배치를 완성한다.
1. 선 번호로 아이템 배치하기
그리드의 배치는 선을 기준으로 이루어진다. 격자에는 열과 행을 나누는 선들이 있고, 각 선에는 번호가 매겨진다. 아이템에 어느 선에서 시작해 어느 선에서 끝날지를 지정하면, 그 아이템은 두 선 사이의 영역을 차지한다. 나는 선 번호를 이해하고 나서 아이템을 정확한 자리에 놓을 수 있게 되었다.
선 번호는 왼쪽 위에서 시작해 하나씩 늘어난다. 열이 세 개면 세로 선은 네 개가 되고, 각각 번호를 갖는다. 아이템에 시작 선과 끝 선을 지정하면 그 사이가 아이템의 자리다. 첫 번째 선에서 두 번째 선까지면 첫 칸, 첫 번째 선에서 세 번째 선까지면 두 칸을 차지하는 식이다.
이 선 번호 방식의 장점은 아이템을 여러 칸에 걸치게 하기 쉽다는 것이다. 시작 선과 끝 선의 간격을 넓히면 아이템이 그만큼 넓어진다. 나는 대시보드에서 특정 위젯만 두 칸을 차지하게 만들 때 이 방식을 쓴다. 자동 배치로는 어려운 이런 불규칙한 배치가 선 번호로는 간단하다.
몇 칸을 걸칠지를 직접 지정하는 편리한 표현도 있다. 끝 선을 일일이 계산하는 대신, 시작점에서 몇 칸을 차지할지를 명시하는 것이다. 나는 아이템이 두 칸을 걸치게 하고 싶으면 시작 선과 걸침 개수를 함께 준다. 끝 선 번호를 매번 세지 않아도 되어 편하다.
선 번호는 끝에서부터 음수로도 셀 수 있다. 마지막 선을 특정 번호로 두면, 열 개수가 바뀌어도 항상 끝에 붙는다. 나는 아이템을 격자의 끝까지 늘리고 싶을 때 이 음수 번호를 쓴다. 격자 크기가 유동적이어도 안정적으로 동작한다는 점이 유용하다.
선 번호 배치는 정밀하지만, 번호를 일일이 세야 해서 복잡한 레이아웃에서는 헷갈리기 쉽다. 그래서 나는 불규칙한 몇몇 아이템에만 선 번호를 쓰고, 규칙적인 배치는 자동 흐름에 맡긴다. 정밀한 제어가 필요한 곳에만 선 번호를 동원하는 것이 관리하기 편하다.
2. 영역 이름으로 배치하기
선 번호보다 훨씬 직관적인 배치 방법이 영역 이름이다. 컨테이너에 격자의 생김새를 문자로 그리듯 정의하고, 각 영역에 이름을 붙인다. 그다음 아이템에 그 이름을 지정하면 해당 영역에 배치된다. 나는 이 방식을 처음 봤을 때, 코드가 곧 레이아웃의 그림이 된다는 점에 감탄했다.
영역 정의는 마치 아스키 그림처럼 보인다. 각 줄이 격자의 한 행이 되고, 줄 안의 이름들이 각 칸을 나타낸다. 헤더가 위 전체를 차지하고 아래에 사이드바와 본문이 나뉜다면, 그 구조가 문자 배열로 한눈에 그려진다. 코드만 봐도 레이아웃이 어떻게 생겼는지 즉시 이해된다.
같은 이름을 여러 칸에 반복하면 그 영역이 여러 칸에 걸쳐진다. 헤더를 여러 열에 걸치게 하려면 그 이름을 반복하면 된다. 이 방식으로 아이템이 여러 칸을 차지하는 복잡한 배치도 문자 그림만으로 표현된다. 나는 헤더와 푸터가 전체 폭을 차지하는 레이아웃을 이 방식으로 아주 쉽게 만든다.
영역 이름 방식의 가장 큰 장점은 반응형 대응이다. 화면 크기에 따라 이 문자 그림만 다시 정의하면 레이아웃이 통째로 바뀐다. 넓은 화면에서는 사이드바와 본문을 나란히, 좁은 화면에서는 위아래로 쌓는 배치를, 그림 두 개로 표현할 수 있다. 아이템 코드는 건드리지 않고 컨테이너의 그림만 바꾼다.
나는 페이지의 큰 골격을 이 영역 이름 방식으로 짜는 것을 선호한다. 선 번호보다 의도가 명확히 드러나고, 나중에 다른 사람이 봐도 구조를 즉시 파악하기 때문이다. 특히 여러 사람이 함께 작업하는 프로젝트에서 이 가독성은 큰 자산이 된다. 코드가 문서 역할까지 하는 셈이다.
빈 칸을 표현하는 방법도 있어서, 일부러 비워둘 자리를 지정할 수 있다. 이로써 아이템이 없는 여백까지 격자에 포함해 설계할 수 있다. 나는 복잡한 대시보드 레이아웃에서 빈 공간까지 명시해 의도를 분명히 한다. 영역 이름 방식은 이렇게 격자 전체를 그림처럼 설계하게 해준다.
3. 아이템을 칸 안에서 정렬하기
아이템을 격자의 어느 칸에 놓느냐와, 그 칸 안에서 어떻게 정렬하느냐는 다른 문제다. 그리드는 칸 안에서의 정렬도 세밀하게 제어한다. 가로 방향 정렬과 세로 방향 정렬을 각각 지정할 수 있어, 아이템을 칸의 왼쪽 위, 가운데, 오른쪽 아래 등 원하는 위치에 놓을 수 있다.
컨테이너 차원에서 모든 아이템의 정렬을 한 번에 정하는 속성이 있다. 가로 방향과 세로 방향 정렬을 각각 지정하면, 모든 칸의 아이템이 같은 방식으로 정렬된다. 나는 카드 격자에서 모든 카드를 칸 가운데에 놓고 싶을 때 이 컨테이너 정렬을 쓴다. 한 곳에서 전체를 통제하는 것이다.
완벽한 중앙 정렬을 위한 간결한 표현도 있다. 가로세로 정렬을 한 번에 가운데로 두는 단축 속성으로, 이 한 줄이면 아이템이 칸의 정중앙에 놓인다. 나는 무언가를 화면 한복판에 놓아야 할 때, 그리드로 칸 하나를 만들고 이 단축 속성을 주는 방식을 즐겨 쓴다. 가장 짧은 중앙 정렬 코드다.
개별 아이템만 다르게 정렬하고 싶으면 그 아이템에 정렬 속성을 준다. 대부분의 아이템은 컨테이너 규칙을 따르되, 특정 아이템 하나만 칸의 아래나 오른쪽에 붙이는 식이다. 나는 카드 안 버튼을 항상 아래에 붙이고 싶을 때 이 개별 정렬로 예외를 만든다. 전체 규칙과 개별 예외를 함께 다룬다.
기본적으로 아이템은 칸을 가득 채우도록 늘어난다. 그래서 특별한 정렬 지정이 없으면 아이템들이 칸 크기에 맞춰 커진다. 이 자동 채움이 카드 높이를 맞춰주는 편리함을 주지만, 내용만큼만 차지하게 하려면 정렬을 시작점으로 바꿔야 한다. 나는 이 기본 동작을 이해하고 상황에 맞게 조절한다.
칸 안 정렬까지 다루면 그리드의 배치가 완성된다. 아이템을 어느 칸에 놓고, 그 칸 안에서 어디에 정렬할지를 모두 통제할 수 있기 때문이다. 나는 이 두 층위의 배치를 나눠 생각한다. 먼저 큰 자리를 정하고, 그다음 그 안의 미세 위치를 맞춘다. 이 순서가 복잡한 배치도 정리해준다.
4. 서브그리드와 중첩 정렬
그리드를 쓰다 보면 중첩된 격자의 정렬이 어긋나는 문제를 만난다. 카드 격자 안에서 각 카드의 제목, 본문, 버튼의 높이를 카드끼리 맞추고 싶은데, 각 카드가 독립된 격자라 정렬이 따로 논다. 이 오래된 골칫거리를 해결한 것이 서브그리드다. 비교적 최근에 표준으로 자리 잡은 기능이다.
서브그리드는 자식 격자가 부모 격자의 트랙을 물려받게 한다. 즉 카드가 자기만의 행을 만드는 대신, 바깥 격자의 행 선을 그대로 따른다. 그러면 모든 카드의 제목이 같은 선에서 시작하고, 본문과 버튼도 줄이 맞는다. 나는 이 기능을 처음 썼을 때, 그토록 어렵던 카드 정렬이 단번에 해결되어 놀랐다.
서브그리드가 없던 시절에는 이런 정렬을 위해 고정 높이를 억지로 주거나 자바스크립트로 높이를 계산해야 했다. 내용 길이가 다른 카드들의 줄을 맞추는 건 정말 골치 아픈 작업이었다. 서브그리드는 이 모든 편법을 대체한다. 자식이 부모의 트랙을 상속한다는 단순한 원리로 문제를 근본에서 푼다.
서브그리드는 행과 열 각각에 적용할 수 있다. 카드의 세로 정렬만 맞추려면 행에만, 가로까지 맞추려면 둘 다에 적용한다. 나는 대개 카드 목록에서 행 방향 서브그리드를 써서 제목과 본문과 버튼의 높이를 통일한다. 필요한 방향에만 선택적으로 적용하는 것이 유연하다.
서브그리드는 이제 주요 브라우저에서 안정적으로 지원되어 실무에 쓸 만하다. 나는 카드처럼 반복되는 컴포넌트의 내부 정렬이 필요하면 서브그리드를 우선 고려한다. 지원이 걱정되면 간단한 대체 배치를 함께 두기도 하지만, 대부분의 최신 환경에서는 문제없이 동작한다. 정렬 고민을 크게 덜어주는 기능이다.
서브그리드까지 익히면 그리드의 배치 능력이 한 단계 더 올라간다. 격자 안의 격자를 부모와 정렬시켜, 복잡한 컴포넌트도 반듯하게 맞출 수 있기 때문이다. 나는 이 기능이 그리드를 진정한 이차원 배치 도구로 완성했다고 본다. 중첩된 정렬까지 다룰 수 있게 되면 웬만한 레이아웃은 두렵지 않다.
5. 그리드 배치를 실무에서 쓰는 법
실무에서 나는 배치의 규칙성에 따라 방식을 나눈다. 규칙적인 카드 목록은 자동 배치에 맡기고, 큰 골격은 영역 이름으로 짜며, 불규칙한 예외 아이템만 선 번호로 정밀 배치한다. 세 방식을 상황에 맞게 조합하면 대부분의 레이아웃이 깔끔하게 정리된다. 한 방식만 고집할 필요가 없다.
페이지 골격은 영역 이름 방식이 가장 유지보수하기 좋다. 코드가 레이아웃의 그림이 되어 의도가 분명하고, 반응형 전환도 그림만 바꾸면 된다. 나는 헤더, 사이드바, 본문, 푸터로 이루어진 전형적인 레이아웃을 이 방식으로 짜고, 화면 크기별로 그림을 다시 정의한다. 가독성과 유연성을 함께 얻는다.
카드 목록은 앞 편에서 본 반응형 격자 한 줄과 서브그리드를 결합한다. 반응형 격자로 열 개수를 자동 조절하고, 서브그리드로 카드 내부 정렬을 맞춘다. 이 조합이면 미디어 쿼리 없이도 모든 화면에서 반듯한 카드 목록이 나온다. 나는 이 패턴을 목록 화면의 기본 뼈대로 삼는다.
아이템을 정밀하게 놓아야 하는 대시보드 같은 화면에서는 선 번호와 걸침 표현을 쓴다. 특정 위젯이 두 칸이나 두 줄을 차지하는 불규칙한 배치를 명시적으로 지정하는 것이다. 이때는 자동 배치보다 명시적 배치가 의도를 정확히 담는다. 나는 위젯마다 차지할 영역을 계획한 뒤 코드로 옮긴다.
배치가 꼬이면 나는 개발자 도구의 그리드 확인 기능을 켠다. 선 번호와 트랙 경계가 화면에 표시되어, 아이템이 어느 선에서 어느 선까지 걸쳐 있는지 눈으로 확인할 수 있다. 감으로 추측하지 않고 격자를 직접 보며 손대는 이 방식이 배치 문제를 훨씬 빨리 해결해준다.
정리하면 그리드 아이템은 선 번호, 걸침 표현, 영역 이름의 세 방식으로 배치하고, 칸 안에서 다시 정렬하며, 서브그리드로 중첩 정렬까지 맞춘다. 큰 골격은 영역 이름으로, 목록은 반응형 격자와 서브그리드로, 예외는 선 번호로 다룬다. 이제 배치의 뼈대가 완성되었으니, 다음 편에서는 그 위에 입힐 색상과 배경을 다룬다.