CSS 그리드(Grid) - 3. 열 구획 속성(Attribute)과 메서드(Method)

그리드는 레이아웃을 동적으로 만들고 영역을 배치할 수 있는 다양한 방법을 제공합니다.

그중 가장 대표적인 방법이 앞서 배웠던 영역 매핑(Area mapping)을 이용해 구획 영역을 합치는 방법입니다.

우리가 만드는 그리드 레이아웃은 웹페이지의 전체 구조를 생성하는 것 일수도 있지만, 배너나 아이템 목록 처럼 조금 더 단순하고 반복되는 항목들의 나열일 수도 있습니다.

그리드 속성, 또는 속성 값 중 열을 기준으로 구획하는 속성과 메서드들을 이용하면 이런 반복되는 요소들을 일괄로 배치하고 관리할 수 있습니다.

CSS 그리드에서 사용하는 메서드(Method)는 일반적인 스크립트 언어의 메서드와는 약간 다릅니다.

엑셀의 내장 함수처럼 아주 단순하고 정해진 기능 만을 할 수 있기 때문에 아주 아주 단순한 조건, 또는 반복 처리만을 할 수 있습니다. CSS 내장 메서드(Method), 또는 내장 함수(Function)라고도 할 수 있으며, 이 강의에서는 메서드로 용어를 통일해서 사용합니다.

메서드를 사용하는데 익숙하지 않다면, 괄호 안에 원하는 값을 넣을 수 있는 CSS 속성으로 이해를 해도 됩니다. CSS의 메서드 사용 방법이 대부분 정해진 규칙에 따라서만 사용할 수 있도록 파라메터 갯수까지 정해져 있기 때문에 메서드 작성에 고민을 할 여지가 없습니다.

사용 메서드도 "repeat()", "minmax()" 2개 밖에 없으므로 이 두 메서드에 대해서만 알면 됩니다.

그리드로 레이아웃을 만들 때의 기본은 몇 개의 열로 나누는지를 결정하는 것에서 부터 시작합니다.

열을 나누는 그리드 속성인 "grid-template-columns" 속성을 이용해 열을 다양한 방법으로 표현하며, 가장 많은 속성 값과 메서드를 열 속성 정의에 사용합니다.

열을 기준으로 구획을 나누는 다양한 속성과 메서드들을 하나씩 알아보겠습니다.

1. 열 반복 처리 - repeat()

반복할 숫자와 반복 회수를 표기해 같은 숫자를 여러번 나열해 많은 열을 선언하는 수고를 덜어주는 메서드입니다.

다음처럼 "반복 횟수"와 "열 너비" 숫자 값으로 표현합니다.

grid-template-columns: repeat(반복 횟수, 열 너비);

grid-template-columns: repeat(8, 1fr);

이렇게 표시한 반복 값은

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

과 같은 표현이 됩니다. repeat() 메서드를 사용하면 반복되는 많은 열의 너비 표현을 극단적으로 단순화 할 수 있습니다. 열 갯수가 아주 많은 테이블 같은 내용을 표시하는 경우 필수 사용 메서드입니다.

repeat() 메서드는 다음과 같이 다른 너비 표현과 조합해서 사용할 수도 있습니다.

grid-template-columns: 2fr repeat(3, 1fr) 2fr;

이 열 영역 선언은

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

과 같습니다.


또는, 좌우의 너비를 픽셀 값으로 정의해 고정하고 그 안의 열들만 너비를 분배해서 같은 크기가 되도록

grid-template-columns: 150px repeat(3, 1fr) 120px;

과 같은 표현도 가능합니다.

이렇게 열 너비를 표현하면 그리드 전체 너비가 줄어들어도 좌우 끝의 열은 너비가 고정되고, 사이에 있는 열들만 같은 비율로 너비가 줄어들면서 전체 레이아웃이 유지됩니다.

2. 열 너비 제한 조절 - minmax()

열의 너비 범위를 정해서 그 범위 안에서만 너비가 늘어나거나 줄어들도록 열의 너비 범위를 제한하는 메서드입니다.

열 안에 표시할 컨텐츠가 최소 너비를 보장해야 하는 경우, 또는 컨텐츠 좌우의 여백이 너무 많아지는 것을 원하지 않는 경우 다음처럼 minmax() 메서드로 열(셀)의 너비 범위를 제한할 수 있습니다.

grid-template-columns: minmax(최소너비, 최대너비);

"최소너비", "최대너비" 값은 사용 가능한 CSS 숫자 단위는 모두 사용할 수 있으며, "fr" 단위도 사용할 수 있습니다.

특히 "최대너비" 값을 "fr" 단위로 사용하면 그리드 너비 변화에 따라 열의 너비가 비율에 맞춰 자동으로 늘어나기 때문에 열 너비를 그리드 너비에 맞춰 채울 수 있는 장점이 있습니다.

다음처럼 minmax() 범위를 제한하면

grid-template-columns: 300px minmax(150px, 1fr);

2개의 열을 가진 그리드가 되고, 첫 번째 열은 300px로 고정되지만, 두 번째 열은 최소 150px에서 최대 "(그리드 전체 너비 - 300px - (열 갯수 - 1 * 갭 너비))" 너비가 됩니다.

계산식이 복잡해 보이지만 풀어서 설명하면 두 번째 열은 첫 번째 열의 너비와 갭 너비를 그리드 너비에서 제외한 나머지 영역을 모두 채우지만 최소 크기는 "150px" 로 제한됩니다.

남은 그리드 영역이 "150px" 보다 작아지면, "150px"로 두 번째 열 너비가 고정되고, 그리드 오른쪽 바깥으로 두 번째 열 영역이 빠져나오게 됩니다.

minmax() 메서드를 사용할 때는 "px", "em" 같은 고정 크기를 사용해 너비 범위를 정확하게 지정할 수도 있지만, 반응형 웹을 위해 동적으로 크기가 변하도록 "fr" 단위로 크기를 정하는 것이 좋습니다.

비율에 맞춰 크기가 조정되도록 "%", "fr" 단위를 사용더라도 다음처럼 minmax() 범위의 "최소크기" 에 "%" 나 "fr" 단위를 적용하면 

grid-template-columns: 300px minmax(50%, 100%);

두 번째 열은 그리드 전체 너비의 최소 50%에서 최대 남은 영역 전체를 사용하기 때문에 잘 동작하지만, 다음과 같이 repeat() 메서드로 반복 열을 만들면 최소 크기 문제가 생기므로, 최소 크기를 상대 크기 단위로 사용할 때는 주의해야 합니다.

열의 최소 크기 50% 는 그리드 전체 너비의 50% 가 됨

minmax() 메서드의 최소 크기에 사용하는 "%" 단위는 그리드 전체 너비를 기준으로 하기 때문에 단일 열을 제외한 경우에는 사용하지 않는 것이 좋습니다.

앞서 설명한 열 반복 메서드인 repeat() 과 minmax() 메서드를 함께 사용하면 최소 크기와 최대 크기가 제한된 여러 개의 그리드 열을 한꺼번에 만들 수 있습니다.

다음과 같이 그리드 열을 정의하면

grid-template-columns: repeat(3, minmax(100px, 1fr)) 150px;

마지막 열은 150px 로 고정되고, 앞의 3열은 최소 100px 에서 최대 "((그리드너비 - 150px - (열갯수 - 1) * 갭너비) / 3)px" 까지 너비가 넓어집니다. 계산 방식이 다소 복잡해 보이지만, 그리드 전체 너비에서 고정된 크기인 첫 번째 열의 너비와 갭 너비 합을 뺀 후 반복 열 갯수(3개)로 나누면 가변 열 1개의 너비가 됩니다.

이 열 설정은 다음과 같이 펼칠 수 있습니다.

grid-template-columns: minmax(100px, 1fr) minmax(100px, 1fr) minmax(100px, 1fr) 150px;

여러 컬럼에 minmax() 메서드를 적용하면, 그리드 너비 변화에 따라 다음과 같이 같이 여러 컬럼이 자동으로 너비가 조절됩니다

3. 열 갯수 자동 맞춤 - auto-fit, auto-fill

각각의 그리드 셀 안에는 표시할 HTML 컨텐츠가 들어가게 되므로 언제나 컨텐츠에 맞춰서 그리드 너비가 조정되도록 하는 것이 좋습니다. 

minmax() 메서드를 사용하면 표시되는 컨텐츠의 내용에 맞춰 최소/최대 값의 한계를 정해줄 수 있습니다.

이렇게 가변 범위를 지정하는 방식은 어디까지나 표시될 컨텐츠의 너비를 예측해서 범위를 정하는 것이기 때문에 실제 컨텐츠의 너비에 100% 맞출 수는 없습니다. repeat() 와 minmax() 를 사용해도 결국 한 행에 표시되는 열의 갯수가 변하는 것은 아니기 때문에 그리드가 좁아지거나 넓어지면서 정해놓은 범위의 한계를 넘게되면 컨텐츠 내용에 여백이 너무 많거나, 여러 줄로 컨텐츠가 표시되면서 레이아웃이 그리 보기 좋지 않게 변합니다.

미디어 쿼리를 사용하면 반응형 레이아웃을 만들 수 있으므로 미디어쿼리로 그리드 너비에 대한 대응을 할 수 있어 이런 문제는 해결되지만, 열의 갯수가 아주 많아지면 repeat()와 minmax() 메서드로 브라우저 크기별로 레이아웃을 관리하는 것이 꽤나 번거로운 일이 됩니다.

그리드 속성 값 중에는 웹 브라우저 너비에 맞춰서 열의 너비와 갯수가 자동으로 조절되도록 하는 기능을 하는 속성 값인 "auto-fit" 과 "auto-fill" 이 있습니다.

특히 이 속성 값의 좋은 점은 한 행에 표시되는 열의 갯수가 그리드 너비에 맞춰 자동으로 변하기 때문에 미디어 쿼리를 작성해 그리드 너비 별로 따로 처리를 해줄 필요가 없습니다.

특별한 속성 값이고 자주 사용되므로 minmax() 메서드와 함께 꼭 알아두어야 합니다.

지금까지 우리가 만들어 왔던 그리드는 다음 처럼 열의 갯수가 정해져 있습니다.

열의 크기는 다양한 크기로 변경할 수 있지만, 열의 갯수는 그리드 너비와 무관하게 항상 고정입니다.

grid-template-columns: repeat(4, minmax(250px, 1fr));

이 그리드 "repeat()" 메서드의 첫 번째 파라메터인 "열 갯수 숫자" 대신 "auto-fit" 을 입력하면 다음처럼 한 행에 3열을 가지는 그리드가 완성됩니다.

grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

완성된 그리드는 다음과 같습니다.

.grid{
    max-width: 800px;
    display: grid;
    padding: 10px;
    box-sizing: border-box;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 10px;
}

한 행이 3열이 들어가게 된 것은 최소 열 너비가 "250px" 인데, 그리드 너비를 최대 너비를 800px로 제한했기 때문에 한 행에 4개의 열이 들어갈 수는 없어서 3개의 열이 한 행에 들어가게 된 것입니다. 최대 값은 1fr로 동일하므로 남는 영역은 갭 10px를 제외하고 모든 열에 동일하게 분배됩니다.

여기까지는 우리가 알아왔던 그리드와 동일합니다.

웹 브라우저 너비를 800px 밑으로 줄여서 계속 줄이다 보면 어느순간 열 갯수가 2개로 줄어들고, 다음처럼 열 너비가 거기에 맞춰서 넓어집니다.

열 갯수가 자동으로 줄어드는 그리드

정확히 "790px" 미만으로 웹브라우저 너비가 줄어들면 2열로 변경됩니다.

최소 크기인 "250px" 3개(750px)와 갭 2개(20px), 그리고 좌우 패딩 20px 들어갈 수 있는 최소 너비가 "790px" 이기 때문에 이 미만으로 줄어들 면 3열을 넣을 수 없기 때문에 2열과 갭 1개 구성으로 바뀌고, 열 너비가 거기에 맞춰 늘어나는 것입니다.

"auto-fit"의 이런 효과는 마치 미디어 쿼리로 다음과 같이 반응형 처리를 한 것과 같아집니다.

@media screen and (max-width: 789px){
  .grid{
	  grid-template-columns: repeat(2, minmax(250px, 1fr));
  }
}

그리드 속성 값 "auto-fit" 하나만으로 반응형 그리드 구현에 필요한 모든 처리를 자동으로 처리할 수 있습니다.

"auto-fit" 을 사용할 때는 주의할 점이 있습니다.

고정 크기 열이 필요한 경우, 다음과 같이 고정 크기 열을 선언해서 조합할 수도 있습니다.

grid-template-columns: 180px repeat(auto-fit, minmax(250px, 1fr));

단, 이렇게 선언한 그리드는 고정된 첫 번째 열에 들어가는 컨텐츠가 그리드 너비에 따라서 계속 변하게 되는 단점이 있습니다.

1열이 고정 너비인 자동 열 갯수 조절 그리드

"auto-fit" 외에 유사한 속성 값으로 "auto-fill" 속성 값이 있습니다.

"auto-fit" 과 "auto-fill" 속성 값은 기본적으로는 같은 기능을 하지만, 세부적으로는 다음과 같은 차이가 있습니다.

auto-fit 최소 너비를 기준으로 열을 채우고 남는 공간은 분배해서 열을 넓힙니다. 아이템 갯수가 부족한 경우 열을 넓혀서 빈공간을 채웁니다.
auto-fill 최소 너비를 기준으로 채울 수 있는 최대 갯수를 무조건 채웁니다. 채울 수 있는 아이템 갯수가 부족할 경우 빈 공간으로 셀 영역을 채웁니다.

두 속성 값은 그리드 아이템들의 최소 너비 합이 첫 행을 다 채우지 못하고 공간이 남을 때 처리하는 방법에서 극적인 차이가 나타나며, 그 외에는 같은 방식으로 동작합니다.

다음 같은 6개의 열을 가진 그리드는 

<div class="coloring grid">
    <div class="item">아이템1</div>
    <div class="item">아이템2</div>
    <div class="item">아이템3</div>
    <div class="item">아이템4</div>
    <div class="item">아이템5</div>
    <div class="item">아이템6</div>
</div>

"auto-fit" 은 다음처럼 남는 공간을 존재하는 열들에 분배해서 채우지만

"auto-fill"은 남는 공간을 빈 열로 채우기 때문에 오른쪽에 빈 공간이 남게 됩니다.

말도 안되게 환상적인 속성 값이지만, 좋은 만큼 제약도 큽니다.

앞서 잠깐 언급했지만 열의 갯수가 그리드 너비에 따라 변하기 때문에 고정 크기를 가지는 열을 선언해도 실제 해당 열에 들어가는 컨텐츠 내용이 계속 바뀌기 때문에 고정 크기 열 너비를 선언하는게 의미가 없습니다.

"auto-fit" 과 "auto-fill" 속성 값은 같은 타입의 컨텐츠를 목록으로 표현할 때만 제한적으로 사용하는 것이 좋습니다.

또, 셀의 갯수가 배수로 맞아 떨어지지 않는 경우 그리드 끝 행에는 빈 셀 공간이 생기는 현상이 있기 때문에 레이아웃을 만드는데는 부적합하며, 같은 종류의 아이템을 같은 너비로 순서대로 나열하는 경우에만 제한적으로 유효한 속성입니다.