본문 바로가기

카테고리 없음

Isomorphic JavaScript: The Future of Web Apps

https://medium.com/airbnb-engineering/isomorphic-javascript-the-future-of-web-apps-10882b7a2ebc#.4nyzv6jea


Airbnb에서는 지난 몇 년 동안 풍부한 웹 경험을 구축하면서 많은 것을 배웠습니다. 우리는 2011년에 모바일 웹 사이트를 통해 앱 세계에 뛰어들었고 그 이후로 Wish Lists와 함께 재설계된 검색 페이지를 출시했습니다. 이들 각각은 대규모 JavaScript 앱입니다. 즉, 보다 현대적인 대화형 경험을 지원하기 위해 대부분의 코드가 브라우저에서 실행됩니다. 이 접근 방식은 오늘날 일반적이며 Backbone.js, Ember.js및 Angular.js와 같은 라이브러리를 통해 개발자는 이러한 풍부한 JavaScript 앱을 보다 쉽게 구축할 수 있습니다. 그러나 이러한 유형의 앱에는 몇 가지 중요한 제한 사항이 있음을 발견했습니다. 그 이유를 설명하기위해 먼저 웹 앱의 역사를 빠르게 살펴보겠습니다. 

 

JavaScript Grows Up

웹이 등장한 이래 브라우징 경험은 다음과 같이 작동했습니다. 웹 브라우저는 특정 페이지를 요청하고 인터넷 어딘가에 있는 서버가 HTML 페이지를 생성하고 유선으로 다시 보내도록 합니다. 이것은 브라우저가 그다지 강력하지 않았고 HTML 페이지가 대부분 정적이고 독립적인 문서를 나타내었기 때문에 잘 작동했습니다. 웹 페이지를 보다 동적으로 만들기 위해 만들어진 JavaScript는 이미지 슬라이드 쇼와 날짜 선택기 위젯 이상을 활성화하지 못했습니다. 

 

수년간 개인용 컴퓨팅이 발전한 후 창의적인 기술자들은 웹을 한계까지 밀어붙였고 웹 브라우저는 이를 따라잡기 위해 진화했습니다. 이제 웹은 완전한 기능을 갖춘 애플리케이션 플랫폼으로 성숙했으며 빠른 JavaScript 런타임 및 HTML5 표준을 통해 개발자는 이전에는 네이티브 플랫폼에서만 가능했던 풍부한 앱을 만들 수 있습니다. 

 

The Single-Page App

얼마 지나지 않아 개발자는 이러한 새로운 기능을 활용하여 JavaScript를 사용하여 브라우저에서 애플리케이션을 구축하기 시작했습니다. 단일 페이지 앱의 전형적인 예인 Gmail과 같은 앱은 사용자 상호 작용에 즉시 응답할 수 있으므로 더 이상 새페이지를 렌더링하기 위해 서버를 왕복할 필요가 없습니다. 

 

Backbone.js, Ember.js 및 Angular.js와 같은 라이브러리는 종종 클라이언트 측 MVC(Model-View-Controller) 또는 MVVM(Model-View-ViewModel)라이브러리라고 합니다. 

전형적인 클라이언트 사이드 MVC 아키텍처

대부분의 애플리케이션 로직(뷰, 템플릿, 컨트롤러, 모델등)은 클라리언트 사이드에 상주하며 데이터를 위해 API와 통신합니다. 서버는 Ruby, Python 또는 Java와 같은 모든 언어로 작성될 수 있으며 대부분 HTML의 초기 껍데기 페이지를 처리합니다. JavaScript 파일이 브라우저에서 다운로드되면 평가되고 클라이언트 측 앱이 초기화되어 API에서 데이터를 가져오고 나머지 HTML 페이지를 렌더링합니다. 

 

앱이 처음 로드되면 페이지를 새로 고치지 않고 페이지 간 빠른 탐색을 지원할 수 있고 올바르게 수행하면 오프라인에서도 작업할 수 있기 때문에 이는 사용자에게 좋습니다. 

 

Trouble in Paradise

그러나 실제로 이 접근 방식에는 많은 사용 사례에 적합하지 않은 몇가지 치명적인 결함이 있습니다. 

 

SEO

클라이언트 측에서만 실행할 수 있는 애플리케이션은 HTML을 크롤러에 제공할 수 없으므로 기본적으로 SEO가 좋지 않습니다 . 웹 크롤러는 웹 서버에 요청을 하고 그 결과를 해석하는 방식으로 동작합니다. 그러나 서버가 빈 페이지를 반환하면 큰 가치가 없습니다. 해결 방법이 있지만 일부 후프를 뛰어 넘지 않고는 아닙니다. 

 

Performance

마찬가지로 서버가 HTML의 전체 페이지를 렌더링하지 않고 대신 클라이언트측 자바스크립트가 렌더링할 때까지 기다리는 경우 사용자는 페이지의 콘텐츠를 보기 전에 몇 초 동안 빈 페이지 또는 스피너 로드를 경험하게 됩니다. 느린 사이트가 사용자와 수익에 미치는 엄청난 영향을 보여주는 많은 연구가 있습니다. Amazon은 페이지 로드 시간이 100ms 감소할 때마다 수익이 1 % 증가한다고 주장합니다. Twitter는 클라이언트 대신 서버에서 렌더링하기 위해 1년 동안 40명의 엔지니어가 사이트를 재구축했으며 인지 로딩 시간이 5배 향상되었다고 주장했습니다. 

 

Maintainability

이상적인 경우는 우려 사항을 훌륭하고 깔끔하게 분리할 수 있지만 불가피하게 애플리케이션 논리 또는 view logic이 클라이언트와 서버에 중복될 수 있으며 가끔은 다른 언어로 작성되기도 합니다. 일반적인 예로는 날짜 및 통화 서식, 양식 유효성 검사 및 라우팅 로직이 있습니다. 이것은 특히 더 복잡한 앱의 경우 유지보수를 악몽으로 만듭니다. 

 

단일 페이지 앱을 빌드하는데 시간과 노력을 투자한 후에야 단점이 무엇인지 명확해지는 경우가 많습니다. 

 

A Hybrid Approach

결국 우리는 새로운 접근 방식과 이전 접근 방식의 혼합을 원합니다. 우리는 성능 및 SEO를 위해 서버에서 완전한 형식의 HTML을 제공하고 싶지만 클라이언트 측 애플리케이션 로직의 속도와 유연성을 원합니다. 이를 위해 우리는 Airbnb에서 클라이언트측과 서버 측 모두에서 실행할 수 있는 Isomorphic JavaScript 앱을 실험하고 있습니다. 동형 앱은 다음과 같을 수 있으며 여기에서는 "클라이언트-서버 MVC"라고 합니다.

이 세계에서 일부 응용 프로그램 및 뷰 로직은 서버와 클라이언트 모두에서 실행될 수 있습니다. 이것은 성능 최적화, 더 나은 유지 보수성, 기본 SEO 및 더 많은 상태 저장 웹 앱과 같은 모든 종류의 문을 엽니다. 빠르고 안정적인 서버 측 JavaScript 런타임인 Node.js를 사용하여 이제 이 꿈을 현실로 만들 수 있습니다. 적절한 추상화를 만듬으로써 우리는 서버와 클라이언트 모두에서 실행되는 애플리케이션 로직(동형 자바스크립트)을 작성할 수 있습니다. 

 

Isomorphic JavaScript in the Wild

이 아이디어는 새로운 것이 아닙니다. Nodejitsu는 2011년에 동형 자바스크립트 아키텍처에 대한 훌륭한 설명을 작성했습니다. 그러나 채택이 더디었습니다. 이미 등장한 몇 가지 동형 프레임워크가 있습니다. 

 

Mojito는 언론의 주목을 받은 최초의 오픈 소스 동형 프레임워크였습니다. 고급 풀스택 Node.js 기반 프레임 워크이지만 YUI에 대한 의존성 및 Yahoo! 관련 특성으로 인해 20212년 4월 오픈소스 이후 JavaScript 커뮤니티에서 큰 인기를 끌지 못했습니다. Meteor는 아마도 오늘날 가장 잘 알려진 동형 프로젝트일 것입니다. Meteor는 처음부터 실시간 앱을 지원하도록 구축되었으며 팀은 패키지 관리자 및 배포 도구를 중심으로 전체 생태계를 구축하고 있습니다. Mojito와 마찬가지로 크고 독단적인 Node.js 프레임워크이지만 JavaScript 커뮤니티 참여를 훨씬 더 잘 수행했으며 많은 기대를 모았던 1.0 릴리스가 곧 출시될 예정입니다.

 

Facebook 공동 창립자 Dustin Moskovitz가 설립한 작업 관리 앱인 Asana에는 흥미로운 동형 스토리가 있습니다. Moskovitz가 세계 최연소 억만장자라는 점을 고려하면 자금 조달에 어려움을 겪지 않고 Asana는 동형 JavaScript 의 가장 발전된 사례 중 하나인 폐쇄 소스 Luna 프레임워크를 개발하는 R & D에 수년을 보냈습니다. Node.js 가 존재하기 전에 이전에 원래 v8cgi에 구축된 Luna를 사용하면서 모든 단일 사용자 세션에 대해 앱의 전체 복사본을 서버에서 실행할 수 있습니다. 각 사용자에 대해 별도의 서버 프로세스를 실행하여 클라이언트에서 실행중인 서버에서 동일한 JavaScript 애플리케이션 코드를 실행하고, 강력한 오프라인 지원 및 빠른 실시간 업데이트와 같은 모든 종류의 고급 최적화를 가능하게 합니다. 우리는 올해 초 자체 동형 라이브러리를 시작했습니다. Rendr이라고 하는 이 앱을 사용하면 서버 측에서 완전히 렌더링할 수도 있는 Backbone.js + Handlebars.js 단일 페이지 앱을 빌드할 수 있습니다. 

 

Rendr는 대기 시간이 긴 모바일 연결 사용자에게 특히 중요한 페이지 로드 시간을 대폭 개선하기 위해 Airbnb 모바일 웹 앱을 재 구축한 경험의 산물입니다. Rendr은 프레임워크가 아닌 라이브러리가 되기 위해 노력하므로 Mojito 또는 Meteor에 비해 문제를 덜 해결하지만 수정 및 확장이 쉽습니다.

 

Abstraction, Abstraction, Abstraction

이러한 프로젝트는 대규모 풀스텍 웹 프레임 워크인 경향이 있어 문제의 어려움을 말해줍니다. 

클라이언트와 서버는 매우 다른 환경이므로 애플리케이션 개발자에게 단일 API를 노출할 수 있도록 기본 구현에서 애플리케이션 로직을 분리하는 일련의 추상화를 생성해야합니다. 

 

Routing

URI 패턴을 라우팅 처리기에 매핑하는 단일 경로 집합이 필요합니다. 우리의 경로 처리기는 HTTP 헤더, 쿠키 및 URI 정보에 엑세스 할 수 있어야 하며 window.location(브라우저) 또는 req 및 res(Node.js)에 직접 엑세스하지 않고 리다이렉션을 지정할 수 있어야 합니다. 

 

Fetching and persisting data

fetching 메커니즘과 독립적으로 특정 페이지 또는 구성 요소를 렌더링하는데 필요한 리소스를 설명하려고 합니다. 리소스 설명자는 JSON endpoint를 가리키는 간단한 URI 이거나 더 큰 응용 프로그램의 경우 모델 및 컬렌션의 리소스를 캡슐화하고 어느 시점에서 URI로 변환되는 모델 클래스 및 기본 키를 지저앟는 것이 유용할 수 있습니다. 

 

View rendering

DOM을 직접 조작하든 문자열 기반 HTML 템플릿을 고수하든 DOM 추상화를 사용하는 UI 컴포넌트 라이브러리를 선택하든 우리는 마크업을 동형으로 생성할 수 있어야합니다. 애플리케이션의 필요에 따라 서버나 클라이언트에서 모든 view를 렌더링할 수 있어야합니다. 

 

Building and packaging

동형 애플리케이션 코드를 작성하는 것은 전투의 절반에 불과합니다. Grunt 및 Browserify와 같은 도구는 실제로 앱을 시작하고 실행하기 위한 워크플로의 필수 부분입니다. 클라이언트 측 의존성을 포함한 템플릿 컴파일, 변환 적용, 축소등 여러 빌드 단계가 있을 수 있습니다.  간단한 경우는 모든 애플리케이션 코드, 보기 및 템플릿을 단일 번들로 결합하는 것이지만 더 큰 앱의 경우 수백 킬로바이트를 다운로드할 수 있습니다. 보다 발전된 접근 방식은 동적 번들을 생성하고 지연 로딩을 도입하는 것이지만 이것은 빠르게 복잡해집니다. Esprima와 같은 정적 분석도구를 사용하면 야심찬 개발자가 고급 최적화 및 메타 프로그래밍을 시도하여 상용구 코드를 줄일 수 있습니다. 

 

Composing Together Small Modules

동형 프레임워크를 처음으로 시장에 출시한다는 것은 이러한 모든 문제를 한 번에 해결해야 한다는 것을 의미합니다. 그러나 이것은 이미 존재하는 앱에 채택하고 통합하기 어려운 크고 다루기 힘든 프레임워크로 이어집니다. 

 

더 많은 개발자가 이 문제를 해결함에 따라 동형 앱을 빌드하기 위해 함께 통합할 수 있는 작고 재사용 가능한 모듈이 폭발적으로 증가하는 것을 보게 될 것입니다. 대부분의 JavaScript 모듈들은 이미 거의 또는 전혀 수정하지 않고 동형으로 사용할 수 있습니다. 예를 들어 Underscore, Backbone.js Handlebars.js, Moment, 심지어 jQuery와 같은 인기있는 라이브러리를 서버에서 사용할 수도 있습니다. 

 

The View From Here

더 많은 조직이 프로덕션에서 Node.js를 편안하게 실행함에 따라 점점 더 많은 웹 앱이 클라이언트와 서버 코드 간에 코드를 공유하기 시작할 것입니다. 동형 자바스크립트는 스펙트럼이라는 점을 기억하는 것이 중요합니다. 템플릿을 공유하는 것으로 시작하여 전체 애플리케이션의 뷰 레이어로 진행하고 앱의 비즈니스 로직 대부분에 이를 수 있습니다. JavaScript 코드가 환경간에 정확히 무엇을 어떻게 공유되는지는 전적으로 빌드 중인 애플리케이션과 고유한 제약 조건 집합에 따라 다릅니다. Nicholas C. Zakas는 앱이 클라이언트에서 서버로 UI 레이어를 끌어내려 성능 및 유지 관리 최적화를 가능하게 하는 방법에 대한 멋진 설명을 제공합니다. 앱은 동형 자바스크립트를 사용하기 위해 백엔드를 제거하고 Node.js로 대체할 필요가 없습니다. 대신 합리적인 API와 RESTful 리소스를 생성하여 기존 백엔드가 Node.js 레이어와 함께 동작할 수 있습니다. Airbnbn에서는 이미 Grunt 및 Browserify와 같은 Node.js 기반 도구를 사용하도록 클라이언트 측 빌드 프로세스를 재정비하기 시작했습니다.