[번역] CSS 수직 가운데 정렬 (테이블 없이!) Vertical Centering with CSS

, ,

2017-01-05

이제는 세월이 흘러 IE8 지원도 하지 않는 곳이 많아졌다. IE6까지 지원하느라 고생했던 것을 생각하면 격세지감이다. 하지만 여전히 아래 기법들은 유효하다. (IE5~6을 지원하기 위한 코드 자체를 제외하면.)

display: flex를 대부분의 브라우저에서 사용할 수 있게 된 것도 희소식이다. 수직 가운데 정렬이 훨씬 편해졌다. 다만 IE9이 이걸 지원하지 않는다는 점이 문제다.

이 글은 2009년에 나온 글이라 플렉스박스에 대한 설명은 없다. display: flex를 이용한 기법을 간단하게 살펴 보고 싶다면 Vertical Centering in CSS를 참고하라.

기본적으로 flexbox를 이용한 수직 가운데 정렬은 아래 코드를 이용하면 된다. (2021년 6월 4일에 추가함.)

.flex-container {
  display: flex;
  align-items: center;
  justify-content: center;
}

원문은 CSS로 수직 가운데 정렬 하기(Vertical Centering with CSS)다.12 2009년 2월 말에 씌어졌고, 이 때는 아직 IE8 베타 버전만 나와있던 때다. IE8 정식 버전은 2009년 3월 19일에 출시됐다.


CSS로 요소를 수직 가운데 정렬하는 몇 가지 방법이 있다. 하지만 적당한 걸 고르기는 힘들다. 나는 내가 본 것 중에 최고의 방법을 보여 주겠다. 수직 가운데로 잘 정렬된 간단한 웹사이트를 만드는 방법도 보여 주겠다.

CSS로 수직 가운데 정렬을 하는 것은 쉽지 않다. 서로 다른 방법들이 있고, 어떤 방법은 몇몇 브라우저에서는 작동하지 않는다. CSS를 다룰 때, 작동이 어떻게 되는지 아는 것은 중요하다. 그러니 요소를 수직 가운데 정렬하는 5가지 방법을 살펴 보자. 그리고 장단점을 살펴 보자. (각 방법을 간략하게 요약 설명한 테스트 페이지를 볼 수 있다.

(테스트 페이지 보기)

일러 두기: 나는 오스트레일리아인이다. 그래서 center라고 쓰지 않고 centre라고 쓴다. css에서는 center라고 써야 하니 헷갈리지 말기 바란다.

방법 1

(테스트 페이지 보기)

이 방법은 테이블처럼 만들어서 테이블의 vertical-align 속성을 사용할 수 있도록 <div>를 몇 개 세팅한다. (테이블의 vertical-align 속성은 다른 요소들에서는 굉장히 다르게 작동한다.)

일러 두기: 아마 “테이블을 사용하려던 건 아닌데” 하고 생각할지 모르겠다. 테이블을 사용할 때 주된 문제는 마크업을 시멘틱하게 유지해야 한다는 것이다. 테이블 코드를 짜지만 그게 사실 진짜 표는 아닌 경우, 테이블로 코딩하지 말아야 한다. (실제 표인 경우엔 테이블을 사용해도 아무 문제 없다.) <div>를 테이블로 생각하게 만들어서 생기는 유일한 문제는 브라우저가 일관되게 작동하지 못하게 된다는 점이다. 이건 미친 짓은 아니다. adisplay:block;을 주거나 <h2>display:inline;을 주는 것과 비슷한 것뿐이다.

[역자 주 – 한 줄에 여러 요소를 넣어야 할 때는 display: table 대신 display: inline-table을 사용할 수도 있다.]

<div id="wrapper">
  <div id="cell">
    <div>
      Content goes here
    </div>
  </div>
</div>
#wrapper {
  display: table;
}
#cell {
  display: table-cell; 
  vertical-align: middle;
}

장점

  • 높이가 변해도 상관없다. (CSS에 높이를 지정하지 않아도 된다.)
  • wrapper에 공간이 없어도 내용이 잘리지 않는다.

단점

  • 인터넷 익스플로러에서 작동하지 않는다. (심지어 IE8 베타에서도.)
  • 태그 단계가 깊어진다. (그렇게 나쁜 건 아니다. 이건 주관적인 부분이다.)

방법 2

(테스트 페이지 보기)

이 방법은 position absolute를 사용한다. top을 50%로 설정하고 margin-top을 콘텐츠 높이 절반만큼 음수로 설정한다. 이것은 요소의 높이를 CSS에서 지정해야 한다는 걸 의미한다.

높이를 지정해 두기 때문에, 내용이 넘치면 div 밖으로 튀어나간다. 대신에 스크롤바가 생기도록 콘텐츠 divoverflow:auto;를 주고 싶을 것이다.

<div id="content">
  Content Here    
</div>
#content {
  position: absolute; 
  top: 50%; 
  height: 240px; 
  margin-top: -120px; /* 높이의 절반을 음수 마진으로 */
}

장점

  • 모든 브라우저에서 작동한다.
  • 태그가 깊이 들어가지 않는다.

단점

  • 충분한 공간이 없으면 내용이 잘린다. (div가 body 밑에 있고, 사용자가 브라우저 창을 줄이면, 스크롤바가 나타나지 않는다.)

방법 3

(테스트 페이지 보기)

이 방법은 내용 요소 위에 div를 두는 방법이다. 이 divheight: 50%;, margin-bottom: -content높이의절반; 이렇게 설정한다. content엔 clear 속성을 줘서 floatdiv 밑으로 오게 하면 content가 가운데 오게 된다.

<div id="floater"></div>
<div id="content">
  Content Here    
</div>
#floater {
  float: left; 
  height: 50%; 
  margin-bottom: -120px;
}
#content  {
  clear: both; 
  height: 240px; 
  position: relative;
}

장점

  • 모든 브라우저에서 작동한다.
  • 충분한 공간이 없을 때 (예컨대 윈도우 사이즈를 줄일 때) 콘텐츠가 잘리고 스크롤바가 나타난다.

단점

  • 내가 생각할 수 있는 유일한 단점은, 빈 요소가 사용된다는 점이다. (그렇게 나쁜 건 아니다. 이것 역시 주관적인 부분이다.)

방법 4

(테스트 페이지 보기)

이 방법은 높이와 너비를 지정하고 position: absolute;를 매긴 div를 사용한다. div는 그러면 top: 0;부터 bottom: 0;까지 뻗는다. 근데 사실 높이를 지정해 뒀기 때문에 그게 가능하진 않다. 그래서 margin: auto; 라고 주면 요소가 가운데로 가게 된다. 이건 우리가 흔히 블럭 요소에 margin: 0 auto; 를 적용해서 사용하는 수평 가운데 정렬과 아주 비슷하다.

<div id="content">
  Content Here    
</div>
#content {
  position: absolute; 
  top: 0; 
  bottom: 0; 
  left: 0; 
  right: 0;
  margin: auto; 
  height: 240px; 
  width: 70%;
}

장점

  • 쉽다

단점

  • 인터넷 익스플로러에서 작동하지 않는다. (심지어 IE8 베타에서도.)
  • 컨테이너에 공간이 없으면 내용이 스크롤바 없이 잘린다.

방법 5

(테스트 페이지 보기)

이 방법은 오직 한 줄짜리 텍스트만 수직 가운데 정렬을 해 준다. 간단히, line-height를 요소의 높이로 설장한다. 그러면 텍스트가 가운데로 간다.

<div id="content">
  Content
</div>
#content {
  height:100px; 
  line-height: 100px;
}

장점

  • 모든 브라우저에서 작동한다.
  • 공간이 없어도 잘리지 않는다.

단점

  • 오직 텍스트에서만 작동한다. (블럭 요소에선 작동하지 않는다.)
  • 한 줄 이상이 되면 (즉, 두 줄이 되면), 보기 싫게 깨진다.

이 방법은 버튼이나 한 줄짜리 텍스트 필드를 수직 가운데 정렬해야 하는 경우처럼 작은 요소들에 사용하기 유용하다.

어떤 방법을 사용할까?

내가 좋아하는 방법은 3번째 방법 — float시킨 요소와 clear를 적용한 콘텐츠를 사용하는 방법이다. 이 방법엔 치명적인 결점이 하나도 없다. 콘텐츠가 clear: both;돼 있으니, 우리는 다른 요소를 그 위에 얹을 수도 있다. 그리고 윈도우가 접혀도 콘텐츠가 그 위를 덮지 않는다. 데모를 봐라.

<div id="top">
  <h1>Title</h1>
</div>
<div id="floater"></div>
<div id="content">
  Content Here    
</div>
#floater {
  float: left; 
  height: 50%; 
  margin-bottom: -120px;
}
#top {
  float: right; 
  width: 100%; 
  text-align: center;
}
#content {
  clear: both; 
  height: 240px; 
  position: relative;
}

이제 어떻게 작동하는지 감이 잡힐 거다. 간단하지만 흥미로운 웹사이트를 만들어 보자! 최종 결과물은 아래처럼 보일 거다.

6

스텝 1

시멘틱한 마크업으로 시작하는 건 늘 좋은 일이다. 이것은 우리가 만들 페이지의 구조를 보여 준다.

  • #floater (콘텐츠를 수직 가운데로 밀어내기 위한 요소)
  • #centered (수직 가운데 정렬할 박스)
    • #side
      • #logo
      • #nav (순서 없는 목록 <ul>)
    • #content
  • #bottom (저작권 등)

이건 내가 사용할 xhtml 코드다.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>A Centred Company</title>
  <link rel="stylesheet" href="styles.css" type="text/css" media="all" />
</head>

<body>
  <div id="floater"></div>
  <div id="centered">

    <div id="side">
      <div id="logo"><strong><span>A</span> Company</strong></div>
      <ul id="nav">
        <li><a href="#">Home</a></li>
        <li><a href="#">Products</a></li>
        <li><a href="#">Blog</a></li>
        <li><a href="#">Contact</a></li>
        <li><a href="#">About</a></li>
      </ul>
    </div>

    <div id="content">

      <h1>Page Title</h1>

      <p>
      Holisticly re-engineer value-added outsourcing after process-centric collaboration and idea-sharing. 
      Energistically simplify impactful niche markets via enabled imperatives. 
      Holisticly predominate premium innovation after compelling scenarios. 
      Seamlessly recaptiualize high standards in human capital with leading-edge manufactured products. 
      Distinctively syndicate standards compliant schemas before robust vortals. 
      Uniquely recaptiualize leveraged web-readiness vis-a-vis out-of-the-box information. 
      </p>

      <h2>Heading 2</h2>

      <p>
      Efficiently embrace customized web-readiness rather than customer directed processes. 
      Assertively grow cross-platform imperatives vis-a-vis proactive technologies. 
      Conveniently empower multidisciplinary meta-services without enterprise-wide interfaces. 
      Conveniently streamline competitive strategic theme areas with focused e-markets. 
      Phosfluorescently syndicate world-class communities vis-a-vis value-added markets. 
      Appropriately reinvent holistic services before robust e-services. 
      </p>

    </div>

  </div>

  <div id="bottom">
    <p>
      Copyright notice goes here
    </p>
  </div>
</body>
</html>

스텝 2

페이지 레이아웃을 잡기 위해 기본적인 CSS를 잡는다. 이걸 styles.css에 넣는다. 스타일은 html 맨 위쪽에 연결해 뒀다.

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

body {
    background: url('page_bg.jpg') 50% 50% no-repeat #FC3;
    font-family: Georgia, Times, serifs;
}

#floater {
    position: relative;
    float: left;
    height: 50%;
    margin-bottom: -200px;
    width: 1px;
}

#centered {
    position: relative;
    clear: left;
    height: 400px;
    width: 80%;
    max-width: 800px;
    min-width: 400px;
    margin: 0 auto;
    background: #fff;
    border: 4px solid #666;
}

#bottom {
    position: absolute;
    bottom: 0;
    right: 0;
}

#nav {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    right: 70%;
    padding: 20px;
    margin: 10px;
}

#content {
    position: absolute;
    left: 30%;
    right: 0;
    top: 0;
    bottom: 0;
    overflow: auto;
    height: 340px;
    padding: 20px;
    margin: 10px;
}

콘텐츠를 수직으로 가운데 정렬하도록 만들기 위해서는 bodyhtml의 높이가 100%여야 한다. 높이는 paddingmargin 안쪽에 들어가는 것이니, paddingmargin은 0으로 해야 한다. 안 그러면 작은 margin을 보여 주려고 스크롤바가 나타난다.

float시킨 요소의 margin-bottom은 콘텐츠 높이(400px)의 절반인 -200px이다.

아래처럼 보여야 한다.

#centered의 너비는 80%다. 이렇게 하면 작은 스크린에서는 웹사이트가 작게 표현되고, 큰 스크린에서는 웹사이트가 크게 표현된다. (내 스크린은 약간 큰데, 낡은 웹사이트들 상당수가 좌측 상단 코너에 조그맣게 표현된다. 좀 짜증난다.) 이것은 유동 레이아웃으로 알려진 것이다. min-widthmax-width가 너무 커지거나 너무 작아지는 것을 막아 준다. Internet Explorer는 min&max width를 지원하지 않는다. 그렇긴 하지만 전용 expression 값을 이용해서 나중에 수정할 것이다. 당연히, 이렇게 하는 대신에 고정폭을 선택할 수도 있다.

#centeredposition: relative기 때문에, 우리는 그 안에 요소를 배치할 때 absolute position 요소를 사용할 수 있다. overflow: auto;#content에 적용됐기 때문에, 내용이 넘치면 스크롤바가 나타날 것이다. Internet Explorer는 overflow: auto;를 좋아하지 않는다. 우리가 높이를 말해 주기 전까지는 말이다. (단지 topbottom 위치를 지정하는 것으론 안 된다. 그리고 높이를 %로 지정해도 안 된다.)3 그래서 그렇게 했다.

스텝 3

마지막으로 할 것은 스타일을 추가해서 좀더 예쁘게 만드는 거다. 메뉴를 다듬어 보자.

#nav ul {
list-style: none;
padding: 0;
margin: 20px 0 0 0;
text-indent: 0;
}

#nav li {
padding: 0;
margin: 3px;
}

#nav li a {
display: block;
background-color: #e8e8e8;
padding: 7px;
margin: 0;
text-decoration: none;
color: #000;
border-bottom: 1px solid #bbb;
text-align: right;
}

#nav li a::after {
content: '

👇 카테고리 글 목록

, ,

대표글

댓글 남기기