반응형 웹 디자인 | 미디어 쿼리와 모바일 최적화
이 글의 핵심
반응형 웹 디자인에 대한 실전 가이드입니다. 미디어 쿼리와 모바일 최적화 등을 예제와 함께 상세히 설명합니다.
들어가며
반응형 웹 디자인은 뷰포트 너비·해상도에 따라 레이아웃과 글자 크기를 바꾸는 기법입니다. Flexbox·Grid·미디어 쿼리를 함께 쓰는 경우가 많습니다.
1. Viewport 설정
Meta 태그
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
설명:
width=device-width: 화면 너비를 기기 너비로 설정initial-scale=1.0: 초기 확대 비율
2. 미디어 쿼리
기본 문법
/* 모바일 (기본) */
.container {
width: 100%;
padding: 10px;
}
/* 태블릿 이상 */
@media (min-width: 768px) {
.container {
width: 750px;
margin: 0 auto;
}
}
/* 데스크톱 이상 */
@media (min-width: 1024px) {
.container {
width: 1000px;
}
}
브레이크포인트
/* 모바일 */
@media (max-width: 767px) {
body {
font-size: 14px;
}
}
/* 태블릿 */
@media (min-width: 768px) and (max-width: 1023px) {
body {
font-size: 16px;
}
}
/* 데스크톱 */
@media (min-width: 1024px) {
body {
font-size: 18px;
}
}
미디어 타입
/* 화면 */
@media screen and (min-width: 768px) {
/* 스타일 */
}
/* 인쇄 */
@media print {
.no-print {
display: none;
}
}
/* 가로/세로 */
@media (orientation: landscape) {
/* 가로 모드 */
}
@media (orientation: portrait) {
/* 세로 모드 */
}
3. 반응형 이미지
기본 반응형
img {
max-width: 100%;
height: auto;
}
Picture 태그
<picture>
<source media="(min-width: 1024px)" srcset="large.jpg">
<source media="(min-width: 768px)" srcset="medium.jpg">
<img src="small.jpg" alt="반응형 이미지">
</picture>
srcset 속성
<img
src="small.jpg"
srcset="small.jpg 480w, medium.jpg 768w, large.jpg 1024w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="반응형 이미지"
>
4. 반응형 폰트
vw 단위
h1 {
font-size: 5vw;
}
clamp() 함수
h1 {
font-size: clamp(1.5rem, 5vw, 3rem);
}
p {
font-size: clamp(1rem, 2vw, 1.2rem);
}
미디어 쿼리
h1 {
font-size: 24px;
}
@media (min-width: 768px) {
h1 {
font-size: 32px;
}
}
@media (min-width: 1024px) {
h1 {
font-size: 40px;
}
}
5. 모바일 퍼스트
모바일 우선 접근
/* 모바일 기본 */
.nav {
flex-direction: column;
padding: 10px;
}
.nav-item {
width: 100%;
}
/* 태블릿 이상 */
@media (min-width: 768px) {
.nav {
flex-direction: row;
padding: 20px;
}
.nav-item {
width: auto;
}
}
6. 실전 예제
예제 1: 반응형 네비게이션
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>반응형 네비게이션</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.navbar {
background: #333;
padding: 1rem;
}
.nav-menu {
list-style: none;
display: flex;
flex-direction: column;
gap: 1rem;
}
.nav-menu a {
color: white;
text-decoration: none;
padding: 0.5rem;
display: block;
}
.nav-menu a:hover {
background: #555;
}
@media (min-width: 768px) {
.nav-menu {
flex-direction: row;
justify-content: center;
}
}
</style>
</head>
<body>
<nav class="navbar">
<ul class="nav-menu">
<li><a href="#">홈</a></li>
<li><a href="#">소개</a></li>
<li><a href="#">서비스</a></li>
<li><a href="#">연락처</a></li>
</ul>
</nav>
</body>
</html>
예제 2: 반응형 그리드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>반응형 그리드</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f5f5f5;
}
.grid {
display: grid;
gap: 20px;
padding: 20px;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
.card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.card h3 {
margin-bottom: 10px;
color: #333;
}
.card p {
color: #666;
line-height: 1.6;
}
</style>
</head>
<body>
<div class="grid">
<div class="card">
<h3>카드 1</h3>
<p>반응형 그리드 예제입니다.</p>
</div>
<div class="card">
<h3>카드 2</h3>
<p>화면 크기에 따라 열 개수가 변합니다.</p>
</div>
<div class="card">
<h3>카드 3</h3>
<p>모바일: 1열, 태블릿: 2열, 데스크톱: 3열</p>
</div>
<div class="card">
<h3>카드 4</h3>
<p>CSS Grid를 활용한 레이아웃입니다.</p>
</div>
<div class="card">
<h3>카드 5</h3>
<p>gap 속성으로 간격을 조정합니다.</p>
</div>
<div class="card">
<h3>카드 6</h3>
<p>미디어 쿼리로 반응형을 구현합니다.</p>
</div>
</div>
</body>
</html>
예제 3: 반응형 레이아웃
<style>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.hero {
display: flex;
flex-direction: column;
gap: 20px;
}
.hero-image {
width: 100%;
}
.hero-content {
flex: 1;
}
@media (min-width: 768px) {
.hero {
flex-direction: row;
align-items: center;
}
.hero-image {
width: 50%;
}
}
@media (min-width: 1024px) {
.hero {
gap: 40px;
}
}
</style>
<div class="container">
<div class="hero">
<img class="hero-image" src="hero.jpg" alt="히어로 이미지">
<div class="hero-content">
<h1>반응형 웹 디자인</h1>
<p>모든 기기에서 완벽하게 보이는 웹사이트를 만드세요.</p>
</div>
</div>
</div>
7. 반응형 디자인 패턴
Container Queries
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: flex;
}
}
Fluid Typography
:root {
--fluid-min-width: 320;
--fluid-max-width: 1140;
--fluid-min-size: 16;
--fluid-max-size: 20;
}
body {
font-size: clamp(
1rem,
calc(1rem + (20 - 16) * ((100vw - 320px) / (1140 - 320))),
1.25rem
);
}
8. 미디어 쿼리 브레이크포인트 정리
고정 숫자에 매달리기보다, 콘텐츠가 깨지는 지점에서 끊는 것이 이상적입니다. 그래도 팀·디자인 시스템과 맞출 출발점은 아래처럼 잡는 경우가 많습니다.
| 구간 | 대략적인 너비 | 용도 |
|---|---|---|
| 모바일 | ~ 575px | 1열, 햄버거 메뉴, 세로 스택 |
| 큰 모바일~소형 태블릿 | 576px ~ 767px | 2열 시도, 여백 조정 |
| 태블릿 | 768px ~ 1023px | 2열 그리드, 일부 가로 배치 |
| 데스크톱 | 1024px ~ 1279px | 3열, 사이드바 노출 |
| 와이드 | 1280px 이상 | 최대 너비(max-width) + 여백 |
min-width vs max-width: 모바일 퍼스트라면 기본 스타일을 좁은 화면에 두고 min-width로 단계적으로 덧씌웁니다. 데스크톱 퍼스트는 반대로 max-width로 줄여 나갑니다.
/* 모바일 퍼스트 예시 */
.layout { padding: 1rem; }
@media (min-width: 768px) {
.layout { padding: 1.5rem; max-width: 720px; margin-inline: auto; }
}
@media (min-width: 1024px) {
.layout { max-width: 960px; }
}
9. 모바일 퍼스트 vs 데스크톱 퍼스트
| 모바일 퍼스트 | 데스크톱 퍼스트 | |
|---|---|---|
| 기본 CSS | 좁은 화면(한 열, 단순 타이포) | 넓은 화면(다열, 큰 여백) |
| 미디어 쿼리 | min-width로 확장 | max-width로 축소 |
| 장점 | 작은 화면에 필요한 CSS만 먼저 로드하는 느낌으로 작성하기 쉬움, 성능·접근성 논의와 잘 맞음 | 기존 데스크톱 사이트를 줄이는 리팩터에 적합할 수 있음 |
| 주의 | “데스크톱 디자인”을 나중에 추가하다 보더라도 기본이 단순해야 유지보수가 쉬움 | 모바일에서 불필요한 스타일을 덮어쓰는 비용이 커질 수 있음 |
신규 프로젝트는 모바일 퍼스트 + min-width 단계 확장이 일반적인 권장 패턴입니다.
10. clamp(), min(), max() — 레이아웃·타이포에 쓰기
이 함수들은 미디어 쿼리 없이 유동적인 값을 만들 때 유용합니다.
clamp(최소, 선호, 최대)
선호값이 계산되지만, 항상 최소~최대 안에 가둡니다.
/* 제목: 너무 작아지지도, 너무 커지지도 않게 */
h1 {
font-size: clamp(1.5rem, 4vw + 1rem, 3rem);
}
/* 카드 패딩 */
.card {
padding: clamp(1rem, 3vw, 2rem);
}
min() / max()
min(A, B): 둘 중 더 작은 값 — “상한”을 걸 때 (width: min(100%, 40rem)등).max(A, B): 둘 중 더 큰 값 — “하한”을 걸 때.
/* 뷰포트에 맞추되 1200px를 넘지 않는 컨테이너 */
.container {
width: min(100% - 2rem, 1200px);
margin-inline: auto;
}
/* 스크롤 여백까지 고려한 여백 (자주 쓰는 패턴) */
.section {
padding-inline: max(1rem, (100vw - 1200px) / 2);
}
clamp는 폰트·패딩·갭에, min/max는 너비·여백 한계를 동시에 잡을 때 자주 짝을 이룹니다.
11. 실전 반응형 패턴 모음
- 유동 컨테이너 + 상한:
width: min(100%, 72rem); margin-inline: auto;— 큰 화면에서만 읽기 폭 제한. - 그리드 열 개수:
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));— 카드가 알아서 줄바꿈( Grid 편과 연계). - 터치 타깃: 모바일에서 클릭 영역은 약 44×44px 이상을 목표로
padding또는min-height로 확보. - 이미지:
max-width: 100%; height: auto;+ 필요 시object-fit: cover로 비율 유지. - 숨김 처리:
display: none으로 네비 항목을 없앨 때, 스크린 리더 전용 링크가 필요한지 함께 검토합니다.
/* 예: 작은 화면에서만 세로 스택 */
.stack {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 768px) {
.stack {
flex-direction: row;
align-items: center;
}
}
정리
핵심 요약
- Viewport:
<meta name="viewport"> - 미디어 쿼리:
@media (min-width: 768px) - 모바일 퍼스트: 작은 화면부터 시작
- 반응형 이미지:
max-width: 100%,<picture> - 브레이크포인트: 768px(태블릿), 1024px(데스크톱) — 필요 시 576 / 1280 등을 보강
clamp/min/max: 미디어 쿼리 없이 유동 값 제한
반응형 체크리스트
- Viewport 메타 태그 설정
- 모바일 퍼스트 접근
- 적절한 브레이크포인트 사용
- 반응형 이미지 최적화
- 터치 친화적 UI (44px 이상)
다음 단계
- CSS 애니메이션
- JavaScript DOM 조작
- CSS Grid
관련 글
- HTML/CSS 시작하기 | 웹 개발 첫걸음
- CSS 기초 | 선택자, 속성, 색상, 폰트
- CSS 박스 모델 | Margin, Padding, Border 완벽 정리
- CSS Flexbox | 플렉스박스 레이아웃 완벽 가이드
- CSS Grid | 그리드 레이아웃 완벽 가이드