๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Dev-diary/์ผ์ƒ

๊ธฐ๋Šฅ์ถ”๊ฐ€ : 4์ฃผ์ฐจ ํ”Œ์  ๋ฉ”์ธํŽ˜์ด์ง€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌํ˜„ (1)

by ciocio 2022. 2. 26.

๐Ÿ“ ์˜ค๋Š˜ ๋„์ „ํ•  ๊ธฐ๋Šฅ

ํ”„๋กœ์ ํŠธ๋ฅผ ์†Œ๊ฐœํ•˜๋Š” ๋ฉ”์ธํŽ˜์ด์ง€ ๋””์ž์ธ์€ ์‚ฌ์‹ค ๊ฐ€์žฅ ๋Šฆ๊ฒŒ ๋„์ถœ๋˜์—ˆ๋‹ค.
๋…ธ์…˜์ฒ˜๋Ÿผ [ ๊ธฐ๋Šฅ + ๊ฐ„๋‹จํ•œ ์†Œ๊ฐœ + gif ] ๊ธฐ๋Šฅ์„ ์ปดํŒฉํŠธํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๋Š” ๊ฑด ์–ด๋ ต์ง€์•Š๋‹ค๊ณ  ๋Š๊ผˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
ํ…์ŠคํŠธ ๋ช‡ ์ž๋ž‘ gif ๋„ฃ์ง€ ๋ญ ..  ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์œผ๋‚˜ ๊ฒฐ๊ณผ๋ฌผ์€ ๊ฝค ๋นˆ์•ฝํ•ด๋ณด์˜€๋‹ค ๐Ÿค” ๐Ÿ˜จ
์ง„์งœ ์•ˆ์ผํ•œ ์ƒ๊ฐ์ด์—ˆ๋‹ค !!

๊ฐœ์ธ์ ์œผ๋กœ ์•„์‰ฝ๊ธฐ๋„ ํ•˜๊ณ , ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ณต๋ถ€๋„ ํ• ๊ฒธ ๋ฉ”์ธํŽ˜์ด์ง€ ์ „์ฒด๋ฅผ ์ˆ˜์ •ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ”„๋กœ์ ํŠธ๋ฅผ ๋” ์ ๊ทน์ ์œผ๋กœ ์†Œ๊ฐœํ•˜๋ ค๊ณ  ํ•œ๋‹ค !! ๋ฉ”์ธํŽ˜์ด์ง€๋Š” ์ฒซ์ธ์ƒ์„ ์ขŒ์šฐํ•˜๋‹ˆ๊นŒ ์ค‘์š”ํ•˜๋‹ค !!

 

๐Ÿ“Ž ์ฐธ๊ณ  ๋ ˆํผ๋Ÿฐ์Šค

 

UI ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŽ์€ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ๋ž‘ ์ž˜ ๋งž๊ฒ ๋‹จ ์ƒ๊ฐ์„ ํ–ˆ๋‹ค !! ๐Ÿ˜Š

์Šคํฌ๋กค ์ด๋ฒคํŠธ๋กœ ์ธํ•ด ์Šคํฌ๋ฆฐ ์ „์ฒด์˜ ์ด๋ฏธ์ง€๊ฐ€ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ฐ”๋€Œ๋Š” ๊ฒƒ๋„ ๋งˆ์Œ์— ๋“ค์—ˆ๊ณ ,

ํŠนํžˆ ์ € ์„œ์„œํžˆ ํŠ€์–ด๋‚˜์˜ค๋Š”ํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ...! ๊ผญ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค ใ…Žใ…Ž

 

•  Bare-minimum Requirements

โœ” ์Šคํฌ๋กค ์ด๋ฒคํŠธ : up & down ์— ๋”ฐ๋ผ ์œ„์น˜ ์ด๋™
โœ” ํ…์ŠคํŠธ, ์ปดํฌ๋„ŒํŠธ transition ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ถ”๊ฐ€
โœ” ์‚ฌ์šฉ์ž ํ›„๊ธฐ ๋ณผ ์ˆ˜ ์žˆ๋Š” < , > ๋ฒ„ํŠผ ๊ตฌํ˜„
โœ” ์ฒดํ—˜ํ•˜๊ธฐ ๋ฒ„ํŠผ, Footer ๋””์ž์ธ

 

•  Advanced Requirements

โœ” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™์  ์• ๋‹ˆ๋ฉ”์ด์…˜ ? (ํ˜„์žฌ๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ์ง€ ๊ฐ์ด ์•ˆ์˜ด)

 

 

๐Ÿ“Œ ์Šคํฌ๋กค ์ด๋ฒคํŠธ : up & down ์— ๋”ฐ๋ผ ์œ„์น˜ ์ด๋™

๊ณ ๋ คํ•ด์•ผํ•˜๋Š” ์กฐ๊ฑด๋“ค
โœ” ๋งˆ์šฐ์Šค ํœ ์„ ๋‚ด๋ ธ์„ ๋•Œ ๋‹ค์Œ ์„น์…˜ element๋กœ ์ด๋™ํ•ด์•ผํ•œ๋‹ค.
โœ” ๋งˆ์šฐ์Šค ํœ ์„ ์˜ฌ๋ ธ์„ ๋•Œ ์ด์ „ ์„น์…˜ element๋กœ ์ด๋™ํ•ด์•ผํ•œ๋‹ค.
โœ” ์Šคํฌ๋กค์„ ์ง์ ‘ ์žก๊ณ  ๋งจ ์œ„๋กœ ์˜ฌ๋ ธ์„ ๋•Œ ํ˜„์žฌ element๋กœ ์ƒํƒœ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์–ด์•ผํ•œ๋‹ค.
โœ” ์Šคํฌ๋กค์„ ์ง์ ‘ ์žก๊ณ  ๋งจ ์•„๋ž˜๋กœ ๋‚ด๋ ธ์„ ๋•Œ ํ˜„์žฌ element๋กœ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผํ•œ๋‹ค.
โœ” ๋ธŒ๋ผ์šฐ์ €์˜ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ–ˆ์„ ๋•Œ ์„น์…˜ element์˜ ํฌ๊ธฐ๊ฐ€ ๋Œ€์‘๋˜์–ด์•ผํ•œ๋‹ค.

 

๐Ÿ’พ  ์ค‘๊ฐ„ ๊ณผ์ • ๋ Œ๋”๋ง

 

 

์Šคํฌ๋กค ์ด๋ฒคํŠธ๋Š” ์ตœ์†Œํ•œ์œผ๋กœ, (ํœ  ์›€์ง์ž„ 1๋ฒˆ๋‹น ์ด๋ฒคํŠธ ๋ฐœ์ƒ 1๋ฒˆ) 1:1 ๋Œ€์‘์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค๊ณ„ํ•˜์˜€๋‹ค.

์ฝ˜์†”์ฐฝ์— ์Šคํฌ๋กค์„ ๋ฐ‘์œผ๋กœ ๋‚ด๋ฆด ๋• scroll down ์ด, ์œ„๋กœ ์˜ฌ๋ฆด ๋• scroll up ์ด ์ถœ๋ ฅ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์Šคํฌ๋กค์„ ์ง์ ‘ ์žก๊ณ  ์˜ฎ๊ฒผ์„ ๋• ์‹œ์ž‘๊ณผ ๋ ๋ถ€๋ถ„๋งŒ ์ธ์‹ํ•˜๊ฒŒ ํ•˜์˜€๋‹ค. (๊ทธ ์™ธ์—” ์˜๋ฏธ์—†๋‹ค๊ณ  ํŒ๋‹จ)

 

 

๋ฆฌ์‚ฌ์ด์ฆˆ ์ด๋ฒคํŠธ ๋˜ํ•œ re-rendering ์„ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐฉํ–ฅ์„ ์„ค๊ณ„ํ–ˆ๋‹ค.

๋ฆฌ์‚ฌ์ด์ฆˆ๋œ view์— ๋งž์ถฐ ์„น์…˜์˜ ๋†’์ด๊ฐ€ ์กฐ์ ˆ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ตฌํ˜„ํ•˜๋ฉด์„œ ๋Š๋‚€๊ฑด๋ฐ, ๋ฆฌ์‚ฌ์ด์ฆˆ์ด๋ฒคํŠธ์— scrollBy ์ด๋ฒคํŠธ๋ฅผ ๋ถ™์—ฌ์•ผํ•˜๋‚˜ ๊ณ ๋ฏผ์ด ๋œ๋‹ค.

(ํ˜„์žฌ ๋ฆฌ์‚ฌ์ด์ฆˆ์ด๋ฒคํŠธ๋Š” custom Hook ์œผ๋กœ ์Šคํฌ๋กค์ด๋ฒคํŠธ์™€ ๋กœ์ง์ด ๋ถ„๋ฆฌ๋˜์–ด์žˆ์Œ)

(์ด๋ฒคํŠธ๋ฅผ ๋ถ™์ด๊ฒŒ ๋˜๋ฉด custom Hook์„ ํ•ด์ฒดํ•ด์•ผํ•  ์ˆ˜๋„ ์žˆ ... ๋‹ค)

 

 

๐Ÿ’พ  ์ค‘๊ฐ„ ๊ณผ์ • ์ฝ”๋“œ

 

// view height ๊ตฌํ•˜๋Š” custom Hook

export function useHeight() {
  const [docsHeight, setDocsHeight] = useState<number>(window.innerHeight);

  useEffect(() => {
    let mounted = true; // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋˜์—ˆ์„ ๋•Œ
    let timer: any = null; // useEffect ์•ˆ์—์„œ setTimeout์ด ํ•œ๋ฒˆ๋งŒ ๋™์ž‘ํ•˜๋„๋ก ์ œํ•œ
    window.addEventListener('resize', () => {
      if (mounted) {
        // timer๋ผ๋Š” ๋ณ€์ˆ˜์— ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์žฌํ• ๋‹นํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ด๋ฒคํŠธ ํ˜ธ์ถœ์„ ์ œํ•œํ•œ๋‹ค
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log('๋ฆฌ์‚ฌ์ด์ฆˆ ์ด๋ฒคํŠธ ์‹œ์ž‘');
          setDocsHeight(window.innerHeight);
        }, 300);
      }
    });
    // useEffect์—์„œ return function์€
    // component๊ฐ€ unmount๋  ๋•Œ ์‹คํ–‰๋˜๋Š” callback function์ด๋‹ค!
    return () => {
      // console.log('๋ฆฌ์‚ฌ์ด์ฆˆ ์ด๋ฒคํŠธ ์ข…๋ฃŒ');
      mounted = false; // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜์—ˆ์„ ๋•Œ
    };
  });
  return {
    docsHeight,
  };
}

 

// scroll event ๋Œ€์‘ ๋กœ์ง

const { docsHeight } = useHeight();
const [currSectIdx, setCurrSectIdx] = useState<number>(0);
const [scrollY, setScrollY] = useState<number>(0);

useEffect(() => {
  const elementsArray = document.querySelectorAll('.mainpage-section');
  let mounted = true;
  let timer: any = null;
  window.addEventListener('scroll', () => {
    if (mounted) {
      clearTimeout(timer);
      timer = setTimeout(() => {
        // scroll top
        if (window.pageYOffset === 0) {
          console.log('scroll top!');
          setCurrSectIdx(0);
        }
        // scroll bottom
        else if (
          window.pageYOffset ===
          (elementsArray.length - 1) * docsHeight
        ) {
          console.log('scroll bottom!');
          setCurrSectIdx(elementsArray.length - 1);
        }
        // scroll down
        else if (
          window.pageYOffset &&
          currSectIdx * docsHeight < window.pageYOffset &&
          window.pageYOffset < (currSectIdx + 1) * docsHeight
        ) {
          console.log('scroll down');
          setCurrSectIdx((prev) => {
            const curr = prev + 1;
            if (elementsArray !== undefined) {
              window.scrollBy({
                top: elementsArray[curr].getBoundingClientRect().top,
                behavior: 'smooth',
              });
            }
            return curr;
          });
        }
        // scroll up
        else if (
          window.pageYOffset &&
          window.pageYOffset < currSectIdx * docsHeight
        ) {
          console.log('scroll up');
          setCurrSectIdx((curr) => {
            const prev = curr - 1;
            if (elementsArray !== undefined) {
              window.scrollBy({
                top: elementsArray[prev].getBoundingClientRect().top,
                behavior: 'smooth',
              });
            }
            return prev;
          });
        }
        setScrollY(window.pageYOffset);
      }, 100);
    }
  });
  return () => {
    mounted = false;
  };
}, [scrollY]);
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€