CSS 그리드(Grid) - 4. 그리드 영역 구획하기
1. 영역 구획 속성들
시작/끝 위치로 영역 지정
바둑판 모양으로 나누어진 그리드의 셀 영역 1개에는 1개의 자식 요소(태그)가 자리를 차지합니다.
행, 또는 열의 크기를 바꿀 수는 있지만, 모든 행, 또는 열 안에 1개의 요소(태그)가 자리를 차지하는 것에는 변함이 없기 때문에 여러 셀에 걸쳐서 영역을 차지하는 레이아웃을 표현 하기에는 부족합니다.
그리드 속성 중에는 2개 이상의 셀을 하나로 합쳐서 자식 요소(태그)를 위치시킬 수 있는 속성들이 제공됩니다.
시작 위치와 끝나는 위치 경계선(Grid line)을 지정 함으로써 경계선 안에 포함된 셀 들을 하나의 영역으로 합칠 수 있습니다.
여기서 다소 생소한 용어인 "그리드 경계선" 이라는 용어가 나옵니다. 행이나 열과는 다른 것이므로 "경계선" 이라는 용어를 혼동하지 않도록 주의해야 합니다. 밑에서 더 자세히 설명합니다..
그리드 셀을 합치는데 필요한 속성은 "grid-column", "grid-row" 2개입니다.
시작 위치 경계선 값과 끝나는 위치 경계선 속성 값을 "/"로 구분해서 다음과 같이 순서대로 값을 표시합니다.
grid-column: 시작경계선/끝경계선;
grid-row: 시작경계선/끝경계선;
끝나는 경계선 값 은 생략할 수 있습니다. 다음처럼 1개의 경계선 속성 값만 표시하면
grid-column: 2;
grid-row; 1;
2번째 열 경계선에서부터 열 1개만을 선택하는 것이며
grid-column: 2/3;
grid-row: 1/2;
과 동일합니다.
행과 열 범위 속성의 단순화
열 영역 범위 속성은 "grid-column", 행 영역 범위 속성은 "grid-row" 속성으로 범위를 각각 지정해 최종적으로 합칠 영역을 정하는 방법을 더 단순화 해 하나의 속성으로 행과 열의 영역 범위를 정할 수 있습니다.
다음처럼 "grid-area" 속성을 사용해 4개의 경계선을 순서대로 표시합니다.
grid-area: grid-row-start/grid-column-start/grid-row-end/grid-column-end;
예를 들어 다음과 같이 "grid-area" 로 정의한 그리드 영역은
grid-area: 1/1/2/3;
다음과 같이 풀어서 사용할 수도 있습니다.
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 2;
grid-column-end: 3;
4개를 모두 명시해야 하며, 뒤에 오는 값이 생략된 경우 생략된 갯수만큼 "auto" 값으로 채워집니다.
경계선 4개를 모두 명시하지 않은 경우 원하지 않는 영역이 선택될 수 있으므로 4개를 모두 명시해서 사용하는 것을 권장합니다.
그리드 경계선의 이해
행, 또는 열이 아니라 경계선이라고 표현하는 것은 "grid-column", "grid-row" 에서 표시하는 속성 값이 다음과 같이 경계선을 기준으로 시작과 끝 위치를 표현하고, 경계선 안에 표함된 셀들을 합치기 때문입니다.
경계선 숫자의 시작 위치는 항상 왼쪽(열) 위(행)가 되며, 끝나는 위치는 시작 위치를 기준으로 오른쪽(열) 아래(행) 방향의 경계선이 됩니다. 언제나 시작 위치는 1행 1열이며, 값 또한 1에서부터 시작합니다.
영역 구획 속성 값은 음수(-)를 사용할 수도 있습니다.
일반적이지는 않지만, CSS에서 음수를 허용하는 위치 속성 값들이 가지는 특성과 동일하게 첫 행/열 위치를 기준으로 음수 값만큼 왼쪽(열), 또는 위쪽(행) 방향으로 이동한 위치가 끝나는 행/열이 됩니다.
음수 값의 기준 위치는 항상 1행 1열입니다.
다음처럼 끝나는 열이 "-2" 인 경우
grid-column: 1/-2;
1열에서 반대 진행 방향(양수 진행 방향인 오른쪽의 반대인 왼쪽이 됨)으로 2만큼 이동한 경계선이 끝나는 경계선이 됩니다.
1열에서 -1만큼 이동한 경계선이 마지막 경계선이 되고, 마지막 경계선에서 다시 -1만큼 이동한 경계선, 즉, 마지막에서 2번째 경계선이 끝나는 경계선이 됩니다.
속성 값이 음수인 경우 다음과 같이 경계선 위치가 정해집니다.
갯수로 영역 크기 지정
시작과 끝나는 경계선 절대 위치를 지정하는 방식은 행이나 열의 갯수가 많아지면 경계선 위치를 파악하기가 만만치 않아집니다.
이런 문제를 피할 수 있도록 시작 위치에서 부터 합칠 셀 갯수를 표시하는 방식(또는 끝 위치까지 합칠 셀 갯수를 표시)으로 합칠 셀을 선택할 수도 있습니다.
다음과 같이 "시작위치/span 합칠갯수" 형식으로 표현을 하면 "합칠갯수" 만큼 셀을 하나의 영역으로 합치게 됩니다.
grid-column: 시작위치/span 합칠셀갯수;
grid-column: span 합칠셀갯수/끝위치;
"span"은 경계선 위치가 아닌 갯수를 표시하기 위한 특별 키워드로 공백을 사이에 두고 뒤에 갯수를 표시하는 방식으로 표협합니다.
grid-column: 2/span 2;
이 열 구획 속성은 2번째 경계선에서부터 오른쪽으로 2개의 셀을 합친다는 선언이 됩니다.
"합칠갯수"는 위치 속성이 아니기 때문에 음수를 사용할 수 없습니다. 음수를 사용하면 값은 무시되고 1로 적용됩니다.
앞서의 열 구획 속성은
grid-column: 2/4;
와 같은 표현이 됩니다.
"grid-column" 속성은 시작 열과 끝나는 열을 나눠서 속성으로 표현을 할 수 있으며, "grid-column-start", "grid-column-end" 속성으로 표현할 수 있습니다. 기능은 동일하며 "/"로 구분해서 표현하는 시작과 끝 열 위치를 단순히 나누어 표현하는 것입니다.
"grid-row" 속성 또한 "grid-row-start", "grid-row-end" 속성으로 시작 행과 끝나는 행을 나누어 표현할 수 있습니다.
다음의 열 구획 속성은
grid-column: 2/4;
다음과 같이 나누어 표현할 수도 있습니다.
grid-column-start: 2;
grid-column-end: 4;
다만, 실제로 이렇게 나누어 사용하지는 않으므로, 이렇게 표현하는 방법이 있다는 것만 알아두면 됩니다.
2. 영역을 매핑해서 구획하기
영역을 구획하는 속성인 "grid-column" 과 "grid-row" 을 이용하면 원하는 위치에 원하는 컨텐츠(태그)를 배치할 수 있습니다.
다만, 이 방법은 모든 구획 영역에 대해서 시작과 끝나는 경계선을 표시해야 하는 번거로움이 있습니다.
구조가 복잡한 레이아웃인 경우 시작과 끝 위치를 일일이 찾아야 하는 번거로움 뿐만 아니라, 영역을 이동하이나 구획 영역이 차례로 밀릴 경우 인접한 모든 구획 영역의 시작과 끝 위치까지 변경하는 작업을 해야 합니다.
유지보수나 관리 측면에서 아주 번거롭고 불편한 방식이기 때문에 페이지 레이아웃을 만들 때는 영역 구획 속성보다는 영역 매핑(Area mapping)을 하는 방식을 더 많이 사용합니다.
영역 매핑은 그리드 영역 구획 속성의 유지보수 및 관리에 들어가는 번거로움을 줄일 수 있도록 각 영역에 이름을 부여해 이름 기반으로 영역을 관리할 수 있는 방법을 제공합니다.
영역 매핑(Area mapping)은 전용 속성인 "grid-template-areas" 속성과 "grid-area" 속성 2개를 사용해 영역을 이름 단위로 설정하고 관리하게 됩니다.
그리드 영역 매핑해서 사용하는 과정 먼저 영역을 매핑하는 이름을 만들고, 만든 이름으로 영역을 구획하는 2단계로 진행합니다.
그리드 영역 이름 매핑하기
먼저 앞서의 HTML 페이지 구조를 기준으로 각 영역의 이름을 부여해 보겠습니다.
<div class="grid">
<header class="header">헤더</header>
<h1 class="title">타이틀</h1>
<main class="main">컨텐츠영역</main>
<aside class="sidebar">사이드바</aside>
<footer class="footer">푸터</footer>
</div>
앞서 만들었던 HTML 페이지 구조는 클래스를 각 영역을 가리키는 선택자로 사용을 했습니다.
각각의 그리드 HTML 영역을 가리키는 클래스(Class)에 영역 이름을 선언하는 속성인 "grid-area" 속성을 사용해 다음과 같이 이름을 부여하면 됩니다.
grid-area: 영역 이름;
그리드 "영역 이름"은 CSS에서 클래스 이름을 정하는 규칙과 같은 방식으로 원하는 이름을 그리드 안에서 중복되지 않게 정하면 됩니다.
언제나 그렇지만, 알아보기 쉽고, 중복되지 않을 수 있는 이름을 사용하는 것이 좋습니다.
영역 이름을 부여한 클래스는 "grid-template-area" 속성으로 영역의 구획 범위를 제어할 수 있게 됩니다.
영역 이름을 부여한 클래스는 그리드 안에서 한번만 사용해야 합니다.
CSS로 영역 이름을 정하다 보면 클래스 이름과 영역 이름을 같은 것을 사용하게 되는 경우가 있게 되는데, 꼭 같지 않아도 되며, 경우에 따라서는 혼동을 할 수도 있기 때문에 앞에 접두어로 "grid-", "g-" 와 같은 키워드를 붙여서 영역 이름임을 따로 구분하는 것이 더 좋은 경우도 있습니다.
예를 들어 다음과 같이 영역 이름을 CSS에 설정할 수 있습니다.
.grid .header {grid-area: grid-header;}
.grid .title {grid-area: grid-title;}
.grid .main {grid-area: grid-main;}
.grid .sidebar {grid-area: grid-sidebar;}
.grid .footer {grid-area: grid-footer;}
이름을 부여한 클래스는 그리드의 각 영역에 해당하는 HTML 태그에 클래스로 부여합니다.
영역 이름을 정하는 규칙은 다음과 같습니다.
- 이름은 CSS 클래스명을 정하는 규칙을 사용해야 합니다.
- 영역 이름은 페이지 안에서 유일해야 합니다.
- 영역은 페이지 안에서 중복 사용할 수 있습니다.
주의할 점이 있습니다.
여기서는 클래스를 사용해 각 HTML 영역을 구분하고 가리키는 선택자 용도로 사용했습니다. ".grid .header" 클래스는 헤더 영역, 또는 헤더 태그를 가리하는 클래스 이름이고 이 클래스로 지정한 그리드 영역의 이름을 "grid-header"로 정했습니다.
CSS 클래스는 선택자(Selector) 이기 때문에 HTML 페이지의 그리드 안에서 영역 이름을 정할 대상이 유일하면 꼭 클래스가 아니어도 됩니다.
예를 들어 다음과 같이 영역 이름을 정할 수도 있습니다.
.grid header {grid-area: grid-header;}
.grid h1 {grid-area: grid-title;}
.grid main {grid-area: grid-main;}
.grid aside {grid-area: grid-sidebar;}
.grid footer {grid-area: grid-footer;}
이렇게 하면 HTML 페이지 안의 그리드 안에 있는 해당 태그에 대해 영역 이름을 부여하게 됩니다.
그리드 안에서 해당 태그가 유일한 경우, 클래스를 따로 정의해서 매핑할 필요가 없기 때문에 조금 더 간결하고 명확한 구분이 가능합니다.
다만, 페이지 구조가 복잡하거나, 중복 태그가 있을 경우에는 사용할 수 없습니다.
그리고, CSS 클래스 이름을 만든 후 클래스에 그리드 영역 이름을 매핑해서 사용하는 방법이 유지보수와 레이아웃 관리 측면에서 훨씬 더 유리하고 편하기 때문에 클래스 이름에 그리드 "영역 이름"을 매핑해서 사용하는 것이 더 좋습니다.
같은 방식으로 아이디(ID) 를 사용해 그리드 영역 이름을 HTML 태그에 매핑할 수도 있습니다.
그리드의 각 "영역 태그"에 아이디를 부여하고 이 아이디에 그리드 영역 이름을 할당하면 태그나 클래스 중복으로 인한 실수를 막을 수도 있고 CSS 코드도 간결해지는 장점이 있습니다.
단, CSS 에서 아이디(ID)를 남발하는 것은 권장하지 않기 때문에 가능하면 아이디를 사용하는 방법은 피하는 것이 좋습니다.
#header {grid-area: grid-header;}
#title {grid-area: grid-title;}
#main {grid-area: grid-main;}
#sidebar {grid-area: grid-sidebar;}
#footer {grid-area: grid-footer;}
영역 이름으로 그리드 구획하기
그리드 "영역 이름"으로 HTML 영역(태그)를 매핑했으므로, 영역 이름을 기준으로 구획을 나눠야 합니다.
그리드 영역 구획은 "grid-template-areas" 속성을 사용합니다.
그리드 영역(들)의 너비와 열 갯수 설정은 이미 배워서 알고 있는 "grid-template-columns" 속성으로 하고, "gap" 속성으로는 셀(행, 열) 사이의 여백을 정합니다.
영역 구획은 "grid-template-columns" 속성으로 정한 열의 갯수에 맞춰서 해야 합니다.
"grid-template-area" 속성으로 다음처럼 정의합니다.
하나의 행은 쌍따옴표로 감싸 영역 이름들을 공백으로 띄워서 표시하고, 여러 행을 공백으로 띄워서 나열함으로써 전체 레이아웃을 정의합니다.
한 행의 "영역 이름" 표시 갯수는 "grid-template-columns" 속성으로 정의한 열의 갯수와 같습니다.
grid-template-columns: 1fr 180px;
grid-template-areas: "영역이름1 영역이름2" "영역이름3 영역이름2" "영역이름4 영역이름4";
"영역이름"은 인접한 셀에 같은 "영역이름"을 표기해 하나의 영역으로 합칠 수 있습니다.
다음과 같은 영역 구획에서
grid-template-areas: "header header" "title sidebar" "main sidebar" "footer footer";
- 1행의 "header header" 는 1행의 열 2개를 "header" 그리드 영역 이름으로 합칩니다.
- 2행과 3행의 "sidebar" 는 2행과 3열의 2열을 "sidebar" 그리드 영역 이름으로 합칩니다.
- 4행의 "footer footer" 는 4행의 열 2개를 "footer" 그리드 영역 이름으로 합칩니다.
"영역 이름"은 그리드 안에서 고유해야 하며, 영역을 구획할 때 같은 분리된 영역에 같은 영역 이름을 사용할 수 없습니다.
앞서 만든 레이아웃을 확장해서 사이드바를 좌우에 배치한 3단 레이아웃을 만든다면 다음과 같이 사이드바 영역 이름을 2개를 만들어 사용하면 됩니다.
<div class="grid">
<header class="item header">헤더</header>
<aside class="item sidebar1">사이드바</aside>
<h1 class="item title">타이틀</h1>
<main class="item main">컨텐츠영역</main>
<aside class="item sidebar2">사이드바</aside>
<footer class="item footer">푸터</footer>
</div>
만들게 될 구조는 다음과 같습니다.
4행 3열의 구조가 되며, 행 높이는 조금 더 보기 좋게 보이도록 임의로 정한 것입니다.
CSS 그리드는 다음과 같이 작성하면 됩니다.
.grid{
width: 600px;
display: grid;
grid-template-columns: 150px auto 150px;
grid-template-areas: "header header header" "sidebar1 title sidebar2" "sidebar1 main sidebar2" "footer footer footer";
gap: 10px;
margin: 0 auto;
}
.grid .item{
background-color: #a99;
}
.grid .header{grid-area: header;}
.grid .title{grid-area: title;}
.grid .main{grid-area: main;}
.grid .sidebar1{grid-area: sidebar1;}
.grid .sidebar2{grid-area: sidebar2;}
.grid .footer{grid-area: footer;}
같은 그리드 영역 이름을 중복해서 사용하는 경우, 예를 들어 다음과 같이 "sidebar" 클래스를 2번 사용해서 같은 그리드 "영역 이름"을 하나의 그리드 안에 2번 사용하면 레이아웃이 깨지게 되므로 주의해야 합니다.
<div class="coloring grid">
<header class="item header">헤더</header>
<aside class="item sidebar">사이드바1</aside>
<h1 class="item title">타이틀</h1>
<main class="item main">컨텐츠영역</main>
<aside class="item sidebar">사이드바2</aside>
<footer class="item footer">푸터</footer>
</div>