기본적으로 콘텐츠의 흐름은 좌에서 우로, 위에서 아래로 흐릅니다. 먼저 수직으로 분할하고, 수직으로 분할된 div에서 height 속성을 이용해 수평 분할을 하면 큰 도움이 됩니다.
여러분들이 예를 들어 VS Code와 비슷한 레이아웃의 앱을 만든다고 가정해봅시다.
위와 같은 앱은 큰 틀에서 추상화하면, 다음과 같이 영역을 나눌 수 있습니다.
앞서 언급한, 수직 분할 그리고 그 후 수평 분할을 생각해보면 다음과 같이 레이아웃 구성이 가능할 것입니다.
<div id="container">
<div class="col w10">
<div class="icon">아이콘 1</div>
<div class="icon">아이콘 2</div>
<div class="icon">아이콘 3</div>
</div>
<div class="col w20">
<div class="row h40">영역1</div>
<div class="row h40">영역2</div>
<div class="row h20">영역3</div>
</div>
<div class="col w70">
<div class="row h80">영역4</div>
<div class="row h20">영역5</div>
</div>
</div>
참고: w70이나, h40과 같은 클래스 이름의 구현은 실제로 다음과 같을 것입니다. (이와 같이 클래스 이름과 구현을 1:1로 일치시키는 CSS 작성 기법을 Atomic CSS 방법론이라고 합니다. 클래스 이름 짓는 법에 대한 더 자세한 이야기는 이 링크를 참고하세요. 지금 당장은 몰라도 됩니다!)
.w70 { width: 70%; }
.h40 { height: 40%; }
때로는 HTML 문서가 갖는 기본 스타일이 레이아웃을 잡는 데 방해가 될 수 있습니다. 어떤 사례가 있는지 찾아볼까요?
이러한 수요에 따라 초기화(리셋)을 위한 다양한 라이브러리가 등장했지만, 사실 굳이 라이브러리를 사용할 필요는 없으며, 위에 언급한 문제를 해결할 몇 줄의 코드를 적용시키기만 한다면, 레이아웃을 잡는데 도움이 될 것입니다.
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
flexbox 레이아웃은, 말 그대로 박스를 유연하게 늘리고 줄이는 방법을 토대로 레이아웃을 잡는 방법입니다. 앞서 CSS 따라하기를 통해 flex라는 CSS 속성을 본 적이 있을겁니다. 이제 그 내용이 무엇인지 구체적으로 알아봅시다.
flex는 부모 박스에 display: flex
를 적용해줌으로, 자식 박스의 방향과 크기를 결정하는 레이아웃입니다.
기본적으로, flex가 적용된 부모 박스의 자식 박스는 왼쪽으로 차례대로 붙게 됩니다.
<div id="outer">
<div class="box">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
</div>
#outer {
display: flex;
border: 1px dotted red;
padding: 10px;
}
.box {
border: 1px solid green;
padding: 10px;
}
기본적으로 flexbox는 박스가 수직으로 분할되지만, 수평으로도 분할할 수 있습니다. flex-direction 속성은 부모 박스에 적용합니다. 자식 박스에 특별한 속성을 주지 않아도 영향을 미칩니다.
주요 속성은 다음과 같습니다. 한번씩 적용해보세요.
자식 박스에 어떠한 속성도 주지 않으면, 그저 오른쪽으로 컨텐츠 크기만큼 배치됩니다. 이 때의 자식 박스의 flex 속성 기본값은 다음과 같습니다.
(flex 속성은 부모에 적용하는 것이 아닌, 자식 박스에 적용합니다.)
flex: 0 1 auto;
직관적이지 않은 이 숫자와 속성들은 처음에는 조금 당황스럽게 느껴질 수도 있습니다. 하지만, flexbox가 기본 크기를 바탕으로 필요에 따라 늘릴 수 있다 라는 컨셉을 이해하고 나면, 어떤 식으로도 박스를 나눌 수 있습니다. flex 속성은 자식 박스를 어떤 식으로 늘릴지를 결정합니다. 각각의 값이 의미하는 것은 다음과 같습니다.
flex: <grow> <shrink> <basis>
이 순서는 반드시 외웁시다. 속으로 따라해보세요 "grow shrink basis" 기본값도 외웁시다 "0 1 auto"...
각각을 따로 지정할 수도 있습니다.
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
grow, shrink는 단위가 없으며, 비례하는 값입니다. 박스를 여러개로 나누었을 때, 각 자식 박스가 갖는 grow값의 총 합이 n이라고 칩시다. 이 때의 1은 1/n의 크기를 의미합니다, 2는 2/n의 크기를 의미합니다.
HTML을 다음과 같이 바꾸고, target 클래스를 추가해서 이런저런 테스트를 해보면서 이해해봅시다.
<div id="outer">
<div class="box target">.box.target</div>
<div class="box">.box</div>
<div class="box">.box</div>
</div>
grow를 1로 바꿔봅시다. 해당 박스는 가로의 남은 영역 전부만큼 늘어납니다. (총 grow값의 합은 1+0+0이므로, .target은 1/1 = 100%이여야 하지만, .box 안에 이미 콘텐츠가 존재하므로 콘텐츠가 담긴 크기는 보장합니다)
.target {
flex: 1 1 auto;
}
box 클래스의 flex 속성에 grow를 1로 주면, 모든 박스가 늘어나려고(grow) 하므로, 결과적으로 동일한 비율로 박스가 늘어나게 됩니다. (총 grow 값 1+1+1, 각 박스는 1/3씩 크기를 가짐)
.box {
flex: 1 1 auto;
}
grow 값은 비율을 의미한다는 것을 잊지 마세요. 모든 자식 박스가 동일한 grow 값을 가질 경우, 각 박스가 동일 비율만큼 차지하므로, 다음 코드는 전부 같은 모양으로 렌더링됩니다.
.box {
flex: 1 1 auto;
/*
flex: 2 1 auto;
flex: 3 1 auto;
flex: 4 1 auto;
*/
}
.target {
/* flex 값을 지정하지 않음 */
}
.box는 그대로 두고, .target만 바꿀 경우, 차지하는 전체 비율만큼 .target의 크기는 커지게 됩니다.
.box {
flex: 1 1 auto;
}
.target {
flex: 2 1 auto;
/* 자식 박스가 총 세개인데, target만 2의 비율을 가지므로, 결과적으로 가로의 50%를 차지하게 됨 */
}
shrink는 grow와 반대로, 차지하는 비율만큼 박스 크기가 작아지게 만듭니다. 사실 비율로 레이아웃을 지정할 경우 grow를 주로 사용합니다. 왜냐하면 shrink는 width나 이후 설명할 basis에 따른 비율이므로 실제 크기를 예측하기 어렵습니다. 보통 shrink는 기본값인 1로 두어도 무방합니다.
박스가 grow나 shrink에 의해 늘어나거나 줄어들기 전 갖게 되는 기본 크기를 의미합니다. grow가 0일 때, basis 크기를 지정하면 그 크기는 보장됩니다.
<div id="outer">
<div class="left">메뉴</div>
<div class="right">본문</div>
</div>
.left {
flex: 0 1 100px; /* grow를 0으로 설정해줘야 100px 이상으로 늘어나지 않습니다. */
}
.right {
flex: 1 1 auto; /* 우측 박스는 grow를 1로 설정해 나머지 공간을 채워줍시다 */
}
질문: 잠깐, 앞서 언급한 1:1:1 비율의 박스가 정말 1:1:1이 맞나요? 크롬 Element 탭으로 실제 크기를 재보셨나요? 각 박스의 basis를 0%로 한번 만들어보세요. auto와 어떤 차이가 있나요?
한편, grow가 0일때만 크기가 보장된다는 이야기를 다른 말로 표현하면, 경우에 따라 flex 컨테이너 안에 박스가 들어갔을 때, basis로 설정된 크기가 항상 보장되지 않는다는 의미이기도 합니다. grow이 1 이상일 경우, 100px보다 커질 수도 있습니다. 다음은 실제로 레이아웃을 구현할 때, "왜 내 마음대로 안되지?" 라는 생각이 드실 때, 참고하면 좋은 원리입니다.
참고 (이런건 무작정 외우는 게 아니라, 해보면서 원리를 파악하는 겁니다)
width
와 flex-basis
를 동시에 적용하면, flex-basis
가 우선됩니다.width
가 정확한 크기를 보장하지 않을 것입니다.width
대신 max-width
를 쓸 수 있습니다. (flex-basis
를 사용하지 않을 경우)바깥 박스에 justify-content
속성을 이용하면, 안쪽 박스의 수평 정렬을 지정할 수 있습니다. 주요 옵션을 소개합니다. 한번씩 적용해보세요.
flex-start
flex-end
center
space-between
바깥 박스에 align-items
속성을 이용하면, 안쪽 박스의 수직 정렬을 지정할 수 있습니다. 주요 옵션을 소개합니다. 한번씩 적용해보세요.
flex-start
flex-end
center
stretch
맨 위에서 잠깐 보여드렸던 VS Code 레이아웃 클론, 직접 만들어 볼 수 있을까요? 정답을 여러분들에게 전달해드리기 이전에, 스스로 도전해보았으면 좋겠습니다.
다음 레이아웃을 flexbox를 이용해 만들어보세요.