프론트엔드 개발자에게 필요한 능력에는 어떤 것들이 있을까?
사용자 관점에서 애플리케이션을 설계하고, 디자이너와 백엔드 개발자 사이에서 일 하기에 커뮤니케이션 능력을 필요로 한다.
물론 위의 두 가지 역량도 굉장히 중요하지만 기술적인 부분이나 성능 개선을 위한 지식을 보유하는 것도 중요하다.
최근에 성능 최적화에 관심이 생겨 프론트엔드 개발자가 성능을 최적화하기 위해 적용할 수 있는 기술들에 대해 정리하고, 일부 기능은 최근에 진행한 여행 가계부 애플리케이션 'Shall we trip?'에 적용해 보려고 한다 🙂
Lighthouse와 개발자 도구의 network 탭을 사용해 성능을 측정하였다
사용자가 처음 애플리케이션을 사용할 때도 좋은 성능을 제공하고 싶었기 때문에 network 탭의 Disable cache 버튼을 체크해서 캐시를 사용하지 않는 환경에서 테스트를 진행하였다
성능 최적화 적용 이전 화면
Lighthouse 결과
FOUT(Flash of unstyled text) 현상에 의해 심각한 레이아웃 변화가 발생하고, boxicon도 마찬가지로 완전히 로드되기 전까지 화면에 제대로 보이지 않는 상황이다.
브라우저가 DOM 콘텐츠의 첫 번째 부분을 렌더링하는 데 걸리는 시간을 나타내는 지표인 FCP나, 뷰포트에 포함된 모든 HTML 요소들이 브라우저 화면에 렌더링 완료되는데까지 걸리는 시간을 나타내는 지표인 LCP도 개선이 필요함을 느꼈다
이제 성능 최적화를 통해 기존의 프로젝트를 개선해보자! 🤔
HTML을 파싱할 때, CSS나 JavaScript 파일을 만나게 되면 파싱을 멈추고 해당 파일을 파싱하거나 다운로드 후 실행하게 되는데, 이처럼 HTML 파싱을 차단하는 요소를 블록 차단 리소스라고 한다.
<head>
태그 안에 임포트해야 하며, <script>
태그로 실행되는 js는 일반적으로 <body>
맨 하단에 위치시킨다.defer
나 async
어트리뷰트를 이용해서 블로킹을 방지할 수 있다. 다만 defer
의 경우 IE9 이하에서 문제를 발생시키고, async
의 경우 DOM 생성 도중 파일을 다운로드 받고 실행까지 시켜서 문제를 발생시킬 수 있으므로 사용에 주의해야 한다.Cumulate Layout Shift는 누적 레이아웃 변화의 약자다. 어떤 페이지에 들어갔을 때 갑작스럽게 발생하는 레이아웃 이동의 정도를 합산 이동 거리라는 개념을 도입해서 만들어낸 지표이다. 늦게 로딩되는 리소스에 의해 레이아웃이 변화하는 현상을 최소화하는 것은 중요하다.
여행 가계부 애플리케이션인 'Shall We Trip'에 CLS를 낮추기 위한 방법 고민했다.
가장 먼저 발견한 것은 폰트와 boxicon이 @import
방식으로 css 파일에 들어가 있었다는 것이다.
@import
방식은 성능을 생각한다면 좋은 방법이 아니다. link 태그는 로딩 시 병렬방식으로 다운로드 하는 반면, @import
방식은 직렬 방식으로 다운로드 하기 때문에 로딩시간이 길어지는 문제점이 발생한다.
또한 @import
방식은 edge 브라우저에서 제대로 동작하지 않는다는 문제점이 존재한다.
link 태그는 병렬방식으로 다운로드 하여 로딩속도가 빠르고, 여러개를 link 방식으로 다운로드 하더라도 익스플로러에서 순서가 동일하게 작동한다.
preload는 브라우저에게 페이지에서 필요한 자원을 일찍이 fetch 하라는 지침이다.
폰트는 반드시 사용할 자원이기에 preload 속성을 넣어주었다.
그리고 폰트를 가져오는 사이트들에 preconnect 속성을 주어 브라우저가 외부의 도메인과 미리 연결을 할 수 있도록 했다.
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;800&family=Noto+Sans+KR:wght@400;500;700&display=swap');
@import url('https://unpkg.com/boxicons@2.0.9/css/boxicons.min.css');
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
rel="preload"
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;800&family=Noto+Sans+KR:wght@400;500;700&display=swap"
as="font"
/>
CLS 최적화 결과
Lighthouse 결과
이전과는 다르게 다시 페이지를 불러와도 레이아웃 변화가 발생하지 않고, CLS가 엄청나게 개선되었다.
단순하게 폰트의 로드 방식만 바꿔줬을 뿐인데, 한 눈에 알아볼 수 있을 정도로 크게 개선되었다.
리소스의 용량을 줄임으로써 리소스 다운로드 시간을 최적화할 수 있다
압축 - 들여쓰기와 공백이 제거되고, 전체 코드가 한 줄로 병합된다. 원본 코드에서 들여쓰기, 공백, 콤마 등이 제대로 사용되지 않았다면 문제가 발생할 수 있다.
난독화 - JS 코드 자체를 분석하기 어렵게 만드는 과정. 난독화 단계를 높일수록 코드를 해석하고 실행하는 속도가 느려질 수 있다.
import { debounce } from 'lodash';
import debounce from 'lodash/debounce';
Lighthouse 결과
lodash에서 필요없는 코드를 제거하고 사용하는 기능만 받아서 JS 파일 용량을 최적화했다
그 결과 expense.bundle.js의 파일 용량이 약 절반으로 줄어들었고, 성능이 개선되었다
CSS 파일은 약 10%, expense.bundle.js 파일은 약 5분의 1 가량으로 파일의 크기가 줄어들었다
용량이 줄어든만큼 리소스 다운로드 시간도 줄어들어 더 빠른 렌더링을 기대할 수 있다
<video
src={imageSrc}
type='video/mp4'
autoPlay
muted
loop
playsInline
/>
위와 같이 작성하면 마치 GIF처럼 보이게 할 수 있다.
리소스 요청 개수 자체를 줄여서 사용자에게 더 빠르게 페이지를 보여줄 수 있는 성능 최적화
여러 개의 이미지를 하나의 이미지로 합쳐서 관리하는 이미지
웹 페이지에 이미지가 사용될 경우 해당 이미지를 다운받기 위해 브라우저는 서버에 이미지를 요청하게 된다.
이미지 스프라이트를 사용하면 여러 이미지를 다운로드 받아도 서버 요청을 줄일 수 있다.
-> 로딩 시간 단축
스프라이트 이미지를 다운받아 background-position 속성을 조절하여 여러 이미지를 사용할 수 있다.
웹 페이지를 열면 브라우저가 모든 이미지를 읽고 불러와서 렌더링 할 것이다.
만약 많은 이미지가 한 페이지에 있다면 그 페이지를 여는 시간도 오래 걸릴 것이고 모바일 환경에서 접속한다면 수많은 데이터를 낭비하게 될 것이다.
이러한 상황을 방지하기 위해 스크롤이 하단에 닿았을 때, 동적으로 데이터를 요청하고 화면에 렌더링하도록 구현하였다.
이와 같은 방식으로 구현하면 처음 화면을 렌더링 할 때 로딩 시간을 단축시킨다
Lighthouse 결과
성능 최적화를 이론으로만 학습한 것이 아니라 실제로 진행했던 프로젝트에 적용하여 결과를 내었기에 더욱 뜻깊었던 시간이었다. 성능을 높이는 데 있어서 다양한 관점으로 바라볼 수 있는 시각을 키운 것 같고, 다음에 또 비슷한 문제를 겪게 된다면 이번에 했던 경험이 큰 도움이 될 것이다.
아쉬운 점이 있다면 성능 최적화 관점에서 가장 관심을 가졌던 웹 캐시를 적용하지 못했다는 것이다. 비교적 볼륨이 있고 웹 캐시만 따로 다뤄보고 싶어서 잠시 미뤄뒀는데, 조만간 웹 캐시에 대해 학습하고 적용하는 시간을 가질 예정이다. 또한 주로 SPA에서 사용하는 개념이라 이번 프로젝트에 적용하지 못한 코드 스플리팅에 대해서도 배워보고 싶다.
reference
https://coffeeandcakeandnewjeong.tistory.com/34
프론트엔드 성능 최적화
많은 도움이 됐습니다. 좋은 글 감사합니다!