- Published on
웹 서비스 성능 분석 (1)
- Author
- Name
- yceffort
실제 개발자 피드백
글이 많이 도움이 되었는지:
- 그동안 성능 최적화를 해보려고 인프런 강의를 구매해 듣고, 구글의 성능 가이드도 많이 찾아봤습니다.하지만 모르는 영역이 많다 보니 한 곳을 파다 보면 다른 주제로 빠지게 되고, 결국 어중간한 정보만 쌓은 채 정작 고쳐야 할 부분은 손대지 못한 채 프로젝트를 마무리하곤 했습니다.그래서 늘 찝찝한 마음에 주말마다 계속 들여다보며, ‘성능 최적화는 왜 이렇게 어려운 걸까?’ 하는 생각을 하곤 했습니다.
- 이번에 받은 성능 분석 글은 저에게 완벽한 웹 성능 최적화 가이드가 되었습니다. 덕분에 한 걸음 앞으로 나아갈 수 있는 계기가 되었습니다. 감사합니다.
다른 개발자에게 이러한 성능 분석을 추천할 수 있을지:
- 네, 추천합니다. 얼마전에 웹 성능 최적화 강의를 들으려고 인프런에 접속했는데, 관련된 강의가 많이 없습니다.
- 그리고 성능 최적화 환경을 임의로 만드는 것이 아니라, 실제 실무에서 사용될 수 있는 사이트를 다뤄주셔서 개발자들에게 더욱 큰 도움이 될 것 같습니다.
- 출간하시면 무조건 구매합니다!
추가 피드백
사이트마다 다르겠지만, yceffort 님이 보시기에 복잡한 기능이 크게 없는 사이트에서 100kb 정도면 크다라고 판단할 수 있는 기준은 무엇일까요? 이런 실무적인 감각들이 궁금해서, 책에 함께 담아주시면 큰 도움이 될 것 같습니다!
https://httparchive.org/reports/state-of-javascript?start=earliest&end=latest&view=list#bytesJs
위 사이트 방문하시면 현재 웹에서 운영되는 사이트 들의 평균적인 자바스크립트 크기를 알 수 있습니다. 현재 기준 680kb 정도 인데요, 이에 비해 개발자님의 사이트는 매우 작은 수치라고 볼 수 있습니다. 이부분 내용은 제가 뺴먹었네요. 추가하겠습니다!
콜스택을 이해하는 방법에 대한 설명이 들어간다면, 저처럼 잘 몰랐던 개발자들에게는 정말 유레카! 하는 순간이 되지 않을까 생각합니다.
이부분은 단순히 해당 이미지를 그리기 위해 함수 호출이 18번 발생한다 정도로만 소개하기 위해서 급하게 넘어간 감이 있는 것 같습니다. 이부분은 차후에 보완하도록 하겠습니다 =)
react, react-dom 외에도 빌드 시 용량이 크다고 경고 메시지를 받는 패키지의 경우 → 이런 경우 종속성을 고려해 잘 나눠보는 수밖에 없을까요?
네! 이부분은 개발자의 순전히 취향 정도로 보시면 될 것 같습니다. 저의 경우 과거 create-react-app 기반 프로젝트에서 webpack, react, react-dom 이 세가지 관련 코드를 framework.js
라고 하는 별도의 청크로 나눴었습니다. 위 세 패키지는 제가 작성한 코드 또는 필요에 따라 임의로 설치하는 패키지 대비 어지간하면 변경되지 않는 패키지이 이기 때문에 이렇게 나눴었습니다. 이처럼 개발자님께서 보시기에 변경사항이 거의 없을 것 같은 패키지는 별도 청크로 나눠 사용자 브라우저 캐시의 이점을 누릴 수 있도록 조치해주시면 좋을 것 같습니다.
react-device-detect 외에도 → 이 말씀은, 앞으로 시간이 된다면 라이브러리 사용보다는 직접 구현해보라는 의미로 이해하면 될까요?!
개인적으로는 취향의 영역이라고 생각합니다. 개발 속도와 빠른 배포가 중요한 상황이라면 안정적인 패키지를 설치하는 것이 좋고, 진짜 성능을 극단적으로 깎고 싶거나, 패키지 크기 대비크게 사용하지 않는 경우라면 내재화하는 것이 낫다고 생각합니다. 저는 패키지 사용전에 항상 https://bundlephobia.com/ 에 해당 패키지의 크기와 내부 상황을 꼼꼼히 검토하고 설치하는 편입니다.
다음은 실제 개발자 분에게 전달 드린 글 입니다. 사이트 주소와 이미지 등의 정보는 가려져 있습니다.
https://example.com 성능 분석
1. 요약
요청하신 https://example.com
웹사이트의 성능을 분석해 보았습니다. 전반적으로 웹사이트의 로딩 속도와 사용자 경험을 개선할 수 있는 여지가 충분히 있는 것으로 확인되었습니다.
현재 주요 지표인 LCP(가장 큰 콘텐츠 렌더링 시간)는 약 3~4초 수준으로 측정되었고, 화면 요소들이 예기치 않게 움직이는 정도를 나타내는 CLS(누적 레이아웃 이동) 값은 0.272로 나타나, 두 지표 모두 사용자 경험 향상을 위해 개선이 필요한 상태입니다.
분석 결과, 가장 큰 성능 병목 지점은 다음과 같습니다.
- 초기 이미지 로딩 지연 (LCP): 첫 화면의 핵심 이미지(
/webp/part1/time.webp
)가 표시되기까지 시간이 다소 걸립니다. 이는 웹사이트의 구조상 브라우저가 관련 자바스크립트 코드를 먼저 처리해야 이미지를 불러올 수 있기 때문인 것으로 보입니다. - 레이아웃 불안정성 (CLS): 위 이미지 로딩 시, 해당 이미지의 크기가 미리 지정되지 않아 CLS 점수에 주된 영향을 미치고 있습니다.
이러한 문제들을 해결하고 더 쾌적한 웹사이트 경험을 제공하기 위해 다음과 같은 개선 방안을 우선적으로 제안합니다.
- 핵심 이미지 미리 로드하기 (
<link rel="preload">
): 가장 중요한 LCP 이미지를 브라우저가 다른 작업 이전에 미리 다운로드하도록 하여 화면 표시 속도를 앞당깁니다. - 이미지 공간 미리 확보하기 (CLS 개선): 이미지 태그에 크기(너비/높이) 정보를 명시하여 이미지가 로드될 때 레이아웃이 흔들리지 않도록 안정화합니다. (Preload 적용 시에도 이 문제가 일부 완화되는 것을 확인했습니다.)
- 자바스크립트 코드 분할하기 (Code Splitting): 초기 로딩에 필요한 자바스크립트의 양을 줄여 브라우저의 부담을 덜고, 필요한 코드를 더 빠르게 병렬로 로드하여 전반적인 반응 속도를 높입니다.
이 권장 사항들을 적용하시면 웹사이트의 성능과 사용자 만족도를 크게 높일 수 있을 것으로 기대합니다. 자세한 분석 내용과 구체적인 방법은 이어지는 섹션에서 설명드리겠습니다.
2. 분석 개요
2025년 4월 29일 기준으로 배포되어 있는 웹사이트를 살펴보았습니다.
분석에 사용한 도구는 다음과 같습니다.
- chrome dev tool
- webpagetest
3. 웹사이트 분석
주요 프레임워크 및 라이브러리
react
,react-dom
: index 파일 내부에 리액트 코드로 추정되는 주석과 함수, 그리고 jsx 를 확인할 수 있었습니다. 사용하신 버전은 18.3.1 로 보입니다.styled-components
:data-styled
,styledComponentId
와 같은 변수명,styled.
등을 사용하신 것을 확인했습니다. 사용하신 버전은 6.1.14 입니다.react-router
:useNavigate
useLocation
등은react-router
에서 널리 쓰이는 훅이며, 내부에서 쓰는 오류 메시지로<Router>
등의 존재도 확인했습니다. 사용하신 버전은 7.1.3 입니다.us-parser-js
: UserAgent 분석을 위한 라이브러리 입니다.UAParser
객체와 관련된 메소드가 존재하는 것으로 확인했습니다. 이 패키지는 설치를 의도하지는 않으신 것 같고, 후술할react-device-detect
의 의존성으로 인해 설치된 것으로 보입니다.react-modal
:ReactModal__Overlay
ReactModal__Content
과 같은react-modal
에서 예약어 수준으로 쓰이는 클래스명 의 존재를 통해 알수 있었습니다.react-device-detect
:BrowserView
isMobile
와 같은react-device-detect
특유의 변수명을 확인할 수 있었습니다.vite
: 번들러의 경우 소스코드에 명시적인 특징이 없어 바로 확인할 수 없지만, 파일 시작 부분에vite
를 사용했을 것으로 추정되는modulepreload
관련 코드를 확인할 수 있었습니다. 이 코드는modulepreload
의 존재여부를 확인하고, 이를 지원하지 않는 브라우저라면 폴리필을 적용하려고 시도하기 위해 작성되었는데, 이는vite
기반으로 만들어진 웹 애플리케이션에서 눈에 띄게 확인할 수 있는 내용 중 하나입니다.
빌드 환경
앞서 언급드린 내용을 토대로 살펴보면, create-vite
로 제작된 리액트 프로젝트이며, 기본 설정을 크게 건들지 않고 만들어진 프로젝트일 가능성이 높아보입니다.
배포 환경
amazon cloud front 를 사용하여 서울 리전에서 배포중인 것으로 확인됩니다.
ping example.com
PING example.com (0.0.0.0): 56 data bytes
IP Address Lookup for 0.0.0.0 in Icheon, Korea (the Republic of)
4. 주요 고민
LCP 점수 향상
LCP (Largeset Contentful Paint) 는 잘 아시는 것 처럼 핵심 웹 지표를 측정하는 가장 첫번째 지표로, 사용자 경험 측면에서 페이지가 얼마나 빨리 로드되는 것처럼 느껴지는 지 측정하는 지표 입니다. 이는 뷰포트 내에서 가장 큰 콘텐츠 요소가 화면에 렌더링 되기 까지 얼마나 걸리는지 시간을 측정합니다. 모든 지표가 그렇지만, LCP 역시 빠를 수록 빠르게 로드 된다고 느껴집니다.
현재 제 기준에서 측정한 LCP 점수는 3~4 초 정도 이므로, 개발자님께서 느끼시는 것 처럼 개선이 필요한 상태입니다. 그리고 LCP 에 영향을 주는 요소는 /webp/part1/time.webp
이고, 이것을 기준으로 원인을 파악해보았습니다.
자바스크립트 번들 크기 및 실행 지연
index-mSjck-Fi.js
의 크기는 총 100kb 정도로, 복잡한 기능이 크게 없는 정적인 웹사이트 치고는 큰 편입니다. 그 이유는 서비스의 로직을 담당하는 코드 (=개발자님께서 손수 작성하신 코드) 외에 react
react-dom
styled-components
등 프레임워크를 담당하는 코드들도 모두 포함되어 있기 때문입니다. 코드 스플리팅이 되어 있지 않기 때문에, 병렬 다운로드의 이점을 볼 수 없고, 모든 리소스가 이 자바스크립트 하나에 의존해야 한다는 점은 분며히 성능에 좋은 영향을 미치기 어렵습니다.
사실 100kb 는 엄밀히 말하면 요즘 프론트엔드 트렌드 치고는 큰 파일은 아닙니다만, 문제는 이 서비스가 싱글페이지 애플리케이션이라는 것 입니다. SSR 의 혜택을 전혀 받을 수 없기 때문에 이미지를 그리는 시점은 순전히 자바스크립트에 영향을 받게 되는데, 이 자바스크립트 파싱이 지연될 수록 LCP 이미지를 그려지는게 느려지게 됩니다.
위 스크린샷은 해당 사이트의 성능을 크롬 개발자 도구로 측정한 모습입니다. 모든 이미지가 자바스크립트 리소스 로딩 이후에 인식되고 실행되는 것으로 보아, 이미지 렌더링에 필요한 코드가 모두 자바스크립트 다운로드 및 평가 이후에 이뤄진다는 것을 알 수 있습니다.
위 스크린샷은 해당 크롬 개발자 도구에서 네트워크 탭으로, 해당 이미지를 어디서 로딩했는지 확인할 수 있습니다. initiator
가 해당 리소스를 불러온 주체인데, 이를 누르면 어느 코드에서 이미지를 불러온지 확인할 수 있습니다.
그리고 여기에 크롬 디버거를 걸어둔다면, 이 함수를 호출하기 위한 콜스택을 확인할 수 있습니다.
콜스택이 총 18개 정도로, 해당 이미지를 그리는 과정에 이르기까지 총 18번의 함수를 호출해야한다는 것을 의미합니다. 위 라이브러리 현황, 그리고 현재 상황을 종합해 봤을때 18개 함수를 호출되기까지 이르는 과정은 아마도 다음과 같을 것입니다.
createRoot
- 루트 리액트 컴포넌트 렌더링 시작
- 컴포넌트 트리 순회
- 리액트 라우터 처리
- 컴포넌트 트리 렌더링
- LCP 가 포함된 컴포넌트 렌더링
- 이미지 컴포넌트 렌더링 (
bn
) ⇒ 메일에서 말씀하신 그 부분 인 것 같습니다. <img/>
태그 생성 및 이미지 요청
18 단계가 물론 싱글 페이지로 구성된 리액트 관점에서 그렇게 깊다고 볼수는 없습니다만, LCP 에 영향을 미친다는 것은 부정할 수 없습니다.
해결 방안
위 분석을 토대로 다음과 같은 해결방안을 제시해드립니다.
1. SSR 내지는 SSG 로 전환
Next.js 나 Remix 와 같은 리액트 기반 서버사이드 렌더링 프레임워크를 차용한다면 위 문제를 손쉽게 해결 할 수 있습니다. SSR 로 전환된다면, 이미지의 initiator 가 js 가 아닌 html 이 되기 때문에 굳이 자바스크립트를 로딩하지 않더라도 이미지를 빠르게 불러올 수 있습니다. 물론 이는 프로젝트 구조를 뒤흔들어야 하고, 이를 배포할 인프라도 필요하다는 단점도 있기 때문에 바로 적용하시기는 어려울 것입니다.
2. LCP 이미지에 Preload 적용
브라우저는 프리로드 스캐너라는 특별한 동작이 있습니다. 이 프리로드 스캐너란 HTML 문서를 분석하는 주요 파서외에 보조적으로 동작하는 스캐너로, HTML 문서를 빠르게 로드하기 위한 내부 최적화 도구입니다. 프리로드 스캐너는 다음과 같은 동작을 수행합니다.
- HTML 을 미리 읽으면서
link
script
img
태그등으로 선언된 주요 리소스를 먼저 찾습니다. - 주요 파서가 해당 태그에 도달하기 전이라도, 그리고 다른작업으로 파서가 멈춰있더라도 스캐너는 1번에서 찾은 리소스가 있다면 리소스를 미리 다운로드 합니다.
- CSS, JS 등으로 렌더링이 차단되어 있더라도 리소스를 병렬로 다운로드 할 수 있어 페이지 로딩 속도를 향상시킵니다.
자세한 내용은 아래 블로그 참고 부탁드립니다.
브라우저의 프리로드 스캐너(pre-load scanner)와 파싱 동작의 이해
LCP 이미지는 현재 프리로드 스캐너에 걸릴 수 없는 구조입니다. 왜냐하면 이미지의 존재 자체를 js 가 모두 평가한 시점에서야 비로소 알수 있기 때문입니다. 만약 HTML <head>
에 <link rel="preload" as="image" href="/webp/part1/time.webp">
태그를 추가하여 브라우저가 JS 실행을 기다리지 않고 미리 이미지를 다운로드 할 수 있다면, 설령 이미지 삽입 시점은 JS 실행 이후가 될지라도 보다 빠르게 불러올 수 있습니다. 아래는 개발자님 사이트에서 link 태그를 적용하기 전 후 예시입니다.
적용하기전, 이미 아시는 것 처럼 이미지 다운로드는 JS 실행 시점 이후에 잡혀있습니다. 그 이유는 브라우저가 이미지가 존재한다는 것을 알아채는 시점이 그 이후이기 때문입니다.
하지만 프리로드 스캐너가 인식할 수 있도록 link
태그를 추가한 이후에는 상황이 많이 달라졌습니다. js 를 다운로드하고 파싱하고 있는 와중에도 100kb 에 달하는 이미지를 병렬로 다운로드 하는 것을 볼 수 있습니다. 이는 프리로드 스캐너에 의해 이미지가 미리 스캔되어 다운로드 되었다는 증거이며, 비록 이미지 로딩은 js 시점 이후라 할지라도 렌더링 하는 속도는 훨씬 빨라질 것입니다.
프리로드 스캐너는 단순히 LCP 이미지 외에도 해당 스크린샷에서 보이는 다양한 이미지에도 함께 사용할 수 있습니다. 페이지에 확정적으로 로딩되며 뷰포트에 걸릴 수 있는 중요 이미지라면, 개발자님께서 추가해주신 lazy
속성 제외 외에도 이러한 기법을 사용해보시는 것을 검토해주세요.
LCP에 대해 자세히 아시고 싶다면, 다음 블로그 글을 추천해드립니다.
Largest Contentful Paint (LCP) 최적화하기
3. avif 포맷사용
avif 는 webp 보다 일반적으로 압축률이 더 뛰어난 것으로 알려져 있습니다. 다만 브라우저 지원 범위가 조금더 타이트 하기 때문에 이부분은 충분히 검토해보시길 추천해드립니다.
AVIF vs. WebP: 4 Key Differences and How to Choose
4. 코드 스플리팅 구현
앞서 말씀 드린 것 처럼 현재 index-mSjck-Fi.js
에는 초기 로딩에 필수적이지 않은 코드 (모달 등) 들이 많이 포함되어 있습니다. 초기 데이터 로딩에 필요한 코드들만 별도 청크로 분리하시는 것이 중요합니다. 그렇게 함으로써 초기 로딩에 필요한 JS 크기를줄여 파싱 및 실행시간을 단축할 수 있고, 이로 인해 LCP 렌더링 이미지 시작 시간을 크게 땡겨 올 수 있습니다. vite
는 코드 스플리팅과 관련된 다양한 도구들을 지원하기 때문에, 어렵지 않게 해내실 수 있으리라 생각됩니다.
청크 분리를 에 따른 또 다른 장점은 신규 배포 시에도 사용자에게 영향범위를 미치는 것을 최소화할 수 있다는 것입니다. 예를 들어 개발자님께서 아주 작은 코드 하나를 수정해서 배포했다고 가정해보겠습니다. 현재 구조는 하나의 청크에 모든 변경사항이 담겨져 있기 때문에 새로운 index.js
생성으로 이어질 것이고, 결국 사용자는 캐싱에 따른 이점을 누릴 수 없게 됩니다. react
react-dom
과 같이 변경사항이 매우 적은 프레임워크성 라이브러리를 별도 청크로 분리하면 어떨까요? 이 청크는 CDN 에 한번 업로드 되어 버전업이 일어나지 않은 이상 캐시 전략을 계속 유지할 수 있고 이로 인해 새롭게 배포가 일어나도 페이지 로딩 속도는 여전히 빠르게 유지하실 수 있습니다.
과도한 코드 스플리팅은 여러 페이지가 있는 서비스에서는 독이 될 수 있지만, 현재 서비스 구조에서는 충분히 이점을 누리실 수 있을 것으로 보입니다.
CLS 점수 향상
CLS는 웹 페이지의 시각적 안정성을 측정하는 핵심 지표입니다. 페이지가 로딩되는 동안 사용자에게 예상치 못하게 콘텐츠(요소)의 위치가 얼마나 많이 이동하는지를 정량화한 값입니다. 예를 들어, 글을 읽고 있는데 갑자기 이미지가 로드되면서 글자가 아래로 밀려나거나, 버튼을 누르려는데 버튼 위치가 갑자기 바뀌는 등의 현상이 CLS에 해당합니다. 낮은 CLS 점수는 안정적이고 좋은 사용자 경험을 의미합니다.
이 서비스에서 CLS 에 영향을 미치는 요소 역시 /webp/part1/time.webp
라는 것을 확인했습니다. 따라서 이 이미지와 관련된 내용을 수정하면 자연스럽게 CLS 점수도 향상 될 것입니다.
해결방안
프리로드 스캐너와 이미지 크기
이 문제의 해결방안 역시 LCP , 정확히는 프리로드 스캐너와 맞닿아있습니다. 앞서 프리로드 스캐너는 미리 이미지를 스캔한다고 말씀드렸는데요, 단순히 미리 이미지를 스캔하고 다운로드 하는 것이 아니라 미리 이미지의 사이즈도 알아둡니다. 현재 CLS 가 발생하는 이유는 이미지 다운로드도 늦는데, 이미지 크기 자체도 늦게 알아채게 되서 이미지를 다운로드 한 이후에 크기를 알게 되고, 그 이후에 그 이미지 크기 만큼 HTML 크기를 확보하면서 발생하게 됩니다. 이 점은 <img>
태그 내에 사이즈가 없어서 더욱 부각되는 문제점 입니다.
하지만 프리로드 스캐너로 이미지를 미리 다운로드하고, 사이즈 까지 알아둔다면 어떨까요? img
에 src
로 해당 이미지를 넣는 순간, img
가 DOM 에서 차지해야 할 크기를 미리 알게 되고, CLS 를 계산하기 위해 미리 이미지 다운로드 까지 기다리지 않아도 된다는 큰 장점이 생깁니다.
실제로, 프리로드 스캐너에 인식되기 위해 <link>
태그로 해당 이미지를 넣고 라이트하우스를 비교한다면, CLS 점수가 0 로 나오는 것을 볼 수 있습니다.
즉, 정리하자면 현재 CLS 0.272 는 /webp/part1/time.webp
의 크기를 분석하기 위해 다운로드하고 크기를 알아내기 위해 걸리는 시간이라고 보시면 됩니다. 따라서 CLS 해결을 하기 위해서는 다음 두 가지를 살펴보시면 됩니다.
img
에width
height
또는 css 의aspect-ratio
를 넣어주세요. 이는 브라우저가 이미지 렌더링을 위한 DOM 크기를 미리 확보할 수 있게 해주어 CLS 를 줄일 수 있습니다.- 프리로드 스캐너로 이미지를 미리 스캔할 수 있게 해준다면 이미지 크기를 미리 알수 있게 됩니다.
번들 크기를 줄이고 싶습니다.
자바스크립트 번들 크기는 웹 서비스 성능에 영향을 미치는 중요한 요소입니다. 다운로드, 파싱, 실행 등 에 종합적으로 영향을 미치기 때문에, 이 크기를 줄일 수록 성능을 드라마틱하게 개선할 수 있습니다. 현재 코드 상황이 어떤지 정확히 알 수 없어 명확하게 가이드 드릴 수는 없지만, 제가 지금까지 확인 한 바를 기준으로 말씀해드리겠습니다.
1. 사용량 대비 과하게 큰 라이브러리 내재화
react-device-detect
를 예로 먼저 들어보겠습니다. 정확히 얼마나 쓰는지 까지는 잘 모르겠지만, 소스 코드 상으로 보았을 때는 isMobile
isTablet
정도 인 것으로 추정됩니다. 하지만 개인적인 생각으로는 이 두 값을 판단하기 위해서 react-device-detect
를 설치해서 쓰는 것은 너무 크다고 생각합니다.
react-device-detect v2.2.3 ❘ Bundlephobia
react-device-detect
와 그것이 의존하고 있는 ua-paser-js
는 다양한 UA 분석을 위해 많은 상수들을 내장하고 있는데요. 아마 대부분의 프로젝트가 이부분 까지 모두 사용하지는 않을 것으로 보입니다. 내부 코드가 실제로도 모바일, 태블릿 분기 용도 정도로만 사용하고 있다면 react-device-detect
를 제거 하고 직접 내재화 해보시는건 어떠실지 추천해드립니다. 아마도 styled-components
연동 목적이었던 것 같은데, 그러한 목적이라면 브라우저에서 네이티브로 지원하는 미디어 쿼리를 쓰시는게 훨씬 낫습니다.
react-device-detect
외에도 시간과 여건이 허락한다면 npm 에서 설치하시는 것보다 직접 구현하시는 것을 더 추천해드립니다. 직접 구현해봄으로써 시간과 노력을 소비하고 안정성은 떨어질 수도 있지만, 직접 구현함으로써 기능의 본질적인 동작방식 이해하실 수 있고, 나아가 필요한 코드만 뽑아와서 성능과 번들크기를 최적화 하실 수 있습니다.
상하 캐러셀을 직접 구현하셨던 것으로 보이는데요. 위 작업을 통해 해당 기능을 직접 구현하시면서 배웠던 것을 다른 영역에서도 학습하실 수 있으리라 믿습니다.
2. react-router 제거
제가 파악한 바에 따르면 이 서비스는 단순히 하나 짜리 페이지로 보이는데요. 그렇다면 react-router
를 사용하시는 이유를 잘 모르겠습니다. 아마 coverage 에서 나오는 대부분의 미사용 리소스가 다 여기에 잡혀있는 것으로 보입니다. 이 후에 서비스 확장성을 위해서라면 두셔도 되겠지만, 지금 구조상에서는 크게 필요하다는 것을 느끼기 어려웠습니다.
한가지 유의미하게 사용중인 기능이 있다면 /*
루트에 대해서 다 /
로 리다이렉트 시키는 코드였습니다.
이 기능은 react-router
로 위와 같이 처리할 수도 있지만, nginx, netlfiy, vercel 등 호스팅 서버에 규칙을 추가하여 서버단에서 처리하시는 것이 훨씬 더 효과적입니다. 이러한 history fallback 처리를 react-router
가 아닌 서버에서 처리하는 것을 검토해보시면 좋을 것 같습니다. 그렇게 함으로써 react-router
를 제거할 수 있고, js 번들의 크기를 줄이고 실행 속도를 향상시킬 수 있습니다.
추가로 더 살펴보니, amazon cloud front 에서 배포해서 사용중이신 것 같습니다. 정확히 기억은 안나지만, distributions > error pages 에서 업로드 되지 않은 리소스에 대해서 경로 지정을 할 수 있었던 것 같은데요. 이부분은 한번 설정 만지면서 수정해보시면 될 것 같습니다.
3. 코드 스플리팅
개발자님께서는 React.lazy
로 코드 스플리팅을 많이 해주셨다고 말씀해주셨습니다. 현재 필요한 코드 스플리팅은 위에서도 말씀드렸던 것 처럼 번들러 수준의 코드 스플리팅으로 보입니다. 둘 사이의 차이는 다음과 같습니다.
vite
코드 스플리팅: 빌드 도구가 어떻게 코드를 나눌지 결정하고 실제로 파일을 분리하는 빌드 시점의 작업입니다.React.lazy
: React 애플리케이션이 언제, 어떤 컴포넌트를 동적으로 로드할지 결정하는 런타임 메커니즘입니다.
지금 구조를 정확히 알수는 없지만, 아마도 React.lazy
가 조건부 로딩이 되기 보다는 그냥 직접 lazy 하게 로딩되어있는 형태로 되어 있는 것 같습니다.
앞서 설명 드렸던 것 처럼 index-mSjck-Fi.js
파일은 웹사이트를 작동시키는 데 필요한 거의 모든 JS 코드(리액트, 다른 라이브러리, 모든 페이지 섹션 및 기능 코드 등)를 하나로 합쳐놓은 거대한 단일 번들 파일 입니다. 브라우저는 웹사이트를 처음 로드할 때 이 커다란 파일 하나를 통째로 다운로드하고, 분석하고, 실행해야만 비로소 첫 화면을 보여줄 수 있습니다. 이 과정은 순차적으로 진행되며 시간이 오래 걸리기 때문에 초기 로딩 속도(FCP, LCP 등)가 느려지는 주요 원인이 됩니다. 코드 스플리팅을 수행한다면 다음과 같은 이점을 누릴 수 있습니다.
- 초기 로딩 크기 감소: 사용자가 웹사이트에 처음 접속했을 때, 모든 코드를 한 번에 로드할 필요는 없습니다. 당장 첫 화면을 보여주는 데 필요한 최소한의 코드만 담긴 작은 초기 청크 파일만 먼저 로드합니다. 이렇게 하면 초기 다운로드 용량이 크게 줄어들고 자바스크립트 분석 및 실행 시간도 단축되어 사용자가 훨씬 빠르게 첫 화면을 볼 수 있게 됩니다.
- 자바스크립트 번들의 병렬 다운로드 활용:
- 최신 웹 브라우저는 일반적으로 여러 개의 파일을 동시에 다운로드할 수 있는 능력이 있습니다(HTTP/1.1에서는 도메인당 보통 6개, HTTP/2 이상에서는 더 많음).
- 코드 스플리팅 이전: 브라우저는 커다란
index-mSjck-Fi.js
파일 하나만 다운로드합니다. 파일 크기가 크기 때문에 다운로드 시간이 길고, 브라우저의 병렬 다운로드 능력을 자바스크립트 로딩에는 제대로 활용하지 못합니다. - 코드 스플리팅 이후: 브라우저는 우선 작은 초기 청크 파일(들)을 빠르게 다운로드합니다. 그 후 사용자가 다른 페이지로 이동하거나 특정 기능(예: 모달 열기, 특정 섹션으로 스크롤)을 사용하려고 할 때, 필요한 코드 조각(청크)들만 추가로 요청하게 됩니다. 이때 브라우저는 여러 개의 작은 청크 파일들을 동시에 병렬로 다운로드할 수 있습니다.
- 여러 파일을 동시에 다운로드하면, 하나의 큰 파일을 순차적으로 다운로드하는 것보다 전체 다운로드 시간을 크게 단축시킬 수 있습니다. 네트워크 대역폭을 훨씬 효율적으로 사용하게 되는 것입니다. 이는 초기 로딩뿐만 아니라, 웹사이트를 사용하는 도중에 필요한 코드를 불러올 때도 더 빠르고 부드러운 사용자 경험을 제공합니다.
현재 프로젝트는 큰 단일 자바스크립트 번들로 인해 브라우저의 병렬 다운로드 이점을 제대로 활용하지 못하고 초기 로딩이 느려지는 문제를 겪고 있을 가능성이 높습니다. 코드 스플리팅을 적용하면 초기 로딩에 필요한 코드 양을 줄일 뿐만 아니라, 필요한 청크들을 병렬로 효율적으로 다운로드할 수 있게 되어 LCP를 포함한 전반적인 웹사이트 성능을 크게 향상시킬 수 있습니다. 따라서 코드 스플리팅은 이 프로젝트에 꼭 필요한 최적화 작업으로 보입니다.
5. 그 외 조언
소스맵 활용
현재 분석한 코드는 압축되어 있어 디버깅이나 성능 프로파일링이 매우 어렵습니다. 물론 사용자가 직접 사용하는 리얼 환경 에서는 소스맵을 공개하지 않는 것이 원칙입니다. 다만 개발/스테이지 환경이 있다면 소스맵을 업로드 하여 원본 코드 기준으로 문제를 파악하고 성능 병목 지점을 더 쉽게 찾을 수 있도록 환경을 구성하는 것이 좋습니다.
모바일 우선
분석 리포트에도 모바일 환경에 대한 고려가 있었지만, 실제 성능 테스트와 최적화 과정에서 모바일 환경을 우선적으로 점검하는 것이 중요합니다. 데스크톱보다 네트워크나 기기 성능 제약이 크기 때문에 병목 현상이 더 두드러지게 나타날 수 있습니다. 성능분석은 매우 힘들고 시간이 많이 드는 작업이므로, 모바일을 우선해서 성능 개선을 검토해보시는 것을 추천해드립니다.
지속적인 모니터링
일회성 개선에 그치지 않고, 지속적으로 웹사이트 성능을 측정할 수 있는 체계를 구축하는 것이 좋습니다.
Lighthouse CI Action - GitHub Marketplace
https://github.com/NaverPayDev/size-action
Bundle size diff - GitHub Marketplace
위 도구들은 제가 한번 쯤 혹은 프로젝트에서 꾸준히 사용한 도구 입니다. 특히 깃헙액션 쪽을 사용하신다면, 매번 내 PR 이 실제 프로덕션에 어느정도로 영향을 미치지는지 지속적으로 확인할 수 있어 매우 유용합니다.
위와 같은 도구를 활용하시어 꾸준히 성능 개선 내역을 측정해주시면 좋습니다.
6. 마치며
이번에 https://example.com
웹사이트의 성능을 살펴볼 기회를 갖게 되어 감사드립니다. 분석 결과, 초기 로딩 속도(LCP)와 화면 안정성(CLS) 측면에서 사용자 경험을 더욱 향상시킬 수 있는 몇 가지 지점들을 발견할 수 있었습니다.
특히 초기 자바스크립트 로딩 방식과 LCP 이미지 처리 방식을 최적화하는 것이 긍정적인 영향을 줄 수 있을 것으로 생각됩니다. 본 리포트에서 제안 드린 LCP 이미지 Preload 적용, 명시적인 이미지 크기 설정, 그리고 자바스크립트 코드 스플리팅 과 같은 방법들을 고려해 보시면 서비스 개선에 도움이 될 수 있을 것입니다.
제가 성능 분석 작업을 본격적으로 진행해 보는 것은 처음이라 분석 내용에 다소 미흡하거나 부족한 점이 있을 수 있습니다. 조악한 글이라도 모쪼록 너른 양해 부탁드리며, 리포트 내용 중 추가적으로 궁금하신 점이나 논의가 필요한 부분이 있다면 언제든지 편하게 말씀해주시면 감사하겠습니다.
아무쪼록 본 분석 자료가 웹사이트 성능을 개선하고 발전시키는 데 조금이나마 기여할 수 있기를 바랍니다.