Bottleneck 코드 최적화 1 - 작업양 줄이기
Bottleneck 코드 찾기
크롬 Performance
를 활용해서 페이지가 렌더링될 때 각 컴포넌트 등의 실행시간을 체크해볼 수 있다.
이를 활용해 실행이 오래 걸리는 함수, 컴포넌트 등 Bottleneck 코드를 찾을 수 있다.
여기서 보면, Article
컴포넌트 mount 작업이 오래걸리는데, 아래에서 상세한 수행내역을 보면 removeSpecialCharacter
함수가 실행시간을 다 차지하고 있는 것을 확인할 수 있다.
이런식으로 Bottleneck
코드를 찾을 수 있다.
Bottleneck 코드 분석
/*
* 파라미터로 넘어온 문자열에서 일부 특수문자를 제거하는 함수
* (Markdown으로 된 문자열의 특수문자를 제거하기 위함)
* */
function removeSpecialCharacter(str) {
const removeCharacters = ['#', '_', '*', '~', '&', ';', '!', '[', ']', '`', '>', '\n', '=', '-']
let _str = str
let i = 0,
j = 0
for (i = 0; i < removeCharacters.length; i++) {
j = 0
while (j < _str.length) {
if (_str[j] === removeCharacters[i]) {
_str = _str.substring(0, j).concat(_str.substring(j + 1))
continue
}
j++
}
}
return _str
}
코드를 보면, 각 특수문자별로 for
문을 돌고, 매개변수로 받은 str
의 각 글자를 검사해서 해당 특수문자이면 제거하는 식으로 되어 있다.
불필요하게 for
문을 2번 돌고 있다.
최적화하기
- 효율적으로 제거 -
replace
와 정규식을 활용한다면 가독성도 좋고, 실행시간도 적은 코드로 바꿔줄 수 있다. - 작업양 줄이기 -
summary
에 보여지는 글자는 최대 300자이기 때문에 전체str
을 검사할 필요가 없다. 300자까지만 검사하도록 한다.
function removeSpecialCharacter(str) {
return str.substring(0,300).replace(/[\#\_\*\~\&\;\!\[\]\`\>\n\=\-`]/g, '')
}
얼마나 빨라졌는지 보기
아까는 1초가 넘게 걸리던 작업이 28ms로 줄었다.
Lighthouse 점수도 이전 45점에서 52점으로 늘었다.
Total Blocing Time
이 1초에서 70ms로 매우 줄었다.
Bottleneck 코드 최적화 2 - memoization
Bottleneck 코드 찾기
크롬 Performance로 오래 걸리는 코드를 찾아본다.
모달의 이미지를 기초로 배경색을 계산하는 getAverageColorOfImage
가 매우 오래 걸리는 것을 알 수 있다.
Bottleneck 코드 분석
export function getAverageColorOfImage(imgElement) {
const canvas = document.createElement('canvas');
const context = canvas.getContext && canvas.getContext('2d');
const averageColor = {
r: 0,
g: 0,
b: 0,
};
if (!context) {
return averageColor;
}
const width = (canvas.width =
imgElement.naturalWidth || imgElement.offsetWidth || imgElement.width);
const height = (canvas.height =
imgElement.naturalHeight || imgElement.offsetHeight || imgElement.height);
context.drawImage(imgElement, 0, 0);
const imageData = context.getImageData(0, 0, width, height).data;
const length = imageData.length;
for (let i = 0; i < length; i += 4) {
averageColor.r += imageData[i];
averageColor.g += imageData[i + 1];
averageColor.b += imageData[i + 2];
}
const count = length / 4;
averageColor.r = ~~(averageColor.r / count); // ~~ => convert to int
averageColor.g = ~~(averageColor.g / count);
averageColor.b = ~~(averageColor.b / count);
return averageColor;
}
계산 작업이 복잡하고 시간이 많이 걸린다.
최적화하기
메모이제이션
메모이제이션을 활용해서 이전에 했던 작업을 반복하지 않도록 하면 오버헤드를 줄일 수 있다.
const cache = {}
export function getAverageColorOfImage(imgElement) {
if(cache.hasOwnProperty(imgElement.src)) {
return cache[imgElement.src]
}
/// 생략
cache[imgElement.src] = averageColor
return averageColor
}
리액트 컴포넌트 내부의 메소드인 경우 useMemo
를 활용할 수 있다.
따로 util 함수를 선언해서 사용하면 좋다. 클로져 활용해서 캐시 사용
// memoize.js
// 2번째 인자로 args 해싱하는 함수 넣어서 활용하면 object나 배열에도 활용할 수 있다.
function memoize(fn, hashFn) {
const cache = {}
return function(...args) {
if(args.length !== 1) {
return fn(...args)
}
const key = hashFn(args)
if(cache.hasOwnProperty(key)) {
return cache[key]
}
const result = fn(...args)
cache[key] = result
return result
}
}
'프론트엔드 > 성능 최적화' 카테고리의 다른 글
렌더링 성능 최적화 - Layout Shift 피하기 (0) | 2023.05.01 |
---|---|
렌더링 성능 최적화 - 애니메이션 (0) | 2023.05.01 |
로딩 성능 최적화 - 텍스트 압축, 불필요한 CSS 제거 (0) | 2023.04.28 |
로딩 성능 최적화 - 캐시 (0) | 2023.04.28 |
로딩 성능 최적화 - 폰트 (0) | 2023.04.28 |