왜 나는 JavaScript, Python, 그리고 NPM을 Rust로 바꿨는가 (그리고 당신도 그래야 합니다)

발행: (2025년 12월 4일 오후 12:02 GMT+9)
10 min read
원문: Dev.to

Source: Dev.to

한계점

새벽 3시였습니다. 화면을 바라보며 npm install이 47 000번째 의존성을 천천히 내려가는 모습을 지켜보고 있었습니다. 내 React 앱은 단 하나의 간단한 날짜 선택기만 필요했습니다. 하나. node_modules 폴더는? 1.2 GB. 내가 실제로 작성한 코드는? 200줄.

그때 나는 결심했습니다: 이제는 충분합니다.

모두가 정상이라고 가장하는 JavaScript 악몽

우리가 정상화한 것들을 솔직히 말해봅시다:

  • “그냥 npm audit fix 해” — 번역: 의존성을 러시안 룰렛처럼 돌리고 뭔가 깨지지 않기를 기도한다.
  • “내 컴퓨터에서는 동작해” — 2025년 현재도 빌드를 신뢰성 있게 재현하지 못한다는 뜻.
  • “여기에 @ts-ignore 추가” — TypeScript가 “포기한다, 네가 알아서 해”라고 말하는 방식.
  • node_modules가 800 MB에 불과해” — 마치 할 일 목록 앱에 적당한 수준인 양 진지하게 말한다.

우리는 이런 상황이 괜찮다고 스스로에게 가스라이팅했습니다. 괜찮지 않습니다. 미친 짓입니다.

Python: 좌절 역으로 가는 느린 열차

Python 개발자들은 “단순함”을 자랑합니다. Python의 단순함이란 다음과 같습니다:

def calculate_total(items: List[Item]) -> float:
    return sum(item.price for item in items)

아름답죠? 이제 보세요:

calculate_total("not a list")          # Runtime error! 💥
calculate_total(None)                  # Runtime error! 💥
calculate_total([None, "lol"])         # Runtime error! 💥

그 타입 힌트는? 순수 장식일 뿐입니다. 제안일 뿐이죠. 코드는 이를 무시하고 새벽 2시에 프로덕션에서 폭발합니다.

그리고 GIL에 대해 이야기해봅시다—Python이 “멀티스레드” 코드를 실제로는 단일 코어에서 실행하도록 만드는 매력적인 방법. 2025년 현재, 16코어 머신에서도 말이죠.

그리고 나는 Rust를 만났다

나는 회의적이었습니다. “학습 곡선이 가파르다”고 그들은 말했죠. “빌린 검사기가 귀찮다”고 경고했습니다.

실제로 귀찮은 것은? JavaScript가 undefinednull을 서로 다른 것으로 취급해 3시 새벽에 널 포인터 예외를 디버깅하는 일.

이전 (React + TypeScript)

  • 번들 크기: 220 KB (압축)
  • node_modules: 1.1 GB
  • 빌드 시간: 45 초
  • 런타임 오류: 있음
  • 취약점: 47개 (12개 치명적)

이후 (Rust + Leptos)

  • 번들 크기: 52 KB (WebAssembly)
  • 의존성: 한 번 다운로드하고 영원히 캐시
  • 빌드 시간: 2 분 (첫 번째), 10 초 (증분)
  • 런타임 오류: 사실상 불가능
  • 취약점: 0

Rust 차이점

가비지 컬렉션 없는 메모리 안전

이제 미스터리한 충돌이 없습니다. “어디선가 메모리 누수가 있다”는 말도 사라집니다. 컴파일이 통과하면 동작합니다. 끝.

진정한 타입 안전

fn add(a: i32, b: i32) -> i32 {
    a + b
}

add(5, "hello"); // 컴파일되지 않음. 린터 경고가 아니라 컴파일 오류.

실제 성능

같은 알고리즘을 Python과 Rust로 벤치마크했습니다. Python: 450 ms. Rust: 5 ms. 90배 빠름. 오타가 아닙니다.

하나의 컴파일러, 하나의 패키지 매니저, 하나의 락 파일

Webpack, Babel, Rollup, Parcel, Vite, Turbopack, esbuild, Snowpack 같은 드라마는 없습니다. cargo build만 하면 됩니다. 모든 머신에서 매번 동작합니다.

“하지만 Rust는 어렵다!”

정말 그렇나요?

  • 한 번이라도 Rust의 빌린 검사기를 배우는 것이, 10 000번째 “undefined 속성을 읽을 수 없습니다” 디버깅보다 더 어려운가?
  • Result로 명시적인 오류 처리를 작성하는 것이, 프로덕션에서 Python 스크립트가 조용히 실패한 이유를 추적하는 것보다 더 어려운가?
  • Rust의 컴파일 오류를 다루는 것이, JavaScript의 “컴파일은 성공했지만 런타임에 크래시”를 다루는 것보다 더 어려운가?

Rust는 이를 닦는 것이 어려운 것처럼 보이지만, 실제로는 이를 닦는 것이 치아를 닦는 것만큼 어렵습니다. 노력은 필요하지만, 대안은 부패입니다.

생태계가 실제로 의미가 있다

Cargo vs NPM

  • 의존성을 추가하고 싶나요? cargo add serde — 끝.
  • 업데이트하고 싶나요? cargo update — 끝.
  • 보안 검사를 하고 싶나요? cargo audit — 끝.
  • 무엇이 깨졌는지 알고 싶나요? Cargo가 컴파일 시점에 상세히 알려줍니다.

1,000개의 취약점을 보여주고 업데이트하면 모든 것이 깨지는 npm audit도 없습니다. package-lock.json 병합 충돌도 없습니다. “node_modules를 삭제하고 기도한다”는 의식도 없습니다.

웹 개발에 대한 솔직한 이야기

나는 Rust를 사용해 백엔드(Axum)와 프론트엔드(Leptos + WebAssembly) 모두를 구현한 프로덕션 대시보드를 배포했습니다.

  • Zero JavaScript. “조금이라도 JavaScript”가 아니라 전혀 없음.
  • 백엔드는 눈부시게 빠릅니다.
  • 프론트엔드는 반응형이며 타입‑안전하고, 대부분의 JavaScript 프레임워크보다 작은 WASM으로 컴파일됩니다.
  • WebSocket 연결은 실시간 업데이트를 완벽히 처리합니다.

React와 Node.js로 같은 것을 만들 수 있었을까요? 물론 가능합니다. 하지만 4배 느리고, 10배 버그가 많으며, 100배 더 좌절스러웠을 겁니다. 확실합니다.

결론

JavaScript와 Python이 쓸모없다고 말하는 것이 아닙니다. 그들만의 역할은 있습니다.

하지만 그 역할은 이제 내 삶에 더 이상 없습니다.

Rust는 나에게 다음을 가르쳐 주었습니다:

  • 신뢰성은 사치가 아니라 기본선이다.
  • 성능은 중요하다.
  • 도구는 당신을 돕는 것이어야지 방해해서는 안 된다.
  • “컴파일된다”는 의미가 있어야 한다.

만약 당신이 다음에 지쳤다면:

  • 런타임에 명백한 오류를 디버깅하는 일
  • 빌드 도구와 씨름(npm install이 코드를 쓰는 시간보다 오래 걸리는)
  • “내 컴퓨터에서는 동작한다” 증후군
  • 제안일 뿐인 타입 시스템

Rust를 배우세요. 그들이 말하는 것만큼 어렵지 않습니다. 컴파일러는 당신의 친구이고, 커뮤니티는 친절합니다. 그리고 일단 감이 오면, 왜 그동안 미친 짓을 견뎌왔는지 궁금해질 것입니다.

당신의 선택

아직도 npm install을 실행하고 행운을 기대하고 있나요?
아직도 @ts-ignore를 추가하고 하루를 마무리하나요?
아직도 GIL이 중요하지 않다고 가장하고 있나요?

Rust 컴파일러가 기다리고 있습니다. 그리고 실제로 당신을 보호해 줍니다.

🦀 Rust: “컴파일된다”는 말이 실제로 의미가 있는 곳.

Back to Blog

관련 글

더 보기 »