프롤로그: 수학자가 코드를 만나다
2009년 어느 날, 독일 쾰른. Rudolfplatz 모퉁이의 Starbucks에는 WiFi도 없었습니다.[36] 하지만 한 남자는 매일 그곳에 앉아 8시간씩 코딩을 했습니다. 그의 이름은 Ryan Dahl.
Dahl의 인생은 좀 이상했어요.[32] 샌디에이고에서 자란 그는 UC San Diego에서 수학을 공부했고, University of Rochester에서 **대수적 위상수학(algebraic topology)**으로 대학원에 갔습니다. "매우 추상적이고 아름답다"고 생각했죠.[32]
하지만 2년 만에 질렸습니다. "현실에 적용되지 않아."[32]
그래서? PhD 과정을 중단하고, 편도 티켓과 얼마 안 되는 돈을 들고 남미로 떠났습니다.[35] 거기서 영어를 가르치며 근근이 살았어요. 그러다 웹 개발을 발견했고, Ruby on Rails에 빠졌습니다.
하지만 Rails에는 문제가 있었습니다. 동시 요청을 제대로 처리할 수 없었어요.[35] 애플리케이션이 너무 느렸고, CPU를 완전히 잡아먹었습니다.
1막: 2005년, 사소한 문제가 만든 집착
그리고 Dahl은 정말 사소해 보이는 문제와 마주쳤습니다.[35]
파일 업로드 진행률 표시.
지금 생각하면 웃기죠? 사용자가 파일을 올릴 때 "업로드 중... 47%..." 이런 거 보여주는 게 그렇게 어려운 일이었나요?
2005년에는 그랬습니다.[35]
당시 웹 서버들의 작동 방식은 이랬어요. Apache 같은 전통적인 서버는:
사용자가 파일 업로드를 시작합니다.
서버 스레드 하나가 그 작업에 붙습니다.
그리고... 기다립니다.
파일이 다 올라올 때까지...
계속... 기다립니다...
뭘 할 수 있겠어요? 그냥 기다립니다.
드디어 완료!
그제야 그 스레드가 다음 일을 할 수 있습니다.
Plain Text
복사
이게 블로킹(blocking) I/O였습니다.[31,35,39] 파일이 올라오는 동안 서버 스레드는 멍하니 기다리기만 했어요. 다른 일은 못 합니다.
진행률을 확인하려면? 클라이언트가 계속 서버에 AJAX로 물어봐야 했어요. "지금 몇 퍼센트야?" 하지만 그러려면 또 다른 스레드가 필요했고, 그 스레드도 역시 기다리며 블로킹되었습니다.
Dahl은 이 문제에 집착했습니다.
시도 1: Mongrel (Ruby)
Ruby의 Mongrel 웹서버를 사용해봤습니다.[35] 여기서 AJAX로 업로드 상태를 계속 물어보게 했죠.
하지만 Ruby는 single-threaded였어요. 수백 개의 AJAX 요청이 들어오면? CPU가 녹아버렸습니다. 말이 안 됐어요.
시도 2: C
그럼 C로 만들면? C는 빠르잖아요. 멀티스레딩도 자유자재고.[35]
문제는... 웹 개발자 중에 C로 서버 코드 짜고 싶은 사람이 몇이나 될까요? Dahl도 이내 포기했습니다. "아무도 안 쓸 거야."
시도 3: Haskell
그다음은 Haskell.[35] 함수형 프로그래밍 언어. 그리고 중요하게도, non-blocking I/O를 기본으로 지원했어요!
Haskell에서는 모든 입출력 작업이 비동기였습니다. 기다리지 않아도 됐어요. Single-threaded로도 수천 개의 동시 연결을 처리할 수 있었습니다.
"이거다!"
...라고 생각했지만, 현실은 달랐습니다. Haskell은 너무 어려웠어요. 웹 개발자들이 배우고 싶어 할까? 커뮤니티가 너무 작았습니다.
Dahl은 절망했습니다. 그가 원하는 건 간단했어요:
•
Non-blocking I/O ✓
•
웹 개발자들이 이미 아는 언어 ✗
세상에 그런 게 존재하지 않았습니다.
2막: 2008년 9월, 세상이 바뀌다
그리고 Google이 Chrome을 출시했습니다. 그 안에는 V8이라는 JavaScript 엔진이 들어있었죠.[21,22]
Dahl은 벤치마크를 봤습니다. JavaScript가... 3배 빨라졌다고?[23]
순간 번개가 쳤습니다.[35]
"JavaScript!"
처음에는 말도 안 되는 생각 같았어요. JavaScript? 그 브라우저에서 document.getElementById 하는 그 언어로 서버를?
하지만 생각해보니 완벽했습니다:[35]
1. JavaScript 개발자들은 이미 이벤트 기반 프로그래밍을 알고 있었어요.
// 브라우저에서 개발자들은 이미 이렇게 코딩했습니다
button.addEventListener('click', function() {
// 클릭을 "기다리지" 않아요. 그냥 콜백을 등록하죠.
});
JavaScript
복사
콜백 패턴! Non-blocking 사고방식! 이미 JavaScript에 내장되어 있었습니다.
2. JavaScript에는... 아무것도 없었어요.
이게 장점이었습니다. 파일 시스템? 없음. 네트워크 소켓? 없음. 웹 서버? 없음.
즉, 아무도 블로킹 방식으로 I/O를 만들어놓지 않았다는 거예요. 백지 상태! Dahl은 처음부터 모든 걸 non-blocking으로 만들 수 있었습니다.[35]
다른 언어들은? 이미 수십 년 된 블로킹 I/O 라이브러리들이 있었어요. 바꾸려면 모든 걸 부숴야 했습니다. 하지만 JavaScript는 깨끗했어요.
3. V8이 막 나왔어요.
갑자기 JavaScript가 "진짜" 프로그래밍 언어가 될 만큼 빨라졌습니다.
3막: 쾰른의 고독한 6개월
2009년 초, Dahl은 Engine Yard라는 회사에서 60,000달러를 받았습니다.[36] Ruby on Rails 호스팅 회사였는데, 이상하게도 Dahl의 "JavaScript 서버" 아이디어에 돈을 댔죠.
6개월의 활주로가 생겼습니다.
Dahl은 독일 쾰른에 있었어요. Google 사무실이 없어서 원격으로 일했는데, 남자친구/여자친구가 독일 사람이었나 봐요. 어쨌든, 그는 매일 Rudolfplatz의 WiFi 없는 Starbucks에 앉아 코딩했습니다.[36]
WiFi가 없었다는 게 중요합니다. Stack Overflow도 못 봤어요. 구글 검색도 안 됐습니다. 그냥... 혼자, V8 문서와 씨름하며, 모든 걸 처음부터 만들었습니다.
매일 8시간.[36]
// 모든 I/O는 비동기여야 한다
fs.readFile('file.txt', function(err, data) {
if (err) throw err;
console.log(data);
});
// 파일을 읽는 동안, 다른 요청들을 처리할 수 있다!
JavaScript
복사
2009년 5월 27일, Dahl은 첫 버전을 GitHub에 올렸습니다.[32] GitHub는 그때 막 생긴 서비스였고, Dahl은 처음 100명의 사용자 중 하나였죠.[36]
관심 있는 사람? 수십 명 정도.[36]
Dahl은 외로웠을 겁니다. 쾰른의 Starbucks에서, 이게 정말 될까 싶었을 거예요.
4막: Berlin, 2009년 11월 - 모든 게 바뀌다
2009년 늦가을, Dahl은 유럽에서 열리는 JavaScript 컨퍼런스 소식을 들었습니다. JSConf EU, Berlin에서.[36]
"발표하고 싶은데요."
주최자에게 연락했습니다. GitHub에서 수십 명이 Star를 눌러준 프로젝트를 들고.
발표 기회를 얻었습니다.
2009년 11월, Berlin. 150명의 청중.[36]
무대에 선 Dahl. 약간 떨리는 목소리로 시작했을 겁니다.
"여러분, JavaScript로 서버를 만들 수 있습니다."
청중들: "...뭐?"
Dahl은 데모를 보여줬습니다. Node.js로 만든 웹 서버. 수천 개의 동시 연결을 처리하는.
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
console.log('Server running at port 8000');
JavaScript
복사
이게 다였어요. 웹 서버가. 몇 줄의 JavaScript로.
청중들은 믿을 수 없었습니다. 지금까지 JavaScript는 브라우저에 갇혀 있었어요. alert('Hello')나 하고, getElementById 쓰는 언어. "진짜 프로그래밍"은 서버에서 Java, C#, PHP, Ruby로 하는 거였죠.
하지만 Dahl은 말했습니다: "이제 아닙니다."
벤치마크를 보여줬습니다. Node.js가 Apache보다, 심지어 nginx보다도 특정 워크로드에서 빨랐어요. 동시 연결이 많을 때, Node.js는 압도적이었습니다.
발표가 끝났습니다.
그리고 일어섰습니다. 청중이. 전부.
스탠딩 오베이션.[36]
JSConf 역사상 처음이었습니다.
발표가 끝난 후 몇 주 안에, 수십 명이 수만 명이 되었습니다.[36] JavaScript 커뮤니티가 폭발했어요.
Twitter, Reddit, Hacker News에서 Node.js가 화제였습니다. "JavaScript로 서버를 만들 수 있다고?!"
San Francisco의 스타트업들이 Dahl에게 연락했습니다. 여러 제안 중에 Dahl은 Joyent를 선택했습니다.[36] 클라우드 호스팅 회사였는데, 그들의 전문성이 Dahl과 맞았어요. 그리고 중요하게도, 회사에서 Node.js를 계속 개발할 수 있게 해줬습니다.
남미를 떠돌던 수학 박사 중퇴생이, 드디어 안정적인 직장을 얻었습니다.
5막: 2011년, 누군가의 각주
2010년 4월, 뉴욕의 Charlie Robbins는 Nodejitsu라는 스타트업을 만들었습니다.[59] "Node.js 플랫폼 서비스". Node.js 앱을 쉽게 배포하게 해주는 거였죠. Heroku의 Node.js 버전 같은 거.
Robbins는 Node.js 초기 커뮤니티의 핵심 인물이었어요.[57,58] 수많은 npm 패키지를 만들었습니다. 아마 당신도 npm install로 그의 모듈을 쓰고 있을 거예요.
2011년, Robbins는 Nodejitsu 블로그에 기술 포스트를 하나 올렸습니다. "Scaling Isomorphic JavaScript Code".[51,53]
글의 서문에, 각주로, 용어를 하나 정의했습니다:
"Isomorphic JavaScript: 클라이언트와 서버 양쪽에서 실행될 수 있는 JavaScript 코드"
그리고... 아무 일도 일어나지 않았습니다.[57]
글은 좋았어요. 아이디어도 훌륭했습니다. 하지만 2011년, JavaScript 개발자들은 다른 데 열광하고 있었거든요.
SPA. Single Page Application.
Backbone.js가 2010년에 나왔습니다. Knockout.js도. 모두가 외쳤어요: "클라이언트가 전부다! 서버는 그냥 JSON API면 돼!"
Robbins의 "isomorphic"이라는 각주? 아무도 기억하지 못했습니다.
6막: Airbnb의 모바일 재앙 (2011-2013)
희망에 가득 찬 시작
2011년, Airbnb는 모바일 웹사이트를 만들기로 결정했습니다.[41,43] 당시 유행하던 최신 방식으로 만들었죠.
Backbone.js + Handlebars.js로 SPA.[47]
개발자들은 신났어요. 이게 미래잖아요? 화면 전환이 부드럽고, 앱처럼 느껴지고, 모던하고...
그리고 배포했습니다.
재앙
사용자 피드백이 들어오기 시작했습니다.
"너무 느려요."
"아무것도 안 보여요."
"계속 로딩만 돼요."
특히 모바일에서. 특히 느린 네트워크에서.[41,48]
무슨 일이 일어나고 있었을까요?
사용자가 Airbnb 모바일 사이트를 방문하면:
1단계: HTML 다운로드
→ 빈 페이지. <div id="app"></div>만 있음.
(3G 네트워크에서 1-2초)
2단계: JavaScript 번들 다운로드
→ Backbone, Handlebars, 앱 코드 전부
→ 당시 모바일 네트워크는 느렸습니다!
(3G에서 3-5초)
3단계: JavaScript 파싱 및 실행
→ 2011년 모바일 CPU는 느렸습니다!
(2-3초)
4단계: API 요청
→ 실제 데이터를 가져옴
(2-3초)
5단계: DOM 렌더링
(1초)
총합: 10-15초
Plain Text
복사
사용자는 10초 넘게 빈 화면을 봐야 했어요.[41]
데스크톱에서는? 괜찮았습니다. 하지만 2011년, iPhone 4 시대. 모바일이 폭발적으로 성장하던 시기. Airbnb의 모바일 경험은 재앙이었습니다.
더 나쁜 게 있었습니다.
SEO의 종말
Google 크롤러가 Airbnb 사이트를 방문하면:
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
HTML
복사
이게 다였어요.[43]
Google은 JavaScript를 실행하지 않았습니다. (당시에는.) 그래서 Google이 본 건? 빈 페이지.
Airbnb의 수백만 개 숙소 리스팅이 검색 결과에서 사라졌습니다.
코드 중복의 지옥
그리고 개발자들은 다른 문제를 발견했습니다.[43]
라우팅 로직:
•
클라이언트: Backbone Router (JavaScript)
•
서버: Rails Router (Ruby)
폼 검증:
•
클라이언트: JavaScript
•
서버: Ruby (다시!)
템플릿:
•
클라이언트: Handlebars.js
•
서버: ERB
같은 로직을 두 번 작성해야 했어요. 한번은 JavaScript로, 한번은 Ruby로.
버그를 고치면? 두 곳에서 고쳐야 합니다. 기능을 추가하면? 두 번 구현해야 합니다.
악몽이었습니다.
7막: Spike Brehm의 반란
Airbnb의 엔지니어 Spike Brehm은 밤잠을 설쳤을 겁니다.[41]
"이건 말이 안 돼. 분명 더 나은 방법이 있어."
그리고 문득 깨달았습니다.
"잠깐... Node.js가 있잖아?"
Node.js = JavaScript on the server!
그럼... 그럼:
•
서버에서 Handlebars로 HTML 렌더링 → 가능
•
클라이언트에서도 Handlebars로 렌더링 → 가능
•
Backbone Model을 서버에서도 사용 → 가능
•
라우터를 공유 → 가능
같은 코드를 양쪽에서 실행할 수 있다!
Brehm은 구글을 뒤지기 시작했습니다. "JavaScript server client same code..."
그리고 발견했습니다. 2011년 Charlie Robbins의 블로그 포스트.[44,52]
"Isomorphic JavaScript"
"이거다!"
재작성의 시작
2012년, Airbnb는 모바일 웹을 완전히 재작성하기로 결정했습니다.[41,43]
이번에는 다르게. Node.js 기반으로. Isomorphic하게.
Brehm과 팀은 Rendr라는 라이브러리를 만들었습니다.[47] Backbone.js + Handlebars를 서버와 클라이언트 양쪽에서 실행할 수 있게.
// 이 View 코드가 서버와 클라이언트 양쪽에서 실행됩니다
var ListingView = BaseView.extend({
fetch: function() {
return this.app.fetch(Listings, {
city: this.params.city
});
},
template: require('./listing-template')
});
JavaScript
복사
서버에서 이 코드를 실행하면? 완전한 HTML이 생성됩니다.
클라이언트에서 실행하면? 부드러운 페이지 전환.
2013년 초, 새 모바일 웹 출시.[43]
결과:
•
첫 페이지 로딩: 2-3초 (이전 10초+)
•
SEO: 복구됨
•
코드 중복: 대부분 제거됨
•
개발자 행복도: 상승
8막: Isomorphic의 봄 (2013)
복음의 전파
2013년 4월, Airbnb는 Rendr를 오픈소스로 공개했습니다.[47] TechCrunch가 기사를 썼어요.
2013년 11월, Spike Brehm은 VentureBeat에 글을 썼습니다:[48]
"The Future of Web Apps Is Isomorphic JavaScript"
"당신은 여기서 처음 들었습니다. 몇 년 안에, 서버에서 JavaScript를 실행하지 않는 고급 웹 앱을 보기 힘들 것입니다."
글이 바이럴되었습니다. Twitter, Reddit, Hacker News에서 폭발했어요.
"Isomorphic JavaScript"가 드디어, 2년 만에, 주류가 되었습니다.
Charlie Robbins의 2011년 각주가 드디어 빛을 보는 순간이었죠.[53]
다른 전사들
Airbnb만이 아니었습니다.
Derby (2011)[62,67]
Google 출신 Nate Smith와 Brian Noguchi. 그들은 더 야심찼어요. "그냥 렌더링만? 아니지. 실시간 동기화를 하자!"
// 한 사용자가 데이터를 바꾸면
model.set('room.message', 'Hello!');
// 모든 연결된 클라이언트에 자동으로 전파됩니다!
// Google Docs처럼!
JavaScript
복사
Derby의 Racer 엔진은 Operational Transformation을 사용했어요.[61] 충돌 해결 알고리즘. 여러 사용자가 동시에 같은 데이터를 편집해도 괜찮았습니다.
기술적으로는 놀라웠습니다. 하지만... 너무 복잡했어요. 그리고 커뮤니티가 작았습니다.
Meteor (2012)[56]
아마 가장 유명했던 프로젝트.
// 클라이언트에서 이렇게 쓰면
Posts.find({author: 'Ryan'}).fetch();
// 실시간으로 업데이트됩니다!
// 서버에서 데이터가 바뀌면, 자동으로!
JavaScript
복사
Meteor는 커뮤니티 구축을 잘했어요. 자체 패키지 매니저, 배포 도구, 전체 생태계. JavaScript 커뮤니티의 큰 기대를 받았습니다.
Yahoo Mojito (2012)[41,56]
Yahoo가 만든 첫 isomorphic 프레임워크. 기술적으로는 훌륭했어요.
하지만... YUI(Yahoo User Interface) 라이브러리에 의존했습니다. 2012년에 누가 YUI를 쓰고 싶었을까요? jQuery가 이미 대세였는데.
커뮤니티의 무관심 속에 사라졌습니다.
Asana의 Luna[56]
Facebook 공동창업자 Dustin Moskovitz의 회사. 수년간 R&D에 수백만 달러를 투자해 Luna 프레임워크를 만들었어요.
Luna는 미쳤습니다: 각 사용자마다 별도의 서버 프로세스를 실행했어요. 서버에서 전체 앱을 복제하는 거죠.
돈이 무한하니까 가능했던 접근법이었습니다.
9막: 2014년, 깨진 꿈
그리고 2-3년이 지났습니다.
Spike Brehm이 인터뷰에서 조용히 밝혔습니다:[42]
"Airbnb는 모바일 웹에서 Rendr 사용을 중단할 계획입니다."
...뭐라고요?
왜?
Brehm은 설명했습니다:[42]
문제 1: Brownfield의 저주
Airbnb의 백엔드는 6년 된 Rails 애플리케이션이었어요.
Ruby로 작성된:
•
CSRF 보호 로직
•
세션 관리
•
쿠키 파싱
•
인증/인가
•
특수 HTTP 헤더
•
비즈니스 로직 수천 줄
이 모든 걸... Node.js로 다시 작성해야 했습니다.
두 개의 서버. 두 벌의 코드. 두 배의 유지보수.
"JavaScript를 공유할 수 있어서 좋았지만, 그 대가로 모든 서버 로직을 복제해야 했습니다."[42]
문제 2: Node.js 프로덕션의 현실
2013년 Node.js는 아직 어렸어요. 프로덕션에서:
•
메모리 누수
•
예기치 않은 크래시
•
모니터링 도구 부족
•
팀의 경험 부족
Rails는? 10년 된 성숙한 프레임워크. 모든 게 잘 작동했습니다.
"Node.js 앱을 프로덕션에서 운영하는 오버헤드가 생각보다 컸습니다."[42]
문제 3: Isomorphic의 근본적 한계
그리고 가장 중요한 문제. Brehm은 이걸 말하지 않았지만, 모두가 알고 있었어요.
양쪽에서 실행되는 코드는 제약이 많았습니다.
// ❌ 이런 코드는 isomorphic이 될 수 없습니다
import db from 'database';
function getUser(id) {
const user = db.query('SELECT * FROM users WHERE id = ?', [id]);
return user;
}
// 브라우저에 PostgreSQL이 있나요? 없습니다.
// ❌ 이것도 안 됩니다
import fs from 'fs';
function readConfig() {
return fs.readFileSync('config.json');
}
// 브라우저에 파일 시스템이 있나요? 없습니다.
// ❌ 환경 변수도 안 됩니다
const secret = process.env.API_SECRET;
// 브라우저에서 실행하면 어떡할까요?
JavaScript
복사
Isomorphic 코드는 "최소 공통분모"만 사용할 수 있었습니다:
•
데이터 변환 ✓
•
View 렌더링 ✓
•
라우팅 ✓
•
간단한 검증 ✓
하지만:
•
데이터베이스 ✗
•
파일 시스템 ✗
•
서버 전용 라이브러리 ✗
•
민감한 비즈니스 로직 ✗
결국 진짜 서버 로직은 여전히 따로 작성해야 했어요.
Isomorphic은 "View 레이어"만 공유할 수 있었습니다.
패배의 고백
Brehm은 조심스럽게 덧붙였습니다:[42]
"만약 그린필드 상황에서 새 앱을 만든다면? 내 선택은 여전히 Rendr 같은 isomorphic 접근법일 겁니다."
즉, 처음부터 시작할 때만 좋다는 거였죠.
기존 앱을 마이그레이션? 비용이 너무 컸습니다.
대부분의 회사는 기존 앱을 가지고 있었어요.
에필로그: 2014년, 불완전한 답
2014년 말, 상황을 정리하면:
Isomorphic이 해결한 것:
•
첫 페이지 로딩 속도 ✓
•
SEO ✓
•
View 레이어 코드 공유 ✓
여전히 문제:
•
서버 로직은 제한적 ✗
•
Brownfield 마이그레이션 너무 힘듦 ✗
•
프레임워크들이 너무 크고 복잡함 ✗
•
"진짜" 서버 기능은 못 씀 ✗
개발자들은 깨달았습니다. Isomorphic은 타협이었어요. 이것도 아니고 저것도 아닌.
클라이언트도 아니고, 서버도 아니고... 그 중간 어딘가.
더 나은 답이 필요했습니다.
하지만 어떻게?
다음 장 예고:
2013년, 한 Facebook 엔지니어가 무대에 올라 발표합니다.
"JSX를 소개합니다. HTML을 JavaScript 안에 쓸 수 있어요."
청중들: "...미쳤어?"
하지만 그 뒤에는 2009년부터 Facebook이 고민해온 어떤 것이 숨어있었습니다. PHP에서 시작된 그 무언가. XHP와 BigPipe.
그리고 React의 이야기가 시작됩니다.
참고 문헌 표기:
[31-40] Node.js 및 Ryan Dahl 관련
[41-50] Airbnb Rendr 및 Spike Brehm
[51-60] Charlie Robbins 및 Nodejitsu
[61-70] Derby, Meteor 등 초기 isomorphic 프레임워크
