본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout
TechProjectsBooksAbout
© 2026 Kreath. All rights reserved.
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 2장: Deno 2 아키텍처와 핵심 설계 철학
2026년 7월 12일·프로그래밍·

2장: Deno 2 아키텍처와 핵심 설계 철학

Deno 2의 내부 아키텍처를 분석합니다. V8 엔진, Tokio 비동기 런타임, Rust 기반 구조, 단일 실행 파일 설계, 그리고 내장 도구 체인의 동작 원리를 살펴봅니다.

18분557자9개 섹션
typescriptperformancetoolingsecuritydeveloper-experience
공유
deno-runtime2 / 10
12345678910
이전1장: JavaScript 런타임 전쟁 - Node.js, Deno, Bun다음3장: Deno의 보안 모델 - 권한 기반 실행

Deno 2의 내부를 들여다보기

Deno 2를 제대로 이해하려면 그 내부 구조부터 파악해야 합니다. 표면적으로는 "더 나은 Node.js"처럼 보이지만, 아키텍처 수준에서 보면 완전히 다른 설계 철학 위에 세워진 런타임입니다. 이 장에서는 Deno 2를 구성하는 핵심 기술 요소들과 그 설계 결정의 배경을 깊이 있게 분석합니다.

핵심 아키텍처 개요

Deno 2의 아키텍처는 크게 네 가지 계층으로 구성됩니다.

가장 위에는 사용자가 작성한 TypeScript 또는 JavaScript 코드가 있습니다. 이 코드는 SWC를 통해 JavaScript로 변환된 후 V8 엔진에서 실행됩니다. V8에서 시스템 자원에 접근해야 할 때는 ops 레이어를 통해 Rust로 작성된 네이티브 코드가 호출되고, 비동기 작업은 Tokio가 처리합니다.

V8 엔진: JavaScript 실행의 심장

V8의 역할

Deno는 Node.js와 동일하게 Google의 V8 엔진을 사용합니다. V8은 JavaScript와 WebAssembly를 실행하는 오픈소스 엔진으로, Chrome 브라우저에서도 사용되는 검증된 기술입니다.

Info

V8 엔진은 JIT(Just-In-Time) 컴파일 방식을 사용합니다. 코드를 먼저 인터프리터(Ignition)로 빠르게 실행한 뒤, 자주 실행되는 핫 코드를 최적화 컴파일러(TurboFan)로 네이티브 머신 코드로 변환합니다. 이를 통해 실행 시작 속도와 장기 실행 성능을 모두 확보합니다.

Deno가 V8을 선택한 이유는 명확합니다. 성능이 검증되었고, ECMAScript 표준을 가장 빠르게 구현하며, 보안 샌드박스가 견고합니다. 특히 V8의 아이솔레이트(Isolate) 기능은 Deno의 보안 모델과 잘 맞습니다.

V8 아이솔레이트의 개념
typescript
// 각 Deno 프로세스는 독립된 V8 아이솔레이트에서 실행됩니다.
// 아이솔레이트 간에는 메모리를 공유하지 않으므로,
// 하나의 스크립트가 다른 스크립트의 메모리에 접근할 수 없습니다.
 
// Worker도 별도의 아이솔레이트에서 실행됩니다
const worker = new Worker(
  new URL("./worker.ts", import.meta.url).href,
  { type: "module" }
);
 
// 메인 스레드와 워커 간에는 메시지 패싱으로만 통신
worker.postMessage({ data: "안전한 통신" });

rusty_v8: Rust와 V8의 다리

Deno 팀은 V8을 Rust에서 사용하기 위해 rusty_v8이라는 바인딩 라이브러리를 직접 개발했습니다. 이 라이브러리는 V8의 C++ API를 Rust에서 안전하게 호출할 수 있게 해주며, Rust의 메모리 안전성 보장을 V8 통합 과정에서도 유지할 수 있게 합니다.

Tokio: 비동기 I/O의 엔진

왜 Tokio인가

Node.js가 libuv를 비동기 I/O 엔진으로 사용하는 것처럼, Deno는 Rust 생태계의 Tokio를 사용합니다. Tokio는 Rust의 대표적인 비동기 런타임으로, 네트워크 I/O, 파일 시스템 접근, 타이머 등 비동기 작업을 처리합니다.

Tokio의 강점은 Rust의 async/await 문법과 자연스럽게 결합된다는 점입니다. 이를 통해 수만 개의 동시 연결을 적은 메모리와 CPU 오버헤드로 처리할 수 있습니다.

이벤트 루프 비교

Node.js의 이벤트 루프(libuv)와 Deno의 이벤트 루프(Tokio)는 비슷한 목적을 가지지만 구현 방식이 다릅니다.

이벤트 루프 비교
text
Node.js (libuv)
  - C 언어로 구현
  - 페이즈(Phase) 기반 이벤트 루프
  - 타이머 → I/O 콜백 → idle → poll → check → close 순서
  - 스레드 풀(기본 4개)로 블로킹 작업 처리
 
Deno (Tokio)
  - Rust로 구현
  - 작업 스틸링(work-stealing) 스케줄러
  - 멀티스레드 비동기 실행기
  - epoll/kqueue/IOCP 기반 I/O 다중화
Tip

Tokio의 작업 스틸링 스케줄러는 유휴 스레드가 다른 스레드의 작업 큐에서 작업을 가져와 실행하는 방식입니다. 이를 통해 CPU 코어를 더 효율적으로 활용할 수 있습니다.

Rust: 안전성과 성능의 기반

Deno가 Rust로 작성된 이유

Deno의 코어는 Rust로 작성되었습니다. 이것은 단순한 기술적 유행이 아니라, 런타임에 필요한 핵심 속성인 메모리 안전성과 고성능을 동시에 확보하기 위한 전략적 선택입니다.

Rust의 소유권 시스템 (개념적 예시)
rust
// Rust의 소유권 시스템은 컴파일 타임에 메모리 버그를 방지합니다
fn process_data(data: Vec<u8>) -> Result<String, Error> {
    // data의 소유권이 이 함수로 이동
    // 함수가 끝나면 data는 자동으로 해제
    let result = parse(data)?;
    Ok(result)
}
// 여기서 data를 다시 사용하려고 하면 컴파일 에러 발생

C/C++에서 빈번하게 발생하는 버퍼 오버플로우, use-after-free, 데이터 레이스 같은 메모리 관련 버그가 Rust에서는 컴파일 타임에 차단됩니다. 런타임의 안정성에 직결되는 중요한 특성입니다.

ops 레이어: JavaScript와 Rust의 인터페이스

Deno에서 JavaScript 코드가 시스템 자원에 접근할 때는 반드시 ops(operations) 라는 계층을 거칩니다. 이 계층이 Deno의 보안 모델과 성능 최적화의 핵심입니다.

ops 레이어의 동작 개념
typescript
// JavaScript 측에서 파일을 읽으려고 할 때
const content = await Deno.readTextFile("./data.json");
 
// 내부적으로는 다음과 같은 흐름이 발생합니다:
// 1. Deno.readTextFile 호출
// 2. 권한 검사 (--allow-read 확인)
// 3. op_read_file (Rust 함수) 호출
// 4. Tokio를 통한 비동기 파일 읽기
// 5. 결과를 JavaScript로 반환

모든 시스템 호출이 ops 레이어를 거치기 때문에, 이 지점에서 권한 검사를 수행할 수 있습니다. 이것이 Deno의 보안 모델이 가능한 근본적인 이유입니다.

단일 실행 파일의 설계

올인원 바이너리

Deno는 단일 실행 파일(single binary)로 배포됩니다. Node.js에서는 런타임 설치 후 npm으로 TypeScript 컴파일러, ESLint, Prettier, Jest 등을 별도로 설치해야 했지만, Deno는 이 모든 것이 하나의 바이너리에 포함되어 있습니다.

Deno 단일 바이너리에 포함된 도구들
bash
deno fmt      # 코드 포매터 (dprint 기반)
deno lint     # 린터 (deno_lint)
deno test     # 테스트 러너
deno bench    # 벤치마크 러너
deno doc      # 문서 생성기
deno compile  # 단일 실행 파일 컴파일
deno check    # 타입 체크
deno task     # 태스크 러너
deno coverage # 코드 커버리지
Warning

단일 바이너리 접근 방식의 트레이드오프도 있습니다. 바이너리 크기가 상대적으로 크고(약 100MB+), 내장 도구의 설정 커스터마이징에 제한이 있을 수 있습니다. 그러나 개발 환경 설정의 복잡성을 극적으로 줄이는 이점이 이를 상쇄합니다.

TypeScript 처리 파이프라인

Deno의 TypeScript 지원은 "설정 제로"를 목표로 합니다. .ts 파일을 아무 설정 없이 바로 실행할 수 있습니다.

실행 시에는 SWC(Speedy Web Compiler)를 사용하여 TypeScript에서 타입 정보만 빠르게 제거하고 JavaScript로 변환합니다. 타입 체크는 별도로 deno check 명령으로 수행하며, 이때는 TypeScript 컴파일러(tsc)를 사용합니다. 이렇게 실행과 타입 체크를 분리함으로써 개발 시 빠른 피드백 루프를 유지합니다.

내장 도구 체인 상세

deno fmt: 코드 포매터

Deno의 포매터는 dprint를 기반으로 합니다. Prettier와 유사한 출력을 생성하지만, Rust로 구현되어 훨씬 빠릅니다.

deno.json 포매터 설정
typescript
{
  "fmt": {
    "useTabs": false,
    "lineWidth": 80,
    "indentWidth": 2,
    "semiColons": true,
    "singleQuote": false,
    "proseWrap": "preserve",
    "include": ["src/"],
    "exclude": ["src/vendor/"]
  }
}

deno lint: 정적 분석

deno_lint는 ESLint에 대응하는 내장 린터입니다. Rust로 작성되어 ESLint 대비 수십 배 빠른 속도를 보여줍니다.

린트 실행 예시
bash
# 현재 디렉토리의 모든 파일 린트
deno lint
 
# 특정 규칙 비활성화 (파일 단위)
// deno-lint-ignore no-explicit-any
const data: any = fetchData();
 
# 특정 규칙만 활성화
deno lint --rules-include=no-unused-vars,no-explicit-any

deno test: 테스트 러너

별도의 테스트 프레임워크 설치 없이 바로 테스트를 작성하고 실행할 수 있습니다.

Deno 내장 테스트 예시
typescript
import { assertEquals, assertThrows } from "jsr:@std/assert";
 
Deno.test("기본 덧셈 테스트", () => {
  assertEquals(1 + 1, 2);
});
 
Deno.test("비동기 작업 테스트", async () => {
  const response = await fetch("https://api.example.com/health");
  assertEquals(response.status, 200);
});
 
Deno.test({
  name: "권한이 필요한 테스트",
  permissions: { read: true },
  fn() {
    const content = Deno.readTextFileSync("./test-data.txt");
    assertEquals(content.includes("expected"), true);
  },
});
Tip

deno test는 테스트별로 권한을 개별 설정할 수 있습니다. 이를 통해 테스트가 필요 이상의 시스템 자원에 접근하지 않도록 보장할 수 있으며, 이는 다른 런타임에서는 찾아볼 수 없는 고유한 기능입니다.

deno bench: 벤치마크 러너

성능 측정을 위한 벤치마크도 내장되어 있습니다.

벤치마크 예시
typescript
Deno.bench("문자열 결합 - 더하기 연산자", () => {
  let result = "";
  for (let i = 0; i < 1000; i++) {
    result += "a";
  }
});
 
Deno.bench("문자열 결합 - Array.join", () => {
  const parts: string[] = [];
  for (let i = 0; i < 1000; i++) {
    parts.push("a");
  }
  parts.join("");
});

deno.json: 통합 설정 파일

Node.js 프로젝트에서는 package.json, tsconfig.json, .eslintrc, .prettierrc 등 다수의 설정 파일이 필요합니다. Deno는 deno.json 하나로 이를 통합합니다.

deno.json 전체 구조
json
{
  "compilerOptions": {
    "strict": true,
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  },
  "imports": {
    "@std/": "jsr:@std/",
    "@/": "./src/"
  },
  "tasks": {
    "dev": "deno run --watch --allow-net --allow-read src/main.ts",
    "test": "deno test --allow-read",
    "build": "deno compile --output=app src/main.ts"
  },
  "fmt": {
    "lineWidth": 100,
    "semiColons": true
  },
  "lint": {
    "rules": {
      "exclude": ["no-unused-vars"]
    }
  },
  "nodeModulesDir": "auto"
}
Info

deno.json의 imports 필드는 Import Map 역할을 합니다. 별도의 importmap.json 파일 없이 모듈 경로를 매핑할 수 있으며, 이를 통해 패키지 버전을 중앙에서 관리할 수 있습니다.

설계 철학의 핵심

Deno 2의 아키텍처를 관통하는 설계 철학을 정리하면 다음과 같습니다.

첫째, 보안은 기본값입니다. 모든 시스템 접근은 명시적 허가가 필요하며, 이는 아키텍처 수준에서 강제됩니다.

둘째, 웹 표준을 존중합니다. 가능한 한 브라우저와 동일한 API를 사용하여, 코드의 이식성을 높입니다.

셋째, 도구 체인을 내장합니다. 개발에 필요한 모든 도구를 하나의 바이너리에 포함하여, 설정 복잡성을 제거합니다.

넷째, 점진적 호환성을 추구합니다. Deno 2에서 npm 호환을 도입한 것은 이상과 현실 사이의 균형을 찾은 결과입니다.

다음 장에서는 이 아키텍처 위에 구축된 Deno의 보안 모델을 상세히 살펴봅니다. 권한 기반 실행이 실제로 어떻게 동작하고, 어떤 보안 위협을 방어할 수 있는지를 분석합니다.

이 글이 도움이 되셨나요?

관련 글

프로그래밍

1장: JavaScript 런타임 전쟁 - Node.js, Deno, Bun

JavaScript 런타임의 역사와 Node.js의 한계, Deno와 Bun의 등장 배경, WinterCG 표준화까지 현대 런타임 생태계를 조망합니다.

2026년 7월 9일·16분
프로그래밍

3장: Deno의 보안 모델 - 권한 기반 실행

Deno의 권한 기반 보안 모델을 심층 분석합니다. 각 권한 플래그의 동작 원리, Node.js와의 보안 비교, 공급망 공격 방어, 그리고 실무 보안 모범 사례를 다룹니다.

2026년 7월 16일·20분
프로그래밍

4장: npm 호환과 패키지 관리

Deno 2의 npm 호환성, JSR(JavaScript Registry), import map, deno add를 통한 패키지 관리, 그리고 Node.js에서 Deno로의 마이그레이션 전략을 다룹니다.

2026년 7월 18일·16분
이전 글1장: JavaScript 런타임 전쟁 - Node.js, Deno, Bun
다음 글3장: Deno의 보안 모델 - 권한 기반 실행

댓글

목차

약 18분 남음
  • Deno 2의 내부를 들여다보기
  • 핵심 아키텍처 개요
  • V8 엔진: JavaScript 실행의 심장
    • V8의 역할
    • rusty_v8: Rust와 V8의 다리
  • Tokio: 비동기 I/O의 엔진
    • 왜 Tokio인가
    • 이벤트 루프 비교
  • Rust: 안전성과 성능의 기반
    • Deno가 Rust로 작성된 이유
    • ops 레이어: JavaScript와 Rust의 인터페이스
  • 단일 실행 파일의 설계
    • 올인원 바이너리
    • TypeScript 처리 파이프라인
  • 내장 도구 체인 상세
    • deno fmt: 코드 포매터
    • deno lint: 정적 분석
    • deno test: 테스트 러너
    • deno bench: 벤치마크 러너
  • deno.json: 통합 설정 파일
  • 설계 철학의 핵심