HTML 및 상호작용의 클라이언트 측 렌더링

JavaScript로 HTML을 렌더링하는 것은 서버에서 전송하는 HTML을 렌더링하는 것과 다르며 성능에 영향을 줄 수 있습니다. 이 가이드의 차이점과 웹사이트의 렌더링 성능을 유지하기 위해 취할 수 있는 조치(특히 상호작용과 관련된 경우)에 대해 알아보세요.

HTML 파싱 및 렌더링은 브라우저에 내장된 탐색 로직('기존 페이지 로드'라고도 함)을 사용하는 웹사이트에서 기본적으로 브라우저가 매우 잘 수행하는 작업입니다. '하드 탐색'으로 설정할 수 있습니다. 이러한 웹사이트를 멀티 페이지 애플리케이션 (MPA)이라고도 합니다.

그러나 개발자는 애플리케이션의 요구에 맞게 브라우저 기본값을 우회할 수 있습니다. 자바스크립트를 통해 클라이언트에서 HTML/DOM의 많은 부분을 동적으로 생성하는 단일 페이지 애플리케이션 (SPA) 패턴을 사용하는 웹사이트의 경우 확실히 그렇습니다. 클라이언트 측 렌더링이 이 디자인 패턴의 이름이며, 관련 작업이 과도한 경우 웹사이트의 다음 페인트에 대한 상호작용 (INP)에 영향을 미칠 수 있습니다.

이 가이드는 서버에서 브라우저로 전송한 HTML을 사용하는 것과 JavaScript를 사용하여 클라이언트에서 HTML을 작성하는 것의 차이, 그리고 JavaScript를 사용하여 중요한 순간에 긴 상호작용 지연 시간을 초래하는 경우의 차이를 평가하는 데 도움을 줍니다.

기존 페이지 로드에서 사용되는 탐색 패턴에는 탐색 시마다 서버로부터 HTML을 수신하는 것이 포함됩니다. 브라우저의 주소 표시줄에 URL을 입력하거나 MPA에서 링크를 클릭하면 다음과 같은 일련의 이벤트가 발생합니다.

  1. 브라우저가 제공된 URL에 탐색 요청을 보냅니다.
  2. 서버는 청크로 된 HTML을 사용하여 응답합니다.

이 단계의 마지막 단계가 핵심입니다. 또한 서버/브라우저 교환에서 가장 기본적인 성능 최적화 중 하나이며 스트리밍으로 알려져 있습니다. 서버가 가능한 한 빨리 HTML을 전송할 수 있고 브라우저가 전체 응답이 도착할 때까지 기다리지 않으면 브라우저는 HTML이 도착할 때 분할하여 처리할 수 있습니다.

<ph type="x-smartling-placeholder">
</ph> 서버에서 전송한 HTML의 파싱을 Chrome DevTools의 성능 패널에 시각화한 스크린샷 HTML이 스트리밍되면 청크가 여러 개의 짧은 작업에서 처리되고 렌더링이 증분됩니다. <ph type="x-smartling-placeholder">
</ph> Chrome DevTools의 성능 패널에 시각화된 대로 서버에서 제공하는 HTML의 파싱 및 렌더링 HTML을 파싱하고 렌더링하는 작업과 관련된 작업은 청크로 분할됩니다.

브라우저에서 발생하는 대부분의 것과 마찬가지로 HTML 파싱은 작업 내에서 발생합니다. HTML이 서버에서 브라우저로 스트리밍되면 브라우저는 해당 스트림의 비트가 청크로 도착할 때 한 번에 조금씩 이러한 작업을 수행하여 HTML의 파싱을 최적화합니다. 그 결과, 브라우저는 각 청크를 처리한 후 주기적으로 메인 스레드에 의존하며, 이로 인해 장기 작업을 피할 수 있습니다. 즉, HTML 파싱 중에 사용자에게 페이지를 표시하는 데 필요한 점진적 렌더링 작업 및 페이지의 중요한 시작 기간 동안 발생할 수 있는 사용자 상호작용을 처리하는 작업 등 다른 작업이 발생할 수 있습니다. 이 접근 방식을 사용하면 페이지의 다음 페인트에 대한 상호작용 (INP) 점수가 향상됩니다.

이 사실은 무엇을 시사할까요? 서버에서 HTML을 스트리밍하면 HTML을 점진적으로 파싱하고 렌더링하며 기본 스레드에 무료로 자동 생성할 수 있습니다. 클라이언트 측 렌더링에서는 이를 얻지 못합니다.

브라우저가 JavaScript에서 제공하는 HTML을 렌더링하는 방법

페이지에 대한 모든 탐색 요청은 일정량의 HTML을 서버가 제공해야 하지만, 일부 웹사이트에서는 SPA 패턴을 사용합니다. 이 방식은 대개 서버에서 제공하는 HTML의 최소한의 초기 페이로드를 포함하지만, 클라이언트가 페이지에서 가져온 데이터로 조합된 HTML로 페이지의 기본 콘텐츠 영역을 채웁니다. 후속 탐색('소프트 탐색'이라고도 함) 이 경우 는 완전히 JavaScript에 의해 처리되어 페이지를 새 HTML로 채웁니다.

클라이언트 측 렌더링은 HTML이 JavaScript를 통해 DOM에 동적으로 추가되는 좀 더 제한적인 경우 비SPA에서도 발생할 수 있습니다.

자바스크립트를 통해 HTML을 만들거나 DOM에 추가하는 몇 가지 일반적인 방법이 있습니다.

  1. innerHTML 속성을 사용하면 브라우저가 DOM으로 파싱하는 문자열을 통해 기존 요소의 콘텐츠를 설정할 수 있습니다.
  2. document.createElement 메서드를 사용하면 브라우저 HTML 파싱을 사용하지 않고 DOM에 추가할 새 요소를 만들 수 있습니다.
  3. document.write 메서드를 사용하면 문서에 HTML을 쓸 수 있습니다 (1번 접근 방식과 마찬가지로 브라우저가 문서를 파싱함). 하지만 여러 가지 이유로 인해 document.write 사용은 권장하지 않습니다.
Chrome DevTools의 성능 패널에 시각화된 JavaScript를 통해 렌더링된 HTML의 파싱 스크린샷 작업은 기본 스레드를 차단하는 긴 단일 작업에서 발생합니다.
Chrome DevTools의 성능 패널에 시각화된 대로 클라이언트에서 JavaScript를 통해 HTML 파싱 및 렌더링 파싱 및 렌더링과 관련된 작업이 청크로 분할되지 않아 기본 스레드를 차단하는 작업이 길어집니다.

클라이언트 측 자바스크립트를 통해 HTML/DOM을 만들면 상당한 결과가 발생할 수 있습니다.

  • 탐색 요청에 대한 응답으로 서버에서 스트리밍하는 HTML과 달리 클라이언트의 JavaScript 작업은 자동으로 분할되지 않으므로 기본 스레드를 차단하는 긴 작업이 발생할 수 있습니다. 즉, 클라이언트에서 한 번에 너무 많은 HTML/DOM을 만들면 페이지의 INP에 부정적인 영향을 줄 수 있습니다.
  • 시작 중에 클라이언트에서 HTML이 생성되면 HTML에서 참조된 리소스는 브라우저 미리 로드 스캐너에서 검색되지 않습니다. 이는 페이지의 최대 콘텐츠 페인트 (LCP)에 부정적인 영향을 미칩니다. 이는 런타임 성능 문제는 아니지만 (대신 중요한 리소스를 가져오는 데 네트워크 지연 문제임) 이 기본적인 브라우저 성능 최적화를 회피하여 웹사이트의 LCP에 영향을 받는 것은 바람직하지 않습니다.

클라이언트 측 렌더링이 성능에 미치는 영향에 대해 취할 수 있는 조치

웹사이트에서 클라이언트 측 렌더링에 크게 의존하는 경우 필드 데이터의 INP 값이 좋지 않음이 발견된 경우 클라이언트 측 렌더링이 문제와 관련이 있는지 궁금할 수 있습니다. 예를 들어 웹사이트가 SPA인 경우 필드 데이터는 상당한 렌더링 작업을 담당하는 상호작용을 표시할 수 있습니다.

원인이 무엇이든, 다음 몇 가지 원인을 탐색하여 문제를 해결할 수 있습니다.

서버에서 가능한 한 많은 HTML 제공

앞서 언급했듯이 브라우저는 기본적으로 매우 우수한 성능의 방식으로 서버의 HTML을 처리합니다. 이는 긴 작업을 피하는 방식으로 HTML의 파싱 및 렌더링을 분할하고 총 기본 스레드 시간을 최적화합니다. 그 결과 총 차단 시간 (TBT)이 줄어들고 TBT는 INP와 밀접한 관련이 있습니다.

웹사이트를 구축하기 위해 프런트엔드 프레임워크를 사용하고 있을 수 있습니다. 그렇다면 서버에서 구성요소 HTML을 렌더링하고 있는지 확인하는 것이 좋습니다. 이렇게 하면 웹사이트에서 필요한 초기 클라이언트 측 렌더링 시간이 제한되므로 더 나은 환경을 제공할 수 있습니다.

  • React의 경우 Server DOM API를 사용하여 서버에서 HTML을 렌더링하는 것이 좋습니다. 하지만 기존의 서버 측 렌더링 방식은 동기식 접근 방식을 사용하므로 첫 바이트까지의 시간 (TTFB)은 물론 첫 콘텐츠 페인트 (FCP), LCP와 같은 후속 측정항목이 더 길어질 수 있습니다. 가능한 경우 서버가 최대한 빨리 브라우저로 HTML 스트리밍을 시작할 수 있도록 Node.js 또는 기타 JavaScript 런타임용 스트리밍 API를 사용하고 있는지 확인하세요. React 기반 프레임워크인 Next.js는 기본적으로 많은 권장사항을 제공합니다. 서버에서 HTML을 자동으로 렌더링하는 것 외에도 사용자 컨텍스트 (예: 인증)에 따라 변경되지 않는 페이지의 HTML을 정적으로 생성할 수도 있습니다.
  • Vue는 기본적으로 클라이언트 측 렌더링도 실행합니다. 그러나 React와 마찬가지로 Vue는 서버에서 구성요소 HTML을 렌더링할 수도 있습니다. 가능한 경우 이러한 서버 측 API를 활용하거나 Vue 프로젝트에 더 높은 수준의 추상화를 사용하여 권장사항을 더 쉽게 구현하세요.
  • Svelte는 기본적으로 서버에서 HTML을 렌더링합니다. 하지만 구성요소 코드가 브라우저 독점 네임스페이스 (예: window)에 액세스해야 하는 경우 서버에서 구성요소의 HTML을 렌더링하지 못할 수도 있습니다. 불필요한 클라이언트 측 렌더링이 발생하지 않도록 가능한 경우 대체 접근 방식을 살펴보세요. SvelteKit(Next.js가 React로 나타남)는 Svelte 프로젝트에 최대한 많은 권장사항을 포함하기 때문에 Svelte만 사용하는 프로젝트에서 잠재적인 문제를 피할 수 있습니다.

클라이언트에서 생성되는 DOM 노드의 양을 제한합니다.

DOM이 크면 렌더링하는 데 필요한 처리가 증가하는 경향이 있습니다. 웹사이트가 완전한 SPA이거나 MPA에 대한 상호작용의 결과로 기존 DOM에 새 노드를 삽입하든 상관없이 이러한 DOM은 가능한 한 작게 유지하는 것이 좋습니다. 이렇게 하면 클라이언트 측 렌더링 중에 해당 HTML을 표시하기 위해 필요한 작업을 줄일 수 있으므로 웹사이트의 INP를 낮게 유지하는 데 도움이 됩니다.

스트리밍 서비스 워커 아키텍처를 고려

이는 고급 기법으로 모든 사용 사례에서 쉽게 작동하지 않을 수 있지만 사용자가 한 페이지에서 다음 페이지로 이동할 때 MPA를 즉시 로드되는 것처럼 느끼는 웹사이트로 전환할 수 있습니다. 서비스 워커를 사용하면 CacheStorage에 웹사이트의 정적 부분을 사전 캐시하고 ReadableStream API를 사용하여 서버에서 페이지의 나머지 HTML을 가져올 수 있습니다.

이 기술을 성공적으로 사용하면 클라이언트에서 HTML이 생성되지는 않지만, 캐시에서 콘텐츠 일부를 즉시 로드하면 사이트가 빠르게 로드되고 있다는 인상을 줄 수 있습니다. 이 접근 방식을 사용하는 웹사이트는 SPA처럼 느껴질 수 있지만 클라이언트 측 렌더링의 단점은 없습니다. 또한 서버에 요청하는 HTML의 양을 줄입니다.

간단히 말해 스트리밍 서비스 워커 아키텍처는 브라우저에 내장된 탐색 로직을 대체하는 것이 아니라 추가합니다. Workbox를 사용하여 이를 달성하는 방법에 관한 자세한 내용은 스트림으로 멀티 페이지 애플리케이션 속도 향상을 참고하세요.

결론

웹사이트에서 HTML을 수신하고 렌더링하는 방식은 실적에 영향을 미칩니다. 웹사이트가 작동하는 데 필요한 HTML을 전부 (또는 대량) 전송하는 데 서버에 의존하면 점진적 파싱 및 렌더링, 장기 작업을 피하기 위해 기본 스레드에 자동 생성되는 많은 이점을 무료로 얻을 수 있습니다.

클라이언트 측 HTML 렌더링은 많은 경우에 피할 수 있는 여러 가지 잠재적인 성능 문제를 일으킵니다. 그러나 개별 웹사이트의 요구사항 때문에 100% 완벽하게 피할 수는 없습니다. 과도한 클라이언트 사이트 렌더링으로 인해 발생할 수 있는 긴 작업을 방지하려면 가능한 한 많은 웹사이트 HTML을 서버에서 전송하고 있는지 확인하고, 클라이언트에서 렌더링되어야 하는 HTML의 DOM 크기를 가능한 한 작게 유지하고, 브라우저가 서버에서 로드되는 HTML의 점진적 파싱 및 렌더링을 제공하는 점진적 파싱 및 렌더링을 활용할 수 있는 대안 아키텍처를 고려해야 합니다.

웹사이트의 클라이언트 측 렌더링을 가능한 한 최소화하면 웹사이트의 INP뿐만 아니라 LCP, TBT, 경우에 따라 TTFB와 같은 다른 측정항목도 개선할 수 있습니다.

Unsplash의 히어로 이미지, 마이크 조니츠 제공