setTimeout๊ณผ ์ฐ๋กํ๋ง
9/16/2022
์์ฑ์ : ํ์๋ฐฐ
์ด์
ย
stickyํจ๊ณผ๋ก nav ๋ฐ์ ์ด๋ ํจ๊ณผ๋ฅผ ์ฃผ๋ คํ๋ค.
ํฐ ํ๋ฉด ๋ง์ง๋ง ์ํ์ ์ ๋ค๋ค๋์๋ interactiveํ๊ฒ ์ฌ๋ผ์ง๋ ํจ๊ณผ๋ฅผ ์ฃผ๊ธฐ ์ํด ์คํฌ๋กค์ ๊ฐ์งํด์ ๋ค์ ์คํ์ผ์ด ๋ฐ๋๊ฒ ๊ตฌํํด๋ณด์๋ค
ย
import { useEffect, useState } from 'react'; import styles from './nav.module.scss'; import cx from 'classnames'; export default function Nav(){ const [isScroll, setIsScroll] = useState(false); useEffect(() => { const handleScroll= () => setTimeout(() => { window.scrollY !== 0 ? setIsScroll(true) : setIsScroll(false); }, 200); window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, []); return ( <div className={cx(styles.container, { [styles.transNav]: isScroll })}> <div className={styles.title}> . . }
ย
์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ํตํด์ ์คํฌ๋กค์ด ์์ง์์ ๋ transNav ํจ๊ณผ๋ฅผ ์ฃผ๊ฒ ๊ตฌํํ๋ค. ๋ํ, setTimeout์ ํตํด 200ms ํ์ ์ด๋ฒคํธ์ ์ฝ๋ฐฑํจ์๊ฐ ์คํ๋๊ฒ ์ ์ฉํ๋ค. 200ms์ ์ ํ์ ๊ฑธ์ด๋ ๊ฒ์ ๋ฌดํ์ ์คํ๋ ์ ์๋ ์คํฌ๋กค์ ์ ํํ๊ธฐ ์ํจ์ด๋ค.
ย
์ดํํธ๊ฐ ์ผ์ด๋๊ฒ ํ๋๋ฐ๋ ๋ฌธ์ ๊ฐ ์์ง๋ง, ์ด ์ฝ๋๋ ๋ฌธ์ ๊ฐ ์๋ค
ย
ย
ย
์คํฌ๋กค์ด ์ผ์ด๋ ๋๋ง๋ค setTimeout์ ์ฝ๋ฐฑ๋ค์ด ๋ฌด์ํ ๋ง์ด ๋ฑ๋ก๋์ด ์คํ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ด๊ฒ์ ํ๋ก ํธ์๋ ์ต์ ํ์ ์
์ํฅ์ ์ด์ด์ ธ ํผํฌ๋จผ์ค๋ฅผ ๋ฎ์ถ ์ ์๋ค.
ย
๋ฌธ์ ๋ setTimeout์ ๋น๋ฒํ ์คํ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก ํด๋นํจ์์ ์ง์์ ์ธ ์คํ์ ์ ์ฝ์ ๊ฑธ์ด์ผ ํ๋ค. ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ค ์ ์์๊น?
ย
์ฐ๋กํ๋ง
์ด๋ด ๊ฒฝ์ฐ ์ฒ๋ฆฌํด์ค ์ ์๋ ์ด๋ฒคํธ ์ ์ด๋ฐฉ๋ฒ์ด ์ฐ๋กํ๋ง์ด๋ค.
์ฐ๋กํ๋ง์ ๋ง์ง๋ง ํจ์๊ฐ ํธ์ถ๋ ํ ์ผ์ ์๊ฐ์ด ์ง๋๊ธฐ ์ ์ ๋ค์ ํธ์ถ๋์ง ์๋๋ก ํ๋ ์ด๋ฒคํธ ์ ์ด๋ฐฉ๋ฒ์ผ๋ก, ํํ ๊ฒ์์์ ๋งํ๋ ์คํฌ ์ฟจํ์๊ณผ ๋น์ทํ๋ค. ํ๋ฒ ์ฌ์ฉํ๊ณ ๋๋ฉด ์ผ์ ์๊ฐ ๋์ ์ ํ์ด ๊ฑธ๋ ค ์คํฌ์ ์ฌ์ฉํ์ง ๋ชปํ๋ ๊ฒ์ด๋ผ ์ฝ๊ฒ ์ดํดํ ์ ์๋ค.
ย
์ด์ธ์ ๋๋ฐ์ด์ฑ ๊ธฐ๋ฒ์ด๋ ๊ฒ๋ ์กด์ฌํ๋๋ฐ, ์ฐ์์ผ๋ก ํธ์ถ๋๋ ํจ์๋ค ์ค์ ๋ง์ง๋ง์ ํธ์ถ๋๋ ํจ์๋ง ์คํ๋๊ฒ ํ๋ ๋ฐฉ๋ฒ์ผ๋ก ajax ํต์ ์ ํ ๋ ํธ์ถ ํ์๋ฅผ ๋ฎ์ถ๋ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉ๋ ์ ์๋ค. ์ ์ฉ๋ฐฉ๋ฒ์ ํด๋น๊ธฐ๋ฒ์ ํ์ฉํด APIํธ์ถ์ ์ต์ ํํ๋ ๊ณผ๊ฑฐ ํ๋ก์ ํธ๋ฅผ ์ฐธ๊ณ ํ์ [์ฐธ์กฐ์ฝ๋- useDebounce ์ด์ฉํ ์ปดํฌ๋ํธ]
ย
ย
์ฝ๋ ์ ์ฉ
์ด์ ์ฐ๋กํ๋ง์ ์ด์ฉํด ์ฝ๋๋ฅผ ๊ตฌํํด๋ณด์.
ย
useEffect(() => { let timer: null | ReturnType<typeof setTimeout>; const throttling = () => { if (!timer) { timer = setTimeout(() => { timer = null; window.scrollY > 180 ? setIsScroll(true) : setIsScroll(false); }, 200); } }; window.addEventListener('scroll', throttling); return () => { window.removeEventListener('scroll', throttling); }; }, []);
ย
์คํฌ๋กค์ด ์ผ์ด๋ ๋๋ง๋ค throttling์ด ์ฝ๋ฐฑํจ์๋ก ์คํ๋์ง๋ง, ์ฝ๋ฐฑํจ์ ๋ด์์ ์ด๋ฒคํธ๊ฐ ๋ฑ๋ก๋ timer๊ฐ ์์ ๊ฒฝ์ฐ์๋ง setTimeout์ด ๋ฑ๋ก๋๋๋ก ๊ตฌํํ๋ค. ํ์ด๋จธ๊ฐ ์๋ค๋ฉด ์๋ฌด๋ฐ ํ๋์ ํ์ง์๊ณ ์ด๋ฒคํธ ์ฝ๋ฐฑ์ ์ข
๋ฃ๋๋ค. ์ฐ๋กํ๋ง ํด๋ฒ์ ๋ง๊ฒ ์ ์ฉ๋์๋ค. [์ฐธ์กฐ์ฝ๋]
ย
๊ฒฐ๊ณผ
ย
ย
๋น์ทํ ํ๊ฒฝ์์ ์คํํ ๊ฒฐ๊ณผ, setTImeout์ ์ฐพ์๋ณด๊ธฐ ํ๋ค์ด์ก๋ค๋ ๊ฑธ ํ์ธ ํ ์ ์๋ค
ย
setTimeout์ ์ด์ฉํ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ
ย
setTimeout์ ์ด์ฉํ์ฌ ๋์ผ ํ๋ก์ ํธ์ ๋ก๋ฉํ๋ฉด๋ ๊ฐ์ ํ ์ ์์๋ค
import Router, { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; export const usePageLoading = () => { const { query: { title }, } = useRouter(); const [isPageLoading, setIsPageLoading] = useState(false); useEffect(() => { const routeEventStart = () => { setIsPageLoading(true); }; const routeEventEnd = () => { setIsPageLoading(false); }; Router.events.on('routeChangeStart', routeEventStart); Router.events.on('routeChangeComplete', routeEventEnd); Router.events.on('routeChangeError', routeEventEnd); return () => { Router.events.off('routeChangeStart', routeEventStart); Router.events.off('routeChangeComplete', routeEventEnd); Router.events.off('routeChangeError', routeEventEnd); }; }, []); if (!title) return false; return isPageLoading; };
route๊ฐ ์ด๋ํ ๋๋ง๋ค ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํด์ Loading ์ํ๋ฅผ ์ค์ ํ ์ ์๋ ์ปค์คํ
ํ
์ด๋ค. [์ฐธ์กฐ์ฝ๋]
ย
๋ฌธ์ ๋ ์ฒด๊ฐ์ ์์ฃผ ์ ๊น ์ด๋ํ๋ ํ์ด์ง ์ ํ์ ์์ด์๋ ๋ก๋ฉ ์คํผ๋๋ฅผ ๊ณ์ํด์ ๋์ด๋ค๋ ๊ฒ์ด๋ค. ์ ์ ์
์ฅ์์ 3~4์ด ์ ๋ ๊ฑธ๋ฆฌ๋ ํ๋ฉด์ โ๋ก๋ฉ์ด ๋๊ณ ์๊ตฌ๋โํ๊ณ ์ธ์ํ๊ณ ํธ์ํจ์ ๋๋ผ์ง๋ง, 0์ด ๋๋ก ์์ง์ด๋ ํ๋ฉด์๋ ๋ก๋ฉ์ฐฝ์ด ๋น๋ฒํ๊ฒ ๋จ๋ ๊ฒ์ UX๋ฅผ ์
ํ์ํจ๋ค๊ณ ํ๋จํ๋ค
ย
ย
์์์ ์ ์ฉํ setTimeout์ ์ด์ฉํด ๊ฐ์ง๊ณ ๋ก๋ฉ์ ์ ์ฝ์ ๊ฑธ์ด๋ณด์
ย
import Router from 'next/router'; import { useEffect, useState } from 'react'; export const usePageLoading = ({timeout}: number) => { const [isPageLoading, setIsPageLoading] = useState(false); useEffect(() => { let timer: ReturnType<typeof setTimeout> | null; const routeEventStart = () => { timer = setTimeout(() => { setIsPageLoading(true); }, timeout); }; const routeEventEnd = () => { clearTimeout(timer!); timer = null; setIsPageLoading(false); }; Router.events.on('routeChangeStart', routeEventStart); Router.events.on('routeChangeComplete', routeEventEnd); Router.events.on('routeChangeError', routeEventEnd); return () => { Router.events.off('routeChangeStart', routeEventStart); Router.events.off('routeChangeComplete', routeEventEnd); Router.events.off('routeChangeError', routeEventEnd); }; }, []); return isPageLoading; };
ย
๋ง์นจ
setTimeout API๋ ์ฝ๊ณ ๊ธฐ๋ณธ์ ์ธ ๊ฐ๋
์ด์ง๋ง, ํ๋ก์ ํธ๋ด์์ ๋น๋๊ธฐ์ ์ธ ์ด๋ฒคํธ๋ฅผ ์ ์ดํ๋๋ฐ ํฐ ๋์์ด ๋์๊ธฐ์ ์ ๋ฆฌ๋ฅผ ํด๋ณด์๋ค.
๊ทธ๋ฐ๋ฐ, ํ๋ก์ ํธ๋ฅผ ๋ง๋ฌด๋ฆฌํ๋ฉด์ ๋๋ ์๋ฌธ์ด ์์๋ค. ๋ง์ฝ, ๋ณต์กํ task๋ค์ด ์์ฌ์์ ๋, setTimeout์ผ๋ก ํธ์ถ๋ ์ฝ๋ฐฑํจ์๊ฐ ์ต์ฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋๊ฒ ํด์ฃผ๊ณ ์ถ์ ๋๋ ์ด๋ป๊ฒ ํ ์ ์์๊น?
๋ธ๋ผ์ฐ์ ์ ๋์๋ฐฉ์ ๊ด์ ์์ setTimeout์ ์ฝ๋ฐฑ์ ๋ง์ดํฌ๋ก ํ์คํฌ ํ์ ๋นํด์ ํ์์ฒ๋ฆฌ ๋๋ ๋งคํฌ๋ก ํ์คํฌ ํ์ ์ํด ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ ๋ฃจํ์ ์ต์ฐ์ ์ฒ๋ฆฌ๋์์ด ์๋๋ค.
ย
์กฐ์ฌํด๋ณธ ๋ฐ๋ก๋ ์ด๋ฌํ ์์์ ์ฐ์ ์์๋ฅผ ๋งค๊ฒจ์ ์ ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ด NodeJS์์ setImmedate์ process.nextTick์ด๋ผ๊ณ ํ๋ค. ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ๋ค๋ฃฐ ๋๋ง๋ค ๊ด๋ จ ๊ฐ๋
์ผ๋ก NodeJS๊ฐ ์ด์ด์ง๋ ํฌ์คํธ๋ค์ ๋ง์ด ๋ณด๊ณค ํ๋ค. ๊ทธ๋ ๊ธฐ์ NodeJS์ ๋ํด์๋ ํธ๊ธฐ์ฌ์ด ์ปค์ง๋ค. ์ด๋ค ๊ด๋ จ ํ๋ก์ ํธ๋ก ์งํํ ์ ์์์ง ๊ณ ๋ฏผ์ ํด๋ด์ผ๊ฒ ๋ค.
ย
+๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฌํ๊ฐ์๋ฅผ ์ฐพ์๋ค
๋ณธ ํฌ์คํธ๋ฅผ ๋ณด๋ ๋ถ๋ค์๊ฒ ์ถ์ฒ ๋๋ฆฝ๋๋ค