티스토리 뷰

Frontend

Vue3, Nuxt3 - Image Lazy Loading

hjkang 2026. 2. 6. 12:47

Image Lazy Loading

웹 성능 최적화 할 때 이미지 레이지 로딩은 비용 대비 효과가 좋은 방법 중 하나

페이지 로드 시 모든 이미지를 한 번에 불러오지 않고, 사용자가 실제로 보게 될 때 필요한 이미지만 로드하는 기법

 

 

Vue3에서의 사용

Vue3에서는 브라우저의 네이티브 lazy loading 활용 가능

<!-- 기본 사용 -->
<template>
  <img 
    src="/images/photo.jpg"
    alt="Photo"
    loading="lazy"
  />
</template>

<!-- 반응형 이미지 -->
<template>
  <img
    src="/images/photo-400.jpg"
    srcset="
      /images/photo-400.jpg 400w,
      /images/photo-800.jpg 800w,
      /images/photo-1200.jpg 1200w
    "
    sizes="(max-width: 600px) 400px, 
           (max-width: 1200px) 800px, 
           1200px"
    loading="lazy"
    alt="Photo"
  />
</template>

<!-- WebP 지원 -->
<template>
  <picture>
    <source 
      srcset="/images/photo.webp" 
      type="image/webp"
    >
    <source 
      srcset="/images/photo.jpg" 
      type="image/jpeg"
    >
    <img 
      src="/images/photo.jpg" 
      alt="Photo"
      loading="lazy"
    >
  </picture>
</template>

 

 

Intersection Observer API 활용

- 더 세밀한 시점 제어가 필요한 경우 사용!!

 

Composable

export function useLazyImage(el, callback) {
  const observer = new IntersectionObserver(
    ([entry]) => {
      if (entry.isIntersecting) {
        callback()
        observer.disconnect()
      }
    },
    { rootMargin: '50px' }
  )

  observer.observe(el)
}
<template>
  <img
    ref="img"
    :src="loaded ? src : placeholder"
    alt="Photo"
    class="lazy-image"
  />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useLazyImage } from '@/composables/useLazyImage'

const img = ref(null)
const loaded = ref(false)

const src = '/images/photo.jpg'
const placeholder = '/images/placeholder.jpg'

onMounted(() => {
  useLazyImage(img.value, () => {
    loaded.value = true
  })
})
</script>

 

재사용 가능한 Component

<!-- components/LazyImage.vue -->
<template>
  <div ref="target" :style="{ height }">
    <img
      v-if="visible"
      :src="src"
      :alt="alt"
      class="lazy-image"
    />
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

defineProps({
  src: String,
  alt: String,
  height: {
    type: String,
    default: '400px',
  },
})

const target = ref(null)
const visible = ref(false)

let observer

onMounted(() => {
  observer = new IntersectionObserver(([entry]) => {
    if (entry.isIntersecting) {
      visible.value = true
      observer.disconnect()
    }
  })

  observer.observe(target.value)
})

onUnmounted(() => {
  observer?.disconnect()
})
</script>

<style scoped>
.lazy-image {
  width: 100%;
  display: block;
}
</style>
<template>
  <div class="gallery">
    <LazyImage 
      v-for="(image, index) in images" 
      :key="index"
      :src="image.url"
      :alt="image.alt"
    />
  </div>
</template>

<script setup>
const images = [
  { url: '/images/photo-1.jpg', alt: 'Photo 1' },
  { url: '/images/photo-2.jpg', alt: 'Photo 2' },
  { url: '/images/photo-3.jpg', alt: 'Photo 3' },
]
</script>

 

 

Nuxt3에서의 사용

Nuxt3에서 공식적으로 제공하는 이미지 최적화 모듈 @nuxt/image 설치

npm install @nuxt/image

 

설정

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  
  image: {
    quality: 80,
    formats: ['webp', 'jpeg'],
    screens: {
      xs: 320,
      sm: 640,
      md: 768,
      lg: 1024,
      xl: 1280,
    }
  }
})

 

기본 사용법

<template>
  <!-- 기본 사용 -->
  <NuxtImg 
    src="/images/photo.jpg" 
    alt="Photo"
    loading="lazy"
  />
  
  <!-- 반응형 이미지 -->
  <NuxtImg 
    src="/images/photo.jpg" 
    alt="Photo"
    loading="lazy"
    sizes="sm:100vw md:50vw lg:400px"
    format="webp"
    quality="80"
  />
  
  <!-- Placeholder 사용 -->
  <NuxtImg 
    src="/images/photo.jpg" 
    alt="Photo"
    loading="lazy"
    placeholder
  />
</template>

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함