웹 개념

웹 최적화 정리

코딩질문자 2024. 8. 5. 19:25
728x90

! 이미지 지연 로딩

 

- 분석 툴

1.  Network 패널 Perfornance 패널, Lighouse 패널

2. 크롬 개발자 도구의 Coverage 패널

3. Squoosh - 서비스 이미지 압축 도구

4. PurgeCss - 사용하지 않는 css를 제거해주는 툴

 

Intersection Observer - 브라우저 제공 api

: 웹 페이지의 특정 요소를 관찰하면 페이지 스크롤 시, 해당 요소가 화면에 들어왔는지 아닌지 알려 준다.

 

 

### Intersection Observer 적용 코드

import React, {useRef, useEffect} from 'react'

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

	useEffect(() => {
		const options = {};
		const callback = (entries, observer) => {
			entries.forEach(entry => {
				if (entry.isIntersecting) {
					console.log('is isIntersecting >>> ', 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 data-src={props.image} ref={imgRef} />
			<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

 

 

entry는 IntersectionObserver 콜백 함수에서 제공하는 IntersectionObserverEntry 객체를 나타냅니다. 이 객체는 Intersection Observer가 관찰하고 있는 각 요소의 교차 상태에 대한 정보를 제공합니다. entry의 구조와 각 속성에 대해 더 자세히 설명하겠습니다.

IntersectionObserverEntry 객체 구조

  1. target: entry.target은 관찰 중인 대상 요소(HTML 요소)를 참조합니다. 이는 Intersection Observer가 가시성을 추적하고 있는 실제 DOM 요소입니다. 예를 들어, 위 코드에서 imgRef.current에 해당하는 이미지 요소가 target이 됩니다.
  2. isIntersecting: 이 속성은 대상 요소가 현재 뷰포트 또는 지정된 root 요소와 교차하고 있는지를 나타냅니다. true이면 요소가 보이는 상태, false이면 요소가 보이지 않는 상태를 의미합니다.
  3. 기타 속성들:
    • boundingClientRect: 대상 요소의 크기와 위치 정보를 담은 DOMRectReadOnly 객체입니다.
    • intersectionRect: 교차된 영역의 크기와 위치 정보를 담은 DOMRectReadOnly 객체입니다.
    • intersectionRatio: 교차된 비율을 나타내며, 0.0부터 1.0까지의 값을 가집니다.
    • rootBounds: 뷰포트 또는 지정된 root 요소의 크기와 위치 정보를 담은 DOMRectReadOnly 객체입니다.

위 코드의 구체적인 설명

javascript
코드 복사
entry.target.src = entry.target.dataset.src;

이 코드에서 사용된 부분을 좀 더 구체적으로 살펴보겠습니다:

  1. entry.target: Intersection Observer가 관찰하고 있는 대상 HTML 요소를 가리킵니다. 위 예시에서는 이미지 요소 (<img>)입니다.
  2. src 속성: 이는 HTML 이미지 요소의 src 속성입니다. 이미지를 표시하기 위해 필요한 실제 이미지 파일의 URL을 지정합니다. 브라우저는 src에 지정된 URL을 사용하여 이미지를 로드하고 표시합니다.
  3. dataset.src: HTML 표준에서는 데이터 속성을 요소에 저장할 수 있게 해주는 data-* 속성을 제공합니다. 예를 들어, <img data-src="image.jpg">처럼 사용됩니다. dataset은 이와 같은 data-* 속성들을 JavaScript에서 접근할 수 있도록 하는 객체입니다. dataset.src는 data-src 속성에 저장된 값을 반환합니다.

Lazy Loading (지연 로딩)

entry.target.src = entry.target.dataset.src;는 지연 로딩(lazy loading)을 구현하는 중요한 부분입니다. 지연 로딩은 다음과 같은 방식으로 동작합니다:

  1. 초기 로드 시점: 이미지의 src 속성은 비워두거나 낮은 해상도의 플라시보 이미지를 설정하고, 실제 이미지 URL은 data-src 속성에 저장해 둡니다. 이렇게 하면 초기 페이지 로드 시점에 이미지가 로드되지 않고 대기 상태에 있게 됩니다.
  2. Intersection Observer 감지: 사용자가 스크롤을 통해 이미지를 보게 될 때, 즉 이미지가 뷰포트에 들어오면 IntersectionObserver는 이를 감지하고 콜백 함수를 실행합니다.
  3. 이미지 로드: 콜백 함수에서 entry.target.src = entry.target.dataset.src;를 통해 실제 이미지를 로드하도록 합니다. 이로써 data-src에 저장된 URL이 이미지 요소의 src 속성으로 설정되면서 브라우저는 해당 이미지를 서버에서 받아와 화면에 표시합니다.

이 방식은 초기 페이지 로딩 속도를 개선하고, 사용자가 실제로 볼 가능성이 있는 이미지들만 로드함으로써 네트워크와 메모리 자원을 효율적으로 사용할 수 있게 해줍니다.

 


! 이미지 사이즈 최적화

 

https://squoosh.app/

 

Squoosh

Simple Open your image, inspect the differences, then save instantly. Feeling adventurous? Adjust the settings for even smaller files.

squoosh.app

을 이용하여 이미지를 최적화한 파일을 만든다.

 

 

import React from "react";
import { useEffect } from "react";
import { useRef } from "react";

function Card(props) {
	const imgRef = useRef(null);
	console.log(props)

	useEffect(() => {
		const options = {};
		const callback = (entries, observer) => {
			entries.forEach((entry) => {
				if (entry.isIntersecting) {
					const sourceEl = entry.target.previousSibling;
					sourceEl.srcset = sourceEl.dataset.srcset;
					entry.target.src = entry.target.dataset.src; // src에 값을 할당함으로써 이미지를 로드한다.
					observer.unobserve(entry.target); // 이미지를 로드한 이후에는 관찰을 중지한다.
				}
			});
		};

		const observer = new IntersectionObserver(callback, options);
		observer.observe(imgRef.current);

		return () => observer.disconnect();
	}, []);

	return (
		<div className="Card text-center">
			<picture>
				<source data-srcset={props.webp} type="image/webp" />
				<img data-src={props.image} ref={imgRef} />
			</picture>
			<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;

 

2. 지연 로딩을 할 때, useEffect를 이용하여 최적화된 파일을 담아서 보여준다.

 

useEffect 내부 로직 설명

  1. options 객체: 현재 이 부분은 비어 있으며, Intersection Observer에 대한 설정 옵션을 정의하는 곳입니다. 예를 들어, root, rootMargin, threshold 등을 설정할 수 있습니다. 여기서는 기본 설정으로 브라우저의 뷰포트가 root로 사용됩니다.
  2. callback 함수: 이 함수는 IntersectionObserver가 관찰하는 요소들의 교차 상태를 확인할 때 호출됩니다. entries는 교차 상태에 대한 정보를 가진 IntersectionObserverEntry 객체들의 배열이며, observer는 해당 IntersectionObserver 인스턴스를 가리킵니다.
  3. entries.forEach((entry) => { ... }): 관찰하고 있는 각 요소에 대해 반복문을 돌면서 교차 상태를 확인합니다.
  4. if (entry.isIntersecting):
    • entry.isIntersecting은 대상 요소가 뷰포트에 들어왔는지를 나타냅니다. 이 값이 true이면 요소가 현재 화면에 보이는 상태입니다.
  5. const sourceEl = entry.target.previousSibling;:
    • entry.target은 IntersectionObserver가 관찰하고 있는 대상 요소입니다.
    • previousSibling은 대상 요소의 바로 이전 형제 요소를 가리킵니다. 이 코드에서는 이미지 요소의 이전 형제 요소로, <source> 태그일 가능성이 있습니다. <source> 태그는 <picture> 요소 안에서 사용되며, 다양한 이미지 파일 포맷과 해상도를 제공하는데 사용됩니다.
  6. sourceEl.srcset = sourceEl.dataset.srcset;:
    • sourceEl 요소의 srcset 속성에 data-srcset 속성의 값을 설정합니다.
    • data-srcset 속성에는 실제 이미지 파일 경로들이 저장되어 있으며, 이 값들이 srcset 속성으로 설정되면서 브라우저가 적절한 이미지를 로드하게 됩니다. srcset 속성은 다양한 해상도에 따라 다른 이미지를 로드할 수 있게 해줍니다.
  7. entry.target.src = entry.target.dataset.src;:
    • 대상 이미지 요소의 src 속성에 data-src 속성의 값을 설정합니다.
    • data-src에는 이미지의 실제 경로가 저장되어 있으며, 이를 src에 할당함으로써 브라우저가 해당 이미지를 로드하게 됩니다.
  8. observer.unobserve(entry.target);:
    • 이미지를 로드한 후에는 더 이상 Intersection Observer로 관찰할 필요가 없으므로, unobserve 메서드를 호출하여 해당 요소에 대한 관찰을 중지합니다. 이를 통해 불필요한 연산을 줄이고 성능을 최적화할 수 있습니다.

 


!동영상 최적화

- 동영상 압축 서비스 ( Media.io )

https://www.media.io/

 

Media.io - Online Video, Audio, Image AI Tools

Unlock the power of AI with our all-in-one online media processing tools for video, audio, and image. Perfect for content creators, our tools include advanced features such as a video editor, object remover, and noise reducer to easily enhance your media.

www.media.io

 

import React from 'react'
import video from '../assets/banner-video.mp4'
import video_webm from '../assets/_banner-video.webm'

function BannerVideo() {
	return (
		<div className="BannerVideo w-full h-screen overflow-hidden relative bg-texture">
			<div className="absolute h-screen w-full left-1/2">
				<video
					className="absolute translateX--1/2 h-screen max-w-none min-w-screen -z-1 bg-black min-w-full min-h-screen"
					autoPlay loop muted>
					<source style={{filter: 'blur(10px)'}} src={video_webm} type="video/webm"/>
					<source src={video} type="video/webm"/>
				</video>
			</div>
			<div className="w-full h-full flex justify-center items-center">
				<div className="text-white text-center">
					<div className="text-6xl leading-none font-semibold">KEEP</div>
					<div className="text-6xl leading-none font-semibold">CALM</div>
					<div className="text-3xl leading-loose">AND</div>
					<div className="text-6xl leading-none font-semibold">RIDE</div>
					<div className="text-5xl leading-tight font-semibold">LONGBOARD</div>
				</div>
			</div>
		</div>
	)
}

export default BannerVideo

 

 

 


! 폰트 최적화

폰트가 변화하는 현상 - FOUT(Flash of Unstyled Text) or FOIT(Flash of Invisible Text)

 

폰트 다운로드 시점을 알 수 있는 라이브러리 - fontfaceobserver

 

폰트 포맷 - TTF, OTF 

웹 환경에 적절한 포맷 WOFF (Web Open Font Format)

 

파일 크기 EOF > TTF/OTF > WOFF > WOFF2

 

WOFF, WOFF2 변환 사이트: Transfonter

https://transfonter.org/

 

Online @font-face generator

The @font-face CSS rule allows web developers to specify online fonts to display text on their web pages. By allowing authors to provide their own fonts, @font-face eliminates the need to depend on the limited number of fonts users have installed on their

transfonter.org

 

일부의 폰트 스타일만 가지고 있는 것 : 서브셋 폰트

 

적용하는 모습

적용 하는 모습_그림

 

 

또는 Data-URI 형태로 CSS 파일에 포함켜서 할 수 있다. Data-URL 이란 data 스킴이 접두어로 붙은 문자열 형태의 데이터인데, 쉽게 말해서 파일을 문자열 형태로 반환하여 문서(HTML, CSS, js 등)에 인라인으로 삽입하는 것

 

* 적용 코드 *

.react-player iframe {
	height: 100% !important;
}

@font-face {
	font-family: "BMYEONSUNG";
	src: url("./assets/fonts/subset-BMYEONSUNG.woff2") format("woff2"),
	url("./assets/fonts/subset-BMYEONSUNG.woff") format("woff"),
	url("./assets/fonts/BMYEONSUNG.ttf") format("truetype");
	font-display: block;
}

.BannerVideo {
	font-family: "BMYEONSUNG", sans-serif;
}

 


 

! 캐시 최적화

 

캐시된 리소스와 서버의 최신 리소스와 같은 어떻게 체크할까?

 

캐시 유효 시간이 만료되면 브라우저는 캐시된 리소슬 계속 사용해도 될지 서버에 확인. 이 때 서버에서는 캐시된 리소스의 응답 헤더에 있는 Etage 값이 서버에 있는 최신 리소스 Etag 값을 비교하여 캐시된 리소가 최신인지 아닌지 판단 합니다.

 

* 캐시를 적용하는 코드 * 

const express = require("express");
const app = express();
const port = 5000;
const path = require("path");

const header = {
    setHeaders: (res, path) => {
        const format = path.slice(path.lastIndexOf("."));
        // prettier-ignore
        if (format === ".html")
            res.setHeader("Cache-Control", "no-cache");
        else if ([".js", ".css", ".webp", ".jpeg"].includes(format))
            res.setHeader("Cache-Control", "public, max-age=31536000");
        else
            res.setHeader("Cache-Control", "no-store");
    },
};

app.use(express.static(path.join(__dirname, "../build"), header));
app.get("*", (req, res) => {
    res.sendFile(path.join(__dirname, "../build/index.html"));
});

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`));

 

 


 

! 불필요한 css 제거

 

 

LightHouse 에서 Coverage라는 패널이 있다.

: 새로고침 해보면 새로고침 되는 과정에서 실행한 코드를 리소스별로 표시 해준다.

 

purgeCSS : 사용하지 않는 CSS 코드를 제거하는 툴

https://purgecss.com/

 

PurgeCSS - Remove unused CSS | PurgeCSS

 

purgecss.com

 

- 파일에 들어 있는 모든 키워드를 추출하여 해당 키워드를 이름으로 갖는 CSS 클래스만 보존하고 나머지 매칭되지 않는 클래스는 모두 지우는 방식으로 CSS를 최적화

 

npm install --save-dev purgecss

 

실행 명령어 - ex

 "purge": "purgecss --css ./build/static/css/*.css --output ./build/static/css/ --content ./build/index.html ./build/static/js/*.js --config ./purgecss.config.js"

 

 

실행시 설정 코드 - ex (루트/purgecss.config.js 에 설정)

module.exports = {
  defaultExtractor: (content) => content.match(/[\w\:\-]+/g) || [],
};

 


이상 웹 최적화 정리를 마칩니

728x90