11. SSR의 기쁨과 슬픔 - 렌더링의 변화와 흐름을 통해 알아보는 SSR과 Streaming SSR
https://www.youtube.com/watch?v=hPyyFu3lrEg
😎 렌더링 역사
1. Static Website
- 웹 서버에서 정적인 HTML을 중심으로 제공
- URL 을 통해 해당 디렉토리에 있는 파일을 내려 주는 방식
- 디렉토리 파일 구조 === URL
- 모든 페이지별로 정적인 페이지가 존재
2. Common Gateway Interface (CGI)
- 정적인 HTML 제공의 한계를 탈피하기 위해 등장
- 웹서버에서 요청을 받으면 특정 프로그램을 실행해서 그 결과를 내려주기
3. Server side Template
- 웹서버에서 직접 정적인 HTML 을 제공하는게 아닌, Server Application에서 여러가지 조건과 로직에 따라서 HTML을 생성해서 내려주는 방식
- 웹서버가 없어도 됨
- 자바,파이썬,루비 등등으로 구성될 수 있고, 하나의 언어에서도 다양한 템플릿 존재
- JSP, handlebars, EJS,...
- 렌더링 된 이후, 인터렉션 처리 등은 JavaScript를 Client에서 실행하여 처리한다.
- 문제점
- UI 가 복잡한 경우 처리하기 어렵다.
- 기능 구현에 따라서 같은 뷰 로직이 Server Side 에서도 구현되어야하고, Client 에서도 구현되어야했음
- 더보기 기능, 무한스크롤 등
- 결국 렌더링 시점이 뒤섞이면서, 복잡한 인터렉션 UI를 다룰 수록 작업자의 고통이 심해짐
- Server에서 API나 DB Query 등에서 시간이 오래 걸릴 경우, 사용자에게 보여지는 화면이 늦어지는 문제가 있음.
- 이 부분 때문에 API 요청이 오래거리면 FID 등 점수가 낮아짐.
3. Client Side Rendering
-
브라우저 성능의 발전과, JS 스펙의 고도화로 아예 렌더링을 Client에서 다 해버리자는 움직임
-
API와 웹 어플리케이션은 완전히 분리되어 있고, 웹 어플리케이션에는 HTML/CSS/JS만 존재하는 형태
-
뷰 로직은 전부 Client에 있고 서버는 API 만 내려주는 형태
-
Client Application은 HTML/CSS/JS와 같은 정적인 파일들만 있으므로 개념적으로는 Static Website와 거의 같다.
-
그렇기 때문에 별도의 Application Server 없이 CDN을 통한 배포 가능.
-
Application Server가 없기 때문에, 클라이언트 입장에서는 트래픽에 대응해서 스케일업/스케일아웃을 할 필요가 없음. CDN에서 알아서 해주기 때문
-
기존 서버사이드방식에서 해주던 URL에 따른 라우팅 처리도 client 에서 처리
- SPA
-
기본적으로 404에러가 발생하면, index.html 로 요청을 돌린다.
-
서버사이드템플릿에서는 api 가 오래걸리면 화면이 하얗게 나오는데, CSR을 할 경우, API 요청이 오래걸렸을 때 UI 대응 가능(매우 큰 장점)
-
문제점
- meta tag
- SPA 형태로 만들어진 어플리케이션의 경우 어느 페이지를 접속해도 index.html을 내려줘서, meta:og 태그 관련 문제 발생
- why? 어느 페이지를 요청해도 같은 index.html로 요청을 되돌려서 내려주고, 이 후 클라에서 다 처리하기 때문..
- lamdbda edge, firebase function (서버리스 펑션)을 사용해서 요청을 가로챌 수있긴 함
- 성능
- 초기에 body 가 텅 비어있다가, 자바스크립트가 로딩 되고 각종 코드들이 실행되며 화면이 채워지는 방식
- 그래서 클라이언트에서 초기에 실행해야하는 자바스크립트 양이 많아질 수 밖에 없는데, 이것의 체감 속도가 생각보다 느리게 느껴질 수 있다. (특히나 모바일 웹뷰에서)
- 메모리 누수에 취약함
- 과거의 서버사이드 방식은 페이지 변경 시 아예 새로고침 되어서 메모리를 비워버림
- SPA는 새로고침을 하는게 아니고 계속 JS를 그리는 방식이므로...
- meta tag
4. Server Side Rendering
- node.js 의 발전으로 Client와 Server에서 같은 언어로 쓸 수 있게 되며, 렌더링의 책임을 다시 Server로 돌리려는 움직임
- 같은 언어를 Client 와 Serve에서 쓸 수 있다는 점 때문에 Java, Python, Ruby 등의 Server Side Template 으로 처리하던 방식과는 엄연히 다름
- 거의 모든 뷰로직을 클라와 서버가 공유한다.
- 서버에서 HTML string 을 생성하는 측면에서는 Server Side Template 과는 유사함
- 문제점
- 더이상 serverless 가 아니므로, server 관리 책임이 생긴다.
- 트래픽이 몰릴 때 스케일업/스케일아웃 등의 작업이 필요하다.
- 도커로 말아서 배포할 경우, 배포 시간이 CSR 대비 더 늘어난다.
- 세팅이 복잡하다.
- next.js , remix, vite-plugin-ssr 등이 등장하며 많이 희석되긴 함.
SSR의 기쁨
- 확실히 초기로 로딩 속도가 빨라짐
- 서버사이드에서 Suspense를 사용하지 못해서 반쪽짜리 (prefetch 만해주는) SSR이었지만, 그것만으도 속도가 빨라짐
SSR의 슬픔
- 기존 vercel 배포 대비, 인프러 관리 서버운영에 비용이 너무 많이 든다.
- 특정 상황에서 렌더링이 느려지는 문제가 있다.
- 초기 서버 요청까지 오래걸리거나
- 렌더링 내 API 호출(prefetch)이 오래걸리거나 -> 요걸 해결하자!
Streaming SSR
- 기존 SSR의 단점을 보완하기 위한 방법
- 기존에는 Sever에서 HTML을 전부 생성한 이후 화면을 그림
- Streaming SSR 방식은, 가장 빠르게 그릴 수 있는 부분을 먼저 그리고, 이후에는 Node.js의 스트림을 사용하여 점진적으로 렌더링하는 방식
- HTML 자체가 스트림이 가능하다.
- Node.js의
renderToPipeableStream
함수를 이용하면 Node.js 스트림을 이용한 방식으로 렌더링이 가능하다. - Suspnese 를 기준으로 화면을 나눌 수 있다.
Streaming SSR의 기본개념
- Shell, Suspense으로 경계지어진 컴포넌트를 기준으로 렌더링을 처리한다.
- Shell: 서스펜스로 감싸진 최상단 바깥의 영역 (API 호출이 필요 없는 부분 / GNB..등등)
- Shell 이 먼저 렌더링 된 후 이후 Suspense로 나눈 부분을 Streaming하는 방식
또 다른 슬픔..
- Docker 방식 배포
- 배포를 다시 하면, 배포가 끝나기 전 까지 404 에러가 많이 발생하던 문제
- 도커 이미지를 만들면서, yarn build로 생성했던 asset 들이 새로 배포되면서 어느 pod 은 이전 버전의 번들을 가지고 있고, 또 어느 pod 은 새버전의 번들을 가지고 있으면서 생기는 문제
- yarn build 된 결과를 도커로 말기 때문에 매번 도커 이미지를 만들 때 마다, 이전 번들은 날아간다.
- (해결방법) 생성된 번들은 모두 s3로 올리고, vite 내에서도 번들을 불러올 때는 cf + s3를 사용해서 불러오기 / 즉 모든 배포본이 누적이 되었다. (이존 번들 요청되도 괜찮게)
- 빌드를 두번 해야하는 문제
- 도커로 이미지 말 때, 알파용 빌드버전 / 프로덕션 빌드버전..이 있어야하는 문제
- (해결방법) 빌드 타임 환경변수를 런타임 환경변수로 변경
- 배포를 다시 하면, 배포가 끝나기 전 까지 404 에러가 많이 발생하던 문제