JavaScript로 HTML을 렌더링하는 것은 서버에서 전송하는 HTML을 렌더링하는 것과 다르며, 이는 성능에 영향을 줄 수 있습니다. 이 가이드에서 두 가지의 차이점을 알아보고, 특히 상호작용과 관련하여 웹사이트의 렌더링 성능을 보존하기 위해 취할 수 있는 조치를 알아보세요.
HTML 파싱 및 렌더링은 브라우저의 내장 탐색 로직('기존 페이지 로드' 또는 '하드 탐색'이라고도 함)을 사용하는 웹사이트의 경우 브라우저에서 기본적으로 매우 잘 처리합니다. 이러한 웹사이트를 멀티페이지 애플리케이션 (MPA)이라고도 합니다.
그러나 개발자는 애플리케이션 요구사항에 맞게 브라우저 기본값을 우회할 수 있습니다. 이는 JavaScript로 클라이언트에서 HTML/DOM의 상당 부분을 동적으로 만드는 단일 페이지 애플리케이션 (SPA) 패턴을 사용하는 웹사이트에 특히 해당합니다. 클라이언트 측 렌더링은 이 디자인 패턴의 이름으로, 관련 작업이 과도하면 웹사이트의 다음 페인트에 대한 상호작용 (INP)에 영향을 미칠 수 있습니다.
이 가이드에서는 서버에서 브라우저로 전송된 HTML을 사용하는 것과 클라이언트에서 JavaScript로 HTML을 만드는 것의 차이점과 후자가 중요한 순간에 높은 상호작용 지연을 초래할 수 있는 이유를 설명합니다.
브라우저가 서버에서 제공한 HTML을 렌더링하는 방법
기존 페이지 로드에 사용되는 탐색 패턴은 탐색할 때마다 서버에서 HTML을 수신하는 것을 포함합니다. 브라우저의 주소 표시줄에 URL을 입력하거나 MPA의 링크를 클릭하면 다음과 같은 일련의 이벤트가 발생합니다.
- 브라우저가 제공된 URL에 대한 탐색 요청을 보냅니다.
- 서버는 HTML로 응답합니다.
이 중 마지막 단계가 중요합니다. 또한 서버/브라우저 교환에서 가장 기본적인 성능 최적화 중 하나이며 스트리밍이라고도 합니다. 서버가 최대한 빨리 HTML 전송을 시작할 수 있고 브라우저가 전체 응답이 도착할 때까지 기다리지 않으면 브라우저는 HTML이 도착할 때마다 HTML을 청크 단위로 처리할 수 있습니다.

브라우저에서 발생하는 대부분의 작업과 마찬가지로 HTML 파싱은 태스크 내에서 발생합니다. HTML이 서버에서 브라우저로 스트리밍되면 브라우저는 스트림의 비트가 청크 단위로 도착할 때마다 한 번에 조금씩 HTML을 파싱하여 파싱을 최적화합니다. 그 결과 브라우저는 각 청크를 처리한 후 주기적으로 기본 스레드에 양보하므로 긴 작업이 방지됩니다. 즉, HTML이 파싱되는 동안 사용자에게 페이지를 표시하는 데 필요한 증분 렌더링 작업과 페이지의 중요한 시작 기간 동안 발생할 수 있는 사용자 상호작용 처리를 비롯한 다른 작업이 발생할 수 있습니다. 이 접근 방식은 페이지의 다음 페인트에 대한 상호작용 (INP) 점수를 개선하는 데 도움이 됩니다.
이 사실은 무엇을 시사할까요? 서버에서 HTML을 스트리밍하면 HTML의 증분 파싱 및 렌더링과 기본 스레드에 대한 자동 반환이 무료로 제공됩니다. 클라이언트 측 렌더링에서는 이러한 이점을 얻을 수 없습니다.
브라우저가 JavaScript에서 제공하는 HTML을 렌더링하는 방법
페이지로의 모든 탐색 요청에는 서버에서 제공해야 하는 HTML이 어느 정도 필요하지만 일부 웹사이트에서는 SPA 패턴을 사용합니다. 이 접근 방식에서는 서버에서 최소한의 초기 HTML 페이로드를 제공하지만 클라이언트는 서버에서 가져온 데이터로 조합된 HTML로 페이지의 기본 콘텐츠 영역을 채웁니다. 후속 탐색(이 경우 '조용히 탐색'이라고도 함)은 JavaScript에서 완전히 처리하여 페이지를 새 HTML로 채웁니다.
JavaScript를 통해 HTML이 DOM에 동적으로 추가되는 제한된 경우에 SPA가 아닌 곳에서도 클라이언트 측 렌더링이 발생할 수 있습니다.
JavaScript를 통해 HTML을 만들거나 DOM에 추가하는 몇 가지 일반적인 방법이 있습니다.
innerHTML
속성을 사용하면 브라우저에서 DOM으로 파싱하는 문자열을 통해 기존 요소의 콘텐츠를 설정할 수 있습니다.document.createElement
메서드를 사용하면 브라우저 HTML 파싱을 사용하지 않고도 DOM에 추가할 새 요소를 만들 수 있습니다.document.write
메서드를 사용하면 문서에 HTML을 쓸 수 있으며 브라우저는 접근 방식 1과 마찬가지로 이를 파싱합니다. 그러나 여러 가지 이유로document.write
사용은 권장하지 않습니다.

클라이언트 측 JavaScript를 통해 HTML/DOM을 생성하면 상당한 결과가 초래될 수 있습니다.
- 탐색 요청에 대한 응답으로 서버에서 스트리밍하는 HTML과 달리 클라이언트의 JavaScript 작업은 자동으로 청크되지 않으므로 기본 스레드를 차단하는 긴 작업이 발생할 수 있습니다. 즉, 클라이언트에서 한 번에 너무 많은 HTML/DOM을 만들면 페이지의 INP에 부정적인 영향을 미칠 수 있습니다.
- 시작 중에 클라이언트에서 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만 사용하는 프로젝트에서 발생할 수 있는 잠재적 문제를 방지할 수 있도록 최대한 많은 권장사항을 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에 대해 브라우저에서 제공하는 증분 파싱 및 렌더링을 활용하면서 클라이언트로 HTML을 더 빠르게 전송하는 대체 아키텍처를 고려하세요.
웹사이트의 클라이언트 측 렌더링을 최대한 최소화할 수 있다면 웹사이트의 INP뿐만 아니라 LCP, TBT, 경우에 따라 TTFB와 같은 다른 측정항목도 개선할 수 있습니다.
Unsplash의 Maik Jonietz님 제공 히어로 이미지