최적화는 주어진 조건으로 최대 효율을 낼 수 있도록 하는 것을 말한다.
컴퓨터 공학에서의 최적화는 가능한 적은 리소스를 소모하면서 가능한 한 빠르게 원하는 결과를 얻을 수 있도록 하는 것을 의미하며,
웹 개발에서의 최적화는 주어진 조건 아래에서 최대한 빠르게 화면을 표시하도록 만드는 것을 말한다.
오늘 공부한 내용은 웹 개발 중에서도 프론트엔드 단에서 할 수 있는 최적화 방법이다.
HTML 최적화
CSS 최적화
CSS 파일 불러오기
: CSSOM 트리 가능한 빠르게 구성할 수 있도록 HTML 문서 최상단에 배치하는 것 권장. (head 요소 안에서)
Js 파일 불러오기
: Js는 DOM 트리와 CSSOM 트리를 동적으로 변경할 수 있다.
HTML 코드 파싱 중 script 요소 만나면 해당 스크립트가 실행되며 script 요소 이전까지 생성된 DOM까지만 접근 가능.
script 요소를 HTML 코드 중간에 넣는다면 해당 요소 이후 생성될 DOM을 수정하는 코드가 있는 경우 화면이 의도한 대로 표시되지 않음.게다가 스크립트 실행 완료 전까지 DOM 트리 생성이 중단되므로 랜더링 완료 시간이 늦춰진다.
이런 이유로 JS 파일을 DOM 트리 생성 완료 시점인 HTML 문서 최하단에 배치하는 것이 좋음. (body 요소가 닫히기 직전)
미디어 파일이 차지하므로 이미지의 용량을 줄이거나 요청의 수를 줄이는 것을 우선적으로 고려할 시, 사용자 경험을 빠르게 개선할 수 있다.
이미지 스프라이트 사용
: 여러 개의 이미지를 모아 하나의 스프라이트 이미지로 만들고 CSS의 background-position 속성을 이용해 일정 부분만 클래스 등으로 구분해 사용하는 방법.
하나의 이미지를 배경 이미지로 사용하되, 표시하고 싶은 부분에 맞춰 width
, height
, background-position
속성을 주어 아이콘을 만든다.
한번의 이미지 요청으로 대부분의 개별 이미지를 사용할 수 있기 때문에 네트워크 로딩 시간을 줄일 수 있다.
많은 이미지 파일을 개별로 관리할 필요없이 특정 스프라이트 이미지 파일만을 관리하면 되므로 관리가 용이하다는 장점도 있음.
아이콘 폰트 이용하기
: 아이콘 사용이 많을 때에는, 모든 아이콘을 이미지로 사용하는 것이 아니라 아이콘 폰트를 사용하여 용량을 줄일 수 있다.
대표적인 아이콘 글꼴 서비스 Font Awesome 사용법
- CDN으로 사용
: 가입 후 발급해주는 키트를 HTML 파일<head>
요소에 넣어주고, 원하는 아이콘을 사용할 환경(HTML, React, Vue)에 맞는 코드로 복사해서 붙여넣기 해 사용 가능.- Font Awesome 모듈 설치
리액트 환경에서 사용할 경우 다음과 같은 패키지들 설치// 핵심 패키지 설치 npm i --save @fortawesome/fontawesome-svg-core // 아이콘 패키지 설치 (해당 코드는 무료 아이콘들) npm i --save @fortawesome/free-solid-svg-icons npm i --save @fortawesome/free-regular-svg-icons npm i --save @fortawesome/free-brands-svg-icons // Font Awesome React 구성 요소 설치 npm i --save @fortawesome/react-fontawesome@latest
설치 후 사용하고 싶은 아이콘 정보 확인한 뒤 불러와 사용하면 되고, 아이콘 이름은 카멜 케이스로 작성해야 함.
불러온 아이콘은 클래스명을 직접 붙이거나 Font Awesome이 정해준 방법을 사용해 스타일링 해줄 수 있음.
<picture>
태그 이용해 각 브라우저 호환에 맞도록 분기 대체할 수 있음.// 접속한 브라우저에서 source 태그 내 srcset에 정의한
//WebP 포맷을 지원하지 않는다면 해당 source 태그는 무시됨.
<picture>
<source srcset="logo.webp" type="image/webp">
<img src="logo.png" alt="logo">
</picture>
CDN은 네트워크 지연(유저와 호스팅 서버간 물리적 거리의 한계로 인해 발생)을 해결하고자 세계 곳곳에 분산된 서버에 콘텐츠를 저장한다.
CDN은 유저가 가까운 곳에 위치한 테이터 센터(서버)의 데이터를 가져오는데, 데이터가 전달되기 위해 거쳐야하는 서버 개수가 크게 줄기 때문에 로딩 속도가 빨라진다.
CloudFront, Cloudflare와 같은 CDN 서비스들이 있음.
캐시는 다운로드 받은 데이터나 값을 미리 복사해놓는 임시 장소를 뜻하며 데이터에 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우 사용.
캐시 효과
캐시 검증 헤더와 조건부 요청
캐시 유효 시간은 지났지만 캐시에 저장된 파일과 완전히 동일한 파일을 받아와야 할 때 사용할 수 있는 HTTP 헤더들
- Last-Modified : 데이터가 마지막으로 수정된 시점을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-Modified-Since 와 묶어서 사용합니다.
- Etag : 데이터의 버전을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-None-Match 와 묶어서 사용합니다.
- If-Modified-Since : 캐시된 리소스의 Last-Modified 값 이후에 서버 리소스가 수정되었는지 확인하고, 수정되지 않았다면 캐시된 리소스를 사용합니다.
- If-None-Match : 캐시된 리소스의 ETag 값과 현재 서버 리소스의 ETag 값이 같은지 확인하고, 같으면 캐시된 리소스를 사용합니다.
실제 사용
//Last-Modified와 If-Modified-Since 확인 후
//서버와 캐시의 데이터가 동일한 데이터임이 검증되면 데이터가 수정되지 않았음을 의미하는
//304 Not Modified 라는 응답을 보내주고 캐시 데이터 유효 시간이 갱신되면서 해당 데이터 재사용할 수 있게 됨
//Etag와 If-None-Match 확인 후
//서버 데이터의 Etag와 캐시에 저장된 데이터의 Etag가 동일하면
//데이터가 수정되지 않았음을 의미하는 304 Not Modified 응답 보내주고
//캐시 데이터 유효 시간 갱신되면서 해당 데이터 재사용 가능해짐
두 쌍의 헤더 중 한 쌍만 사용할 수도 있지만 보통은 두 종류를 동시에 사용한다.
둘 중 하나만 사용했다 매칭되는 응답 헤더가 없는 경우에는 재사용할 수 있는 경우에도 리소스 다시 받아와야 하는 경우가 생길 수 있기 때문.
나무를 흔들어 잔가지를 털어내듯 불필요한 코드 제거하는 것 의미
Java Script에 트리쉐이킹이 필요한 이유
🙄 다운로드가 필요한 경우 요청 보내 파일 다운 받아온 다음 압축 해제 ⇒ Js 코드 파싱해 DOM트리 생성 ⇒ 파싱 끝나면 컴파일해 컴퓨터가 이해할 수 있는 언어로 바꾸기 ⇒ 코드 실행
Java Script 트리쉐이킹
웹팩 4버전 이상 사용시 ES6 모듈(import, export 사용하는 모듈) 대상으로 기본적인 트리쉐이킹 제공함.
Create React App 통해 만든 React 어플리케이션도 웹팩 사용하므로 트리쉐이킹 가능.
{
“presets”: [
[
“@babel/preset-env”,
{
"modules": false
}
]
]
}
//modules 값이 true이면 항상 ES5 문법으로 변환
{
"name": "tree-shaking",
"version": "1.0.0",
"sideEffects": false
}
특정 파일에서 발생하지 않을 것임을 알려주려면 아래와 같이 작성{
"name": "tree-shaking",
"version": "1.0.0",
"sideEffects": ["./src/components/NoSideEffect.js"]
}
import
해서 사용하지 않는 코드는 빌드할 때 제외되기 때문)구글에서 개발한 오픈소스로서 웹 페이지의 품질을 개선할 수 있는 자동화 툴.
Lighthouse는 다양한 지표를 이용하여 웹페이지의 성능 검사를 해줄 뿐만 아니라 그에 대한 개선책도 제공해줌.
성능, 접근성, PWA, SEO 등을 검사하며 이를 이용해 사용자는 어떤 웹페이지든 품질 검사를 할 수 있다.
Lighthouse는 Chrome DevTools부터 CLI, 노드 모듈 등 다양한 경로를 통해 사용할 수 있다. 검사할 페이지의 url을 Lighthouse에 전달하면 Lighthouse는 해당 페이지에 대한 여러 검사를 실행함.
그 후, 검사 결과에 따른 리포트를 생성하고 개발자는 해당 리포트를 통해 점수가 낮은 지표에 대해 개선할 수 있음. 또한 각각의 지표가 왜 중요한지, 어떻게 개선할 수 있는 지에 대한 레퍼런스도 리포트에서 참고할 수 있다.
실행 방법
//Node CLI에서 실행하려면 설치 필요
//설치
npm install -g lighthouse
//검사 실행
lighthouse <url>
//모든 옵션 볼 수 있음
lighthouse --help
Lighthouse 분석 결과 항목
<meta>
요소는 잘 작성되어 있는지, 텍스트 크기가 읽기에 무리가 없는지 등을 확인함.주요 Metric들이 무엇을 측정하는가?
페이지에 FCP로 측정된 컨텐츠가 표시된
이벤트 핸들러가 가장 잘 보이는 페이지의 엘리먼트에 등록됨
페이지가 0.05초안에 사용자의 상호작용에 응답함.
TTI 점수는 아카이브된 HTTP 데이터를 기반으로 백분위 단위로 점수를 측정하며 아래의 표를 기준으로 점수를 해석할 수 있다.
네이버로 검사를 해봤는데 위는 모바일, 그러니까 m.naver.com일 때의 검사 결과이고, 아래는 데스크탑일 때 naver.com일 때의 검사 결과이다.
다른 항목들은 비슷한데, 모바일 환경에서 성능이 안 좋게 나왔다.
아래는 모바일 환경 네이버 웹 리포트의 Opportunities 부분이다.
1. Serve images in next-gen formats : 이미지들을 다음 세대 포맷으로 다룰 것
: 브라우저 이미지 최적화 파트에서 배웠듯이 WebP, AVIF는 PNG나 JPEG보다 더 나은 압축을 제공하는 이미지 포맷이고, 더 빠른 다운로드들과 적은 데이터를 소비한다고 한다.
오늘 소그룹에서 한 팀원이 이야기해주시기론 네이버 같은 큰 회사들은 작년까지는 WebP, AVIF 같은 포맷을 잘 사용하지 않았는데 인터넷 익스플로러에서 해당 포맷들을 지원하지 않았기 때문이라고 한다.
모쪼록 이제는 편히 잠든 인터넷 익스플로러이므로...이제는 해당 포맷들을 좀 더 자유롭게 사용해볼 수 있지 않을까...
2. use video formats for animated content : 애니메이션이 있는 콘텐츠들을 비디오 포맷으로 사용할 것
: 네이버에서 모바일 환경에서 움직이는 이미지들에 GIF 포맷을 사용하고 있는데, 해당 포맷은 애니메이션이 있는 포맷들을 제공하는 데 비효율적이라고 한다. 리포트에서 추천하기로는 MPEG4/WebM 비디오 포맷을 사용할 것을 권장하고 있었다. PNG나 WebP와 같은 정적인 이미지들을 GIF 대신 사용하는 것도 네트워크의 byte들을 아끼는 방법으로 추천했다.
3. Reduce unused Js, Reduce unused CSS : 사용되지 않는 JS나 CSS를 줄일 것
: 해당 부분을 펼쳐서 보면 어떤 부분들에서 얼마나 감소를 시킬 수 있는지 'Potential Savings'에 대해서도 알려주고 있는데 URL을 눌러보면... 엄청난 양의 코드가 나와서 사실 눈으로 직접 확인하기는 어려워보였다. 모쪼록 사용되지 않는 JS, CSS를 포착해서 고칠 수 있다면 성능 최적화에 도움이 될 것이다.
4. Efficiently encode images : 효율적으로 이미지들을 인코드할 것
: 이 부분도 1번과 비슷하게 이미지 용량을 최적화하라는 의미인데, 이런 이미지 용량 최적화를 위한 방법으로 Lighthouse에서 추천해주고 있는 방법은 이미지 CDN을 사용하는 방법, 수동 이미지 압축 등이 있다.
이미지 CDN은 서버로부터 이미지를 불러오면 이미지 사이즈를 조절해주는 회사에 의뢰해서 원하는 형태로 이미지를 바꾸는 방법을 말한다.
5. Eliminate render-blocking resources : 렌더링을 지연시키는 리소스들을 제거할 것
: Lighthouse는 웹 페이지의 first paint를 지연시키는 리소스를 알려주고, 이 리스트를 확인해서 중요하지 않은 리소스들을 연기하고, 사용되지 않는 경우 제거하는 방식으로 성능을 높이도록 권장하고 있다.
Lighthouse는 두 종류의 렌더링 차단 리소스를 찾는데, scripts와 stylesheet이다.
아래와 같은
<script>
를 지정한다.
- 문서의 head 안에 있는 것
- defer 속성이 없는 것
- async 속성이 없는 것
아래와 같은
<link rel="stylesheet">
를 지정합니다.
- disabled 속성이 없는 것. (disabled 속성이 지정되어 있다면 브라우저는 그 파일을 다운로드하지 않습니다.)
- 유저의 기기에 맞는 media 속성을 가지지 않은 것.