본문 바로가기
StyleSheet/CSS

[CSS] CSS 기초와 핵심 개념 완벽 가이드

반응형

웹 개발에 입문하는 거의 모든 사람들이 가장 먼저 접하는 기술은 HTML과 CSS입니다. 코딩 부트캠프나 온라인 강의에서는 보통 "HTML은 웹페이지의 뼈대를 만들고, CSS는 그 뼈대를 예쁘게 꾸미는 역할을 합니다"라고 간단명료하게 설명하고 넘어갑니다. 하지만, 깊이 있는 프론트엔드 개발자가 되기 위해서는 단순히 이 문장을 외우는 것을 넘어, "대체 왜 처음부터 HTML 자체에 디자인 기능을 빵빵하게 넣지 않고 굳이 CSS라는 완전히 다른 언어를 발명하여 분리해야만 했는가?"에 대한 역사적이고 구조적인 근본 이유를 이해해야 합니다. 이 분리의 철학을 이해하지 못하면, 실무에서 수천 줄의 코드가 얽히고설키는 유지보수 지옥을 마주하게 됩니다.

오늘 포스팅에서는 초창기 웹의 혼란스러웠던 역사부터 CSS가 가져온 패러다임의 전환, 그리고 프론트엔드 아키텍처의 핵심인 '구조와 표현의 분리(Separation of Concerns)'가 주는 4가지 압도적인 실무적 이점에 대해 상세히 알아보겠습니다.

1. CSS 탄생 이전: 혼돈과 야만의 웹(Web) 시대

인터넷과 월드 와이드 웹(WWW)이 처음 등장했던 1990년대 초반을 상상해 보십시오. 당시의 웹은 대학과 연구소에서 논문이나 텍스트 위주의 학술 정보를 교환하기 위한 목적으로 만들어졌습니다. 즉, 애초에 화려한 그래픽이나 레이아웃이라는 개념 자체가 존재하지 않았습니다. 하지만 인터넷이 대중에게 개방되고 기업들이 웹사이트를 만들기 시작하면서 상황은 180도 달라졌습니다. 사람들은 밋밋한 검은 글씨 대신 화려한 색상과 아름다운 글꼴, 정돈된 레이아웃을 원했습니다.

당시 브라우저 제조사들(넷스케이프, 인터넷 익스플로러 등)은 이러한 요구에 부응하기 위해 앞다투어 HTML에 디자인 전용 태그들을 추가하기 시작했습니다. <font>, <center>, <b>, <i> 같은 태그들이 쏟아져 나왔고, 심지어 레이아웃을 잡기 위해 표(Table) 태그 안에 또 표를 넣는 기괴한 방식이 표준처럼 쓰였습니다. 그 결과, 문서의 진짜 '내용(데이터)'과 그것을 꾸미는 '디자인 코드'가 하나의 파일 안에 끔찍하게 뒤엉켜 버렸습니다. 웹 마스터(당시 개발자를 부르던 말)가 100페이지짜리 웹사이트에서 모든 제목 텍스트를 파란색에서 빨간색으로 바꾸려면, 100개의 HTML 파일을 일일이 열어 수천 개의 <font color="blue"><font color="red">로 수정하는 막노동을 해야만 했습니다. 이는 개발자의 생명과도 같은 DRY(Don't Repeat Yourself, 중복 방지) 원칙을 철저히 위배하는 행위였으며, 쓸데없는 태그의 범람으로 인해 문서의 용량을 기형적으로 뻥튀기시켜 모뎀 시절의 인터넷 속도로는 페이지 하나를 띄우는 데 수 분이 걸리게 만드는 주범이었습니다.

<!-- 과거의 끔찍했던 하드코딩 방식: HTML 태그에 직접 디자인을 구겨 넣음 -->
<table width="100%" border="0">
    <tr>
        <td align="center">
            <h1><font color="blue" face="Arial" size="6"><b>웹사이트 첫 번째 페이지 제목</b></font></h1>
        </td>
    </tr>
</table>

2. 구원자의 등장: CSS (Cascading Style Sheets)의 철학

이러한 끔찍한 대혼란을 잠재우고 웹의 질서를 바로잡기 위해 W3C(월드 와이드 웹 컨소시엄)는 1996년 12월, 하콘 비움 리(Håkon Wium Lie)와 버트 보스(Bert Bos)가 제안한 CSS(Cascading Style Sheets)를 웹의 새로운 표준으로 채택했습니다. CSS가 들고 나온 핵심 철학은 단 하나였습니다. "HTML은 오직 정보의 구조(Structure)와 의미(Semantics)만을 정의하는 본연의 역할로 돌아가고, 시각적인 디자인(Presentation)은 CSS라는 완전히 새로운 언어가 전담하게 하라!" 이것이 바로 오늘날 소프트웨어 공학에서 가장 중요하게 다루는 '관심사의 분리(Separation of Concerns)'가 웹의 세계에 구현된 역사적인 순간이었습니다.

3. HTML과 CSS를 완벽히 분리해야 하는 4가지 폭발적인 실무적 이유

오늘날 모든 웹 프론트엔드 개발은 HTML과 CSS를 철저하게 분리하여 작성하는 것을 절대적인 원칙으로 삼고 있습니다. 단순히 코드가 예뻐 보이는 것을 넘어서, 이 분리가 가져다주는 비즈니스적이고 기술적인 이점은 실로 엄청납니다. 실무 관점에서 이 4가지 이유를 깊이 있게 살펴보겠습니다.

① 유지보수(Maintenance) 비용의 혁명적 단축

앞선 예시에서 100개의 HTML 파일을 수정해야 했던 지옥 같은 작업이, CSS를 외부 파일로 분리하는 순간 style.css 파일 딱 한 곳만 수정하면 되는 1초짜리 작업으로 변모합니다. 10,000개의 페이지가 하나의 CSS 파일을 공유하고 있다면, 그 CSS 파일 안의 색상 값 하나만 변경해도 10,000개 페이지의 디자인이 일제히 업데이트됩니다. 이러한 중앙 집중식 스타일 관리는 대형 서비스의 디자인 개편이나 버그 수정에 들어가는 개발 인력과 시간을 수백 배 단축시켜 줍니다.

/* style.css 단 한 곳에서 수만 개 웹페이지의 디자인을 중앙 통제합니다 */
h1 {
    color: red;
    font-family: 'Arial', sans-serif;
    text-align: center;
}

② 검색 엔진 최적화(SEO)와 시각 장애인 접근성(A11y) 향상

구글(Google)이나 네이버의 검색 로봇(Crawler)은 시각적인 디자인을 볼 수 없습니다. 오로지 텍스트(HTML) 문서 구조만 읽고 사이트의 가치를 평가합니다. HTML 태그에 디자인 코드가 덕지덕지 붙어있으면, 로봇은 문서의 핵심 키워드를 찾지 못해 혼란에 빠지고 검색 순위는 나락으로 떨어집니다. 반면, CSS를 걷어내고 순수한 시맨틱(Semantic) 태그로만 이루어진 깨끗한 HTML 문서는 검색 로봇이 내용을 100% 완벽하게 이해할 수 있게 해 주어 검색 엔진 상위 노출에 엄청난 가산점을 받습니다. 또한, 시각 장애인들이 사용하는 화면 낭독기(Screen Reader) 역시 불필요한 디자인 코드를 건너뛰고 핵심 정보만을 정확한 억양과 순서로 사용자에게 읽어줄 수 있게 되어 웹 접근성(Web Accessibility)이 극적으로 향상됩니다.

③ 브라우저 캐싱(Caching)을 통한 극한의 네트워크 성능 최적화

CSS를 별도의 .css 파일로 분리하여 서버에 올려두면, 사용자가 웹사이트의 첫 페이지에 접속할 때 브라우저는 해당 CSS 파일을 다운로드하여 사용자 컴퓨터의 하드디스크나 메모리에 임시로 저장(캐싱)해 둡니다. 사용자가 두 번째, 세 번째 페이지로 이동할 때, 브라우저는 매번 무거운 디자인 코드를 다시 다운로드하지 않고 컴퓨터에 저장해 둔 CSS 파일을 0.001초 만에 불러와서 재사용합니다. 즉, HTML 문서의 용량이 극단적으로 다이어트되어 네트워크 데이터 소모가 줄어들고, 페이지 로딩 속도가 비약적으로 상승하게 됩니다. 모바일 환경에서 이 속도 차이는 사용자의 이탈률을 결정짓는 치명적인 요소입니다.

④ 하나의 구조, 다양한 기기 대응 (반응형 웹의 근간)

HTML이 오직 뼈대만 가지고 있다면, 우리는 CSS라는 옷만 바꿔 입혀서 똑같은 문서를 데스크톱용, 태블릿용, 스마트폰용, 심지어 종이 인쇄용으로 자유자재로 변신시킬 수 있습니다. 이것이 바로 미디어 쿼리(Media Queries)를 활용한 반응형 웹 디자인(Responsive Web Design)의 근본 원리입니다. 만약 HTML 안에 디자인이 하드코딩되어 있었다면, 모바일용 웹사이트를 만들기 위해 똑같은 내용을 담은 새로운 HTML 파일 100개를 다시 만들어야 했을 것입니다.

4. 디자인을 입히는 타겟팅 기술: CSS 선택자(Selectors)의 세계

HTML과 CSS를 분리했다면, 이제 CSS 파일 안에서 "HTML 문서의 어느 부분(어떤 태그)에 이 디자인을 입힐 것인가?"를 정확하게 지목(Targeting)하는 수단이 필요합니다. 그것이 바로 선택자(Selector)입니다. 선택자는 실무에서 가장 많이 쓰이는 4가지 기본 형태로 나뉩니다.

  • 전체 선택자 (*): 문서 내의 모든 요소에 무식하게 스타일을 쏟아붓습니다. 보통 여백 초기화 등 브라우저 기본 스타일을 리셋할 때 씁니다.
  • 태그 선택자 (h1, p): 특정 HTML 태그를 모두 찾아 스타일을 적용합니다. 폰트나 줄 간격 등 사이트 전체의 기본 틀을 잡을 때 유용합니다.
  • 클래스 선택자 (.classname): 요소에 class 속성을 부여하고, 점(.)을 찍어 선택합니다. 가장 자유롭게 이름을 지을 수 있으며, 여러 요소에 중복해서 사용할 수 있어 실무 CSS 아키텍처의 90% 이상을 차지하는 핵심 선택자입니다.
  • 아이디 선택자 (#idname): 요소에 id 속성을 부여하고, 해시(#)를 찍어 선택합니다. 아이디는 한 페이지 내에서 무조건 유일(Unique)해야 하므로 재사용이 불가능합니다. 때문에 최신 실무에서는 스타일링 목적으로는 아이디 선택자를 거의 사용하지 않고, 자바스크립트 조작이나 책갈피 기능에 양보하는 추세입니다.

정리하며

이번 글에서는 초창기 웹의 흑역사부터 시작하여, CSS가 웹 프론트엔드 생태계에 가져온 위대한 '분리의 패러다임'을 깊이 있게 살펴보았습니다. CSS가 없었다면 지금의 화려하고 빠르며 다양한 기기에 적응하는 모던 웹은 절대 탄생할 수 없었을 것입니다. 이제 우리는 HTML과 CSS가 왜 남남이 되어야 했는지, 그리고 CSS가 어떻게 HTML의 특정 요소를 콕 찝어내는지 완벽하게 이해했습니다. 그렇다면 다음 단계는 무엇일까요? 선택자로 콕 찝어낸 그 HTML 요소들은 과연 브라우저 화면 안에서 어떤 형태로 존재하고 있을까요? 놀랍게도 브라우저가 바라보는 모든 요소는 '투명한 직사각형 박스'입니다. 다음이번 포스팅에서는 웹 레이아웃을 지배하는 절대 법칙이자 프론트엔드 개발자들의 첫 번째 통곡의 벽, "CSS Box Model: 마진과 패딩의 완벽한 이해"에 대해 상세히 파헤쳐 보겠습니다!


CSS Box Model - 브라우저가 세상을 바라보는 네모난 시선

웹 개발에 입문하여 CSS로 글씨 색깔도 바꿔보고 배경색도 칠해보다 보면, 문득 "이제 버튼들을 옆으로 나란히 배치해 볼까?" 또는 "이 사진과 텍스트 사이의 간격을 좀 띄워볼까?" 하는 시도를 하게 됩니다. 그리고 이때부터 끔찍한 절망이 시작됩니다. 분명히 간격을 10px 주었는데 옆으로 튕겨 나가고, 테두리를 그렸더니 레이아웃 전체가 아래로 와르르 무너져 내리는 기이한 현상들을 마주하게 됩니다. 구글에 검색해 가며 margin, padding 값을 이것저것 아무렇게나 때려 맞추다 보면 어찌어찌 화면은 예뻐지지만, 코드는 누더기가 되어버립니다. 왜 이런 일이 발생할까요? 그것은 바로 웹 브라우저가 HTML 요소들을 화면에 그릴 때 사용하는 절대적인 수학 공식, 'CSS 박스 모델(Box Model)'의 작동 원리를 완벽하게 이해하지 못했기 때문입니다. 브라우저의 눈에는 동그란 프로필 사진도, 구불구불한 텍스트 단락도 결국은 '4겹의 투명한 직사각형 박스'일뿐입니다. 이번 글에서는 프론트엔드 레이아웃의 근간이자 수많은 주니어 개발자들을 울리는 Box Model의 해부학적 구조와 '마진 병합(Margin Collapsing)'이라는 미스터리한 현상, 그리고 이 모든 고통을 한 방에 해결해 주는 box-sizing 마법에 대해 상세히 알아보겠습니다.

1. 모든 것은 네모다: 박스 모델의 4가지 계층 구조

CSS 박스 모델은 요소 하나가 화면에서 차지하는 공간을 양파 껍질처럼 4개의 층으로 나누어 정의합니다. 가장 안쪽부터 바깥쪽으로 순서대로 살펴보겠습니다.

① Content (콘텐츠): 심장부

텍스트나 이미지가 실제로 표시되는 알맹이 공간입니다. 우리가 CSS에서 widthheight를 지정하면, 기본적으로 이 Content 영역의 가로/세로 크기만을 의미하게 됩니다. (이것이 나중에 엄청난 비극을 불러옵니다.)

② Padding (패딩): 안쪽 여백과 쿠션

Content와 테두리(Border) 사이의 푹신한 내부 여백입니다. 패딩의 가장 중요한 특징은 '배경색(background-color)이 적용되는 영역'이라는 점입니다. 버튼을 만들 때 글씨 주변으로 넉넉하게 색상이 칠해진 공간을 만들고 싶다면, width를 키우는 것이 아니라 padding을 빵빵하게 주어야 클릭하기도 편하고 아름다운 버튼이 완성됩니다.

③ Border (보더): 영역의 경계선, 테두리

패딩을 감싸는 딱딱한 외곽선입니다. 두께(px), 스타일(solid, dashed 등), 색상을 지정할 수 있습니다. 보더는 물리적인 두께를 가지기 때문에, 1px의 보더를 추가하면 박스의 전체 너비는 좌우 합쳐서 2px이 늘어나게 됩니다. 레이아웃이 1px 차이로 틀어질 때 가장 먼저 의심해 봐야 할 범인입니다.

④ Margin (마진): 남을 밀어내는 바깥 여백 (절대 거리)

테두리 바깥쪽에 존재하는 보이지 않는 쉴드(Shield) 영역입니다. 나와 옆에 있는 다른 요소 사이의 간격을 벌릴 때 사용합니다. 마진의 핵심은 '완전히 투명하다'는 것입니다. 마진 영역에는 어떠한 배경색이나 이미지도 칠해지지 않으며, 오직 투명한 진공 공간으로서 남을 밀어내는 역할만 수행합니다.

2. 주니어 개발자의 통곡의 벽: 마진 병합(Margin Collapsing) 미스터리

박스 모델을 배운 초보자들이 실무에서 가장 먼저 마주하는 기괴한 현상이 있습니다. "위 박스에 margin-bottom: 20px을 주고, 아래 박스에 margin-top: 30px을 주면 두 박스 사이의 거리는 50px이 되겠지?"라고 계산하지만, 브라우저에서 확인해 보면 거리는 고작 30px밖에 되지 않습니다. 브라우저가 고장 난 걸까요? 아닙니다. 이것은 W3C가 의도적으로 설계한 '마진 병합(Margin Collapsing)'이라는 현상입니다.

마진 병합은 블록(Block) 요소들이 위아래로 인접해 있을 때, 두 요소의 마진이 서로 닿으면 더 큰 마진 값 하나로 흡수 통합되어 버리는 현상입니다. (좌우 마진은 병합되지 않고 온전히 덧셈이 됩니다.) 왜 이런 이상한 규칙을 만들었을까요? 과거 웹은 텍스트 중심이었고, 문단(<p>)과 문단 사이의 간격을 일정하게 유지하기 위해서였습니다. 모든 <p> 태그에 위아래 마진 20px이 있다면, 문단과 문단 사이의 거리가 40px로 벌어지는 것을 막고 20px로 우아하게 합쳐주기 위한 W3C의 배려(?)였습니다. 하지만 현대의 복잡한 그리드 레이아웃 시대에는 이 배려가 레이아웃 붕괴의 원인이 되곤 합니다.

더 끔찍한 부모-자식 간의 마진 병합

형제 요소 간의 병합은 양반입니다. 부모 박스 안에 자식 박스가 있고, 부모 박스에 테두리(Border)나 패딩(Padding)이라는 '벽'이 없는 상태에서 자식 박스에 margin-top을 주면 어떻게 될까요? 자식만 쑥 내려가는 것이 아니라, 자식의 마진이 부모를 뚫고 나가서 부모 박스 전체가 밑으로 처져 버리는 충격적인 현상이 발생합니다. 이를 막기 위해서는 부모 요소에 border: 1px solid transparent;를 주어 얇은 벽을 세우거나, padding-top: 1px;을 주거나, overflow: hidden;을 설정하여 블록 포맷팅 컨텍스트(BFC)를 강제로 생성하는 등의 실무 꼼수들이 필요합니다.

3. 레이아웃 붕괴를 막는 궁극의 마법: box-sizing: border-box

우리가 어떤 박스의 너비를 width: 100px;로 맞추어 놓았습니다. 그런데 디자인이 좀 밋밋해서 안쪽으로 여백을 주고자 padding: 20px;border: 1px solid black;을 추가했습니다. 놀랍게도 이 박스의 실제 브라우저 렌더링 가로 크기는 100px이 아니라 142px (콘텐츠 100 + 좌우 패딩 40 + 좌우 보더 2)로 비대하게 팽창해 버립니다. 당연히 옆에 있던 요소들은 42px만큼 공간이 부족해져서 아래 줄로 튕겨 나가게 됩니다.

기본적으로 CSS의 box-sizing 속성은 content-box로 설정되어 있기 때문입니다. width 값을 오직 '알맹이(Content)'의 크기로만 취급하고, 그 위에 패딩과 보더를 덧붙이는 족족 전체 덩치가 커져 버리는 수학 모델입니다. 레이아웃을 짤 때마다 개발자는 패딩과 보더 두께를 계산해서 width 값에서 빼주는 암산(100 - 40 - 2 = 58px)을 해야만 했습니다.

과거 인터넷 익스플로러(IE6) 시절, 마이크로소프트는 이 계산법을 버그로 구현하여 width: 100px을 주면 패딩과 보더를 합쳐서 무조건 100px을 유지하고 그 대신 안쪽 알맹이를 찌그러뜨리는 방식을 사용했습니다. 당시 W3C 표준주의자들은 MS를 맹비난했지만, 웹 디자인이 고도화될수록 프론트엔드 개발자들은 "아무리 생각해도 마이크로소프트의 박스 모델(전체 너비 고정 방식)이 레이아웃 잡기 훨씬 편한데?"라는 깨달음을 얻게 됩니다. 결국 W3C는 백기를 들고, IE의 렌더링 방식을 정식 CSS 속성으로 편입시켜 주었습니다. 그것이 바로 프론트엔드 평화의 상징, box-sizing: border-box입니다.

/* 💡 현대 웹 개발의 국룰(표준): 모든 웹사이트 최상단에 이 코드를 무조건 박고 시작합니다. */
* {
    box-sizing: border-box;
}

위 코드를 문서 최상단에 선언해 두면, 앞으로 여러분이 width: 100px;이라고 선언한 요소는 패딩을 10px을 주든 보더를 5px을 주든 무슨 짓을 해도 바깥 덩치가 100px을 절대 넘지 않게 됩니다. 대신 패딩이 늘어난 만큼 안쪽의 콘텐츠 여유 공간이 스스로 쪼그라들어 전체 크기를 방어합니다. 바깥 크기가 변하지 않으니, 더 이상 픽셀 계산 실수로 레이아웃이 무너지는 대참사는 발생하지 않습니다.

정리하며

이번 글에서는 화면을 구성하는 절대적인 뼈대인 Box Model의 4가지 계층과, 주니어 개발자들을 혼란에 빠뜨리는 마진 병합 현상, 그리고 수학적 스트레스를 날려버리는 border-box의 위대한 마법까지 깊이 있게 파헤쳐 보았습니다. 모든 HTML 요소가 보이지 않는 직사각형 박스라는 사실을 인지했다면, 이제 우리는 100px 같은 하드코딩된 크기 지정을 넘어서야 합니다. 스마트폰, 태블릿, 데스크톱 등 화면 크기가 쉴 새 없이 변하는 모던 웹 환경에서 박스의 크기를 유연하게 늘리고 줄이려면 어떤 '단위'를 써야 할까요? 다음이번 포스팅에서는 단순한 픽셀(px)을 넘어 퍼센트(%), rem, vw 등 화면을 고무줄처럼 다루는 "CSS 단위(Units) 완벽 가이드: 상대 단위와 뷰포트 단위의 마법"에 대해 상세히 파헤쳐 보겠습니다!


CSS 단위(Units) 완벽 가이드 - 픽셀(px)의 감옥에서 탈출하여 유연함을 얻다

웹 디자인을 시작할 때 우리가 가장 먼저 집어 드는 무기는 픽셀(px)입니다. width: 300px;, font-size: 16px; 등 직관적이고 절대적인 수치를 제공하는 px은 포토샵이나 일러스트레이터 같은 고전적인 그래픽 툴에 익숙한 디자이너와 개발자들에게 엄청난 안도감을 줍니다. 하지만 모던 웹 개발의 세계로 깊숙이 들어가는 순간, 이 '절대적인 고정 단위'는 반응형 웹(Responsive Web)과 웹 접근성(Accessibility)을 파괴하는 거대한 족쇄로 돌변합니다. 사용자가 손바닥만 한 아이폰으로 접속하든, 거대한 32인치 4K 모니터로 접속하든 똑같이 300px의 크기를 고집하는 요소는 필연적으로 화면 밖으로 잘리거나 현미경으로 봐야 할 만큼 작아지는 촌극을 빚어냅니다. 현대 프론트엔드의 진정한 고수가 되기 위해서는 고정된 px의 감옥에서 벗어나, 주변 환경에 맞춰 카멜레온처럼 크기를 바꾸는 **상대 단위(Relative Units)**와 **뷰포트 단위(Viewport Units)**를 자유자재로 다뤄야만 합니다. 이번 글에서는 실무에서 필수적으로 쓰이는 %, em, rem의 치명적인 차이점과 모바일 레이아웃의 구세주 vw, vh, 그리고 골치 아픈 '100vh 모바일 스크롤 버그'의 원인 및 해결책까지 상세히 알아보겠습니다.

1. 불변의 단위와 그 착각: 픽셀(px)은 진짜 픽셀이 아니다?

우리는 흔히 1px이 모니터 화면의 진짜 빛나는 점(Dot) 하나라고 생각합니다. 과거 1990년대 배불뚝이 CRT 모니터 시절에는 그것이 사실이었습니다. 하지만 애플의 레티나(Retina) 디스플레이를 필두로 초고해상도 스마트폰 시대가 열리면서 이 공식은 산산조각 났습니다. 최신 스마트폰은 물리적인 모니터 픽셀은 엄청나게 많지만, CSS가 그 물리적 픽셀 1개에 1px을 매칭 시켜버리면 웹사이트의 글씨가 개미만큼 작아져서 읽을 수 없게 됩니다. 그래서 W3C와 브라우저 제조사들은 'CSS 픽셀(논리적 픽셀)'이라는 개념을 만들었습니다. 고해상도 기기에서는 CSS의 1px을 그리기 위해 기기의 실제 물리적 픽셀 4개(2x2) 또는 9개(3x3)를 병합하여 사용합니다. 즉, 우리가 코드에 적는 px은 더 이상 절대적인 물리 단위가 아니라, 기기의 디스플레이 픽셀 비율(Device Pixel Ratio)에 따라 고무줄처럼 늘어나는 가상의 기준점일 뿐입니다. 그럼에도 불구하고 px은 여전히 부모나 화면 크기가 변해도 '자기 자신의 크기를 꿋꿋이 유지하려는' 속성이 강하기 때문에, 레이아웃 골조보다는 1px 테두리 두께(Border)나 미세한 그림자(Box-shadow) 오프셋을 조정할 때만 제한적으로 사용하는 것이 현대 웹의 트렌드입니다.

2. 상대 단위의 핵심: 백분율(%), em, 그리고 구원자 rem

반응형 웹을 구축하려면 부모나 화면 환경에 따라 크기가 알아서 수학적으로 계산되는 상대 단위를 써야 합니다. 상대 단위를 지배하는 자가 곧 반응형 웹을 지배합니다.

① 직관적인 비율: 백분율 (%)

width: 50%;는 아주 직관적입니다. "나를 감싸고 있는 직속 부모 박스 너비의 딱 절반만 차지할게!"라는 뜻입니다. 하지만 백분율에는 주니어 개발자들을 미치게 만드는 함정이 하나 숨어있습니다. 만약 자식 요소에 padding-top: 50%를 주면, 이 50%는 부모의 '높이'를 기준으로 계산될까요? 아닙니다! 마진과 패딩에 적용된 % 단위는 무조건 부모 요소의 '너비(Width)'를 기준으로 수학적 계산이 들어갑니다. 이 기이한 스펙 덕분에 우리는 aspect-ratio 속성이 없던 과거 시절에, 유튜브 영상이나 반응형 이미지를 16:9 비율로 찌그러지지 않게 끼워 넣는 'Padding Hack'이라는 마법 같은 꼼수를 부릴 수 있었습니다.

② 폰트 기반 상대 단위: em의 복리 이자 재앙

em 단위는 인쇄 타이포그래피에서 유래했으며, '자기 자신의 요소에 적용된 폰트 사이즈'를 1em으로 취급합니다. 폰트가 16px이면 1em도 16px입니다. padding: 2em;을 주면 글씨가 커지면 패딩도 같이 두꺼워지므로 버튼의 비율을 유지하는 데 환상적입니다. 하지만 폰트 사이즈 지정에 em을 쓰기 시작하면 지옥문이 열립니다. 부모가 font-size: 1.2em을 주고, 그 자식이 font-size: 1.2em을 주면 크기가 누적곱(1.2 * 1.2)되어 손자 뻘로 가면 글씨가 집채만 해집니다. 문서가 깊어질수록 크기를 예측할 수 없게 되는 '중첩 재앙(Compounding Issue)'이 발생합니다.

③ 현대 웹 타이포그래피의 표준: rem (Root em)

em의 끔찍한 연쇄 팽창을 막기 위해 rem(Root em)이라는 구원자가 등장했습니다. rem은 부모가 누구든 족보를 완전히 무시하고 오직 최상단 뿌리 태그인 <html>에 지정된 폰트 사이즈(기본 16px) 단 하나만을 바라봅니다. 1rem = 16px, 2rem = 32px로 계산이 극도로 직관적입니다. 게다가 사용자가 시력이 나빠 브라우저 설정에서 기본 글꼴 크기를 아주 크게 키우면, px로 짠 사이트는 글씨가 커지지 않지만 rem으로 짠 사이트는 모든 레이아웃과 글씨가 사용자 설정에 맞춰 유연하게 뻥튀기됩니다. 즉, rem을 사용하는 것은 선택이 아니라 시각 장애인과 저시력자를 위한 웹 접근성(A11y) 확보의 필수 의무입니다.

3. 브라우저 창을 기준점으로: 뷰포트(Viewport) 단위와 100vh 버그

백분율(%)은 훌륭하지만 항상 '부모'의 크기에 종속된다는 맹점이 있습니다. "부모가 아무리 좁아터졌어도, 나는 브라우저 전체 화면을 꽉 채우는 팝업창이 될 거야!"라고 선언하고 싶을 때 등장하는 것이 뷰포트 단위입니다.

  • vw (Viewport Width): 브라우저 창(화면) 전체 너비의 1/100 크기입니다. 50vw는 현재 화면 너비의 절반(50%)을 의미합니다.
  • vh (Viewport Height): 브라우저 창(화면) 전체 높이의 1/100 크기입니다. 랜딩 페이지 맨 처음에 화면을 꽉 채우는 대형 배경 이미지를 넣을 때 height: 100vh;는 거의 공식처럼 쓰입니다.

🚨 모바일 브라우저의 100vh 스크롤 버그와 차세대 단위

모바일 사파리(iOS)나 크롬(Android)에서 height: 100vh;를 주면, 화면 하단에 있는 버튼이 잘려서 안 보이는 기괴한 버그가 수년간 전 세계 프론트엔드 생태계를 괴롭혀왔습니다. 원인은 모바일 브라우저 상단의 주소창(URL bar)과 하단 네비게이션 바가 뷰포트 높이 계산에 꼈다 빠졌다 하면서 혼란을 주기 때문이었습니다. 브라우저 제조사들은 '주소창이 보이는 상태의 높이'와 '스크롤해서 주소창이 숨겨진 상태의 높이' 사이에서 100vh를 어찌할 바를 몰라 헤맸습니다. 이를 해결하기 위해 수많은 자바스크립트 꼼수들이 난무했지만, 최근 W3C는 이 문제를 원천 해결하는 강력한 차세대 단위들을 발표했습니다.

  • dvh (Dynamic Viewport Height): 주소창이 생기고 사라질 때마다 화면의 실제 보여지는 높이를 실시간(Dynamic)으로 재계산하여 빈틈없이 맞춰줍니다. 모바일 웹 풀스크린 개발의 궁극적인 정답입니다.
  • svh (Small) / lvh (Large): 주소창이 가장 크게 펼쳐져서 화면이 제일 좁을 때(Small)와, 주소창이 사라져서 화면이 제일 클 때(Large)를 고정적으로 참조하는 하드코어 단위입니다.

정리하며

이번 글에서는 절대 굽히지 않는 고집쟁이 px부터, 똑똑하지만 위험한 복리 이자 em, 접근성의 수호자 rem, 그리고 모바일의 한계를 돌파한 dvh까지 웹 레이아웃을 주무르는 마법의 단위 체계를 완벽하게 파헤쳐 보았습니다. 단위에 대한 확고한 철학이 세워졌다면, 뼈대에 생명을 불어넣을 차례입니다. 인간의 눈길을 가장 먼저 사로잡는 것은 결국 화려한 '색상'과 감각적인 '배경'입니다. 단순히 color: red를 넘어서, 심리학이 섞인 컬러 모델과 이미지를 화면에 까는 정밀한 기법들. 다음이번 포스팅에서는 화면을 화려한 캔버스로 바꿔버리는 "CSS Color & Background: 헥스, RGB, HSL 컬러의 세계와 배경 이미지 완벽 제어"에 대해 상세히 파헤쳐 보겠습니다!


CSS Display - 레이아웃의 뿌리, Block과 Inline의 본질적 차이

우리가 HTML 문서에 <div> 박스 3개를 연달아 코딩하고 결과를 확인해 보면, 신기하게도 세 박스가 나란히 옆으로 붙지 않고 층층이 위아래로 쌓여 있는 모습을 보게 됩니다. 반대로 <a> 태그나 <span> 태그를 여러 개 쓰면, 이번에는 위아래로 쌓이지 않고 글씨처럼 좌우로 길게 나열됩니다. 대체 브라우저 안에서는 무슨 보이지 않는 룰(Rule)이 존재하길래 어떤 녀석은 줄 바꿈을 일으키고 어떤 녀석은 가로로 붙어 버리는 걸까요? 이 모든 마법 같은 배치를 결정짓는 절대적인 단 하나의 CSS 속성, 그것이 바로 'Display' 속성입니다. display 속성을 완벽하게 장악하지 못하면, 버튼에 패딩(Padding)을 주었는데 위아래로 겹쳐버린다거나, 폭(Width)을 지정했는데 아무런 반응이 없는 등 수많은 기괴한 버그들을 해결하지 못한 채 구글링만 반복하게 됩니다. 이번 글에서는 웹 브라우저가 화면에 요소를 배치하는 'Normal Flow(일반 흐름)'의 양대 산맥인 Block과 Inline의 DNA 구조적 차이, 그리고 둘의 장점만 악마처럼 섞어놓은 Inline-block 하이브리드, 마지막으로 화면에서 요소를 완벽하게 증발시키는 display: none의 이면까지 다뤄보겠습니다.

1. 공간 독재자: Block 레벨 요소의 횡포

우리가 흔히 쓰는 <div>, <p>, <h1> 태그들은 날 때부터 display: block이라는 유전자를 가지고 태어납니다. Block 요소의 가장 큰 특징은 극단적인 '가로 공간 독식(Monopoly)'입니다. Block 요소는 자신의 내용물(텍스트)이 한 글자뿐이라도, 자신이 속한 부모 컨테이너의 가로 넓이(Width) 100%를 무조건 강제로 밀고 나가서 차지해 버립니다.

그 결과, 그 옆에는 다른 어떤 요소도 존재할 수 없게 되어 자연스럽게 '강제 줄 바꿈(Line Break)'이 발생합니다. 이것이 바로 우리가 <div>를 여러 개 쓰면 세로로 층층이 쌓이는 벽돌집 레이아웃이 만들어지는 이유입니다. 공간을 확실하게 차지하는 특성 덕분에, width, height, margin, padding 속성 4가지가 동서남북 사방으로 완벽하게 제어된다는 압도적인 장점을 가집니다. 페이지의 뼈대나 큰 구역을 나눌 때는 무조건 Block 요소를 사용해야 합니다.

2. 유연한 물줄기: Inline 레벨 요소의 한계

반면 <span>, <a>, <strong> 태그들은 display: inline 유전자를 가지고 있습니다. Inline은 문자 그대로 '문장 안의 한 줄'처럼 행동합니다. 자신이 품고 있는 알맹이(텍스트 내용물)의 크기만큼만 최소한의 공간을 차지하며, 공간이 허락하는 한 옆에 있는 다른 Inline 요소들과 사이좋게 좌우로 찰싹 달라붙습니다. 만약 브라우저 끝에 도달하면 자연스럽게 다음 줄로 흐르듯(Flow) 줄 바꿈이 일어납니다.

🚨 주니어 개발자를 울리는 Inline의 치명적 함정

Inline 요소는 태생적으로 문서의 '글씨(Text)'를 꾸미기 위해 만들어진 녀석입니다. 박스 레이아웃을 잡기 위한 용도가 아닙니다. 그래서 Inline 요소에는 widthheight를 아무리 숫자로 때려 넣어도 브라우저가 철저하게 무시해 버립니다. 더 환장할 노릇은 marginpadding입니다. 좌우 여백은 정상적으로 옆 요소를 밀어내며 작동하지만, 위아래(Top, Bottom) 여백은 시각적으로 색칠만 될 뿐, 위아래에 있는 다른 줄의 요소들을 물리적으로 밀어내지 못하고 글씨 위에 겹쳐버리는(Overlap) 끔찍한 사태를 유발합니다. <a> 태그로 링크 버튼을 만들었는데 윗단락 글씨를 파먹고 올라간다면, 100% Inline의 특성을 간과한 것입니다.

3. 두 세계의 융합: 하이브리드 Inline-block

"Block처럼 가로세로 크기와 마진을 내 마음대로 픽셀 단위로 통제하고 싶어! 그런데 Inline처럼 옆으로 나란히 배치됐으면 좋겠어!" 이 인간의 끝없는 욕심을 채워주기 위해 탄생한 변종 돌연변이가 바로 display: inline-block입니다.

실무에서 가장 많이 쓰이는 곳은 바로 네비게이션 메뉴(GNB)의 버튼들이나 페이지네이션(1, 2, 3...) 번호 버튼입니다. 기본적으로 <a> 태그나 <li>를 사용하여 가로로 나란히(Inline) 배치한 뒤, inline-block 속성을 덮어씌워 padding: 10px 20px; 같은 쿠션을 빵빵하게 주어 클릭하기 편한 통통한 버튼으로 변신시키는 것이 프론트엔드 퍼블리싱의 가장 기초적이고 완벽한 패턴입니다.

4. 화면에서 삭제하기: display: none vs visibility: hidden

모달 팝업창이나 드롭다운 메뉴를 만들 때, 우리는 평소에는 화면에 박스를 숨겨두었다가 클릭 시 나타나게 해야 합니다. 이때 사용되는 두 속성은 결과는 같아 보이지만 구조적으로는 천지차이입니다.

  • display: none: 요소를 브라우저의 화면 렌더링 트리(Render Tree)에서 흔적도 없이 완전히 뜯어내어 삭제해 버립니다. 당연히 요소가 차지하던 공간(박스 크기)도 소멸되어 주변 요소들이 그 자리를 메꾸기 위해 땡겨져 옵니다. 시각 장애인을 위한 스크린 리더기도 이 요소를 읽지 못하고 완전히 무시합니다.
  • visibility: hidden: 요소를 투명 인간으로 만듭니다. 눈에는 보이지 않지만, 브라우저는 그 요소가 차지하던 가로세로 부피(공간)를 그대로 유지하여 화면에 빈 구멍(여백)을 남겨둡니다. 스크린 리더기도 투명 인간이 된 요소의 텍스트 내용을 정상적으로 읽어냅니다. 접근성을 유지해야 하거나 공간 레이아웃이 무너지는 것을 막아야 할 때 전략적으로 사용합니다.

정리하며

이번 글에서는 화면에 요소를 배치하는 가장 기초적인 룰, Block과 Inline의 차이점을 파헤쳐 보았습니다. 내가 쓴 코드가 почему 옆으로 안 붙는지, 왜 높이 값이 안 먹히는지 며칠 밤낮을 새워 고민하던 주니어 시절의 의문이 이 display 속성 하나로 명쾌하게 해소되었을 것입니다. 그런데 지금까지 우리가 배운 이 'Normal Flow(일반 흐름)'는 위에서 아래로 순서대로 차곡차곡 쌓이는 중력의 법칙을 철저히 따릅니다. 만약 쇼핑몰의 우측 하단에 '최상단으로 가기(Top)' 버튼을 스크롤과 무관하게 허공에 계속 둥둥 띄워놓고 싶다면 어떻게 해야 할까요? 브라우저의 중력(Normal Flow)을 거스르고 요소를 3차원 공간으로 뜯어내는 강력한 마법, 다음이번 포스팅에서는 프론트엔드 공간 지배술의 핵심인 "CSS Positioning: relative, absolute, fixed, sticky의 완벽한 해부"에 대해 상세히 자세히 알아보겠습니다.

반응형