반응형 웹 디자인 | 미디어 쿼리와 모바일 최적화

반응형 웹 디자인 | 미디어 쿼리와 모바일 최적화

이 글의 핵심

반응형 웹 디자인에 대한 실전 가이드입니다. 미디어 쿼리와 모바일 최적화 등을 예제와 함께 상세히 설명합니다.

들어가며

반응형 웹 디자인은 뷰포트 너비·해상도에 따라 레이아웃과 글자 크기를 바꾸는 기법입니다. 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. 미디어 쿼리 브레이크포인트 정리

고정 숫자에 매달리기보다, 콘텐츠가 깨지는 지점에서 끊는 것이 이상적입니다. 그래도 팀·디자인 시스템과 맞출 출발점은 아래처럼 잡는 경우가 많습니다.

구간대략적인 너비용도
모바일~ 575px1열, 햄버거 메뉴, 세로 스택
큰 모바일~소형 태블릿576px ~ 767px2열 시도, 여백 조정
태블릿768px ~ 1023px2열 그리드, 일부 가로 배치
데스크톱1024px ~ 1279px3열, 사이드바 노출
와이드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. 실전 반응형 패턴 모음

  1. 유동 컨테이너 + 상한: width: min(100%, 72rem); margin-inline: auto; — 큰 화면에서만 읽기 폭 제한.
  2. 그리드 열 개수: grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); — 카드가 알아서 줄바꿈( Grid 편과 연계).
  3. 터치 타깃: 모바일에서 클릭 영역은 약 44×44px 이상을 목표로 padding 또는 min-height로 확보.
  4. 이미지: max-width: 100%; height: auto; + 필요 시 object-fit: cover로 비율 유지.
  5. 숨김 처리: display: none으로 네비 항목을 없앨 때, 스크린 리더 전용 링크가 필요한지 함께 검토합니다.
/* 예: 작은 화면에서만 세로 스택 */
.stack {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

@media (min-width: 768px) {
  .stack {
    flex-direction: row;
    align-items: center;
  }
}

정리

핵심 요약

  1. Viewport: <meta name="viewport">
  2. 미디어 쿼리: @media (min-width: 768px)
  3. 모바일 퍼스트: 작은 화면부터 시작
  4. 반응형 이미지: max-width: 100%, <picture>
  5. 브레이크포인트: 768px(태블릿), 1024px(데스크톱) — 필요 시 576 / 1280 등을 보강
  6. clamp / min / max: 미디어 쿼리 없이 유동 값 제한

반응형 체크리스트

  • Viewport 메타 태그 설정
  • 모바일 퍼스트 접근
  • 적절한 브레이크포인트 사용
  • 반응형 이미지 최적화
  • 터치 친화적 UI (44px 이상)

다음 단계

  • CSS 애니메이션
  • JavaScript DOM 조작
  • CSS Grid

관련 글

  • HTML/CSS 시작하기 | 웹 개발 첫걸음
  • CSS 기초 | 선택자, 속성, 색상, 폰트
  • CSS 박스 모델 | Margin, Padding, Border 완벽 정리
  • CSS Flexbox | 플렉스박스 레이아웃 완벽 가이드
  • CSS Grid | 그리드 레이아웃 완벽 가이드