Deno를 소개합니다

YOKITOMI·2022년 8월 25일
37
post-thumbnail
post-custom-banner

NodeJS 그 동안의 역사

2000년대 말

잠깐 NodeJS가 세상에 나오기 직전인 2000년대 후반으로 돌아가보죠.

그때만 해도 JS는 웹 개발을 위해 어쩔 수 없이 써야하는 스크립트 언어에 불과하다는 인식이 있었죠. Java나 C와 같은 근본 충만한 진짜 프로그래밍 언어와는 구분해야할, 약간은 격이 떨어지는 존재였습니다.

여기서 스크립트 언어라는 게 무슨 뜻일까요? DX(개발자 경험)의 관점에서 볼때 다음과 같은 정의를 생각해볼 수 있습니다.

복붙해서 실행하면 잘 돌아가긴하지만 그게 전부인 언어. 규모있는 소프트웨어를 개발하는 데에는 부적합하다.

확실히 JS는 이런 관점에서 스크립트 언어가 맞는 것 같죠. 하던대로 웹 개발에만 툴툴거리면서 쓰면 그만이었을지도요.

그런데 조금 다르게 생각하는 사람들도 있었습니다.

  • Crome의 JS 엔진인 V8이 그렇게 성능이 좋다던데? 브라우저에서만 쓰기 아까운걸.

  • JS, 그동안 만만하게 봤는데 생각해보니 이런 금수저 언어가 따로 없군.
    웹이 망하지 않는 이상 JS 생태계는 계속 성장할거야.

  • php같은 녀석도 웹서버를 돌리는데 JS라고 못할쏘냐.

디노 (소프트웨어) - 위키백과, 우리 모두의 백과사전

Ryan Dahl도 그런 사람들 중 하나였고, 그는 NodeJS를 만들었습니다.

2022년 지금

이제 NodeJS가 나온지도 10년이 넘었고 그 동안 많은 것이 바뀌었습니다.

NodeJS 생태계의 성장과 함께, JS는 진정한 범용 프로그래밍 언어로 거듭났습니다. 웹 프론트엔드는 물론이고, 백엔드, 네이티브 어플리케이션까지 개발할 수 있게 되었습니다. 새로 나온 SDK들의 NodeJS 지원은 당연한 일이 됐고요. 그러는 동안 npm은 모든 생태계를 통틀어 가장 큰 패키지 저장소가 되었지요.

즉, JS로 큰 소프트웨어를 개발하는게 가능하다는게 입증되었습니다!

물론 이게 공짜로 이루어진것은 아닙니다.

ECMAScript의 진화

그동안 JS 언어 자체도 꾸준한 진화했습니다. ECMAScript라는 이름으로 버전을 올려나가고 있지요. async/await부터 Array의 고차 함수들까지 크고 작은 변화들이 JS를 더 쓸만한 언어로 만들었습니다. 그렇지만 이런 발전을 NodeJS 사용자들이 손쉽게 누리진 못했습니다. NodeJS의 ECMAScript의 최신 버전에 대한 지원이 늦다보니 babel로 트랜스파일하는 작업이 수반되었지요.

ES Module처럼 핵심적인 변화이면서도 여전히 도입이 더딘 기능도 있습니다.

TypeScript의 등장

JS란 언어 자체가 성에 차지 않는 사람들이 있었죠. CoffeeScript, ClosureScript, TypeScript 등등 JS로 컴파일 되는 언어들이 춘추전국시대를 벌였습니다. 최종 승자는 아시다시피 TypeScript였지요. 현재 많은 사람들에게 TS는 암묵적으로 JS의 적통으로 여겨집니다. 심지어 JS를 TS로 확장시키자는 제안도 있을 정도지요.

TS의 모토를 아시나요?

JavaScript that scales.

네, 처음부터 JS로 큰 규모의 소프트웨어를 개발할수 있도록 하는 것이 목표였습니다. 결국 VS Code, Grafana, TypeScript(자기 자신!) 등의 프로젝트를 성공시켰으니, 목표가 달성되었다고 봐도 되겠지요?

TS는 다 좋은데, 정말 다 좋은데, NodeJS가 바로 실행할 수 없다는 사소한 단점이 있습니다. tsconfig.json을 설정하고, JS로 트랜스파일해서 배포해야하죠. 최신 ECMAScript를 사용할 때와 같은 종류의 번거로움이 있습니다.


NodeJS와 그 생태계는 JS는 규모있는 소프트웨어를 개발하는 데에는 부적합하다는 문제를 해결했습니다. 기대한 것 이상으로 훌륭하게요. 그러는 동안 JS는 더 이상 스크립트 언어가 아니게 되었습니다. 이제 어엿한(?) 빌드 과정이 필요한 언어가 되었지요.

음, 그게 뭐가 문제일까요?

NodeJS 게 섯거라, Deno가 간다

복붙해서 실행하면 잘 돌아가긴하지만 그게 전부인 언어.

가만 보면, 복붙해서 실행하면 돌아가는 건, 순수한 장점입니다. 쉽고 간편하게 공유할 수 있단 얘기죠. 그런데 지금 JS 또는 TS 코드가 그런가요? 일단 babel이나 tsc로 컴파일해야 하고요. 또 npm의 그 많은 패키지 중 하나도 사용하지 않았을리가 없지요. 의존성을 기술한 package.json도 필요합니다.

이렇게 된 데에는 NodeJS에게도 어느 정도의 책임이 있습니다. 10년간 언어와 생태계가 발전하는 동안, NodeJS는 변화에 미온적인 대응으로 일관했으니까요. 사실 바꾸려면 하위호환을 포기해야할 경우도 있었으니, 이미 궤도에 오른 프로젝트에겐 너무 무리한 요구로 보이기도 합니다. 어쨋든, 2022년 지금 NodeJS를 쓴다는건, 여러 외부 패키지와 개발 도구들의 사용을 전제한 결정이어야 할 것입니다.

Ryan Dahl은 한 발표에서 NodeJS를 개발하며 내렸던 결정들 중 후회하는 것 10개를 고백하며, 그 문제들을 해결한 Deno를 소개합니다. 현재 Deno의 Github 스타 수는 84.5k로 NodeJS의 89.4K와 비교해 5K의 차이밖에 나지 않습니다. 둘의 점유율 차이를 고려해봤을때, 실로 어마어마한 기대와 관심을 받고 있습니다. Deno는 생태계의 동력이 필요한 종류의 프로젝트인데 그 부분은 걱정하지 않아도 되겠습니다.

이쯤되면 NodeJS 2 또는 NodeJS 리메이크라고 봐도 무리가 없지 않을까요?

디노 (소프트웨어) - 위키백과, 우리 모두의 백과사전

그럼 이제 Deno가 NodeJS와 비교해 어떤 점이 달라졌는지 살펴봅시다.

TypeScript는 이제 공짜

Deno는 TS를 기본으로 지원합니다. 더이상 JS로 빌드하거나, ts-node를 사용하지 않아도 됩니다.

이제 tsconfig.json과도 안녕입니다. 원한다면 tsconfig.json를 명시적으로 설정할 수도 있긴 해요. 하지만, 우리가 평소에 건드리는 tsconfig.json 설정의 상당 수가 낡은 환경까지 배려해 ES5같은 타겟으로 컴파일 할때를 위한 것임을 고려했을때, 최신 런타임을 제공하는 Deno에서는 손 댈 일이 거의 없을겁니다.

Deno가 사용하는 TS 컴파일러인 swc는 한국인 개발자 강동윤 씨가 개발한 것으로 유명합니다.

ES Module

Deno에서의 import는 NodeJS와 다릅니다. package.json과 node_modules는 이제 필요없습니다. require 또한 사라졌고요, 모든 모듈은 ES Module입니다. 외부 패키지는 다음과 같이 URL로 import합니다.

import { colors, tty } from "https://deno.land/x/cliffy/ansi/mod.ts";

얼핏 특이하다고 생각할 수 있지만, ES Module 스펙을 구현한 브라우저에서도 가능한 방법입니다. Deno 설계의 많은 부분은 브라우저에서와 동일한 개발 경험을 제공하는 쪽으로 결정되었습니다.

Go도 이런 방식의 import를 지원합니다.

이제 app.ts 안에 외부 의존성까지 다 포함되있으므로, 파일 하나짜리 스크립트를 공유할때는 gist 주소면 충분합니다. 또 새로 패키지를 개발했을때 따로 npm과 같은 레지스트리에 배포할 필요도 없습니다. 어딘가에(Github 등) 코드를 올리면, 그게 곧 패키지입니다.

그런데 코드가 커지면 저 긴 URL이 여기저기 나타나지 않을까요? Deno 공식 문서는 deps.ts와 같은 파일을 별도로 만들어 그곳에서 re-export하기를 제안합니다.

export * as ramda from "https://x.nest.land/ramda@0.27.0/source/index.js";
export * as cliffy from "https://deno.land/x/cliffy/ansi/mod.ts";
/* ... */

deps.tspackage.json의 역할을 하게 되지요. 또한 브라우저에 있는 import maps 기능도 제공합니다.

프로덕션 환경 배포를 위해선 100% 재현되는 실행이 필요하기에, lock 파일 생성도 지원합니다.

정리하면, 간단한 스크립팅을 할때는 NodeJS에 비해 확실한 강점이 있습니다. 하지만 코드가 커지면 결국 같은 문제에 대한 비슷한 해결책을 선택하게 됩니다. 그렇다고 NodeJS보다 오히려 더 나빠지는 건 아니고(사실을 말하자면, import하는 패키지의 버저닝이 잘 안되어 있으면 그럴 가능성도 있습니다), 고만고만해진다고 보면 되겠습니다.

배터리 포함

얼마전에 휴대폰을 아이폰13으로 바꾸었는데요. 상자를 개봉하면서 뭔가 허전하다 느꼈는데, 알고보니 전원 어댑터가 빠져 있더라고요. 충전을 하고 싶으면 전원 어댑터를, 아니 설마 맥북을 따로 사라는 뜻일까요?

다른 멀쩡한 회사들은 이런 짓을 하지 않죠. 제품을 사면 웬만한 소품들은 함께 딸려옵니다. 이 당연한 미덕을 프로그래밍 언어 런타임에도 요구할 수 있겠죠. 파이썬의 모토이기도 한 배터리 포함Battery included 얘깁니다.

NodeJS는 배터리를 포함하고 있다고 보기 힘듭니다. 대부분을 생태계에 맡겨 놓았지요. 이는 여러 매력적인 서드파티 패키지들을 세상에 나오게 했지만, 개발자 개인에겐 선택의 고민과 설정의 번거로움을 안겨주었습니다.

그럼 Deno에선 어떻게 바뀌었을까요?

코어 API

Deno는 Deno 모듈로 코어 API를 제공합니다. 그리고 top-level await을 지원하기 때문에 다음과 같은 코드가 가능합니다.

const file = await Deno.open("hello.txt");
const buffer = new Uint8Array(5);
const bytesRead = await file.read(buffer);
console.log(`Read ${bytesRead} bytes`);

별도의 패키지 의존성 없이, 쉘 스크립트로 했던 일들은 다 할 수 있습니다. 이번엔 타입과 함께요.

표준 라이브러리

잘 설계된 코어 API는 개발자에게 큰 도움이 되지만, 현실의 문제를 풀때는 그것만으론 부족합니다. NodeJS에는 코어 API만 있고(그마저도 import가 필요하며) 표준 라이브러리라고 할만한 것은 없습니다. 가령 fs는 코어 API에 해당하는 기본적인 기능만 있기에, 그 이상을 원할땐 fs-extra를 찾게 됩니다.

Deno는 std라는 이름의 표준 라이브러리를 제공합니다. Deno 팀이 직접 개발하고 관리합니다. 목록에 dotenv나 semver와 같은 익숙한 이름이 보이죠. 물론 원한다면 std를 쓰지 않아도 됩니다. 그럼 이게 무슨 의미가 있냐고요?

표준 라이브러리를 관리한다는건, 언어의 사용자들이 공통적으로 마주칠 문제에 대한 모범답안을 궁리하는 팀이 운영되고 있다는 걸 의미합니다. GoRust와 같은 현대적인 프로그래밍 언어들은 언어 자체의 개선외에, 표준 라이브러리의 품질 관리에도 큰 힘을 쏟고 있습니다.

개발 도구

Deno의 CLI는 다음과 같은 명령어들을 제공합니다.

  • fmt
  • lint
  • test
  • bundle
  • compile

이름만 봐도 알겠지요? 여태 prettier, eslint, jest, webpack으로 했던 일입니다.

지루한 프로젝트 세팅은 이제 안녕입니다. 그냥 시작하세요.

총평

Deno는 과거 NodeJS가 택했을 수도 있는 과감한 선택지를 상당수 수용하고, NodeJS 이후에 등장한 런타임들에서 볼 수 있는 세련된 인터페이스까지 도입했습니다. 그 결과 DX의 측면에서는 NodeJS보다 한 차원 높은 수준을 기대할만 합니다. 제가 기대할 만하다고 한 것은, 이론상 그렇게 될 여지는 충분하지만 지금 당장 그렇다고 단언할 순 없기 때문입니다.

Deno에서는 NodeJS보다 더 간편하게 패키지를 import할 수 있습니다. 그 패키지가 있다면 말이죠. Deno는 npm의 패키지들을 사용하지 못합니다. NodeJS API에 의존하지 않는 상당수의 npm 패키지가 자명하게 Deno 패키지로 포팅될 수 있긴 합니다. 그렇지만 이 또한 공짜는 아니기 때문에, Deno 진영에 npm과 같은 규모의 패키지 저장소를 구축하는 데에는 상당한 시간이 걸릴 것입니다.

...이었는데, 곧 Deno에서 '대부분의' npm 패키지를 지원합니다! https://deno.com/blog/changes#compatibility-with-node-and-npm

장차 Deno가 NodeJS를 제치고 JS 런타임의 새로운 표준으로 자리잡을 수 있을까요? 얼마전에 Bun도 성능을 무기로 경쟁에 합세해 상황은 더욱 알수 없게 되었습니다.

지금부터 서로 죽여라

사용자인 우리가 Deno와 NodeJS의 미래를 걱정해줄 이유는 딱히 없습니다. 경쟁을 통해 서로의 장점을 흡수하고 발전할테니 우리로썬 좋은 일이지요. 다만, 그냥 지켜만 보는것보단 좀더 재밌을 방법을 생각해보았습니다.

  • Codemod

    Codemod는 코드를 변환하는 스크립트를 말합니다. 종종 우리는 공통 라이브러리를 버전업하고, 파트 전체에 변경 사항에 대응하라는 공문을 보내곤 하지요. 대부분은 큰 일이 아니고, 머리 식히면서 할만 하긴 해요. 진짜 문제는 파트 구성원들이 기존 코드에 전역적인 변화를 발생시키는 결정을 주저하게 만드는 데에 있습니다.

    deno run https://yourscript

    이제 대규모 코드 변경의 수고를 동료들 대신 부담할 수 있습니다. 저 yourscript를 작성함으로써요.

    실제로 codemod로 NodeJS 패키지를 Deno 패키지로 변환한 사례를 공유한 글을 소개합니다.

    (번역) Node.js 라이브러리를 Deno로 컨버팅한 방법

  • Github Actions

    Github Actions에서 Deno를 쓰는 것은 정말 쉽습니다. setup-deno 워크플로우가 있거든요.

    - uses: denoland/setup-deno@v1
      with:
        deno-version: v1.x

    이러면 준비 끝입니다.

    물론 스크립팅을 하지 않고 workflow의 조합만으로 원하는 바를 달성할 수 있다면 그게 최선입니다. 하지만 종종 그럴 수 없는 경우가 있고, 그때마다 썩 마음에 들지 않는 쉘 스크립트로 문제를 해결해왔지요. 그냥 돌아간다는 장점 하나만은 확실하니까요. 위의 짧은 설정을 추가하는 것만으로, JS/TS도 이제 그 장점을 같은 수준으로 갖추게 됩니다.

  • 번들러 설정

    webpack 설정이 포함된 프로젝트의 경우, package.json을 보면 의존성의 상당 수가 webpack 플러그인인 경우가 있습니다. 한 프로젝트에서 모든 종류의 외부 의존성을 package.json에 모아서 관리해야하기 때문에 생기는 일인데요.

    만약 webpack.config.js가 Deno 스크립트라면 어떨까요? 어플리케이션의 의존성과와 번들러 설정의 의존성이 깔끔하게 분리되고, 번들러 설정을 공유하고 실행하는 것은 훨씬 간편한 일이 될겁니다. 아쉽게도 아직 webpack을 Deno에서 사용할 순 없습니다.

    esbuild는 이미 Deno 패키지가 나와있으니, 조금 더 기다리며 쓸만한 물건이 나오길 기대해봅시다.

이상 Deno를 부담없이 도입할만한 곳들입니다. 지금 당장 누릴 수 있는 Deno의 우월한 DX에 초점을 맞추었습니다. Deno와 함께 머지 않은 미래를 살짝 훔쳐보면 어떨까요?

profile
어쩌다 쓴 글들이 아까운 사람
post-custom-banner

6개의 댓글

comment-user-thumbnail
2022년 8월 26일

아니 이렇게 좋은글에 ..? 댓글이하나도없다니 데노는 미래이군요 아직 기다려봐야하겠지만 좋은 행보를 걷고있는것같습니다

답글 달기
comment-user-thumbnail
2022년 8월 26일

확실히 희망찬 미래가 있다는 것만으로도 뭔가 편안해지네요 ㅋㅋㅋ 얼른 디노가 메이저해지길...

답글 달기
comment-user-thumbnail
2022년 8월 28일

npm을 사용 못한다고 알아서 deno는 아예 생각하지 않았었는데 이제 지원한다면 deno도 영향력이 커지겠네요!

node, deno, bun 이 경쟁 속에서 javascript진영이 더 성숙해질 것 같습니다!

답글 달기
comment-user-thumbnail
2022년 8월 31일

Deno의 특징 외에 NodeJS의 역사까지 설명되어 있어서 흥미롭게 읽었습니다! deno가 미래라는 말을 약 2년전부터ㅋㅋㅋ들은것 같은데 누가 차세대 런타임이 될지 기대되네용

답글 달기
comment-user-thumbnail
2022년 9월 2일

오 deno 말만 들었는데 한번 써보고 싶어요

답글 달기
comment-user-thumbnail
2022년 9월 26일

아주 좋은 글입니다. 잘봤습니다. deno 와 bun의 경쟁이 기대가 됩니다.

답글 달기