Skip to content

useMediaQuery

미디어 쿼리에 대한 일치 여부를 추적하는 React 훅입니다. 다양한 화면 크기, 디바이스 특성, 사용자 환경 설정에 따라 반응형 UI를 구현할 수 있습니다.

기본 사용법

tsx
import { useMediaQuery, media } from '@teamsparta/react';

function Example() {
  const isTabletOrLarger = useMediaQuery(media.min(768));
  const isDarkMode = useMediaQuery(media.prefersColorScheme.dark);

  return (
    <div className={isDarkMode ? 'dark-theme' : 'light-theme'}>
      {isTabletOrLarger ? <TabletContent /> : <MobileContent />}
    </div>
  );
}

Props

이름타입설명기본값
queryMediaQuery확인할 미디어 쿼리-
defaultValuebooleanSSR 환경에서 사용할 기본값false

Return

boolean - 미디어 쿼리 일치 여부 (true 또는 false)

미디어 쿼리 유틸리티 (media)

media 객체는 타입 안전한 방식으로 미디어 쿼리 문자열을 생성하는 유틸리티입니다. 직접 미디어 쿼리 문자열을 작성하는 대신 이 객체를 사용하면 타입 체크와 자동 완성을 통해 안전하게 미디어 쿼리를 작성할 수 있습니다.

카테고리별 미디어 쿼리

화면 크기 관련
함수/속성설명생성되는 쿼리예시
min(width)최소 화면 너비(min-width: {width}px)media.min(768)
max(width)최대 화면 너비(max-width: {width}px)media.max(767)
between(minWidth, maxWidth)화면 너비 범위(min-width: {minWidth}px) and (max-width: {maxWidth}px)media.between(768, 1023)
width(width)정확한 화면 너비(width: {width}px)media.width(1024)
디스플레이 방향 및 비율
함수/속성설명생성되는 쿼리예시
orientation.portrait세로 방향(orientation: portrait)media.orientation.portrait
orientation.landscape가로 방향(orientation: landscape)media.orientation.landscape
aspectRatio.exact(w, h)정확한 화면 비율(aspect-ratio: {w}/{h})media.aspectRatio.exact(16, 9)
aspectRatio.min(w, h)최소 화면 비율(min-aspect-ratio: {w}/{h})media.aspectRatio.min(16, 9)
aspectRatio.max(w, h)최대 화면 비율(max-aspect-ratio: {w}/{h})media.aspectRatio.max(16, 9)
디바이스 입력 관련
함수/속성설명생성되는 쿼리예시
pointer.fine마우스 등 정밀한 포인팅 장치(pointer: fine)media.pointer.fine
pointer.coarse터치스크린 등 덜 정밀한 포인팅 장치(pointer: coarse)media.pointer.coarse
pointer.none포인팅 장치 없음(pointer: none)media.pointer.none
pointer.anyFine최소 하나의 정밀한 포인팅 장치(any-pointer: fine)media.pointer.anyFine
pointer.anyCoarse최소 하나의 터치스크린(any-pointer: coarse)media.pointer.anyCoarse
hover.hover호버 기능이 있는 디바이스(hover: hover)media.hover.hover
hover.none호버 기능이 없는 디바이스(hover: none)media.hover.none
hover.anyHover최소 하나의 입력 장치가 호버 지원(any-hover: hover)media.hover.anyHover
디스플레이 품질
함수/속성설명생성되는 쿼리예시
resolution.min(dppx)최소 해상도(min-resolution: {dppx}dppx)media.resolution.min(2)
resolution.max(dppx)최대 해상도(max-resolution: {dppx}dppx)media.resolution.max(2)
resolution.exact(dppx)정확한 해상도(resolution: {dppx}dppx)media.resolution.exact(2)
resolution.retina레티나 디스플레이(min-resolution: 2dppx)media.resolution.retina
resolution.dpi(dpi)DPI 기반 해상도(resolution: {dpi}dpi)media.resolution.dpi(192)
colorGamut.srgbsRGB 색 영역(color-gamut: srgb)media.colorGamut.srgb
colorGamut.p3P3 색 영역(color-gamut: p3)media.colorGamut.p3
colorGamut.rec2020Rec2020 색 영역(color-gamut: rec2020)media.colorGamut.rec2020
사용자 환경 설정
함수/속성설명생성되는 쿼리예시
prefersColorScheme.dark다크 모드(prefers-color-scheme: dark)media.prefersColorScheme.dark
prefersColorScheme.light라이트 모드(prefers-color-scheme: light)media.prefersColorScheme.light
prefersReducedMotion.reduce모션 감소 선호(prefers-reduced-motion: reduce)media.prefersReducedMotion.reduce
prefersReducedMotion.noPreference모션 감소 선호하지 않음(prefers-reduced-motion: no-preference)media.prefersReducedMotion.noPreference
prefersReducedTransparency.reduce투명도 감소 선호(prefers-reduced-transparency: reduce)media.prefersReducedTransparency.reduce
prefersReducedTransparency.noPreference투명도 감소 선호하지 않음(prefers-reduced-transparency: no-preference)media.prefersReducedTransparency.noPreference
prefersContrast.more높은 대비 선호(prefers-contrast: more)media.prefersContrast.more
prefersContrast.less낮은 대비 선호(prefers-contrast: less)media.prefersContrast.less
prefersContrast.custom사용자 정의 대비 선호(prefers-contrast: custom)media.prefersContrast.custom
prefersContrast.noPreference대비 선호 없음(prefers-contrast: no-preference)media.prefersContrast.noPreference
prefersReducedData.reduce데이터 사용량 감소 선호(prefers-reduced-data: reduce)media.prefersReducedData.reduce
prefersReducedData.noPreference데이터 사용량 감소 선호하지 않음(prefers-reduced-data: no-preference)media.prefersReducedData.noPreference
환경 관련
함수/속성설명생성되는 쿼리예시
invertedColors.inverted색상 반전 적용(inverted-colors: inverted)media.invertedColors.inverted
invertedColors.none색상 반전 없음(inverted-colors: none)media.invertedColors.none
forcedColors.active강제 색상 활성화 (고대비 모드 등)(forced-colors: active)media.forcedColors.active
forcedColors.none강제 색상 비활성화(forced-colors: none)media.forcedColors.none
monochrome.any흑백 화면 (비트 수 상관없음)(monochrome)media.monochrome.any
monochrome.bits(bits)특정 비트 수의 흑백 화면(monochrome: {bits})media.monochrome.bits(8)
복합 쿼리
함수/속성설명생성되는 쿼리예시
and(...queries)모든 조건 충족 (AND){query1} and {query2} and ...media.and(media.prefersColorScheme.dark, media.max(767))
or(...queries)하나 이상의 조건 충족 (OR){query1}, {query2}, ...media.or(media.orientation.portrait, media.max(767))
not(query)조건 부정 (NOT)not {query}media.not(media.hover.hover)

유용한 쿼리 조합 예제

용도쿼리설명
반응형 디자인media.min(768)768px 이상 화면 (태블릿/데스크탑)
모바일 전용media.max(767)767px 이하 화면 (모바일)
태블릿 전용media.between(768, 1023)768px-1023px 사이 화면 (태블릿)
터치 디바이스media.pointer.coarse터치스크린 기기
마우스 기기media.pointer.fine마우스 사용 기기
다크 모드media.prefersColorScheme.dark다크 모드 사용자
접근성 모션 감소media.prefersReducedMotion.reduce모션 감소 선호 사용자
모바일 다크모드media.and(media.max(767), media.prefersColorScheme.dark)모바일에서 다크 모드
특수 접근성media.or(media.prefersReducedMotion.reduce, media.prefersHighContrast)모션 감소 또는 고대비 선호

헬퍼 훅

useMediaQuery 기반으로 만들어진 편의 훅들입니다.

화면 크기 관련 훅
기능미디어 쿼리
useMinWidth(width)최소 너비 이상인지 확인(min-width: {width}px)
useMaxWidth(width)최대 너비 이하인지 확인(max-width: {width}px)
useBetweenWidth(minWidth, maxWidth)너비 범위 내인지 확인(min-width: {minWidth}px) and (max-width: {maxWidth}px)
useExactWidth(width)정확한 너비인지 확인(width: {width}px)
디바이스 입력 관련 훅
기능미디어 쿼리
useIsTouchDevice()터치 디바이스인지 확인(pointer: coarse)
useIsMouseDevice()마우스 디바이스인지 확인(pointer: fine)
useHasHover()호버 기능이 있는지 확인(hover: hover)
사용자 환경 설정 관련 훅
기능미디어 쿼리
usePrefersDarkMode()다크 모드 선호 여부(prefers-color-scheme: dark)
usePrefersLightMode()라이트 모드 선호 여부(prefers-color-scheme: light)
usePrefersReducedMotion()모션 감소 선호 여부(prefers-reduced-motion: reduce)
usePrefersHighContrast()고대비 선호 여부(prefers-contrast: more)
usePrefersReducedData()데이터 사용량 감소 선호 여부(prefers-reduced-data: reduce)
디스플레이 특성 관련 훅
기능미디어 쿼리
useIsPortrait()세로 방향 화면인지 확인(orientation: portrait)
useIsLandscape()가로 방향 화면인지 확인(orientation: landscape)
useIsRetina()레티나 디스플레이인지 확인(min-resolution: 2dppx)
useSupportsP3ColorGamut()P3 색 영역 지원 여부(color-gamut: p3)
환경 관련 훅
기능미디어 쿼리
useIsForcedColors()강제 색상 모드 활성화 여부(forced-colors: active)
useIsInvertedColors()색상 반전 활성화 여부(inverted-colors: inverted)
useIsMonochrome()흑백 화면 여부(monochrome)

사용 예제

반응형 레이아웃
tsx
function ResponsiveLayout() {
  const isMobile = useMaxWidth(767);
  const isTablet = useBetweenWidth(768, 1023);
  const isDesktop = useMinWidth(1024);

  return (
    <div className="layout">
      {isMobile && <MobileLayout />}
      {isTablet && <TabletLayout />}
      {isDesktop && <DesktopLayout />}
    </div>
  );
}
다크 모드 지원
tsx
function DarkModeSupport() {
  const prefersDarkMode = usePrefersDarkMode();

  return (
    <div className={prefersDarkMode ? 'dark-theme' : 'light-theme'}>
      <h1>안녕하세요!</h1>
      <p>현재 {prefersDarkMode ? '다크' : '라이트'} 모드입니다.</p>
    </div>
  );
}
접근성 향상
tsx
function AccessibleButton() {
  const prefersReducedMotion = usePrefersReducedMotion();

  return (
    <button
      className={prefersReducedMotion ? 'no-animation' : 'animate'}
      aria-label="확인"
    >
      확인
    </button>
  );
}
디바이스별 인터페이스
tsx
function DeviceAwareInterface() {
  const isTouchDevice = useIsTouchDevice();
  const hasHover = useHasHover();

  return (
    <div>
      {isTouchDevice ? <TouchFriendlyControls /> : <PreciseControls />}
      {hasHover && <HoverEffects />}
    </div>
  );
}
복합 조건 활용
tsx
function SmartUI() {
  // 모바일이거나 모션 감소 선호 시 간단한 애니메이션 사용
  const useSimpleAnimation = useMediaQuery(
    media.or(media.max(767), media.prefersReducedMotion.reduce),
  );

  // 터치 기능이 있는 큰 화면(태블릿 등)에서는 특별한 UI 제공
  const isLargeTouch = useMediaQuery(
    media.and(media.min(768), media.pointer.coarse),
  );

  return (
    <div>
      <Animations complexity={useSimpleAnimation ? 'simple' : 'rich'} />
      {isLargeTouch && <TouchOptimizedUI />}
    </div>
  );
}

유의사항

SSR 지원

서버 사이드 렌더링 환경에서는 defaultValue 값을 사용합니다. 이는 브라우저 환경이 아니기 때문에 실제 미디어 쿼리를 확인할 수 없기 때문입니다.

tsx
// 기본적으로 서버에서는 모바일로 처리 (max-width: 767px)
const isMobile = useMediaQuery(media.max(767), true);
호환성

React 18 이상에서는 useSyncExternalStore를 사용하였습니다.

18버전 미만에서는 동작하지 않습니다.