CSS3의 유동적인 박스 모듈을 활용한 어플리케이션 레이아웃

이 글은 Simon Speich와 Robert Nyman의 Application Layout with CSS3 Flexible Box Module의 한국어 번역본입니다.

 

CSS3만의 유동적인 박스 레이아웃 모듈 덕분에 유동적인 어플리케이션 레이아웃을 제작하는 일이 매우  쉬워졌습니다. 이번 글에서는 화면 전체를 차지하면서 브라우저 창에 따라 유동적으로 사이즈가 변하며 추가적으로 드래그로 구역을 나누는 것이 가능한 간단한 어플리케이션 레이아웃을 만들어 볼 것입니다.

기존의 <div> 엘리먼트 대신에 몇가지 HTML5의 구조적 태그들을 사용해봅시다. 이번 예제는 코드를 좀 더 의미론적(semantic)일 뿐만 아니라 협업에 편리하도록 짜야하는데 그 이유는 id 속성이나 부모-자식 관계에 의존적이지 않는 CSS 타입 선택자를 이용하여 엘리먼트를 직접 조작하기 때문입니다.

어떻게 작동하는 지는 완성된 데모를 한 번 보시길 바랍니다.

1 단계: 수직으로 나열된 박스

우리는 body에 오직 세가지 태그들(<header>, <main>, <footer>)만으로 시작할 것입니다.

<!DOCTYPE html>
<html>
<head>
    <title>CSS3 Application Layout</title>
</head>

<body>
<header></header>
<main></main>
<footer></footer>
</body>
</html>

이 세 엘리먼트들이 수직으로 빈 공간에 가득 찰 수 있도록 CSS를 추가해보겠습니다. body의 CSS display 속성을 flex로 설정하고 flex-direction 속성을 column로 설정하면 적용이 됩니다. 이 속성들은 브라우저로 하여금 body의 자식들이(<header>, <main>, <footer>) 수직으로 유동적인 박스 형태의 레이아웃으로 놓이게 만들어 줍니다.

내부의 공간들이 어떤 모양이로 나뉘어 질지는 flex 약칭 속성으로 조절이 가능합니다. MDN에서 관련 내용을 확인하실 수 있습니다. 이번 어플리케이션 레이아웃에서는, 서로 비례하게 늘어나거나 줄어들게 만들지는 않을 것입니다. 대신에 <header><footer> 엘리먼트는 고정된 높이를 가지며 <main> 엘리먼트는 flex 속성을 auto로 설정하여 남은 공간을 차지하도록 만들 것입니다.

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

body {
    display: flex;
    flex-direction: column;
}

header {
    height: 75px;
}

main {
    flex: auto;
}

footer {
    height: 25px;
}

데모 확인하기

2 단계: 수평으로 나열된 박스

<main> 엘리먼트 안에 추가로 세 개의 엘리먼트들(<nav>, <article>, <aside>)을 추가해봅시다. 하지만 이번에는 수직이 아닌 수평으로 <main> 엘리먼트 내부를 채워볼 것입니다.

<body>
<header></header>
<main>
    <nav></nav>
    <article></article>
    <aside></aside>
</main>
<footer></footer>
</body>

<main> 엘리먼트의 display 속성은 역시 flex로 같지만 이번에는 flex-direction 속성은 row 로 설정합니다(이것이 기본입니다). <nav><aside> 엘리먼트는 고정된 너비 길이를 가지며 <article> 엘리먼트는 남은 공간을 차지할 것입니다: 이 내용은 1단계와 같습니다.

main {
    display: flex;
    flex-direction: row;
    flex: auto;
}

nav {
    width: 150px;
}

article {
    flex: auto;
}

aside {
    width: 50px;
}

데모 확인하기

이게 전부입니다. 여러분의 브라우저 창의 크기를 조절하며 유동적인 어플레케이션 레이아웃을 확인해보세요.

다음 단계: CSS 개선

하지만 아직 다가 아닙니다. 브라우저에 많은 내용들이 생겼을 때, 엘리먼트의 크기가 더 작아질 것이며 스크롤바 또한 생겨날 것입니다.

그러므로 min-width 속성을 모든 width 속성을 가진 엘리먼트에 추가해주어야 합니다. 그리고 스크롤바가 윈하는 위치에 나타날 수 있도록 <article><aside> 엘리먼터의 overflow-y 속성을 auto로 설정하고 <body><main> 엘리먼트의 overflow 속성을 hidden으로 설정합니다.

body {
	overflow: hidden;
	display: flex;
	flex-direction: column;
}

header {
	height: 75px;
	min-height: 75px;
}

footer {
	height: 25px;
	min-height: 25px;
}

main {
	display: flex;
	flex-direction: row;
	flex: auto;
	border: solid grey;
	border-width: 1px 0;
	overflow: hidden;
}

nav {
	width: 150px;
	min-width: 150px;
}

article {
	border: solid grey;
	border-width: 0 0 0 1px;
	flex: auto;
	overflow-x: hidden;
	overflow-y: auto;
}

aside {
	width: 50px;
	min-width: 50px;
	overflow-x: hidden;
	overflow-y: auto;
}

추가: 이 소스는 사파리에서는 아직 동작하지 않습니다. -webkit-prefix를 사용하여 원하는 동작을 처리할 수 있습니다.

마지막 단계: 약간의 Javascript를 추가

마지막 단계로 우리는 사용자들이 마우스로 드래그를 하여 <aside> 엘리먼트 사이즈를 조절할 수 있기를 원합니다. 그러기 위해서 드래그 동작을 인식하는 <div>  엘리먼트를 구역을 나누는 역할로서 추가합니다.

<body>
<header></header>
<main>
    <nav></nav>
    <article></article>
    <div class="splitter"></div>
    <aside></aside>
</main>
<footer></footer>
</body>

우리는 사용자가 이 엘리먼트를 좌-우로 크기 조절이 가능하다는 것을 인식할 수 있도록 핸들러의 가로 사이즈를 4px로 설정하고 cursor 속성을 col-resize 값으로 설정합니다.

.splitter {
    border-left: 1px solid grey;
    width: 4px;
    min-width: 4px;
    cursor: col-resize;
}

이제 남은 것은 약간의 자바스크립트 코드를 추가하는 일입니다. 이 코드는 추가한 구역을 움직이게 만들어 줄 것입니다.

var w = window, d = document, splitter;

splitter = {
    lastX: 0,
    leftEl: null,
    rightEl: null,

    init: function(handler, leftEl, rightEl) {
        var self = this;

        this.leftEl = leftEl;
        this.rightEl = rightEl;

        handler.addEventListener('mousedown', function(evt) {
            evt.preventDefault();    /* prevent text selection */

            self.lastX = evt.clientX;

            w.addEventListener('mousemove', self.drag);
            w.addEventListener('mouseup', self.endDrag);
        });
    },

    drag: function(evt) {
        var wL, wR, wDiff = evt.clientX - splitter.lastX;

        wL = d.defaultView.getComputedStyle(splitter.leftEl, '').getPropertyValue('width');
        wR = d.defaultView.getComputedStyle(splitter.rightEl, '').getPropertyValue('width');
        wL = parseInt(wL, 10) + wDiff;
        wR = parseInt(wR, 10) - wDiff;
        splitter.leftEl.style.width = wL + 'px';
        splitter.rightEl.style.width = wR + 'px';

        splitter.lastX = evt.clientX;
    },

    endDrag: function() {
        w.removeEventListener('mousemove', splitter.drag);
        w.removeEventListener('mouseup', splitter.endDrag);
    }
};

splitter.init(d.getElementsByClassName('splitter')[0], d.getElementsByTagName('article')[0], d.getElementsByTagName('aside')[0]);

추가: IE11(, Safari?) 혹은 크롬 31에서 사이즈 조절이 동작하지 않는 몇가지 이유로 display: flex 속성 값이 적용되지 않는 문제가 있습니다.

작성자: Seung Hun Jang

Creative Commons, Mozilla, 게으른 독학하는 히피개발자를 꿈꾸는 웹을 사랑하는 대학생

Seung Hun Jang가 작성한 문서들…


댓글이 없습니다.

댓글 쓰기