게시일: 2019년 2월 6일, 최종 업데이트: 2026년 1월 5일
웹 개발자가 내려야 하는 핵심 결정 중 하나는 애플리케이션에서 로직 과 렌더링을 구현할 위치입니다. 웹사이트를 빌드하는 방법이 너무 많기 때문에 어려울 수 있습니다.
이 공간에 대한 Google의 이해는 지난 몇 년 동안 Chrome에서 대규모 사이트와 대화한 Google의 작업을 기반으로 합니다. 일반적으로 개발자는 서버 측 렌더링 또는 정적 렌더링보다 완전한 리하이드레이션 접근 방식을 고려하는 것이 좋습니다.
이 결정을 내릴 때 선택하는 아키텍처를 더 잘 이해하려면 각 접근 방식에 일관된 용어와 공유 프레임워크 가 필요합니다. 그런 다음 페이지 성능의 관점에서 각 렌더링 접근 방식의 장단점을 더 잘 평가할 수 있습니다.
용어
먼저 사용할 용어를 정의합니다.
렌더링
- 서버 측 렌더링 (SSR)
- 클라이언트에 JavaScript가 아닌 HTML을 전송하기 위해 서버에서 앱을 렌더링합니다.
- 클라이언트 측 렌더링 (CSR)
- JavaScript를 사용하여 DOM을 수정하여 브라우저에서 앱을 렌더링합니다.
- 사전 렌더링
- 빌드 시 클라이언트 측 애플리케이션을 실행하여 초기 상태를 정적 HTML로 캡처합니다. 이 의미의 "사전 렌더링"은 향후 탐색의 브라우저 사전 렌더링과 다릅니다.
- 하이드레이션
- 클라이언트 측 스크립트를 실행하여 서버에서 렌더링된 HTML에 애플리케이션 상태와 상호작용을 추가합니다. 하이드레이션은 DOM이 변경되지 않는다고 가정합니다.
- 리하이드레이션
- 하이드레이션과 같은 의미로 사용되는 경우가 많지만 리하이드레이션은 초기 하이드레이션 후를 포함하여 최신 상태로 DOM을 정기적으로 업데이트하는 것을 의미합니다.
성능
- 첫 바이트까지의 시간 (TTFB)
- 링크를 클릭한 시간과 새 페이지에 콘텐츠의 첫 번째 바이트가 로드되는 시간 사이입니다.
- 첫 콘텐츠 페인트 (FCP)
- 요청된 콘텐츠 (기사 본문 등)가 표시되는 시간입니다.
- 다음 페인트에 대한 상호작용 (INP)
- 페이지가 사용자 입력에 일관되게 빠르게 응답하는지 평가하는 대표적인 측정항목입니다.
- 총 차단 시간 (TBT)
- 페이지 로드 중에 기본 스레드가 차단된 시간을 계산하는 INP의 프록시 측정항목 입니다.
서버 측 렌더링
서버 측 렌더링은 탐색에 대한 응답으로 서버에서 페이지의 전체 HTML을 생성합니다. 렌더러가 브라우저 가 응답을 받기 전에 처리하므로 클라이언트에서 데이터 가져오기 및 템플릿을 위한 추가 왕복이 발생하지 않습니다.
서버 측 렌더링은 일반적으로 빠른 FCP를 생성합니다. 서버에서 페이지 로직을 실행하고 렌더링하면 클라이언트에 많은 JavaScript를 전송하지 않아도 됩니다. 이렇게 하면 페이지 로드 중에 기본 스레드가 자주 차단되지 않으므로 페이지의 TTBT를 줄일 수 있으며 INP도 낮아질 수 있습니다. 기본 스레드가 차단되는 빈도가 낮을수록 사용자 상호작용이 더 빨리 실행될 수 있습니다.
서버 측 렌더링을 사용하면 사용자 브라우저에 텍스트와 링크만 전송하기 때문에 이 방법이 적합합니다. 이 접근 방식은 다양한 기기 및 네트워크 조건에서 잘 작동할 수 있으며 스트리밍 문서 파싱과 같은 흥미로운 브라우저 최적화를 제공합니다.
서버 측 렌더링을 사용하면 사용자가 사이트를 사용하기 전에 CPU 바운드 JavaScript가 실행될 때까지 기다릴 가능성이 줄어듭니다. 서드 파티 JavaScript를 피할 수 없는 경우에도 서버 측 렌더링을 사용하여 자체 퍼스트 파티 JavaScript 비용을 줄이면 나머지 예산을 더 많이 확보할 수 있습니다. 그러나 이 접근 방식에는 한 가지 잠재적인 절충안이 있습니다. 서버에서 페이지를 생성하는 데 시간이 걸리므로 페이지의 TTFB가 증가할 수 있습니다.
서버 측 렌더링이 애플리케이션에 충분한지 여부는 빌드하는 환경의 유형에 따라 크게 달라집니다. 서버 측 렌더링과 클라이언트 측 렌더링의 올바른 애플리케이션에 대한 오랜 논쟁이 있지만 일부 페이지에 서버 측 렌더링을 사용하고 다른 페이지에는 사용하지 않도록 선택할 수 있습니다. 일부 사이트에서는 하이브리드 렌더링 기술을 성공적으로 채택했습니다. 예를 들어 Netflix 는 상대적으로 정적인 방문 페이지를 서버에서 렌더링하는 동시에 prefetching 하여 상호작용이 많은 페이지의 JavaScript를 클라이언트에서 렌더링되는 이러한 더 무거운 페이지가 빠르게 로드될 가능성을 높입니다.
최신 프레임워크, 라이브러리, 아키텍처를 사용하면 클라이언트와 서버 모두에서 동일한 애플리케이션을 렌더링할 수 있습니다. 서버 측 렌더링에 이러한 기술을 사용할 수 있습니다. 그러나 서버 및 클라이언트 모두에서 렌더링이 발생하는 아키텍처는 성능 특성과 절충안이 매우 다른 자체 솔루션 클래스입니다. React 사용자는 서버 측 렌더링에 서버 DOM API 또는 이를 기반으로 빌드된 솔루션(예: Next.js)을 사용할 수 있습니다. Vue 사용자는 Vue의 서버 측 렌더링 가이드 또는 Nuxt를 사용할 수 있습니다. Angular에는 Universal이 있습니다.
가장 인기 있는 솔루션은 일부 형태의 하이드레이션을 사용하므로 도구에서 사용하는 접근 방식을 알고 있어야 합니다.
정적 렌더링
정적 렌더링 은 빌드 시에 발생합니다. 이 접근 방식은 페이지의 클라이언트 측 JavaScript 양을 제한하는 한 빠른 FCP와 낮은 TBT 및 INP를 제공합니다. 서버 측 렌더링과 달리 페이지의 HTML을 서버에서 동적으로 생성할 필요가 없으므로 일관되게 빠른 TTFB를 달성합니다. 일반적으로 정적 렌더링은 각 URL에 대해 별도의 HTML 파일을 미리 생성하는 것을 의미합니다. 미리 생성된 HTML 응답을 사용하면 정적 렌더링을 여러 CDN에 배포하여 에지 캐싱을 활용할 수 있습니다.
정적 렌더링 솔루션은 다양한 모양과 크기로 제공됩니다. Gatsby 11ty, Jekyll, Metalsmith와 같은 정적 사이트 생성 도구는 정적 특성을 채택하여 템플릿 기반 접근 방식을 제공합니다.
정적 렌더링의 단점 중 하나는 가능한 모든 URL에 대해 개별 HTML 파일을 생성해야 한다는 것입니다. 이러한 URL을 미리 예측해야 하고 고유한 페이지 수가 많은 사이트의 경우 어려울 수 있으며 불가능할 수도 있습니다.
React 사용자는 Gatsby, Next.js 정적 내보내기 또는 Navi에 익숙할 수 있습니다. 이러한 모든 기능을 사용하면 구성요소에서 페이지를 편리하게 만들 수 있습니다. 그러나 정적 렌더링과 사전 렌더링은 다르게 동작합니다. 정적으로 렌더링된 페이지는 많은 클라이언트 측 JavaScript를 실행하지 않고도 상호작용할 수 있는 반면 사전 렌더링은 페이지를 진정으로 상호작용할 수 있도록 클라이언트에서 부팅해야 하는 단일 페이지 애플리케이션의 FCP를 개선합니다.
특정 솔루션이 정적 렌더링인지 사전 렌더링인지 확실하지 않은 경우 JavaScript를 사용 중지하고 테스트하려는 페이지를 로드해 보세요. 정적으로 렌더링된 페이지의 경우 JavaScript가 없어도 대부분의 상호작용 기능이 여전히 존재합니다. 사전 렌더링된 페이지에는 JavaScript가 사용 중지된 링크와 같은 기본 기능이 있을 수 있지만 대부분의 페이지는 비활성 상태입니다.
또 다른 유용한 테스트는 Chrome DevTools에서 네트워크 제한 을 사용하고 페이지가 상호작용하기 전에 다운로드되는 JavaScript의 양을 확인하는 것입니다. 사전 렌더링은 일반적으로 상호작용하기 위해 더 많은 JavaScript가 필요하며 이 JavaScript는 정적 렌더링에 사용되는 점진적 개선 접근 방식보다 더 복잡한 경향이 있습니다.
서버 측 렌더링과 정적 렌더링 비교
서버 측 렌더링은
동적 특성으로 인해 상당한 컴퓨팅 오버헤드 비용이 발생할 수 있으므로 모든 경우에 가장 적합한 솔루션은 아닙니다. 많은 서버 측
렌더링 솔루션은 조기에 플러시하지 않거나 TTFB를 지연하거나 전송되는 데이터를 두 배로 늘립니다
(예: 클라이언트의 JavaScript에서 사용되는 인라인 상태). React에서
renderToString()은 동기식 및 단일 스레드이므로 느릴 수 있습니다.
최신 React 서버 DOM API
는 스트리밍을 지원하므로 서버에서 나머지 부분이 계속 생성되는 동안 HTML 응답의 초기 부분을
브라우저에 더 빨리 가져올 수 있습니다.
서버 측 렌더링을 "올바르게" 가져오려면 구성요소 캐싱 솔루션을 찾거나 빌드하고 구성요소 캐싱, 메모리 소비를 관리하고 메모이제이션 기술을 사용하는 등의 문제가 발생할 수 있습니다. 클라이언트에서 한 번, 서버에서 한 번 동일한 앱을 두 번 처리하거나 다시 빌드하는 경우가 많습니다. 서버 측 렌더링에서 콘텐츠를 더 빨리 표시한다고 해서 작업량이 줄어드는 것은 아닙니다. 서버에서 생성된 HTML 응답이 클라이언트에 도착한 후 클라이언트에서 많은 작업 을 수행해야 하는 경우 웹사이트의 TBT 및 INP가 여전히 높아질 수 있습니다.
서버 측 렌더링은 각 URL에 대해 요청 시 HTML을 생성하지만 정적으로 렌더링된 콘텐츠를 제공하는 것보다 느릴 수 있습니다. 추가 작업을 수행할 수 있다면 서버 측 렌더링과 HTML 캐싱 을 통해 서버 렌더링 시간을 크게 줄일 수 있습니다. 서버 측 렌더링의 장점 은 정적 렌더링으로 가능한 것보다 더 많은 "실시간" 데이터를 가져오고 더 완전한 요청 집합 에 응답할 수 있다는 것입니다. 개인 맞춤설정이 필요한 페이지는 정적 렌더링과 잘 작동하지 않는 요청 유형의 구체적인 예입니다.
서버 측 렌더링은 PWA를 빌드할 때 흥미로운 결정을 내릴 수도 있습니다. PWA 전체 페이지 서비스 워커 캐싱을 사용하는 것이 더 나은가요, 아니면 콘텐츠의 개별 부분을 서버에서 렌더링하는 것이 더 나은가요?
클라이언트 측 렌더링
클라이언트 측 렌더링은 JavaScript를 사용하여 브라우저에서 직접 페이지를 렌더링하는 것을 의미합니다. 모든 로직, 데이터 가져오기, 템플릿, 라우팅은 서버가 아닌 클라이언트에서 처리됩니다. 실질적인 결과는 서버에서 사용자 기기로 더 많은 데이터가 전달되며 자체 절충안이 있다는 것입니다.
클라이언트 측 렌더링은 휴대기기에서 빠르게 만들고 유지하기 어려울 수 있습니다.
JavaScript 예산을 엄격하게 유지하고
가능한 한 적은 왕복으로
가치를 제공하기 위해 약간의 작업을 수행하면 클라이언트 측 렌더링이 순수 서버 측 렌더링의 성능을 거의 복제하도록 할 수 있습니다.
를 사용하여 중요한 스크립트와 데이터를 제공하면 파서가 더 빠르게 작동하도록 할 수 있습니다. 또한 초기 및 후속 탐색이 즉각적으로 느껴지도록 PRPL
와 같은 패턴을 사용하는 것이 좋습니다.<link rel=preload>
클라이언트 측 렌더링의 주요 단점은 애플리케이션이 커짐에 따라 필요한 JavaScript 의 양이 증가하는 경향이 있어 페이지의 INP에 영향을 줄 수 있다는 것입니다. 처리 능력을 놓고 경쟁하고 페이지 콘텐츠를 렌더링하기 전에 처리해야 하는 경우가 많은 새 JavaScript 라이브러리, 폴리필, 서드 파티 코드가 추가되면 특히 어려워집니다.
클라이언트 측 렌더링을 사용하고 대규모 JavaScript 번들에 의존하는 환경에서는 페이지 로드 중에 TBT 및 INP를 낮추기 위해 적극적인 코드 분할 을 고려해야 하며, 필요할 때 사용자에게 필요한 것만 제공하기 위해 JavaScript를 지연 로드해야 합니다. 상호작용이 거의 또는 전혀 없는 환경의 경우 서버 측 렌더링은 이러한 문제에 대한 확장 가능한 솔루션 을 나타낼 수 있습니다.
단일 페이지 애플리케이션을 빌드하는 경우 대부분의 페이지에서 공유되는 사용자
인터페이스의 핵심 부분을 식별하면
애플리케이션 셸 캐싱
기술을 적용할 수 있습니다. 서비스 워커와 결합하면 페이지가 CacheStorage에서 애플리케이션 셸 HTML과 종속 항목을 매우 빠르게 로드할 수 있으므로 반복 방문 시 인식되는 성능을 크게 개선할 수 있습니다.
리하이드레이션은 서버 측 렌더링과 클라이언트 측 렌더링을 결합합니다.
하이드레이션 은 클라이언트 측 렌더링과 서버 측 렌더링 간의 절충안을 모두 수행하여 완화하는 접근 방식입니다. 전체 페이지 로드 또는 다시 로드와 같은 탐색 요청은 애플리케이션을 HTML로 렌더링하는 서버에서 처리합니다. 그런 다음 렌더링에 사용되는 JavaScript와 데이터가 결과 문서에 삽입됩니다. 주의해서 수행하면 서버 측 렌더링과 같은 빠른 FCP를 달성한 다음 "선택"하여 클라이언트에서 다시 렌더링합니다.
이는 효과적인 솔루션이지만 상당한 성능상의 단점이 있을 수 있습니다.
리하이드레이션을 사용한 서버 측 렌더링의 주요 단점은 FCP를 개선하더라도 TBT 및 INP에 상당한 부정적인 영향을 미칠 수 있다는 것입니다. 서버 측에서 렌더링된 페이지는 로드되고 상호작용하는 것처럼 보일 수 있지만 구성요소의 클라이언트 측 스크립트가 실행되고 이벤트 핸들러가 연결될 때까지 실제로 입력에 응답할 수 없습니다. 휴대기기에서는 몇 분이 걸릴 수 있어 사용자에게 혼란과 불만을 줄 수 있습니다.
리하이드레이션 문제: 두 개의 앱 가격으로 하나의 앱
클라이언트 측 JavaScript가 서버가 중단된 위치를 정확하게 인계하려면 서버가 HTML을 렌더링한 모든 데이터를 다시 요청하지 않고 대부분의 서버 측 렌더링 솔루션이 UI의 데이터 종속 항목에서 응답을 문서의 스크립트 태그로 직렬화합니다. 이렇게 하면 많은 HTML이 중복되므로 리하이드레이션은 상호작용 지연보다 더 많은 문제를 일으킬 수 있습니다.
서버는 탐색 요청에 대한 응답으로 애플리케이션의 UI 설명을 반환하지만 해당 UI를 구성하는 데 사용되는 소스 데이터와 클라이언트에서 부팅되는 UI 구현의 전체 사본도 반환합니다. bundle.js가 로드 및 실행을 완료할 때까지 UI는 상호작용하지 않습니다.
서버 측 렌더링 및 리하이드레이션을 사용하여 실제 웹사이트에서 수집된 성능 측정항목은 최적의 옵션이 거의 없음을 나타냅니다. 가장 중요한 이유는 페이지가 준비된 것처럼 보이지만 상호작용 기능이 작동하지 않을 때 사용자 환경에 미치는 영향입니다.
리하이드레이션을 사용한 서버 측 렌더링에 대한 희망이 있습니다. 단기적으로 캐시 가능성이 높은 콘텐츠에만 서버 측 렌더링을 사용하면 TTFB를 줄여 사전 렌더링과 유사한 결과를 얻을 수 있습니다. 점진적 으로, 점진적 또는 부분적으로 리하이드레이션하는 것이 향후 이 기술을 더 실용적으로 만드는 데 핵심이 될 수 있습니다.
서버 측 렌더링 스트리밍 및 점진적으로 리하이드레이션
서버 측 렌더링은 지난 몇 년 동안 여러 가지 발전을 거듭해 왔습니다.
서버 측 렌더링 스트리밍
을 사용하면 브라우저가 수신될 때 점진적으로 렌더링할 수 있는 청크로 HTML을 전송할 수 있습니다. 이렇게 하면 마크업을 사용자에게 더 빠르게 가져와 FCP를 높일 수 있습니다. React에서
renderToString() 동기식과 비교하여
renderToPipeableStream()의 스트림이 비동기식이라는 것은 백프레셔가 잘 처리된다는 의미입니다.
점진적 리하이드레이션 도 고려해 볼 만합니다 (React에서 구현했습니다). 이 접근 방식을 사용하면 현재 일반적인 접근 방식인 전체 애플리케이션을 한 번에 초기화하는 대신 서버에서 렌더링된 애플리케이션의 개별 부분이 시간이 지남에 따라 "부팅" 됩니다. 이렇게 하면 페이지를 상호작용할 수 있도록 하는 데 필요한 JavaScript의 양을 줄일 수 있습니다. 페이지의 우선순위가 낮은 부분의 클라이언트 측 업그레이드를 지연하여 기본 스레드를 차단하지 않도록 하고 사용자가 시작한 후 더 빨리 사용자 상호작용이 발생하도록 할 수 있기 때문입니다.
점진적 리하이드레이션을 사용하면 가장 일반적인
서버 측 렌더링 리하이드레이션 함정 중 하나인 서버에서 렌더링된 DOM 트리가
삭제된 후 즉시 다시 빌드되는 것을 방지할 수도 있습니다. 이는 초기
동기식 클라이언트 측 렌더링에 아직 해결되지 않은
Promise와 같이 완전히 준비되지 않은 데이터가 필요하기 때문인 경우가 많습니다.
부분 리하이드레이션
부분 리하이드레이션은 구현하기 어려운 것으로 입증되었습니다. 이 접근 방식은 페이지의 개별 부분(구성요소, 뷰 또는 트리)을 분석하고 상호작용이 거의 또는 전혀 없는 부분을 식별하는 점진적 리하이드레이션의 확장입니다. 이러한 대부분의 정적 부분 각각에 대해 해당 JavaScript 코드는 비활성 참조 및 장식 기능으로 변환되어 클라이언트 측 공간을 거의 0으로 줄입니다.
부분 리하이드레이션 접근 방식에는 자체 문제와 절충안이 있습니다. 캐싱에 몇 가지 흥미로운 문제가 발생하며 클라이언트 측 탐색은 전체 페이지 로드 없이 애플리케이션의 비활성 부분에 서버에서 렌더링된 HTML을 사용할 수 있다고 가정할 수 없음을 의미합니다.
트리소모픽 렌더링
서비스 워커가 옵션인 경우 트리소모픽 렌더링을 고려해 보세요. 이 기술 을 사용하면 초기 또는 JavaScript가 아닌 탐색에 서버 측 렌더링 스트리밍을 사용한 다음 서비스 워커가 설치된 후 탐색을 위한 HTML 렌더링을 수행하도록 할 수 있습니다. 이렇게 하면 캐시된 구성요소와 템플릿을 최신 상태로 유지하고 동일한 세션에서 새 뷰를 렌더링하기 위해 SPA 스타일 탐색을 사용 설정할 수 있습니다. 이 접근 방식은 서버, 클라이언트 페이지, 서비스 워커 간에 동일한 템플릿 및 라우팅 코드를 공유할 수 있을 때 가장 잘 작동합니다.
검색엔진 최적화 고려사항
웹 렌더링 전략을 선택할 때 팀은 검색엔진 최적화의 영향을 고려하는 경우가 많습니다. 서버 측 렌더링은 크롤러가 해석할 수 있는 "완전한 모양" 환경을 제공하는 데 널리 사용되는 선택입니다. 크롤러는 JavaScript를 이해할 수 있지만 렌더링 방식에는 제한 이 있는 경우가 많습니다. 클라이언트 측 렌더링은 작동할 수 있지만 추가 테스트와 오버헤드가 필요한 경우가 많습니다. 최근에는 동적 렌더링 도 아키텍처가 클라이언트 측 JavaScript에 크게 의존하는 경우 고려해 볼 만한 옵션이 되었습니다.
결론
렌더링 접근 방식을 결정할 때는 병목 현상이 무엇인지 측정하고 이해하세요. 정적 렌더링 또는 서버 측 렌더링으로 대부분의 작업을 수행할 수 있는지 고려해 보세요. 최소한의 JavaScript로 HTML을 주로 제공하여 환경을 상호작용할 수 있도록 하는 것이 좋습니다. 다음은 서버-클라이언트 스펙트럼을 보여주는 유용한 인포그래픽입니다.
크레딧
검토와 영감을 주신 모든 분께 감사드립니다.
제프리 포스닉, 후세인 지르데, 슈비 파니커, 크리스 해럴슨, 세바스찬 마크바게