## 싱글 페이지 애플리케이션의 시대
### 싱글 페이지 애플리케이션
싱글 페이지 애플리케이션이란 하나의 HTML만 유지하고 그 안에서 모든 동작을 자바스크립트와 브라우저 API로 처리하는 애플리케이션을 의미한다.
싱글 페이지 애플리케이션에는 페이지 전환이 일어날 때 서버에 새로운 HTML을 요청하지 않는다. 대신 초기에 빈 HTML 하나를 받아온다. 페이지 전환이 일어나면 서버에 새로운 페이지를 요청하는 대신 새로운 페이지를 그리기 위한 데이터만을 요청한다. 그리고 받은 데이터를 이용해서 자바스크립트가 빈 HTML을 채우거나, 아니면 기존 DOM을 수정한다.
이렇게 빈 HTML 하나만을 가지고 자바스크립트로 모든 작업을 수행하기 때문에 "싱글 페이지" 애플리케이션이라고 부르는 것이다.
싱글 페이지 어플리케이션은 초기에 로드해야 할 자바스크립트 자원이 많기 때문에 초기 로딩 비용이 비교적 많이 든다는 단점이 있다. 하지만 그 이후에는 추가로 서버에 페이지를 요청할 필요가 없으므로 화면 전환이 매끄러워 좀 더 나은 사용자 경험을 제공한다는 장점이 있다.
### 전통적인 방식의 애플리케이션과의 비교
전통적인 웹 애플리케이션은 페이지를 전환할 때마다 새로운 HTML을 서버로부터 불러왔다. 그래서 화면을 전환할 때마다 HTML을 파싱하는 작업이 필요했고, 이 과정에서 화면이 깜빡이거나 흰 화면이 잠깐 보이는 등 화면 전환이 부자연스러웠다.
이 현상은 네이버에서도 확인할 수 있는데, 네이버 홈과 네이버 스포츠는 다른 환경에서 개발되었기 때문에 불가피하게 홈 화면에서 스포츠로 이동할 때 새로운 HTML을 불러와야 한다. 이 과정을 실제로 경험해 보면 중간에 흰 화면이 잠깐 뜨는데, 만약에 네트워크 속도가 느린 환경이라면 이 깜빡임이 더 크게 느껴질 것이다.
싱글 페이지 애플리케이션 중에 가장 완성도가 높다고 여겨지는 것은 Gmail이다. Gmail에 처음 접속하면 조금 긴 로딩을 경험하게 된다. (로딩 progress bar까지 있다) 하지만 일단 접속을 완료하고 나면 메일을 클릭했을 때 주소는 변경되지만, 그 화면 전환이 아주 매끄러운 것을 확인할 수 있다. 이런 차이로 인해서 전통 방식 애플리케이션과 비교했을 때 SPA에서 좀 더 나은 사용자 경험을 제공할 수 있다.
### 싱글 페이지 렌더링 방식의 유행. JAM 스택의 등장
이렇게 SPA는 초기 로딩이 좀 무겁긴 하지만 그 이후의 페이지 전환이 빨라 더 나은 사용자 경험을 제공할 수 있다는 장점이 있어 요즘 많은 웹 페이지들이 싱글 페이지 렌더링 방식을 채택했다.
---
과거 PHP/JSP 시대에는 웹 애플리케이션이 서버 중심의 렌더링 구조로 되어 있었다. 페이지를 요청하면 완성된 HTML을 전달하고, 페이지가 전환되면 새로운 페이지를 요청하는 방식이었다. 이 시기까지만 해도 JavaScript는 단순히 사용자 경험을 좀 더 풍부하게 하는 보조 역할만을 수행했었다.
그러다 JavaScript가 수행할 수 있는 작업이 점점 늘어나면서 코드가 길어지고 복잡해지기 시작했다. 이 때문에 JavaScript의 모듈화 필요성이 대두되게 되었고, 이때 등장한 것이 바로 CommonJS와 AMD이다.
JavaScript 모듈화를 위한 노력이 결실을 보고, 그와 더불어 인터넷 속도와 하드웨어 성능이 향상되면서 JavaScript의 역할이 점점 더 확대되었다.
이러한 변화와 함께 2010년경에 Backbone.js, AngularJS, Knockout.js 같은 JavaScript MVx 프레임워크가 등장했다. 즉 JavaScript가 서버의 역할까지도 일부 수행하기 시작한 것이다. 그 이후에 React, Vue, Angular의 시대가 오면서 자바스크립트만으로 웹 애플리케이션의 동작을 구현하는 SPA 패러다임이 본격적으로 유행하기 시작했다.
SPA가 유행하기 시작한 것은 단순히 화면 전환이 매끄럽기 때문만은 아니었다. 과거 PHP로 웹 애플리케이션을 개발하던 시절에는 자바스크립트 외에도 신경 쓸 것이 많았다. 하지만 SPA 패러다임에서는 개발자는 JavaScript 코드만 잘 작성하면 되었다. 즉 프론트엔드 개발자들에게도 좀 더 좋은 개발 경험을 할 수 있다는 장점도 있었다.
기존의 웹 개발은 LAMP(Linux, Apache, MySQL, PHP/Python(웹 프레임워크)) 스택으로 구성되어 있었다. 이 스택은 당시 인기 있는 구조이기도 했지만, 자바스크립트가 수행할 수 있는 역할이 별로 없었기에 어쩔 수 없는 선택이기도 했다. 이 구조는 확장성 측면에서도 번거로움이 있었는데, 서비스의 기능을 확장하거나, 더 많은 사용자를 대비하기 위해서는 서버 자체를 확장해야 했기 때문이다. 이 당시에는 서버를 확장하는 것이 매우 번거로운 작업이었다.
하지만 JavaScript가 할 수 있는 일이 많아지고 SPA 패러다임이 유행하기 시작하면서 새로 등장한 것이 JAM(JavaScript, API, Markup) 스택이다. 대부분의 작업을 자바스크립트가 수행할 수 있었기 때문에 프론트엔드 개발자는 마크업만 정적으로 배포하고, 이후 작동들은 자바스크립트와 사용자의 브라우저에 맡길 수 있게 되었다. 이 덕분에 서버의 부담이 줄어들고 서비스의 확장 측면에서도 장점이 있게 되었다.
JAM 스택 이후에도 Node.js가 고도화되면서 MEAN/MERN 스택과 같은 API 서버 개발까지도 JavaScript로 하는 구도 또한 인기를 얻고 있다.
### 새로운 패러다임의 웹서비스를 향한 요구
JavaScript의 역할이 많아진다는 것은 애플리케이션에서 로드해야 하는 JavaScript 코드가 크고 복잡해진다는 것을 의미한다. 그에 따라서 초기 로딩 시간이 길어지는 것에 대해서 우려의 목소리가 나오기 시작했다. 하지만, 이 문제는 하드웨어와 네트워크 성능의 발전으로 인해서 자연스럽게 해결될 것이라는 목소리도 있었다.
실제로 이 당시에는 하드웨어와 네트워크 성능의 발전 속도가 눈부셨다. 하지만 실제 지표를 보면, 이 눈부신 발전 속도에도 불구하고 웹 애플리케이션의 초기 로딩 속도는 개선되지 않았다.
특히 눈에 띄는 부분은 CPU 비용이다. JavaScript 파싱과 실행에 큰 비용을 쓰고 있는데, 이것은 JavaScript 코드가 실제로 규모가 크고, 이 규모가 애플리케이션 성능에 유의미한 영향을 미치고 있다는 것을 의미한다.
그리고 사용자는 여전히 20초 전후의 긴 로딩 시간을 경험하고 있었다. 물론 이전의 웹 애플리케이션과 비교했을 때 요즘 애플리케이션의 더 복잡한 기능을 갖고 있기 때문에 비교하기 어렵다고 생각할 수도 있다. 하지만 중요한 것은 네트워크와 하드웨어 성능이 좋아졌음에도 불구하고 여전히 사용자는 긴 로딩 시간을 경험하고 있다는 것이다. 웹 개발자들은 더 좋은 사용자 경험을 위해 책임감을 가질 필요가 있었고, 새로운 패러다임을 요구하는 흐름이 등장하게 되었다.
## 서버 사이드 렌더링이란?
### 서버 사이드 렌더링
서버 사이드 렌더링이란 서버에 렌더링 책임이 존재하는 렌더링 방식을 의미한다. 클라이언트 렌더링에서는 빈 HTML만 서버에서 내려주고 이후의 모든 렌더링 과정을 브라우저와 자바스크립트가 담당했다. 반면에 서버 사이드 렌더링에서는 서버에서 렌더링 작업을 거쳐 완성된 HTML을 내려준다.
서버 사이드 렌더링에서는 최초에 JavaScript 실행 없이도 화면을 그릴 수 있기 때문에 최초 로딩 시간이 줄어든다. 그리고 클라이언트쪽에서 실행되는 JavaScript 코드 또한 줄어들기 때문에 사용자 기기 성능 의존도가 감소한다.
### 장점
우선 서버 사이드 렌더링에서는 페이지 진입 시에 서버에서 이미 완성된 HTML을 응답해 준다. 따라서 브라우저에서는 JavaScript 실행 없이도 초기 화면을 그릴 수 있으므로 FCP가 감소한다는 장점이 있다.
서버 사이드 렌더링에서는 브라우저 대신 서버가 API를 요청하게 되는데 일반적으로 서버에서 HTTP 요청을 수행하는 것이 좀 더 빠르다. HTML을 구성하는 것도 브라우저에서 DOM을 직접 변경하는 것보다 서버 쪽에서 텍스트로 HTML을 미리 그리는 것이 더 빠르다.
> [!tip]- 왜 서버에서 HTTP 요청이 더 빠를까
> - 네트워크 환경이 안정적
> - 지역 간 지연 차이가 적음
> - 브라우저보다 더 최적화된 네트워크 스택 사용
SEO 측면에서도 서버 사이드 렌더링의 장점이 있다. 검색 엔진 봇이 페이지를 방문할 때는 자바스크립트 코드를 실행하지 않는다. 싱글 페이지 애플리케이션에서는 대부분의 기능이 자바스크립트에 의존하고, 검색 엔진이 수집하는 오픈 그래프나 메타 태그 역시도 예외가 아니라 특별히 조치하지 않으면 SEO에 불이익이 있을 수 있다. 반면에 서버 사이드 렌더링에서는 서버에서 HTML을 완성하기 때문에 자바스크립트를 실행하지 않는 검색 엔진 봇의 환경에서도 유의미한 자료를 수집할 수 있다.
서버 사이드 렌더링에서는 누적 레이아웃 이동(Cumulative Layout Shift, CLS)이 감소한다는 장점도 있다. 누적 레이아웃 이동이란 화면에 그려지는 것들의 순서가 제각각 달라서 사용자가 예상치 못한 순간에 레이아웃에 변화가 생기는 것을 의미한다. 싱글 페이지 애플리케이션에서는 화면에 그릴 데이터를 API로 받아오게 되는데, API 응답은 의도한 순서대로 도착한다는 보장이 없다. 따라서 특별히 조치하지 않으면 먼저 그려졌어야 하는 콘텐츠가 뒤늦게 그려져 앞서 그려져 있던 콘텐츠를 밀어내는 CLS가 발생할 수 있다. 반면에 서버 사이드 렌더링에서는 서버에서 완성된 HTML을 내려주는 만큼 CLS 문제에서 비교적 벗어날 수 있다. API 응답 속도가 모두 다른 경우에 서버 사이드 렌더링에서는 모든 API 응답을 기다려야 하므로 최초 페이지 다운로드가 굉장히 느려질 수도 있다. 하지만, 이 문제는 React 18에서 추가된 스트림으로 해결될 수도 있는데 이에 대해서는 나중에 다룬다.
서버 사이드 렌더링은 애플리케이션 성능의 사용자 디바이스 성능 의존도가 낮아진다는 장점도 있다. 싱글 페이지 애플리케이션은 모든 작업이 사용자 클라이언트에서 실행되는 자바스크립트에 의존하기 때문에 절대적으로 사용자 클라이언트의 성능에 의존하게 된다. 하지만 서버 사이드 렌더링에서는 JavaScript 실행에 들었던 부담을 서버가 일부 분담하게 된다. 따라서 사용자 디바이스 성능 의존도가 낮아지고, 비교적 느린 디바이스에서도 일정한 렌더링 경험을 제공할 수 있다는 장점이 있다. 물론 이것은 서버 사이드 렌더링을 수행하는 서버가 충분한 자원을 가지고 있다는 가정이 필요하다. 만약 서버의 자원이 부족하다면 오히려 싱글 페이지 애플리케이션보다 더 성능이 낮을 수 있다.
보안 측면에서도 장점을 가지고 있다. JAM 스택을 채택한 웹 애플리케이션의 공통적인 문제점은 모든 작업이 브라우저에 노출된다는 것이다. 여기에는 인증 같은 민감한 작업도 포함되기 때문에 정상적인 비즈니스 로직이 아닌 접근을 막을 수 있도록 추가 조치가 필요하다. 하지만 서버 사이드 렌더링에서는 일부 작업을 서버에서 처리하기 때문에 브라우저에 직접 노출되는 로직을 줄일 수 있다. 민감한 작업 또한 서버 쪽에서 처리할 수 있기 때문에 보안성이 증가한다는 장점이 있다.
### 단점
서버 사이드 렌더링 코드를 작성할 때는 싱글 페이지 애플리케이션과는 달리 서버 환경에 대한 부분을 추가로 고려해야 한다. 대표적인 예로는 서버 사이드 렌더링에서는 window나 document, sessionStorage 같은 브라우저 전용 객체를 사용할 수 없다. 따라서 이러한 객체를 사용하지 말아야 하고, 만약에 필요하다면 그 코드는 서버에서 실행되지 않도록 해야 한다. 직접 작성하는 코드뿐 아니라 라이브러리도 SSR 환경을 지원하는지를 확인해야 한다. 만약 지원하지 않는다면 역시 서버에서 실행되지 않도록 해야 한다. 하지만 이렇게 클라이언트에서만 실행되는 코드가 많아질수록 SSR의 이점이 줄어든다는 점 또한 기억해야 한다.
SPA에서는 서버의 역할이 정적 자원을 제공하는 것뿐이었다. 따라서 서버를 구축할 때도 정적 자원을 제공할 수 있게만 구축하면 되었다. 하지만 SSR에서는 말 그대로 렌더링 작업을 수행할 서버가 필요하다. 사용자의 요청마다 렌더링을 수행해야 하므로 요청에 따라 적절하게 대응할 수 있는 자원이 필요하고, 트래픽 증가 상황을 대비해서 요청을 적절히 분산시킬 수 있어야 하고 서버 부담이 늘어나는 것을 대비하기 위해서 쿠버네티스 같은 툴 또한 필요할 수 있다. 즉 SSR은 SPA에서보다 서버 구축 난도가 높다.
SPA에서는 좀 느린 작업을 진행할 때 사용자에게 로딩 화면을 띄울 수 있다. 따라서 사용자는 어리둥절하지 않고 작업을 기다릴 수 있었다. 하지만 SSR에서는 서버에서의 렌더링 작업이 늦어지면 화면에 그릴 수 있는 것이 없어 사용자는 백지 화면만을 보게 된다. 작업 지연에 대한 적절한 피드백을 제공할 수 없어 만약 작업이 늦어질 때에는 부정적인 사용자 경험을 제공할 수 있다.
## SPA와 SSR을 모두 알아야 하는 이유
### 서버 사이드 렌더링 역시 만능이 아니다
해결책이라고 생각했던 SPA에도 단점이 있었듯이 SSR 역시 만능은 아니다.
우선 클라이언트에서 수행되는 무거운 작업을 서버로 넘긴다고 해서 모든 문제가 해결되는 것은 아니다. 브라우저의 부담은 좀 줄었을 수도 있지만 서버를 관리해야 한다는 부담이 새로 생겼다. SSR을 사용하기로 한 애플리케이션 설계 자체가 잘못되었다면 성능 개선 효과는 미미한 채로 오히려 관리 포인트가 하나 더 늘어날 수 있다.
무엇을 우선순위로 제공할지를 염두에 두고 설계하는 것이 중요하다. 상황에 따라서는 SPA가 더 효율적일 수도 있다.
### 싱글 페이지 애플리케이션과 서버 사이드 렌더링 애플리케이션
두 방법론 중에서 어떤 것이 옳다고 단언하긴 어렵지만, SPA와 MPA(멀티 페이지 애플리케이션)을 비교했을 때는 다음과 같이 말할 수 있다.
**최고로 최적화된 SPA는 최고로 최적화된 MPA보다 낫다.**
최고로 최적화된 SPA는 초기 렌더링 지연을 줄이기 위해서 꼭 필요한 자원만 초기에 요청하고, 부가적인 자원은 lazy loading으로 불러와서 렌더링에 방해되지 않도록 처리한다. 코드 스플리팅을 잘 설계해서 불필요한 자바스크립트를 로드하거나 실행하지 않도록 한다. 또한 교체해야 하는 HTML 요소만 다시 렌더링해서 사용자의 피로감을 최소화한다.
물론 MPA도 최적화할 수 있긴 하지만 SPA보다 뛰어난 성능을 보여주기는 어려울 것이다.
**평균적인 SPA는 평균적인 MPA보다 느리다.**
MPA는 서버 기반이기 때문에 안정적인 성능을 유지하기 쉽다. 하지만 일반적인 SPA는 최적화가 되어 있지 않을 가능성이 높으므로 사용자 기기에 따라 성능이 들쑥날쑥할 것이다. 최적화가 가능하긴 하지만 최적화 난도가 높고 노력 대비 효과가 일정하지도 않다. 게다가 MPA의 라우팅 문제를 해결할 수 있는 브라우저 최신 API들(Back-forward cache, Paint Holding 등)이 등장했기 때문에 MPA로 안정적인 성능을 내는 것이 더 쉬워졌다. SPA로도 이 API들을 구현할 수는 있겠지만 까다롭다.
따라서 같은 애플리케이션을 평균적인 노력을 들여서 SPA와 MPA로 구현한다고 가정했을 때 MPA가 좀 더 안정적인 사용자 경험을 제공할 수 있을 것이다.
결론은 두 방식 모두 장단점이 있다는 것이다. 따라서 상황에 따라 적절한 방법론을 선택하는 것이 중요하다.
### 현대의 서버 사이드 렌더링
요즘 말하는 SSR는 LAMP 스택 시절의 SSR과는 조금 다르다. LAMP 스택 시절에는 라우팅마다 완전한 페이지를 재요청했기 때문에 페이지 전환이 느리다는 단점이 있었다. 그래서 현대의 SSR은 여기에 SPA의 장점을 더했다.
현대 SSR에서는 최초 진입 시에는 SSR로 빠르게 초기 페이지를 렌더링한다. 그리고 이후 라우팅에서는 자바스크립트를 바탕으로 싱글 페이지 애플리케이션처럼 동작한다. 요즘 나오는 서버 사이드 렌더링 프레임워크들(Next.js, Remix 등)은 모두 이러한 방식으로 동작한다.