SPA 프레임워크 정의

서버사이드 렌더링과 페이지 방식 호출

asp, php, jsp 와 같은 전통적인  웹페이지는 페이지를 요청하고 받는 서버사이드 렌더링 방식이다. 즉, 아래와 같은 방식으로 호출된다.

브라우저가 hello.php라는 페이지를 요청하면 서버는 데이터베이스를 쿼리하여 그 결과를 받아서 서버에서 html페이지를 만들어서 리턴하면 브라우저는 단순히 해당 html을 렌더링해서 보여주는 방식이다. 렌더링이 서버에서 되어거 렌더링된 결과인 html + css + javascript 코드가 리턴되기 때문에 이런 방식을 서버사이드 렌더링이라고 한다. 또한 asp, php, jsp 와 같은 언어를 서버사이드 언어라고 부를수 있다.

asp, jsp 개발자 모두 마찬가지이지만, php 개발자라면 hello.php라는 화일에 html 과 css, javascript 를 모두 섞어 사용하고 <% ~ /%> 라는 php 코드를 중간 중간 섞어서 개발할 것이다. 이 php 코드는 모두 서버에서 렌더링 되고 렌더링 된 결과는 html + css + javascript 언어가 되므로 브라우저가 이것을 파싱해서 동작하게 된다.

그런데, 모바일이 등장하면서 이 페이지 방식의 문제점이 부각되게 된다. 첫번째 페이지를 로딩후에 다시 두번째 페이지를 로딩하게 되면 두번째 페이지에 포함된 css 화일들과 javascript 화일들이 또 다시 로딩되게 된다. 보통 큰 프로젝트라면 하나의 페이지에 수십개의 css 화일과 javascript 화일들이 포함될 것인데, 페이지를 이동시 마다 이 화일들이 다시 로딩되므로 모바일에서는 로딩 속도로 인해서 적합하지 않게 된 것이다.

서버와 클라이언트의 분리 – RESTful 서비스

모바일의 등장으로 페이지 단위 호출 방식은 로딩 속도가 저하된다고 했지만 그것보다 더 중요한 점은 서버사이드 렌더링이 가진 한계점이다. 아이폰과 안드로이드라는 네이티브 앱을 개발하기 위해서는 네이티브에서 제공되는 UI 를 사용해서 개발을 해야 한다. 그런데, 서버에서 렌더링이 되어서 html이 리턴된다면 더이상 UI를 렌더링 할 수 가 없게 되는 것이다. 

그래서 서버는 RESTful 서비스만 제공해주도록 역할이 축소가 되었다. REST란 Representational State Transfer 의 약자로 리소스만 xml 혹은 json 형태로 제공해준다는 것이다. 

게시판을 예를 들어보자. 게시판을 안드로이드 앱으로 개발한다면, 안드로이드에서는 서버에 API만 호출해서 데이터를 받은 후에 받은 데이터를 안드로이드의 고유 UI를 사용해서 네이티브 앱을 개발하게 된다. 아이폰도 마찬가지이다. 

게시판의 내용을 가져오기 위해서는 HTTP의 GET 메서드를 게시판을 작성시에는 HTTP의 POST 메서드, 게시판 내용을 수정할때는 HTTP 의 PUT 메서드, 게시판 내용을 삭제할때는 DELETE 메서드를 사용하게 되면 CRUD를 모두 구현할 수 있으므로 서버와 클라이언트가 분리되게 되었다.

클라이언트는 REST API를 호출해서 데이터만 수신한다. 수신한 데이터를 가지고 안드로이드는 안드로이드 앱을 아이폰은 아이폰 네이티브 앱을 구현할 수 있다. 게시판의 경우라면 아래와 같이 REST api를 만들수 있다.

  • GET /board?page=1 => 1페이지의 모든 게시판 조회
  • GET /board/1 => 1번 게시판 상세 보기
  • POST /board => 게시판 글쓰기
  • PUT /board/1 => 1번 게시판 수정
  • DELETE /board/1 => 1번 게시판 삭제

클라이언트 사이드 렌더링과 SPA

이제 모바일이 아닌 웹의 경우를 보자. 서버는 RESTful 서비스를 이용해서 데이터를 제공받을 수 있으므로, 이 데이터를 클라이언트가 가공해서, 즉, 클라이언트 사이드 렌더링을 이용해서 하나의 페이지에서 제공해 줄 수 있다.

물론 SPA를 가능하게 해준 가장 큰 공은 webpack과 같은 모듈 번들러 툴이다. 여러개의 페이지에서 사용되는 자바스크립트 css html을 컴파일 및 압축하여 하나의 페이지로 제공해주는 것이 가능하다. 하나의 페이지이므로 첫번째 페이지 접속시에 javascript, CSS를 가져온 후에 더 이상 다른 페이지에 접속하기 위해서 서버에 접근하지 않는다. 이것이 어떻게 가능한지 아래 그림을 보자.

예를 들어, 메뉴3개로 구성된 페이지라고 하면 메뉴1에 해당하는 페이지를 선택시에는 메뉴1 ~ 메뉴3 의 전체 페이지에서 메뉴1에 해당하는 화면만 보여주게 된다. html 속성중에 id 속성을 사용하게 되면 해당 아이디에 접근하기 위해서는 해쉬를 앞에 붙여서 접근할수 있다는 것을 알고 있을 것이다. !menu1 이라는 id 속성을 부여하고 이 메뉴만 보여주고 다른 메뉴는 안보여주는 것이다. 메뉴2를 선택하면 마찬가지로 !menu2 의 id 속성만 보여주고 나머지 메뉴는 보여주지 않게 된다. 이런식으로 다수의 페이지를 하나의 페이지인 Single Page Application으로 보여줄 수가 있다.

그런데 #!라는 유알엘이 그렇게 익숙하지 않으므로 #!를 / 로 바꿔주게 되면 /menu1, /menu2, /menu3 이렇게 보여줄수 있다. #! 된 것을 해쉬 모드라고 부르고 /로 된 것을 히스토리모드라고 한다. 리액트 라우터에서는 이것을 해쉬 라우터 와 브라우저 라우터로 각각 부르고 있다.

그리고 각각의 메뉴에서는 REST api를 호출해서 화면을 구성하게 된다. 그러면 이런 질문을 할 수 있을것이다. jquery 사용시에도 Ajax를 이용해서 비동기 통신이 가능했는데 그것과 차이점이 없지 않나요?  있다. 그것은 브라우저가 해당 메뉴를 히스토리에 저장한다는 것이다. 그러므로 뒤로 가기를 한다거나, 즐겨찾기가 가능해준다.

SPA에서의 라우팅과 SEO 문제점

SPA는 모든 페이지를 포함한 정적인 화일들을 가져와서 단 하나의 페이지로 렌더링하게 된다. 그리고 페이지 사이의 이동은 id 태그를 사용하여 한 페이지 내에서 특정 메뉴를 보였다 안보였다 하도록 처리한다. 이것을 그림으로 나타내면 다음과 같다. 

html과 css, javascript 와 같은 화일들은 크기가 변하지 않는 정적인 화일들이므로 캐시가 가능하도록 보통 nginx 같은 웹서버로 구성한다. 그래서 처음 접속시 nginx 같은 웹서버에서 모든 페이지를 모두 가져오게 된다. 그리고 메뉴간의 이동시에는 서버를 통하지 않고 클라이언트 사이드 렌더링을 통해서 처리하게 된다. 그리고 해당 메뉴에서 필요한 동적인 데이터들은 REST API를 구성한 backend 서버를 통해서 가져오게 된다.

기존 jquery 방식의 ajax 와 비슷하지 않느냐고 생각할 수 있겠지만 id 태그를 통해서 메뉴를 이동하기 때문에 브라우저는 해시모드일때는 #!menu1 혹은 히스토리 모드일 경우는 /memu1 의 유알엘은 브라우저가 히스토리에 저장이 가능하다. 따라서 즐겨찾기 기능과 뒤로가기가 가능해진다.

그런데 SPA 방식에서는 결정적인 단점 하나가 존재한다. 바로 SEO가 불가능하다는 것이다. SEO란 Seach Engine Optimization으로 페이지 방식일 경우에는 페이지마다 이동시 html 헤더의 meta 태그에 페이지의 메타 정보를 입력해서 검색시에 해당 페이지로 갈수 있도록 해주는 기능이다.
SPA는 페이지가 하나밖에 없으므로 당연히 html 헤더는 최초에 한번만 지정이 가능하고 id별로 페이지를 처리하므로 SEO를 구현할 수가 없다.

그래서 SPA에서 SEO를 구현하기 위해서 angular 에서는 angular university 라는것이 등장했고, react는 next 라는 프레임웍이, vue에서는 nuxt 라는 프레임웍이 등장하였다. 요즘은 이런 프레임웍을 사용하지 않고도 특정 모듈만 사용해서 이것을 구현하는 것도 가능해졌다. 예를 들어 react에서 Helmet 이라는 모듈을 사용하면 SPA 방식으로 라우팅 시에도 페이지마다 메타 태그를 바꿀수 있게 되었다.

SPA 프레임웍 비교

SPA 프레임웍은 구글이 2010년 초반에 Angular 1.x 를 발표하면서 엄청난 호응을 얻었다. Angular 라는 이름도 기존 jquery와 같은 DOM 방식이 아니라 완전히 새로운 개념이라서 등장한 이름이다. 그렇지만 시간이 지나면서 모바일에서 성능 문제를 드러내면서 페이스북에서 만든 React가 각광받기 시작했다.  그리고 그 이후에 Vue가 Angular 1.x 의  장점과 React의 장점을 흡수하여 View에만 집중해서 개발을 할 수 있도록 하면서 호응을 얻게 되었다. 그리고 React 의 발표시기에 구글에서는 Angular 1.x의 성능상의 문제점을 인식하고 계속 Angular 2.x의 베타버전을 만들고 있었다. 그러다가 2016년에 angular 2.x를 발표하였는데 기존 angular 1.x 방식과는 방식이 전혀 다르고 호환이 안되기 때문에 angular 1.x는 AngularJS라 부르고 angular 2.x를 그냥 Angular 라고 부른다.  사이트도 angularjs.io 사이트는 Angular 1.x 개발 사이트이며 이며 angular 2.x는 JS를 빼고 그냥 angular.io 사이트로 접속해야 한다.

Vue React Angular 2.x
Learning Curve
Binding Virtual DOM Virtual DOM ngZone
컴포넌트간 통신 emit-on, event bus
Vuex
status, props
event
react-redux
Service
ReactiveX의 Observable, Subject
프레임워크 유무 라이브러리 라이브러리 프레임워크
라우터 vue-router react-router-dom angular 패키지에 포함
네이티브개발 ionic – vue React Native
Ionic react
Ionic

* learning curve

learning curve는 만일 자바스크립트를 잘알고 있다면 react가 가장 쉬울것이다. 왜냐하면 react는 JSX와 JSX expression을 제외하고 특별하게 배워야할 템플릿 언어는 없고 순수하게 자바스크립트만 사용하기 때문이다. Vue는 angular에서 가져온 ng-for 같은 반복문, ng-if 같은 조건문 등 화면을 만들기 편리한 템플릿 언어가 많이 갖춰져 있다. angular는 리액트와 뷰와 다르게  웹을 만들기 위해서 필요한 모든것이 갖춰진 프레임웍이다 보니 배워야 할게 많다.  특히 ReactiveX 라이브러리리인 RxJS가 기본 내장되어있다 보니 angular를 배우기 위해서는 반드시 이 라이브러리를 이해해야 한다. 어쩌면 angular 보다 RxJS가 더 어려울수 있으나, 이 고비를 넘기면 디바운싱 기법 등 RxJS를 이용해서 많은 것을 할 수 있다.

* binding

react 와 vue 는 one way binding 방식으로 virtual DOM이라는 모델 데이터를 변경하면 이 모델 데이터와 바인딩 되어있는 실제 view가 변경되는 방식이다. angular 는 two way 바인딩 방식으로 모델 데이터를 변경하면 view 도 변경되며 view를 변경하면 모델 데이터도 변경되는 방식이다.

* 컴포넌트 통신방식

뷰는 props를 통한 기본적인 통신 방식과 emit, event bus , $parent, $children 등 많은 통신 방식이 혼재하고 있다. 리액트는 부모가 자식과 통신하기 위해서 props 를 사용하고 자기 자신의 상태를 표현하기 위해서 state를 사용한다. angular도 부모와 자식간에 통신하는 방법이 있지만 보통 서비스라는 컴포넌트를 사용하여 통신하게 된다. 

글로벌 스토어로 vue는 Vuex를 사용하는게 일반적이고 리액트는 외부 라이브러리인 redux, mobx를 사용하게 된다. angular는 이미 ReactiveX라는 훌륭한 라이브러리를 이용해서 자체적으로 publish subscribe를 구현할수 있기 때문에 외부 라이브러리가 필요하지 않다.

* 네이티브 개발

네이티브 개발이란 웹으로 개발해서 안드로이드, 아이폰 네이티브 코드로 변환하는것을 말한다.

Ionic 은 초기에는 angular만 지원하였지만 최근에는 vue, react를 모두 지원한다. 리액트는 리액트 네이티브로 네이티브 개발을 또한 할 수 있다.

* PWA 지원

뷰, 리액트, 앵규러 모두 기본적으로 PWA를 지원한다. PWA는 Progressive Web Application 의 약자로 네이티브 앱처럼 만들어 준다. manifest.json 에 설치 아이콘을 지정하면 웹으로 개발을 하더라도 스마트폰 바탕화면에 론칭 아이콘을 설치하여 아이콘을 클릭하여 진입할수 있다. service worker를 사용하여 웹을 종료를 하더라도 이 서비스 워커가 계속 살아 있어서 푸쉬서비스를 받아서 노티피케이션을 구성할 수 있다.