ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ๋ฆฌ์คํธ ๋ ๋๋ง ๊ด๋ จ ๊ณ ๋ฏผ์ ๋ ๋น ์ง์ง ์์ฃ . ํนํ "๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉ์์๊ฒ ๋น ๋ฅด๊ฒ, ๋ถ๋๋ฝ๊ฒ ๋ณด์ฌ์ค ๊ฒ์ธ๊ฐ?"๋ ๋ชจ๋ ์๋น์ค์์ ์ค์ํ ๋ฌธ์ ์ ๋๋ค.
์ต๊ทผ ์ ๋ ๊ตฌ์ฑ์ ์ ๋ณด๋ฅผ ๋ณด์ฌ์ฃผ๋ ํ ์ด๋ธ UI๋ฅผ ๋ฆฌ๋ด์ผํ๋ฉด์ ์ด ๋ฌธ์ ๋ฅผ ์ง์ ๊ฒช์๊ณ , ๊ทธ ํด๊ฒฐ์ ์ํด IntersectionObserver ๊ธฐ๋ฐ Lazy Rendering์ ์ ํํ์ต๋๋ค. ์ค๋์ ๊ทธ ์ฌ์ ๊ณผ ์ค๋ฌด ์ ์ฉ ํฌ์ธํธ๋ฅผ ๊ณต์ ํด๋ณด๋ ค ํฉ๋๋ค.
๐งฉ ๊ธฐ์กด ๊ตฌ์กฐ์ ํ๊ณ: ํ์ด์ง๋ค์ด์ ์์ ๋ฌดํ ์คํฌ๋กค๋ก
๊ธฐ์กด ๊ตฌ์ฑ์ ์ ๋ณด ํ์ด์ง๋ ํ์ด์ง๋ค์ด์ ๊ธฐ๋ฐ์ด์์ต๋๋ค. ์ฌ์ฉ์๋ 1ํ์ด์ง, 2ํ์ด์ง๋ฅผ ๋๊ธฐ๋ฉฐ ๋ฐ์ดํฐ๋ฅผ ๋ด์ผ ํ๊ณ , UX ์ธก๋ฉด์์ ๊ฝค ๋ถํธํ์ต๋๋ค. ์ด์ ๋ฐ๋ผ:
- โ ๋ ์์ฐ์ค๋ฝ๊ฒ ๋ฐ์ดํฐ๋ฅผ ํ์ํ ์ ์๋ ๋ฌดํ ์คํฌ๋กค๋ก ๋ณ๊ฒฝ
- โ ํ์ง๋ง ๋ฌดํ ์คํฌ๋กค๋ก ์ ํํ์ ๊ณง๋ฐ๋ก ๋ ๋๋ง ์ฑ๋ฅ ์ด์๊ฐ ๋ํ๋ฌ์ต๋๋ค
๋ฌธ์ ๋ "๋ ๋๋ง์ด ๋๋ฌด ๋ง๋ค"๋ ๊ฒ.
๋ฆฌ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ๋ ๋๋งํด๋ฒ๋ฆฌ๋ฉด ๋ธ๋ผ์ฐ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ํฐ์ง ์๋(?) ์์ฃ . ํนํ ๊ฐ ๊ตฌ์ฑ์๋ง๋ค ํ๋กํ ์ด๋ฏธ์ง(Avatar), ์์ด์ฝ, ์์ธ์ ๋ณด ๋ฑ์ด ๋ณต์กํ๊ฒ ์ฝํ ์๋ ํฐ๋ผ ์ฑ๋ฅ์ด ๊ธ๊ฒฉํ ๋จ์ด์ก์ต๋๋ค.
๐ ๊ฐ์ ๋ฆฌ์คํธ๋ฅผ ๋์ ํ ๊น? ๊ณ ๋ฏผ์ ์์
๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฅธ ๊ฑด ๋น์ฐํ ๊ฐ์ ๋ฆฌ์คํธ(Virtual List) ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ต๋๋ค.
react-window, react-virtualized, @tanstack/virtual ๋ฑ ํ๋ฅญํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ง์ฃ .
ํ์ง๋ง ํ์ค์ ์ธ์ ๋ ์ด์๊ณผ ๋ค๋ฆ ๋๋ค...
๊ฐ์ ๋ฆฌ์คํธ์ ๋์ ์ด ์ด๋ ค์ ๋ ์ด์
- ์ฐ๋ฆฌ ๋ฆฌ์คํธ๋ ๋จ์ํ ํ ์คํธ๊ฐ ์๋๋ผ ์ค์ฒฉ๋ ํ ์ด๋ธ ๊ตฌ์กฐ + ๋ ์ด์์์ด ๋ค์ด๋๋ฏน
- ์คํฌ๋กค ์ปจํ ์ด๋๊ฐ iframe ๊ตฌ์กฐ๋ฅผ ํ๊ณ ๋ค์ด๊ฐ ์์ด ์ง์ ์ ์ด๊ฐ ์ด๋ ค์
- ๊ธฐ์กด ์ฝ๋์ ๊ฐ์ ๋ฆฌ์คํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ถํฉ์ด ์ ๋ง์, ๋์ ๋์ด๋๊ฐ ๋์
→ "์ข์ ๊ธฐ์ ์ด์ด๋ ๋์ ๋น์ฉ์ด ๋๋ฌด ํฌ๋ค๋ฉด, ์ค๋ฌด์์ ์ ์ ํ์ง ์๋ค"
๐ฏ ๊ทธ๋์ ์ ํํ ๊ฒ์ IntersectionObserver ๊ธฐ๋ฐ Lazy Rendering
๋ณต์กํ ๊ตฌ์กฐ๋ฅผ ๊ฑด๋๋ฆฌ์ง ์์ผ๋ฉด์๋, ํ์ฌ ๋ณด์ด๋ ์์๋ง ๋ ๋๋งํ๊ณ ์ถ์๊ณ , ๊ทธ๋์ ๋ ์ค๋ฅธ ๊ฒ IntersectionObserver์์ต๋๋ค.
const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { setIsRendered(true); } }); }, { root: document.getElementById("main-content-box"), threshold: 0.1, });
ํต์ฌ์ ์ฌ๊ธฐ ๐
- root๋ฅผ null์ด ์๋ #main-content-box๋ก ์ง์
→ ์ค์ ์คํฌ๋กค์ด ๋ฐ์ํ๋ ์ง์ ๋ ์ปจํ ์ด๋ ๊ธฐ์ค์ผ๋ก ์ ํํ ๊ฐ์ง - threshold๋ฅผ ์กฐ์ ํด ์ผ๋ง๋ ๋ณด์ฌ์ผ "๋ ๋๋งํ์"๊ณ ํ๋จํ ์ง ์ ํจ
- isRendered ํ๋๊ทธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Avatar, Icon ๋ฑ์ ์กฐ๊ฑด๋ถ ๋ ๋๋ง
๋จ์ํ๋ฉด์๋ ์ ์ฐํ๊ฒ, ์ง์ง ๋ณด์ด๋ ๊ฒ๋ง ๋ ๋๋งํ๋ ๊ตฌ์กฐ ์์ฑ!
๐ก Lazy Rendering์ ์ฅ์ (vs Virtual List)
์ ์ฉ ๋์ด๋ | โ ๋ฎ์ (๊ธฐ์กด ์ฝ๋ ๊ฑฐ์ ์ ์ง) | โ ๋์ (๊ตฌ์กฐ ์ฌ์ ๋น ํ์) |
์ ์ฐ์ฑ | โ ์ปดํฌ๋ํธ๋ง๋ค ๊ฐ๋ณ ์ ์ด ๊ฐ๋ฅ | โ ๋จ์ผ ๋ฆฌ์คํธ๋ก๋ง ๊ตฌ์ฑ |
์ฑ๋ฅ ์ต์ ํ | โ ์ค์ง์ ๋ ๋๋ง ์ ๊ฐ์ | โ DOM ์ ์ ํ์ผ๋ก ์ต์ |
๋ถ์์ฉ | โ ์ํ ์ด๊ธฐํ ์ด์ ๊ฐ๋ฅ | โ ์คํฌ๋กค/๋ ์ด์์ ๊นจ์ง ์ ์์ |
โจ ๊ฒฐ๊ณผ์ ํจ๊ณผ
- ์ต์ด ๋ ๋๋ง DOM ์ ์ฝ 80% ๊ฐ์
- ๋ถํ์ํ ์ปดํฌ๋ํธ ๋ ๋๋ง ์ ๊ฑฐ
- ์ฌ์ฉ์ ์ฒด๊ฐ ์ฑ๋ฅ ํฅ์ (์คํฌ๋กค ๋ถ๋๋ฌ์ + ๋ก๋ฉ ์ง์ฐ ์์)
- Virtual Scroll ๋์ ์์ด๋ ํผํฌ๋จผ์ค ์ต์ ํ ๋ฌ์ฑ
๐งญ ๊ฒฐ๋ก : ์ ๋ต์ ์๋ค. “์ง๊ธ” ์ฐ๋ฆฌ์๊ฒ ๋ง๋ ํด๋ฒ์ด ์ค์
"๊ธฐ์ ์์ฒด๋ณด๋ค ์ค์ํ ๊ฑด ์ํฉ์ ๋ง๋ ํ๋จ๊ณผ ์คํ์ด๋ค"
๊ฐ์ ๋ฆฌ์คํธ๋ ํ๋ฅญํ ๊ธฐ์ ์ด์ง๋ง, ํ์ฌ์ ๊ตฌ์กฐ์ ์ฌ๊ฑด์์๋ ์คํ๋ ค ๋ ํฐ ๋ถ๋ด์ด ๋ ์ ์์ต๋๋ค. ์คํ๋ ค IntersectionObserver๋ ์์ง๋ง ์ ํํ ์ ํ์ด์์ต๋๋ค.
์ง๊ธ ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์์๋ "๋ ๋๋ง์ด ๋๋ฌด ๋ง์์ ๋๋ฆฌ๋ค"๋ ์ด์๊ฐ ์๋ค๋ฉด,
๊ผญ ๊ฑฐ์ฐฝํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋์
์ด ์๋์ด๋ IntersectionObserver ๊ธฐ๋ฐ Lazy Rendering์ ๊ณ ๋ คํด๋ณด์ธ์.
๊ฐ๊ฒฐํ ์ฝ๋, ์ฆ๊ฐ์ ์ธ ์ฑ๋ฅ ๊ฐ์ , ๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ์์ ํ๋ณต๊น์ง ๋ค์ผ๋ก ์ป์ ์ ์์ต๋๋ค. ๐