[모모] 라이트하우스 성능 개선기
라이트하우스?
구글에서 개발한 웹 페이지의 품질을 개선할 수 있는 오픈소스 형태의 자동화 도구입니다.
Lighthouse is an open-source, automated tool for improving the performance, quality, and correctness of your web apps.
다음과 같은 지표를 확인할 수 있습니다.
Performance
1. Fist Contentful Paint(FCP)
- 브라우저가 첫 번째 콘텐츠를 랜더링 하는 데 걸리는 시간
- 웹 페이지 초기 로딩 속도
2. Largest Contentful Paind(LCP)
- 가장 큰 크기의 콘텐츠를 랜더링 하는 걸리는 시간
- 사용자가 주요 콘텐츠를 볼 수 있을 때까지 걸리는 시간
3. Total blocking Time(TBT)
- 첫 번째 DOM이 랜더링 되고 사용자와 상호작용할 수 있는 시간(Time To Interactive, TTI) 사이의 시간
- 사용자의 입력에 반응하지 않는 시간 측정
4. Cumulative Layout Shift(CLS)
- 웹 페이지가 로딩되는 동안 뷰포트 내에 보이는 요소들 움직임 측정
- 예를 들어 로드 전 이미지 자리에 글자들이 먼저 로드되었다가 이미지가 로드되면 아래로 내려가는 현상
5. Speed index
- 웹 페이지의 시각적인 로딩 속도
- 모든 요소(텍스트, 이미지, 비디오 등)가 화면에 나타날 때까지 걸리는 시간기반으로 계산
Accessbility
웹 접근성을 의미하며 모든 사용자에게 동일한 서비스를 제공합니다.
Best Practice
웹 개발 및 디자인에서 권장되는 가이드라인 및 규칙을 나타냅니다.
SEO
검색 엔진 최적화는 웹 사이트가 검색 엔진에서 높은 순위로 표시되고 노출되도록 최적화하는 과정을 의미합니다.
결과 미리 보기
🛑 Before
✅ After
✔️ 제가 담당한 게시글 목록 리스트페이지 (/list) 기준입니다.
✔️ performance는 네트워크 환경에 따라 점수가 오르락내리락 하지만 계속 초록불은 유지되네요 :)
Perfomance
1. 코드 스플리팅 / react lazy, Suspense 사용하기
문제 원인
네트워크 비용을 줄이려 모듈 번들러를 사용하지만
프로젝트가 커지고 번들링 파일 크기도 커지면서
로드하는데 시간이 많이 걸리게 됩니다.
이 말인즉슨 페이지를 로드하는데 시간이 걸리고
사용자 경험을 저해하게 됩니다.
코드 스플리팅은 이러한 상황을 방지하기 위해 존재하는 방법입니다.
하나의 번들 파일을 여러 개로 나눈 뒤 실제로 필요한 번들 파일만 불러오고
나머지 번들 파일들은 호출을 지연시켜 빠른 속도로 화면을 로드하게 도와줍니다.
해결
React의 lazy API를 이용해서 페이지 컴포넌트를 미리 로딩하지 않고
코드를 분할하여 필요하지 않은 코드들을 불러오지 않습니다.
필요한 컴포넌트가 로딩되는 동안 빈 화면 대신
Suspense 컴포넌트의 fallback에 정의된 컴포넌트를 보여주어
사용자 경험을 향상할 수 있습니다.
import { Route, Routes } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import Loading from './common/components/Loading';
const Home = lazy(() => import('./pages/Home/views/Home'));
const Signup = lazy(() => import('./pages/Signup/views/Signup'));
const Login = lazy(() => import('./pages/Login/views/Login'));
const User = lazy(() => import('./pages/User/views/User'));
const Lists = lazy(() => import('./pages/Lists,Search/views/Lists'));
const Router = () => {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/user/:memberId" element={<User />} />
<Route path="/lists" element={<Lists />} />
</Routes>
</Suspense>
);
};
export default Router;
2. Reduce Unused Javascript / @react/all-files로 대체하기 (적용 안 함)
문제 원인
개발자 도구에서 Coverage 탭을 보면
사용하지 않는 코드들을 분석할 수 있습니다.
대부분 react-icons 라이브러리로 나타나는데
react-icons 라이브러리 파일을 보면
아이콘 종류별로 하나의 js 파일에 아이콘 전체를 포함하고 있어
위에서 언급한 번들 사이즈가 큰 문제가 나타났습니다.
그래서 대안으로 하나의 아이콘마다 개별 js 파일을 가진
@react-icons/all-files를 사용해서 빌드시 트리쉐이킹이 되어
더 작은 크기의 chunk가 만들어집니다.
그런데 해당 라이브러리를 다운로드하고 실행해 보았으나
제가 사용한 아이콘 중 일부는 지원하지 않는 문제가 있었습니다.
해당 문제를 해결하기 위해 react-icons
레포지토리 이슈탭을 확인했고
저와 같은 이슈들이 작성되어 있지만 답변을 확인할 수 없었습니다.
해결?
그러다 static import문을 사용하면
프로덕션 빌드에서는 명시적으로 가져오지 않는 ES6 모듈에 대해
트리쉐이킹을 한다는 글을 보았습니다.
import { MdCancel } from 'react-icons/md';
배포 환경에서 라이브하우스를 측정한 결과
Coverage 탭에서 사용되지 않은
react-icons 파일이 없음을 확인할 수 있었습니다.
3. Ensure text remains visible during webfont load / 웹폰트 최적화
문제 원인
폰트 파일을 로드하는 동안 해당 글자가 보이지 않는 현상이 일어납니다.
브라우저 랜더링시 돔 트리가 형성된 다음에
폰트는 나중에 요청되기 때문입니다.
해결
1. 폰트 용량 줄이기
확장자 변환
기존에 사용하고 있던 폰트 확장자는 ttf 형식이었습니다.
확장자 별로 압축률이 다른데 그중 woff2 형식이 가장 가볍습니다.
폰트를 제공하는 사이트에서 woff2를 제공하지 않았지만
폰트 파일 확장자 변환 서비스를 제공하는 사이트를 통해
woff2 파일을 사용할 수 있었습니다.
서브셋폰트 사용
불필요한 글자를 제거하여 폰트 파일 사이즈를 줄이는 방법입니다.
이러한 서비스를 제공하는 사이트도 있습니다.
하지만 작성되지 않은 글자의 불편함으로
지금의 폰트를 사용하기에 이 부분은 사용하지 않았습니다.
2. font-display: swap 속성 사용하기
이 속성은 웹 폰트를 사용할 때 임시로 브라우저에 미리 설치된
시스템 글꼴을 사용하고 웹 폰트 로드가 완료되면
표시된 텍스트를 폰트로 교체하여 사용자에게 빠르게 텍스트 렌더링을 제공합니다.
폰트 최적화 총 결과
FCP가 1.4s에서 0.5s으로 거의 3배 줄었습니다!
4. Properly size images / 이미지의 width , height 명시적 작성
문제 원인
이미지와 동영상 요소가 로드되는 동안 브라우저는 요소의 크기만큼
자리를 미리 선점해 두어 브라우저 랜더링 과정시 많은 비용을 차지하는
리플로우와 레이아웃 재배치를 최소화할 수 있습니다.
width와 height 속성을 명시적으로 작성이 되어있지 않다면
브라우저는 요소의 크기를 알 수 없으니 공간을 할당해 두지 않았다가
뒤늦게 갑자기 로드되는 이미지나 동영상 요소 때문에
사용자 경험을 저해할 수 있습니다.
라이트 하우스 설명란에 연결되어 있는 페이지의 영상을 보시면 이해가 더욱 쉬울 것입니다.
해결
당연하게도 img 요소에 명시적으로 height와 width 속성을 작성해 줍니다.
<img width={300} height={300} src={cryingMomo} alt="error" />
Accessbility
1. Select elements do not have associated label elements
select, button의 요소들은 사용자와의 인터렉션에 중요하므로
어떤 역할을 하는지 명시를 해주어야 합니다.
<select aria-label="select-city" id="city" value={city} onChange={handleCityChange}>
2. Background and foreground colors do not have a sufficient contrast ratio
글자와 배경의 색상이 비슷해 일부 사용자들은 글자를 읽는데 어려움이 있을 수 있습니다.
확실한 대비와 강조의 효과를 주었습니다.
3. Image elements do not have [alt] attributes
모든 이미지에 alt속성을 이용해서 명시적으로 이미지에 대한 설명을 작성해 줍니다.
<img width={300} height={300} src={cryingMomo} alt="no-data" />
SEO
1. Does not have a <meta name="viewport"> tag with width or initial-scale
index.html에서 meta 태그로 웹페이지 정보를 명시적으로 작성해 줍니다.
해결
<meta
name="description"
content="지역과 카테고리별로 모임을 만들고 채팅을 할 수 있는 서비스 입니다."
/>
2. robots.txt is not valid
라이트하우스 성능 개선을 하면서 가장 애를 먹었던 부분입니다.
커밋을 얼마나 했는지....
🤖 robots.txt란
The robots.txt file tells search engines which of your site's pages they can crawl. An invalid robots.txt configuration can cause two types of problems:
- It can keep search engines from crawling public pages, causing your content to show up less often in search results.
- It can cause search engines to crawl pages you may not want shown in search results.
robots.txt 파일은 검색 엔진에 크롤링이 가능한 페이지를 알려줍니다.
잘못된 robots.txt 는 두 가지 문제를 일으킬 수 있습니다.
- 검색 엔진이 크롤링하지 못하게 하여 검색 결과에 덜 자주 표시됩니다.
- 검색 엔진이 검색 결과에 표시하지 않으려는 페이지를 크롤링할 수 있습니다.
즉, robots.txt를 구성하여
검색엔진이 어플리케이션을 크롤링해서
검색결과에 반영을 하게 해 주어
검색 엔진 최적화 (SEO)를 할 수 있습니다.
robots.txt 작성법
//robots.txt
User-agent: *
Allow: /
Sitemap: https://letsmomo.netlify.app/sitemap.xml
User-agent
검색 엔진을 명시적으로 작성할 수 있습니다. ex)
Googlebot
저는 *를 사용하여 모든 검색 엔진을 허용하였습니다.
Allow
특정 페이지만 크롤링할 수 있게 허용합니다.
저는 / 을 사용하여 모든 페이지를 허용하였습니다.
Disallow
특정 페이지를 크롤링하지 못하게 막을 수 있습니다.
Sitemap
사이트맵을 구성하여 검색 엔진에게 페이지에 대한 정보를 제공합니다.
구글 검색 엔진은 이 파일을 읽고 사이트를 더 효율적으로 크롤링합니다.
대게 큰 규모의 사이트에서 효율적인 크롤링을 위해 사용하는 것을 권장한다고 합니다.
저는 url을 입력하면 xml파일을 만들어주는 사이트를 사용하였습니다.
robots.txt 에는 절대경로를 적어주면 됩니다.
잘 적용되었는지 확인하려면
브라우저 창에서 url 뒤에 robots.txt 또는 sitemap.xml을 입력해보면 됩니다.
구글 robots.txt입니다. Disallow가 정말 많네요
구글의 sitemap입니다. 페이지 별로 sitemap이 있군요!
robots.txt를 모두 작성한 후 바로 접근이 가능하게 root 폴더에 위치시키면 되는데
저는 url에 포함되지 않는 public 폴더에 위치시켰습니다.
SEO 100점을 달성하였습니다!
그리고 뒤늦게 안 사실,
프론트엔드는 netlify로 배포를 했는데
특정 파일 작성 시 netlify에서 빌드시 자동으로 robots.txt를 작성해 준다고 합니다.
머리가 나쁘면 머리가 고생합니다 (?)
미래를 위해 링크를 남깁니다.
마무리
네 개의 항목이 모두 100점이 되면 폭죽을 터뜨려준다는데
ALL 100을 맞지 못해 아쉽지만 이대로 종료해보도록해보겠습니다.
성능 개선을 하는 동안 블로그에 꾸준히 임시저장을 해두었는데
설명을 하면서 알아가는 게 역시 더 많은 것 같습니다.
Ref
성능 최적화 기록
라이트 하우스 지표 기반 성능 최적화 기록
velog.io
https://velog.io/@leyuri/robots.txt-%EC%86%8C%EA%B0%9C%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95
robots.txt 소개와 사용법
aws에 front 서버를 배포하기 위해 aws s3 에 버킷에 객체를 저장하기 위해, 프로젝트를 build & export 하는 과정을 거쳤다. 이후 export한 파일들 중 public 폴더 내 robots.txt 라는 파일을 발견했다.
velog.io
SEO 최적화 - 사이트맵 만들기
사이트맵에 대해서 알아보자🌟
velog.io
https://fourward.co.kr/blog/what-is-robots-txt-and-sitemap-xml
Robots.txt와 Sitemap.xml 개념 이해 및 활용 방법 | 포워드스퀘어
테크니컬 SEO의 가장 기본이 되는 Robots.txt와 Sitemap.xml 파일을 통해, 나의 웹 사이트가 더 효율적으로 검색엔진에 크롤링 될 수 있도록 설정할 수 있습니다. 크롤러들을 제어하고 페이지 리스트를
fourward.co.kr