CSS 플렉스박스(Flexbox) - 3. 레이아웃 만들기

플렉스박스는 유연한 만큼 레이아웃을 만드는 방식도 자유도가 높은 편입니다.

사용하는 속성에 따라 빠르고 간편하게 만들 수도, 복잡한 레이아웃을 만들 수도 있습니다.

1. flex-wrap 으로 레이아웃 만들기

flex-wrap은 레이아웃 배치를 하는데 필요한 많은 부분을 자동화 해줍니다. 플렉스박스 아이템의 너비, 또는 한 행에 표시할 아이템 갯수만 정해주면 여백과 정렬은 자동으로 처리됩니다.

그러나 flex-warp 속성만으로는 부족해서, 여백의 예외처리와 반응형 레이아웃에 대한 부분은 사용자가 직접 구현해야 합니다. 그리고 이런 부분들은 블록 태그(<DIV>)로 레이아웃을 구현할 때와 같은 방법을 사용해야 합니다.

다음과 같은 HTML 레이아웃에

<div class="flexbox content3">
    <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>

flex-wrap 으로 레이아웃을 만들면

.content{
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    width: 600px; /* 너비 값 지정 */
}
.content .item{
    margin-bottom: 1.5em;
}
.content .item:nth-last-child(1){
    margin-bottom: 0; /* 마지막 아이템의 밑 마진을 없앰 */
}
/* 아이템들의 너비를 지정 */
.content .item:nth-child(1){width: 25%;}
.content .item:nth-child(2){width: 70%}
.content .item:nth-child(3){width: 100%;}
.content .item:nth-child(4){width: 70%}
.content .item:nth-child(5){width: 25%}
.content .item:nth-child(6){width: 100%}

다음과 같은 레이아웃을 만들 수 있습니다.

flex-wrap 속성으로 만든 레이아웃

이렇게 만든 레이아웃은 너비가 600px 로 만든 것입니다. 아이템 너비를 % 값으로 지정했으므로 브라우저 너비에 반응하도록 플렉스박스 너비를 100%로 해서 브라우저 너비만큼 플렉스박스가 채워지도록 하면, 다음처럼 한 행에 아이템이 2개인 경우에는 아이템 사이에 여백이 벌어지는 문제가 생깁니다.

아이템간 사이가 벌어진 레이아웃

이 문제를 해결하려면 밑에서 배우게 될 flex-grow 속성을 사용하거나, 줄바꿈 아이템을 이용해 레이아웃을 만들어야 합니다.

아니면 너비에 따라 미디어쿼리를 적당히 적용해 여백 너비를 적당히 조절해야 합니다.

아이템1 너비를 픽셀 크기로 완전히 고정하고 아이템2 너비가 브라우저 너비에 따라 가변 조절되도록 하는 레이아웃은 flex-wrap 만으로는 구현할 수 없습니다.

2. flex-grow 로 레이아웃 만들기

flex-grow 속성을 이용하면 가변 너비에 대한 대응을 쉽게할 수 있기 때문에 반응형 레이아웃을 빠르게 만들 수 있습니다.

flex-grow 속성은 플렉스박스 너비에서 한행에 표시되는 아이템들의 너비 합을 뺀 나머지 너비를 아이템들에 분배하는 비율을 말합니다. 기본 값은 0이며, 0은 남은 너비가 분배되지 않고, 처음 크기를 그대로 유지합니다.

예를 들어 사이드바 크기는 280px로 고정하고, 본문 영역 너비가 브라우저 너비에 따라 자동으로 변하도록 하려면 사이드바의 flex-grow 속성 값은 0으로, 본문의 flex-grow 속성 값은 1로 설정하면 됩니다.

HTML 코드가 다음과 같은 플렉스박스 코드를 기준으로 확인해보겠습니다.

<div class="flexbox content">
    <div class="item"><span>로고</span></div>
    <div class="item"><span>타이틀</span></div>
    <div class="item"><span>헤더</span></div>
    <div class="item"><span>메인</span></div>
    <div class="item"><span>사이드바</span></div>
    <div class="item"><span>푸터</span></div>
</div>

먼저 "flex-grow: 1;" 속성을 아이템에 부여해서 브라우저 너비가 변하는데에 따라 변경되도록 합니다.

.content{
    width: 100%;
    max-width: 1200px;
    display: flex;
    flex-wrap: wrap;
}
.content .item{
    margin-bottom: 1em;
    height: 50px;
    display: flex;
    flex-grow: 1;
}
.content .item:nth-last-child(1){/* 마지막 푸터 밑의 마진은 제거 */
    margin-bottom: 0;
}

아이템들의 너비 값이 없기 때문에 아이템 안의 컨텐츠 영역에 맞춰진 크기에 남는 공간이 똑같이 분배되면서 다음과 같은 레이아웃이 만들어집니다.

헤더와 푸터는 너비를 100%로 해서 한행을 모두 차지하도록 하고, 로고와 사이드바는 고정 크기를 가지도록 280px로 너비를 설정합니다.

로고와 사이드바는 브라우저 너비가 바뀌어도 고정 크기를 가져야 하기 때문에 "flex-grow: 0;" 속성을 부여합니다.

아이템들에 CSS를 추가로 정의하면 다음과 같습니다.

.content .item:nth-child(1){width: 280px; margin-right:1.25em;flex-grow: 0;}
.content .item:nth-child(3){width: 100%;}
.content .item:nth-child(4){margin-right:1.25em;}
.content .item:nth-child(5){width: 280px; flex-grow: 0;}
.content .item:nth-child(6){width: 100%;}
반응형 레이아웃으로 완성

웹브라우저 크기를 변경해서 "타이틀"과 "메인" 아이템이 크기가 변하는지 확인합니다.

3. 플렉스박스 중첩으로 컨텐츠 수직 정렬하기

레이아웃을 만들 때는 필요가 없지만, 아이템 안의 컨텐츠 내용을 수직 정렬하려면 플렉스박스를 중첩 해서 적용하면 됩니다.

앞서 2번의 flex-grow 속성으로 만든 HTML 코드를 보면 아이템안의 텍스트 내용을 "<SPAN>" 태그로 감싸 놓은 것은 수직 정렬을 위해 아이템을 다시 플렉스박스로 정의하기 위해서입니다.

아이템에 "display: flex;" 속성을 추가해서 아이템 안의 "<SPAN>" 태그가 아이템 안의 아이템이 되도록 합니다.

그리고 아이템에 "align-items: center;" 속성을 추가하면 "<SPAN>" 태그 안의 컨텐츠가 수직 정렬이 됩니다.

그리고, "<SPAN>" 태그에는 너비를 100%로 지정해서 컨텐츠 내용이 왼쪽으로 쏠리지 않도록 해야 합니다.

.content .item{
    margin-bottom: 1em;
    height: 50px;
    display: flex; /* 아이템을 플렉스박스로 선언 */
    align-items: center; /* 내부 아이템인 <SPAN> 태그를 수직 정렬 */
    flex-grow: 1;
}
.content .item span{
    width: 100%; /* 너비를 100%를 지정해서 컨텐츠가 내부 아이템이 왼쪽으로 쏠리지 않도록 함 */
}

수직 정렬을 하면 다음과 같이 적용됩니다.

컨텐츠 내용 수직 정렬

4. 줄바꿈 아이템으로 레이아웃 만들기

줄바꿈 아이템이라고 특별한 것은 아니고 레이아웃을 만드는 트릭 중의 하나입니다.

"flex-grow: 100%;" 인 높이갚 0의 아이템을 중간에 끼워 넣어 강제로 아이템들의 배치 영역을 구분 함으로써 아이템 크기를 자동으로 조절하는 방법입니다.

"flex-grow: 100%;" 인 높이갚 0의 아이템을 줄바꿈 아이템이라고 하고, 나머지 일반 아이템들은 "flex-grow: 1;" 을 지정해서 너비가 자동으로 늘어나도록 하면 됩니다.

예를 들어 다음과 같이 줄바꿈 아이템과 아이템 속성을 부여합니다.

눈여겨 봐야할 것은 아이템들에 너비를 정하지 않은 것입니다. 줄바꿈 아이템으로 행을 구분할 경우 한행에 포함된 아이템들은 "flex-grow: 1;" 속성의 영향을 받아 같은 너비를 가지도록 분배됩니다.

너비 값이 없고 flex-grow 가 모두 1이므로 줄바꿈 아이템으로 나눌 때까지 한줄로 표시되는 아이템들은 같은 너비를 가지고 분배됩니다.

줄바꿈 아이템은 ".spacer" 클래스로 정의합니다.

.content{
    width: 100%;
    max-width: 1200px;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;/*수평정렬 배분*/
}
.content .item{
    margin: 1em;
    height: 50px;
    flex-grow: 1;
}
.spacer{
    flex-basis: 100%;
    height: 0;
}

HTML 코드는 다음처럼 줄바꿈 아이템으로 나눌 행들을 구분합니다.

<div class="content">
    <div class="item"><span>로고</span></div>
    <div class="item"><span>타이틀</span></div>
    <div class="spacer"></div>
    <div class="item"><span>헤더</span></div>
    <div class="spacer"></div>
    <div class="item"><span>메인</span></div>
    <div class="item"><span>사이드바</span></div>
    <div class="spacer"></div>
    <div class="item"><span>푸터</span></div>
</div>

적용을 하면 다음처럼 아이템들이 줄바꿈 아이템을 기준으로 나누어지고, 한 행의 아이템들은 같은 크기를 가지게 됩니다.

줄바꿈 아이템으로 행을 구분

한 행에 2개 이상의 아이템이 위치하면 같은 크기로 배분되는 것은 "grid-grow" 속성에 의한 것입니다.

우리가 원하는 것은 로고와 사이드바의 너비가 고정된 크기로 고정되고, 메인과 타이틀 영역이 브라우저 너비에 따라 변하는 반응형 레이아웃입니다.

다음과 같이 로고와 사이드바 아이템의 너비를 CSS로 고정해줍니다.

.content5 .item:nth-child(1){width: 280px; flex-grow:0;}
.content5 .item:nth-child(7){width: 280px; flex-grow:0;}

너비를 따로 설정하는 아이템을 위한 CSS로 2줄 밖에 안되지만, 많은 기능을 합니다.

로고와 사이드바의 너비를 280px로 설정하고, "flex-grow: 0;" 속성을 부여해 플렉스박스 크기가 반응형으로 변해도 로고와 사이드바의 크기는 변하지 않고 고정됩니다. 대신 타이틀과 메인 아이템 영역이 늘어나거나 줄어들면서 영역을 채웁니다.

너비를 따로 설정할 아이템만 너비 값을 지정하면 되기 때문에 최소의 CSS 작성으로 반응형 레이아웃을 만들 수 있는 장점이 있습니다.

로고와 사이드바 아이템이 고정된 가변 레이아웃

이제 미디어쿼리로 모바일 화면에서는 로고와 타이틀, 그리고 메인과 사이드바가 1행에 1 아이템씩 표시되도록 해야 합니다. 다음과 같이 로고와 사이드바 아이템의 너비와 flex-grow 속성을 변경하면 됩니다.

@media screen and (max-width:639px){
    .content5 .item:nth-child(1), .content5 .item:nth-child(7){
        width: 100%;
        flex-grow: 1;
    }
}

웹 브라우저 너비를 미디어쿼리 설정 너비보다 작게 변경하면 다음과 같이 레이아웃이 변경됩니다.

모바일 반응형 레이아웃