💡 이번에 배운 내용
- Section4. 사람과 기계가 모두 쉽고 빠르게 접근 가능한 Web App을 만들 수 있다.
- Unit8. Optimization 최적화: 최적화의 개념 및 기법에 대해 배우고, 실제로 최적화 방안을 고안하는 법에 대해 학습한다.
웹 개발에서의 최적화 개념에 대해 배우면서 Lighthouse라는 유용한 툴을 발견했다. 나중에 사이트를 최적할 때 사용하기 좋을 것 같다. 막상해보니까 편리하고 좋다. 이미지 스프라이트 개념은 원래도 알고 있었지만 장단점이 있었던 걸로 기억한다. 프로젝트시 디자이너와 다른 구성원들과도 잘 상의해서 했고, 규모가 큰 프로젝트가 아니면 잘 사용하지 않았던 것 같다. (css로 그리거나 svg를 사용하는 경우도 있어서) 그래도 예전에 알고 있었던 개념을 보니 반갑기도 하다. 오늘도 블로깅 완료!
최적화 Optimization, 이미지 스프라이트, Font Awesome, WebP, AVIF, <picture>, CDN, 캐시 Cache, 트리쉐이킹 Tree Shaking, Lighthouse
<최적화, 最適化, optimization>
주어진 상황에서 원하는 가장 알맞은 결과를 얻을 수 있도록 처리하는 과정.
최적화는 허용된 자원의 한계 내에서 주어진 요구사항을 만족시키면서 최선의 결과를 얻는 과정이다. 수익과 관련되는 분야에서는 이익을 최대로 내는 과정을 말하기도 한다. 다양한 분야와 때에 따라 다르게 정의할 수 있고 물류(logistics), 설계(design) 문제 등에 응용된다.- 한국정보통신기술협회의 정보통신 용어사전 -
위의 내용을 바탕으로, 컴퓨터 공학에서의 최적화는 가능한 적은 리소스를 소모하면서 빠르게 원하는 결과를 얻는 과정을 의미한다. 그 예로 알고리즘 문제를 푸는 것, 더 효율적인 코드를 작성하는 것들이 있다.
그리고 웹 개발에서의 최적화는 주어진 조건 아래 최대한 빠르게 화면을 표시하는 것이다.
최적화는 다음과 같은 이유로 필요하다.
이탈률을 감소시키기 위해
화면을 불러오는 시간이 길어지면 사용자가 페이지를 이탈할 확률이 높아진다.
(이탈: 방문자가 웹 사이트의 첫 페이지에서 아무런 상호작용도 하지 않고 종료하는 것)
구글의 조사 결과 페이지 로드 속도가 늘어날수록, 이탈률이 3초 32%, 5초 90%, 6초 106%, 10초 123%까지 증가했다. 따라서 웹 사이트의 성능 최적화를 통해 페이지 로딩 속도를 줄여 이탈률을 줄여야 한다.
전환율을 증가시키기 위해
전환율은 웹 사이트를 방문한 사용자 중 어떤 행위(회원가입, 상품 구매, 게시글 조회, 다운로드 등)를 한 방문자의 비율을 의미한다. 그러나 사용자가 이탈해버리면 전환율은 0%이므로 먼저 이탈률을 줄이는 것이 중요하다.
수익 증대
사용자가 많고 활동이 활발해야(이탈률 감소, 전환율 증가) 트래픽이 증대되고 회원 수가 증가한다. 이는 곧 수익 증대로 이어진다. 1~2%만 증가해도 돈으로 환산하면 엄청난 양의 매출 증가로 이어진다. (아마존 판매량이 1% 증가하면 68억 달러 정도의 매출이 증가한다고 한다.)
사용자 경험(UX) 향상
페이지 로드 속도 개선을 우선으로 하면 사용자 경험이 향상된다.
그래도 시간이 좀 걸린다면 스피너, 프로그레스 바, 스켈레톤 등 로딩 중임을 알려주는 UI를 먼저 표시하는 방법이 있다.
화면을 렌더링할 때는 HTML 파일과 CSS 파일이 필요하며 각각 DOM 트리, CSSOM 트리를 만들어지며 렌더링할 때 사용된다. 즉 HTML, CSS 코드를 최적화하여 리렌더링을 낮추고 렌더링 성능을 향상시킬 수 있다.
HTML 파일에서 <script>
요소, <link>
요소의 위치에 따라 렌더링 완료 시점이 달라질 수 있다.
<script>
요소를 만나면 이전까지 생성된 DOM까지만 접근하며 스크립트가 실행된다. 이 때 다운로드 및 스크립트 실행이 완료될 때까지 DOM 트리 생성이 중단되고, 그만큼 렌더링 완료 시간은 늦춰지게 된다.이미지 파일과 같은 미디어 파일은 보통 전체 페이지 용량의 약 51%를 차지한다. 때문에 이미지의 용량을 줄이거나 요청의 수를 줄이는 것을 우선적으로 고려한다면 사용자 경험을 빠르게 개선할 수 있다.
아래는 네이버의 이미지 스프라이트의 예시이다.
하나의 이미지를 배경 이미지로 사용하되, 나타내고자 하는 부분의 width, height, background-position 속성으로 배경을 주어 아이콘을 만들 수 있다.
<picture>
태그를 이용해 각 브라우저의 호환에 맞도록 분기할 수 있다.<picture>
는 img 다중 리소스(multiple image resources)로 사용하는 컨테이너로 안에 <source>
를 작성하여 사용한다. 만약 브라우저에서 <source>
태그의 리소스를 지원하지 않는다면 그 안의 다른 요소로 렌더링 한다. 사용 예는 다음과 같다.<picture>
<source srcset="logo.webp" type="image/webp">
<!-- source의 리소스를 표시할 수 없으면 아래 img가 표시된다. -->
<img src="logo.png" alt="logo">
</picture>
CDN은 유저가 가까운 곳에 위치한 데이터 센터(서버)의 데이터를 가져오는 것으로 데이터가 전달되는 거리와 거쳐야 하는 서버의 수가 줄어들어 로딩 속도가 빨라진다.
CDN은 세계 곳곳에 분포한 분산된 서버에 콘텐츠를 저장한다.
예시로 CloudFront, Cloudflare 등의 서비스가 있다.
캐시(Cache)는 다운로드 받은 데이터나 값을 미리 복사해 놓는 임시 장소를 의미한다.
데이터에 접근 시간이 길거나 같은 값을 다시 계산하지 않기 위해 사용한다.
브라우저의 캐시를 활용하면 다음과 같은 효과가 있다.
또한 브라우저에 해당 데이터를 캐시로 보관하기 위해 유효기간을 주는 속성으로
Cache-Control: max-age=60
등이 있다.
이 의미는 캐시의 유효기간을 60초로 설정한다는 의미다.
이 뿐만 아니라 아래와 같이 헤더에 캐시와 관련된 여러 설정들이 있다.
서버로 부터 데이터를 받아올 때 캐시를 활용하기 위해 응답 헤더에 몇가지 데이터가 담겨있다.
이 때 사용되는 되는 개념은 다음과 같다.
Last-Modified
, Etag
)If-Modified-Since
, If-None-Match
)각 내용을 좀 더 살펴보면 다음과 같다.
Last-Modified
: 데이터가 마지막으로 수정된 시점을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-Modified-Since
와 함께 사용한다.If-Modified-Since
: 캐시된 리소스의 Last-Modified
값 이후에 서버 리소스가 수정되었는지 확인하고, 수정되지 않았다면 캐시된 리소스를 사용한다.Etag
: 데이터의 버전을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-None-Match
와 함께 사용한다.If-None-Match
: 캐시된 리소스의 ETag
값과 현재 서버 리소스의 ETag
값이 같은지 확인하고, 같으면 캐시된 리소스를 사용한다.보통 위의 두 쌍의 헤더를 동시에 사용한다. 둘 중 한 쌍만 사용했다가 매칭되는 응답 헤더가 없다면, 재사용할 수 있어도 리소스를 다시 받아와야 하는 경우가 생길 수 있기 때문이다.
트리쉐이킹(Tree Shaking)은 말 그대로 나무를 흔들어 잔가지를 털어내듯 불필요한 코드를 제거하는 것을 의미한다. 이런 불필요한 코드들을 찾아내어 제거하면 웹 사이트 성능 최적화에 큰 도움이 된다. 특히 JavaScript를 트리쉐이킹 해줘야 하는 이유는 다음과 같다.
JavaScript 파일의 크기
기술이 발달하며 JavaScript 파일의 크기는 이전에 비해 크기가 많이 증가하였다. 때문에 JavaScript 파일을 요청하는 HTTP 요청 수 또한 증가하면서 네트워크 리소스 소모도 그만큼 커지게 되었다.
JavaScript 파일 크기의 증가, 요청 횟수의 증가는 화면 표시가 늦어지게 되고, 네트워크 속도가 느리다면 다른 리소스를 불러오는 것까지 영향을 미치게 된다.
따라서 트리쉐이킹을 통해 파일 크기를 가능한 줄여야 한다.
JavaScript 파일의 실행 시간
JavaScript 파일이 실행되려면 다음의 과정을 거쳐야 한다.
요청 -> 파일 다운로드 -> 압축 해제 -> JavaScript 코드 파싱 -> 컴파일 -> JavaScript 코드 실행
이처럼 코드 실행까지 거쳐야 하는 과정이 많기 때문에 JavaScript는 다른 리소스에 비해서 실행까지 상대적으로 많은 시간을 소모하게 된다. 이렇게 시간이 많이 소모되게 되면 CPU에 영향을 받게 되고, PC와 모바일의 상황에 따라 렌더링 시간도 길어지게 된다. 결국 이는 Ch2의 에서 언급한 사용자의 이탈율을 증가시키게 된다.
웹팩 4버전 이상에서는 ES6 모듈(import, export 사용 모듈)을 대상으로 기본적인 트리쉐이킹을 제공하고 있다. Create React App을 통해 만든 React 애플리케이션도 웹팩을 사용한다.
이렇게 웹팩을 사용하는 환경에서 효과적으로 트리쉐이킹을 수행하는 방법에 대해서 알아본다.
필요한 모듈만 import 하기
import 구문 사용시 라이브러리 전체가 아니라 필요한 모듈만 불러온다.
Babelrc 파일 설정하기
Babel은 자바스크립트 문법이 구형 브라우저에서도 호환이 가능하도록 ES5 문법으로 변환하는 라이브러리다. 이 때 ES5문법은 import를 지원하지 않아 commonJS 문법의 require로 변경시키는데, 이 require는 export 되는 모든 모듈을 불러온다.
결국 이는 필요한 모듈만 불러오는 게 아니라 전체를 불러오게 된다.
이를 방지하기 위해 Barbelrc 파일에 다음과 같이 설정하면 ES5로 변환하는 것을 막을 수 있다.
{
“presets”: [
[
“@babel/preset-env”,
{
"modules": false
}
]
]
}
반대로, modules 값을 true로 설정하면 항상 ES5 문법으로 변환하므로 주의해서 작성해야 한다.
{
"name": "tree-shaking",
"version": "1.0.0",
"sideEffects": false
}
위와 같이 false
로 작성하면 앱 전체에서 사이드 이펙트가 발생하지 않을 것이라고 알려주는 것이다. false
대신 특정 파일 링크를 아래처럼 작성할 수도 있다.
{
"name": "tree-shaking",
"version": "1.0.0",
"sideEffects": ["./src/components/NoSideEffect.js"]
}
Lighthouse는 구글에서 개발한 오픈소스로 웹 페이지의 품질을 개선할 수 있는 자동화 툴이다. 다양한 지표를 이용하여 웹페이지의 성능 검사를 해줄 뿐만 아니라 그에 대한 개선책도 제공해준다.
Lighthouse는 Chrome 개발자 도구나 CLI, 노드 모듈 등 다양한 방법으로 사용할 수 있다.
검사시 검사할 페이지의 url을 Lighthouse에 전달하면 해당 페이지에 대한 여러 검사가 실행되고, 검사 결과에 따른 리포트를 생성한다.
Chrome 개발자 도구에서 실행하기
Node CLI에서 실행하기
npm install -g lighthouse
로 Lighthouse를 설치한다.-g
옵션을 사용해 전역 모듈로 설치하는 것이 좋다.lighthouse <url>
명령어로 검사를 실행한다.lighthouse --help
명령어로 모든 옵션을 볼 수 있다.Lighthouse 노드 모듈
노드 모듈을 이용해 동적으로 프로그래밍하여 페이지 검사 리포트를 생성할 수도 있다. 이를 이용하면 성능 테스트를 자동화할 수 있다.
다음은 www.naver.com을 검사한 Lighthouse 리포트이다.
해당화면은 위에서 언급했듯이 크롬 개발자도구로도 확인할 수 있고 프린트할 수도 있다.
각 항목에 대한 상세한 내용은 리포트의 하단에서 확인할 수 있다.
<meta>
요소는 잘 작성되어 있는지Lighthouse 리포트를 보면(위 이미지의 하단) METRICS 항목이 있다.
각각의 메트릭이 어떤 것을 측정하는 지 다음과 같다.
First Contentful Paint : 첫 번째 부분을 렌더링하는 데 걸리는 시간을 측정
FCP는 사용자가 페이지에 접속했을 때부터 브라우저가 DOM 컨텐츠의 첫 번째 부분을 렌더링하는 데 걸리는 시간을 측정한다. 우수한 사용자 경험을 제공하려면 FCP가 1.8초 이하여야 한다.
여기서 DOM 컨텐츠란 페이지의 이미지, <canvas>
, SVG 등도 포함되며 <iframe>
는 포함하지 않는다.
성능(Performance) 지표를 추적하는 매트릭이다.
Largest Contentful Paint : 가장 큰 콘텐츠의 렌더 시간 측정
여기서 가장 큰 콘텐츠란 뷰포트를 차지하는 이미지 또는 텍스트 블록을 의미한다.
이 콘텐츠를 주요 콘텐츠로 보고 이것이 유저에게 보이는 시간까지를 측정한다.
일부 콘텐츠의 첫 번째 렌더링 시점을 측정하는 FCP와는 다르게 주요 콘텐츠 로딩이 '완료'된 시점을 측정한다.
LCP 점수 기준은 다음과 같다.
LCP time(in seconds) | Color-coding |
---|---|
0-2.5 | Green (fast) |
2.5-4 | Orange (moderate) |
Over 4 | Red (slow) |
Speed Index : 페이지를 로드하는 동안 컨텐츠가 얼마나 빨리 표시되는지 측정
라이트하우스는 먼저 브라우저의 페이지 로딩과정을 각 프레임마다 캡쳐하고,
프레임 간 화면에 시각적으로 보이는 요소들을 계산한다.
이후 Speedline Node.js module을 이용해 Speed Index 점수를 그래프의 형태로 나타낸다.
성능(Performance) 지표를 추적하는 매트릭이다.
점수 기준은 다음과 같다.
Speed Index(in seconds) | Color-coding |
---|---|
0–3.4 | Green (fast) |
3.4–5.8 | Orange (moderate) |
Over 5.8 | Red (slow) |
Time to interactive : 페이지가 로드되는 시점부터 상호작용이 가능한 시점까지의 시간을 측정
TTI는 페이지가 완전히 상호 작용 가능하기까지의 시간을 측정하는데 그 기준은 다음과 같다.
- 페이지에 FCP로 측정된 컨텐츠가 표시되어야 한다.
- 이벤트 핸들러가 가장 잘 보이는 페이지의 엘리먼트에 등록된다.
- 페이지가 0.05초안에 사용자의 상호작용에 응답한다.
TTI 점수는 아카이브된 HTTP 데이터를 기반으로, 백분위 단위로 점수를 측정한다.
점수 기준은 다음과 같다.
TTI metric(in seconds) | Color-coding |
---|---|
0–3.8 | Green (fast) |
3.9–7.3 | Orange (moderate) |
Over 7.3 | Red (slow) |
Total Blocking Time : 유저와 상호작용하기까지의 막혀있는 시간을 측정
대부분의 사용자는 0.05초가 넘는 작업에는 페이지가 느리다고 인식하고, 응답이 올때까지 키보드를 두드리거나 마우스를 클릭한다. 이러한 시간을 '막혀있다(Blocking)'고 한다.
Lighthouse에서는 FCP와 TTI 사이에 긴 시간이 걸리는 작업들을 모두 기록하여 TBT를 측정한다.
다음과 같은 내용으로 측정한다.
Cumulative Layout Shift : 컨텐츠가 화면에서 얼마나 많이 움직이는지(불안정한 지)를 수치화
'얼마나 많이 움직이는지(불안정한 지)'는 다음과 같은 상황을 의미한다.
페이지를 살펴보던 중 갑자기 페이지 일부분이 바뀌거나, 아무런 경고 없이 텍스트가 움직이거나, 링크나 버튼을 탭하려고 할 때 링크가 갑자기 움직여서 의도하지 않은 광고 링크 등을 클릭하는 경우 등등
이런 경우는 결제 같은 상황에서는 상당히 크리티컬한 경우가 되므로 해당 지표를 통해 개선할 수 있어야 한다.
Lighthouse는 성능만 측정하지 않고 무엇이 시간을 얼마나 소모하는지, 어떻게 개선해야할지 해결책도 제시한다.
Opportunities 항목을 확인하면 위에서 언급한 각 메트릭별 문제에 대한 설명과 어떤 코드에서 확인했는지 등을 함께 확인할 수 있다.
이런 Lighthouse의 기능을 이용하면, 웹 성능 최적화와 웹 접근성, 웹 표준, SEO 관련 항목도 확인하고 해결책을 얻을 수 있다.
1. HTTP 아카이브
🔗 https://httparchive.org/reports/loading-speed#ttci