티스토리 뷰
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>
'Frontend' 카테고리의 다른 글
| React - useMemo & useCallback (0) | 2026.02.11 |
|---|---|
| Vue3, Nuxt3 - Suspense (0) | 2026.02.06 |
| Vue3 - vue3-virtual-scroller (0) | 2026.02.03 |
| Nuxt3 - Google Tag Manager(GTM) 연동 (0) | 2026.01.20 |
| [iOS] Safari에서 전화번호 자동 링크 변환 문제 해결 (0) | 2025.01.17 |