- 23/08/31 스터디 면접 질문
🙋 웹사이트 성능 최적화에는 어떤 방법이 있나요?
성능 최적화를 하는 이유와 어떻게 개선되는지를 이해하기 위해선 브라우저의 렌더링 과정을 알아야한다.
브라우저 렌더링 과정
https://imnayoung.tistory.com/25
📚 성능 최적화
성능 최적화를 위해서는 웹 사이트의 `로딩 성능(Loading Performance)`과 `렌더링 성능(Rendering Performance)`을 개선해야 한다.
1) 웹 페이지 로딩 최적화
1. 블록 리소스(CSS, 자바스크립트) 최적화
브라우저 로딩 과정에서 파싱이 일어날 때 CSS 또는 자바스크립트로 인해 파싱이 중단될 수 있는데, 이때 원인이 되는 리소스를 블록 리소스(Block resource)라고 한다.
블록 리소스는 브라우저 로딩 단계 중 페인트 과정을 지연시키므로, 블록 리소스가 HTML 파싱을 막는 상황이 발생하지 않도록 해야 한다.
CSS 최적화
CSSOM 트리는 CSS를 모두 해석해야 구성됨.
즉, CSSOM 트리는 구성되지 않으면 렌더 트리를 만들지 못하고 렌더링이 차단됨.
👉 렌더링이 차단되지 않도록 항상 HTML 문서 최상단(<head> 아래)에 배치
자바스크립트 최적화
스크립트 실행이 완료될 때까지 DOM 트리 생성이 중단됨.
👉 HTML 문서 최하단(</body> 직전)에 배치
<head>
<link href="style.css" rel="stylesheet" /> // css 최적화
</head>
<body>
<div>...</div>
<script src="app.js" type="text/javascript"></script> // js 최적화
</body>
2. 리소스 용량 줄이기
용량이 큰 리소스도 웹 페이지 로딩 시간을 느리게 하는 원인이 됨. 리소스의 용량을 줄임으로써 성능을 개선할 수 있음.
- 불필요한 코드는 제거한다.
- 압축 및 난독화로 용량을 최소화 한다.
- 간결한 셀렉터를 사용한다.
- 모듈번들러(webpack) css, js 번들링한다.
- 캐싱할 필요 없는 style은 내부 스타일 시트를 사용한다.
2) 웹 페이지 렌더링 최적화
웹 페이지를 렌더링 하기 위해서는 DOM, CSS가 필요하다. 그러나 다양한 기능과 효과를 구현하기 위해선 자바스크립트를 많이 사용한다. 자바스크립트는 브라우저에서 단일 스레드로 동작하기 때문에 자바스크립트의 실행 시간은 곧 렌더링 성능과 직결된다. 결국엔 자바스크립트에서 실행되는 코드가 최적화 될 수 있게 구성해야 한다.
레이아웃 최적화: Reflow를 최소화 하는 방법
렌더링 과정에서 레이아웃은 DOM요소들이 화면에 어느 위치에서 어떤 크기로 배치될지를 결정하게 되는 계산 과정이다. 레이아웃은 글자의 크기를 일일이 계산하고 요소 간 관계를 모두 파악해야 하는 과정이므로 시간이 오래걸린다.
📌 강제 동기 레이아웃
- DOM 속성을 변경할 때 화면 업데이트를 위해 동기적인 레이아웃이 발생하는 상황을 의미.
- 이로 인해 자바스크립트 실행 시간이 증가할 수 있음.
강제 동기 레이아웃 피하기
- 강제 동기 레이아웃은 다음의 경우에 발생한다
- 요소의 스타일 속성을 변경한 후에, 해당 요소의 레이아웃 정보를 즉시 읽어올 때.
- `offsetHeight`, `offsetWidth`, `offsetTop`, `offsetLeft`, `scrollTop`, `scrollLeft` 등의 속성을 읽을 때.
- 스타일 변경 후 계산된 값을 속성으로 읽기보다 스타일 변경을 최소화하는 것이 중요하다.
const tabBtn = document.getElementById('tab_btn');
tabBtn.style.fontSize = '24px';
console.log(testBlock.offsetTop); // offsetTop 호출 직전 브라우저 내부에서는 동기 레이아웃이 발생한다.
tabBtn.style.margin = '10px';
// 레이아웃
레이아웃 스래싱(thrashing) 피하기
- 레이아웃 스레싱은 한 프레임 내에서 연속적으로 강제 동기 레이아웃이 발생하는 상황을 말한다.
- 이를 피하기 위해 반복문 안에서 스타일을 설정하고 값을 읽어오는 것보다, 반복문 밖에서 한 번만 값을 읽어오는 방식을 사용하는 것이 성능 최적화에 도움이 된다.
function resizeAllParagraphs() {
const box = document.getElementById('box');
const paragraphs = document.querySelectorAll('.paragraph');
for (let i = 0; i < paragraphs.length; i += 1) {
paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
// 레이아웃 스래싱을 개선한 코드
function resizeAllParagraphs() {
const box = document.getElementById('box');
const paragraphs = document.querySelectorAll('.paragraph');
const width = box.offsetWidth;
for (let i = 0; i < paragraphs.length; i += 1) {
paragraphs[i].style.width = width + 'px';
}
}
영향받는 노드 최소화하기 (position fixed, absolute)
- 상위 노드의 스타일을 변경하면 하위 노드에 모두 영향을 주게 된다.
- 따라서 다른 엘리먼트 레이아웃에 영향을 주지 않는 `fixed`와 `absolute` 속성을 사용하면 비용을 줄일 수 있다.
숨겨진 엘리먼트 수정
- `display: none`의 경우 Reflow와 Repaint가 발생하지 않는다.
- 많은 수의 엘리먼트를 변경해야 할 경우, 숨겨진 상태에서 변경하고 다시 보이도록하여 레이아웃 발생을 최대한 줄일 수 있다.
- 주의: `visibility: hidden`의 경우 보이지 않기 때문에 Repaint는 발생하지 않지만, 공간을 차지하기 때문에 Layout은 발생하게 된다.
transform, opacity 속성 사용하기
- 두 속성은 reflow와 repaint가 일어나지 않는다.
- left, right 대신 `transform` 속성을 사용하고
- visibility, display 대신 `opacity` 속성을 사용하면 reflow, repaint 발생을 최소화 할 수 있다.
Reference
🌟 https://ui.toast.com/fe-guide/ko_PERFORMANCE
https://devocean.sk.com/blog/techBoardDetail.do?ID=164863&boardType=techBlog