웹 성능 최적화 - Lazy Loading

Mayton·2022년 12월 22일
0

ComputerScience

목록 보기
2/7
post-thumbnail

웹 성능 최적화란 웹 개발자가 최소한의 데이터로 가장 빠른 시간에 사용자가 불편함을 느끼지 않는 최적의 화면을 띄우는 것이라고 할 수 있다. 성능 최적화를 통해 사람들이 많이 찾는 서비스가 될 수 있다. 단순히 사용자 경험이 뛰어나서 뿐만아니라 구글 SEO 기본 가이드 내에도 HTML 모바일 최적화, 사이트를 모바일 친화적으로 만들기 등을 확인할 수 있어 최적화 된 웹페이지는 구글에서 검색순위도 상위에 위치하게 된다.

⭐️ 브라우저 렌더링

프론트엔드 개발자라면 브라우저 렌더링 과정은 기본적으로 알고 있겠지만, 다시한번 복습해보자, reflow와 repaint 과정이 최대한 덜 일어나도록 CSS를 구성해야, 웹 성능을 최적화 시킬수 있는데 왜 언제 reflow가 일어나는지 알아야 덜 일어나도록 구성할 수 있을 것이다. 또한 지금 살펴볼 Lazy Loading 또한 reflow가 일어나지 않도록 해서 웹 성능 최적화가 가능한 방법 중 하나이다.

👉🏻 브라우저 렌더링 동작과정

Parsing

HTML파일과 CSS파일을 파싱해서 각각 Tree를 만든다.

윗 사진의 DOM Tree와 CSSDOM TREE 구성 작업을 진행한다.

Styling

두 Tree를 결합하여 Rendering Tree를 만든다.

Render Tree를 구성한다. Render Tree는 실제 화면에 그려질 Tree를 말한다. 예를들어 visibility: hidden은 Render Tree에 포함이 되지만 display:none은 Render Tree에서 제외된다.

Layout

Rendering Tree에서 각 노드의 위치와 크기를 계산한다.

각 노드의 정확한 위치와 크기를 계산하고 RenderTree에 반영한다. 특히 %값으로 설정이 되어있다면 픽셀단위로 변환하게 된다.

여기서 어떠한 액션이나 이벤트에 의해 DOM요소의 크기나 위치 등을 변경하면 해당 노드의 하위노드와 상위의 노드를 포함하여 Layout 단계를 다시 수행하며, 변경하려는 특정요소의 위치와 크기, 연관된 요소들의 위치와 크기도 재계산을 한다.

이 때 정확한 크기와 치수가 없는 이미지들은 이미지를 불러온 이후 reflow를 발생시켜 퍼포먼스를 저하시킨다. 따라서 이미지 및 비디오 요소에 width, height 속성을 포함하거나 aspect ratio를 잡는다. 이를 통해 이미지가 로드되는 동안 문서의 공간을 올바르게 할당할 수 있다.

  • reflow를 발생시키는 속성
position / width / height / margin / padding / display / top / left / right / bottom / 
box-sizing / border-color / text-align / border / border-width / 
font-family / float / font-size / font-weight / line-height / vertical-align / 
white-space / word-wrap / text-overflow / text-shadow ...

Paint

계산된 값을 이용하여 각 노드의 화면상의 실제 픽셀로 변환하고, 레이어를 만든다.

Layout 단계에서 계산한 값을 실제 픽셀로 변환하고, 여러개의 레이어를 통해 관리한다.

  • repaint를 발생시키는 속성
color / border-style / visibility / background / background-color / 
background-image / background-position / background-repeat / background-size / 
text-decoration / outline / outline-style / outline-color / outline-width / 
border-radius / box-shadow ...
  • reflowrepaint를 발생시키지 않는 속성
opacity / transform / cursor / z-index ...

Composite

레이어를 합성하여 실제 화면에 나타낸다.

Paint 단계에서 생성된 레이어를 합성하여 실제 화면에 나타낸다.

⭐️ NEXTJS Image Component란?

Next JS 프레임워크에서는 기본 img 태그가 아닌 Image Component를 사용해서 이미지를 구현한다. 이 Image component만을 사용함으로 인해서 다른 프레임워크를 사용할 시에는 추가적으로 해주어야하는 여러가지 성능 최적화 요소들을 자동으로 구현할 수 있다.

👉🏻 Image Component의 장점

  • 이미지 크기에 맞는 Scaling을 자동으로 해주어, 최적의 크기로 웹에 전송한다.

  • 이미지 캐싱 및 expiration time 설정 가능.

  • Loader을 통해서 url을 커스터마이징할 수 있습니다.
    loader옵션을 통해 원하는 클라우드 이미지 로더를 사용할 수 있다

  • Lazy Loading을 통해 이미지 최적화를 지원합니다.
    Image Component를 사용하면 기본적으로 Lazy Loading을 지원한다

  • Skeleton UI를 기본적으로 지원하여 CLS 방지 가능.
    next/image 에서는 이미지를 로딩하는 중의 blur 처리된 화면을 보여주는 옵션 또한 포함하고 있다.

  • next.config.js를 통해 지정된 곳에서만 이미지를 받아오며 악의적인 유저로부터 앱을 보호

(공식문서에는 크게 아래 네가지 최적화 구성요소로 기술되어 있다.Improved Performance, Visual Stability, FasterPage Loads, Asset Flexibility)

👉🏻 Image Component 단점

기본적인 html이 아니라 component라는 점에서 단점을 가지고 있습니다. styledComponent, emotion등을 사용해서 스타일링을 할때, next/image에 기본적으로 지정된 스타일이 있어 원하는 방향으로 스타일링이 안될 때가 종종 있다.

⭐️ React에서의 이미지 최적화

👉🏻 Lazy Loading

Browser-level

모던 브라우저의 경우 img 태그 내의 속성을 통해 lazy-loading을 지원한다. 모던 브라우저가 아닌 경우, 해당 속성은 무시된다.

다음과 같이 img 태그 내의 loading 속성 값을 부여하면 된다.

<img loading="lazy">
<picture>
  <source media="(min-width: 800px)" srcset="large.jpg 1x, larger.jpg 2x">
  <img src="photo.jpg" loading="lazy">
</picture>

loading의 속성 값은 다음 3가지와 같다.

auto : 디폴트 값으로, 속성값을 지정하지 않은 것과 동일하다.

lazy : 뷰포트 상에서 해당 이미지의 위치를 계산하여 이미지 자원을 요청한다.

eager : 어느 위치에 있든지 이미지 자원을 바로 요청받는다.

해당 속성을 사용할 경우 되도록 해당 이미지 영역의 크기를 지정하는 것이 권장된며, background-image에서는 적용 안 된뿐 아니라 아직 지원하지 않는 브라우저들도 여럿 존재한다.

Intersection Observer

intersection Observer는 2016년 4월에 구글 개발자 페이지를 통해 소개된 방법이다. 이는 scroll이벤트와 getBoundingClientRect의 문제점으로 인해 만들어졌다.

scroll 이벤트는 스크롤 시에 짧은 시간안에 무수한 이벤트들이 동기적으로 실행되고, 그 스크롤 이벤트를 리스닝하는 무수한 콜백들이 대기하고 실행될 수 있어 메인 스레드에 큰 부하를 줄 수 있다.

getBoundingClientRect를 호출 시에는 값을 정확히 읽어들이기위해 큐를 flush하고 스타일을 적용해 다수의 reflow를 발생시킬 가능성이 있습니다. 이는 본래 브라우저가 최적화를 위해 여러 작업을 큐에 쌓아 대기하다가 한번에 reflow하는 방식을 완전히 깨버린다.

Intersection Observer는 루트요소와 target과의 교차여부를 관찰합니다. 이 때 비동기적으로 실행되며, reflow를 발생시키지 않아 성능상 유리하다.

  • 기본적인 사용방법
let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

observer 객체의 observe 객체를 통해 target을 구독하게 된다
react 에서는 useRef를 사용해주면 된다

let target = document.querySelector('#listItem');
observer.observe(target);

// the callback we setup for the observer will be executed now for the first time
// it waits until we assign a target to our observer (even if the target is currently not visible)

콜백 네이서는 entries와 observer props를 가지며, entry를 통해 각 target에 대한 설정을 할 수 있으며, observer를 통해 target의 구독해제 등을 할 수 있다.

let callback = (entries, observer) => {
  entries.forEach((entry) => {
    // Each entry describes an intersection change for one observed
    // target element:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
  });
};

한계

일부 다른 블로그들을 살펴보던 중 무조건적으로 intersection Observer가 좋은 것이 아니라, scroll방식과 선택이 있어야 한다는 의견들도 있었다.

예를 들어 광고 요금 청구를 위한 통계를 적절히 쌓기위해서는 한계가 있다는 것이다. 고속으로 빠르게 스크롤 했는데, 과연 그 광고요금들이 청구되는 것이 맞는 것인가? 혹은 스크롤 되었기때문에 정보를 받아오는 것이 맞는 것인가에 대한 이야기로 상당히 흥미로웠다. 특히, 역시 모든 새로운 기술들이 맞는 것이 아니라 내가 알아보고 동작원리를 확인한 뒤에 취사선택하여 사용해야함을 알게 되었다.

아래는 해당내용에대한 블로그 이다.
-실무에서 느낀 점을 곁들인 Intersection Observer API 정리

👉🏻 Image scaling

image scaling , video scaling의 개념은 아주 단순하다. 300*300의 이미지에는 dslr로 찍거나 용량이 아주 큰 파일이 필요가 없다는 것이다. 그래서 300*300의 파일이라면 적어도 600*600의 크기로 image의 크기를 조절하여, image를 다운받는데 걸리는 시간을 줄여주는 것을 image scaling이라고 하며, 브라우저 로딩 시간내에 단순하지만 큰 비중을 차지한다

참고자료

profile
개발 취준생

0개의 댓글