useMediaQuery
๋ฏธ๋์ด ์ฟผ๋ฆฌ์ ๋ํ ์ผ์น ์ฌ๋ถ๋ฅผ ์ถ์ ํ๋ React ํ ์ ๋๋ค. ๋ค์ํ ํ๋ฉด ํฌ๊ธฐ, ๋๋ฐ์ด์ค ํน์ฑ, ์ฌ์ฉ์ ํ๊ฒฝ ์ค์ ์ ๋ฐ๋ผ ๋ฐ์ํ UI๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
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
์ด๋ฆ | ํ์ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
---|---|---|---|
query | MediaQuery | ํ์ธํ ๋ฏธ๋์ด ์ฟผ๋ฆฌ | - |
defaultValue | boolean | SSR ํ๊ฒฝ์์ ์ฌ์ฉํ ๊ธฐ๋ณธ๊ฐ | 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.srgb | sRGB ์ ์์ญ | (color-gamut: srgb) | media.colorGamut.srgb |
colorGamut.p3 | P3 ์ ์์ญ | (color-gamut: p3) | media.colorGamut.p3 |
colorGamut.rec2020 | Rec2020 ์ ์์ญ | (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) |
์ฌ์ฉ ์์
๋ฐ์ํ ๋ ์ด์์
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>
);
}
๋คํฌ ๋ชจ๋ ์ง์
function DarkModeSupport() {
const prefersDarkMode = usePrefersDarkMode();
return (
<div className={prefersDarkMode ? 'dark-theme' : 'light-theme'}>
<h1>์๋
ํ์ธ์!</h1>
<p>ํ์ฌ {prefersDarkMode ? '๋คํฌ' : '๋ผ์ดํธ'} ๋ชจ๋์
๋๋ค.</p>
</div>
);
}
์ ๊ทผ์ฑ ํฅ์
function AccessibleButton() {
const prefersReducedMotion = usePrefersReducedMotion();
return (
<button
className={prefersReducedMotion ? 'no-animation' : 'animate'}
aria-label="ํ์ธ"
>
ํ์ธ
</button>
);
}
๋๋ฐ์ด์ค๋ณ ์ธํฐํ์ด์ค
function DeviceAwareInterface() {
const isTouchDevice = useIsTouchDevice();
const hasHover = useHasHover();
return (
<div>
{isTouchDevice ? <TouchFriendlyControls /> : <PreciseControls />}
{hasHover && <HoverEffects />}
</div>
);
}
๋ณตํฉ ์กฐ๊ฑด ํ์ฉ
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
๊ฐ์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์ด ์๋๊ธฐ ๋๋ฌธ์ ์ค์ ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ฅผ ํ์ธํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
// ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฒ์์๋ ๋ชจ๋ฐ์ผ๋ก ์ฒ๋ฆฌ (max-width: 767px)
const isMobile = useMediaQuery(media.max(767), true);
ํธํ์ฑ
React 18 ์ด์์์๋ useSyncExternalStore
๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
18๋ฒ์ ๋ฏธ๋ง์์๋ ๋์ํ์ง ์์ต๋๋ค.