이번강의에서는 MPA,SPA,SSR 등을 비교하고,Univeral Rendering을 다루었습니다.
(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)
현재 프론트엔드에서는 SPA를 많이 사용하는 추세기때문에, 많이 사용되지는 않지만, 이전에는 하나의 정적 페이지를 실제로 여러개 만들고, 요청이 들어오면 그에 맞는 페이지를 응답하였습니다.
JSP와 같이, Index / Auth와 같이 각각의 html파일을 만들어 주어진 역할만을 수행하는 방식입니다.
MPA와 같은경우 간단하게 html + CSS + JS정도만으로 처리가 가능합니다.
이러한 방식을 사용할경우 필요한 Lib가 적어지고, 정적 페이지를 그대로 노출하기때문에, SEO(Search Engine Optimization)적인 면에서는 이점일수 있습니다. MPA는 만들어진 결과를 노출시키기 때문에, SSR방식 입니다.
단, MPA와 같은 경우, 새로운 요청을 보낼경우 새로운 HTML파일을 받고 그것을 렌더링 하는 과정이 필요하기때문에, 요청사이에 빈 화면이 노출되게 됩니다.
또한, 데이터의 추가 수정등의 데이터 변경 요청을 보냈을때 요청이후 변경 사항이 클라이언트측에서 갱신되게 하기 위해서 페이지를 새로 고침하는 방식이였습니다.
form의 submit 버튼이 클릭시 page를 새로고침 하는 이유이기도 합니다.
이러한 단점을 보완하기 위해서 Ajax가 도입되었고 요청 이후에 바로 변경 사항을 갱신할 수 있게 되었습니다.
시간이 흘러, 개개인의 컴퓨터 사양이 급속히 올라가며, 이전보다 더 클라이언트측에 역할을 맡기는 개발환경이 되었습니다.
그런환경속에서 클라이언트 측에 렌더링 역할을 맡기는 CSR(client side rendering)을 사용하는 SPA(sing page application)이 탄생하게 되었습니다.
CSR을 사용하는 SPA의 장점으로는 요청시 빈 페이지(흰 화면)을 보여주지 않음으로서 사용자의 UX가 좋아지며, 기존 매 요청마다 HTML을 주고 받는 형식의 불필요한 통신이 줄어든다는 장점이 있습니다.
이것을 구현하기 위해서 CSR을 사용하는 SPA는 다음과 같은 과정을 거칩니다.
1. 사용자가 SPA을 사용하는 페이지에 초기 요청을 보냅니다.
2. 웹서버는 요청에 대해 head를 제외한 body부분이 비어있는 index.html을 반환합니다.
3. 클라이언트 측 브라우저는 head를 읽으며 추가로 필요한 자원(js,css)을 서버로 다시 요청합니다.
4. 다운로드가 완료되면, CSS와 자바스크립트는 입력된 로직에 따라 화면을 렌더링 하기 시작합니다.
여기서 초기 요청 이외에 다른 요청을 할경우 아래와 같은 추가 과정을 거칩니다.
5. 요청한 API에서 데이터를 받아와 index.html의 데이터를 새롭게 교체하고 데이터에 맞게 렌더링 합니다.
6. 만약 페이지 이동이 필요한 경우, 기본적인 HTTP GET 요청을 막아 페이지 이동은 제한하고, 브라우저 주소는 변경된 상태를 유지하도록 합니다. 그리고 요청을 통해 받은 데이터를 통해 새롭게 페이지를 렌더링 합니다.
이러한 방식은 아래와 같이 정리할수 있습니다.
이전 강의에서 소개되었듯이 CRP는 브라우저가 화면을 렌더링하는 과정입니다.
다시금 정리하면,
이러한 과정은 화면 변경시 변경되는 수준에 맞게 재실행됩니다. 만약 width만 변경된다면 Layout 이하의 단계가 새롭게 실행되며, 색상만이 변경된다면 Painting 이하의 단계만이 실행됩니다.
만약 MAP와 같이 새로운 요청시 새로운 페이지를 요청하게 된다면 이러한 과정은 매번 일어나게 될것이고, 비효율적입니다.
또한, 일반적인 JS 및 Jquery를 사용하게 될 경우도, 사소한 하나의 변화에도 매번 실행되기 때문에 비효율적 입니다.
이러한것을 효율적으로 실행하기 위해서 리액트는 JS객체로 이루어진 가상돔(virtual DOM)을 사용하고 있습니다.
리액트가 가상돔을 사용하는 방법은, 만약 부모 Props가 교체되었다면, 하위 자식컴포넌트들은 기본적으로 모두 교체가 되는것으로 간주합니다.
변경되는 변화들은 리액트의 스케쥴러에 의해서 한번에 모아진다음, 가상돔에 재조정(Reconciliation) 과정을 가지고, 이것을 실제 DOM에 전달하여 CRP를 최소화 합니다.
이러한것을 실행하기 위해서 리액트는 불변성을 전제로 하고 있습니다.
변화들을 감지하는 방법으로
일일이 가상돔의 변경된 부분을 모두 찾아 비교하는것보단
변경시 새로운 가상돔을 생성하여, 얕은 비교를 통하여 바뀐 부분만을 교체하는것이 훨씬 효율적이기 때문입니다.
리액트는 CSR을 사용하는 SPA 앱이며,
또한 위와 같은 방식을 통하여 가상돔을 사용한 CRP를 효율적으로 관리하고 있습니다.
MPA에서도 언급했듯이, SSR은 결과물이 완성되어 있는 HTML파일을 전해줍니다.
리액트와 같은 SPA은 비어있는 HTML문서를 전해줍니다.
이러한 두 방식의 차이는 몇가지가 있지만, 크게 3가지로 볼 수 있을것 같습니다.
많은 검색 크롤러는 SSR에서 제대로 작동합니다. CSR 페이지로의 접근시에는 초기 값이 비어있기때문에, 그대로 넘어가는 경우가 많기 때문입니다.
초기 요청시, CSR은 필요한 재료(js,css등)을 다 받고 작동하기 때문에 FCP에서 TTI까지의 시간이 오래걸리지만, SSR은 초기에 미리 렌더링이 완성된 HTML을 제공하기때문에, FCP 및 TTI 시간이 짧습니다.
CSR은 클라이언트측에서 대부분의 로직을 처리하고 렌더링 하지만, SSR은 서버측에서 로직을 처리하고 렌더링한 결과를 반환하기때문에 서버측에 부담이 많이 가게 됩니다.
SSR은 요청이 들어올때마다 렌더링이 일어나지만,
SSG는 개발자의 빌드 요청시에 렌더링이 일어나게됩니다.
즉, SSR은 매 요청시마다 서버가 요청에 맞는 로직을 실행하고 렌더링된 값을 반환하며, SSG는 매 요청시마다 서버가 준비해둔 렌더링 값을 반환합니다.
SSR은 변화가 적은 시간내에 잦은 경우 사용될수있으며, SSG는 변화가 적은 경우 사용될 수 있습니다.
SSG를 사용하는경우 CI/CD를 사용하여 정해진 주기마다 빌드를 통해 새로운 데이터를 받아 새롭게 렌더링된 페이지를 준비해둘수 있습니다.
Universal Rendering 이란, 하나의 환경에서 CSR과 SSR을 함께 지원하는것을 말합니다.
React와 같은경우 react-dom/server를 사용하여 이것을 구현하고 있습니다.
SSR은 서버측에서 자원을 마련하여, Index.html에 값을 채워 보내준다는것입니다. 때문에 SSR을 위해서는 기존의 CSR에서는 필요 없던 내부 리액트 server가 필요하게 됩니다.
대략적인 설명으로서,
리액트 서버에서는 우선, SSR을 할 컴포넌트에 대한 요청이 들어오면 해당 컴포넌트를 직렬화 하게됩니다.
직렬화(Serialize) 란 어떠한 객체를 송수신이 가능한 형태로 변환해주는것을 말합니다. 문자열을 JSON, XML등의 형식으로 변환해준다는것을 떠올리면 될것 같습니다. 리액트 서버에서는 renderToString함수를 통해 React컴포넌트를 HTML 문자열로 직렬화 합니다.
그리고 직렬화를 통해 얻은 컴포넌트 값을 서버측에서 읽어들인 HTML파일의 문자열 <div id="root"></div>
사이에 HTML로 변환된 replace를 사용하여 끼어 넣습니다.
그리고 해당 파일(문자열)을 status(200)과 함께 서버응답으로 내보내게 됩니다.
이렇게 서버측에서 HTML파일은 완성되어 주어지지만, 상호작용(Interaction)이 가능한 함수들이 있을경우클라이언트 측에서 수화(Hydrate) 과정을 거쳐, 페이지가 최종적으로 완성되어지게 됩니다.
수화 과정이 끝나는 시점은 TTI를 의미합니다.
수화(Hydrate)란 직렬화된 이벤트 함수를 실제 렌더링된 HTML을 읽어 이벤트 함수가 달려 있어야 하는 DOM들의 위치를 찾아 이벤트 리스너를 달아주는 것입니다.
정리하자면, SSR은 서버측 React에서 HTML 템플릿을 문자열로 읽고, SSR하고자 하는 컴포넌트의 값을 직렬화해서 얻은 값을, HTML 문자열의 적절한 위치에 값을 교체하고, 서버 응답으로 실어서 내보낸다는 것입니다.
이후, 클라이언트측에서는 수화(Hydrate) 과정이 필요하다면 과정을 거쳐 페이지를 최종 완성하게됩니다.
기존 MPA는 이전에 자바의 스프링과 바닐라 JS를 사용한 프로젝트를 했을때 그런방식으로 구현하였던 경험이 있었습니다
그때는 별 생각없이 했던 form의 submit시 페이지 이동 또는 새로고침등에 대해서 별 의문을 안 가졌었는데
이번 강의를 통해 뒤늦게 나마 의미를 알게 되었네요.
React가 CSR이고 SPA라는것, 가상돔을 사용한다는것에 대해서도 매우 중요하고, 기본적인것이니 만큼 알고는 있었지만, 왜 그런 방식을 택하였는지와 그것에 대한 이점, 그리고 작동방식에 대해서는 명확하게 알지 못하였는데 이 강의를 통해서 이해하게 되어 좋았습니다.
SSR은 NextJS를 사용하면 알아서 해주는 해주는 마법같은 테크닉이라고 막연히 생각하였습니다. 이번 강의를 통하여 SSR이 어떠한 방식으로 돌아가는지 알게 되었는데, 마술의 트릭을 알게된것마냥, 생각보다 간단하면 간단하고, 대단하다면 대단한. 어쨌거나 이번 코스의 기본 목표였던 시야의 확장을 이번에도 깨우쳤던것 같습니다.
추가적으로, SSR 이외에 SSG를 배웠고, SSG와 같은경우 SSR과 같이 SEO적인 측면에서도 좋고, CDN에서 캐시된 데이터를 반환해주기 때문에 서버측 부담도 덜 들어간다는것을 알게되었는데, 이후에 블로그 또는 어떠한 글을 긁어오는 Page등을 만들시 사용하면 좋겠다라는 생각을 해보았습니다.