CSS Grid 레이아웃은 무엇이고, 어떻게 사용할까? (예제와 함께 알아보기) — 코딩크리처
제목 : CSS grid 란 무엇이고, 어떻게 사용할까?
이 글을 작성하는 이유 :
아주 최근에 작성한 CSS 에 대한 글 몇 개와 연동하여
알아야 할 지식으로 생각하여 CSS grid 에 대해서 다루게 되었다.
이전 글은,
미리 알아두면 좋았을 CSS 기초 및 응용 예제
CSS 레이아웃, display 에 대해서 알아놓자
예제와 함께 알아보는 CSS flex 와 Flexbox
위의 3 글이며,
레이아웃의 핵심인 Flexbox 와 더불어, Grid 에 대해서도 알아보려고 한다.
혹시라도 CSS 에 대해서 잘 모르시는 분이 있다면,
위의 3 개의 글을 순차적으로 읽고 오시면 도움이 됩니다.
그리고, 질문은 환영이기 때문에,
으로 메일을 보내주시면 감사하겠습니다.
내가 예제를 블로그에 만드는 방법
나의 블로그 템플릿은 hELLO 라는 템플릿에서 나의 커스텀 CSS 를 덧붙인 형태이기 때문에,
현재 블로그 스타일 시트의 영향을 받는다면, 날 것의 형태를 볼 수 없다.
따라서, 내가 선택한 방식은 iframe 태그를 이용하여 현재 페이지에 종속되어 있는 StyleSheet
로부터 떼어놓는 방식이다.
혹시라도 블로그를 작성 할 생각이 있다면, 내가 사용하는 방식은 추후 많은 도움이 될 것이다.
예시 :
<iframe
style="width:70%; max-height:20rem;"
srcdoc='
<style>
body {
background : white;
}
</style>
<body>
기본 상태는 이러합니당
</body>
'
>
<p>이 문구가 보인다면, iframe 이 막혀 CSS 예시를 볼 수 없습니다.
</iframe>
이 사이트 자체에서 iframe 의 백그라운드는 투명에 속하기 때문에,
강제로 하얀색으로 초기화 시키고 시작한다.
<head> 에 메타데이터로 스타일을 넣지 않아도,
이러한 형식으로 분리된 HTML + CSS + JS 형식을 만들 수 있다.
Grid 란 무엇인가?
개별 컴포넌트는 외부(부모, 형제) 컴포넌트가 자신을 어떻게 인식할지,
내부에 포함된 컴포넌트는 "어떻게 정렬될지" 선택할 수 있다.
이러한 정렬은 display 라는 CSS 속성으로 결정된다.
HTML 태그는 정말 다양한데, 리스트, 테이블, 단순 영역 등등
고유의 HTML 태그들은 제 역할에 맡게 적당한 display Default 값을 가지고 있다.
우리가 다루는 display 속성은, 주로 여러 컴포넌트를 담게 되는 오브젝트에 선언하게 된다.
즉, 우리는 여러 컴포넌트를 담을 컨테이너가 하위 요소를 어떻게 정렬 할 지 선택 할 수 있다.
div, section, article, 과 같은 영역 태그들은 block 이라는 기본 값이 있으나,
개발자들의 무한한 스타일링 기법을 충족시키기 위해,
display : flex
display : grid
가 대표적으로 존재한다.
Flex 란, 컨테이너가 내부 컴포넌트를 1차원 나열 형식으로 표현하고자 할 때 사용되며,
flex-wrap : wrap 이라는 명시를 통해 다양한 너비의 디바이스에서
크기에 따른 자동 줄넘김을 유도 할 수 있다.
flex 에 대해서 더 자세한 것은,
예제와 함께 알아보는 CSS flex 와 Flexbox
여기서 배우고 오면 이해가 될 것이다.
그렇다면, Grid 란 무엇일까?
Grid 란, 내부 컴포넌트를 row 와 column 나열 형태로 표현하기 위해 존재한다.
즉, 2차원이라고 말할 수 있다.
row(행), column(열) 을 직접 선언하는 것이 언제 중요하냐고 할 수 있을 테지만,
헤더, 사이드바, 아티클 등등 여러 요소가 순서대로 항상 배치되어야 할 때
grid 를 사용하여 간단하게 해결 할 수 있다.
grid 에 추가되는 다양한 속성들
Grid 기본 속성 (Grid)
grid-template-columns
grid-auto-columns
grid-template-rows
grid-auto-rows
Grid 영역 속성 (Grid Areas)
grid-area
grid-template-areas
Grid 영역의 새로운 단위
Grid 에서 사용되는 함수
repeat()
minmax()
fit-content()
Grid 의 아이템 배치
grid-column-start
grid-column-end
grid-row-start
grid-row-end
Grid 의 여백
grid-column-gap
grid-row-gap
grid gap
Grid 의 단축어(짧은 표기) 속성
grid-column
grid-row
grid-template
grid
위와 같이 Grid 디스플레이를 위해 다양하며 새로운 속성들이 기다리고 있다.
Grid 에서 알아야 할 용어에 대한 설명
그리드에서부터는 위에서 언급한 새로운 특화 속성들을 자유로이 다루기 위해서,
Term(사용될 용어) 를 간단하게 볼 필요가 있다.
그래서 따로 이미지를 만들어 봤다.
Grid Line
Grid Line 은 시작 지점을 나타내는 용도이다.
시작 인덱스는 0 이 아니라, 1 부터 시작한다.
추후 사용될 이 용어와 의미는 Column, Row 둘 다 동일하게 적용된다.
Grid Track
행을 의미하는 Row 로 작성되지 않고, 여기서는 Track 이라고 불린다.
왜 하나의 줄이라고 부르지 않고, 도대체 왜 "트랙" 이라고 부르는가? 해서 의미를 계속 살펴보았다.
여기서 위의 Grid Line 과 연관되어 나오는데,
"행 트랙" 이란, 두 행(row) Line 사이에 있으며,
"열 트랙" 이란, 두 열(column) Line 사이에 있다는 것이다.
여기서나는 Line 을 공간으로 바라보았는데,
그게 아니고, 위에 설명된 진짜 "그어진 Grid Line" 으로 바라보아야 한다.
사실상 이 라인까지도 포함된 개념을 Row 혹은 Column 으로 보고,
"그 사이의 공간" 을 Track 이라고 부르는 것이다.
Grid Cell
셀은 간단하다. 위에서 말한 영역, 즉, Grid Line 사이의 영역을 Track 이라고 말했다면,
이 Cell 은 이 자리의 "행 Track", "열 Track" 의 교차 영역을 의미한다.
이렇게 어렵게 설명하는 이유는, 간단히 말해 경계선을 포함시키지 않기 위함이다.
Grid Area
위의 용어는 특정 엘리먼트, 혹은 위치에 배치된 엘리먼트가
글 작성 시 줄넘김 하는 것 처럼 넘어가지 않고,
해당 위치에서 정해진 위치까지 포함하도록 만든다.
Grid Gap
내부에 채워질 각 엘리먼트들의 Margin 이라고도 볼 수 있다.
각 엘리먼트가 얼마나 떨어져 있을지 선언한다.
새로운 단위, fr
display : flex 환경에서는 굉장히 유용하게 사용되는 기능이 존재하는데,
바로 비율로 각 컴포넌트가 얼마나 이 컨테이너를 차지 할 것인지를 지정하는 것이다.
단축 키워드인 flex 를 이용할 수 있는데,
하위 엘리먼트에서 스스로 flex : 1, flex : 2 가 선언되었다면,
각 엘리먼트는 1/3, 2/3 의 공간을 차지 할 것이다.
이 fr 이라는 단위는 나중에 CSS 의 "함수" 와 직접적으로 연관되어 있다.
아직 현재는 fr 이라는 단위가, 선언된 위치에 따라 해당 Column, or Row 가
이 단위와 함께 비율적으로 커지거나 작아진다는 것이다.
예를 들어, grid-template-column : 1fr 1fr 1fr 2fr; 으로 선언 될 경우,
하나의 트랙에 들어가는 순서, column(열) 에 따라 1 : 1 : 1 : 2 비율로 너비가 나뉜다.
엘리먼트가 6 개 일 경우, 2 줄로 편성되며, 배치된 열에 따라 위와 같은 비율의 너비를 가진다.
엘리먼트가 그보다 작은 2 일경우, 1 : 1 로 계산되지 않으며, 배치되지 않은 1fr 2fr 은 빈 칸으로 간주한다.
위의 설명을 본다면, fr 이라는 단위는 "비율" 에 해당한다는 것을 알 수 있다.
그리고, 이 단위는 px, em, .. 등등 정확한 절대 단위와 같이 사용될 수 있다.
fr 은 이 절대적인 값들과 Gap 을 뺀 나머지 영역에서 fr 비율로 나눈다.
Grid 를 사용 해 보자.
display : flex 를 통해 Flexbox 를 본격적으로 여는 것 처럼,
Grid 는 display : grid 를 통해 2차원 엘리먼트 배치를 본격적으로 구성하게 해 준다.
grid-template-xxx (행, 열)
grid 는 행, 그리고 열 을 선언하는 특수? 전용 속성이 존재한다.
바로,
grid-template-columns : 열 설정
grid-template-rows : 행 설정
여기서 다른 css 속성과는 확실히 다른 점이 존재하는데,
바로 인자의 개수 제한이 없다시피 한다는 것이다.
인자의 개수 제한이 없다는 게 무슨 의미일까?
CSS 에서 Propertie : Value 선언 쌍을 이루는 것이 일반적이다.
그러나, 몇몇 속성에서는 다중 값 선언이 허용된다.
예를 들어,
display : inline flex; : 블록이 아닌 인라인 형식, 그리고 Flexbox.
border-width : 0 0 10px 0; : 아래에 10px 의 경계선이 있으며, 나머지는 경계가 안보임
flex : 2 0 auto : flex-item 에서 선언할 수 있는데, 이 아이템은 비율 수준 2 를 가져간다.
생각나는 다중 값 속성들은 일단, 이러하다.
그리고, grid-template-xxx 에 해당하는 이 2 가지 속성은,
grid-template-??? : 100px 1fr 100px 10px 10px 10px ...
이러한 식으로 거의 무한하게 뻗어나갈 수 있다.
그리고 위의 값이 선언된다면, 이러한 의미가 된다.
row or column 에서의 의미 (행이던 열이던 상관이 없음.)
1 번째 인자 : 절대적인 100px 확보
2 번째 인자 : 남는 공간에서 비율 1 을 가져간다.
3 번째 인자 : 절대적인 10px 확보
4 번째 인자 : 동일
... 10px 이 지속적으로 이어진다고 가정.
그렇다면, 행과 열 개수를 자유롭게 바꾸기 어렵지 않나?
라고 생각을 했었는데, 그렇지 않다. 바로, CSS 문법의 "함수" 가 존재하기 때문이다.
함수를 사용하기까지는 아직 내가 적응이 된 것은 아니라서, Grid 의 기초를 다루며 천천히 진입 할 것이다.
내가 만든 예제를 살펴보자.
.container {
/* grid 선언 */
display : grid;
/* 이 컨테이너는 20 rem 의 너비를 고정적으로 가진다 */
width : 20rem;
margin : 1rem;
border : 4px solid black;
/* grid 컨테이너 내부의 요소들이 서로 간격을 10px 로 떨어진다 */
gap : 10px;
/* 내가 선언하는 '열' 이 가질 요소 */
grid-template-columns : 50px 1fr 30%;
}
div {
text-align : center;
border : 2px solid gray;
border-radius : 3px;
box-shadow : 0 5px 5px 0 darkgray;
}
<body>
<button onclick="plusElem()">요소 추가하기</button>
<br/>
<section class="container" id="container">
<div>
1
</div>
</section>
<script>
const container = document.getElementById("container");
let currNum = 2;
function plusElem() {
const newElem = document.createElement("div");
newElem.textContent = currNum++;
container.appendChild(newElem);
}
</script>
</body>
Preview :
'
>
이 문구가 보인다면, 개인의 설정으로 인해 iframe 이 거부되었을 확률이 높습니다.
end
위의 예제에 아주 간단한 엘리먼트 추가 기능을 넣었는데,
이는 엘리먼트가 하나의 Track 에서 포함 기준치를 넘어가면,
어떻게 엘리먼트를 배치하는지 직관적으로 보여주기 위함이다.
위에서 내가 선언한 Grid Template 을 보자.
grid-template-columns : 50px 1fr 30%;
첫 번째 요소는 고정적으로 50px 을 얻으며,
세 번째 요소는 Grid Container 의 영역에 따라 30% 를 가진다.
그리고 남은 Free Area 는,
유일하게 이 단위를 사용하는 두 번째 요소에 남는 영역을 모두 몰아준다.
따라서 위의 예제를 보면, 2 번째 요소가 제일 큰 것이다.
만약에 위의 1fr 을 3fr 로 바꿔도, 같은 단위를 사용하는 "열" 이 없기 때문에,
동일하게 너비를 가진다.
만약에 50px 1fr 30% 1fr 이라면,
50px, 30% 에 해당하는 너비를 뺀 결과를,
1fr, 1fr 이 서로 비율로 1/2, 1/2 로 나눠 갖는다.
그렇다면, 반대로 grid-template-rows 만 선언되면 어떻게 될까?
.container {
/* grid 선언 */
display : grid;
/* 이 컨테이너는 20 rem 의 높이를 고정적으로 가진다 */
height : 20rem;
margin : 1rem;
border : 4px solid black;
/* grid 컨테이너 내부의 요소들이 서로 간격을 10px 로 떨어진다 */
gap : 10px;
/* 내가 선언하는 행 이 가질 요소 */
grid-template-rows : 50px 1fr 30%;
grid-auto-flow : column;
}
div {
text-align : center;
border : 2px solid gray;
border-radius : 3px;
box-shadow : 0 5px 5px 0 darkgray;
}
<!-- 위의 예제와 동일합니다. -->
<body>
<button onclick="plusElem()">요소 추가하기</button>
<br/>
<section class="container" id="container">
<div>
1
</div>
</section>
<script>
const container = document.getElementById("container");
let currNum = 2;
function plusElem() {
const newElem = document.createElement("div");
newElem.textContent = currNum++;
container.appendChild(newElem);
}
</script>
</body>
안타깝게도, grid-template-columns 를 단독으로 선언하여 다음 줄로 넘길 수 있었던 것에 비해,
grid-template-rows 만을 단독으로 선언해서는, 원하는 바를 만들 수가 없다.
나는 굳이 grid-template-columns 를 사용하고 싶지 않았다.
그 이유가, 컬럼을 선언하게 되면, 내부의 컨텐츠가 컬럼에 따라 "미리 분리" 가 되어있기 때문이다.
내가 원한 것은, "컨텐츠의 수에 따라 영역이 그때그때 나뉘는 것" 을 원했다.
이에 대한 답을 알기 위해 Gemini-2.5 를 사용했는데,
위의 예제에서 CSS 에 grid-auto-flow : column; 단 한 줄만 추가하면 되었다.
.container {
/* grid 선언 */
display : grid;
/* 이 컨테이너는 20 rem 의 높이를 고정적으로 가진다 */
height : 20rem;
margin : 1rem;
border : 4px solid black;
/* grid 컨테이너 내부의 요소들이 서로 간격을 10px 로 떨어진다 */
gap : 10px;
/* 내가 선언하는 행 이 가질 요소 */
grid-template-rows : 50px 1fr 30%;
grid-auto-flow : column;
}
div {
text-align : center;
border : 2px solid gray;
border-radius : 3px;
box-shadow : 0 5px 5px 0 darkgray;
}
HTML 은 그대로
Preview
end
위의 예제를 만들고 나서, 원하던 형식으로 표시되는 것을 확인 할 수 있었다.
Grid 컨테이너 트랙 고유 크기 조정 키워드
우리가 생각해 볼 수 있는 것은, 절대값 선언인 50px, 2rem, 30%
를 제외하고,
Grid 시스템에서 가장 많이 사용 되는 단위는 fr 이라는 단위다.
즉, 이는 free 의 약자로서, 남는 공간을 fr 끼리 비율로 분배하는 효율적인 단위이다.
여기서 생각 해 볼 수 있는 점은,
위의 단위들에서 "컨텐츠에 따른 크기" 는 고려 대상이 아니라는 것이다.
즉, 절대 값이나, Grid 컨테이너에서 유연한 비율을 선언할 수 있는 fr 단위 또한, 컨텐츠를 신경쓰지 않는다.
따라서 여기서 grid-template-columns or rows 에 선언할 수 있는 또 다른 키워드들이 존재하는데,
auto
min-content
max-content
fit-content(..)
이다.
아직 본격적으로 CSS Function 을 다룰 때가 아닌 것 같아서 밑의 아주 간단한 예시를 들자면,
grid-template-columns : auto min-content max-content fit-content(3rem);
이러한 방식으로 선언이 가능하다.
만약에, 1 개의 트랙에 동일한 속성을 선언하고자 한다면,
grid-template-columns : repeat(4, 1fr); 와 같은 형식으로 선언이 가능하다.
위의 repeat 라는 함수는 동일한 인자를 결과적으로 펼쳐주는 역할을 하는데,
4 : 4 번 동일하게 반복 선언한다.
1fr : 1fr 을.
Result : grid-...-columns : 1fr 1fr 1fr 1fr; 이 된다.
다시 돌아와서, Grid Track 의 고유 크기 조정 키워드는
위와 같은 repeat 함수의 "인자" 로 건네주어 하위 컴포넌트의 크기를 다룰 수 있다.
예를 들어보자.
나는 총 7 개의 엘리먼트를 넣을 수 있게 만들 것이며,
하나의 줄에 총 3 개가 들어 갈 수 있다.
하위 컴포넌트들은 컨테이너의 선언에 따라 모두 같은 키워드로 조정된다.
그러나, 각각의 컴포넌트들은 다른 컨텐츠 길이를 가진다.
그리고, 각 단위의 정확한 의미를 알아보자.
auto : 컨텐츠의 크기에 따라 공간을 분배하며, 컴포넌트들이 하나의 트랙을 전부 차지한다.
min-content : 내부 컴포넌트에서 "가장 긴 단어" 만큼 너비를 가진다.
max-content : 내부 컴포넌트에서 줄넘김이 발생하지 않을 만큼 큰 너비를 가진다. 이 키워드는 오버플로우를 발생시킬 수 있다.
fit-content(?) : ? 에 선언된 크기만큼 커질 수 있으며, 그 이상은 늘어나지 않는다. 트랙의 끝에 도달하면 컨텐츠 내부를 자동으로 줄넘김하므로, 유용한 기능이다.
/* 전역 변수로서 트랙 키워드를 동적으로 변화시키기 위해서 선언 */
/* 또한, CSS 내부에서 함수를 통해 전역적으로 가져올 수 있음. */
:root {
--grid-track-keyword : auto;
}
.container {
display : grid;
/* 오버플로를 유발시키기 위한 적정한 길이로 판단 */
max-width : 20rem;
/* repeat, var 함수를 통해 앞으로 grid 를 구성하게 됨. */
grid-template-columns : repeat(3, var(--grid-track-keyword));
/* 1fr 이 아니라, auto 로 선언하여, 하얀 빈 칸이 생겨나지 않도록 조치. */
grid-template-rows : repeat(3, auto);
padding : 1rem;
margin : 1rem;
border : 2px solid dodgerblue;
}
div {
text-align : center;
padding : 1rem;
border : 2px solid gray;
border-radius : 3px;
box-shadow : 0 5px 5px 0 darkgray;
}
<body>
각 개체의 컬럼 사이즈를 골라보자 :
<br/>
<select id="grid-track-select">
<option value="auto">auto</option>
<option value="min-content">min-content</option>
<option value="max-content">max-content</option>
<option value="fit-content(7rem)">fit-content(7rem)</option>
</select>
<br/>
<section id="container" class="container">
<div>
컨테이너 1
</div>
<div>
컨테이너 2
</div>
<div>
컨테이너 3 인데, max-content 를 선택하면 오버플로가 납니다.
</div>
<div>
컨테이너 4
</div>
<div>
컨테이너 5
</div>
<div>
컨테이너 6
</div>
<div>
컨테이너 7
</div>
</section>
<script>
const select = document.getElementById("grid-track-select");
const container = document.getElementById("container");
const root = document.documentElement;
select.addEventListener("change", function () {
/* CSS 시트에서 :root 로 선언된 변수는 이와 같이 동적으로 변화시키거나 선언할 수 있음. */
root.style.setProperty("--grid-track-keyword", select.value);
});
</script>
</body>
Preview :
end
위에 만들어 놓은 예제를 통해 Grid 의 고유 크기 조정 keyword 들을 익히기를 바란다.
위의 키워드에서 중요한 것은, 일방적으로 컨테이너가 선언하는 크기만을 선언 할 수 있는 것이 아니라,
컨테이너 내부에 존재하는 컴포넌트들의 "컨텐츠 양" 에 따라 조절할 수 있다는 것이 핵심이다.
조금 더 직접적으로 알아보는 fr, 그리고 추가 함수들
이 글에서는 fr 이라는 단위에 대해서 먼저 설명하긴 했지만,
컨텐츠의 양에 따라 자동적으로 영역을 분배 해 주는 auto 가 먼저 배울 내용이라고 생각된다.
fr 단위에 대해서 본격적으로 들어가기 전에, 다시 정보를 짚고 넘어가자.
fr 단위는 남는 grid 컨테이너에서 비율로 나눈다.
컨테이너에 하나의 엘리먼트가 존재한다면, 1fr, 3fr 이던 동일한 크기를 가진다.
fr 단위가 여러번 선언된다면, 해당 트랙 전체 크기에서 비율로 나눈다.
그리드 템플릿 행, 열은 여러 번 인자를 선언하거나 전달하는 방식으로 구성되므로, repeat 와 같은 함수를 자주 사용하게 된다.
먼저 예제로 하나의 예시를 보고 가자.
나는 예제를 이렇게 만들었다.
row 방향이며, 총 3 개의 열을 가질 수 있다.
2 번째 열의 크기만을 바꾸고, 나머지는 1fr 로 동결이다.
크기를 선택 해 가며, 비율의 감각을 키우고 넘어가자.
물론, 나도 css 를 잘하는 사람이 절대 아니기 때문에, 예시를 만들면서 조금씩 감각을 키우고 있다.
:root {
--select-free : 1fr;
}
.container {
display : grid;
grid-template-columns : 1fr var(--select-free) 1fr;
width : 20rem;
border : 2px solid dodgerblue;
margin : 1rem;
padding : 1rem;
gap : 0.75rem;
}
div {
text-align : center;
border : 2px solid gray;
border-radius : 3px;
box-shadow : 0 5px 5px 0 darkgray;
}
<body>
두 번째 엘리먼트의 비율을 변화시켜봅시다.
<br/>
<select id="select-fr">
<option value="1fr">1fr</option>
<option value="2fr">2fr</option>
<option value="3fr">3fr</option>
<option value="4fr">4fr</option>
</select>
<br/>
<section class="container">
<div>
컨테이너 1
</div>
<div style="background: lightblue;">
컨테이너 2 - 변화
</div>
<div>
컨테이너 3
</div>
</section>
<script>
const select = document.getElementById("select-fr");
const rootStyle = document.documentElement;
select.addEventListener("change", function () {
rootStyle.style.setProperty("--select-free", select.value);
})
</script>
</body>
Preview :
end
하나의 트랙 안에서, 선택한 2 번째의 엘리먼트가
가지는 fr 단위에 따라서 어떤 변화를 보이는지 볼 수 있을 것이다.
fr 단위를 가지는 엘리먼트들은 남는 영역을 경쟁하므로,
1fr --> 3fr 했을 때, 1fr 에 비해 3 배 커진 것이 아니라,
(3/5)fr 의 크기를 가지게 되었다는 것이 중요하다.
만약 1fr 을 선택했다면, (1/3)fr 의 크기를 가지게 된다.
새로운 함수 minmax 와 함수의 조합
grid 템플릿 선언에서는 인자의 중복 선언으로 개별 열, 혹은 행을 생성 할 수 있게 해 준다.
그러나, 이러한 행동은 코드의 길이를 과도하게 늘릴 뿐만 아니라, 번거롭게 한다.
따라서, 위의 예제에서 repeat CSS Function 을 작성하며 간단하게 선언했다.
그리고 이번에는 새로운 함수, minmax 를 알아 볼 시간이다.
minmax 함수란 무엇인가?
이 함수는 우리가 여태까지
grid-template-columns or rows : 50px 1rem 30% auto min-content max-content fit..
이렇게 하나의 인자에 다양한 단위를 넣을 수 있던 것 처럼,
이 인자 중 하나에 넣을 수 있는 함수값에 해당한다.
minmax(1 번째 인자, 2 번째 인자) 의 형태를 띄게 되는데,
1 번째 인자는 이 "열" 의 "최소 크기" 를 의미하며,
2 번째 인자는 이 "열" 의 "최대 크기" 를 의미한다.
여기서 다양한 조합이 나올 수 있게 되는데,
단순한 50 ~ 100 px 로도 만들 수 있겠지만,
이러한 조합이 가능하다.
minmax(auto, 1fr) : 최소 크기는 "컨텐츠에 따라" 맞추며, 최대 크기는 하나의 Grid Track 이다.
minmax(0, 1fr) : 최소 크기는 컨텐츠 여부에 상관없이 0 길이에 도달 가능하며, 최대 크기는 하나의 Grid Track 을 의미한다.
repeat(12, minmax(0, 1fr)) : 컨테이너의 요소 개수에 따라 분배하며, 트랙에 빈 공간은 없다.
요약하자면 ,
선언된 열, 혹은 행 은 인자에 따라 "최소" 혹은 "최대" 크기를 갖게 되는 함수라는 의미이다.
repeat 인자로 사용될 수 있는 새로운 단어, "auto-fill" "auto-fit"
기존에 repeat 라는 CSS 함수를 사용하기 위해서는,
repeat(반복 횟수, "들어갈 문장"); 형식으로 사용했다.
그런데, 위의 auto-fill, auto-fit 이라는 단어는 "반복 횟수" 인자에 들어 갈 수 있다.
직관적으로 이해되지 않았는데,
이 문장을 몇 번 인자로 넣을 것인지에 대한 대답이, auto-fill, auto-fit 이라는 것에
의문을 가졌다.
그리고 repeat 안에 들어가는 auto-??? 선언은 무조건 하나의 함수가 더 조합되어야 하는데,
바로 minmax 이다.
밑에서 예제로 minmax 를 사용하지 않고 절대단위로 사용했는데, 변화가 없었다.
즉, 이 두 속성, auto-fill, auto-fit 에 대한 이해가 부족했기 때문에
변화를 직접 만들 수가 없었다.
먼저, 설명과 예제를 보자.
이 2 개의 공통점
둘 다, Grid 를 반응형 디자인으로 만들어 준다.
즉, 명시적으로 "횟수" 를 선언하는 게 아니라,
자연스러운 줄넘김을 구사할 수 있는 것이다.
먼저 간단하게 짚고 넘어가자면,
auto-fill : repeat(auto-fill, minmax(200px, 1fr));
auto-fit : repeat(auto-fit, minmax(200px, 1fr));
이러한 방식으로 사용한다.
auto-fill 은, 하나의 트랙이 오버플로 되지 않을 만큼, (padding, gap 더하면 오버플로 값이 나올 수 있으므로,)
채우되, 만약에 Grid Track 에 남는 공간이 있다면,(오른쪽 공간) 이를 냅둔다.
그러나,
auto-fit 은, 하나의 트랙이 오버플로 되지 않을 만큼 추가해 주는 기능을 동일하게 가지지만,
남는 공간을 분배하여 트랙이 꽉 차게 만든다.
왜 minmax 를 사용해야 하는가?
여기서 중요한 게, 두 선언 auto-??? 는 Grid Track 을 인식하고,
"컨테이너의 너비" 는 고려 대상이 아니라는 것이다.
아니 컨테이너의 너비가 결국 Grid Track 의 너비가 아닌가? 라고 생각했는데,
하위 컴포넌트에서 Grid Track 의 최종 길이 (padding 과 gap 등등을 뺀 결과)
를 인식하기 위해서는, 1fr 을 사용해야 한다는 것이다.
이러한 이유로 인해, auto-??? 두 속성은 minmax 함수와 결합되어 사용된다.
:root {
--auto-var : auto-fill;
}
.container {
display : grid;
width : 23rem;
grid-template-columns : repeat(var(--auto-var), minmax(5rem, 1fr));
margin : 1.5rem;
padding : 1.5rem;
gap : 1rem;
background-color : gray;
}
div {
text-align : center;
background : white;
box-shadow : 0 0 0.5rem darkgray;
border-radius : 0.25rem;
}
<body>
값을 바꿔가며 변화를 확인 해 보세요.
<br/>
<select id="auto-select">
<option value="auto-fill">auto-fill</option>
<option value="auto-fit">auto-fit</option>
</select>
<br/>
<section id="container" class="container">
<div>
엘리먼트 1
</div>
<div>
엘리먼트 2
</div>
</section>
<br/>
<button onclick="appending()">엘리먼트 추가</button>
<br/>
<button onclick="initial()">초기화</button>
<script>
const select = document.getElementById("auto-select");
const rootStyle = document.documentElement;
const container = document.getElementById("container");
select.addEventListener("change", function () {
rootStyle.style.setProperty("--auto-var", select.value);
})
// 예제를 위한 추가 함수이므로, 위에 집중하셔도 됩니다.
function appending() {
const newElem = document.createElement("div");
newElem.textContent = "새 엘리먼트";
container.appendChild(newElem);
}
function initial() {
container.innerHTML = "<div>엘리먼트 1</div><div>엘리먼트 2</div>";
}
</script>
</body>
Preview :
end :
auto-fit, auto-fill 은,
하나의 줄에 엘리먼트를 나열하는 상태에서, 아직 컬럼을 넣을 수 있는 상태에서 차이를 보인다.
즉, 이미 하나의 트랙이 채워진 상태에서는, auto-fit, auto-fill 은 동일하게 보인다.
자동 배치
지금까지 볼 수 있었던 배치는, 주로 왼쪽에서 오른쪽으로 향하는 배치 방식이었다.
그런데, Grid 레이아웃에서는, 위아래 작성 후 오른쪽으로 줄넘김하는 것이 가능하다.
row, column 두 개의 방향을 모두 만족하기 위해서는,
들어갈 수 있는 최대 엘리먼트를 인자를 통해 지정 해 주어야 한다.
grid-auto-flow
위의 기능을 가능하게 만들어 주는 것이 바로 이 속성이다.
여기에 row, column 2 개의 값이 들어 갈 수 있다.
row : 여태까지 봤던 것 처럼 엘리먼트가 순서대로 배치됨.
column : 수직으로 먼저 작성되는데, 위아래로 향하며, 수직이 채워지면 그 다음 열에서 시작한다.
역시, 이러한 것도 예제를 통해 직접적으로 변화를 보는 것이 좋다고 생각한다.
:root {
--auto-flow : row;
}
.container {
display : grid;
width : 20rem;
grid-template-columns : repeat(3, 1fr);
grid-template-rows : repeat(3, 1fr);
grid-auto-flow : var(--auto-flow);
margin : 1.5rem;
padding : 1.5rem;
gap : 1rem;
background-color : gray;
}
div {
text-align : center;
background : white;
box-shadow : 0 0 0.5rem black;
border-radius : 0.25rem;
}
<body>
<select id="select-flow">
<option value="row">row</option>
<option value="column">column</option>
</select>
<br/>
<section class="container">
<div>
엘리먼트 1
</div>
<div>
엘리먼트 2
</div>
<div>
엘리먼트 3
</div>
<div>
엘리먼트 4
</div>
<div>
엘리먼트 5
</div>
</section>
<script>
const select = document.getElementById("select-flow");
const rootStyle = document.documentElement;
select.addEventListener("change", function() {
rootStyle.style.setProperty("--auto-flow", select.value);
})
</script>
</body>
Preview :
end
지정, 자동 배치 그리고 트랙 늘리기
이제까지 우리는 Grid Container 가 선언된 엘리먼트가
하위 엘리먼트를 어떻게 정렬하고, 조정하는지 보았다.
지금부터 작성되는 것은, Grid Container 바로 하위에 존재하는 grid-item 들이 선언하는 항목이다.
Flexbox 즉, display : flex 가 선언되었을 때도 flex-item 들이 존재했는데,
여기서도 display : grid 가 선언되었을 때, grid-item 에 선언하는 속성들이 존재했다.
컨테이너 하위에 존재하는 요소에 이러한 항목을 선언 할 수 있다.
먼저 보고 세부 내용을 보도록 하자.
grid-item 에서 선언 할 수 있는 속성들
grid-column-start : 이 엘리먼트의 Grid Column Line 시작 지점
grid-column-end : 이 엘리먼트의 Grid Column Line 끝 지점
grid-row-start : 이 엘리먼트의 Grid Row Line 시작 지점
grid-row-end : 이 엘리먼트의 Grid Row Line 끝 지점
grid-column : Grid Column Line 의 시작과 끝을 모두 선언하는 단축 속성
grid-row : Grid Row Line 의 시작과 끝을 모두 선언하는 단축 속성
만약에 같은 열, 혹은 행 에 대해서 시작과 끝이 모두 선언되었을 때,
그 간격이 1 이상이라면, span(확장) 을 의미한다.
모든 속성에는 span 이 인자로 들어갈 수 있다.
EX - span 2 --> 선언된 속성에 따라 해당 방향으로 2 칸으로 확장.
그리고 꼭 기억해야 할 게, 단축 속성인 grid-column, grid-row 는,
시작과 끝을 / 로 나눈다. 이는 나눗셈으로 생각해서는 안되고,
시작과 끝을 나누는 델림(delim) 으로 해석해야 한다.
한번, 간단하게 이 예제가 적용된 예시를 보자.
:root {
--select-row : 1;
--select-col : 1;
}
.container {
display : grid;
width : 20rem;
grid-template-columns : repeat(5, 1fr);
grid-template-rows : repeat(5, 1fr);
margin : 1.5rem;
padding : 1.5rem;
gap : 1rem;
background-color : gray;
}
div {
text-align : center;
background : white;
box-shadow : 0 0 0.5rem black;
border-radius : 0.25rem;
}
div.span-row {
grid-column : auto / span var(--select-row);
}
div.span-col {
grid-row : auto / span var(--select-col);
}
<body>
grid-column 적용 엘리먼트의 크기를 조정 해 보세요.
<br/>
<select id="select-row-span">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<br/>
grid-row 적용 엘리먼트의 크기를 조정 해 보세요.
<br/>
<select id="select-col-span">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<section class="container">
<div>
기본 엘리먼트
</div>
<div class="span-row">
grid-column 적용 엘리먼트
</div>
<div class="span-col">
grid-row 적용 엘리먼트
</div>
</section>
<script>
const selectRow = document.getElementById("select-row-span");
const selectCol = document.getElementById("select-col-span");
const rootElem = document.documentElement;
function changeVal (event) {
const id = event.target.id;
if(id == "select-row-span") {
rootElem.style.setProperty("--select-row", event.target.value);
} else {
rootElem.style.setProperty("--select-col", event.target.value);
}
}
selectRow.addEventListener("change", changeVal);
selectCol.addEventListener("change", changeVal);
</script>
</body>
preview :
end
위에서 선언된 grid-column, grid-row 는, 복합 인자를 받는다.
여기서 첫 번째 인자로 auto 가 들어갔는데, 이 의미는
"원래 하던 대로 행동" 하겠다는 것이다. 유동적으로 들어가겠다는 의미이다.
그리고 나는 끝을 넣지 않고, span <숫자> 를 이용해서 엘리먼트를 넓히는 예제를 선택했다.
시작과 끝을 지정하는 것을 하기 전에, 늘리는 예제를 먼저 배우는 것이 나중을 위해서 더 쉽겠다는 생각이 들었다.
만약에 위의 예제를 단순한 끝과 마지막으로 표현했다면,
grid-column : 1 / 3
grid-row : 2 / 4
이러한 형태로 나타 낼 수도 있었다.
즉, 위의 내용으로 알 수 있는 것은,
위에서 선언했던 grid-item 에서 사용 할 수 있는 속성들로,
Grid Track 들을 "걸쳐서" 보여 줄 수 있다는 것이다.
이는 Grid Area 이기도 하다.
각 방향에서의 시작과 끝을 마음대로 조정 해 보자
위에서는 단순한 열, 행 방향으로의 확장을 보았다.
이번에는 5 x 5 크기의 컨테이너에서, 하나의 엘리먼트가 어떻게 확장될 수 있는지,
그에 대해서 예제를 통해 알아보자.
내가 의도하는 것은,
시작 지점과 끝 지점을 직접 조정하면서 어떻게 늘려질 수 있는지 알아보기
시작과 끝은 같거나, 역으로 성립되면 기본 크기인 1 을 가진다. - css 문법에 어긋나는 행동
:root {
--select-row-start : 1;
--select-col-start : 1;
--select-row-end : 2;
--select-col-end : 2;
}
.container {
display : grid;
width : 20rem;
height : 20rem;
grid-template-columns : repeat(5, 1fr);
grid-template-rows : repeat(5, 1fr);
margin : 1.5rem;
padding : 1.5rem;
gap : 1rem;
background-color : gray;
}
div {
text-align : center;
background : white;
box-shadow : 0 0 0.5rem black;
border-radius : 0.25rem;
}
div.span-elem {
grid-column-start : var(--select-col-start);
grid-column-end : var(--select-col-end);
grid-row-start : var(--select-row-start);
grid-row-end : var(--select-row-end);
}
<body>
grid-column-start : <select id="select-col-start">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select><br/>
grid-column-end : <select id="select-col-end">
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select><br/>
<br/>
grid-row-start : <select id="select-row-start">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select><br/>
grid-row-end : <select id="select-row-end">
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
<section class="container">
<div class="span-elem">
엘리먼트 증감 해 보기
</div>
</section>
<script>
const selectColStart = document.getElementById("select-col-start");
const selectColEnd = document.getElementById("select-col-end");
const selectRowStart = document.getElementById("select-row-start");
const selectRowEnd = document.getElementById("select-row-end");
const rootElem = document.documentElement;
selectColStart.addEventListener("change", changeRange);
selectColEnd.addEventListener("change", changeRange);
selectRowStart.addEventListener("change", changeRange);
selectRowEnd.addEventListener("change", changeRange);
function changeRange(event) {
rootElem.style.setProperty(`--${event.target.id}`, event.target.value);
}
</script>
</body>
Preview :
end
위에서,
각 시작과 끝을 2, 5 로 설정하면,
정 중앙에 엘리먼트를 위치하게 만들 수도 있다.
위에서 사용한
grid-column-start
grid-column-end
grid-row-start
grid-row-end
이 4 개의 속성을 항상 같이 사용해야 하는 것은 아니다.
해당 Grid-Item 이 행, 열 이 2 개의 상황에서,
어떤 위치부터 시작해야 하는지, 필요한 속성을 빼서 사용하면 된다.
각각의 속성은 하나씩 선언될 수 있으며, span 이라는 키워드와 함께 사용이 가능하다.
예를 들어, column, row 던지,
EX - grid-column-end : span 2 라면,
시작 부분에서 2 개의 트랙을 가지며,
혹은 끝 부분에서 반대로 2 개의 트랙을 가진다는 의미가 된다.
grid-area 사용하기
바로 위의 방식, grid-xxx-start or end 키워드로 트랙을 걸쳐 생성되는 엘리먼트를 생성할 수 있지만,
헤더, 사이드바, 컨텐츠, 푸터 의 방식으로 직관적으로 나눌 수 있다.
단, 직접적으로 문자열로 선언 해 주어야 한다.
grid-area 는, grid-item 에 해당하는 엘리먼트에서 선언하는 속성이다.
grid-template-areas 사용하기
grid-area 라는 직관적인 속성을 grid-item 에서 사용하기 위해서는,
grid-template-areas 를 Grid Container 에서 선언 해 주어야 한다.
이게 어떻게 직관적으로 사용 가능한 grid container 가 되냐면,
.container {
display : grid;
width : 18rem;
grid-template-columns : repeat(1, 3fr);
grid-template-areas :
"header header header"
"sidebar content content"
"sidebar footer footer"
}
.header {
grid-area : header;
}
.sidebar {
grid-area : sidebar;
}
.content {
grid-area : content;
}
.footer {
grid-area : footer;
}
.container 의, grid-template-area 를 살펴보자.
보면, 문자열로 "직접" 영역을 지정해 놓은 것을 볼 수 있다.
여기서 grid-area : 위에서 선언한 나의 속성; 으로 쉽게 grid-area 를 만들 수 있다.
이 주제를 마무리하며
이번에는 Flexbox 에 이은, Grid 사용법을 알아보았다.
만약에 Grid 레이아웃이 존재하지 않는다고 해도, 이 표현이 불가능한 것은 아니다.
그러나, 2 차원 나열 레이아웃에서 엘리먼트의 영역 분할,
혹은 컨텐츠에 따른 영역 분할을 쉽고 동적으로 분할하도록 도와준다는 점에서 호감이 갔다.
Grid 에 대해서 더 이해하고 싶다면.
Grid 에 대한 깊은 이해를 하고 싶다면,
MDN - 그리드
CSS-TRICKS - CSS Grid Layout Guide
위의 두 문서를 같이 본다면 추가적인 정보를 얻을 수 있다.
또한, Grid Layout 를 직접 작성하여 배우는 게임이 존재하는데,
Grid Garden 이라는 게임이다.
나는 예제를 전부 직접 작성하며 공식문서를 보고 익힌 것 보다 더 높을 수 밖에 없다.
이 링크를 통해 끝까지 풀어 Grid 레이아웃을 익히는 것을 추천한다.
Grid Garden - 게임
참조 사이트
web.deb 사이트- 그리드
https://web.dev/learn/css/grid?hl=ko
MDN 문서 - Grid
https://developer.mozilla.org/ko/docs/Glossary/Grid
Grid Garden - 게임(직감을 발달시키기에 굉장히 유용함)
https://cssgridgarden.com/#ko
MDN 문서 - Grid Layout
https://developer.mozilla.org/ko/docs/Web/CSS/CSS_grid_layout
CSS-TRICS - CSS Grid Layout Guide
https://css-tricks.com/snippets/css/complete-guide-grid/
CSS Grid 레이아웃은 무엇이고, 어떻게 사용할까? (예제와 함께 알아보기)