CSS Flex와 미디어 쿼리로 카드 페이지를 어떻게 반응형으로 만드나요?
좁은 화면의 세로 배치를 기본으로 만들고, 공간이 넓어질 때 미디어 쿼리로 확장합니다. Flexbox로 카드 방향과 간격을 조절하고 브라우저 폭을 바꾸며 줄 배치와 내용 넘침을 확인합니다.
적용 조건·범위: index.html을 브라우저에서 열고 DevTools의 반응형 화면 너비를 375px과 900px로 바꿔 보세요. 기본 글자 크기 16px 환경에서 375px은 카드 세 장이 세로로 쌓이고 900px은 한 줄에 나란히 보입니다. DOM에서는 두 경우 모두 HTML → CSS → JavaScript 순서가 유지됩니다.
공식 문서 참고: MDN: Flexbox 주축·교차축·줄바꿈 · MDN: 화면 조건에 따라 CSS를 적용하는 미디어 쿼리
독립 실습: 화면 너비에 따라 카드 방향 바꾸기
CSS Flex와 미디어 쿼리 입문: 반응형 카드 페이지 만들기 · 독립 실습
화면 폭을 바꾸며 학습 카드의 배치가 바뀌는 지점을 확인하세요.
예제 크게 보기 (새 탭)TaskFlow 파일 없이 같은 폴더에 index.html과 style.css를 만듭니다.
index.html
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>반응형 카드 실습</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>오늘 배울 내용</h1>
<section aria-labelledby="cards-heading">
<h2 id="cards-heading">학습 카드</h2>
<div class="cards">
<article class="card"><h3>HTML</h3><p>문서 구조</p></article>
<article class="card"><h3>CSS</h3><p>화면 스타일</p></article>
<article class="card"><h3>JavaScript</h3><p>화면 동작</p></article>
</div>
</section>
</main>
</body>
</html>
style.css
* { box-sizing: border-box; }
body { margin: 0; color: #222; font-family: sans-serif; }
main { width: calc(100% - 32px); max-width: 960px; margin: 32px auto; }
.cards { display: flex; flex-direction: column; gap: 16px; }
.card { flex: 1; min-width: 0; padding: 16px; border: 1px solid #999; }
@media (min-width: 48rem) {
.cards { flex-direction: row; }
}
index.html을 브라우저에서 열고 DevTools의 반응형 화면 너비를 375px과 900px로 바꿔 보세요. 기본 글자 크기 16px 환경에서 375px은 카드 세 장이 세로로 쌓이고 900px은 한 줄에 나란히 보입니다. DOM에서는 두 경우 모두 HTML → CSS → JavaScript 순서가 유지됩니다.
스스로 확인: @media 블록을 지우면 900px에서도 어떻게 배치될까요? 기본 flex-direction: column만 남아 세로로 쌓입니다. 결과가 다르면 파일 연결, viewport 설정, 실제 창 너비를 확인하세요. 48rem은 기기 이름이 아니라 이 세 카드가 가로로 놓일 여유가 생기는지 시험할 출발점입니다.
Flexbox는 1차원 배치에 적합합니다
.nav {
display: flex;
gap: 16px;
}
한 방향으로 요소를 정렬할 때 편리합니다.
주축과 교차축
기본 flex-direction: row에서:
justify-content
→ 가로 주축 배치
align-items
→ 세로 교차축 정렬
.site-header__inner {
display: flex;
align-items: center;
justify-content: space-between;
}
작은 화면부터 시작
기본:
.feature-list {
display: flex;
flex-direction: column;
gap: 16px;
}
넓은 화면:
@media (min-width: 48rem) {
.feature-list {
flex-direction: row;
}
.feature-list > * {
flex: 1;
}
}
작은 화면에서는 카드가 세로로 쌓이고 48rem 이상에서 가로로 배치됩니다.

flex-wrap
내비게이션 링크나 버튼이 한 줄에 들어가지 않으면 줄바꿈을 허용할 수 있습니다.
.nav {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
}
하지만 wrap으로 모든 문제를 숨기기보다 콘텐츠 길이와 available width를 확인합니다.
breakpoint는 콘텐츠 기준
768px = 태블릿처럼 기기 이름만 외우기보다 브라우저 폭을 줄이며 실제 카드가 읽기 어려워지는 지점을 찾습니다.
360px
480px
640px
768px
1024px
중간 폭에서 갑자기 겹치거나 버튼이 줄바꿈되는지 확인합니다.
독립 예제
<section class="cards">
<article class="card">A</article>
<article class="card">B</article>
<article class="card">C</article>
</section>
.cards {
display: flex;
flex-direction: column;
gap: 16px;
}
@media (min-width: 48rem) {
.cards {
flex-direction: row;
}
.cards > * {
flex: 1;
}
}
Flexbox와 Grid 예고
Flexbox로 3개 카드 폭을 같게 만들 수 있지만 카드가 여러 행으로 늘어나고 행/열 정렬이 중요해지면 Grid가 더 자연스러울 수 있습니다.
6회차에서 비교합니다.
자주 발생하는 오류
justify-content로 세로 정렬이 안 됩니다
현재 flex-direction을 확인합니다.
고정 width를 너무 많이 씁니다
flex item을 유연하게 두고 max-width 등으로 전체 범위를 제한하는 편이 반응형에 유리합니다.
모바일 media query를 max-width로 계속 덧붙입니다
작은 화면 기본 → min-width 확장 흐름을 먼저 검토합니다.
학습 완료 체크리스트
- Flexbox가 한 방향 배치에 적합한 이유를 설명할 수 있습니다.
- justify-content와 align-items를 구분할 수 있습니다.
- flex-wrap과 gap을 사용할 수 있습니다.
- mobile-first media query를 작성할 수 있습니다.
- 실제 콘텐츠가 깨지는 지점으로 breakpoint를 고를 수 있습니다.
독립 실습은 여기서 마칠 수 있습니다. 아래 선택 실습은 TaskFlow 프로젝트 파일로 진행합니다.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 간단한 서비스 화면에서 시작해 할 일 앱으로 이어 만드는 예제입니다. 이 선택 실습에서는 HTML과 CSS로 이번 화면을 만듭니다.
TaskFlow header와 기능 카드 영역을 작은 화면부터 반응형으로 구성합니다.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow 프로젝트 적용
CSS Flex와 미디어 쿼리 입문: 반응형 카드 페이지 만들기 · 단계별 TaskFlow
TaskFlow 누적 3단계입니다. 화면 폭을 바꾸며 학습 카드의 배치가 바뀌는 지점을 확인하세요.
예제 크게 보기 (새 탭)실행 가능한 시작본과 이번 단계
이번 단계 시작 ZIP을 새 폴더에 압축 해제합니다. 이 ZIP은 직전 단계의 완료 파일과 같으며 앞 글의 코드를 따로 모을 필요가 없습니다. 이번 단계 완료 ZIP은 다른 폴더에 풀어 비교합니다.
본문의 header·feature-list 코드를 기존 영역에 합치고 Flex 반응형 CSS를 추가합니다.
index.html: 이 파일의 기존 내용을 아래 완료 파일 전체와 대조하여 교체합니다. 다른 파일은 유지합니다.style.css: 이 파일의 기존 내용을 아래 완료 파일 전체와 대조하여 교체합니다. 다른 파일은 유지합니다.
짧은 코드 예제는 변경 위치를 설명합니다. 파일 전체를 작성하거나 교체할 때는 완료 파일 전체를 사용하세요. 생략 표시가 있는 부분 코드를 파일 전체로 저장하지 않습니다.
실행: ZIP의 index.html을 브라우저에서 엽니다. 외부 서버나 npm 설치 없이 로컬 HTML·CSS·이미지를 사용합니다. 문의 폼은 서버 전송을 연결하지 않은 문서 예제입니다. 연습 값만 사용하고 입력 형식을 확인합니다.
직접 확인할 결과: 기능 카드 세 개가 좁은 화면에서 세로, 48rem 이상에서 가로로 배치됩니다.
전체 파일·코드 보기
완료 파일 전체
index.html 전체 파일
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>
TaskFlow
</title>
<link href="./style.css" rel="stylesheet"/>
</head>
<body>
<header class="site-header">
<div class="page site-header__inner">
<a class="brand" href="#home">
TaskFlow
</a>
<nav aria-label="주요 메뉴" class="nav">
<a href="#features">
기능
</a>
<a href="#contact">
문의
</a>
</nav>
</div>
</header>
<main class="page" id="home">
<section>
<h1>
해야 할 일을 한눈에 정리하세요
</h1>
<p>
TaskFlow는 오늘의 작업을 빠르게 정리하는 서비스입니다.
</p>
</section>
<section id="features">
<h2>
주요 기능
</h2>
<div class="feature-list">
<article class="card">
<h3>
빠른 추가
</h3>
<p>
작업을 바로 기록합니다.
</p>
</article>
<article class="card">
<h3>
간단한 분류
</h3>
<p>
해야 할 일을 구분합니다.
</p>
</article>
<article class="card">
<h3>
진행 확인
</h3>
<p>
완료 여부를 확인합니다.
</p>
</article>
</div>
</section>
<section id="contact">
<h2>
문의하기
</h2>
<form>
<div class="form-field">
<label for="name">
이름
</label>
<input autocomplete="name" id="name" name="name" type="text"/>
</div>
<div class="form-field">
<label for="email">
이메일
</label>
<input autocomplete="email" id="email" name="email" type="email"/>
</div>
<div class="form-field">
<label for="message">
문의 내용
</label>
<textarea id="message" name="message" rows="5"></textarea>
</div>
<button type="submit">
문의 보내기
</button>
</form>
</section>
</main>
<footer class="site-footer">
<div class="page">
<p>
© TaskFlow
</p>
</div>
</footer>
</body>
</html>
style.css 전체 파일
:root {
color: #171717;
background: #f5f5f5;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family:
Arial,
sans-serif;
line-height: 1.6;
color: #171717;
background: #f5f5f5;
}
.page {
width: min(
100% - 32px,
960px
);
margin-inline: auto;
}
.site-header,
.site-footer {
padding-block: 20px;
}
main {
padding-block: 48px;
}
section + section {
margin-top: 64px;
}
.card {
padding: 24px;
border: 1px solid #d7d7d7;
background: #fff;
}
form {
display: grid;
gap: 16px;
}
.form-field {
display: grid;
gap: 8px;
}
input,
textarea {
width: 100%;
min-height: 44px;
padding: 10px 12px;
border: 1px solid #b8b8b8;
background: #fff;
}
button {
min-height: 44px;
padding: 10px 16px;
border: 1px solid #171717;
background: #171717;
color: #fff;
}
a { color: inherit; }
input, textarea, button { font: inherit; }
h1 { font-size: 32px; font-weight: 700; }
h2 { font-size: 24px; }
h3 { font-size: 20px; }
.site-header__inner {
display: flex;
flex-direction: column;
gap: 12px;
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
}
.feature-list {
display: flex;
flex-direction: column;
gap: 16px;
}
@media (min-width: 48rem) {
.site-header__inner {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.feature-list {
flex-direction: row;
}
.feature-list > * {
flex: 1;
}
}
위 파일과 ZIP의 동명 파일은 동일합니다. 회차별 README·CHANGES 문서는 ZIP에 포함합니다. 생성 산출물·node_modules·실제 환경 변수 파일은 포함하지 않습니다.
index.html
header 내부:
<header class="site-header">
<div class="page site-header__inner">
<a
class="brand"
href="#home"
>
TaskFlow
</a>
<nav
class="nav"
aria-label="주요 메뉴"
>
<a href="#features">기능</a>
<a href="#contact">문의</a>
</nav>
</div>
</header>
기능 카드:
<div class="feature-list">
<article class="card">
<h3>빠른 추가</h3>
<p>작업을 바로 기록합니다.</p>
</article>
<article class="card">
<h3>간단한 분류</h3>
<p>해야 할 일을 구분합니다.</p>
</article>
<article class="card">
<h3>진행 확인</h3>
<p>완료 여부를 확인합니다.</p>
</article>
</div>
style.css
.site-header__inner {
display: flex;
flex-direction: column;
gap: 12px;
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
}
.feature-list {
display: flex;
flex-direction: column;
gap: 16px;
}
@media (min-width: 48rem) {
.site-header__inner {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.feature-list {
flex-direction: row;
}
.feature-list > * {
flex: 1;
}
}
시작본과 이번 변경
CSS 입문: 박스 모델·선택자·캐스케이드 직접 확인하기의 완료 체크포인트를 시작본으로 사용합니다.
index.html의 header와 기능 카드 구조를 수정하고 style.css에 기본 column과 48rem row 조건을 추가합니다. 작은 화면과 큰 화면의 DOM 순서가 같은지 확인합니다.
연속 프로젝트 선수 조건
- 박스 모델과 class selector를 이해하고 있어야 합니다.
- 2회차 TaskFlow 기본 카드와 폼 스타일을 기준으로 진행합니다.
이전 체크포인트
publishing-step-02-css-basics
이번 목표
TaskFlow header와 기능 카드 영역을 작은 화면부터 반응형으로 구성합니다.
실행 확인
- 360px에서 header가 자연스럽게 세로인지 확인합니다.
- 768px 이상에서 header가 한 줄로 바뀌는지 확인합니다.
- 카드 세 개가 좁은 화면에서는 세로, 넓은 화면에서는 가로인지 봅니다.
- 페이지 전체에 가로 스크롤이 생기지 않는지 확인합니다.
완료 체크포인트
publishing-step-03-flex-responsive
다음 학습
다음 글에서는 relative·absolute·fixed·sticky가 어떤 기준 좌표와 문서 흐름을 사용하는지 TaskFlow 요소로 실습합니다.
글 소개·학습 준비·전체 목차

웹 퍼블리싱 · 3 / 15 · 필수 학습 · Flex와 미디어 쿼리
반응형 웹은 데스크톱 화면을 만든 뒤 모바일에서 억지로 줄이는 작업이 아닙니다.
작은 화면을 기본으로 만들고 콘텐츠가 넓어질 수 있는 시점에 Flexbox와 media query로 레이아웃을 확장하는 mobile-first 접근이 관리하기 쉽습니다.
이번 글에서 배우는 내용
- display:flex
- flex-direction
- gap
- justify-content와 align-items
- flex-wrap
- flex: 1
- mobile-first media query
- 콘텐츠가 깨지는 지점으로 breakpoint 정하기
먼저 알아야 할 내용
HTML class와 CSS 박스 모델·선택자를 알면 됩니다. 독립 카드 세 개와 스타일을 아래 파일로 제공합니다.
목차
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 15개 · 필수 13개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.