1. 이미지 최적화 - 이미지 CDN
화면에 렌더될 이미지의 크기가 100px인데 다운받은 사진이 1000px일 경우 리소스 낭비됨.
때문에 CDN을 이용해 처음에 다운받을 때 부터 사이즈를 조절해서 다운받거나
미국서버가 아닌 좀 더 가까운 한국 서버에서 다운 받도록 하여 다운 속도를 향상시킬 수 있음
※ CDN 사이트 예제 : https://developers.cloudflare.com/images/image-resizing/url-format/'
100px로 렌더될 이미지라면 2배수인 200 사이즈로 줄이고, 품질을 낮춰서 다운받는다
<img src="/cdn-cgi/image/width=200,quality=75/uploads/avatar1.jpg" />
// https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>
2. 병목코드 최적화
performance 탭에서 컴포넌트가 렌더링 될 때 오래걸리는 부분을 파악 -> 그 부분의 코드 수정하여 최적화하기
3. 코드 분할(code splitting) , 지연로딩(lazy loading)
- 하나의 번들 파일을 여러 개로 쪼개서(코드 분할) 필요한 시점에 로드되어 실행됨(지연 로딩)
: 장) 페이지를 로드할 때 당장 필요 없는 코드는 번들에 포함되지 않아 로드할 파일이 작아지고, 초기 로딩 속도 빨라짐
: 단) 해당 페이지에 들어갈 때 새로 로드해야 되서 지연 발생 (->사전 로딩으로 해결가능함)
- 동적 import 사용
: import 문을 빌드할 때가 아닌, 런타임에 로드하는 것
: 동적 import는 Promise를 반환함. Promise안에 컴포넌트가 감싸진 형태로
때문에 React.lazy로 로드된 컴포넌트를 프로미스 밖으로 빼내는 것임.
(※ 동적으로 모듈 가져오기 : https://ko.javascript.info/modules-dynamic-imports )
- React.Suspense
: lazy로 import하면 컴포넌트를 로딩하는 시간이 걸리는데, 그 시간동안 화면이 보여지도록 하는 역할
: 컴포넌트 로드되기 전엔 fallback이 보여진다.
function App() {
const ListPage = React.lazy(() => import("./pages/ListPage/index"));
const ViewPage = React.lazy(() => import("./pages/ViewPage/index"));
return (
<div className="App">
<React.Suspense fallback={<div>loading...</div>}>
<Switch>
<Route path="/" component={ListPage} />
<Route path="/view/:id" component={ViewPage} />
</Switch>
</React.Suspense>
</div>
);
}
4.사전로딩
✔️ 컴포넌트 사전 로딩
: 미리 코드를 로드해 두어 컴포넌트를 빠르게 띄울 수 있음
[컴포넌트 사전로딩 시점]
1) 버튼 위에 마우스 올려놓았을 때 사전로딩 하기
: mouseEnter 이벤트를 통해 버튼 누르기 전에 미리 로드하여 준비시켜 놓기
function App() {
const handlerMouseEnter = () => {
const component = import("./components/ImageModal");
};
return (
<ButtonModal onMouseEnter={handlerMouseEnter}>
사진 보기
</ButtonModal>
}
2) 최초에 페이지가 로드되고 모든 컴포넌트 마운트 끝났을 때
: 미리 준비해야 할 컴포넌트 크기가 큰 경우 사용함. mouseEnter하기도 전에 미리 파일을 준비하게 됨.
: 마운트 완료 시점 파악
React - componentDidMount, useEffect , VUE - mounted에서 $nextTick
✔️이미지 사전로딩
: js의 new Image를 통해 이미지를 직접 로드해놓음
: 브라우저가 해당 이미지를 로드하고, 로드된 이미지는 브라우저 캐시에 저장됨.
동일 이미지에 대한 후속 요청이 있을 때 다시 다운하지 않고 캐시된 이미지를 보여주어 사전로딩이 가능한 것임
useEffect(() => {
const img = new Image();
img.src = "https://photos/...";
}, []);
5. 리플로우, 리페인트 지양
[리렌더링(리플로우,리페인트)]
브라우저 리소스를 많이 잡아먹어, 화면을 새로 그리는 것이 느려진다.
- 리플로우 : cssom , render Tree > 리레이아웃 > 리페인트 > composition
- 리페인트 : cssom , render Tree > x > 리페인트 > composition
[대책]
CPU에서 해야할 작업을 GPU(그래픽 처리 장치)로 전가
: GPU는 애초에 그래픽 작업을 처리하기 위해 만들어진 것이여서 화면은 빠르게 그려줌
: GPU를 활용하여 그래픽 작업이 이뤄져, CPU에서 리레이아웃,리페인트 거칠 필요 없이 스타일 변경 가능해짐
✔️ transform , opacity 사용
transform , opacity은 브라우저에서 합성 계층으로 취급되어 다른 계층과 격리되어 있어,
해당 계층이 변화되도 다른 요소에 영향X
- left, right, width, height 보단 transform사용
: transform은 실제 레이아웃에 영향을 주지 않음. 요소의 크기나 위치가 변경되지 않아 리플로우X - visibility, display보단 opacity 사용
: opacity는 픽셀의 변화를 일으키지 않음. 따라서 리페인트X
✔️ 적용
만약 progress bar를 width 속성으로 채운다면 width가 변할 때마다 리플로우,리페인트 가 발생한다.
그러나 transform:scaleX 을 이용한다면 리플로우를 막을 수 있다. (> composite만 일어남)
※ 추가 자료
https://wit.nts-corp.com/2017/06/05/4571
https://boxfoxs.tistory.com/408
6. 이미지 지연로딩 - Intersection Observer
당장 보여지지 않는 이미지는, 스크롤하여 뷰포트에 요소가 들어온 때 이미지를 로드하는 방식 (요소를 관찰하는 것)
페이지 스크롤시 요소가 화면에 들어왔는지 감시하여 로직이 실행되게 됨.
- scroll 이벤트를 사용하면 스크롤 할 때마다 계속 로직이 실행되는 문제가 있어 성능적으로 좋지 않음
import React, { useEffect, useRef } from "react";
function Card(props) {
const imgRef = useRef(null);
useEffect(() => {
const option = {};
const callback = (entreis, observer) => {
entreis.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target); // 요소에 대한 관찰을 제거 (이미지 한번 로드 후 다시 호출할 필요가 없으므로 관찰 해제)
}
});
};
const observerInstance = new IntersectionObserver(callback, option);
observerInstance.observe(imgRef.current);
return () => observerInstance.disconnect(); // cleanUp funtion - 인스턴스 제거 (리소스 낭비 방지)
}, []);
return (
<div>
<img data-src={props.image} ref={imgRef} />
</div>
);
}
7. 이미지 사이즈 최적화
✔️ 이미지 포맷 종류
1) PNG : 무손실 압축(원본 회손 없이 압축), 투명도 표현 ㅇ
2) JPG : 손실 압축(원본 정보에 손실 발생), 투명도 표현X
: 일반적으로 웹에서 이미지 사용시 고화질이거나 투명도 정보가 필요한게 아니라면 jpg 사용함
3) WEBP : 손실,무손실 압축 모두 제공
webp만으로 이미지 렌더링 할 경우
최신 이미지 포맷이라 지원되지 않는 브라우저 있을 수 있음
=> picture, source 태그를 사용하여 이미지 렌더하여 해결 가능
✔️ picture
picture 태그는 다양한 타입의 이미지를 렌더링하는 컨테이너로 사용됨
1. 브라우저별 지원되는 타입의 이미지를 찾아 렌더
- source태그가 여러개 있으면 위에서 부터 적용됨 (webp가 지원되지 않는 브라우저라면 > jpg로 적용)
- source에 지원되는 이미지 형식이 없다면, 기본 img태그를 기본 이미지로 사용한다.
<picture>
<source srcset="./assets/main1.webp" type="image/webp">
<source srcset="./assets/main1.jpg" type="image/jpeg">
<img src="./assets/main1.jpg" alt="Main Item">
</picture>
2. 브라우저 사이즈에 따라 지정된 이미지 렌더
<picture>
<source srcset="./assets/main1-large.jpg" media="(min-width: 800px)"> <!-- 800px 이상인 경우 사진 바뀜 -->
<source srcset="./assets/main1-medium.jpg" media="(min-width: 600px)"> <!-- 600px 이상인 경우 사진 바뀜 -->
<img src="./assets/main1-small.jpg" alt="Main Item"> <!-- 기본 이미지 -->
</picture>
✔️ picture 태그
- webp 지원이 안될시 기본 img태그를 쓰도록 처리하는 picture 태그 처럼, 동영상에선 video 태그가 동일한 역할을 해준다.
- webp 처럼 비디오에선 webm이 있다.
<vidio autoPlay loop muted>
<source src={'video_webm'} type="video/webm" />
<source src={'video'} type="video/mp4" />
</vidio>
8. 폰트 최적화
✔️ 폰트 로드하는 방식
1) FOUT (Flash of Unstyled Text)
- 사용 브라우저 : Edge
- 폰트 다운여부와 상관없이, 일단 기본 텍스트를 여줘준 뒤 다운완료되면 그때 폰트 적용하는 방식
- 적용 : 중요한 텍스트인 경우 (글자가 빠르게 나타나는게 중요)
2) FOIT (Flash of Invisible Text)
- 사용 브라우저 : 크롬, 사파리, 파이어폭스 등
- 폰트가 완전히 다운로드 되기 전까지 텍스트를 보여주지 않다가 -> 폰트 다운 완료되면 폰트 적용된 텍스트 보여줌
- 적용 : 사용자에게 꼭 전달하지 않아도 되는 텍스트의 경우
✔️ 폰트 최적화 방법1. 폰트 적용 시점 제어하는 법
CSS font-display 속성으로 폰트 적용 시점을 제어할 수 있다.
fout 방식인 브라우저에서 foit 방식 적용할 수 있고, foit 방식인 브라우저에서 fout 방식 적용할 수 있다.
@font-face{
font-display: fallback,
font-family : ...,
scr : url("")
}
font-display 속성
1. auto : 브라우저 기본 동작 (default)
2. block : FOIT (timout = 3s)
: 글꼴 다운로드 최대 3초까지 기다렸다 -> 기본폰트 -> 커스텀 폰트 적용
: 다운된 글꼴이 적용될 때 자연스럽게 표현하기
1) fontfaceobserver 라이브러리를 이용해 폰트가 다운되는 시점을 파악
2) 다운된 폰트가 적용될 때 애니메이션(페이드인)을 적용하여 부드럽게 나타낼 수 있다.
3. swap : FOUT
: 글꼴 다운 완료될 때 까지 기본글꼴 적용
4. fallback : FOIT (timeout = 0.1s) :
: 0.1초 까지는 텍스트 보여주지 않음.
: 0.1초 이후엔 폴백 폰트 적용 (※ 폴백 폰트: 폰트가 로드되지 않거나 사용할 수 없을 때 대신 사용하는 대체 폰트)
: 3초 후에도 폰트 다운하지 못한 경우, 다운 완료 되더라도 폰트 적용하지 않고 캐시해둠
: 이후 페이지가 다시 로드왰을때 캐시된 폰트를 바로 적용함
5. optional : FOIT (timeout = 0.1s)
: 0.1초 까지는 텍스트 보여주지 않음.
: 0.1초 이후엔 폴백 폰트 적용
: 네트워크 상태에 따라 폰트 적용할지 여부 결정, 다운 완료 되더라도 폰트 적용하지 않고 캐시해둠
: 이후 페이지가 다시 로드됐을때 캐시된 폰트를 바로 적용함
✔️ 폰트 최적화 방법2. 폰트 사이즈 줄이는 법
1. 압축률이 좋은 확장자로 변경
파일 크기 : EOT > TTF/OTF > WOFF > WOFF2
- 폰트 압축 사이트 : transfonter
- png,jpg보다 압축률이 좋은 webp가 있는 것 처럼, 폰트에는 TTF 보다 압축률이 좋은 WOFF가 있다.
- WOFF, WOFF2 : 브라우저 호환성 문제 있음
- webp 지원 안될때 png로 나오게 한 원리처럼 폰트도 마찬가지
@font-face {
font-family: BMYEONSUNG;
src: url('./assets/fonts/BMYEONSUNG.woff2') format('woff2'), // woff2 적용
url('./assets/fonts/BMYEONSUNG.woff') format('woff'), // 지원안되면 woff 적용
url('./assets/fonts/BMYEONSUNG.ttf') format('truetype'); // 지원안되면 ttf 적용
font-display: block;
}
2. 필요한 문자의 폰트만 로드
서브셋 폰트 사용 TODO
읽기!