Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 리소스 최적화 및 번들 크기 감소 #365

Merged
merged 9 commits into from
Sep 26, 2024

Conversation

Yoonkyoungme
Copy link
Contributor

@Yoonkyoungme Yoonkyoungme commented Sep 26, 2024

관련 이슈

작업 내용

성능 개선 전

성능 개선 전에는 Lighthouse 성능 측정에서 데스크탑 기준 69점, 모바일 기준 45점을 기록했다.

데스크탑 모바일
image image

성능 개선 후

성능 개선 후에는 Lighthouse 성능 측정에서 데스크탑 기준 90점, 모바일 기준 63점을 기록했다.

데스크탑 모바일
image image

최종적으로 번들 파일이 코드 스플리팅되어 1.1 kB + 226 kB + 21.3 kB + 55.6 kB로 나뉘어 합계 304 kB가 되었다.

1. JS 리소스 크기 줄이기

1-1. aws cloudfront의 content-Encoding을 gzip에서 br로 변경

CloudFront 파일 압축 문제 해결

AWS는 기본적으로 br 압축 방식을 지원하며, 브라우저가 br를 지원하지 않는 경우에는 gzip을 제공한다. 하지만 테스트 과정에서 모든 브라우저에 대해 gzip 압축이 적용되는 문제가 발생하였다.
image

해당 문제를 해결하기 위해 CloudFront의 캐싱 정책을 점검하였고, 응답 헤더에 캐싱 정책이 제대로 설정되어 있지 않음을 발견했다. 이로 인해 gzip이 기본적으로 제공된 것으로 파악되었다. 캐싱 정책을 올바르게 적용한 후, br로 압축 방식이 변경된 것을 확인했다.
image

결과

br로 압축 방식이 변경된 것을 확인했으나, 번들 파일의 총 크기는 여전히 3.9MB로 동일했다. 이는 압축 방식에 따른 전송 크기 변화는 있지만, 번들 파일 자체의 크기에는 영향을 미치지 않기 때문이다. 브라우저가 파일을 다운로드할 때의 전송 데이터 양이 줄어들어, 네트워크 성능이 향상될 것이라고 예측한다.


1-2. Code splitting

모모 서비스에서 코드 스플리팅이 필요한 주요 이유는 페이지별 불필요한 리소스 다운로드로 인한 초기 로딩 속도 저하 문제 때문이다.

예를 들어, 약속 시간 조회 페이지에 방문한 사용자가 단순히 시간을 확인하려고 할 때, 다음과 같은 페이지들의 리소스를 모두 다운로드하게 된다. → 약속 시간 조회 페이지의 초기 로딩 시간이 길어지는 문제가 발생

  • 약속 생성 페이지
  • 약속 링크 공유 페이지
  • 약속 추천 및 확정 페이지

이 페이지들은 사용자가 반드시 방문할지 알 수 없기 때문에, 모든 리소스를 미리 다운로드할 필요가 없다고 판단하였다. 따라서 코드 스플리팅을 통해 번들 파일의 크기를 분리하고, 각 페이지에 방문할 때만 필요한 리소스를 다운로드받도록 개선했다.

이를 위해 다음과 같은 webpack 설정을 적용했다.

  • SplitChunks: 코드 스플리팅을 자동화하기 위해 splitChunks 옵션에서 chunks: 'all'을 사용하여 중복된 코드나 공통 모듈을 별도의 청크로 분리했다.

    // webpack.common.js
    optimization: {
      splitChunks: {
        chunks: 'all',
      },
    }
  • Output 설정: 각 번들 파일에 해시를 추가하여 캐싱 최적화를 적용했다. contenthash를 사용해 파일 내용이 변경될 때만 해시가 달라지도록 하여, 파일이 변경되지 않은 경우에는 브라우저가 캐시를 활용할 수 있도록 했다.

    // webpack.common.js
    output: {
      filename: '[name].[contenthash].js',
    }

또한, dynamic importlazy loadingSuspense를 적용하였다. UX 개선을 위해 새로운 페이지의 리소스를 다운로드하는 동안 Loading fallback UI를 구성하여 사용자에게 로딩 상태를 안내할 수 있도록 했다.

이로 인해 초기 로딩 속도가 개선되었고, 페이지 로드 시 불필요한 리소스 다운로드를 최소화함으로써 사용자 경험이 향상되었다.


2. 이미지 크기 줄이기

2-1. svg 파일 최적화

SVG 사용 이유

로고, 아이콘, 캐릭터와 같은 이미지 요소를 SVG로 사용하였다. 그 이유는 다음과 같다.

  • 벡터 그래픽이기 때문에 다양한 화면 크기에서도 해상도 저하 없이 선명한 이미지를 제공한다.
  • 아이콘의 크기를 자유롭게 조절해도 품질이 떨어지지 않아 모든 크기의 브라우저와 해상도를 원활히 지원할 수 있다.

또한, 프로젝트에서 SVGR을 사용하여 SVG 파일을 React 컴포넌트처럼 import해서 사용하고 있다. SVGR은 SVG 파일을 React 컴포넌트로 변환하는 도구로, SVGO(SVG Optimizer)를 내장하고 있어 SVG 최적화 기능도 제공한다.

문제 상황

하지만, 몇 가지 원본 크기 자체가 1MB를 넘는 SVG 파일들이 4가지가 있었다. logo, logoSunglass, momoCharacter, questionMomoCharacter 파일들은 모두 Figma에서 SVG 파일을 바로 추출해서 사용했었다. 해당 이미지들은 Base64로 인코딩되어 SVG 파일 안에 인라인으로 포함되어 있었다. Base64 인코딩된 이미지 데이터는 파일 크기가 커지기 쉬운데, 이로 인해 SVG 파일 자체가 1MB 이상으로 비대해졌던 것이다.

<svg width="current" height="current" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="120" height="120" fill="url(#pattern0_2548_1918)"/>
<defs>
<pattern id="pattern0_2548_1918" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_2548_1918" />
</pattern>
</defs>
<image id="image0_2548_1918" width="120" height="120" xlink:href="data:image/png;base64, ..." />

문제 해결 과정

그래서 다음과 같은 방식으로 SVG 파일의 크기를 줄였다.

  1. Figma에서 로고를 PNG 파일로 추출 (기존에는 Figma에서 SVG 파일을 바로 추출해 사용했으나, 이번에는 PNG로 추출하여 최적화를 진행했다.)
  2. ResizePixel을 사용하여 PNG의 해상도를 줄이고, 압축하였다.
  3. 압축된 PNG 파일을 ASPOSE사이트를 이용해 Base64로 인코딩한 후, 기존 logo.svg 파일에서 Base64 데이터로 대체했다.

결과

개선 전 logo.svg (1.3MB) 개선 후 logo.svg (35KB)
logo.svg 개선 전 logo.svg 개선 후

1MB 이상이었던 SVG 파일 4개의 크기를 줄였더니 bundle.js의 크기가 300 kB로 줄었다.


3. 사용하지 않는 폰트 제거하기

느린 4G 환경에서 모모 서비스를 사용해 보면, 폰트를 다운로드받는 데 상당한 시간이 소요되었다. (느린 4G 환경에서 약 13초 이상)

3-1. 꼭 필요한 Glyph만 포함하는 폰트 파일 만들기

웹 페이지에서 노출될 일이 없는 Glyph를 삭제하여 폰트 파일을 최적화했다.

Glyph: 자체(字體), 자형(字形)은 글자의 모양을 가리킨다. 자체는 하나 이상의 자소로 이루어진다. 글리프(glyph)라는 개념은 자체의 문자 코드에서 뜻과 소리를 지니지 않은 도형 기호(구두점, 괄호, 공백 등)의 추상화를 포함한다. (Wikipedia)

Pretendard 공식 GitHub에서 제공하는 subset 폰트를 사용하였다.
또한, 이전에는 모든 폰트 크기를 포함한 글꼴을 사용하였지만, 서비스 내에서 300, 400, 500, 700의 네 가지 크기만 사용하고 있다는 것을 확인했다. 따라서, 필요한 폰트 크기만 남기고 나머지는 제거함으로써 폰트 파일의 크기를 줄였다.
image

결과

Lighthouse 성능 측정에서 데스크탑 기준 96점을 기록했다.
image


3-2. 폰트 preload 적용

폰트 preload는 웹 페이지 로딩 시 폰트를 미리 로드할 수 있게 하여 FOUT 현상을 방지한다.
webpack-font-preload-plugin을 사용하여 폰트를 preload하는 방식을 적용하였다.
subset 폰트로 교체한 후, Lighthouse 성능 측정에서 데스크탑 기준 96점을 기록했다.
하지만 폰트를 preload할 경우, 폰트가 bundle.js보다 먼저 로드되기 때문에 LCP 점수가 낮아지는 현상이 발생했다. 이러한 문제로 인해 폰트 preload 적용 여부에 대해 고민하게 되었다.
image
image
위와 같은 현상이 발생하여 폰트 preload 적용 여부를 고민하게 되었다. 폰트 깜빡임 현상을 개선하기 위해 preload를 적용할지, 아니면 preload를 적용하지 않고 Lighthouse에서 LCP 점수를 높일지를 두고 논의한 결과, 폰트 깜빡임을 해결하는 것이 더 중요하다고 판단했다.
따라서 LCP 점수가 다소 낮아지더라도 폰트 preload를 적용하여 폰트 깜빡임 현상을 제거하기로 결정했다.

특이 사항

참고 자료

자세한 내용은 작업 현황 정리 노션을 참고해 주세요. :)

리뷰 요구사항 (선택)

@Yoonkyoungme Yoonkyoungme added 🐈 프론트엔드 프론트엔드 관련 이슈에요 :) ⚙️ 환경설정 환경을 설정해요 :) ♻️ 리팩터링 코드를 깎아요 :) labels Sep 26, 2024
@Yoonkyoungme Yoonkyoungme added this to the 5차 데모데이 milestone Sep 26, 2024
@Yoonkyoungme Yoonkyoungme self-assigned this Sep 26, 2024
Copy link
Contributor

@Largopie Largopie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋네요 어프루브 드리겠씁니다! 군더더기 없이 깔끔합니다 :) 👍

Copy link
Contributor

@hwinkr hwinkr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨어요~ 빙봉뱅봉!! 🔮 모모 서비스 성능이 개선되는 것을 보니 기분이 좋아지네요 ㅎㅎ

Copy link

Test Results

9 tests   9 ✅  10s ⏱️
2 suites  0 💤
1 files    0 ❌

Results for commit 1394c24.

@Yoonkyoungme Yoonkyoungme merged commit 1df794e into develop Sep 26, 2024
5 checks passed
@Yoonkyoungme Yoonkyoungme deleted the chore/353-optimize-resource-size branch September 26, 2024 12:23
@Yoonkyoungme Yoonkyoungme changed the title [FE] 리소스를 최적화 및 번들 크기 감소 [FE] 리소스 최적화 및 번들 크기 감소 Sep 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
♻️ 리팩터링 코드를 깎아요 :) ⚙️ 환경설정 환경을 설정해요 :) 🐈 프론트엔드 프론트엔드 관련 이슈에요 :)
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[FE] 리소스를 최적화하고, 번들 크기를 줄여요 :)
3 participants