SEO 성능과 사용자 경험(UX)을 개선하기 위해 이미지를 Lazy Load 방식으로 로드하고, 렌더링 중 블러 처리된 작은 이미지를 표시하며, 원본 이미지는 뷰포트에 들어오면 로드하도록 구현했습니다. 이 글에서는 해당 기능의 구현 방법과 주요 코드 로직을 설명합니다.
주요 기능 설명
- Lazy Load:
- 이미지를 뷰포트(viewport) 영역 내에 들어왔을 때만 로드하여 불필요한 네트워크 요청을 줄입니다.
- IntersectionObserver를 활용하여 효율적으로 뷰포트 내 이미지 감지.
- 블러 처리된 사전 렌더링:
- 원본 이미지를 로드하기 전에 블러 처리된 50x50 작은 이미지를 보여줍니다.
- 원본 이미지를 로드한 후 블러를 제거하여 자연스러운 전환.
- 반응형 및 디바이스 최적화:
- 디바이스 유형(모바일, 데스크톱)에 따라 이미지 크기를 조정.
- 이미지가 작은 경우 원본 크기에 맞게 스타일을 설정.
- 예외 처리:
- 이미지 로드 실패 시 onError를 통해 기본 이미지를 대체 표시.
코드 구현
1. Lazy Load 기능 구현
Lazy Load를 위한 유틸리티 함수를 작성하여 모든 이미지에 대해 동작하도록 합니다.
import isMobileDevice from './deviceCheck';
interface LazyLoadReturn {
changeImage: () => void;
}
const lazyLoad = (selector: string, attribute: string): LazyLoadReturn => {
const handleIntersection = (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const element = entry.target as HTMLImageElement;
const attributeValue = element.getAttribute(attribute);
if (attributeValue !== null) {
element.setAttribute('src', attributeValue); // 원본 이미지 로드
element.removeAttribute(attribute);
element.onload = () => {
// 블러 제거 및 스타일 적용
element.style.filter = 'blur(0px)';
if (element.closest('.photo')) {
if (isMobileDevice()) {
element.style.maxWidth = `${element.naturalWidth}px`;
element.style.maxHeight = `${element.naturalHeight}px`;
element.style.width = '100%';
element.style.height = 'auto';
} else {
if (element.naturalWidth < 816) {
element.style.maxWidth = `${element.naturalWidth}px`;
element.style.maxHeight = `${element.naturalHeight}px`;
element.style.width = '100%';
element.style.height = 'auto';
} else {
element.style.maxWidth = '100%';
element.style.width = '100%';
element.style.height = 'auto';
}
}
}
};
}
// 이미지 로드 실패 시 기본 이미지 처리
element.onerror = function () {
this.onerror = null;
this.src = `${env.url}/static/default_thumb_img.png`;
};
observer.unobserve(element); // 관찰 해제
}
});
};
// IntersectionObserver 설정
const observer = new IntersectionObserver(handleIntersection, {
root: null,
rootMargin: '0px',
threshold: 0.1, // 10% 노출 시 로드
});
// 이미지 관찰 시작
const observeImages = () => {
const elements = document.querySelectorAll(selector);
elements.forEach(element => observer.observe(element));
};
observeImages();
return {
changeImage: observeImages,
};
};
export default lazyLoad;
2. 주요 로직 설명
2-1. IntersectionObserver 활용
IntersectionObserver는 이미지가 뷰포트에 들어왔는지 감지합니다. 감지 조건(threshold)을 조정하여 로드 시점을 미세 조정할 수 있습니다.
const observer = new IntersectionObserver(handleIntersection, {
root: null,
rootMargin: '0px',
threshold: 0.1, // 10% 노출 시 로드
});
2-2. 원본 이미지 로드 및 블러 제거
이미지가 로드되면 onload 이벤트에서 블러 스타일을 제거하고, 반응형 크기를 설정합니다.
element.onload = () => {
element.style.filter = 'blur(0px)'; // 블러 제거
// 디바이스 및 크기 조정
if (isMobileDevice()) {
element.style.width = '100%';
element.style.height = 'auto';
}
};
2-3. 기본 이미지 처리
이미지 로드 실패 시 onError를 활용하여 기본 이미지를 표시합니다.
element.onerror = function () {
this.onerror = null; // 무한 호출 방지
this.src = `${env.url}/static/default_thumb_img.png`;
};
3. 사용 방법
3-1. HTML 구조
Lazy Load를 적용할 이미지의 src 대신 data-src에 원본 이미지를 지정합니다. 초기 로딩 시 50x50 블러 이미지를 src로 설정합니다.
<img
class="photo"
src="small-blur-image.jpg"
data-src="original-image.jpg"
alt="Example Image"
/>
3-2. Lazy Load 호출
lazyLoad 함수를 호출하여 해당 이미지에 Lazy Load 기능을 적용합니다.
import lazyLoad from './lazyLoad';
lazyLoad('.photo', 'data-src');
최적화 포인트
- SEO 개선:
- 초기 렌더링 시 블러 이미지를 사용해 콘텐츠 로딩 시간을 단축.
- Lazy Load를 통해 초기 네트워크 요청을 최소화하여 페이지 속도 향상.
- UX 개선:
- 뷰포트에 들어온 이미지만 로드하여 사용자에게 필요한 정보만 제공.
- 블러 이미지를 통해 빈 화면이 표시되지 않도록 함.
- 반응형 처리:
- 모바일 및 데스크톱 환경에서 적절한 이미지 크기를 자동으로 설정.
- 에러 처리:
- 이미지가 로드되지 않는 경우를 대비한 기본 이미지 대체 처리.
결론
위 코드와 기능을 통해 SEO 성능을 개선하고 사용자 경험을 최적화할 수 있습니다. 특히, 대규모 이미지가 포함된 웹사이트에서 Lazy Load와 사전 블러 렌더링은 필수적인 성능 개선 전략으로 활용될 수 있습니다.