Issue No. 20200330

이번에야말로 CSS Grid를 익혀보자

이 포스트에는 실제 코드가 적용된 부분들이 있으므로, 해당 기능을 잘 지원하는 최신 웹 브라우저로 보시는게 좋습니다.
(대충 인터넷 익스플로러로만 안보면 된다는 이야기)

이 튜토리얼은 “차세대 CSS 레이아웃” 시리즈의 두번째 포스트입니다.
혹시 CSS Flex에 대해 잘 모르신다면, 첫번째 포스트 “이번에야말로 CSS Flex를 익혀보자”를 먼저 읽고 오시는걸 추천드립니다! Flex랑 Grid가 겹치는 부분이 좀 있어서, Flex를 알면 더 수월하게 공부할 수 있거든요~ (어차피 둘 다 알아야 해요😭)

벌써부터 스크롤의 압박이 느껴지고,
‘좀 편안하게 누군가 나의 공부를 이끌어주면 좋겠다.’라고 생각하고 계신다면, 아래 배너의 강좌를 살펴보세요.

CSS 플렉스 그리드 온라인 강좌
학습시간_절약의_길.jpg

CSS 레이아웃의 끝판왕이라고 할 수 있는 Grid(그리드) 튜토리얼에 잘 오셨습니다!
앞서 공부한 Flex와 지금 공부할 Grid의 큰 차이점은

  • Flex는 한 방향 레이아웃 시스템이고 (1차원)
  • Grid는 두 방향(가로-세로) 레이아웃 시스템 (2차원)

이라는 점입니다.

따라서 Flex보다 더 복합적인 레이아웃 표현이 가능하지요.
참고로 지금 보고 계신 이 1분코딩 웹사이트는 전체적인 레이아웃은 Grid로 잡았고, 부분 부분에 Flex와 Grid를 적절히 사용해 주었습니다.

Grid에 관한 오해

“Grid는 인터넷 익스플로러(이하 IE)에서 지원하지 않기 때문에, 우리나라에서는 그림의 떡이다.”

아닙니다. IE 10과 11에서도 구버전 스펙[새 창]을 지원을 하기 때문에 좀 귀찮기는 해도 지원할 방법이 있습니다. 분명 한계는 있지만, 뭐가 다른지를 잘 알아두고 대응하면 충분히 사용할 수 있습니다. 지금 보시는 이 웹사이트도 Grid가 사용되었으며, IE 11에서 문제없이 레이아웃이 동작합니다. IE 10은 제가 확인을 못해봤지만 굳이 지원하고 싶지 않습니다(?).

저는 원래 크롬을 좋아해서 개발할 때도 크롬 개발자 도구를 사용하는데요, Grid를 처음 공부하실 때는 파이어폭스로 하시는게 더 편합니다. 파이어폭스 개발자 도구는 그리드를 명확하게 보여주는 기능이 있거든요~ (사실 Flex도 되는데 Flex보다 복잡한 Grid를 할 때 확실히 더 유용하더라고요)
파이어폭스 개발자도구의 “검사기” 탭을 열고, 아래 버튼이나 체크박스를 클릭하면 됩니다.

그럼 이렇게 쫜!

크롬을이기기위한파폭개발진의눈물겨운노력.jpg

Grid 레이아웃을 만들기 위한 기본적인 HTML 구조는 다음과 같습니다.
Flex와 마찬가지로, 컨테이너와 아이템이 있으면 됩니다.

<div class="container">
	<div class="item">A</div>
	<div class="item">B</div>
	<div class="item">C</div>
	<div class="item">D</div>
	<div class="item">E</div>
	<div class="item">F</div>
	<div class="item">G</div>
	<div class="item">H</div>
	<div class="item">I</div>
</div>

부모 요소인 div.container를 Grid Container(그리드 컨테이너)라고 부르고,
자식 요소인 div.item들을 Grid Item(그리드 아이템)이라고 부릅니다.
“컨테이너가 Grid의 영향을 받는 전체 공간이고, 설정된 속성에 따라 각각의 아이템들이 어떤 형태로 배치되는 것”이라고 생각하시면 됩니다.

Flex와 마찬가지로, Grid는 컨테이너에 display: grid; 를 설정하는 것으로 시작합니다.

.container {
	display: grid;
}

용어 정리

사실 지금 보면 뭔가 되게 복잡하고 “이게 뭐야..” 하실 수도 있는데, 일단은 어딘가 정리할 필요는 있어서 지금 넣어 보았습니다. 이 포스트를 읽다 보시면 자연스럽게.. 알게 될 거에요.

  • 그리드 컨테이너 (Grid Container)
    display: grid를 적용하는, Grid의 전체 영역입니다. Grid 컨테이너 안의 요소들이 Grid 규칙의 영향을 받아 정렬된다고 생각하면 됩니다. 위 코드 <div class=”container”></div>가 Grid 컨테이너에요.
  • 그리드 아이템 (Grid Item)
    Grid 컨테이너의 자식 요소들입니다. 바로 이 아이템들이 Grid 규칙에 의해 배치되는 거에요. 위 코드에서 <div class=”item”></div>들이 Grid 아이템입니다.
  • 그리드 트랙 (Grid Track)
    Grid의 행(Row) 또는 열(Column)
  • 그리드 셀 (Grid Cell)
    Grid의 한 칸을 가리키는 말이에요. <div>같은 실제 html 요소는 그리드 아이템이고, 이런 Grid 아이템 하나가 들어가는 “가상의 칸(틀)”이라고 생각하면 됩니다.
  • 그리드 라인(Grid Line)
    Grid 셀을 구분하는 선입니다.
  • 그리드 번호(Grid Number)
    Grid 라인의 각 번호입니다.
  • 그리드 갭(Grid Gap)
    Grid 셀 사이의 간격입니다.
  • 그리드 영역(Grid Area)
    Grid 라인으로 둘러싸인 사각형 영역으로, 그리드 셀의 집합이에요.

Grid의 속성들은 Flex와 마찬가지로,

  • 컨테이너에 적용하는 속성
  • 아이템에 적용하는 속성

이렇게 두 가지로 나뉘어요.

display: grid;

Grid 컨테이너에 display: grid;를 적용하는게 시작이에요.
아이템들이 block 요소라면 이 한 줄 만으로는 딱히 변화는 없습니다.

.container {
	display: grid;
	/* display: inline-grid; */
}
/* 그리고 아무 일도 일어나지 않았다 */

inline-grid도 있는데, 이건 block과 inline-block의 관계를 생각하시면 돼요.
아이템의 배치와 관련이 있다기 보다는, 컨테이너가 주변 요소들과 어떻게 어우러질지 결정하는 값입니다. inline-grid는 inline-block처럼 동작해요. 이것 역시 Flex랑 비슷하죠?

그리드 형태 정의
grid-template-rows
grid-template-columns
-ms-grid-rows
-ms-grid-columns

컨테이너에 Grid 트랙의 크기들을 지정해주는 속성입니다.
여러가지 단위를 사용할 수 있고 섞어서 쓸 수도 있어요.

.container {
	grid-template-columns: 200px 200px 500px;
	/* grid-template-columns: 1fr 1fr 1fr */
	/* grid-template-columns: repeat(3, 1fr) */
	/* grid-template-columns: 200px 1fr */
	/* grid-template-columns: 100px 200px auto */

	grid-template-rows: 200px 200px 500px;
	/* grid-template-rows: 1fr 1fr 1fr */
	/* grid-template-rows: repeat(3, 1fr) */
	/* grid-template-rows: 200px 1fr */
	/* grid-template-rows: 100px 200px auto */
}
  • grid-template-rows는 행(row)의 배치
  • grid-template-columns는 열(column)의 배치

를 결정합니다.
예를 들어

grid-template-columns: 200px 200px 500px;

는 column을 200px, 200px, 500px로 만들겠다는 의미에요.

grid-template-columns: 1fr 1fr 1fr;

fr은 fraction(뜻은 여기로[새 창])인데요, 숫자 비율대로 트랙의 크기를 나눕니다.
즉 위의 1fr 1fr 1fr은 균일하게 1:1:1 비율인 3개의 column을 만들겠다는 의미에요.
이렇게요↓

고정 크기와 가변 크기를 섞어 쓸 수도 있고요.
아래처럼 하면 왼쪽의 첫번째 column은 100px로 고정되고, 나머지 두번째 세번째 column은 2:1의 비율로 유연하게 움직이게 됩니다.

grid-template-columns: 100px 2fr 1fr;

아래 radio를 선택해서 직접 컨테이너 폭을 조절해 보세요!

값에 따른 변화를 느껴느끼지 않겠는가보세요.

repeat 함수

.container {
	grid-template-columns: repeat(5, 1fr);
	/* grid-template-columns: 1fr 1fr 1fr 1fr 1fr */
}

repeat는 반복되는 값을 자동으로 처리할 수 있는 함수입니다.
repeat(반복횟수, 반복값)
즉, 위 코드의 repeat(5, 1fr)1fr 1fr 1fr 1fr 1fr과 같아요.
repeat(3, 1fr 4fr 2fr); 이런 식의 패턴도 가능합니다.

minmax 함수

최솟값과 최댓값을 지정할 수 있는 함수입니다.
minmax(100px, auto)의 의미는 최소한 100px, 최대는 자동으로(auto) 늘어나게.. 입니다. 즉 아무리 내용의 양이 적더라도 최소한 높이 100px은 확보하고, 내용이 많아 100px이 넘어가면 알아서 늘어나도록 처리해 준 예시입니다.

.container {
	grid-template-columns: repeat(3, 1fr);
	grid-template-rows: repeat(3, minmax(100px, auto));
}

auto-fill과 auto-fit

auto-fill과 auto-fit은 column의 개수를 미리 정하지 않고 설정된 너비가 허용하는 한 최대한 셀을 채웁니다.
auto-fill 먼저 살펴 볼게요.

.container {
	grid-template-columns: repeat(auto-fill, minmax(20%, auto));
}

auto-fill의 크기를 20%로 설정했으므로, 1개의 row에는 5개의 셀이 들어갑니다.

셀의 개수가 5개보다 모자라면, 이런↓ 식으로 공간이 남게 되고요.

auto-fill 대신 auto-fit을 사용하면, 남는 공간을 채웁니다.
바로 이게↓ auto-fill과 auto-fit의 차이에요.

간격 만들기
row-gap
column-gap
gap

그리드 셀 사이의 간격을 설정합니다.

.container {
	row-gap: 10px;
	/* row의 간격을 10px로 */
	column-gap: 20px;
	/* column의 간격을 20px로 */
}
.container {
	gap: 10px 20px;
	/* row-gap: 10px; column-gap: 20px; */
}
.container {
	gap: 20px;
	/* row-gap: 20px; column-gap: 20px; */
}

초기에는 앞에 gird-를 붙여서 grid-gap, grid-row-gap, grid-column-gap이었는데, 브라우저 호환 범위를 넓히기 위해 아래처럼 이전 버전의 이름과 현재 버전의 이름을 둘 다 쓰기도 해요.

.container {
	grid-gap: 20px;
	gap: 20px;
}

IE에서는 gap의 대체 속성이 없기 때문에, IE와 구조를 통일하고 싶으면 아예 처음부터 gap을 사용하지 않고 구조를 설계하는게 편해요.

그리드 형태를 자동으로 정의
grid-auto-columns
grid-auto-rows

grid-template-columns(또는 grid-template-rows)의 통제를 벗어난 위치에 있는 트랙의 크기를 지정하는 속성이에요.
속성 이름이 헷갈린다면 -template- 자리에 – auto-가 들어간다고 생각하세요~

“통제를 벗어난”이 무슨 의미일까요? 아까 이 코드 기억 나시나요?

.container {
	grid-template-rows: repeat(3, minmax(100px, auto));
}

각 셀마다 최소 100px의 높이를 확보하고, 컨텐츠가 높이 100px을 넘어가면 알아서 자동으로 늘어나도록(auto) 하려고 저 코드를 썼는데, 우리가 만든 예시가 row가 3개였기 때문에 repeat 회수를 3으로 지정해 줬었지요? 그런데 row 개수를 미리 알 수 없는 경우면 어떻게 할까요? 바로 이 grid-auto-rows가 그 해결책입니다.

.container {
	grid-auto-rows: minmax(100px, auto);
}

이렇게 grid-auto-rows를 써주면, 굳이 횟수를 지정해서 반복할 필요 없이 “알아서” 처리됩니다. 즉, grid-template-rows로 미리 세팅해 둔 것이 없이 때문에 여기있는 모든 row들은 grid-template-rows의 통제를 벗어난 row가 되는 것이고, 바로 grid-auto-rows가 처리를 하는 거지요! “통제를 벗어난”의 의미는 바로 이겁니다.

각 셀의 영역 지정
grid-column-start
grid-column-end
grid-column
grid-row-start
grid-row-end
grid-row
-ms-grid-row
-ms-grid-column
-ms-grid-row-span
-ms-grid-column-span

이 속성들은 Grid 아이템에 적용하는 속성으로, 각 셀의 영역을 지정합니다.
아래 그림을 보면,

1부터 4까지의 Grid 라인 번호가 매겨져 있는데요, 바로 그 번호를 이용해서 column과 row의 범위를 결정하는 겁니다.
column으로 살펴보면, grid-column-start가 시작 번호, grid-column-end가 끝 번호입니다. grid-column은 start와 end 속성을 한번에 쓰는 축약형입니다.
위의 빨간색 영역을 코드로 쓰면 아래와 같아요. 이 두 코드↓는 같은 영역을 가리킵니다.

.item {
	grid-column-start: 1;
	grid-column-end: 3;
	grid-row-start: 1;
	grid-row-end: 2;
}
.item:nth-child(1) {
	grid-column: 1 / 3;
	grid-row: 1 / 2;
}

이렇게 설정하면 영역은 이렇게↓ 됩니다.

시작번호 / 끝번호를 지정하는 방법 외에, 몇 개의 셀을 차지하게 할 것인지를 지정해줄 수도 있어요.

.item:nth-child(1) {
	/* 1번 라인에서 2칸 */
	grid-column: 1 / span 2;
	/* 1번 라인에서 3칸 */
	grid-row: 1 / span 3;
}

여기서 잠깐 위에서 공부했던 grid-auto-columns를 다시 볼게요.
grid-auto-columns는 grid-template-columns의 통제를 받지 않는 column들의 배치를 결정하는 규칙이라고 했는데요, 이 grid-column을 이용해 ‘통제받지 않는’ column들을 만들 수 있어요.

.container {
	grid-template-columns: 50px;
	grid-auto-columns: 1fr 2fr;
}
.item:nth-child(1) { grid-column: 2; }
.item:nth-child(2) { grid-column: 3; }
.item:nth-child(3) { grid-column: 4; }
.item:nth-child(4) { grid-column: 5; }
.item:nth-child(5) { grid-column: 6; }
.item:nth-child(6) { grid-column: 7; }
/* end를 생략하면 그냥 한 칸임 */

이렇게 하면, 첫번째 column만 grid-template-columns의 통제를 받아 50px로 되고, 나머지 column들은 grid-auto-columns의 규칙에 따라 1:2의 비율이 반복됩니다.
이렇게요↓

영역 이름으로 그리드 정의
grid-template-areas

각 영역(Grid Area)에 이름을 붙이고, 그 이름을 이용해서 배치하는 방법입니다.
정말 직관적인 방법이에요.
아래의 그림↓에서 각 셀에 매칭되는 이름을 어떤 식으로 썼는지 살펴 보세요.

.container {
	grid-template-areas:
		"header header header"
		"   a    main    b   "
		"   .     .      .   "
		"footer footer footer";
}

위의 형태로 각자 차지하는 셀의 개수만큼 해당 위치에 이름을 써주면 됩니다.
각 셀마다 공백을 하나씩 넣어서 구분해주시면 되고요.
header는 첫번째 row에서 3개의 column을 차지하니 맨 위에 3번 쓴 것, 이해 되시죠?
빈칸은 마침표 또는 “none”을 사용하면 되고, 마침표의 개수는 여러개를 써도 상관 없습니다.

그럼 각 영역의 이름은 어떻게 매칭할까요? 해당 아이템 요소에 grid-area 속성으로 이름을 지정해주면 됩니다!
예를 들면 이런↓ 식이 되겠지요.

.header { grie-area: header; }
.sidebar-a { grie-area: a; }
.main-content { grie-area: main; }
.sidebar-b { grie-area: b; }
.footer { grie-area: footer; }
/* 이름 값에 따옴표가 없는 것에 주의하세요 */

자동 배치
grid-auto-flow

아이템이 자동 배치되는 흐름을 결정하는 속성입니다.

.container {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(25%, auto));
	grid-template-rows: repeat(5, minmax(50px,auto));
}
item:nth-child(2) { grid-column: auto / span 3; }
item:nth-child(5) { grid-column: auto / span 3; }
item:nth-child(7) { grid-column: auto / span 2; }

B, E, G는 각각 셀을 3개 또는 2개를 점유하도록 설정했는데요, 그 때문에 셀에 들어갈 자리가 없어서 빈 셀들이 생겼습니다. Grid 배치의 기본 설정은 아이템이 row를 기준으로 순서대로 배치가 되다가 들어갈 자리가 없으면 그 칸은 비워두고 아래로 배치가 됩니다.
dense는 기본적으로 빈 셀을 채우는 알고리즘이며, row와 column에 따라 기준이 달라집니다.

자, 이제 정렬입니다.

세로 방향 정렬
align-items

아이템들을 세로(column축) 방향으로 정렬합니다. 컨테이너에 적용합니다.

.container {
	align-items: stretch;
	/* align-items: start; */
	/* align-items: center; */
	/* align-items: end; */
}

가로 방향 정렬
justify-items

아이템들을 가로(row축) 방향으로 정렬합니다. 컨테이너에 적용합니다.

.container {
	justify-items: stretch;
	/* justify-items: start; */
	/* justify-items: center; */
	/* justify-items: end; */
}

place-items

align-items와 justify-items를 같이 쓸 수 있는 단축 속성이에요.
align-items, justify-items의 순서로 작성하고, 하나의 값만 쓰면 두 속성 모두에 적용됩니다.

.container {
	place-items: center start;
}

아이템 그룹 세로 정렬
align-content

Grid 아이템들의 높이를 모두 합한 값이 Grid 컨테이너의 높이보다 작을 때 Grid 아이템들을 통째로 정렬합니다.

.container {
	align-content: stretch;
	/* align-content: start; */
	/* align-content: center; */
	/* align-content: end; */
	/* align-content: space-between; */
	/* align-content: space-around; */
	/* align-content: space-evenly; */
}

아이템 그룹 가로 정렬
justify-content

Grid 아이템들의 너비를 모두 합한 값이 Grid 컨테이너의 너비보다 작을 때 Grid 아이템들을 통째로 정렬합니다.

.container {
	justify-content: stretch;
	/* justify-content: start; */
	/* justify-content: center; */
	/* justify-content: end; */
	/* justify-content: space-between; */
	/* justify-content: space-around; */
	/* justify-content: space-evenly; */
}

place-content

align-content와 justify-content를 같이 쓸 수 있는 단축 속성이에요.
align-content, justify-content의 순서로 작성하고, 하나의 값만 쓰면 두 속성 모두에 적용됩니다.

.container {
	place-content: space-between center;
}

개별 아이템 세로 정렬
align-self
-ms-grid-row-align

해당 아이템을 세로(column축) 방향으로 정렬합니다. 아이템에 적용합니다.

.item {
	align-self: stretch;
	/* align-self: start; */
	/* align-self: center; */
	/* align-self: end; */
}

개별 아이템 가로 정렬
justify-self
-ms-grid-column-align

해당 아이템을 가로(row축) 방향으로 정렬합니다. 아이템에 적용합니다.

.item {
	justify-self: stretch;
	/* justify-self: start; */
	/* justify-self: center; */
	/* justify-self: end; */
}

place-self

align-self와 justify-self를 같이 쓸 수 있는 단축 속성이에요.
align-self, justify-self의 순서로 작성하고, 하나의 값만 쓰면 두 속성 모두에 적용됩니다.

.item {
	place-self: start center;
}

배치 순서
order

각 아이템들의 시각적 나열 순서를 결정하는 속성이에요.
숫자값이 들어가며, 작은 숫자일 수록 먼저 배치됩니다. "시각적" 순서일 뿐, HTML 자체의 구조를 바꾸는 것은 아니므로 접근성 측면에서 사용에 주의하셔야 합니다. 시각 장애인분들이 사용하는 스크린 리더로 화면을 읽을 때, order를 이용해 순서를 바꾼 것은 의미가 없다는 것을 기억해 주세요.

.item:nth-child(1) { order: 3; } /* A */
.item:nth-child(2) { order: 1; } /* B */
.item:nth-child(3) { order: 2; } /* C */

z-index

z-index로 Z축 정렬을 할 수 있어요. 숫자가 클 수록 위로 올라옵니다.
(position에서의 z-index랑 똑같이 생각하시면 됩니다.)

.item:nth-child(5) {
	z-index: 1;
	transform: scale(2);
}
/* z-index를 설정 안하면 0이므로, 1만 설정해도 나머지 아이템을 보다 위로 올라온다 */

끝으로, IE(인터넷 익스플로러) 10, 11에서 사용할 수 있는 Grid 속성을 알려드릴게요.
한계는 있지만, 적절히 사용하면 충분히 상용 서비스에도 적용할 수 있습니다!
IE에서 지원하는 고인물 스펙 참고[새창]

표준😃 그지같은 IE😩
display: grid; display: -ms-grid;
grid-template-rows -ms-grid-rows
grid-template-columns -ms-grid-columns
grid-row-start -ms-grid-row
grid-column-start -ms-grid-column
grid-row: 1 / span 2;에서 span 2 대신 -ms-grid-row-span: 2
grid-column: 1 / span 2;에서 span 2 대신 -ms-grid-column-span: 2
align-self -ms-grid-row-align
justify-self -ms-grid-column-align
고생 많이 하셨습니다!

Flex와 Grid에 대해 더 깊게 공부하고 싶다면 아래 링크의 온라인 강좌를 살펴보세요~
1분코딩에서 출시한 따끈한 신작입니다.

CSS 플렉스 그리드 온라인 강좌
열심히_만들었습니다.jpg