티스토리 뷰

(1) 스프링, isomorphic, 서버사이드 렌더링

(2) 스프링, isomorphic, 서버사이드 렌더링 - Handlebars



 SPA(Single Page Web Application)가 등장하고 활성화됨으로인해 클라이언트 렌더링은 사용자에게 더 나은 경험을 제공하기 위한 중요한 요소가 되었습니다. 


 그렇다면 어디까지 서버에서 렌더링하고 어디부터 클라이언트에서 렌더링해야할까 고민을 하게 됩니다.

 

 어디까지 서버에서 렌더링해야 할까에서 고려해야할 첫번째는 SEO(검색 엔진 최적화) 입니다. 네트워크상에는 컨텐츠를 수집하는 다양한 bot들이 존재합니다. 대표적으로 구글봇이 있습니다. 봇의 수집된 콘텐츠는 여러 검색엔진의 검색대상으로 분류될 수 있습니다. 대부분 봇들은 자바스크립트를 실행하지 못하며, 구글봇은 그나마 자바스크립트를 실행시킬 수는 있으나 동적인 호출(ajax)을 통한 렌더링까지 실행시키지는 못합니다. 이런 봇들은 렌더링된 HTML에서만 콘텐츠를 수집하기 때문에 클라이언트에서만 렌더링하는 웹 페이지를 빈 페이지로 인식하게 됩니다. 페이지의 내용에 따라 SEO를 고려하여 어느 부분가지 서버에서 렌더링 되어야 하는지 생각해볼 수 있습니다.


 두번째는 클라이언트 렌더링의 초기구동 속도입니다. 최초 클라이언트 렌더링은 페이지 로딩 시간, 페이지를 그리는 시간, 자바스크립트 로딩 시간 등 브라우저 구동방식마다 다른 복잡한 과정을 거쳐서야 자바스크립트가 실행이 되고 렌더링이 되게 됩니다. 이 과정에서 콘텐츠를 호출하는 동적 호출과정이 있다면 시간은 더 늘어날 것 입니다. 이러한 초기구동 속도는 속도가 빠를지라도 사용자에게 깜빡 거리는 듯한 느낌으로, 사용자에게 페이지가 느리다는 인상을 줄 수 있습니다. 어느정도까지 클라이언트에서 렌더링하는 것이 빠를지는 각 페이지마다 다를 것이지만, 최초 페이지 로딩시에는 큰 틀을 포함하여 최대한 서버에서 렌더링하는 것이 사용자에게 더 좋은 느낌을 줄 수 있습니다.


 각 어플리케이션의 특성대로 서버 사이드 렌더링, 클라이언트 사이드 렌더링을 나누었다면 두번째로 생각해야 할 문제가 발생합니다. 바로 Isomorphic 문제입니다.


 개발에서 말하는 Isomorphic은 동일한 소스코드를 가지고 서버쪽과 클라이언트쪽에서 작성하는 형태를 지칭합니다.(참고)


 자바에서 사용하는 서버 템플릿 엔진(jsp, freemarker 등)과 자바스크립트에서 사용하는 클라이언트 프레임워크 혹은 템플릿 엔진, 뷰 라이브러리 (Angularjs, Backbonejs, handlebars, Reactjs 등)의 차이로 서버와 클라이언트에서 작성하는 형태가 달라질 뿐만 아니라, 동일한 View를 서버와 클라이언트의 렌더링이 모두 필요하다면 서버 렌더링 코드와 클라이언트의 렌더링 코드를 모두 작성하여야 합니다. 

 Isomorphic 방식으로 한 가지 템플릿 엔진을 사용하면 서버, 클라이언트 로직의 분리없이 하나의 로직을 사용할 수 있으므로 코드양이 줄이들며, 클라이언트/서버 렌더링 간의 변경도 용이해지며, 유지보수 또한 간단한 어플리케이션이 될 수 있습니다.


 그러나 서버 템플릿 엔진을 브라우저에서 사용할 수 없었고, 자바를 사용하는 스프링에서 빠른 속도를 지원하는 자바스크립트 엔진이 없었기 때문에 자바에서의 isomorphic은 다른 언어에서만 가능한 이야기였습니다. 


그러나 이제는! 


  V8 엔진을 사용할 수 있는 다른 언어들에서나 가능했던 Isomorphic을 가능하게 해주는 빠른속도의 Java용 자바스크립트 엔진들이 등장했습니다!


1. Nashorn


 JAVA 8부터 JDK 기본 라이브러리에 포함된 Nashorn! 



 JAVA 8 이전버전까지는 Rhino를 사용하였고 JAVA 8에서는 새롭게 Nashorn이 나왔습니다. (Rhino, Nashorn : 둘 다 '코뿔소'라는 뜻) Nashorn은 JVM 위에서 자바스크립트를 컴파일하는 스크립트 엔진으로, 다른 스크립팅 엔진처럼 사용할 수 있고, 자바와 상호 동작하는 특수 기능도 제공합니다.



 Rhino에서 Nashorn으로 올라오면서 성능에 대한 향상이 많이 이루어졌습니다. (참고) 그러나 아직 V8 엔진만큼은 아닙니다.

 

 SpringOne2GX 2015에서 발표되었던 Isomorphic Templating with Spring Boot, Nashorn and React에 따르면 속도가 매우 빨랐다고 합니다. 

 


 초당 6천 페이지라면 서비스하는데도 괜찬을 것 같지 않나요? 어떤 스크립트를 돌리냐에 따라 많은 성능차를 보일 수 있으므로 적용은 직접 판단을 해보시는 것이!



2. J2V8


 이클립스 재단에서 오픈소스 프로젝트로 진행 중인 J2V8은 자바로 바인딩된 V8 프로젝트입니다.

 

J2V8의 포커스는 실제 V8 엔진과 성능을 거의 맞추는 것이라고 하며 자바 8에서만 사용가능한 Nashorn과는 다르게 자바 6부터 사용 가능합니다.


 Nashorn을 열심히 공부하던 중 지난 JSCON:16에서 장동수님의 발표로 알게 된 J2V8입니다. 장동수님의 컨퍼런스 발표 자료에 의하면 퍼포먼스가 Nashorn보다 비슷하거나 좋다고 합니다. 


 또한 장동수님이 커스터마이징으로 눈에 띄는 성능 차이를 이루어내셨다 합니다(아래 그래프를 얻어내기 위해!?)

 



 그러나! 아직 해결되지 않은 몇몇가지 문제들(JNI Performance bottleneck 등)이 있으며 CPU 사용률이 조금 높다고 합니다. 


 어떤 엔진이 더 좋다는 결론은 없지만, 두 엔진에서 렌더링을 하는 속도는 실제 사용자가 느끼기에 거의 차이를 못느낄 정도의 차이라고 생각을 하기 때문에 JDK에 공식적으로 내장된 Nashorn이 조금 더 안정적이지 않나 생각해봅니다.


스프링에서는 어떻게 스크립트 엔진을 사용해서, 어떻게 Isomorphic 실현할 수 있을까요?


이전에는 스크립트엔진을 Bean으로 등록하고 직접 ThreadLocal을 구성하여 Thread Safe 등 여러가지를 고민하며 구현해야 했지만, Spring 4.2부터 Script Templating을 지원하며 간단한 구성으로 쉽게 스크립트 엔진을 사용 가능합니다.


 ScriptTemplateViewResolver를 viewResolver로 등록하고

1
2
3
4
5
6
7
@Bean
ViewResolver viewResolver() {
    ScriptTemplateViewReslover viewResolver = new ScriptTemplateViewReslover();
    viewResolver.setPrefix("~~");
    viewResolver .setSuffix(".html");
    return viewResolver;
}
cs


 ScriptTemplateConfiguerer를 등록하여 ScriptTemplateViewReslover에서 사용할 스크립트 엔진과 그 외 설정을 합니다


1
2
3
4
5
6
7
8
9
10
11
@Bean
ScriptTemplateConfigurer configurer() {
 
    ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
 
    configurer.setEngineName("nashorn");
 
    configurer.setScripts("/static/polyfill.js",
 
                            "/META-INF/resources/webjars/handlebars/3.0.0-1/handlebars.js",
 
                            "/static/render.js");
 
    configurer.setRenderFunction("render");
 
    configurer.setSharedEngine(false);
 
    return configurer;
 
}
cs


 저는 nashorn 스크립트 엔진을 사용할 것입니다. 


 polyfill은 js 라이브러리를 사용하기 위한 최소 전역 객체 등을 사용자 설정으로 생성하는 내용 입니다. polyfill이란 이름으로 통상적으로 쓰이며 그 내용은 사용하는 js 라이브러리에 따라 var window = {}, var console = {} 등 스크립트 오류를 막을 최소한의 전역 객체를 채워줍니다.


 RenderFunction은 setScripts로 등록된 스크립트들을 모두 로드한 공간에서 viewResolver를 통해 view가 들어왔을 때 사용할 명령어 입니다. 아래 API 문서와 같이 render funtion은 파라미터를 받을 수 있습니다.


 render 함수를 통해 template, model, url을 활용하여 자바스크립트로 출력될 내용을 구현할 수 있습니다. render 함수의 리턴 값이 화면에 출력될 HTML 코드가 되는 것 입니다.

 어떤 스크립트를 사용하고, render 함수를 어떻게 구현하냐에 따라 클라이언트 템플릿을 서버에서 렌더링 할 수 있을 것 입니다!


댓글
댓글쓰기 폼