Deno 보안 모델과 표준 라이브러리

Deno의 기본 차단 보안 모델, 권한 플래그 시스템, JSR 기반 @std 표준 라이브러리 활용법을 완전히 정리합니다. --allow-* 플래그 조합, Deno.permissions API, deno.json 설정, 주요 @std 모듈까지 다룹니다.

· 7 min read · PALDYN Team

지난 글에서 Node.js의 node_modules 호이스팅과 패키지 해석 알고리즘을 살펴봤다. 이번에는 Node.js와 전혀 다른 철학으로 설계된 Deno를 다룬다. Deno는 Ryan Dahl이 Node.js의 설계 실수를 반성하며 2018년에 발표한 런타임으로, V8 위에 Rust로 구현되어 있다. 가장 큰 특징은 기본 차단(deny-by-default) 보안 모델로, 모든 시스템 리소스 접근이 명시적 권한 없이는 차단된다.

Deno가 해결하려 한 문제

Node.js는 탄생 당시 보안보다 성능과 사용 편의성을 우선시했다. require()로 불러온 모듈은 파일 시스템, 네트워크, 환경 변수에 무제한 접근할 수 있었고, node_modules의 공급망 공격에 취약했다. Deno는 이 문제에 두 가지 답을 제시한다.

  1. 기본 차단 보안 모델: 파일, 네트워크, 환경 변수, 서브프로세스 실행 모두 기본 차단 후 플래그로 허용
  2. 중앙 registry 의존 탈피: npm 대신 URL 직접 임포트 또는 JSR(jsr:) 사용

권한 플래그 시스템

Deno 프로그램을 실행할 때 필요한 권한을 --allow-* 플래그로 명시한다. 권한 없이 접근을 시도하면 Deno.errors.PermissionDenied가 즉시 던져진다.

Deno 권한 모델 — 기본 차단 원칙

# 모든 권한 거부 (기본 상태)
deno run script.ts

# 네트워크만 허용
deno run --allow-net script.ts

# 특정 호스트만 허용 (세밀한 제어)
deno run --allow-net=api.example.com script.ts

# 읽기·쓰기 경로 한정
deno run --allow-read=/data --allow-write=/tmp script.ts

# 모든 권한 허용 (지양)
deno run --allow-all script.ts

주요 플래그를 정리하면 다음과 같다.

플래그제어 범위세밀 지정 예
--allow-read파일 읽기--allow-read=/etc
--allow-write파일 쓰기--allow-write=/tmp
--allow-net네트워크 요청--allow-net=api.com:443
--allow-env환경 변수--allow-env=PORT,HOST
--allow-run서브프로세스--allow-run=git
--allow-sys시스템 정보--allow-sys=osRelease

Deno.permissions API — 런타임 권한 조회

--allow-* 플래그가 정적 선언이라면, Deno.permissions API는 런타임에 권한 상태를 조회하거나 사용자에게 대화형으로 요청할 수 있다.

// 현재 네트워크 권한 조회
const netStatus = await Deno.permissions.query({
  name: "net",
  host: "api.example.com",
});

console.log(netStatus.state); // "granted" | "denied" | "prompt"

if (netStatus.state === "granted") {
  const res = await fetch("https://api.example.com/data");
  console.log(await res.json());
} else {
  // 대화형 요청 (터미널 prompt)
  const req = await Deno.permissions.request({
    name: "net",
    host: "api.example.com",
  });
  if (req.state === "granted") {
    /* ... */
  }
}

state"prompt"인 경우, 사용자가 터미널에서 y/n으로 응답할 때까지 대기한다. 이 흐름 덕분에 CLI 도구가 필요한 권한을 명확히 안내하면서도 최소 권한 원칙을 유지할 수 있다.

deno.json — 프로젝트 설정과 권한 선언

deno.json은 Node.js의 package.json에 해당한다. 태스크(task) 정의, 임포트 맵, 컴파일러 옵션을 한 곳에서 관리한다.

{
  "tasks": {
    "start": "deno run --allow-net --allow-read src/main.ts",
    "test":  "deno test --allow-read tests/"
  },
  "imports": {
    "@std/path": "jsr:@std/path@^1.0.0",
    "@std/fs":   "jsr:@std/fs@^1.0.0"
  },
  "compilerOptions": {
    "strict": true
  }
}

imports 필드가 임포트 맵 역할을 하여, 코드에서 "@std/path"처럼 짧게 참조할 수 있다.

JSR @std 표준 라이브러리

Deno는 별도 npm install 없이 JSR(JavaScript Registry)의 @std 네임스페이스를 통해 표준 라이브러리를 제공한다. 모든 모듈은 TypeScript 타입 내장, 테스트 커버리지 100%, 버전 SemVer 관리가 보장된다.

JSR @std 표준 라이브러리

주요 @std 모듈은 다음과 같다.

// @std/path — 경로 처리
import { join, dirname, extname } from "jsr:@std/path";

// @std/fs — 파일 시스템 고수준 유틸
import { ensureDir, copy, walk } from "jsr:@std/fs";

// @std/encoding — Base64, Hex, CSV
import { encodeBase64, decodeBase64 } from "jsr:@std/encoding/base64";

// @std/http — 서버 유틸리티
import { serveDir } from "jsr:@std/http/file-server";

// @std/assert — 테스트 단언
import { assertEquals, assertThrows } from "jsr:@std/assert";

Node.js의 path.join이나 fs.mkdir({recursive: true})처럼 매번 options 객체를 넘겨야 했던 패턴이, join()·ensureDir() 같은 직관적인 API로 교체된다.

Deno의 내장 도구체인

Deno는 런타임 외에도 개발에 필요한 도구를 내장한다.

deno fmt          # 코드 포매터 (Prettier 대체)
deno lint         # 린터 (ESLint 대체)
deno test         # 테스트 러너
deno compile      # 단일 실행 파일 생성
deno bundle       # (deprecated → @std/build)
deno doc mod.ts   # JSDoc 문서 생성

별도 설정 없이 deno fmt만 실행하면 프로젝트 전체를 일관된 스타일로 포매팅한다. CI에서 deno fmt --check로 포매팅 검사를 통합하는 것도 자연스럽다.

Node.js 호환성 레이어

Deno v1.15부터 node: 접두사로 Node.js 빌트인 모듈을 임포트할 수 있고, npm 패키지도 npm: 접두사로 직접 사용할 수 있다.

// Node.js 빌트인 호환
import { createHash } from "node:crypto";
import { EventEmitter } from "node:events";

// npm 패키지 직접 사용
import express from "npm:express@4";
import chalk from "npm:chalk@5";

이 호환성 레이어 덕분에 기존 Node.js 코드베이스를 Deno로 점진적으로 이전하거나, npm 생태계 자산을 그대로 활용할 수 있다.

실전 Deno 서버 예시

// main.ts — 권한: --allow-net --allow-read
import { Hono } from "npm:hono";
import { serveDir } from "jsr:@std/http/file-server";

const app = new Hono();

app.get("/static/*", (c) =>
  serveDir(c.req.raw, { fsRoot: "./public", urlRoot: "/static" })
);

app.get("/api/hello", (c) =>
  c.json({ message: "Hello from Deno!" })
);

Deno.serve({ port: 8000 }, app.fetch);

Deno.serve는 HTTP 서버를 여는 빌트인 API다. Hono 같은 경량 프레임워크와 조합하면 별도 패키지 없이 프로덕션급 서버를 구성할 수 있다.


지난 글: node_modules 호이스팅과 의존성 해석

다음 글: Bun · JSC 기반 런타임과 내장 번들러


읽어주셔서 감사합니다. 😊