애니메이션 라이브러리를 활용해 포트폴리오를 만들면서 처음으로 웹 성능에 대해 고민하게 되었다.
성능, 라이트 하우스 탭을 계속 살펴보며 자바스크립트 번들 줄이는 법 등 이런저런 문서들을 찾아 보았지만, 익숙하지 않은 내용들이라 스스로 해결하기가 어려웠다.
때마침, 네이버 파이넨셜 리드 개발자, 모던 리액트 Deep Dive 저자이신 김용찬 개발자님께서 무료로 성능 분석을 해주신다는 글을 보게 되었고, 곧바로 메일로 요청을 드렸다.
비즈니스 사이트는 아니라서 설마 했는데,
그리고 너무 감사하게도 아주 상세한 피드백을 받을 수 있었다.
그것도 매우 빠르게 ,,, (진짜 빠름)
성능 개선이 필요한 사이트가 있다면 무조건 신청하십쇼
생소했던 웹 성능에 관한 내용들을 자세하게 배울 수 있고, 개선해 볼 수 있으니까요 !!
먼저, 분석 내용을 하나씩 실습해보며 포트폴리오를 개선해봐야겠다고 생각했다.
처음에는 성능 탭과 라이트 하우스의 점수가 다르게 나오지 않아서 메일을 드렸는데,
성능 측정 시 환경 세팅에 대해 친절하게 안내해주셨다.
환경 세팅 후 테스트해보니, 진짜 점수가 다르게 나왔다.
먼저, 라이트하우스와 WebPageTest에서는 꽤 좋은 점수가 나오고 있었다.
하지만 성능 탭에서는 LCP가 4초 대로 떨어진 것을 볼 수 있었다.
왜 LCP가 다르게 나올까? 궁금해졌다.
도구마다 결과가 다른 이유는 단순한 측정 오차가 아니라, 측정 종료 시점을 결정하는 로직과 철학의 차이 때문입니다. Performance 탭은 DevTools가 브라우저의 실제 실행 경로를 그대로 추적합니다. 로드 이벤트가 끝난 이후에도 추가적인 사용자 인터페이스 변화(예: 자바스크립트 애니메이션, 렌더링 등)를 관찰하기 위해, 자동으로 5초를 더 기록합니다.
크로미움 코드베이스에서 다음과 같은 코드를 볼 수 있다.
this.millisecondsToRecordAfterLoadEvent = 5000; // 또는 5e3 (5초)
실제로도 페이지가 로드되고 약 5초 가량 대기한 후에야 성능 측정이 마무리된다.
그렇다면 이 초 단위를 수정해보고, 그리고 실제로 다시 측정해서 점수가 라이트하우스가 낮게 나온다면 이러한 가정이 정말 맞는지 확인해 볼 수 있지 않을까요? 이 대기시간을 수정하는 방법은 다음과 같습니다. 조금 과한 확인일 수도 있지만 재미있을 것 같으니 한번 살펴보겠습니다. 😄
이 부분이 정말 재밌었다. 대기 시간을 낮춘 뒤에 다시 검사했을 때, 라이트하우스와 점수가 비슷하게 나온다면 라이트 하우스의 측정에서 무언가가 누락되었다는 것을 예상할 수 있었기 때문이다.
devtools on devtools
를 연 뒤 다음과 같이 대기 시간을 3초(3000ms)로 줄여보았다.
이렇게 하니 라이트하우스와 비슷하게 좋은 LCP 점수가 나오는 것을 볼 수 있었다.
진짜 궁금해졌다. 그렇다면 라이트 하우스에서 페이지가 완전히 로드되었는지 판단하는 조건은 뭘까?
측정 조건을 알기 위해서는 라이트 하우스의 코어 로직을 살펴볼 필요가 있다.
해당 함수는 아래 세 가지 조건을 순차적으로 확인하며, 그 중 하나라도 만족하지 못하면
maxWaitForLoadedMs
(기본값 30초) 이후 강제 종료됩니다.
Load 이벤트 발생 + 지정된 시간만큼 대기 (pauseAfterLoadMs
)
0ms
이며, Load 이벤트 이후 바로 다음 조건으로 넘어감Network Quiet 상태 유지 (networkQuietThresholdMs
)
5000ms
이상 유지되어야 함"network-2-quiet"
상태로 판단CPU Quiet 상태 유지 (cpuQuietThresholdMs
)
cpuQuietThresholdMs
(예: 5000ms) 이상 지나야 함cpuQuietThresholdMs
가 0일 경우 이 체크는 생략됨종료 조건 경쟁
DevTools의 Performance 탭에서 LCP가 더 느리게 측정되는 이유는 그것이 실제로 유저가 콘텐츠를 보게 되는 순간까지를 감지하려는 목적이기 때문입니다. 반면, Lighthouse는 페이지가 충분히 안정되었다고 판단되면 일찍 측정을 종료하며, 그 이후 나타난 큰 콘텐츠는 LCP 후보로 포함되지 않습니다.
따라서 Lighthouse에서 LCP가 낮게 나오고, Performance 탭에서 높게 나오는 것은 단순한 오차가 아니라 도구의 설계 목표 차이로 이해해야 합니다. 이 차이를 이해하면 어떤 도구에서의 결과가 실제 문제인지를 더 명확히 판단할 수 있습니다.
물론 가장 정확한 성능 측정은 실제 사용자 데이터를 기반으로 한 RUM(Real User Monitoring)입니다. 이를 측정하기 위한 도구인 Lighthouse와 DevTools는 그 보조 수단일 뿐, 진짜 정답은 브라우저가 아니라 사용자의 눈이 가지고 있다는 점을 잊지 마셔야 합니다.
한편으론 정확한 성능 검사를 위해서는 성능 탭만으로 충분하지 않나? 라는 생각이 들었다.
성능에 관해서는 Performance 탭에서 뒤에 등장한 콘텐츠까지 감지할 수 있어 더 정확한 결과를 얻을 수 있는데, 왜 Lighthouse가 필요할까?
하지만 이건 내 포트폴리오와 같은 구조인 경우에 해당하는 내용이고, 일반적으로 제한된 조건에서 성능을 비교하려는 경우 Lighthouse가 필요하다고 한다. 또 구글에서 SEO에 Lighthouse 점수를 반영한다고도 알고 있는데, 앞으로 공부하면서 자연스레 알아가야겠다.
이것저것 서치하면서 Load 이벤트와 LCP는 별개의 개념이라는 걸 알았다. Load 이벤트가 끝난 다음에도 애니메이션 등으로 컨텐츠가 늦게 나타난다면, LCP가 느리게 잡힌다.
지금까지 분석 내용을 정리해보면, Lighthouse와 Performance 탭의 측정 종료 시점을 결정하는 차이로 인해 LCP 측정값이 다르게 나왔던 것이다. 내 포트폴리오 페이지의 렌더링 흐름을 보면 그 이유를 알 수 있다.
앞서 Lighthouse와 Performance 탭 간의 LCP 측정 차이는 측정 종료 시점을 결정하는 기준의 차이에서 발생한다는 점을 설명드렸습니다. 이번 항목에서는 해당 웹사이트의 렌더링 흐름을 바탕으로, 왜 Performance 탭에서 LCP가 상대적으로 더 늦게 측정되는지를 구체적으로 분석해보겠습니다.
페이지 초기 진입 시점에서
div#root
요소는 콘텐츠 없이 빈 상태로 존재합니다. 초기 렌더링 시 뷰포트 내에 표시되는 의미 있는 요소가 없기 때문에, LCP 후보 또한 존재하지 않습니다. 이는 브라우저가 최초로 시각적 콘텐츠를 감지할 수 없는 상태로 해석됩니다.
이후
.introTitle
,.introTitleFill
등의 클래스명을 가진 요소가 등장하며, 짧은 자기소개 문구가 화면에 나타납니다. 해당 동작은 GSAP 기반의 커스텀 애니메이션 구현체를 통해 시퀀스 단위로 재생됩니다. 이 시점에서 해당 텍스트 블록이 LCP 후보로 기록되었을 것입니다. 실제, webpagetest 를 통해 살펴보면 해당 영역이 LCP로 기록되어있음을 볼 수 있습니다.
직접 찾아보니, h1 텍스트 블록이 LCP로 기록되어 있었다.
여기서부터는 포트폴리오의 구조상 문제점에 대한 피드백을 주셨다.
내가 만든 포트폴리오는 이런 인트로 애니메이션이 등장한 뒤 사라지고 난 다음 메인 콘텐츠가 렌더링된다.
- Lighthouse는 내부 로직상 네트워크 및 CPU가 idle 상태에 도달하면 LCP 측정을 종료합니다. 위에서 설명한
updateStep
콜백이 실행되기 이전에 측정이 종료되기 때문에, 실제 콘텐츠가 아닌 자기소개 텍스트가 최종 LCP 후보로 기록됩니다.- Performance 탭은 Load 이벤트 이후에도 5초간 추가로 관찰을 수행합니다. 이로 인해
updateStep
콜백 이후 등장하는 실제 콘텐츠 영역이 LCP 후보로 감지되며, 최종적으로 더 늦은 시점의 콘텐츠가 LCP로 기록됩니다.
따라서 실제 사용자 경험과 가까운 성능 지표를 확보하고자 할 경우, Performance 탭의 결과를 기준으로 판단하거나 RUM 기반 측정 도입을 고려하는 것이 바람직합니다.
지금까지 이해한 대로 내 포트폴리오의 경우에는 Performance 탭의 결과를 기준으로 LCP 측정값을 판단하는 것이 맞는 것이다.
현재 웹사이트는 초기 콘텐츠가 지연되어 등장하는 구조로 인해, 사용자 경험과 성능 측정 지표, 검색 최적화 측면에서 몇 가지 한계를 드러내고 있습니다. 이는 의도적으로 연출된 시각 효과 측면에서는 장점이 될 수 있으나, 실제 운영되는 포트폴리오 웹사이트로서의 목적에는 다소 부합하지 않는 면이 있습니다.
이러한 문제를 해결하기 위해 Progressive Enhancement 원칙에 대해 소개해주셨다. 자바스크립트로 기능을 추가하기 이전에, 먼저 기본적인 형태로 내용을 제공하는 것에 충실하자! 로 이해해 볼 수 있었다.
웹 접근성과 안정성을 보장하기 위한 설계 철학으로, 핵심 콘텐츠와 기능을 가장 기본적인 형태로 먼저 제공한 뒤, 브라우저나 기기의 성능, 사용자의 환경에 따라 점진적으로 시각적·상호작용적 기능을 추가하는 방식을 말합니다.
즉, 콘텐츠와 의미 구조가 항상 최우선으로 제공되어야 하며, 자바스크립트나 고급 스타일링은 기본 기능이 보장된 이후에 선택적으로 동작해야 합니다.
이 원칙은 다양한 사용자 환경을 고려한 웹 개발의 기본 전략으로 널리 채택되고 있으며, 성능 최적화, 접근성, SEO 개선 등에도 직접적인 영향을 미칩니다.
가장 우선적으로 고려해야 할 개선 사항은 주요 콘텐츠를 초기 HTML에 포함시키는 것입니다. 현재 구조에서는 인트로 애니메이션이 완료된 이후에야 실질적인 콘텐츠가 자바스크립트를 통해 마운트되기 때문에, 브라우저 및 성능 측정 도구는 콘텐츠가 존재하지 않는 페이지로 인식하게 됩니다.
이러한 콘텐츠 지연 노출 구조는 LCP와 같은 성능 지표를 악화시킬 뿐만 아니라, 검색 엔진이 콘텐츠를 적절히 인덱싱하지 못해 SEO 측면에서도 불리한 결과를 초래할 수 있습니다.
애니메이션을 적용할 때는 콘텐츠 자체의 렌더링 시점을 지연시키는 방식보다는, 이미 렌더링된 콘텐츠에 시각적 스타일을 통해 점진적으로 등장하는 효과를 주는 방식이 바람직합니다.
예를 들어, 현재 구조처럼 자바스크립트 실행 후 특정 DOM 노드를 생성하고 마운트하는 방식은 브라우저와 성능 측정 도구가 해당 콘텐츠를 늦게 인식하도록 만들며, LCP, FCP 등의 지표가 불리하게 측정될 수 있습니다. 또한 콘텐츠가 늦게 삽입되면 검색 엔진 크롤러가 콘텐츠를 발견하지 못할 가능성도 있습니다.
포트폴리오의 성격상 첫 방문자에게는 시각적으로 인상적인 연출을 제공하는 것이 중요할 수 있습니다. 그러나 반복 방문 사용자에게는 콘텐츠 접근성이 더 중요합니다. 이에 따라 방문 이력을 기반으로 애니메이션 실행 여부를 분기하는 전략이 유효할 수도 있습니다.
위와 같이 근본적인 문제들에 대한 개선 방안을 제시해주셨다.
이외에도, 코드 스플리팅을 통한 번들 최적화를 제안 등 디테일한 성능 개선 전략을 알려주셔서 감사했다. 이 부분에 대해서는 실제로 포트폴리오를 개선하면서 2탄으로 올릴 예정이다!
이렇게 생생한 후기를 보내드렸다. 포트폴리오를 만들어 놓길 잘했다는 생각이 든다...
구조적으로도 아쉬움이 남았는데, 다시 개선해 볼 수 있는 계기가 되어 다행이라고 생각하고 멋찐 포트폴리오로 완성해봐야겠다.
자세하게 포트폴리오 웹 성능을 분석해주시고, 응원까지 해주신 저자님께 다시 한 번 감사드립니다 😀
그럼 열심히 개선해서 돌아오겠습니다,,