안녕하세요! 지금까지 텍스트, 박스 모델 등 CSS의 기초적인 블록들을 쌓아오셨군요. 이제 그 블록들을 화면 원하는 곳에 자유자재로 배치할 수 있는 CSS 레이아웃(Layout)의 세계로 진입할 차례입니다.
오늘은 그 시작으로, 브라우저가 기본적으로 요소들을 어떻게 배치하는지(Normal Flow)를 알아보고, 앞으로 우리가 어떤 마법 같은 도구들을 써서 이 기본 배치를 깰 수 있는지 전체적인 그림을 그려보는 시간을 갖겠습니다. 실무에서 가장 중요한 뼈대가 되는 내용이니, 저와 함께 차근차근 짚어볼까요?
이전 페이지 (Overview: CSS layout) | 다음 페이지 (Floats)
이번 레슨에서는 이전에 살짝 다루었던 다양한 display 값 같은 CSS 레이아웃 기능들을 다시 한번 훑어보고, 앞으로 이 모듈 전체에 걸쳐 깊게 파고들 개념들을 미리 소개할 것입니다. 특히 '정상 흐름(normal flow)'이라는 개념을 아주 깊이 있게 다룰 예정입니다.
| 사전 준비 지식: | HTML로 콘텐츠 구조화하기, CSS 스타일링 기초, 기본 텍스트 및 폰트 스타일링. |
| 학습 목표: |
|
CSS 페이지 레이아웃 기술들은 웹 페이지 안에 있는 요소들을 가져다가, 다음 요소들을 기준으로 우리가 원하는 곳에 정확히 위치시킬 수 있게 해 줍니다:
우리가 아래에서 짧게 언급하고 앞으로 모듈 전체에서 자세히 다룰 페이지 레이아웃 기술들은 저마다 쓰임새가 다르고 장단점이 있습니다. 그 어떤 기술도 완벽하게 혼자서만 쓰이도록 설계되지 않았습니다. 각 레이아웃 방식이 어떤 목적을 위해 만들어졌는지 이해한다면, 여러분은 특정 작업을 할 때 어떤 방법이 가장 적합한지 스스로 판단할 수 있는 훌륭한 안목을 갖추게 될 것입니다.
웹페이지의 요소들은 여러분이 요소의 행동을 바꾸기 위해 어떠한 CSS도 적용하지 않았다면, 기본적으로 정상 흐름(normal flow)에 따라 배치됩니다. 이 '정상 흐름' 속에서 요소의 위치를 살짝 조정하거나, 아니면 요소 자체를 이 흐름 밖으로 완전히 끄집어냄으로써 우리는 요소의 행동을 바꿀 수 있습니다.
어떤 웹페이지를 만들든 가장 좋은 출발점은 "정상 흐름 상태일 때도 훌륭하게 읽히는, 구조가 탄탄한 HTML 문서를 만드는 것"입니다. 이렇게 하면 사용자가 구형 브라우저를 쓰든, 혹은 화면을 읽어주는 스크린 리더기를 사용하든 콘텐츠를 읽는 데 아무런 문제가 생기지 않습니다. 게다가 정상 흐름 자체가 원래 '문서를 읽기 좋게' 만들려고 디자인된 것이기 때문에, 이 기초를 잘 다져놓고 시작하면 억지로 문서 구조와 싸워가며 레이아웃을 바꾸는(struggling against) 대신, 문서 구조를 잘 활용하며(working with) 수월하게 작업할 수 있습니다.
다른 레이아웃 방법들을 깊게 파고들기 전에, 이전 모듈에서 배웠던 문서의 정상 흐름과 관련된 내용들을 다시 한번 복습해 보는 것이 좋겠습니다.
💡 강사의 실무 팁! (마크업의 중요성)
"CSS로 나중에 다 위치 바꿀 건데, HTML 순서 대충 막 써도 되는 거 아닌가요?" 절대 아닙니다!
시각 장애인용 스크린 리더기나 구글의 검색 엔진 봇(Bot)은 CSS가 만들어낸 화려한 시각적 배치를 보는 게 아니라, 오직 HTML 태그가 적힌 순서(정상 흐름)대로만 문서를 읽습니다. 시각적으로는 맨 밑에 있는 버튼을 CSS로 억지로 끌어올려 맨 위에 배치했더라도, 스크린 리더기는 그걸 맨 나중에 읽어줍니다. 이것이 바로 웹 접근성(a11y)과 SEO를 망치는 지름길입니다. 항상 뼈대(HTML)부터 올바른 순서로 탄탄하게 짜야 합니다!
이 과정은 개별 요소의 박스들이 자신이 가진 패딩, 테두리, 마진을 몽땅 콘텐츠 크기에 더해서 자신의 영역을 확보하는 것으로 시작됩니다. 우리는 이것을 박스 모델(box model)이라고 불렀죠.
기본적으로 블록 레벨 요소(block-level element)의 콘텐츠는 부모 요소가 제공하는 인라인(가로) 방향의 공간을 100% 꽉 채우고, 블록(세로) 방향으로는 자신이 가진 콘텐츠의 양만큼만 늘어납니다. 반면 인라인 레벨 요소(inline-level elements)의 크기는 딱 자기가 품고 있는 콘텐츠의 크기만큼만 됩니다. <img> 태그처럼 기본 display 값이 inline이더라도 width나 height 속성이 먹히는 특이한 녀석도 있지만, 기본적인 디스플레이 성질은 여전히 inline에 머뭅니다.
만약 인라인 레벨 요소에 너비나 높이를 강제로 제어하고 싶다면, CSS를 사용해 요소가 블록 레벨 요소처럼 행동하도록 바꿔주면 됩니다. (display: block;을 주거나, 두 가지 특성을 섞은 display: inline-block;을 주는 식이죠.)
자, 요소들이 각자 어떻게 구성되는지는 알겠는데, 이 녀석들이 서로 모였을 때는 어떻게 상호작용하며 배치될까요? 여기서 바로 '정상 레이아웃 흐름'이라는 시스템이 브라우저 화면(viewport)에 요소들을 배치하는 규칙으로 작동합니다.
기본적으로 블록 레벨 요소들은 부모의 쓰기 모드(writing mode)에 기반하여 블록 흐름 방향으로 하나씩 배치됩니다. (기본 쓰기 모드는 horizontal-tb 입니다.) 각 요소는 직전 요소의 바로 아래, 즉 새로운 줄(new line)에서 시작하며, 각각 지정된 마진(margin) 크기만큼 서로 뚝 떨어져서 자리를 잡습니다. 예를 들어 영어나 한국어처럼 가로로, 위에서 아래로 쓰는 환경에서는 블록 레벨 요소들이 세로로 차곡차곡 쌓이게 됩니다.
인라인 요소들은 이와 다르게 행동합니다. 얘네들은 새로운 줄에서 시작하지 않아요. 대신 부모 블록 요소의 가로 너비(width)가 허용하는 한, 옆에 있는 다른 인라인 텍스트나 요소들과 함께 한 줄에 나란히 옹기종기 모여 앉습니다. 공간이 꽉 차서 더 이상 앉을 자리가 없으면? 그때야 비로소 넘치는 콘텐츠가 다음 줄로 밀려 내려갑니다.
만약 위아래로 인접해 있는 두 블록 요소가 모두 마진을 가지고 있고 그 마진들이 서로 닿는다면, 두 마진 중 더 큰 값 하나만 살아남고 작은 값은 사라져 버립니다. 이것을 마진 상쇄(margin collapsing) 현상이라고 부릅니다. 마진 상쇄는 오직 수직(vertical) 방향에서만 일어나는 일이라는 점을 꼭 기억하세요!
지금까지 설명한 모든 것을 보여주는 간단한 예제를 한번 보겠습니다.
<h1>Basic document flow</h1>
<p>
I am a basic block level element. My adjacent block level elements sit on new
lines below me.
</p>
<p>
By default we span 100% of the width of our parent element, and we are as tall
as our child content. Our total width and height is our content + padding +
border width/height.
</p>
<p>
We are separated by our margins. Because of margin collapsing, we are
separated by the size of one of our margins, not both.
</p>
<p>
Inline elements <span>like this one</span> and <span>this one</span> sit on
the same line along with adjacent text nodes, if there is space on the same
line. Overflowing inline elements will
<span>wrap onto a new line if possible (like this one containing text)</span>,
or just go on to a new line if not, much like this image will do:
<img
src="[https://mdn.github.io/shared-assets/images/examples/long.jpg](https://mdn.github.io/shared-assets/images/examples/long.jpg)"
alt="snippet of cloth" />
</p>
body {
width: 500px;
margin: 0 auto;
}
p {
background: rgb(255 84 104 / 30%);
border: 2px solid rgb(255 84 104);
padding: 10px;
margin: 10px;
}
span {
background: white;
border: 1px solid black;
}
결과는 다음과 같이 나옵니다.
HTML 코드가 소스 코드에 적힌 그 순서 그대로 화면에 출력되는 것을 눈여겨보세요. 블록 요소인 문단(p)과 제목(h1)은 서로 위아래로 층층이 쌓여있고, 인라인 요소인 span과 img는 문단 안에서 글자들과 섞여서 줄바꿈 없이 가로로 흐르다가 자리가 모자라니 아래로 내려갔습니다.
(브라우저 렌더링 화면: h1 아래에 4개의 p 태그가 세로로 나열되고, 마지막 p 태그 안의 span과 img가 텍스트와 함께 가로로 이어지는 모습)
웹페이지에 있는 수많은 요소들에게는, 이 정상 흐름(Normal flow)만으로도 여러분이 딱 원하는 완벽한 레이아웃이 만들어집니다. 하지만 조금만 더 복잡한 디자인을 하려고 하면, 우리는 CSS가 제공하는 도구들을 사용해서 이 기본 동작 방식을 뒤틀어야(alter)만 합니다. 그렇기에 처음 뼈대인 HTML 문서를 탄탄하게 잡아두는 것이 아주 중요한 것입니다. 기본 배치를 거스르며 억지로 싸우기보단, 이 정상 흐름을 십분 활용하며 디자인을 얹어갈 수 있으니까요.
브라우저의 이 완벽한 '정상 흐름'을 무시하고, 요소들의 배치를 우리 맘대로 바꿀 수 있게 해주는 CSS의 방법들은 다음과 같습니다. 이 모듈 내내 이 녀석들을 아주 자세히 파고들 예정입니다.
display 속성 (The display property)
block, inline, inline-block 같은 표준 값들은 요소가 정상 흐름 안에서 어떻게 행동할지를 바꿔버립니다. 예를 들면, 원래 블록 레벨인 요소를 인라인 레벨 요소처럼 행동하게 만들 수 있죠. (우리는 이 내용을 이전의 박스 모델 레슨에서 이미 다루었습니다.)
float 속성에 left나 right 같은 값을 적용하면, 블록 레벨 요소가 둥둥 떠서 한쪽 구석으로 밀려나고 그 주변으로 텍스트가 감싸며 흐르게(wrap) 만들 수 있습니다. 잡지 레이아웃에서 사진 옆으로 글씨가 흐르는 모습을 상상하시면 딱 맞습니다!
position 속성은 다른 박스들 안에서 특정 박스가 놓일 위치를 '아주 정밀하게' 컨트롤할 수 있게 해줍니다. static 포지셔닝이 정상 흐름에서의 기본값이지만, 다른 값을 쓰면 요소들을 완전히 다르게 배치할 수 있습니다. 예를 들어 position: fixed를 사용하면 사용자가 스크롤을 내려도 브라우저 화면(viewport) 상단에 메뉴바가 찰싹 달라붙어 고정되게 만들 수 있습니다.
display를 통해 활성화되는 특수 레이아웃 시스템들
우리는 특정 display 값을 주었을 때만 켜지는 완전히 독자적인 레이아웃 방식(Layout methods)들을 가지고 있습니다. 여러분이 반드시 알아야 할 가장 중요한 두 가지는 바로 CSS Grid (그리드)와 Flexbox (플렉스박스)입니다. 이 둘은 부모 컨테이너가 자기 안의 자식 요소들을 배치하는 방식을 완전히 새롭고 파격적인 방식으로 뜯어고칩니다.
반응형 디자인은 웹페이지가 렌더링되는 다양한 기기(예를 들어, 널찍한 데스크톱 모니터와 좁은 스마트폰 화면)의 환경에 맞춰 레이아웃이 알아서 카멜레온처럼 적응하는 것을 말합니다. 반응형 디자인 그 자체가 어떤 특정 레이아웃 도구를 제공하는 것은 아닙니다. 가장 핵심적인 요소는 @media 조건부 규칙(미디어 쿼리)이며, 이를 통해 화면의 너비나 해상도 같은 기기 특성에 따라 완전히 다른 레이아웃 CSS를 상황에 맞게 꺼내어 적용할 수 있습니다.
💡 강사의 실무 팁!
"그럼 저 많은 걸 언제 다 쓰나요?"
현대 웹 프론트엔드 실무에서는 화면의 큰 틀을 잡고 1차원 배치(가로 혹은 세로)를 할 때는 Flexbox를 압도적으로 많이 씁니다. 바둑판처럼 복잡한 2차원 배치(가로 세로 동시 제어)가 필요할 때는 Grid를 쓰고요.
특정한 요소를 화면 구석에 겹치게 띄워야 할 때(예: 모달 팝업, 헤더 고정, 툴팁 등)는 Position을 씁니다.
반면, Float는 텍스트가 이미지를 감싸게 만드는 원래의 목적 외에 '레이아웃 용도'로는 이제 수명이 다하여 실무에서 거의 쓰이지 않으니 개념만 알아두셔도 충분합니다!
이 모듈에서는 다루지 않겠지만, 비교적 덜 사용되는 다른 레이아웃 기법들도 존재하긴 합니다.
테이블 레이아웃 (Table layout)본래 HTML 표(table)를 꾸미기 위해 만들어진 기능들이지만, display: table과 그에 관련된 속성들을 사용하면 표가 아닌 일반 요소들도 표처럼 행동하고 배치되도록 억지로 만들 수 있습니다. (Flexbox가 나오기 전, 옛날 웹을 만들던 슬픈 시절의 유물입니다...)
다단 레이아웃 속성들을 사용하면, 신문 기사에서 보는 것처럼 하나의 블록 안의 콘텐츠가 여러 개의 단(columns)으로 나뉘어 흐르도록 만들 수 있습니다.
이 글은 여러분이 학습의 현 단계에서 반드시 알고 넘어가야 할 모든 레이아웃 기술들에 대해 간략하게 훑어보는 예고편이었습니다! 이제 각 기술들이 실제로 어떻게 동작하는지 깊이 파헤치기 위해 다음 글들을 계속 읽어 나가시면 됩니다.
다음 시간에는 고전적인 레이아웃 방식이었던, 플로트(Floats)에 대해 알아보겠습니다. 준비되셨나요?