LabHub

블로그

Deno 2·3 심층 분석 2026 — Node 호환의 시대, JSR·Fresh 2·Deno KV까지 (런타임 베팅이 어디로 갔나)

한국어English日本語

프롤로그 — Ryan Dahl의 두 번째 자기 부정

Ryan Dahl은 2009년 Node를 만들었고, 2018년 JSConf EU에서 "Node에서 후회하는 10가지"라는 유명한 발표를 했다. 그리고 Deno를 만들었다. 보안은 기본 deny, TS는 일급, 의존성은 URL import, package.json은 없다 — 모든 것이 Node에 대한 명시적인 답이었다.

그런데 2024년 9월, Deno 2가 나왔다. 가장 큰 변화는 자기 부정이다.

Deno가 처음에 거부했던 거의 모든 Node의 디자인 결정을, 6년이 지나서 받아들였다. 자존심을 죽이고.

이건 후퇴인가, 진화인가. 둘 다다. 이상주의에서 실용주의로의 전환이고, 동시에 JS 생태계의 현실(npm은 사라지지 않는다)을 인정한 베팅이다.

이 글은 Deno 2 출시 후 약 20개월이 지난 2026년 5월 시점에서, Deno의 베팅이 어디까지 왔는지, Bun과 Node 22+와의 삼파전이 어떻게 정리됐는지, 그리고 보안 모델·JSR·Fresh 2·Deno KV가 현실에서 어떤 자리를 차지했는지를 정리한다.


1장 · Deno 2의 핵심 — Node 호환이 기본이 되다

Deno 1의 세계관, 그리고 그 한계

Deno 1은 순수한 세계를 그리려 했다.

문제는 단순했다. JS 세계의 90%가 이미 npm 위에 있었다. Express, React, Prisma, AWS SDK, Sentry, Stripe SDK — 다 npm. Deno는 처음에 호환 레이어로 npm: specifier를 추가했지만, 진짜 production에 가져가려면 부족했다. package.json에 적힌 의존성을 그대로 못 읽고, node_modules 풀스택 도구가 다 깨지고, monorepo 워크플로우가 어색했다.

Deno 2의 답은 명확했다. "Node가 했던 방식 그대로도 돈다."

무엇이 바뀌었나 (2024년 9월)

# Deno 2 — 기존 Node 프로젝트에서
deno install            # package.json 읽어서 node_modules 생성
deno run npm:express    # npm 패키지 직접 실행
deno task dev           # package.json의 scripts.dev 실행
영역Deno 1Deno 2
package.json무시 / 부분 지원1급 — deno install이 읽음
node_modules의도적으로 없음옵션 — nodeModulesDir: auto
npm 패키지npm: specifier동일 + package.json에서 자동
deno installURL 기반 binary installNode 호환 패키지 매니저
deno run npm:foo부분 지원안정적, 광범위 호환
workspace없음1급 — deno.jsonworkspace 필드

Deno 2의 모토는 더 이상 "Node를 대체한다"가 아니다. "Node와 Deno 사이의 마찰을 0에 가깝게 만든다"다.

마이그레이션이 쉬워졌나

실제로 기존 Node 프로젝트를 가져다 Deno로 도는지 시도해봤다.

처음 1년 마찰이 큰 영역은 native addon(sharp, canvas, bcrypt-native 같은 C++ 바인딩)이었고, 2025년 동안 N-API 호환이 점진적으로 좋아졌다. 2026년 mid 시점에서 일반 npm 패키지의 90% 이상은 별 문제 없이 Deno 2에서 돈다.


2장 · deno install — npm 호환 패키지 매니저

두 모드

deno install은 두 가지 모드로 동작한다.

# 글로벌 binary 설치 (Deno 1부터)
deno install -gA --name lint jsr:@std/cli/parser

# 프로젝트 의존성 설치 (Deno 2 신규)
deno install            # package.json 읽기, node_modules 생성
deno install --add npm:express@4   # 새 의존성 추가
deno install --add jsr:@hono/hono  # JSR 의존성 추가

두 번째 모드가 패러다임 전환이다. npm install, pnpm install, yarn install을 그대로 대체할 수 있는 명령이 된 것이다.

속도와 lockfile

hot cache, 500개 deps:
  npm install:    14s
  pnpm install:    4s
  bun install:     1.3s
  deno install:    2.1s

bun install만큼은 아니지만, pnpm install보다 빠르고, npm install보다는 한참 빠르다. lockfile은 deno.lock (텍스트 JSON)이고, 처음부터 diff-friendly로 설계됐다. Bun이 binary lockfile로 한 번 헛발 디딘 것과 대조적이다.

node_modules 모드

// deno.json
{
  "nodeModulesDir": "auto",   // 또는 "manual" 또는 false
  "lock": true
}

auto로 두면 package.json을 읽고 자동으로 node_modules를 만든다. manual은 npm/pnpm/yarn으로 깔린 기존 node_modules를 그대로 쓴다 (큰 monorepo 이전 시나리오). false이면 Deno 1 스타일의 글로벌 캐시만 사용.

이 옵션이 있다는 것 자체가 이상주의를 버리고 실용주의를 택한 증거다.


3장 · deno run으로 npm 패키지 직접 실행

npm: specifier의 진화

Deno 2의 모토 중 하나는 "npm 패키지를 추가 설정 없이 import한다"이다.

// app.ts
import express from "npm:express@4";
import { z } from "npm:zod";
import OpenAI from "npm:openai";

const app = express();
app.get("/", (_, res) => res.json({ ok: true }));
app.listen(3000);
deno run --allow-net --allow-read --allow-env app.ts

이게 그냥 돈다. package.json도 필요 없고, node_modules도 필요 없다 (글로벌 캐시에 가져옴). Bun과 Node가 package.json을 필수로 요구하는 것과 비교해, 단일 파일 스크립트에서 Deno가 가장 매끄럽다.

Bun과 Node와의 비교

# 단일 파일 npm 의존성 스크립트

# Deno
deno run --allow-net script.ts

# Bun
bun script.ts            # bun add 했어야 함, 또는 inline shebang trick

# Node
npm init -y && npm i express && node script.js

Quick script + npm 한두 개의 영역에서는 Deno가 dev 경험 우세. 이건 Deno 1부터 이어진 강점이 Deno 2에서도 살아남은 영역이다.


4장 · JSR — JavaScript Registry의 베팅

왜 또 다른 레지스트리

JSR(jsr.io)은 Deno 팀이 2024년 초 출시한 새 JavaScript 레지스트리다. npm이 이미 있는데 왜 또?

JSR의 베팅 명제는 이렇다.

  1. TS-native publishing — TS 소스를 그대로 publish. 받는 쪽이 빌드 도구를 골라 쓴다. (npm은 보통 컴파일된 JS+.d.ts 묶음.)
  2. 표준 ESM only — CJS 없음, package.json의 미로 없음.
  3. runtime-agnostic — Deno뿐 아니라 Node, Bun, browser에서 import 가능.
  4. 품질 점수score로 문서·테스트·타입의 완성도 표시.
  5. scope 1급 시민@scope/package 모델이 기본.

사용 예

// 그냥 import
import { parse } from "jsr:@std/cli/parser";
import { Hono } from "jsr:@hono/hono";
# package.json 스타일
deno install --add jsr:@hono/hono
# Node에서도 사용 가능
npx jsr add @hono/hono
# 또는 직접 — 빌드된 형태로 받음

채택 현실 — 2026 mid

카테고리JSR 채택
Deno 1st-party @std 라이브러리100% — @std의 새 home
Deno 생태계 도구광범위 — Hono, Fresh, Oak 등
일반 JS 라이브러리천천히 — npm도 같이 publish하는 경우 多
사용자 수npm의 작은 일부 — 단 빠르게 성장

JSR은 "npm을 죽인다"가 아니라 "TS-first의 다른 옵션을 만든다"이다. 그리고 일정 부분 성공했다 — Deno 생태계 안에서는 standard, 그 밖에서는 niche.

JSR이 가져온 변화

npm 쪽의 반응

흥미로운 디테일 하나. Deno는 2024년 npm CLI의 BSD 라이선스 fork를 만들었다 — 이름은 npm이 아니라 pkg 비슷한 다른 이름으로 (커뮤니티 가이드라인 준수 차원). 이게 작은 라이선스/네이밍 논쟁을 일으켰지만, npm Inc. 쪽에서 큰 법적 대응은 없었다. 결국 메시지는 "npm 호환이 우선이지, 적대가 우선이 아니다".


5장 · Fresh 2 — 아일랜드 아키텍처의 두 번째 라운드

Fresh 1의 약속

Fresh는 Deno의 공식 풀스택 프레임워크였다. 핵심 컨셉:

훌륭한 베팅이었지만, 1.x 시기에는 routing 모델의 한계 (file-based + handler 분리)와 React 생태계 호환의 부재가 도입을 제한했다.

Fresh 2가 바꾼 것

// routes/api/hello.ts (Fresh 2 스타일)
import { define } from "$fresh/server.ts";

export const handler = define.handlers({
  async GET(ctx) {
    return new Response(JSON.stringify({ msg: "hello" }), {
      headers: { "content-type": "application/json" },
    });
  },
});
// routes/index.tsx
import { define } from "$fresh/server.ts";
import Counter from "../islands/Counter.tsx";

export default define.page<{ now: string }>((props) => (
  <main>
    <h1>현재 시각: {props.data.now}</h1>
    <Counter start={0} />
  </main>
));

Fresh 2의 주요 변경:

Fresh가 채택을 못 따라잡은 이유 (솔직히)

Fresh 2는 Deno Deploy에 묶인 풀스택이라면 매우 좋은 선택. 그러나 풀스택의 default가 될 길은 좁다.


6장 · Deno KV — 내장 KV가 production-ready로

Deno KV의 정체

Deno KV는 Deno 런타임 안에 내장된 key-value 스토어다. SQLite와 비슷한 단일 노드 모드, 그리고 Deno Deploy 환경에서는 글로벌 분산 모드.

// kv.ts
const kv = await Deno.openKv();

// set
await kv.set(["users", "alice"], { name: "Alice", joined: Date.now() });

// get
const result = await kv.get<{ name: string }>(["users", "alice"]);
console.log(result.value?.name);  // "Alice"

// list (prefix query)
for await (const entry of kv.list({ prefix: ["users"] })) {
  console.log(entry.key, entry.value);
}

// atomic transaction
await kv.atomic()
  .check({ key: ["counter"], versionstamp: null })
  .set(["counter"], 1)
  .commit();

2026 시점의 상태

측면상태
Local (SQLite-backed)GA (production-ready)
Deno Deploy (FoundationDB-backed, global)GA
Cross-region replication자동
Atomic transactions지원
Queue API지원 (kv.enqueue)
Watch API지원 — 실시간 구독
Limitper-key 64KB value, 일부 결제 플랜 제한

Deno KV는 Cloudflare KV와 Workers KV의 자리를 노린 것이지만, Deno 생태계 안에 묶여 있는 만큼 포지셔닝이 더 빡빡하다. 그러나 "1줄로 시작 가능한 분산 KV"라는 슬로건은 진짜 작동한다.

어디서 잘 쓰이나

어디가 약한가


7장 · workspace — Deno의 monorepo 답

모노레포가 1급 시민이 된다

Deno 2에서 deno.jsonworkspace 필드가 추가됐다.

// 루트 deno.json
{
  "workspace": [
    "./packages/api",
    "./packages/web",
    "./packages/shared"
  ],
  "tasks": {
    "dev": "deno task --filter=* dev"
  }
}
// packages/api/deno.json
{
  "name": "@app/api",
  "version": "0.1.0",
  "exports": "./mod.ts",
  "tasks": {
    "dev": "deno run --watch --allow-net mod.ts"
  }
}

내부 패키지를 import { foo } from "@app/shared"로 import 가능. pnpm workspace나 Bun workspace와 거의 같은 모델인데, Deno의 lockfile/캐시 일원화 덕에 설치 시간이 매우 짧다.

Turborepo·Nx와의 비교

도구장점단점
Deno workspace무설정, lockfile 통합, 빠른 installtask orchestration이 단순
pnpm workspace광범위 호환, 모두 익숙install이 Deno보다 느림
Turborepo빌드 캐싱, 강력한 그래프설정 무게
Nx강력한 generator, 큰 모노레포 표준학습 곡선 큼

Deno workspace는 작은~중간 모노레포에서 매끄럽다. 빌드 그래프 캐싱이 필수가 되는 큰 모노레포는 Turborepo/Nx를 그대로 쓰는 게 안전.


8장 · 보안 모델 — 여전히 킬러 피처

Deno의 처음부터 끝까지 살아남은 차별점이 이거다. 기본 deny.

permission flag 한눈에

deno run script.ts                    # 거의 아무것도 못 함
deno run --allow-read script.ts       # 파일 읽기
deno run --allow-net script.ts        # 네트워크
deno run --allow-env script.ts        # 환경변수
deno run --allow-write=./data script.ts   # 특정 디렉토리만 쓰기

# Deno 2부터: 더 세밀한 지정
deno run --allow-net=api.example.com,db.internal:5432 script.ts
deno run --allow-env=DATABASE_URL,REDIS_URL script.ts

운영에서의 실제 의미

좋은 점:

마찰:

Deno 2의 보안 개선

보안 모델은 소프트웨어 공급망 공격이 늘어나는 2025~2026 시기에 가치가 커진 영역이다. xz utils 사건, npm event-stream 사건 이후, "기본 deny"의 가치를 진지하게 보는 회사가 늘었다.


9장 · Deno 3는 어디에 있나

2026 mid 시점의 상태

Deno 3는 아직 공식 출시 전이다 (2026년 5월 기준). 로드맵 글과 RFC 토론에서 다음 방향이 보인다.

진짜 큰 베팅이 나올지, 아니면 점진적인 업그레이드로 갈지는 아직 정해지지 않았다. Deno 2가 큰 자기 부정이었던 만큼, Deno 3는 보수적인 evolution일 가능성이 더 크다.


10장 · Deno vs Bun vs Node 22+ — 삼파전의 정리

2026 mid 시점의 자리

Node 22+BunDeno 2
채택률1위 (압도적)2위 (개발 도구 중심)3위 (특정 영역)
시작 시간35~60ms10~20ms25~40ms
Node 호환100% (본인)90~95%90~95% (Deno 2 이후)
TS native--experimental-strip-types무설정무설정
보안 모델표준 (모두 허용)표준기본 deny
표준 라이브러리큰 코어작음@std 풍부
패키지 매니저npm/pnpm/yarnbun installdeno install
레지스트리npm onlynpm onlynpm + JSR
풀스택 프레임워크Next/Astro/Remix(그대로 Node 위의 것)Fresh 2
내장 KV없음bun:sqliteDeno KV
Edge 배포Vercel/NetlifyCloudflare Workers는 별도 isolateDeno Deploy
AI 에이전트 sandbox가능 (수동)가능매우 적합 (권한 모델)

의사결정 가이드

Deno 2가 맞는 곳:

  1. AI 에이전트 sandbox — 모델이 생성한 코드를 권한 제한해 실행. 이 영역에서 Deno는 분명한 1위.
  2. 고보안 / 정부 / 금융의 일부 워크로드 — permission 모델이 audit 추적에 의미 있음.
  3. 새 프로젝트의 빠른 prototype — 단일 파일 스크립트 + npm 의존성을 가장 매끄럽게.
  4. Deno Deploy 위의 작은 풀스택 — Fresh 2 + Deno KV.
  5. @std 중심의 backend 도구 — 표준 라이브러리의 안정성이 매력적.

Bun이 맞는 곳:

  1. CI 속도 (bun install, bun test).
  2. CLI 도구 (bun build --compile).
  3. Edge cold start.
  4. SQLite-heavy 작은 서비스.

Node 22+가 여전히 맞는 곳:

  1. 대부분의 enterprise production.
  2. APM/observability가 중요한 환경.
  3. 큰 monorepo with native addons.
  4. 회사 표준 런타임 — 사람을 뽑기 쉬움.

세 런타임은 zero-sum이 아니다. 한 회사 안에서 Node (메인 서비스), Bun (CLI/CI), Deno (AI 에이전트 sandbox, 고보안 부분)을 섞는 패턴이 점점 흔하다.


11장 · npm 호환의 broken / over-delivered 영역

Over-delivered

Broken / 아직 약한


12장 · 누가 production에서 Deno 2를 쓰나

케이스 A — AI 에이전트 sandbox

가장 분명한 win 영역. 모델이 생성한 코드를 실행할 때 권한을 좁히는 것이 중요한 환경:

케이스 B — Deno Deploy 풀스택

Fresh 2 + Deno KV로 만든 작은 풀스택:

케이스 C — 표준 라이브러리 헤비한 backend 도구

@std/cli, @std/path, @std/fs, @std/encoding을 적극 쓰는 CLI 도구나 dev tool. 외부 의존성 최소화가 가치 있는 곳.

케이스 D — Slack/Discord bot, webhook handler

Deno.serve + Deno.openKv()로 작은 webhook 서비스. 권한 모델로 안전하게 좁힘, 글로벌 배포는 Deno Deploy로.

케이스 E — 거의 없음 — 대규모 enterprise 모놀리스

Bun과 같은 이유. APM, debugger, 사람의 익숙함, 기존 코드베이스의 무게.

명시적 사례 (공개 글 기준)


13장 · 마이그레이션 — Node 프로젝트를 Deno 2로 옮기는 단계

단계적 도입

  1. deno fmt, deno lint만 도입 — 다른 도구 변경 없음. 무리 없는 첫 step.
  2. CI에서 deno test — Vitest/Jest와 병행. Deno test의 속도와 무설정 매력 확인.
  3. 단일 script를 Deno로scripts/migrate.ts 같은 일회성 스크립트부터.
  4. 새 작은 서비스를 Deno로 — 신규 마이크로서비스 / 내부 도구.
  5. deno install로 dep 관리 — Node 런타임 유지하면서 Deno의 install만 사용.
  6. production 런타임을 Deno로 — 가장 큰 step, 마지막.

도입 체크리스트

흔한 anti-pattern


에필로그 — 이상주의에서 실용주의로

Deno 1은 Node에 대한 명시적 반박으로 태어났다. Deno 2는 그 반박을 절반 거두고 실용주의를 택했다. 그 결과:

Deno는 "Node를 대체한다"는 narrative를 포기했고, 대신 "Node의 옆에서 의미 있는 자리를 만든다"는 narrative로 갔다. 그 자리가 AI 에이전트 sandbox, Deno Deploy, 표준 라이브러리 중심 도구다.

기억할 점:

채택 체크리스트 (Deno를 도입하기 전)

흔한 anti-pattern (재강조)

다음 글 예고


참고 / References

Deno 공식 자료

Deno 2 출시 자료

JSR

Fresh

Deno KV

Deno Deploy

Node.js (비교)

Bun (비교)

보안 / 권한 모델

운영 채택 사례 (공개 글 기준)

비판적 시각 / 회고

표준화 / WinterCG


댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다