데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      OpenLab 2기 - 웹 프론트엔드 성능 최적화 : 4번째 스터디 (3장 홈페이지 최적화 - 1편 : 이미지 지연 로딩)

      jang.se.yeong 24.10.27
      177 2 0
      DEVOTEE 요약
      OpenLab 2기 웹 프론트엔드 성능 최적화 스터디에서 이미지 지연 로딩에 대해 스터디했습니다. 스터디는 네트워크 분석, 최적화 방법론 소개, Intersection Observer API를 사용한 지연 로딩 구현, 그리고 최적화 전후 성능 비교로 구성되었습니다. 학습 결과, 성능 최적화를 이해하고 적용하는 것이 중요하다는 생각을 했으며, 다음 스터디에서도 더 다양한 기법을 배울 수 있어 기대된다고 전했습니다.
      DEVOTEE 추천 블로그

      안녕하세요, OpenLab 2기 웹 프론트엔드 성능 최적화 스터디에 참여중인 장세영입니다 😊


      웹 프론트엔드 성능 최적화 스터디에서는 [프론트엔드 성능 최적화 가이드] 도서를 기반으로 챕터 별 발표 형식으로 스터디를 진행하고 있습니다.


      저는 네번째 스터디에서 [3장 홈페이지 최적화]의 이미지 지연 로딩 파트를 담당하여 발표를 진행했습니다.

      발표한 내용을 바탕으로 글을 정리해보고자 합니다.


      다음은 목차입니다.

      1. Network Custom throttling

      2. 최적화할 페이지 구조 및 분석

      3. 이미지 지연 로딩 원리

      4. Intersection Observer API

      5. Intersection Observer로 이미지 지연 로딩 구현하기

      6. 최적화 전/후 성능 및 지표 비교하기

      7. 느낀점



      1. Network Custom throttling

      서비스를 분석하고 최적화 해야할 때에는 어떤 작업을 먼저 해야 한다는 규칙은 없습니다.

      상황에 따라 판단하여 원하는 분석을 진행하면 되는데, 여기서는 네트워크를 먼저 살펴보겠습니다.

      image.png

      브라우저 개발자 도구의 네트워크 탭에서는 네트워크에 throttling을 적용하여 네트워크의 명확한 흐름을 파악할 수 있으며 원하는 속도로 커스텀 설정이 가능합니다.

      저는 3G 설정보다는 빠른 6000kb/s로 Custom throttling 옵션을 추가하여 분석을 진행했습니다.


      2. 최적화할 페이지 구조 및 분석

      image.png

      저희가 최적화할 페이지는 첫 페이지 접속 시 보이는 동영상 배너와 스크롤을 내릴 경우 보이는 이미지들로 구성되어 있습니다.

      1.gif

      Network throttling 설정 후 페이지를 로드해보면, 사용자에게 가장 먼저 보여지는 동영상의 로드가 늦어져서 매끄럽지 못한게 보입니다.

      image.png

      네트워크 탭에서 컨텐츠의 다운로드 순서를 확인해보니, 하단에 표시되는 이미지들이 먼저 다운로드되고 동영상은 팬딩 상태(흰색 바)였다가 다운로드가 늦게 진행되는걸 확인할 수 있습니다.

      현재 비디오는 가장 처음으로 사용자에게 보여지는 콘텐츠인데, 이미지/폰트가 로드된 후에 로드가 되어 사용자 경험에 좋지 못한 상황입니다.


      3. 이미지 지연 로딩 원리

      위의 문제를 해결하기 위해서 당장 사용되지 않는 이미지들을 나중에 다운로드하는 이미지 지연 로딩을 적용하여 동영상이 먼저 다운로드될 수 있도록 개선해보겠습니다.

      image.png

      이미지 지연 로딩은 아직 스크롤이 이미지 위치까지 도달하지 않았을 때는 이미지를 로드하지 않다가, 이미지 위치에 도달하면 그때 이미지를 로드하는 방식으로 적용할 수 있습니다.

      2.gif

      여기서 문제점은 스크롤 이동 시 해당 뷰포트에 이미지를 표시할지 판단하는 로직이 필요한데, 이를 스크롤 이벤트에 직접 연결하면 스크롤할 때마다 이벤트가 발생하여 브라우저의 메인 스레드에 부담을 주게 됩니다.


      위의 간단한 예시를 통해서도 잠깐의 스크롤로 수십번의 이벤트가 발생하는 것을 확인할 수 있습니다.

      (물론 lodash의 throttle 같은 방식으로 이벤트 발생 빈도를 줄일 수는 있지만, 이는 근본적인 해결책이 될 순 없습니다.)


      4. Intersection Observer API

      다행히 이런 스크롤 문제를 Intersection Observer API를 통해 쉽게 처리할 수 있습니다!

      (단, Intersection Observer는 IE에서 지원되지 않으므로 IE 지원이 필요한 경우 polyfill 라이브러리를 설치해야 합니다.)


      Intersection Observer는 웹 페이지에서 특정 요소가 뷰포트나 지정한 영역과 교차하는 것을 감지하는 API입니다.

      예를 들어 스크롤할 때 어떤 요소가 화면에 얼마나 보이는지를 알려줍니다.


      scroll 이벤트는 스크롤할 때마다 계속 실행되어 성능이 저하되지만, Intersection Observer는 교차가 발생할 때만 동작하므로 훨씬 효율적입니다.

      image.png

      Intersection Observer를 활용하기 위해 options, callback, observer 객체에 대해 알아보겠습니다.

      [options]

      const options = {
          root: null,
          rootMargin: "0px",
          thresold: 1.0,
      }
      • root: 관찰할 영역을 지정하는 요소입니다. 따로 지정하지 않으면 브라우저의 뷰포트가 기본값이 됩니다. (default: 브라우저의 뷰포트)

      • rootMargin: root 요소의 바깥 여백을 설정합니다. CSS 마진처럼 '10px 20px 30px 40px' 형태로 위/오른쪽/아래/왼쪽 값을 지정할 수 있습니다. 이 여백으로 교차 영역을 더 크게 혹은 작게 만들 수 있습니다. (default: 0)

      • threshold: 콜백 함수를 실행할 시점을 설정합니다. 예를 들어 0.5로 설정하면 관찰 대상이 50% 보일 때 콜백이 실행됩니다. 0부터 1까지의 숫자나 숫자 배열로 지정할 수 있습니다. (default: 0)

      image.png

      예를 들어 위 이미지처럼 threshold: [0.5]로 설정하면, 관찰 대상이 뷰포트에 50% 정도 들어왔을 때 콜백 함수가 실행됩니다. 이를 통해 요소가 화면에 얼마나 보일 때 동작할지 세밀하게 제어할 수 있습니다.

      [callback]

      callback 함수는 페이지 로드 시점에 최초로 실행되며, 이후 관찰 대상이 지정한 임계값(threshold)을 만족할 때마다 실행됩니다.

      const callback = (entries, observer) => {
        entries.forEach((entry) => {
          // 각 엔트리는 관찰된 하나의 교차 변화를 표시
          // 대상 요소의 경계 사각형 정보:
          // entry.boundingClientRect
          // entry.intersectionRatio   // 교차 비율
          // entry.intersectionRect    // 교차 영역 정보
          // entry.isIntersecting      // 교차 여부
          // entry.rootBounds          // root 요소의 경계 정보
          // entry.target              // 관찰 대상 요소
          // entry.time                // 교차 변경이 발생한 시간
        });
      };

      콜백함수 매개변수

      entries: 교차 정보를 담은 IntersectionObserverEntry 객체들, isIntersecting 값을 통해 타겟 요소가 뷰포트에 들어왔는지에 대한 여부 판단할 수 있습니다.

      observer: 현재 관찰자 인스턴스


      주의점

      1. callback은 메인 스레드에서 실행되기 때문에, 가능한 빠르게 처리되어야 합니다.

      2. 시간이 많이 필요한 작업은 requestIdleCallback*을 사용하여 브라우저 유휴 시간에 처리되도록 해야합니다.

        *브라우저의 유휴 시간을 활용하여 우선순위가 낮은 작업을 비동기적으로 실행하는 웹 API

      [observer]

      const observer = new IntersectionObserver(callback, options);

      IntersectionObserver를 통해 생성한 observer 객체에서는 다음과 같은 메소드를 사용할 수 있습니다.

      1. observe(target) : 지정된 대상 요소에 대한 관찰을 시작합니다.

      2. unobserve(target) : 지정한 대상 요소에 대한 관찰을 중지합니다.

      3. disconnect() : 관찰자를 중지하고 모든 대상에 대한 관찰을 종료합니다.

      4. takeRecords() : 아직 처리되지 않은 모든 Intersection 변경 기록을 반환하고 큐를 비웁니다.

      이제 Intersection Observer를 활용하여 이미지가 화면에 보이는 순간에 로드되는 지연 로딩을 구현해보겠습니다.


      5. Intersection Observer로 이미지 지연 로딩 구현하기

      image.png

      이미지 지연 로딩이 필요했던 Card 컴포넌트 입니다.

      Card 컴포넌트의 img 태그를 관찰 대상으로 지정하고, 이미지가 화면에 나타나는 순간에 실제 이미지를 로드하도록 만들어보겠습니다.

      import React, { useEffect, useRef } from "react";
      
      function Card(props) {
       const imgRef = useRef();
       
       useEffect(() => {
         const options = {};
         const callback = (entries, observer) => {
           entries.forEach((entry) => {
             if (entry.isIntersecting) {
               console.log("is intersecting", entry.target.dataset.src);
               entry.target.src = entry.target.dataset.src;
               observer.unobserve(entry.target);
             }
           });
         };
         
         const observer = new IntersectionObserver(callback, options);
         observer.observe(imgRef.current);
         return () => observer.disconnect();
       }, []);
      
       return (
         <div className="Card text-center">
           <img ref={imgRef} data-src={props.image} alt="메인이미지" />
           <div className="p-5 font-semibold text-gray-700 text-xl md:text-lg lg:text-xl keep-all">
             {props.children}
           </div>
         </div>
       );
      }
      
      export default Card;

      수정된 코드에서는 이미지가 화면에 보이면(isIntersecting이 true) data-src의 값을 src로 옮겨서 이미지를 로드합니다.

      img 태그는 src 속성에 값이 있을 때만 실제로 이미지를 로드하기 때문입니다.


      또 중요한 점으로는, Intersection Observer 인스턴스는 useEffect 내부에서 생성해야 합니다. 그 이유는 다음과 같습니다.

      1. useEffect를 사용하지 않으면 컴포넌트가 렌더링될 때마다 인스턴스가 새로 생성되어 같은 요소에 여러 콜백이 중복 실행될 수 있습니다.

      2. 리소스 관리를 위해 useEffect의 정리(clean-up) 함수에서 observer.disconnect를 호출해야 합니다.

      결과

      4.gif


      네트워크 탭을 통해 확인해보면, 스크롤하여 이미지가 화면에 보이는 순간에 이미지 로드가 시작되는 것을 확인할 수 있습니다.

      6. 최적화 전/후 성능 및 지표 비교하기

      5.gif

      Card 컴포넌트에서 사용한 이미지 지연 로딩을 공통 컴포넌트로 만들어 다른 이미지들에도 적용해보았습니다.

      그 결과, network throttling을 설정했음에도 이전과 달리 비디오가 빠르게 로드되는 것을 확인할 수 있습니다.

      image.png

      네트워크 탭을 보면 비디오가 pending 상태(흰색 바) 없이 바로 다운로드되며 이미지보다 먼저 다운로드가 된 것을 볼 수 있습니다.

      image.png

      lighthouse 분석 지표를 보면 이미지 지연 로딩 적용으로 메인 페이지의 중요 콘텐츠인 비디오가 더 빨리 로드되어 성능(Performance)이 개선되었습니다.

      그리고 페이지의 최대 콘텐츠풀 페인트(LCP) 요소인 비디오의 로딩이 빨라져 측정 시간도 감소된 것을 확인할 수 있습니다.


      7. 느낀점

      프론트엔드 성능 최적화 스터디를 통해 다양한 최적화 기법들을 배우면서, 실습과 발표를 통해 기존에 알고 있던 내용들을 더욱 깊이 있게 이해하게 되었습니다.

      예를 들어 평소 사용하던 Next.js의 경우, 단순히 '이미지나 폰트를 최적화해준다'는 것을 넘어서서 구체적인 최적화 메커니즘과 그 필요성까지 파악할 수 있었습니다.

      프레임워크가 제공하는 최적화 기능을 단순히 '아, 이런 게 있구나' 수준으로 아는 것과, 그것이 왜 필요하고 어떤 문제를 해결하는지 이해하는 것은 분명한 차이가 있다고 생각합니다.

      스터디를 하며 성능 최적화에 대한 다양한 관점과 접근을 통해, '아는 만큼 보인다'는 진리를 다시 한번 체감할 수 있었던 값진 시간이었습니다.

      앞으로 남은 스터디 기간 동안 더 다양한 최적화 기법들을 배우고 적용해볼 수 있다는 점이 기대됩니다.


      감사합니다 😀

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      jang.se.yeong 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기