메인 콘텐츠로 바로가기
노트 목록

동작 줄이기 설정 존중하기 (matchMedia)

김성박711
목차7

튜토리얼 — 동작 줄이기 설정 존중하기 (matchMedia)

DarkModeBadge는 다크 모드 여부를 글자로 보여 주기만 했습니다. 이번엔 matchMedia로 알아낸 조건에 따라 실제로 다른 화면을 그립니다 — 8.1절 개념에서 말한 "다른 컴포넌트를 보여 준다"가 이거예요.

검사할 조건은 prefers-reduced-motion 입니다. 어지럼증 등으로 화면 움직임이 불편한 사용자는 운영체제에서 "동작 줄이기"를 켭니다. 우리는 그 설정을 감지해, 애니메이션 대신 정지 화면을 보여 줍니다.


개념

matchMedia는 다크 모드든 화면 크기든 동작 줄이기든 쓰는 방법이 똑같습니다. 미디어 쿼리 문자열만 갈아 끼우면 됩니다.

window.matchMedia("(prefers-color-scheme: dark)"); // 다크 모드 (배운 것) window.matchMedia("(prefers-reduced-motion: reduce)"); // 동작 줄이기 (이번 것)

(prefers-reduced-motion: reduce)는 "사용자가 동작 줄이기를 켰는가"를 묻습니다. .matches가 true면 켜진 것입니다. 운영체제 설정 위치는 이렇습니다.

  • macOS: 시스템 설정 → 손쉬운 사용 → 디스플레이 → 동작 줄이기
  • Windows: 설정 → 접근성 → 시각 효과 → 애니메이션 효과

DarkModeBadge와 비교하면, matchMedia를 쓰고 정리 함수로 구독을 해제하는 뼈대는 완전히 같습니다. 한 번 익히면 미디어 쿼리만 바꿔 재사용하는 것 — 그걸 이 튜토리얼로 확인합니다.


함께 해보기

1단계 — MotionCard 컴포넌트 만들기

components/MotionCard.tsx 파일을 만듭니다.

"use client"; import { useState, useEffect } from "react"; export default function MotionCard() { const [reduceMotion, setReduceMotion] = useState(false); // 안전한 기본값 useEffect(() => { const mq = window.matchMedia("(prefers-reduced-motion: reduce)"); const update = () => setReduceMotion(mq.matches); update(); // 처음 한 번 검사 mq.addEventListener("change", update); // 설정이 바뀌면 알림 return () => { mq.removeEventListener("change", update); // 정리: 구독 해제 }; }, []); return ( <div className="p-4 space-y-2"> {reduceMotion ? ( // 동작 줄이기 ON → 움직이지 않는 정지 화면 <p className="text-2xl">🔵 정지 — 동작 줄이기 설정을 존중합니다</p> ) : ( // 동작 줄이기 OFF → 통통 튀는 애니메이션 <p className="animate-bounce text-2xl">🔵 통통 튀는 중!</p> )} <p className="text-sm text-gray-500"> prefers-reduced-motion:{" "} {reduceMotion ? "reduce (켜짐)" : "no-preference (꺼짐)"} </p> </div> ); }

DarkModeBadge와 같은 점:

  • useState(false)로 안전한 기본값에서 시작 — 서버에는 window가 없으니까요.
  • matchMedia·addEventListener("change")·정리 함수의 removeEventListener — 뼈대가 똑같습니다.

DarkModeBadge와 다른 점:

  • 미디어 쿼리가 (prefers-reduced-motion: reduce)로 바뀌었습니다.
  • 결과를 글자로만 보여 주지 않고, JSX 분기(reduceMotion ? 정지 : 애니메이션)로 다른 화면을 그립니다. animate-bounce는 통통 튀는 Tailwind 애니메이션입니다.

2단계 — 페이지에 올리고 확인

app/motion/page.tsx 파일을 만듭니다.

import MotionCard from "@/components/MotionCard"; export default function MotionPage() { return ( <main style={{ padding: 24 }}> <h2>동작 줄이기 설정 존중하기</h2> <MotionCard /> </main> ); }

/motion을 엽니다.

  • 기본 상태(동작 줄이기 꺼짐)에서는 🔵 이모지가 통통 튑니다. ✅
  • 운영체제에서 "동작 줄이기"를 켜면 → 새로고침 없이도 화면이 정지 상태로 바뀝니다. ✅

운영체제 설정을 바꾸기 번거롭다면, 크롬 개발자도구로도 됩니다.
F12 → 명령 메뉴(⌘⇧P) → "Show Rendering" → Emulate CSS prefers-reduced-motion 을
reduce로 바꿔 보세요. 배지와 애니메이션이 즉시 따라 바뀝니다.

확인 ✅ — matchMedia로 알아낸 조건(reduceMotion)에 따라 통통 튀는 화면과 정지 화면이 갈립니다. 운영체제 설정을 바꾸면 change 이벤트가 update를 불러, 새로고침 없이 화면이 따라 바뀝니다.


정리

  • matchMedia는 미디어 쿼리만 갈아 끼우면 다크 모드·화면 크기·동작 줄이기에 똑같이 쓰입니다.
  • (prefers-reduced-motion: reduce)로 사용자의 동작 줄이기 설정을 감지할 수 있습니다.
  • .matches 값에 따라 JSX를 분기하면, 글자만이 아니라 다른 화면 자체를 보여 줄 수 있습니다.
  • useEffect 안에서 쓰고, 정리 함수에서 removeEventListener로 구독을 해제합니다.

연습 거리

  1. DarkModeBadge와 MotionCard의 공통 로직을 useMediaQuery(쿼리문자열)라는 커스텀 훅으로 묶어, 두 컴포넌트가 함께 쓰게 만들기
  2. (orientation: portrait)로 세로/가로 화면을 감지해 표시하기
  3. 동작 줄이기가 켜졌을 때는 animate-bounce 대신 animate-pulse(은은한 깜빡임)처럼 더 약한 효과를 주기

댓글

불러오는 중...

댓글을 작성하려면 이 필요합니다.