ํฌํธํด๋ฆฌ์ค ๋ง๋ค๋ฉด์ ๊ธฐ์ ์คํ ์น์ ์ ๋ฌธ ์ด๋ฆฌ๋ ์ ๋๋ฉ์ด์ ์ ๋ฃ์๋ค. ๋ฐ์คํฌํฑ์์๋ ์๋ค๋งํฑํ๊ฒ ๋ฌธ์ด ์ด๋ฆฌ๋ฉด์ ๋ด์ฉ์ด ๋ํ๋๊ณ , ๋ชจ๋ฐ์ผ์์๋ ๊ทธ๋ฅ ๋ฐ๋ก ๋ด์ฉ๋ง ๋ณด์ด๊ฒ ํ๋ ค๊ณ ํ๋๋ฐ...
๋ถ๋ช
ํ useIsMobile ํ
์ผ๋ก ๋ชจ๋ฐ์ผ ๊ฐ์งํด์ ์กฐ๊ฑด๋ถ๋ก ์ฒ๋ฆฌํ๋๋ฐ, ๋ชจ๋ฐ์ผ์์๋ ๊ณ์ ์ ๋๋ฉ์ด์
์ด ์ ์ฉ๋๋ ๊ฑฐ๋ค. ๊ทธ๊ฒ๋ ์ค๊ฐ์ ๋ฉ์ถฐ์ ์ฝํ
์ธ ๊ฐ ํฌ๋ช
ํ๊ฒ ๊ณ ์ ๋์ด ๋ฒ๋ ธ๋ค.
// ScrollAnimations.tsx
const ScrollAnimations = ({ children, className = '' }) => {
const isMobile = useIsMobile(); // ์ฌ๊ธฐ์ ๋ฌธ์ !
useEffect(() => {
const container = containerRef.current;
if (!container) return;
// ๋ชจ๋ฐ์ผ์ด ์๋ ๋๋ง ๋ฌธ ์ ๋๋ฉ์ด์
์์ฑํ๋ ค๊ณ ํ๋๋ฐ...
if (!isMobile) {
const sections = container.querySelectorAll('section');
sections.forEach((section) => {
const doorContent = section.querySelector('[data-direction="doorContent"]');
if (doorContent) {
doorTimeline.fromTo(
doorContent,
{ scale: 0.9, opacity: 0, y: 10 }, // ์ด๊ธฐ ์ํ
{
scale: 1,
opacity: 1,
y: 0,
duration: 0.7,
ease: 'back.out(1.2)',
}
);
}
});
}
}, [isMobile]);
};
// useMediaQuery.ts - ๋ฌธ์ ์ ์์ธ์ด ์ฌ๊ธฐ ์์์
export function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState<boolean>(false); // ๐จ ํญ์ false๋ก ์์!
useEffect(() => {
if (typeof window === 'undefined') return;
const mql = window.matchMedia(query);
setMatches(mql.matches); // ์ฌ๊ธฐ์ ์ค์ ๊ฐ์ผ๋ก ์
๋ฐ์ดํธ
const handler = (e: MediaQueryListEvent) => {
setMatches(e.matches);
};
mql.addEventListener('change', handler);
return () => mql.removeEventListener('change', handler);
}, [query]);
return matches;
}
๋ชจ๋ฐ์ผ์์ ๊ฐ๋ฐ์ ๋๊ตฌ ํ์ธํด๋ณด๋:
<div
style="opacity: 0; transform: translate(0px, 10px) scale(0.9, 0.9);"
>
...
</div>
์์ ํ์ด๋ฐ ๋ฌธ์ ์๋ค.
useState(false) โ isMobile = falseopacity: 0, scale: 0.9 ๋ฑ๋ฑsetMatches(true) โ isMobile = true๋ก ๋ณ๊ฒฝ์ฆ useState ์ด๊ธฐ๊ฐ์ด false๋ก ๊ณ ์ ๋์ด ์์ด์, ์ค์ ๋ชจ๋ฐ์ผ ํ๊ฒฝ์์๋ ์ฒ์์๋ ๋ฐ์คํฌํฑ์ผ๋ก ์ธ์ํด์ ์ ๋๋ฉ์ด์
์ ์ ์ฉํด๋ฒ๋ฆฌ๋ ๊ฑฐ์๋ค.
React์ useState Lazy Initialization์ ์ฌ์ฉํ๋ค.
// ์์ ๋ useMediaQuery.ts
export function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState<boolean>(() => {
<// ์๋ฒ์ฌ์ด๋์์๋ false
if (typeof window === 'undefined') return false;
// ํด๋ผ์ด์ธํธ์์๋ ์ค์ ๋ฏธ๋์ด ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ
return window.matchMedia(query).matches;
});
useEffect(() => {
if (typeof window === 'undefined') return;
const mql = window.matchMedia(query);
const handler = (e: MediaQueryListEvent) => {
setMatches(e.matches);
};
mql.addEventListener('change', handler);
return () => mql.removeEventListener('change', handler);
}, [query]);
return matches;
}
์ฐจ์ด์ :
useState(false) - ๋ฌด์กฐ๊ฑด false๋ก ์์useState(() => window.matchMedia(query).matches) - ์ค์ ๋ฏธ๋์ด ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ก ์์useState์ Lazy Initialization ๊ฐ๋ ์ ์ดํดํ๋ ๊ฒ ํต์ฌ์ด์๋ค.
// ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ
const [state, setState] = useState(initialValue);
// Lazy Initialization
const [state, setState] = useState(() => initialValue);
ํจ์๋ก ์ ๋ฌํ๋ฉด:
useState ์ด๊ธฐ๊ฐ ์ค์ ํ ๋ ์ข ๋ ์ ๊ฒฝ์จ์ผ๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค์๋ค. ํนํ:
๊ทธ๋ฆฌ๊ณ React ๋ ๋๋ง ์์๋ ๋ค์ ํ๋ฒ ์ ๋ฆฌ๋๋ค:
1. useState ์ด๊ธฐ๊ฐ ์ค์
2. ์ปดํฌ๋ํธ ๋ ๋๋ง
3. useEffect ์คํ
const [matches, setMatches] = useState<boolean>(false);
// ๋ชจ๋ฐ์ผ์์๋ ์ฒ์์ false โ ์ ๋๋ฉ์ด์
์ ์ฉ๋จ
const [matches, setMatches] = useState<boolean>(() => {
if (typeof window === 'undefined') return false;
return window.matchMedia(query).matches;
});
// ๋ชจ๋ฐ์ผ์์ ์ฒ์๋ถํฐ true โ ์ ๋๋ฉ์ด์
์ ์ ์ฉ๋จ
๊ฒฐ๊ณผ์ ์ผ๋ก:
๋น์ทํ ์ํฉ์์ ํ์ฉํ ์ ์๋ ํจํด๋ค:
// ๋ก์ปฌ์คํ ๋ฆฌ์ง ์ด๊ธฐ๊ฐ
const [theme, setTheme] = useState(() => {
if (typeof window === 'undefined') return 'light';
return localStorage.getItem('theme') || 'light';
});
// ๋ณต์กํ ๊ณ์ฐ ๊ฒฐ๊ณผ
const [processedData, setProcessedData] = useState(() => {
return expensiveComputation(initialData);
});
// ํ์ฌ ์๊ฐ ๊ธฐ๋ฐ
const [timestamp, setTimestamp] = useState(() => Date.now());
๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ์ ๋๊ตฌ์์ Network ํญ โ Disable cache ์ฒดํฌํ๊ณ ์๋ก๊ณ ์นจํด์ ํ ์คํธํด๋ณด๋ฉด ์ด๊ธฐ ๋ ๋๋ง ๋ฌธ์ ๋ฅผ ๋ ์ ์ก์ ์ ์๋ค.
๊ฒฐ๊ตญ useState ํ๋ ๋๋ฌธ์ ์๊ธด ๋ฌธ์ ์์ง๋ง, ๋๋ถ์ React์ ๋ ๋๋ง ์ฌ์ดํด๊ณผ ์ต์ ํ ๊ธฐ๋ฒ์ ๋ ๊น์ด ์ดํดํ๊ฒ ๋๋ค.