Sua Blog
Lighthouse를 참고하여 성능 개선한 과정과 결과를 적어두려한다.
기존 퍼포먼스 점수가 상당히 낮았었는데 특히 LCP(Largest Contentful Paint) 점수가 낮았다. 큰 자바스크립트 파일과 용량이 큰 이미지 때문이었다.
크게 수정한 부분은 세가지 정도이다.
- 이미지 로드 프로토콜 및 사이즈 변경
- 쿼리, 리졸버 최적화
- js파일 용량 줄이기
배너 이미지 배수 변경
기존 배너에 2배수 짜리 이미지가 들어가 있었다.
디자이너, 운영팀에 얘기하여 이미지를 1배수 이미지(893KB > 174KB)로 변경하였다.
img태그에 "loading=lazy" 속성 적용
<img /> 태그의 loading속성은 브라우저가 이미지를 즉시 로드해야 하는지, 아니면 사용자가 이미지 근처로 스크롤할 때까지 오프 스크린 이미지 로드를 연기해야 하는지 여부를 지정한다.
로딩 지연된 이미지들이 다운로드될 때 다른 감싸고 있는 콘텐츠 내용들이 밀려나는 것을 방지하려면, 반드시 height와 width 속성을 <img />태그에 추가하거나 inline style로 직접 값을 지정해야 한다.
<img loading="lazy" width="400" height="250" />
HTTP2으로 프로토콜 변경
HTTP1.1(웹 상에서 클라이언트와 웹서버간 통신을 위한 프로토콜)은 199년 출시 이후 지금까지 가장 많이 사용되고 있는 프로토콜이다.
HTTP1.1은 기본적으로 연결당 하나의 요청과 응답을 처리하기 때문에 다수의 리소스를 처리하기에 속도와 성능이슈를 가지고 있다. 여러 리소스를 처리하려면 리소스 개수에 비례하여 대기시간(Latency)가 길어지고 불필요한 RTT증가와 네트워크 지연을 초래한다.
HTTP2는 1.1을 수정한 버전이다. End-user가 느끼는 Latency나 네트워크, 서버 리소스 사용량 등과 같은 성능위주의 개선이 이루어졌다.
connection한개로 동시에 여러 메세지를 주도 받을 수 있고(출처 별로 최대 128개 병렬 요청 처리 가능) 응답은 순서에 상관없이 stream으로 주고받는다.
기존에 이미지들을 로드할 때 HTTP 1.1 프로토콜을 사용하여 가져왔었다.

AWS Cloud Front는 자체적으로 HTTP2 프로토콜을 사용할 수 있는 기능을 제공해주고 있기 때문에 간단하게 HTTP2를 적용할 수 있다.
기존에 사용하고 있던 Cloud Front 주소가 있어 이미지를 가져오는 주소를 Cloud Front쪽으로 변경하고 Cloud Front 설정 중 Supported HTTP Versions 설정을 HTTP2를 사용할 수 있도록 수정하였다.

script에 defer, async 속성추가
브라우저가 HTML을 파싱할 때 <script>태그를 만나면 랜더링을 중단하고 script를 로드하고 실행한다. 스크립트가 랜더링 보다 우선되지 않아도 되거나 중요하지 않은 경우 defer나 async 속성을 사용하여 해결할 수 있다.
defer속성이 있는 스크립트는 스크립트를 다운로드 하는 도중에도 HTML 파싱이 멈추지 않고 페이지 구성이 완료되면 실행된다.
async 속성이 있는 스크립트는 defer와 같이 백그라운드에서 다운로드 되지만 실행중에는 HTML 파싱이 멈춘다. 실행 순서가 중요하지 않은 경우 적용하여 성능개선을 할 수 있어 다른 스크립트와 의존성이 없는 마케팅 스크립트(GTM, GA 등)들에 async속성을 적용하였다.
Graphql 쿼리 분기
모든 페이지에서 유저의 데이터를 체크하여 Redirect 시키거나 라우팅을 하는 코드가 있었다. 유저 정보를 가져오는 쿼리 하나로 많은 부분에 사용하고 있어 해당 쿼리에는 다양한 곳에서 필요한 많은 리졸버들이 들어있었다.
Grahql의 장점인 클라이언트에서 원하는 데이터만 요청한다를 사용하지 못하고 있었고 불러오는 데 오래걸리는 리졸버도 있어서 라우터 별로, 페이지마다 호출하고 있던 쿼리도 분기하였다.
메인페이지의 하단 데이터는 마운트 후 Fetch
메인 페이지 로드 시 느리게 느껴지는 이유 또한 필요한 데이터를 useQuery를 사용하여 모두 호출하고 있어 더욱 느리게 느껴졌던거 같다.
가장 먼저 보여야하는 배너와 상품들만 useQuery를 사용하고 스크롤을 내려야 보이는 페이지 하단에서 필요한 데이터들은 useLazyQuery를 사용하는 것으로 바꿔 페이지가 마운트된 뒤 불러오도록 하였다.
먼저 필요한 데이터만 빠르게 불러오고 나중에 필요한 데이터는 그 다음으로 우선순위를 만들었다.
Router 기반 코드 스플리팅
기존 번들링 된 js파일이 1800kB였다.

레이아웃에 React.lazy()가 적용되어있었는데 라우터를 기반으로 lazy를 적용하면 파일이 더 작아질 수 있다.
// import
const SignIn = React.lazy(() =>
import('../screen/SignInScreen').then(({ SignInScreen }) => ({
default: SignInScreen,
}))
);
// Route 컴포넌트에서 render={}에 사용
<Route path="/signin" render={() => <SignIn />} />;
메인 페이지 성능 향상이 목표이고 라우터 이동되었을 때 살짝 로드시간이 더 걸리더라도 파일이 작으므로 로드시간에 많은 차이는 생기지 않을거라고 생각했다.

라우터 기반으로 코드 스플리팅을 적용하고 js 파일은 1800KB 에서 870KB로 줄어들었고 자바스크립트 로드 속도도 2.5s에서 0.68s로 확실히 줄어든 것을 확인할 수 있다.
코드 스플리팅으로 속도 개선 작업 중 점수 향상과 실제 느끼는 속도도 가장 크게 증가 되었다.
보안 취약점이 낮은 라이브러리 버전 변경
퍼포먼스와 관련된 작업은 아니지만 추가로 보안 관련하여 문제가 있는 스크립트를 수정하였다. 제이쿼리를 1점대 버전을 사용하고 있었고 이 버전은 보안관련 이슈가 있는 버전이었다.
최신 버전으로 바꾸면서 보안 이슈를 해결하고 Best Practice 점수도 높였다.
<!-- 수정 전 -->
<script
type="text/javascript"
src="https://code.jquery.com/jquery-1.12.4.min.js"
></script>
<!-- 수정 후 -->
<script
defer
type="text/javascript"
src="https://code.jquery.com/jquery-3.6.1.min.js"
></script>
마무리


전체적인 성능 개선 작업을 하면서 로드시간 4초대에서 1초대로 바뀌었다.