[북리뷰] 프론트엔드 성능 최적화 가이드 정리(#1)

하수도키

·

2023. 2. 9. 14:53

728x90
반응형
SMALL

크롬 LightHouse

Chrome LightHouse

Mode와 Categories 항목 값 소개

Mode

  • Navigation: Lighthouse의 기본값으로, 초기 페이지 로딩 시 발생하는 성능 문제를 분석
  • Timespan: 사용자가 정의한 시간 동안 발생한 성능 문제를 분석
  • Snapshot: 현재 상태의 성능 문제를 분석

Categories

  • Perfomance: 웹 페이지의 로딩 과정에서 발생하는 성능 문제를 분석
  • Accessibility: 서비스의 사용자 접근성 문제를 ㅂ누석
  • Best Practices: 웹사이트의 보안 측면과 웹 개발의 최신 표준에 중점을 두고 분석
  • SEO: 검색 엔진에서 얼마나 잘 크롤링 되고 검색 결과에 표시되는 분석
  • Progressive Web App: 서비스 워커와 오프라인 동작 등, PWA와 관련된 문제를 분석

퍼포먼스 80 점수는

Lighthouse가 측정한 이 웹 페이지의 종합 성능 점수

여섯 가지 지표(metrics)에 가중치를 적용해 평균 낸 점수

이러한 지표를 웹 바이탈(Web Vitals)라고 부른다.

 

 

First Contentful Paint(FCP)

FCP는 페이지가 로드될 때 브라우저가 DOM 콘텐츠의 첫 번째 부분을 렌더링 하는데 걸리는 시간에 관한 지표

FCP는 총점을 계산할때, 10%의 가중치를 갖는다.

 

Speed Index(SI)

SI는 페이지 로드 중에 콘텐츠가 시각적으로 표시되는 속도를 나타내는 지표입니다.

A

1초부터 1개씩 TOP부터 보여지면서 총 4초에 걸쳐 나타난다

TOP

MIDDLE-1

MIDDLE-2

BOTTOM

B

3초까지 TOP만 보이고 4초에 다 나타난다.

TOP

MIDDLE-1

MIDDLE-2

BOTTOM

이럴땐, A가 더 높은 점수를 받는다.

 

Largest Contentful Patin(LCP)

LCP는 페이지가 로드될 때 화면 내에 있는 가장 큰 이미지나 텍스트 요소가 렌더링되기까지 걸리는 시간을 나타내는 지표

LCP는 총점을 계산할때, 25%의 가중치를 갖는다.

 

Time to Interractive(TTI)

TTI는 사용자가 페이지와 상호 작용이 가능한 시점까지 걸리는 시간을 측정한 지표

여기서 상호 작용이란 클릭 또는 키보드 누름 같은 사용자 입력을 의미

TTI는 총점을 계산할 때, 10%의 가중치를 갖는다.

 

Total Blocking Time(TBT)

TBT는 페이지가 클릭, 키보드 입력 등의 사용자 입력에 응답하지 않도록 차단된 시간을 총합한 지표

측정은 FCP와 TTI사이의 시간 동안 일어나며 메인 스레드를 독점하여 다른 동작을 방해나는 작업에 걸린 시간을 총합합니다.

TBT는 총점을 계산할 떄, 30%의 가중치를 갖습니다.

 

Cumulative Layout Shift(CLS)

CLS는 페이지 로드 과정에서 발생하는 예기치 못한 레이아웃 이동을 측정한 지표

CLS는 총점을 계산할때, 15%의 가중치를 갖는다.

웹 페이지의 문제점과 해별 방안, 그리고 문제를 해결함으로써 얻을 수 있는 이점이 무엇인지 보여준다.

 

Opportunites

페이지를 더욱 빨리 로드하는 데 잠재적으로 도움되는 제안을 나열

Diagnostics

로드 속도와 직접적인 관계는 없지만 성능과 관련된 기타 정보를 보여준다.

컴포넌트 코드 분할

import { add } from  './math'
console.log('1 + 4 = ', add(1, 4))
  • 위와 같은 코드 모듈은 빌드 시에 함께 번들링 된다.
import('add').then(() => {
	const { add } = module

	console.log('1 + 4 =', add(1,4))
})
  • webpack은 이 동적 import 구문을 만나면 코드를 분할하여 번들링 합니다.
  • 문제는 바로 동적 import 구문으 Promise 형태로 모듈을 반환해 준다.
  • import 하려는 모듈은 컴포넌트이기때문에 Promise내부에서 로드된 컴포넌트를 Promise 밖으로 빼내야 한다.
  • 리액트는 이런 불편함을 없애 주는 기능을 제공한다
import React, { Suspense } from 'react';

const SomeComponent = React.lazy(() => import('./SomeComponent'))

function MyComponent() {
	return (
		<Suspense fallback={<div>Loading...</div>}>
			<SomeComponent />
		</Suspense>
	)
}

컴포넌트 지연 로딩

  1. mouse hover시 API 호출
const handleMouseEnter = () => {
	const component = import('./components/ImageModal') // 컴포넌트 호추
}

...

<ButtonModal
	onMouseEnter={handleMouseEnter}
/>

  1. 컴포넌트 마운트 완료 후 사전 로딩
useEffect(() =>{
	const component = import('./components/ImageModal') 
}, [])

이미지 사전 로딩

const img = new Image()
img.src = '이미지 주소'

intersection observer

const options = {
	root: null,
	rootMargin: '0px',
	threshold: 1.0,
}

const callback = (entries, observer) => {
	console.log('Entries', entries)
}

const observer = new IntersectionObserver(callback, options)

observer.observe(document.querySelector('#target-element1'))
observer.observe(document.querySelector('#target-element2'))

브라우저 API

  • root는 대상 객체의 가시성(객체가 보여지는)을 확인할 때 사용되는 뷰포트 요소, null이면 브라우저의 뷰포트로 설정 - 뷰포트에 대한 기준 객체 영역
  • rootMargin는 root요소의 여백 범위를 가상으로 확장하거나 축소 가능
  • threshold는 가시성 퍼센티지. 즉, 대상요소가 어느 정도 보일때 실행되는지 결정, 1.0으로 설정하면 대상요소가 다 보여져야 콜백이 실행, 0이면 1px이라도 보이면 콜백 실행
  • options, callback을 정의 후 IntersectionObserver 객체를 생성하면 인스턴스(observer)가 나오는데 이 인스턴스를 이용하여 원하는 요소를 관찰할 수 있다.
  • 콜백 함수 첫번째 인자는 대상 요소(entries)를 배열 형태로 전달 받는다.

Intersection Observer React 예제

function Card(props) {
	const imgRef = useRef(null)

	useEffect(() => {
		const options = {}
		const callback = (entries, observer) => {
			console.log('Entries', entries)
		}

		const observer = new IntersectionObserver(callback, options)
		observer.observe(imgRef.current)
		return () => 	observer.discount()
	}, [])

	return (
		<div>
			<img src={props.image} ref={imgRef} />
			<div className='생략'>{props.children}</div>
		</div>
	)
}
  • 여기서 가장 중요한 속성은 isIntersecting 이다. 이 값은 해당 요소가 뷰포트 내에 들어왔는지를 나타내는 값이다. 즉 이 값을 통해 해당 요소가 화면에 보이는건지, 화면에서 나가는 것인지 알 수 있다.

이미지 지연 로딩

function Card(props) {
	const imgRef = useRef(null)

	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.discount()
	}, [])

	return (
		<div>
			<img data-src={props.image} ref={imgRef} />
			<div className='생략'>{props.children}</div>
		</div>
	)
}
728x90
반응형
LIST