Frontend/Vue

[Vue] Lazy Loading ๋ฌดํ•œ์Šคํฌ๋กค ๊ตฌํ˜„ํ•˜๊ธฐ (Intersection Observer)

sol_git 2024. 12. 2. 15:00

 

๐ŸŽˆ Lazy Loading์„ ๊ตฌํ˜„ํ•ด๋ณธ ๋ฐฉ๋ฒ•

Vue ๊ธฐ๋ฐ˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ, Lazy Loading์„ ์ ์šฉํ•ด๋ณผ ๊ธฐํšŒ๊ฐ€ ์žˆ์—ˆ๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•ด๋ดค๋Š”๋ฐ, 

 

1. ๋ฐฑ์—”๋“œ์— ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•ด์„œ, ํ”„๋ก ํŠธ ๋‹จ์—์„œ Chunk๋กœ ์ž˜๋ผ์„œ ๋ณด์—ฌ์ฃผ๊ธฐ

2. ๋ฐฑ์—”๋“œ์— ๋ถ€๋ถ„์ ์œผ๋กœ ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•ด์™€์„œ ๋ณด์—ฌ์ฃผ๊ธฐ

 

๋ฐ์ดํ„ฐ๊ฐ€ 1000~2000๊ฐœ ์ •๋„์˜€์„ ๋•Œ๋Š” 1๋ฒˆ๋„ ๋ฌด๋ฆฌ ์—†์ด ๋™์ž‘ ํ–ˆ์—ˆ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ 4000~5000๊ฐœ ์ด์ƒ ๋„˜์–ด๊ฐ€๊ธฐ ์‹œ์ž‘ํ•˜๋‹ˆ๊นŒ,

๋ฐฑ์—”๋“œ์—์„œ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ๋„ ์˜ค๋ž˜๊ฑธ๋ฆฌ๊ณ  (ํŠนํžˆ ๋„คํŠธ์›Œํฌ๊ฐ€ ๋А๋ฆฐ ๊ณณ์—์„œ๋Š” ๋”๋”์šฑ),

ํ”„๋ก ํŠธ ๋‹จ์—์„œ ํ›„์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŽ์ด ์žก์•„๋จน๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‘๋ฒˆ์งธ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •!

 


๋‘๋ฒˆ์งธ ๋ฐฉ๋ฒ•์—๋Š” ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์ด ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ,

์ฒ˜์Œ์—๋Š” 'Scroll ์ด๋ฒคํŠธ'๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์„œ ์ „์ฒด ์ฐฝ ์‚ฌ์ด์ฆˆ์™€ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๋น„๊ตํ•ด์•ผํ•˜๋‚˜ ๊ณ ๋ฏผํ–ˆ์—ˆ๋‹ค.

๋ฌผ๋ก , ์ด ๋ฐฉ๋ฒ•์ด ๋ถˆ๊ฐ€๋Šฅํ•œ๊ฑด ์•„๋‹ˆ์ง€๋งŒ, scroll ์ด๋ฒคํŠธ๊ฐ€ ๋ฌด์ˆ˜ํžˆ ๋งŽ์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์— ๋ถ€ํ•˜๊ฐ€ ๊ฑธ๋ฆด ๊ฒƒ์€ ๋ถ„๋ช…ํ–ˆ๋‹ค.

scroll ์ด๋ฒคํŠธ์— `debounce` ๋‚˜ `throttle`์„ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์ƒ๊ฐํ•ด๋ดค์ง€๋งŒ, 

Intersection Observer๋ผ๋Š” API๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋๊ณ ,

์ด API๋ฅผ ์ ์šฉํ•˜๋ฉด `debounce`, `throttle` ์—†์ด๋„ ์„ฑ๋Šฅ์— ๋ฌด๋ฆฌ๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋˜์–ด ๋‚ด๊ฒ ๊ต‰์žฅํ•œ ํ˜์‹ ์ด์—ˆ๋‹ค.

 

โœจ Debounce 
์ด๋ฒคํŠธ๊ฐ€ ์ค‘๋ณต์œผ๋กœ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ, ์ตœ์ดˆ ํ˜น์€ ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ๋งŒ ์‹คํ–‰ํ•˜๋„๋ก ์ œ์–ดํ•˜๋Š” ์„ค์ •


โœจ Throttle
์ด๋ฒคํŠธ๋ฅผ ์ผ์ • ์‹œ๊ฐ„๋™์•ˆ ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก ์ œ์–ดํ•˜๋Š” ์„ค์ •

๐Ÿ’Ž Intersection Observer API

  • Intersection Observer API : ๊ต์ฐจ ๊ด€์ฐฐ์ž API
    • ๋‚ด๊ฐ€ ๊ด€์ฐฐ์ž๋กœ ์ง€์ •ํ•ด๋†“์€ ์š”์†Œ๊ฐ€ ์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜์—ˆ๋Š”์ง€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” API

๐ŸŽ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

1. Observer.vue ์ƒ์„ฑ

<template>
  <div ref="trigger"></div>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';

let observer = null;
let observerOptions = {
  root: null,
  rootMargin: '0px',  // ํŠธ๋ฆฌ๊ฑฐ Margin ์˜์—ญ
  threshold: 0.1,  // ํŠธ๋ฆฌ๊ฑฐ๊ฐ€ ํ™”๋ฉด์— ์–ผ๋งˆ๋‚˜ ๋ณด์˜€์„ ๋•Œ ๊ฐ์ง€ํ•  ๊ฒƒ์ธ์ง€ (0.1 = 10%)
};
const emits = defineEmits(['show', 'hidden']);
onMounted(() => {
  createObserver();  // Observer ๋“ฑ๋กํ•˜๋Š” ํ•จ์ˆ˜๋กœ ์—ฐ๊ฒฐ
});
onBeforeUnmount(() => {
  observer.disconnect();  // Observer ์—ฐ๊ฒฐ ํ•ด์ œ
});

const trigger = ref();
const createObserver = () => {
  observer = new IntersectionObserver(entries => {  // ๋ถ€๋ชจ๋กœ emits
    if (entries[0].isIntersecting) {
      emits('show');
    } else {
      emits('hidden');
    }
  }, observerOptions);

  observer.observe(trigger.value);
};
</script>

 

2. ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ณณ์—์„œ ์‚ฌ์šฉ

<template>
<div>
  <div class="lazy-loading-container">
    <div class="lazy-loading-data" v-for="(imageSrc, index) in rData.imageList" :key="index">
      <img :src="imageSrc" alt="" />
    </div>
    <Observer @show="loadMore" />
  </div>
</div>
</template>
<script setup>
import Observer from "@/components/Observer.vue";
import {reactive} from "vue";

const rData = reactive({imageList: [], page: 1});
const loadMore = async () => {
 const fetchData = await getAllImageList(rData.page + 1);
 if (fetchData) {
   rData.imageList = [...rData.imageList, ...fetchData];
   rData.page += 1;
 }
};
</script>

 

 


โœ” ์ฐธ๊ณ ํ•œ ์‚ฌ์ดํŠธ

 

https://velog.io/@rladpwl0512/lazy-loading์˜-๋ฌดํ•œ-์Šคํฌ๋กค์„-Intersection-Observer-API๋ฅผ-ํ†ตํ•ด-๊ตฌํ˜„ํ•˜๊ธฐ

https://velog.io/@godud2604/์ด๋ฏธ์ง€-์ตœ์ ํ™”-Lazy-Load-Intersection-Observer-API

https://samsara1019.tistory.com/77

https://velog.io/@jejualrock/๋ฌดํ•œ์Šคํฌ๋กค-ํด๋ผ์ด์–ธํŠธ

https://sso-feeling.tistory.com/684