Legacy layout methods

김동현·2026년 3월 18일

mdn 학습 번역 - CSS

목록 보기
32/190

안녕하세요! 프론트엔드 개발자의 길을 걷기 위해 열심히 공부하시는 모습이 정말 멋집니다. 준비 중이신 포트폴리오(웹 프로필 사이트나 독서 기록 사이트 등)를 만들다 보면 레이아웃 배치가 마음처럼 되지 않아 답답할 때가 있죠? 특히 요즘은 React나 Next.js 같은 모던 기술 스택을 활용한 컴포넌트 주도 개발(CDD)이 대세지만, 정작 그 컴포넌트들을 화면에 예쁘게 배치하려면 CSS 레이아웃에 대한 깊은 이해가 필수적입니다.

오늘 우리가 함께 살펴볼 MDN 문서는 '과거의 레이아웃 방식들(Legacy layout methods)'입니다. "최신 CSS Grid나 Flexbox가 있는데 굳이 옛날 방식을 알아야 할까?"라고 생각하실 수도 있어요. 하지만 실무에 나가면 레거시 코드를 다뤄야 할 일도 많고, 이 과거의 방식(특히 float의 원리)을 이해하고 나면 현재의 기술들이 얼마나 우리의 삶을 편안하게 만들어주는지, 그리고 어떤 원리로 발전해왔는지 확실히 깨달을 수 있습니다.

자, 그럼 강사인 저와 함께 공식 문서의 내용을 하나도 빠짐없이 차근차근, 하지만 딱딱하지 않게 구어체로 알아보도록 할까요?


과거의 레이아웃 방식들 (Legacy layout methods)

그리드(Grid) 시스템은 CSS 레이아웃에서 아주 흔하게 사용되는 기능이에요. 하지만 'CSS 그리드 레이아웃(CSS Grid Layout)'이라는 최신 기술이 등장하기 전에는, 주로 float이나 다른 레이아웃 기능들을 편법처럼 사용해서 그리드를 구현하곤 했답니다. 레이아웃을 일정한 수의 가상의 기둥(열, column - 예를 들어 4개, 6개, 또는 12개)으로 나누고, 그 가상의 기둥들 안에 콘텐츠를 끼워 맞추는 방식이죠.

이 문서에서는 이런 오래된 방식들이 어떻게 작동하는지 살펴볼 거예요. 여러분이 나중에 유지보수가 필요한 오래된 프로젝트에 투입되었을 때, 이 코드들이 어떤 의도로 작성되었는지 완벽하게 이해할 수 있도록 말이죠!

전제 조건 (Prerequisites):HTML 기초 지식 (HTML 소개(Introduction to HTML) 학습), 그리고 CSS가 어떻게 작동하는지에 대한 이해 (CSS 스타일링 기초(CSS Styling basics) 학습)
학습 목표 (Objective):브라우저에서 CSS 그리드 레이아웃이 지원되기 이전에 사용되었던 그리드 레이아웃 시스템의 핵심 개념을 이해하는 것.

이 문서의 내용 (In this article)


CSS 그리드 레이아웃 이전의 레이아웃과 그리드 시스템

디자인 쪽에서 오신 분들이라면 아주 최근까지도 CSS에 기본적으로 내장된 그리드 시스템이 없었다는 사실이 꽤 놀라우실 거예요. 대신 우리는 격자(그리드) 형태의 디자인을 만들기 위해 다소 최적화되지 않은(sub-optimal) 여러 가지 꼼수들을 사용해 왔죠. 우리는 이제 이런 방법들을 "레거시(legacy, 과거의 유산)" 방식이라고 부릅니다.

새로 시작하는 프로젝트의 경우, 대부분 'CSS 그리드 레이아웃'이 기반이 되고 다른 최신 레이아웃 방식들과 조합되어 레이아웃의 형태를 잡아갑니다. 하지만 실무를 하다 보면 언젠가는 이 레거시 방식들을 사용한 "그리드 시스템"을 마주치게 될 거예요. 그렇기 때문에 이 레거시 방식들이 어떻게 작동하는지, 그리고 최신 CSS 그리드 레이아웃과는 무엇이 다른지 이해해 두는 것은 아주 가치 있는 일입니다.

이 레슨에서는 floatflexbox를 기반으로 한 그리드 시스템 및 그리드 프레임워크가 어떻게 작동하는지 설명해 드릴게요. 최신 그리드 레이아웃을 먼저 공부하셨다면, 이 과거의 방식들이 얼마나 복잡해 보이는지 깜짝 놀라실 수도 있어요! 하지만 이 지식은 예전 시스템으로 만들어진 기존 프로젝트에서 일할 수 있게 해줄 뿐만 아니라, 최신 기술을 지원하지 않는 구형 브라우저를 위한 폴백(fallback, 대체안) 코드를 작성해야 할 때 아주 큰 도움이 될 거예요.

이러한 시스템들을 살펴볼 때 꼭 명심하셔야 할 점이 하나 있어요. 이 레거시 방식들 중 그 어떤 것도 최신 CSS 그리드 레이아웃이 진짜 그리드를 생성하는 것처럼 동작하지 않는다는 겁니다. 레거시 방식들은 단지 요소들에게 특정한 크기를 부여하고, 이리저리 밀어내서 마치 그리드처럼 보이게끔 일렬로 맞추는 눈속임에 가깝습니다.

💡 강사 팁:
알고리즘 공부하실 때 그리디(Greedy) 알고리즘으로 당장의 최적해를 찾거나, 그래프 컬러링으로 복잡한 노드들을 색칠하며 구조를 잡는 훈련을 하셨죠? 브라우저 렌더링 엔진도 여러분의 CSS 코드를 해석해서 화면의 구조를 그립니다. 예전 개발자들은 float이라는 본래 '텍스트가 이미지를 감싸게 만드는' 속성을 억지로 엮어내어 당장의 최적 레이아웃(그리드)을 만들어냈던 거예요. 일종의 해킹이었죠!


2단(Two column) 레이아웃

가장 기초적인 예제인 '2단 레이아웃'부터 시작해 볼게요. 여러분의 컴퓨터에 새로운 index.html 파일을 하나 만들고, 간단한 HTML 템플릿을 채워 넣은 뒤, 아래의 코드들을 적절한 위치에 붙여 넣으며 따라 해보세요. 이 섹션 맨 아래에서 완성된 코드가 어떻게 보이는지 실제 예제로 확인하실 수 있습니다.

먼저 단(column) 안에 들어갈 콘텐츠가 필요하겠죠. 현재 <body> 태그 안에 있는 내용을 전부 지우고 다음 코드로 바꿔주세요.

<h1>2 column layout example</h1>
<div>
  <h2>First column</h2>
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
    aliquam dolor, eu lacinia lorem placerat vulputate. Duis felis orci,
    pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at
    ultricies tellus laoreet sit amet. Sed auctor cursus massa at porta. Integer
    ligula ipsum, tristique sit amet orci vel, viverra egestas ligula. Curabitur
    vehicula tellus neque, ac ornare ex malesuada et. In vitae convallis lacus.
    Aliquam erat volutpat. Suspendisse ac imperdiet turpis. Aenean finibus
    sollicitudin eros pharetra congue. Duis ornare egestas augue ut luctus.
    Proin blandit quam nec lacus varius commodo et a urna. Ut id ornare felis,
    eget fermentum sapien.
  </p>
</div>

<div>
  <h2>Second column</h2>
  <p>
    Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
    ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
    est. Nam id risus quis ante semper consectetur eget aliquam lorem. Vivamus
    tristique elit dolor, sed pretium metus suscipit vel. Mauris ultricies
    lectus sed lobortis finibus. Vivamus eu urna eget velit cursus viverra quis
    vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum sociis natoque
    penatibus et magnis dis parturient montes, nascetur ridiculus mus.
  </p>
</div>

각각의 단(column)은 콘텐츠를 담아두고 한 번에 조작할 수 있도록 겉을 감싸주는 외부 요소(컨테이너)가 필요합니다. 이 예제에서는 <div> 태그들을 선택했지만, 실제 프로젝트라면 <article>, <section>, <aside> 같은 시맨틱(의미론적)으로 더 적절한 태그를 선택하시는 게 좋습니다.

자, 이제 CSS 차례입니다. 가장 먼저 HTML에 기본적인 설정을 제공하기 위해 아래 코드를 적용해 주세요.

body {
  width: 90%;
  max-width: 900px;
  margin: 0 auto;
}

이렇게 하면 body는 브라우저 창(뷰포트) 너비의 90%를 차지하다가, 너비가 900px에 도달하면 더 이상 늘어나지 않고 창의 한가운데에 스스로 중앙 정렬(margin: 0 auto)됩니다.

기본적으로 body의 자식 요소들(h1과 두 개의 <div>)은 부모 영역의 너비를 100% 꽉 채우게 됩니다. 만약 우리가 두 개의 <div>를 양옆으로 나란히 띄우고(float) 싶다면, 이 요소들이 한 줄에 나란히 들어갈 수 있도록 너비의 합을 부모 너비의 100% 이하로 설정해야 합니다. 여러분의 CSS 파일 맨 아래에 다음 내용을 추가해 보세요.

div:nth-of-type(1) {
  width: 48%;
}

div:nth-of-type(2) {
  width: 48%;
}

여기서 우리는 두 <div>의 너비를 부모 요소 너비의 48%로 설정했어요. 두 개를 합치면 96%가 되죠. 남은 4%는 두 단 사이의 여백(gutter) 역할을 해서 콘텐츠가 너무 답답해 보이지 않고 숨을 쉴 수 있는 공간을 만들어줍니다. 이제 남은 건 이 단들을 실제로 띄우는(float) 것뿐이에요. 아래처럼 속성을 추가해 보세요.

div:nth-of-type(1) {
  width: 48%;
  float: left;
}

div:nth-of-type(2) {
  width: 48%;
  float: right;
}

이 모든 걸 종합하면 텍스트가 좌우로 깔끔하게 나뉜 결과를 얻을 수 있습니다.

여기서 눈여겨보실 점은 모든 너비에 퍼센트(%)를 사용했다는 거예요. 아주 훌륭한 전략입니다! 이렇게 하면 화면 크기가 변해도 단의 비율이 그대로 유지되는 리퀴드 레이아웃(liquid layout, 유동적 레이아웃)이 만들어지거든요. 브라우저 창의 크기를 직접 줄였다 늘였다 해보세요. 반응형 웹 디자인을 할 때 아주 귀중한 도구가 된답니다.

참고:
이 예제가 실제로 동작하는 모습은 0_two-column-layout.html에서 확인하실 수 있어요. (소스 코드도 함께 참고해 보세요.)

💡 강사 팁:
과거에는 이렇게 float: leftfloat: right를 남발해서 레이아웃을 잡았어요. 하지만 이렇게 띄워버리면 부모 요소가 자식들의 높이를 제대로 인식하지 못하는 부작용(자식이 둥둥 떠버려서 부모 높이가 0이 되는 현상)이 발생합니다. 그래서 뒤에 빈 요소를 넣고 clear: both를 해주는 등 복잡한 처리가 필수였죠!


간단한 레거시 그리드 프레임워크 만들기

과거의 프레임워크들은 대부분 float 속성의 동작 원리를 이용해서 하나의 단을 다른 단 옆으로 띄우는 방식을 썼습니다. 마치 그리드처럼 보이게 만들기 위해서요. float을 이용해 그리드를 만드는 과정을 단계별로 따라가다 보면 이 방식이 어떻게 돌아가는지 감이 오실 거예요. 덧붙여서 이전에 float과 clear(floats and clearing) 레슨에서 배운 내용 위에 조금 더 발전된 개념들도 쌓아 올리게 됩니다.

가장 만들기 쉬운 형태는 고정 너비(fixed width) 그리드 프레임워크입니다. 우리가 전체 디자인을 얼마나 넓게 쓸 것인지, 단은 몇 개로 나눌 것인지, 그리고 단과 여백(gutter)의 너비는 각각 몇 픽셀로 할 것인지만 계산하면 되거든요.

반면, 브라우저 창 너비에 맞춰 단의 크기가 늘어났다 줄어드는 유연한 그리드를 만들고 싶다면, 단과 여백의 너비를 퍼센트(%)로 계산해 주어야 합니다.

이어지는 섹션에서 이 두 가지를 모두 만들어 볼 거예요. 우리는 '12단(12-column) 그리드'를 만들 겁니다. 12는 6, 4, 3, 2로 나누어 떨어지기 때문에 다양한 상황에서 아주 유연하게 대처할 수 있어 개발자들 사이에서 매우 흔하게 선택되는 방식이에요.


간단한 고정 너비 그리드

먼저 고정된 너비 값을 사용하는 그리드 시스템을 만들어봅시다.

우리가 제공하는 샘플인 simple-grid.html 파일의 코드를 복사해서 로컬 환경에 저장해 보세요. 이 파일의 <body> 안에는 다음과 같은 마크업이 들어있습니다.

<div class="wrapper">
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
    <div class="col">4</div>
    <div class="col">5</div>
    <div class="col">6</div>
    <div class="col">7</div>
    <div class="col">8</div>
    <div class="col">9</div>
    <div class="col">10</div>
    <div class="col">11</div>
    <div class="col">12</div>
  </div>
  <div class="row">
    <div class="col span1">13</div>
    <div class="col span6">14</div>
    <div class="col span3">15</div>
    <div class="col span2">16</div>
  </div>
</div>

우리의 목표는 이 마크업을 12단 그리드 기반의 2개 행(row)을 가진 데모용 그리드로 탈바꿈시키는 거예요. 첫 번째 행은 개별 단(1칸)의 크기를 눈으로 보여주고, 두 번째 행은 그리드 위에서 서로 다른 크기를 차지하는 영역들을 보여줄 겁니다.

CSS grid with 16 grid items spread across twelve columns and two rows...

<style> 요소 안에 아래의 코드를 추가해 보세요. 전체를 감싸는 래퍼(wrapper) 컨테이너에 980픽셀의 너비를 주고, 오른쪽에 20픽셀의 패딩을 줍니다. 이렇게 하면 우리가 단과 여백(gutter)으로 사용할 수 있는 순수 너비는 960픽셀이 남게 됩니다. 왜냐하면 모든 요소의 box-sizingborder-box로 설정했기 때문에 패딩 값이 전체 콘텐츠 너비 안으로 포함되어 깎여나가거든요. (이에 대한 더 자세한 설명은 대안적인 CSS 박스 모델(The alternative CSS box model)을 참고하세요.)

* {
  box-sizing: border-box;
}

body {
  width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 20px;
}

이제 그리드의 각 행(row)을 감싸고 있는 row 컨테이너를 이용해서, 윗줄과 아랫줄이 겹치지 않게 정리(clear)를 해줄 차례입니다. 방금 작성한 코드 아래에 다음 규칙을 추가하세요.

.row {
  clear: both;
}

이렇게 clear를 적용해주면, 우리가 12개의 단을 꽉 채우지 않고 비워두더라도 다음 행이 위로 딸려 올라가지 않습니다. 각 행들이 서로 간섭하지 않고 온전히 분리되어 유지되는 것이죠.

단과 단 사이의 여백(gutter)은 20픽셀로 할 거예요. 이 여백은 각 단의 왼쪽 마진(margin-left)으로 만들어줍니다. 첫 번째 단에도 20픽셀 마진이 들어가는데요, 이는 아까 컨테이너(wrapper)의 오른쪽에 주었던 20픽셀 패딩과 균형을 맞추기 위함입니다. 자, 그럼 총 12개의 단이 있으니 여백도 12개가 필요하겠죠? 12 x 20 = 240픽셀입니다.

전체 가용 너비 960픽셀에서 여백 240픽셀을 빼면, 순수하게 단(column)들이 차지할 공간은 720픽셀이 남습니다. 이 720픽셀을 12개로 똑같이 나누면 60픽셀! 즉, 단 한 칸의 너비는 60픽셀이 되어야 합니다.

다음 단계는 .col 클래스에 대한 CSS 규칙을 만드는 거예요. 요소들을 왼쪽으로 띄우고(float: left), 여백을 위해 20픽셀의 margin-left를 주고, 60픽셀의 width를 설정합니다. CSS 파일 맨 아래에 추가해 보세요.

.col {
  float: left;
  margin-left: 20px;
  width: 60px;
  background: rgb(255 150 150);
}

이제 첫 번째 행에 있는 1칸짜리 단들이 그리드 형태로 깔끔하게 정렬될 겁니다.

참고:
여러분이 각 단이 얼만큼의 공간을 차지하는지 시각적으로 쉽게 확인할 수 있도록 연한 빨간색 배경을 추가해 두었어요.

여러 개의 단을 차지하게 만들고 싶은 컨테이너가 있다면, 필요한 단의 개수(그리고 그사이의 여백들)만큼 넓어지도록 특별한 클래스를 만들어주어야 합니다. 2칸부터 12칸까지 합칠 수 있도록 추가 클래스들을 만들 텐데요. 각 너비는 '(합칠 단의 개수 × 단 너비) + (여백 개수 × 여백 너비)'로 계산합니다. 이때 여백의 개수는 항상 합치는 단의 개수보다 1개 적다는 점을 기억하세요!

CSS 맨 아래에 이 규칙들을 추가해 보세요.

/* 단 2개 너비 (120px) + 여백 1개 너비 (20px) */
.col.span2 {
  width: 140px;
}
/* 단 3개 너비 (180px) + 여백 2개 너비 (40px) */
.col.span3 {
  width: 220px;
}
/* 이하 동문... */
.col.span4 {
  width: 300px;
}
.col.span5 {
  width: 380px;
}
.col.span6 {
  width: 460px;
}
.col.span7 {
  width: 540px;
}
.col.span8 {
  width: 620px;
}
.col.span9 {
  width: 700px;
}
.col.span10 {
  width: 780px;
}
.col.span11 {
  width: 860px;
}
.col.span12 {
  width: 940px;
}

이 클래스들을 만들고 나면, 그리드 위에 자유롭게 서로 다른 너비의 단들을 배치할 수 있게 됩니다. 파일을 저장하고 브라우저에서 새로고침하여 어떻게 적용되었는지 확인해 보세요!

참고:
위 예제가 마음대로 잘 안 풀린다면, GitHub에 있는 저희의 완성된 버전 코드와 비교해 보세요. (라이브 화면도 볼 수 있습니다.)

요소에 적혀있는 클래스를 마음대로 바꿔보거나 <div>들을 지웠다 추가하면서 레이아웃이 어떻게 변하는지 테스트해 보세요. 예를 들어, 두 번째 행을 다음과 같이 바꿔볼 수도 있습니다.

<div class="row">
  <div class="col span8">13</div>
  <div class="col span4">14</div>
</div>

이제 여러분은 완벽히 작동하는 그리드 시스템을 손에 넣었습니다! 행을 정의하고, 각 행 안에 들어갈 단의 개수를 정의하고, 그 안에 필요한 콘텐츠만 채워 넣으면 됩니다. 훌륭해요!

💡 강사 팁:
여기서 클래스 이름들을 span2, span3 같이 지은 것에 주목하세요. 여러분이 Bootstrap 같은 유명한 CSS 프레임워크를 나중에 써보시면 col-md-4 같은 클래스를 보게 될 텐데, 전부 이런 레거시 방식의 이름 짓기 원리가 그대로 계승된 것이랍니다.


유동적(Fluid) 그리드 만들기

방금 만든 그리드는 참 잘 작동하지만 한 가지 아쉬운 점이 있습니다. 너비가 고정되어 있다는 점이죠. 우리는 브라우저 뷰포트(viewport) 공간에 따라 유연하게 늘어났다 줄어드는 '유동적인(fluid)' 그리드를 원합니다. 이를 위해 우리가 계산했던 픽셀 값들을 전부 퍼센트(%) 비율로 바꿔주면 됩니다.

고정된 픽셀을 비율(%)로 변환해 주는 아주 마법 같은 공식이 하나 있습니다.

타겟 너비(target) / 컨텍스트 너비(context) = 결과(result)

단의 너비를 계산해 볼까요? 우리가 원하는 타겟 너비는 60픽셀이고, 기준이 되는 컨텍스트(전체) 너비는 960픽셀 래퍼입니다. 이 공식에 대입해 보죠.

60 / 960 = 0.0625

소수점을 오른쪽으로 두 칸 이동시키면 6.25%가 됩니다. 와우! 이제 CSS에서 단 너비에 사용했던 60px 대신 6.25%를 쓰면 됩니다.

여백 너비도 동일한 방식으로 계산해 줍니다.

20 / 960 = 0.02083333333

마찬가지로 .col의 왼쪽 마진과 .wrapper의 오른쪽 패딩에 들어갔던 20px2.08333333%로 교체하면 끝입니다.

그리드 업데이트하기

이 작업을 직접 해보기 위해 이전 예제 페이지를 복사하거나, 저희의 simple-grid-finished.html 코드를 다운로드하여 시작점으로 삼으세요.

두 번째 CSS 규칙(.wrapper 선택자 부분)을 아래와 같이 업데이트합니다.

body {
  width: 90%;
  max-width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 2.08333333%;
}

단순히 width에 퍼센트를 적용한 것뿐만 아니라, max-width 속성을 함께 부여했습니다. 브라우저 창이 너무 거대해질 때 레이아웃이 끝없이 늘어나는 걸 방지하기 위해서죠.

다음으로 네 번째 CSS 규칙(.col 부분)을 아래처럼 바꿔줍니다.

.col {
  float: left;
  margin-left: 2.08333333%;
  width: 6.25%;
  background: rgb(255 150 150);
}

이제 약간의 노가다(?)가 필요한 차례입니다. 아까 만들었던 .col.span 규칙들을 전부 픽셀 대신 퍼센트로 다시 계산해서 넣어줘야 하거든요. 계산기 두드리는 수고를 덜어드리기 위해 제가 미리 다 계산해 왔습니다!

여러분의 CSS 하단 블록을 통째로 다음 코드로 교체해 주세요.

/* 단 2개 (12.5%) + 여백 1개 (2.08333333%) */
.col.span2 {
  width: 14.58333333%;
}
/* 단 3개 (18.75%) + 여백 2개 (4.1666666%) */
.col.span3 {
  width: 22.91666666%;
}
/* 이하 동문... */
.col.span4 {
  width: 31.24999999%;
}
.col.span5 {
  width: 39.58333332%;
}
.col.span6 {
  width: 47.91666665%;
}
.col.span7 {
  width: 56.24999998%;
}
.col.span8 {
  width: 64.58333331%;
}
.col.span9 {
  width: 72.91666664%;
}
.col.span10 {
  width: 81.24999997%;
}
.col.span11 {
  width: 89.5833333%;
}
.col.span12 {
  width: 97.91666663%;
}

이제 코드를 저장하고 브라우저에서 불러온 뒤 창 크기를 조절해 보세요. 화면 크기에 맞춰서 단의 너비가 쫀득쫀득하게 조절되는 모습을 보실 수 있을 거예요!

참고:
혹시 제대로 안 나온다면 GitHub에 있는 완성된 버전(fluid-grid.html)과 비교해 보세요. (라이브 화면도 참고하세요.)


calc() 함수를 사용한 더 쉬운 계산

사실, 방금 했던 귀찮은 수학 계산들을 CSS 내부에서 알아서 처리하도록 만들 수 있어요. 바로 calc() 함수를 사용하면 됩니다! 수식 자체를 CSS 값으로 집어넣으면 브라우저가 알아서 결과를 도출해 주거든요. 특히 "부모 높이의 100%에서 딱 50px만 빼고 싶어"처럼 서로 다른 단위를 섞어서 연산할 때 아주 유용합니다. (참고: calc를 활용한 MediaStream Recording API 튜토리얼 예제)

다시 우리 그리드 얘기로 돌아올게요. 여러 단을 합친 컨테이너의 너비는 항상 6.25% * 단의 개수 더하기 2.08333333% * 여백의 개수(단 개수 - 1)로 계산되었죠? calc() 함수를 쓰면 이 공식을 그대로 width에 집어넣을 수 있습니다. 예를 들어 4칸을 합치는 경우엔 이렇게 적으면 됩니다.

.col.span4 {
  width: calc((6.25% * 4) + (2.08333333% * 3));
}

여러분 CSS 파일의 하단 블록을 아래 코드로 대체한 뒤, 브라우저를 새로고침해서 아까와 동일한 결과가 나오는지 확인해 보세요!

.col.span2 {
  width: calc((6.25% * 2) + 2.08333333%);
}
.col.span3 {
  width: calc((6.25% * 3) + (2.08333333% * 2));
}
.col.span4 {
  width: calc((6.25% * 4) + (2.08333333% * 3));
}
.col.span5 {
  width: calc((6.25% * 5) + (2.08333333% * 4));
}
.col.span6 {
  width: calc((6.25% * 6) + (2.08333333% * 5));
}
.col.span7 {
  width: calc((6.25% * 7) + (2.08333333% * 6));
}
.col.span8 {
  width: calc((6.25% * 8) + (2.08333333% * 7));
}
.col.span9 {
  width: calc((6.25% * 9) + (2.08333333% * 8));
}
.col.span10 {
  width: calc((6.25% * 10) + (2.08333333% * 9));
}
.col.span11 {
  width: calc((6.25% * 11) + (2.08333333% * 10));
}
.col.span12 {
  width: calc((6.25% * 12) + (2.08333333% * 11));
}

참고:
완성된 코드는 fluid-grid-calc.html에서 확인 가능합니다. (라이브 화면)


시맨틱(의미론적) vs "비시맨틱" 그리드 시스템

이처럼 HTML 마크업에 레이아웃용 클래스(예: span2, span3)를 마구 추가하는 방식은 한 가지 문제점이 있습니다. 바로 콘텐츠 구조와 시각적 디자인이 서로 찰싹 달라붙게 된다는 점이에요. 개발자들 사이에서는 이런 방식을 두고 "비시맨틱(unsemantic)하다"라고 비판하는 경우가 종종 있습니다. 클래스 이름이 그 안에 담긴 진짜 '의미'를 설명하지 않고, 그저 겉모습(너비가 몇 칸인지)만 묘사하고 있으니까요.

물론 다른 접근법도 존재합니다. 그리드 규칙을 먼저 정해둔 다음, HTML에는 의미 있는 이름(시맨틱 클래스)을 달아두고 CSS에서 그 넓이 값을 직접 주입하는 방식이에요. 예를 들어 HTML에 <div class="content">가 있고 이 녀석을 8칸 넓이로 만들고 싶다면, span8의 공식을 복사해서 그대로 .content 선택자에 적용하는 겁니다.

.content {
  width: calc((6.25% * 8) + (2.08333333% * 7));
}

참고:
Sass 같은 CSS 전처리기(preprocessor)를 사용한다면, 간단한 믹스인(mixin)을 만들어서 이 값을 자동으로 쏙쏙 들어가게 할 수도 있습니다. 프론트엔드 환경이 점점 발전하면서 나온 편의 기능들이죠!


그리드에 오프셋(Offset) 컨테이너 적용하기

우리가 만든 그리드는 모든 요소가 왼쪽 끝에서부터 차곡차곡 채워질 때 아주 완벽하게 작동합니다. 하지만 만약 첫 번째 상자 앞에, 혹은 상자들 사이에 빈 공간(빈 단)을 남겨두고 싶다면 어떻게 해야 할까요? 그리드 상에서 요소를 오른쪽으로 쓱 밀어내기 위한 '오프셋(offset)' 클래스가 필요합니다. 네, 수학 계산이 하나 더 늘어난다는 뜻이에요!

한 번 시도해 보죠.

방금 작성하신 코드나 저희의 fluid-grid.html을 가져와서 진행해 주세요.

요소를 '1칸'만큼 옆으로 밀어버리는 오프셋 클래스를 만들어보겠습니다. CSS 하단에 다음 규칙을 추가하세요.

.offset-by-one {
  margin-left: calc(6.25% + (2.08333333% * 2));
}

머리 아픈 calc 대신, 미리 계산된 퍼센트를 사용하고 싶다면 이렇게 쓰시면 됩니다.

.offset-by-one {
  margin-left: 10.41666666%;
}

이제 왼쪽에 딱 1칸 분량의 빈 공간을 남기고 싶은 요소가 있다면 이 클래스를 추가해주기만 하면 됩니다.
예를 들어 HTML에 이런 요소가 있다면:

<div class="col span6">14</div>

이렇게 바꿔보세요!

<div class="col span5 offset-by-one">14</div>

참고:
옆으로 한 칸 밀어냈기 때문에, 행을 뚫고 나가지 않으려면 원래 차지하던 단의 개수(span6)를 하나 줄여서(span5) 공간을 확보해 주어야 한다는 점을 잊지 마세요!

저장 후 브라우저를 새로고침해서 차이를 확인해 보세요. 아니면 fluid-grid-offset.html 예제를 확인하셔도 됩니다. (라이브 화면)
완성된 모습은 아래와 같아야 합니다.

The grid has 2 rows. The first row has 12 equal-width grid items and the second row has 4 items of different widths...

참고:
추가 과제 하나 내드릴게요! 배운 내용을 토대로 offset-by-two (두 칸 밀어내기) 클래스도 한 번 만들어 보실래요?


float 기반 그리드의 한계

이런 방식의 시스템을 사용할 때는 반드시 전체 너비 합계가 100%를 초과하지 않도록 꼼꼼하게 계산해야 합니다. 하나의 행 안에 들어갈 수 있는 한도를 넘어서면 어떻게 될까요? float의 특성상 마지막 요소가 자리를 찾지 못하고 다음 줄로 강제로 떨어져 버려 레이아웃이 와장창 깨지게 됩니다.

또한 요소 내부의 콘텐츠 내용이 너무 길어지면, 정해진 높이나 범위를 무시하고 흘러넘쳐서 보기 흉해질 수 있다는 점도 기억해 두세요.

하지만 이 시스템의 가장 치명적인 한계는 본질적으로 1차원적인 제어만 가능하다는 것입니다. 우리는 세로 열(단, column)을 다루고, 여러 열을 병합할 순 있지만, 가로 행(row)을 제어할 수는 없습니다. 이 오래된 방식으로는 요소에 구체적인 높이 값을 억지로 주지 않는 이상 일관된 높이를 유지하기가 너무 어렵습니다. 만약 데이터에 따라 내용물 높이가 들쑥날쑥하다면 레이아웃이 엉망이 되기 십상이죠.


Flexbox 그리드?

이전 시간에 배운 Flexbox 레슨을 기억하신다면, "어? Flexbox야말로 그리드를 만들기 완벽한 해결책 아닐까?"라고 생각하실 수 있습니다. 맞아요, 실제로 세상에는 수많은 Flexbox 기반 그리드 시스템들이 있고, 방금 float을 쓰면서 고통받았던 한계점들을 대부분 해결해 줍니다.

하지만, Flexbox는 애초에 '그리드 시스템'을 위해 설계된 기술이 아닙니다. 그래서 이걸 억지로 그리드로 쓰려다 보면 또 다른 새로운 난관에 부딪히게 되죠. 방금 썼던 HTML 마크업은 그대로 두고, CSS만 Flexbox 스타일로 싹 바꿔서 wrapper, row, col을 정의해 볼게요.

body {
  width: 90%;
  max-width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 2.08333333%;
}

.row {
  display: flex;
}

.col {
  margin-left: 2.08333333%;
  margin-bottom: 1em;
  width: 6.25%;
  flex: 1 1 auto;
  background: rgb(255 150 150);
}

여러분의 코드에서 직접 고쳐보시거나, flexbox-grid.html 코드(라이브 화면)를 확인해 보세요.

여기서 우리는 각 행(row)을 flex 컨테이너(display: flex)로 만들었습니다. Flexbox를 그리드처럼 쓰려면 요소들 너비 합이 100%가 되지 않더라도 줄 바꿈이나 구분이 가능하도록 여전히 이 row 단위의 감싸기가 필요하거든요.

그리고 .col에는 flex 속성을 주었습니다. 첫 번째 값(flex-grow)을 1로 줘서 빈 공간이 있으면 늘어나게 하고, 두 번째 값(flex-shrink)도 1로 줘서 좁을 땐 줄어들게 하고, 세 번째 기준값(flex-basis)을 auto로 설정했죠. 우리가 width를 미리 정해두었기 때문에, auto는 그 지정된 width를 flex-basis 값으로 차용하게 됩니다.

결과가 어떨까요? 첫 번째 줄은 12개의 박스가 예쁘게 나열되고 창을 늘렸다 줄여도 똑같이 반응합니다. 하지만 두 번째 줄엔 4개의 박스만 있죠. 이 4개의 박스들도 60px(6.25%)을 기본 크기로 갖지만, 위에 비해 남는 공간이 너무 많아서 엄청나게 팽창(grow)해 버립니다. 결국 4개의 상자가 두 번째 행 전체 너비를 똑같이 나눠 가지는 대참사(?)가 일어납니다.

The grid has two rows. Each row is a flex container. The first row has twelve equal-width flex items. The second row has four equal-width flex items.

이걸 고치려면 결국 요소의 flex-basis 값을 덮어쓸 수 있도록 아까 만들었던 span 클래스들을 또 집어넣어야 합니다. 게다가 아래쪽 상자들은 위쪽 상자들이 구축해 놓은 '그리드 선'에 대해 알 턱이 없기 때문에, 딱딱 맞춰 떨어지는 진짜 그리드처럼 정렬되지도 않습니다.

Flexbox는 디자인부터가 1차원(one-dimensional)입니다. 오직 하나의 방향(가로 혹은 세로)만을 다루죠. 가로세로를 모두 엄격하게 통제하는 그리드를 만들 수가 없어요. 따라서 Flexbox를 쓰더라도 float 때처럼 퍼센트를 일일이 계산해야 하는 수고는 똑같이 듭니다.

물론, 실무 프로젝트에서는 float보다 훨씬 뛰어난 정렬 기능과 공간 분배 능력을 가진 Flexbox를 그리드 용도로 채택하실 수도 있어요. 하지만 "원래 그런 용도로 만들어진 도구가 아닌 것을 억지로 가져다 쓰고 있다"는 점은 분명히 인지하셔야 합니다. 원하는 결과물을 얻기 위해 불필요하게 장애물들을 넘고 있다는 기분이 드실 수 있거든요.

💡 강사 팁:
"Flexbox와 Grid의 차이가 뭔가요?"는 프론트엔드 신입 기술 면접에서 아주 단골로 나오는 질문입니다. Flexbox는 1차원(행이나 열 하나) 배치에 특화되어 있고, CSS Grid는 2차원(행과 열 동시) 배치에 최적화된 진짜 그리드 도구라는 점, 꼭 기억해 두세요!


서드파티 그리드 시스템

자, 이제 그리드 계산 뒤에 숨겨진 수학 공식을 완벽히 이해하셨으니, 현업에서 흔히 쓰이는 '서드파티 그리드 시스템(남들이 만들어둔 프레임워크)'을 구경할 준비가 되셨습니다. 웹에서 "CSS grid framework"라고 검색만 해봐도 고를 수 있는 옵션이 산더미처럼 쏟아져 나옵니다. Bootstrap이나 Foundation 같은 유명 프레임워크들은 자체적인 그리드 시스템을 품고 있죠. 아예 그리드 시스템에만 집중한 독립적인 라이브러리들도 많고요.

이 중 아주 심플한 CSS 프레임워크인 'Skeleton'의 그리드 시스템을 예시로 가져와 볼게요. 그리드 프레임워크를 다룰 때 사용하는 전반적인 테크닉을 보여주기 딱 좋거든요.

시작하려면 Skeleton 웹사이트를 방문하셔서 "Download"를 눌러 ZIP 파일을 받으세요. 압축을 풀고 skeleton.cssnormalize.css를 여러분의 새로운 작업 폴더에 복사해 넣습니다.

저희의 html-skeleton.html 파일을 복사해서 같은 폴더에 저장하신 뒤, HTML 헤더(head)에 아래처럼 두 CSS 파일을 연결해 주세요.

<link href="normalize.css" rel="stylesheet" />
<link href="skeleton.css" rel="stylesheet" />

Skeleton에는 그리드 말고도 폰트(타이포그래피)나 여러 페이지 요소를 위한 초기 스타일링이 포함되어 있어요. 일단 이건 기본값 그대로 두고 넘어가죠. 우리가 진짜 관심 있는 건 그리드니까요!

참고:
Normalize는 Nicolas Gallagher가 만든 작고 소중한 CSS 라이브러리입니다. 브라우저마다 제각각인 기본 스타일링을 통일시켜주고 자잘한 레이아웃 버그들을 알아서 고쳐주는 마법사 같은 녀석이죠.

이전에 썼던 것과 비슷한 HTML을 써봅시다. HTML body 안에 다음 코드를 추가하세요.

<div class="container">
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
    <div class="col">4</div>
    <div class="col">5</div>
    <div class="col">6</div>
    <div class="col">7</div>
    <div class="col">8</div>
    <div class="col">9</div>
    <div class="col">10</div>
    <div class="col">11</div>
    <div class="col">12</div>
  </div>
  <div class="row">
    <div class="col">13</div>
    <div class="col">14</div>
    <div class="col">15</div>
    <div class="col">16</div>
  </div>
</div>

Skeleton을 구동하기 위해서는 가장 바깥을 감싸는 <div>container라는 클래스를 줘야 합니다. (이미 저희 마크업에 포함되어 있죠!) 이렇게 하면 콘텐츠가 최대 너비 960픽셀 안에서 화면 중앙에 예쁘게 자리 잡습니다. 박스들이 960픽셀 이상으로 뚱뚱해지지 않는 걸 보실 수 있을 거예요.

skeleton.css 파일을 열어보면 이 container 클래스가 어떤 마법을 부리는지 확인할 수 있습니다. 좌우 마진(auto)으로 중앙 정렬을 하고, 좌우 20픽셀의 패딩을 주었네요. 앞서 우리가 했던 것처럼 box-sizingborder-box로 세팅해서 패딩과 테두리가 전체 너비 안으로 깔끔하게 들어오도록 조치했습니다.

.container {
  position: relative;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
}

요소들이 그리드의 일원이 되려면 반드시 row (행) 안에 들어가야만 합니다. 이 부분도 아까 우리가 했던 것과 완전히 똑같아요. container와 내용물 사이에 row 클래스를 가진 <div>를 넣어주는 거죠.

자, 상자들을 배치해 볼까요? Skeleton은 12단 그리드 기반입니다. 첫 번째 행에 있는 박스들이 전부 각각 1칸씩 차지하게 만들려면 one column 이라는 클래스를 주어야 합니다.

아래처럼 추가해 보세요.

<div class="container">
  <div class="row">
    <div class="one column">1</div>
    <div class="one column">2</div>
    <div class="one column">3</div>
    /* 이어서 12번까지... */
  </div>
</div>

다음으로, 두 번째 행에 있는 컨테이너들에는 몇 단을 덮어씌울지 알려주는 클래스를 달아주세요. 이렇게요!

<div class="row">
  <div class="one column">13</div>
  <div class="six columns">14</div>
  <div class="three columns">15</div>
  <div class="two columns">16</div>
</div>

HTML 파일을 저장하고 브라우저에서 그 결과를 감상해 보세요.

참고:
예제가 제대로 보이지 않는다면 브라우저 창을 양옆으로 시원하게 넓혀보세요. 화면이 너무 좁으면 모바일 뷰로 전환되어 그리드가 무너지거든요. 그래도 안 된다면 저희의 html-skeleton-finished.html 파일과 비교해 보세요. (라이브 화면 참고)

다시 skeleton.css 파일을 뜯어보면 이게 어떤 원리로 돌아가는지 알 수 있습니다. 예를 들어 Skeleton에는 "three columns"가 추가된 요소를 위해 이런 스타일이 미리 정의되어 있죠.

.three.columns {
  width: 22%;
}

맞아요. Skeleton이나 다른 그 어떤 그리드 프레임워크들도, 결국엔 여러분이 가져다 쓰기 좋게 미리 퍼센트를 계산해서 클래스로 만들어둔 것뿐입니다. 아까 우리가 땀 흘려 직접 계산하고 짰던 그 코드들과 완전히 동일한 일을 하는 거죠.

보시다시피 Skeleton 같은 도구를 쓰면 우리가 직접 작성해야 할 CSS가 확 줄어듭니다. 마크업에 클래스 이름만 적어주면 알아서 float 처리를 싹 다 해주니까요. 이렇게 골치 아픈 레이아웃 책임을 다른 누군가에게 떠넘길 수 있다는 매력 덕분에, 과거에는 그리드 프레임워크 사용이 선택이 아닌 필수처럼 여겨졌습니다.

하지만 지금은 시대가 변했어요! 아주 강력한 기본 기능인 'CSS 그리드 레이아웃'이 브라우저에 탑재되면서, 수많은 개발자들이 무거운 프레임워크를 버리고 순수 CSS로 회귀하고 있는 추세랍니다.


요약

수고하셨습니다! 이제 여러분은 다양한 레거시 그리드 시스템들이 어떻게 만들어졌는지 깊은 원리를 이해하게 되셨습니다. 이 지식은 오래된 웹사이트의 코드를 분석할 때는 물론이고, 모던 'CSS 그리드 레이아웃'이 이런 과거의 시스템들과 비교했을 때 얼마나 혁명적인지 그 진가를 알아보는 데 아주 큰 밑거름이 될 것입니다. 포트폴리오 만드실 때 이 원리를 떠올리시면 레이아웃을 다루는 시야가 한층 더 넓어지실 거예요! 화이팅입니다!


MDN 개선에 참여하기 (Help improve MDN)

이 페이지가 도움이 되셨나요?
[Yes][No]

기여하는 방법 알아보기(Learn how to contribute)
이 페이지의 마지막 수정일은 2025년 6월 24일이며, MDN 기여자들에 의해 수정되었습니다.

profile
프론트에_가까운_풀스택_개발자

0개의 댓글