반응형 레이아웃 (Responsive Layout)
화면 폭을 줄이면 navigation bar가 깨져버리는 경우가 생긴다.
이런 문제를 해결하기 위해서는 반응형 웹 디자인(Responsive Web Design) 을 적용해야 한다.
반응형 웹 디자인이란
사용자가 어떤 디바이스로 웹사이트를 방문할지 알 수 없기 때문에,
데스크탑, 태블릿, 모바일 등 어떤 화면에서도 정상적으로 보여지도록 만든다.
즉, 기기 화면 크기에 따라 자동으로 배치와 스타일을 조정하는 웹 디자인 방식이다.
Viewport Meta Tag
모바일 브라우저에서 웹사이트가 기기 크기에 맞게 보이도록 하려면 viewport meta tag를 설정한다.
이 태그는 브라우저가 페이지를 표시할 때 어떤 기준으로 보여줄지를 지정한다.
프로퍼티 설명 사용 예시
| width | viewport 너비(px). 기본값은 980px이다. | width=device-width |
| height | viewport 높이(px)를 지정한다. | height=800 |
| initial-scale | 초기 배율을 설정한다. | initial-scale=1.0 |
| user-scalable | 사용자가 확대/축소할 수 있는지를 지정한다. | user-scalable=no |
| maximum-scale | 최대 확대 비율을 설정한다. | maximum-scale=2.0 |
| minimum-scale | 최소 확대 비율을 설정한다. | minimum-scale=1.0 |
Media Query
반응형 웹 디자인을 구현할 때 가장 핵심적인 기술은 @media 이다.
@media 를 사용하면 디바이스 종류나 화면 크기, 비율 등에 따라 각각 다른 스타일을 적용할 수 있다.
이 방식을 미디어 쿼리(Media Query) 라고 한다.
@media not|only mediatype and (expressions) {
CSS-Code;
}
예를 들어 아래 코드는 화면 너비가 600px 이상일 때 배경색을 바꾸는 예시이다.
@media screen and (min-width: 600px) {
body {
background-color: lightgreen;
}
}
일반적으로 반응형 웹 디자인은 viewport의 너비(width) 를 기준으로 한다.
따라서 width 프로퍼티를 이용해 화면 크기에 따른 반응 범위(breakpoint)를 지정한다.
Mobile First vs Non-Mobile First
Mobile First Method
모바일 화면을 기준으로 디자인을 먼저 잡고, 화면이 커질수록 점진적으로 스타일을 확장한다.
즉, min-width 를 사용하여 최소 너비를 기준으로 breakpoint를 설정한다.
/*========== Mobile First Method ==========*/
/* iPhone Retina : 320px ~ */
@media only screen and (min-width: 320px) {}
/* Phones : 480px ~ */
@media only screen and (min-width: 480px) {}
/* Tablets : 768px ~ */
@media only screen and (min-width: 768px) {}
/* Desktops : 992px ~ */
@media only screen and (min-width: 992px) {}
/* Wide Screens : 1200px ~ */
@media only screen and (min-width: 1200px) {}
Mobile First란 모바일 화면을 가장 중요하게 생각하는 설계 방식이다.
요즘 대부분의 사용자가 휴대폰으로 웹사이트를 이용하기 때문에 모바일 퍼스트 디자인이 점점 늘어나고 있다.
Non-Mobile First Method
데스크탑 화면을 기준으로 잡고, 화면이 작아질수록 스타일을 조정하는 방식이다.
즉, max-width 를 사용하여 최대 너비를 기준으로 breakpoint를 설정한다.
/*========== Non-Mobile First Method ==========*/
/* Wide Screens : ~1200px */
@media only screen and (max-width: 1200px) {}
/* Desktops : ~992px */
@media only screen and (max-width: 992px) {}
/* Tablets : ~768px */
@media only screen and (max-width: 768px) {}
/* Phones : ~480px */
@media only screen and (max-width: 480px) {}
/* iPhone Retina : ~320px */
@media only screen and (max-width: 320px) {}
추가 팁
모바일에서는 가로(landscape) 와 세로(portrait) 방향에 따라 디자인을 다르게 줄 수도 있다.
@media only screen and (orientation: landscape) {
/* 가로 모드일 때 스타일 */
}
@media only screen and (orientation: portrait) {
/* 세로 모드일 때 스타일 */
}
Flex 박스 레이아웃
이제는 복잡하게 레이아웃을 그리지 않고 flexbox를 사용해서 간단하게 화면을 배치한다.
Flexbox는 컨테이너와 그 안의 아이템을 효율적으로 정렬하고 배치하는 데 사용된다.
기본 속성
1. flex-direction
Flex 컨테이너 안의 아이템이 정렬되는 방향을 설정한다.
flex-direction: row; /* 가로 방향 (기본값) */
flex-direction: row-reverse; /* 가로 반대 방향 */
flex-direction: column; /* 세로 방향 */
flex-direction: column-reverse; /* 세로 반대 방향 */
2. flex-wrap
여러 개의 flex item을 한 줄에 배치할지, 여러 줄로 나눌지를 설정한다.
아래 예시를 통해 nowrap, wrap, wrap-reverse의 차이를 직접 확인해본다.
flex-wrap: nowrap; /* 한 줄로만 배치 */
flex-wrap: wrap; /* 여러 줄로 자동 줄바꿈 */
flex-wrap: wrap-reverse; /* 여러 줄로 배치하되 역순으로 정렬 */
3. justify-content
main axis (주축) 기준으로 flex item을 수평 정렬한다.
justify-content: flex-start; /* 왼쪽 정렬 (기본값) */
justify-content: flex-end; /* 오른쪽 정렬 */
justify-content: center; /* 가운데 정렬 */
justify-content: space-between; /* 양 끝 기준으로 고르게 배치 */
justify-content: space-around; /* 아이템 간 간격 균등하게 배치 */
4. align-items
cross axis (교차축) 기준으로 flex item을 수직 정렬한다.
align-items: stretch; /* 컨테이너 높이에 꽉 차게 (기본값) */
align-items: flex-start; /* 위쪽 정렬 */
align-items: flex-end; /* 아래쪽 정렬 */
align-items: center; /* 세로 중앙 정렬 */
예시 코드
<!DOCTYPE html>
<html>
<head>
<title>Flexbox</title>
<meta charset="UTF-8" />
<style>
.flex-container {
height: 500px;
margin: 10px;
padding: 15px;
border-radius: 5px;
background: #60b99a;
display: flex;
align-items: stretch;
}
.flex-item {
padding: 20px;
color: #fff;
text-align: center;
border-radius: 5px;
background: #4584b1;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="flex-item">11111</div>
<div class="flex-item">22222</div>
<div class="flex-item">33333</div>
<div class="flex-item">44444</div>
<div class="flex-item">55555</div>
</div>
</body>
</html>
이 코드를 실행하면 아래쪽에 여분의 작은 공간이 생긴다.
우리가 따로 지정하지 않았는데도 말이다.
여분의 공간이 생기는 이유
이 문제는 image 요소가 inline 요소이기 때문이다.
브라우저는 inline 요소를 텍스트처럼 취급해서
글자의 디센더(descender) 부분만큼의 공백을 자동으로 남긴다.
해결 방법
이미지 요소를 블록 요소로 바꾼다.
이미지를 더 이상 텍스트로 취급하지 않게 한다.
img {
display: block;
}
블록 요소로 바꿀 수 없다면 vertical-align을 사용한다.
inline 요소 상태에서 이미지의 표시 위치를 조정한다.
img {
vertical-align: bottom;
}
baseline이 기본값이기 때문에, 이를 변경하면 여백이 사라진다.
부트스트랩 (Bootstrap) 간단히 알아보기
부트스트랩은 반응형 웹 디자인을 빠르게 구현할 수 있도록 도와주는 프레임워크다.
CSS, JavaScript, 그리드 시스템 등이 이미 포함되어 있어서
직접 코드를 일일이 작성하지 않아도 일관된 디자인을 유지할 수 있다.
설치 방법은 다음 두 가지 중 하나를 사용한다.
- CDN 사용
- npm 설치
- npm install bootstrap
크로스 브라우징(Cross Browsing)
크로스 브라우징이란 모든 브라우저에서 웹페이지가 동일하게 보이도록 만드는 작업을 말한다.
특정 브라우저에서만 정상 작동하거나, 화면이 깨지는 문제를 방지하기 위해 반드시 고려해야 한다.
크로스 브라우징을 위한 방법
- <!DOCTYPE html> 선언
→ HTML5 문서임을 명시하여 표준 모드로 렌더링되게 한다. - Reset CSS 적용
→ 브라우저별로 다른 기본 스타일을 초기화한다. - 반응형 웹사이트 제작
→ 다양한 해상도와 기기에 대응한다. - Viewport 설정
→ <meta name="viewport" content="width=device-width, initial-scale=1.0">
모바일 환경에서 화면 크기를 기기에 맞게 조정한다. - 문자 인코딩 지정
→ <meta charset="utf-8">
문자가 깨지지 않게 인코딩 방식을 명시한다.
미션
모바일 브레이크 포인트 구현하기
오늘은 반응형 웹 미션으로 모바일 화면(480px 이하) 에서 동작하는
햄버거 메뉴(nav toggle) 를 직접 구현했다.
화면 크기에 따라 메뉴가 접히고 펼쳐지는 구조를 만들면서
미디어 쿼리와 CSS 트랜지션의 활용법을 익혔다.
구현 목표
- 모바일 해상도(480px 이하)에서 기존 네비게이션 메뉴를 숨긴다.
- 햄버거 아이콘을 클릭하면 메뉴가 펼쳐지도록 만든다.
- 클릭 시 아이콘이 X자 모양으로 변하도록 애니메이션을 적용한다.
주요 코드
CSS (모바일 전용 스타일)
/* 모바일 브레이크 포인트 */
@media screen and (max-width:480px) {
header {
height: 60px;
z-index: 1;
}
#wrap {
margin-top: 60px;
}
aside {
top: 60px;
}
/* 토글 버튼 숨기기 */
.nav-toggle {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
/* 햄버거 아이콘 영역 */
.navicon {
position: absolute;
right: 30px;
top: 30px;
width: 23px;
cursor: pointer;
}
/* 막대기 기본 설정 */
.navicon-bar {
position: relative;
display: block;
width: 100%;
height: 3px;
background-color: black;
border-radius: 5px;
transition: all 0.5s ease;
}
/* 위/아래 막대기 생성 */
.navicon-bar::before,
.navicon-bar::after {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 3px;
background-color: black;
border-radius: 2px;
transition: all 0.5s ease;
}
.navicon-bar::before {
top: -7px;
}
.navicon-bar::after {
top: 7px;
}
/* 메뉴 숨기기 */
.nav-items {
display: none;
flex-direction: column;
align-items: center;
background-color: white;
}
/* 체크 시 메뉴 표시 */
.nav-toggle:checked ~ .nav-items {
transition: all 2s ease;
display: flex;
}
/* 클릭 시 높이 조정 */
.nav-toggle:checked + header {
height: 300px;
}
/* 클릭 시 X자 모양 만들기 */
.nav-toggle:checked + .navicon .navicon-bar::before {
transform: rotate(45deg);
top: 0;
}
.nav-toggle:checked + .navicon .navicon-bar::after {
transform: rotate(-45deg);
top: 0;
}
/* 가운데 막대는 사라짐 */
.nav-toggle:checked + .navicon .navicon-bar {
background-color: transparent;
}
}
HTML 구조
<nav>
<div id="nav-open">
<input class="nav-toggle" id="nav-toggle" type="checkbox">
<label class="navicon" for="nav-toggle">
<span class="navicon-bar"></span>
</label>
<ul class="nav-items">
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>
</div>
</nav>
구현하면서 배운 점
- @media screen and (max-width:480px) 를 사용해서 모바일 화면 전용 스타일을 적용했다.
- checkbox와 label을 이용한 순수 CSS 토글 방식으로
자바스크립트 없이 메뉴 열기/닫기 기능을 구현했다. - transition과 transform 속성을 활용해 햄버거 아이콘이
자연스럽게 X자 모양으로 바뀌는 애니메이션을 만들었다. - flexbox를 이용해서 메뉴 항목을 세로로 정렬했다.
후기
이번 미션을 통해 반응형 네비게이션 바를 처음부터 직접 구현해봤다.
화면 크기에 따라 동적으로 메뉴가 바뀌고,
토글 버튼 하나로 시각적인 변화까지 줄 수 있어서 꽤 재미있는 작업이었다.
CSS만으로 인터랙티브한 기능을 구현할 수 있다는 점이 인상 깊었다.