티스토리 뷰

카테고리 없음

Deno 1.0

한장현 2020. 5. 15. 17:37

안녕하세요. 한장현입니다.

 

한국 시각으로 5월 14일 07:38에 deno v1.0.0이 릴리즈 되었습니다.

 

node.js를 대체하기 위해 나온 deno가 얼마나 완성되었는지, deno v1.0.0 릴리즈와 함께 작성된 블로그 글을 번역해 봤습니다.

 


Deno 1.0

by Ryan Dahl, Bert Belder, and Bartek Iwańczuk

 

 

 동적 언어는 그 자체로도 훌륭한 툴이라고 할 수 있습니다. 빠르고 간결하게 작성할 수 있지만 복잡한 시스템은 물론이고 아이디어를 간단하게 구현하는 용도로도 활용할 수 있습니다. 시스템 환경이나 메모리 관리를 신경 쓸 필요도 없습니다. 최근에는 특히 Rust나 Go와 같은 언어를 활용하면서 복잡한 기계어를 좀 더 쉽게 활용할 수 있었고, 컴퓨터 인프라 환경을 발전시키는 데에도 큰 기여를 했습니다. 하지만 우리는 다양한 영역에서 발생하는 문제를 해결할 수 있는 강력한 스크립트 환경을 갖추는 것이 여전히 중요하다고 생각합니다.

 

 현재 동적 언어 중에서 가장 널리 사용되고 있는 것은 JavaScript입니다. JavaScript는 웹 브라우저가 동작하는 모든 디바이스에서 실행할 수 있습니다. JavaScript를 자유롭게 활용할 수 있는 개발자들은 이미 세상에 널려있으며, 개발자들은 이제 코드 최적화에 더 힘쓰고 있습니다. ECMA International이라는 표준 기구가 제 역할을 하면서 JavaScript도 조금씩 개선되고 있습니다. 브라우저 환경이나 독립 프로세스로 실행되는 환경이라면 JavaScript를 선택하는 것이 이제는 당연해 보입니다.

 

 이전에 개발했던 Node.js는 소프트웨어 플랫폼으로써 아주 성공적이었습니다. Node.js는 웹 개발에 활용되는 툴이나 독립적인 웹 서버를 구현하는 것 외에도 수많은 용도로 활용되고 있습니다. 하지만 Node.js는 JavaScript가 지금과는 많이 다른 언어였던 2009년에 설계되었습니다. 그때는 필요했기 때문에 Node.js에 자체적으로 도입된 컨셉들이, JavaScript 표준 기구에서 논의되면서 다른 방식으로 도입된 것들이 있습니다. 자세한 내용은 Node의 설계 실수 발표 영상에서 확인할 수 있습니다. 하지만 이제 Node.js 사용자는 너무나 많기 때문에 Node.js의 잘못된 것들을 당장 바로잡는 것은 너무 어려운 일이 되었습니다.

 

 JavaScript가 변화하면서 TypeScript와 같은 파생 프로젝트도 생겨났습니다. 이제 Node 프로젝트는 빌드 시스템과 프로젝트 자체를 관리하는 툴만으로도 굉장히 복잡해져서 동적 언어의 즐거움을 느끼기 힘들어졌습니다. 게다가 외부 라이브러리가 꼭 npm 저장소를 통해야만 한다는 것은 웹의 이상적인 방향과도 맞지 않습니다.

 

 우리는 JavaScript 생태계와 관련 인프라 환경은 더 단순해져야 한다고 생각했습니다. 그래서 다양한 영역에 활용할 수 있으면서도 즐거움과 생산성을 함께 찾을 수 있는 스크립트 환경을 찾게 되었습니다.

 

커맨드 라인에서 동작하는 웹 브라우저

 

 Deno는 JavaScript나 TypeScript를 웹 브라우저 밖에서 실행하는 새로운 실행환경입니다.

 

 Deno는 복잡한 기능을 단독으로 실행할 수 있는 툴이 되어야 한다고 생각합니다. Deno 애플리케이션은 파일 하나로 구성할 수 있습니다. 그리고 웹 브라우저처럼 추가 라이브러리가 필요하면 가져와서 활용하면 됩니다. Deno를 활용하면 다른 툴 없이도 복잡한 로직을 실행할 수 있습니다.

 

import { serve } from "https://deno.land/std@0.50.0/http/server.ts";

for await (const req of serve({ port: 8000 })) {
  req.respond({ body: "Hello World\n" });
}

 

 HTTP 서버 모듈은 코드 한 줄로 불러올 수 있습니다. 이 모듈에 대한 환경설정은 필요 없으며 추가로 설치해야 할 것도 없습니다. deno run example.js라고 실행하기만 하면 됩니다.

 

 다른 브라우저들과 마찬가지로 이 코드는 안전한 샌드박스 안에서 실행됩니다. 이 스크립트 파일은 하드 드라이브에 접근할 수 없으며, 네트워크를 맘대로 연결할 수도 없고, 권한없이 악성 코드를 실행할 수도 없습니다. 브라우저에서 카메라나 마이크를 활용하려면 먼저 사용자에게 권한을 받아야 합니다. Deno도 이와 비슷한 방식을 터미널에서 활용합니다. 사실 위 코드는 --allow-net 옵션을 지정하지 않으면 제대로 동작하지 않습니다.

 

 Deno는 브라우저의 표준 JavaScript API를 지키기 위해 노력했습니다. 물론 모든 브라우저 API가 Deno와 관련이 있는 것은 아니지만, 최소한 Deno가 표준에서 벗어난 것은 아닙니다.

 

TypeScript 1급 클래스(first class) 지원

 

 우리는 Deno가 한 줄짜리 스크립트부터 복잡한 서버사이드 비즈니스 로직을 다루는 용도로도 활용되기를 바랬습니다. 그리고 프로그램은 날이 갈수록 복잡해지기 때문에 타입을 제대로 구별해야 할 필요성도 계속해서 증가합니다. TypeScript는 JavaScript가 기반이며 개발자가 타입에 대한 정보를 추가할 수 있기 때문에 적절한 대안이 되었습니다.

 

 Deno는 다른 툴 없이도 TypeScript를 지원합니다. 실행환경도 TypeScript를 염두에 두고 설계되었습니다. 커맨드 라인에서 deno types 명령을 실행해보면 Deno가 제공하는 타입 정의를 확인할 수 있습니다. 그리고 Deno의 기본 모듈은 모두 TypeScript로 작성되었습니다.

 

모든 것은 Promise입니다.

 

 Node는 JavaScript가 Promise, async/await 컨셉을 도입하기 이전에 설계되었습니다. Node는 Promise를 EventEmitter로 구현했으며 이 방식으로 소켓 API, HTTP API를 설계했습니다. async/await의 장점을 살펴보지 않아도 EventEmitter 패턴은 백 프레셔(back-pressure) 이슈가 있습니다. TCP 소켓 하나를 받는 경우를 생각해 봅시다. 소켓이 패킷을 받으면 데이터 이벤트를 발생시킵니다. 이렇게 발생한 데이터는 콜백 함수들을 거치는 방식으로 자유롭게 처리될 수 있었기 때문에 프로세스에는 곧 수많은 이벤트들이 발생했었습니다. 하지만 이 와중에도 Node는 계속 새로운 데이터 이벤트를 받기 때문에 TCP 소켓이 데이터를 모두 처리할 수 없는 상태가 되더라도 외부에서는 이 상황을 알 수 없고 결국 데이터 이벤트가 넘쳐나는 상황이 될 수 있습니다. 이 문제를 해결하기 위해 pause() 메소드가 추가되었습니다. 이 메소드는 문제 상황을 해결할 수는 있었지만 코드를 추가로 작성해야 한다는 점에서 베스트 솔루션은 아닙니다. 그리고 프로세스가 바쁜 상태에서는 이 방법으로도 완전히 해결될 수 없기 때문에 응답 시간이 길어지는 문제는 여전히 존재합니다.

 Deno에서도 소켓은 여전히 비동기로 동작하지만 새로운 데이터는 read()가 있을 때만 받습니다. 소켓을 천천히 받기 위해 잠시 멈추는 코드는 이제 필요 없습니다. TCP 소켓 이외에도 이런 방식을 활용했습니다. 시스템과 맞닿은 계층부터 Promise를 활용합니다. 우리는 이런 바인딩을 "ops"라고 부릅니다. Deno에서 실행되는 모든 콜백은 Promise로 동작합니다.

 

 Rust에도 Promise와 비슷한 구현체가 있긴 합니다. Future라고 하는데, Deno는 Rust에서 활용하던 future 방식의 API를 JavaScript Promise로 구현했습니다.

 

Rust API

 

 Deno 구성요소 중 가장 중요한 것은 Deno 커맨드라인 인터페이스(CLI)입니다. 그리고 이 CLI의 버전은 오늘로 1.0입니다. 다만 Deno는 통으로 짜인 프로그램이 아니라 Rust 크레이트(crate) 묶음과 비슷하게 설계되었기 때문에 다양한 계층으로 존재합니다.


 deno_core 크레이트는 Deno의 뼈대입니다. 이 크레이트는 TypeScript나 Tokio와도 직접 관련이 없습니다. 이 패키지는 단순하게 Op과 Resource 인프라 환경만을 제공합니다. 그리고 Rust future와 JavaScript Promise를 바인딩하는 컨셉을 제공합니다. 물론 CLI도 deno_core를 활용하도록 설계되었습니다.


 rusty_v8 크레이트는 Rust와 V8 C++ API를 바인딩하는 크레이트입니다. 이 크레이트는 기존 C++ API를 최대한 활용하도록 구현되었습니다. 그래서 API가 접근하는 Rust 객체는 거의 정확하게 C++ 객체와 매칭되기 때문에 바인딩 비용이 없다고도 할 수 있습니다. 이 크레이트를 활용하면 Github Action CI가 제공하는 바이너리 코드를 그대로 활용할 수 있으면서도 V8 빌드 설정을 변경해서 컴파일하는 방식으로 활용할 수도 있습니다. V8의 소스 코드는 모두 크레이트에 담겨 배포됩니다. 마지막으로 rusty_v8 크레이트는 안전한(safe) 인터페이스를 제공하려고 합니다. 아직 100% 안전하다고 할 수는 없지만, 이 작업은 거의 끝나갑니다. VM과 V8은 아주 복잡하게 상호작용하는 구조였지만 Deno에서 발생하는 수많은 버그를 처리하면서 결국 안전하게 동작할 수 있는 결과물을 만들어 냈습니다.

 

안정성 (Stability)

 

 Deno는 일관된 API를 제공할 것을 약속합니다. Deno에는 수많은 인터페이스와 컴포넌트들이 있습니다. 우리가 말하는 "안정성"이라는 것이 어떤 것인지 확실하게 짚고 넘어갑시다. JavaScript로 OS와 통신하는 API는 모두 "Deno"라는 네임스페이스 안에 있습니다. Deno.open()과 같은 식입니다. 이런 메소드 이름은 신중하게 검토되어 결정되었으며 앞으로도 변경되지 않을 것입니다.

 그리고 아직 안정화되지 않은 기능들은 --unstable 옵션을 붙여야 활용할 수 있습니다. 안정화되지 않은 인터페이스를 확인하려면 여기를 참고하세요. 앞으로 릴리즈될 때마다 이 API 중 일부는 안정화될 수 있습니다.

 setTimeout()이나 fetch() 같이 전역 네임스페이스에서 참조할 수 있는 객체들이 있습니다. 이 인터페이스들은 일단 브라우저에서 사용하던 대로 남겨두기로 했지만 호환성에 문제가 발견되면 다른 방식으로 변경될 수 있습니다. 이런 인터페이스들은 브라우저의 표준이지 우리의 표준은 아니기 때문입니다. 그리고 이런 이유로 인터페이스가 변경된다면 이것은 "인터페이스 변경"이 아니라 "버그 픽스"로 처리할 것입니다. 브라우저 표준 API의 호환성에 문제가 발견되면 이 문제는 메이저 릴리즈 전에 수정될 것입니다.

 Deno에는 Rust를 활용하는 API가 많습니다. deno_core 크레이트와 rusty_v8 크레이트가 특히 그렇습니다. 그래서 이 API들은 아직 1.0이 아닙니다. 물론 작업은 계속되고 있습니다.

 

한계

 

 Deno는 Node를 기반으로 개선된 것이 아닙니다. Deno는 완전히 새롭게 작성되었습니다. Deno는 아직 개발한 지 2년밖에 되지 않았지만, Node가 10년에 걸쳐 개선된 것처럼 Deno도 Node처럼 계속해서 발전하면서 성숙될 것입니다.

 당장 Deno를 사용해서 애플리케이션을 만들 수는 있지만 어떤 기능을 활용하느냐에 따라 한계가 있습니다. Deno를 빠르게 사용해보고 싶은 분들을 위해 지금 Deno가 갖고 있는 한계를 정확하게 안내해 드리겠습니다.

 

 

호환성

 

 Deno를 사용하게 되면 먼저 JavaScript 툴을 사용할 수 없다는 점이 당황스러울 수 있습니다. Deno는 대부분의 경우에 Node (NPM) 패키지와 호환되지 않습니다. Deno의 기본 레이어는 Node와 호환성을 맞추려고 하지만 아직 이 작업은 시간이 더 많이 필요합니다. https://deno.land/std/node/에서 확인할 수 있습니다.

 

 그리고 Deno는 모듈 시스템을 좀 더 간략화하고 싶었지만 이 부분은 Node와 지향하는 바가 비슷했기 때문에 인터페이스도 비슷합니다. 앞으로는 Deno가 좀 더 발전하면서 Node에서 벗어나기를 바랍니다.

 

HTTP 서버 성능

 우리는 계속해서 Deno HTTP 서버의 성능을 검토해 왔습니다. 현재 hello-world Deno HTTP 서버는 초당 25,000 요청을 처리할 수 있으며 최대 지연시간은 1.3ms 입니다. 같은 기능을 하는 Node 서버는 초당 34,000 요청을 처리할 수 있으며 최대 지연시간은 2~300ms입니다.

 

 Deno HTTP 서버는 네이티브 TCP 소켓을 활용하는 TypeScript 코드로 작성되었습니다. Node HTTP 서버는 C언어로 작성되었으며 JavaScript로 활용할 수 있도록 바인딩되었습니다. Deno에도 네이티브 HTTP 서버를 바인딩하는 방식을 사용할 수 있었지만, TCP 소켓 레이어를 좀 더 최적화하고 op 인터페이스를 일관되게 유지하기 위해 그렇게 하지는 않았습니다.

 

 초당 25,000 요청을 비동기로 처리할 수 있다는 것만으로도 당장 활용하는 데에는 문제가 없습니다. 이정도가 문제라면 JavaScript를 사용하지 않는 것을 고려해야 합니다. 앞으로 Deno에서 Promise를 사용하는 코드가 개선된다면 좀 더 나은 성능을 낼 수 있을 것입니다. 다음 릴리즈를 기대해 주세요.

 

TSC 병목

 

 Deno는 내부적으로 Microsoft TypeScript 컴파일러를 활용해서 타입을 체크하고 javaScript 코드를 생성합니다. 그래서 이전에 Node가 그랬던 것처럼 V8이 JavaScript 코드를 파싱하는 것과 비교해보면 상당히 느립니다. "V8 스냅샷"을 활용하면서 이 성능은 눈에 띄게 개선되었지만 아직 충분하지 않습니다. 물론 TypeScript 컴파일러도 계속 개선될 것이라 생각합니다. 하지만 우리는 결국 Rust로 타입 체크 로직을 구현해야겠다는 결론을 냈습니다. 물론 이 작업이 금방 끝나진 않을 것입니다. 하지만 작업이 끝난 후엔 비교할 수 없는 성능 차이가 날 것이고 결국 개발자가 체감하는 것은 이 포인트이기 때문에 이 방향으로 진행하려고 합니다. TSC는 반드시 Rust로 포팅되어야 합니다. 이 문제를 함께 해결하려는 분이 있다면 연락 부탁드립니다.

 

플러그인 / 확장

 

 Deno는 런타임 환경을 확장할 수 있는 플러그인 시스템을 갖추고 있습니다. 하지만 이 시스템은 아직 개발 중이며 안정된 상태라고 하기 어렵습니다. Deno가 제공하는 기능 외에는 네이티브 시스템에 접근하기 어려울 것입니다.

 

감사한 분들

 

 이번 릴리즈가 나올 수 있게 도와주신 컨트리뷰터들에게 감사드립니다. 특히 @kitsonk는 TypeScript 컴파일러 호스트, deno_typescript, deno bundle, deno install, deno types, 스트림 구현 등 시스템 각 영역에서 큰 공헌을 해주셨습니다. 그리고 @kevinkassimo는 이 프로젝트가 진행되는 내내 수많은 버그를 해결해 주셨습니다. 타이머 시스템, TTY 통합, wasm, Deno.makeTempFile, Deno.kill, Deno.hostname, Deno.realPath, std/node 연결부, window.queueMicrotask, REPL 등 여러 분야에 걸쳐 도움을 주셨으며 특히 로고도 만들어 주셨죠! @kt3k는 우리가 계속 사용하고 있는 벤치마크 시스템, 시그널 핸들러, 권한 API를 만드는데 도움을 주셨으며 수많은 크리티컬 버그를 수정해 주셨습니다. @nayeemrmn도 Deno의 각 부분에서 발생하는 버그를 수정하는 데에 도움을 주셨습니다. 특히 @nayeemrmn이 스택 트레이스와 에러 리포팅하는 코드를 훌륭하게 개선해주셨기 때문에 오늘 API가 1.0으로 안정될 수 있었습니다. @justjavac은 웹 표준과 deno API가 연결되는 부분에서 발생하는 크고 작은 버그들을 해결해 주셨으며 VS Code deno 플러그인을 구현해 주셨습니다. @zekth는 std/encoding/csv, std/encoding/toml, std/http/cookies와 같이 std와 관련된 모듈을 개발하는 데에 도움을 주셨으며 버그도 많이 해결해 주셨습니다. @axetroy는 코드 가독성 향상에 도움을 주셨으며 버그 수정도 도와주셨습니다. VS Code 플러그인을 관리하는 분도 이분입니다. @afinch7는 플러그인 시스템 개발에 도움을 주셨습니다. @keroxp는 웹소켓 서버 구현에 도움을 주셨으며 이 분이 고친 버그도 아주 많습니다. @cknight는 수많은 문서작업에 도움을 주셨고 std/node 폴리필 구현도 도와주셨습니다. @lucacasonata는 거의 혼자서 deno.land 웹사이트를 만들어 주셨습니다. @hashrock은 훌륭한 아트워크 작업을 해주셨습니다. doc.deno.land에 있는 로딩 페이지와 이 문서 제일 위쪽에 있는 이미지도 이 분이 작업하셨습니다!

 

 

One last thing

 

Deno v1.0 후드티를 주문하시면 이 오픈 소스 개발에 큰 도움을 주실 수 있습니다:

 

구매 링크

공유하기 링크
TAG
댓글
댓글쓰기 폼
공지사항
Total
396,352
Today
5
Yesterday
154
링크
«   2020/07   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
글 보관함