장경은
Study

[4장] 서버 사이드 렌더링

서버 사이드 렌더링이란?

싱글 페이지 애플리케이션의 세상

  • 서버사이드 렌더링 애플리케이션과 반대되는 개념인 싱글 페이지 애플리케이션
  • <body /> 내부에 아무런 내용이 없다.
  • 네이버 스포츠 화면은 네이버 홈과 다른 환경에서 HTML을 만들어서 제공하므로 어쩔 수 없이 처음부터 HTML을 다시 완성해야 하므로 페이지가 전환될 때 부자연스러운 모습을 보게 된다.
  • 싱글 페이지 애플리케이션은 페이지 전환에 필요한 일부 영역만 다시 그리게 되므로 훨씬 더 매끄러운 UI를 보여줄 수 있게 된다.
  • 과거에는 LAMP 스택, 즉 Linux(운영체제), Apache(서버), MySQL(데이터베이스), PHP/Python 등(웹 프레임워크)으로 구성돼 있었다.
    • 이 LAMP 스택은 과거 매우 인기 있는 웹 개발 구조이기도 했지만 동시에 어쩔 수 없는 선택이기도 했다.
    • 자바스크립트가 할 수 있는 일이 제한적이었기 떄문에 대부분의 처리를 서버에서 해야만 했기 때문이다.
  • 최근 등장한 스택이 바로 JAM(JavaScript, API, Markup) 스택이다.
    • JAM 스택의 인기와 Node.js의 고도화에 힘입어 MERN(MongoDB, Express.js, React, Node.js) 스택처럼 아예 API 서버 자체도 자바스크립트로 구현하는 구조도 인기를 끌기 시작했다.

서버 사이드 렌더링이란?

  • 장점
    • 최초 페이지 진입이 비교적 빠르다.
    • 검색 엔진과 SNS 공유 등 메타데이터 제공이 쉽다.
    • 요청이 완전히 완료된 이후에 완성된 페이지를 제공하므로 완성된 HTML을 보여준다.
    • 서버에서 부담을 나눌 수 있으므로 사용자의 디바이스 성능으로부터 조금 더 자유로워질 수 있다.
    • 보안에 좀 더 안전하다
  • 단점
    • 브라우저 전역 객체인 window 또는 sessionStorage 등에 접근할 수 없다.
    • 사용자의 요청에 따라 적절하게 대응할 수 있는 물리적인 가용량을 확보해야 하고, 때로는 예기치 않은 장애 상황에 대응할 수 있도록 복구 전략도 필요하다. (적절한 서버 구축 필요)
  • 서버에서 사용자에게 보여줄 페이지에 대한 렌더링 작업이 끝나기까지는 사용자에게 그 어떤 정보도 제공할 수 없다.

SPA와 SSR을 모두 알아야 하는 이유

  • 서버 사이드 렌더링은 성능에 있어 만병통치약이 아니다.
  • 가장 뛰어난 싱글 페이지 애플리케이션은 가장 뛰어난 멀티 페이지 애플리케이션보다 낫다.
  • 평균적인 싱글 페이지 애플리케이션은 평균적인 멀티 페이지 애플리케이션보다 느리다.
  • 두 가지 모두 장단점이 있으며 어느 하나가 완벽하다고 볼 수 없다.
  • 현대의 서버 사이드 렌더링
    • Next.js 등은 최초 웹사이트 진입 시에는 서버 사이드 렌더링 방식으로 서버에서 완성된 HTML을 제공받고, 이후 라우팅에서는 서버에서 내려받은 자바스크립트를 바탕으로 마치 싱글 페이지 애플리케이션처럼 작동한다.
  • 프론트엔드 개발자는 서버에서의 렌더링, 그리고 클라이언트의 렌더링을 모두 이해해야 두 가지 장점을 완벽하게 취하는 제대로 된 웹서비스를 구축할 수 있다.

서버 사이드 렌더링을 위한 리액트 API 살펴보기

renderToString

  • renderToString을 사용하면 앞서 언급했던 서버 사이드의 이점, 클라이언트에서 실행되지 않고 일단 먼저 완성된 HTML을 서버에서 제공할 수 있으므로 초기 렌더링에서 뛰어난 성능을 보일 것이다.
  • 검색 엔진이나 SNS 공유를 위한 메타 정보도 renderToString에서 미리 준비한 채로 제공할 수 있으므로 SPA 구조보다 손쉽게 완성할 수 있다.

renderToStaticMarkup

  • renderToString과의 유의미한 차이점은 앞서 루트 요소에 추가한 data-reactroot와 같은 리액트에서만 사용하는 추가적인 DOM 속성을 만들지 않는다는 점이다.
  • renderToStaticMarkup의 결과물을 기반으로 리액트의 자바스크립트 이벤트 리스너를 등록하는 hydrate를 수행하면 서버와 클라이언트의 내용이 맞지 않다는 에러가 발생한다. 그 이유는 보다시피 renderToStaticMarkup의 결과물은 hydrate를 수행하지 않는다는 가정하에 순수한 HTML만 반환하기 때문이다.

renderToNodeStream

  • renderToNodeStream은 완전히 Node.js 환경에 의존하고 있다.
  • stream은 큰 데이터를 다룰 때 데이터를 청크로 분할해 조금씩 가져오는 방식을 의미한다.
    • 이는 유튜브 영상을 볼 때 사용자가 볼 수 있는 몇 초라도 먼저 다운로드되면 그 부분을 먼저 보여주고, 이후에 계속해서 영상을 다운로드하는 것을 가능하게 해준다.
    • 스트림을 활용하면 이러한 큰 크기의 데이터를 청크 단위로 분리해 순차적으로 처리할 수 있다는 장점이 있다.
    • 때문에 대부분의 널리 알려진 리액트 서버 사이드 렌더링 프레임워크는 모두 renderToString 대신 renderToNodeStream을 채택하고 있다.

renderToStaticNodeStream

  • renderToNodeStream과 제공하는 결과물은 동일하나, renderToStaticMarkup과 마찬가지로 리액트 자바스크립트에 필요한 리액트 속성이 제공되지 않는다.

hydrate

  • renderToString과 renderToNodeStream으로 생성된 HTML 콘텐츠에 자바스크립트 핸들러나 이벤트를 붙이는 역할
  • hydrate는 이처럼 정적으로 생성된 HTML에 이벤트와 핸들러를 붙여 완전한 웹페이지 결과물을 만든다.
  • render와의 차이점은 hydrate는 기본적으로 이미 렌더링된 HTML이 있다는 가정하에 작업이 수행되고, 이 렌더링된 HTML을 기준으로 이벤트를 붙이는 작업만 실행한다는 것이다.
    • rootElement 내부에는 <App />을 렌더링한 정보가 이미 포함돼 있어야만 hydrate를 실행할 수 있는 것이다.
    • 따라서 hydrate로 넘겨준 두 번째 인수에는 이미 renderToString 등으로 렌더링된 정적인 HTML 정보가 반드시 담겨 있어야 한다.
    • 아무것도 없는 빈 HTML에 이 정보를 렌더링하는 render와의 차이점이 바로 이것이다.

etc.

  • 페이지가 온전히 서버에서 만들어졌는지 확인하려면 개발자 도구의 소스 보기를 통해 확인하면 된다.

Next.js 톺아보기

  • npm 프로젝트를 볼 때는 pakage.json을 먼저 봐야 한다.
  • next/link로 이동하는 경우 서버 사이드 렌더링이 아닌, 클라이언트에서 필요한 자바스크립트만 불러온 뒤 라우팅하는 클라이언트 라우팅/렌더링 방식으로 작동한다.
  • 사용자가 빠르게 볼 수 있는 최초 페이지를 제공한다는 점과 싱글 페이지 애플리케이션의 장점인 자연스러운 라우팅이라는 두 가지 장점을 모두 살리기 위해 이러한 방식으로 작동한다.

next.config.js

  • swcMinify: SWC라는 오픈소스는 바벨의 대안 / JS 기반이 아닌 Rust로 작성, 병렬로 작업을 처리
  • basePath: URL prefix
  • redirects: 특정 주소를 다른 주소로 보내고 싶을 때 사용
  • assetPrefix: 만약 next에서 빌드된 결과물을 동일한 호스트가 아닌 다른 CDN 등에 업로드 하고자 한다면 이 옵션에 해당 CDN 주소를 명시하면 된다.

[5장] 리액트와 상태 관리 라이브러리

Flux 패턴의 등장

  • 액션이라는 단방향으로 데이터의 흐름이 줄어드므로 데이터의 흐름을 추적하기 쉽고 코드를 이해하기가 한결 수월해진다.
  • 리덕스의 등장 - 하고자 하는 일에 비해 보일러플레이트가 너무 많다는 비판

훅의 탄생, 그리고 React Query와 SWR

  • 기존에 우리가 알고 있는 상태 관리 라이브러리보다는 제한적인 목적으로, 일반적인 형태와는 다르다는 점만 제외하면 분명히 SWR이나 React Query도 상태 관리 라이브러리의 일종이라 볼 수 있다.
  • useState와 useReducer 모두 약간의 구현상의 차이만 있을 뿐, 두 훅 모두 지역 상태 관리를 위해 만들어졌다.
  • useSubscription을 사용하면 리액트 외부에서 관리되는 값에 대한 변경을 추적하고, 이를 리렌더링까지 할 수 있다.

상태관리 라이브러리의 작동 방식

  • useState, useReducer가 가지고 있는 한계, 컴포넌트 내부에서만 사용할 수 있는 지역 상태라는 점을 극복하기 위해 외부 어딘가에 상태를 둔다. 이는 컴포넌트의 최상단 내지는 상태가 필요한 부모가 될 수도 있고, 혹은 격리된 자바스크립트 스코프 어딘가일 수도 있다.
  • 이 외부의 상태 변경을 각자의 방식으로 감지해 컴포넌트의 렌더링을 일으킨다.
  • 이후 여러 상태관리 라이브러리 소개
  • npm에서 제공하는 모든 라이브러리와 마찬가지로 메인테이너가 많고 다운로드가 활발하며 이슈가 관리가 잘되고 있는 라이브러리를 선택하는 것이 좋다.