미디어 쿼리(media query)는 '화면 폭이 640px 이상일 때'처럼 조건을 걸고, 조건이 맞을 때만 그 안의 규칙을 쓰게 하는 CSS입니다. 모바일 먼저(mobile first)는 기본 규칙을 좁은 화면에 맞춰 쓰고, 넓어지면 바뀔 선언만 @media (min-width: 640px) 조건 블록에 두는 차례입니다.
레시피 노트의 머리글은 휴대전화에서는 사이트 이름 아래에 내비게이션을 쌓는 편이 읽기 좋고, 넓은 화면에서는 한 줄 양 끝에 두는 편이 자리를 덜 씁니다. 한 CSS 파일로 두 모양을 내려면 '이 폭부터는 이렇게'라는 조건이 필요합니다. 이 레슨은 조건 블록이 언제 드는지, 기본 규칙과 어떻게 겨루는지, 그리고 모바일 먼저로 쓰는 하위 목표 셋을 봅니다.
그림 준비 중
1조건 블록 — 조건이 맞을 때만 안의 규칙이 든다
머리글을 모바일 먼저로 씁니다. 결과 화면 칸은 640px보다 좁아서, 예제마다 그릴 폭을 적어 그 폭으로 그린 뒤 칸에 맞게 줄여 보입니다.
<header class="site-header">
<a href="index.html">
레시피 노트
</a>
<nav>
<a href="kimchi.html">
김치볶음밥
</a>
</nav>
</header>
/* 1. 좁은 화면 기본 규칙 */
.site-header {
display: flex;
flex-direction: column;
gap: 12px;
}
/* 2. 넓어지면 바뀔 선언만 */
@media (min-width: 640px) {
.site-header {
flex-direction: row;
justify-content:
space-between;
}
}639px.site-header의flex-directioncolumn.site-header의justify-contentnormal
@media 뒤 괄호가 조건이고, 그 뒤 중괄호가 조건 블록입니다. min-width: 640px은 '화면 폭(뷰포트 폭)이 640px 이상'입니다. 화면 폭 639px에서는 조건이 맞지 않아 조건 블록 안 규칙이 없는 것처럼 되고, 머리글은 기본 규칙대로 세로로 쌓였습니다. 조건이 맞을 때만 그 안의 규칙이 요소에 닿습니다(브라우저 규칙 4).
휴대전화에서 이 조건이 기기 화면 폭을 보려면 meta viewport의 뷰포트 줄이 있어야 합니다. 줄이 없으면 휴대전화도 980px 폭으로 배치해 min-width: 640px이 맞습니다.
2640은 든다 — 경계 폭
같은 코드를 1px 넓은 640px로 그립니다.
<header class="site-header">
<a href="index.html">
레시피 노트
</a>
<nav>
<a href="kimchi.html">
김치볶음밥
</a>
</nav>
</header>
.site-header {
display: flex;
flex-direction: column;
gap: 12px;
}
@media (min-width: 640px) {
.site-header {
flex-direction: row;
justify-content:
space-between;
}
}640px.site-header의flex-directionrow.site-header의justify-contentspace-between
640px에서 머리글이 한 줄 양 끝으로 바뀌었습니다. min-은 '이상', max-는 '이하'라 경계 폭 자신이 듭니다 — min-width: 640px은 640에서 맞고 639에서 맞지 않습니다. 모양이 바뀌는 폭을 경계 폭(자료에 따라 브레이크포인트)이라고 부릅니다.
3기본 규칙과 조건 블록은 함께 든다
640px에서 머리글에 닿은 선언을 모아 봅니다.
| 선언 | 어디에 | 639px | 640px |
|---|---|---|---|
display: flex | 기본 규칙 | 듦 | 듦 |
gap: 12px | 기본 규칙 | 듦 | 듦 |
flex-direction: column | 기본 규칙 | 이김 | 짐 |
flex-direction: row | 조건 블록 | 닿지 않음 | 이김 |
justify-content: space-between | 조건 블록 | 닿지 않음 | 듦 |
조건이 맞아도 기본 규칙은 그대로 듭니다. 그래서 조건 블록에는 display: flex를 다시 적지 않고 바뀔 선언만 둡니다. flex-direction처럼 두 곳에 같은 속성이 있으면, 선택자가 같으니 나중에 쓴 쪽이 이깁니다 — 조건 블록이 아래에 있어 row가 이겼습니다(브라우저 규칙 5). 조건 블록 안이라고 명시도가 높아지지는 않습니다.
4모바일 먼저 — 하위 목표 셋
1절 코드의 주석이 하위 목표입니다.
| 하위 목표 | 할 일 | 1절 코드 |
|---|---|---|
| 1. 좁은 화면 기본 규칙 | 휴대전화 화면에 맞는 배치를 조건 없이 쓴다 | 세로로 쌓는 .site-header |
| 2. 넓어지면 바뀔 선언만 조건 블록에 | min-width 조건 블록을 기본 규칙 아래에 두고, 바뀌는 선언만 적는다 | row · space-between |
| 3. 경계 폭에서 확인 | 경계 폭과 1px 좁은 폭에서 둘 다 본다 | 639 · 640 |
좁은 화면을 먼저 쓰는 까닭은 휴대전화에서 조건 블록을 하나도 읽지 않아도 되는 모양이 기본이 되기 때문입니다. 넓은 화면은 자리가 넉넉해 선언 몇 줄을 더해 바꾸기 쉽습니다. 경계 폭은 기기 이름이 아니라 내용을 보고 고릅니다. 창을 넓혀 가며 머리글이 한 줄에 무리 없이 들어가는 폭을 찾아 그 폭을 씁니다.
카드 격자의 열 수는 미디어 쿼리 없이도 바뀝니다. 들어가는 만큼 열 만들기의 auto-fill이 같은 폭에서 열 수를 따로 정합니다. 미디어 쿼리는 머리글처럼 폭에 따라 배치 방식을 바꿀 때 씁니다.
화면 폭이 아니라 그 요소를 담은 부모 상자의 폭을 조건으로 쓰는 컨테이너 쿼리(container query — @container)도 있습니다. 같은 카드가 넓은 칸과 좁은 칸에 함께 놓일 때 쓰며, 이 과목에서는 이름만 봅니다.
예제
후기 폼을 넓은 화면에서 한 줄로
레시피 후기 폼의 이름 칸과 단추를 좁은 화면에서는 세로로, 640px 이상에서는 한 줄로 놓습니다. 넓은 화면 쪽을 그립니다.
<form class="form-row">
<label for="nick">이름</label>
<input
id="nick"
name="nick"
/>
<button type="submit">
보내기
</button>
</form>
/* 1. 좁은 화면 기본 규칙 */
.form-row {
display: flex;
flex-direction: column;
gap: 8px;
}
/* 2. 넓어지면 바뀔 선언만 */
@media (min-width: 640px) {
.form-row {
flex-direction: row;
align-items: center;
}
}800px.form-row의flex-directionrow.form-row의align-itemscenter
- 800px은 640 이상이라 조건 블록이 들어 라벨 · 칸 · 단추가 한 줄입니다.
- 3걸음 확인은 639px과 640px 두 폭에서 합니다. 639px에서는
flex-direction이column이고align-items는 처음 값normal입니다.
자주 틀리는 곳
틀린 생각: min-width: 640px은 640을 빼고 641부터다.
2절 예제처럼 화면 폭 640px에서 flex-direction은 row입니다. 640px에서 이미 조건이 맞았습니다.
맞는 규칙: min-width는 '이상', max-width는 '이하'입니다. 경계 폭 자신이 듭니다.
틀린 생각: 조건 블록 안 규칙은 조건이 맞으면 늘 이긴다.
<header class="site-header">
<a href="index.html">
레시피 노트
</a>
</header>
@media (min-width: 640px) {
.site-header {
flex-direction: row;
}
}
.site-header {
display: flex;
flex-direction: column;
}1280px.site-header의flex-directioncolumn
1280px에서 조건은 맞았지만 머리글은 세로 그대로입니다. 조건 블록을 기본 규칙 위에 두어, 선택자가 같은 아래 기본 규칙이 나중에 쓴 쪽으로 이겼습니다.
맞는 규칙: 조건 블록은 명시도를 올리지 않고 차례로 겨룹니다. 조건 블록은 기본 규칙 아래에 둡니다.
틀린 생각: 조건의 숫자만 맞으면 단위는 없어도 된다.
<header class="site-header">
<a href="index.html">
레시피 노트
</a>
</header>
.site-header {
display: flex;
flex-direction: column;
}
@media (min-width: 640) {
.site-header {
flex-direction: row;
}
}1280px.site-header의flex-directioncolumn
1280px에서도 세로입니다. 640에 단위가 없어 브라우저가 조건을 알아듣지 못했고, 알아듣지 못한 조건은 어느 폭에서도 맞지 않습니다.
맞는 규칙: 조건의 폭에는 px 같은 단위를 붙입니다.
틀린 생각: 휴대전화 기종마다 그 폭의 조건 블록을 만든다.
휴대전화의 화면 폭은 기종마다 다릅니다(360 · 390 · 393 · 412px …). 'min-width: 393px은 iPhone 16용'처럼 만들면 기종이 나올 때마다 조건이 늘고, 390px 휴대전화는 393용 규칙을 받지 못합니다.
맞는 규칙: 경계 폭은 내용이 깨지기 시작하는 폭에서 고릅니다. 조건 블록은 몇 개 안으로 둡니다.
확인 문제
1. 예제 머리글 CSS에서 조건만 min-width: 800px로 바꿨습니다. 화면 폭 700px에서 머리글은 어떻게 보일까요? 보기 셋 가운데 고르세요.
<header class="site-header">
<a href="index.html">
레시피 노트
</a>
<nav>
<a href="kimchi.html">
김치볶음밥
</a>
</nav>
</header>
.site-header {
display: flex;
flex-direction: column;
gap: 12px;
}
@media (min-width: 800px) {
.site-header {
flex-direction: row;
justify-content:
space-between;
}
}
힌트
화면 폭 700px이 조건 min-width: 800px을 채우는지부터 보세요.
정답
① — 700은 800 이상이 아니라 조건 블록이 들지 않고, 기본 규칙대로 세로로 쌓입니다.
700px- ②는 조건이 맞았을 때의 모양입니다. 800px부터 이렇게 바뀝니다.
- ③은 조건 블록이 들어도
flex-direction만 바뀌고justify-content는 안 든다고 본 것입니다. 조건이 맞으면 블록 안 선언이 함께 듭니다.
2. 레시피 노트의 카드 목록을 넓은 화면에서 두 열로 바꾸려고 합니다. 하위 목표 2의 조건 줄 ____ 자리를 채우세요 — 화면 폭 640px부터 두 열입니다.
/* 1. 좁은 화면 기본 규칙 */
.recipe-list {
display: grid;
gap: 16px;
}
/* 2. 넓어지면 바뀔 선언만 */
@media (____) {
.recipe-list {
grid-template-columns:
1fr 1fr;
}
}
힌트
'이상'을 나타내는 접두어와 단위까지 적습니다.
정답
min-width: 640px
@media (min-width: 640px) {이 됩니다. 640px에서 맞고 639px에서 맞지 않습니다.max-width: 640px이면 반대로 640px 이하에서 두 열이 됩니다.min-width: 640처럼 단위를 빠뜨리면 어느 폭에서도 맞지 않습니다.
3. 플로우 카페 메뉴판의 메뉴 줄 CSS입니다. 화면 폭 640px과 641px에서 .menu의 flex-direction을 바르게 짝지은 것을 고르세요.
.menu {
display: flex;
}
@media (max-width: 640px) {
.menu {
flex-direction: column;
}
}
- ① 640px
column· 641pxrow - ② 640px
row· 641pxrow - ③ 640px
column· 641pxcolumn
힌트
max-가 '이하'인지 '미만'인지 정하고, 두 폭을 하나씩 조건에 넣어 보세요.
힌트 둘
조건이 맞지 않는 폭에서는 flex-direction을 적은 곳이 없습니다. 그때는 처음 값입니다.
정답
① — max-width: 640px은 '640px 이하'라 640에서 맞아 column, 641에서는 맞지 않아 처음 값 row입니다.
<ul class="menu">
<li>커피 4000원</li>
<li>라테 4500원</li>
</ul>
.menu {
display: flex;
}
@media (max-width: 640px) {
.menu {
flex-direction: column;
}
}640px.menu의flex-directioncolumn
- ②는 경계 폭 640을 뺀다고 본 것입니다(경계를 빼는 생각).
- ③은 조건 블록이 한 번 들면 넓어져도 남는다고 본 것입니다. 조건은 폭이 바뀔 때마다 다시 봅니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절의 머리글을 index.html과 style.css로 저장하고 link로 이어 엽니다(문서 뼈대의 body 안에 예제 HTML을 넣습니다). 개발자 도구 기기 모드에서 Dimensions: Responsive(크기: 반응형)를 고르고 너비 칸에 639와 640을 차례로 적어, 머리글이 바뀌는 폭을 봅니다. 이번에는 조건의 640을 641로 바꾸고 저장 · 새로고침한 뒤, 640에서 어떻게 될지 먼저 적고 맞춰 봅니다.