본문 바로가기
FrontEnd

SEO 성능 개선을 위한 Lazy Load 및 이미지 사전 블러 렌더링 구현

by E_van 2024. 5. 22.

SEO 성능과 사용자 경험(UX)을 개선하기 위해 이미지를 Lazy Load 방식으로 로드하고, 렌더링 중 블러 처리된 작은 이미지를 표시하며, 원본 이미지는 뷰포트에 들어오면 로드하도록 구현했습니다. 이 글에서는 해당 기능의 구현 방법과 주요 코드 로직을 설명합니다.


주요 기능 설명

  1. Lazy Load:
    • 이미지를 뷰포트(viewport) 영역 내에 들어왔을 때만 로드하여 불필요한 네트워크 요청을 줄입니다.
    • IntersectionObserver를 활용하여 효율적으로 뷰포트 내 이미지 감지.
  2. 블러 처리된 사전 렌더링:
    • 원본 이미지를 로드하기 전에 블러 처리된 50x50 작은 이미지를 보여줍니다.
    • 원본 이미지를 로드한 후 블러를 제거하여 자연스러운 전환.
  3. 반응형 및 디바이스 최적화:
    • 디바이스 유형(모바일, 데스크톱)에 따라 이미지 크기를 조정.
    • 이미지가 작은 경우 원본 크기에 맞게 스타일을 설정.
  4. 예외 처리:
    • 이미지 로드 실패 시 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');

최적화 포인트

  1. SEO 개선:
    • 초기 렌더링 시 블러 이미지를 사용해 콘텐츠 로딩 시간을 단축.
    • Lazy Load를 통해 초기 네트워크 요청을 최소화하여 페이지 속도 향상.
  2. UX 개선:
    • 뷰포트에 들어온 이미지만 로드하여 사용자에게 필요한 정보만 제공.
    • 블러 이미지를 통해 빈 화면이 표시되지 않도록 함.
  3. 반응형 처리:
    • 모바일 및 데스크톱 환경에서 적절한 이미지 크기를 자동으로 설정.
  4. 에러 처리:
    • 이미지가 로드되지 않는 경우를 대비한 기본 이미지 대체 처리.

결론

위 코드와 기능을 통해 SEO 성능을 개선하고 사용자 경험을 최적화할 수 있습니다. 특히, 대규모 이미지가 포함된 웹사이트에서 Lazy Load와 사전 블러 렌더링은 필수적인 성능 개선 전략으로 활용될 수 있습니다.