Wippy가 Lua를 사용하는 이유

모든 기술 평가자가 이 질문을 하므로, 여기 직접적인 답변이 있습니다.

런타임 요구사항

Wippy는 사용자 정의 로직을 격리된 프로세스 안에서 실행합니다. 각 프로세스는 자체 메모리 공간과 자체 사용 가능 기능 집합을 가지며, 런타임이 명시적으로 허용하지 않는 한 경계 밖으로 나갈 방법이 없습니다. 플랫폼은 단일 인스턴스에서 수천 개의 이러한 프로세스를 동시에 실행하며, 각각은 서로 다른 테넌트를 위한 서로 다른 코드를 실행할 수 있습니다.

이는 각 프로세스 안에 임베드되는 언어 런타임이 다음 조건을 만족해야 함을 의미합니다:

  • 작아야 합니다. 각 프로세스는 자체 격리 환경에서 실행됩니다. 수천 개의 동시 프로세스 규모에서는 프로세스당 메모리가 중요합니다. Wippy는 프로세스당 약 13 KB의 기본 오버헤드를 목표로 합니다.
  • 완전히 샌드박싱 가능해야 합니다. 런타임은 각 프로세스가 접근할 수 있는 모듈, 함수, 시스템 호출을 정확히 통제해야 합니다. 주변 권한(ambient authority)이 없어야 하고, 프로세스 간에 전역 상태가 새어 나가지 않아야 합니다.
  • 임베드 가능해야 합니다. 언어 런타임은 Go로 작성된 Wippy 코어가 프로세스마다 인스턴스화하고, 구성하고, 해제할 수 있는 라이브러리여야 합니다. 외부 프로세스나 별도의 바이너리일 수 없습니다.
  • 모듈 로딩이 결정적이어야 합니다. 프로세스가 시작될 때 런타임이 그 프로세스가 볼 수 있는 코드를 결정합니다. 파일 시스템 접근은 없습니다. 임의 경로로 들어가는 require도 없습니다. 의존성은 프로세스 단위로 범위가 지정된 레지스트리에서 옵니다.
  • LLM 친화적인 문법이어야 합니다. 에이전트가 코드를 생성하고 수정합니다. 언어는 LLM이 문법을 환각 없이 안정적으로 읽고, 쓰고, 추론할 수 있을 만큼 단순해야 합니다.

평가한 언어: Python, JavaScript, Go, WASM

Python

AI 워크로드의 기본 선택지입니다. 우리가 배제한 이유는 CPython의 메모리 사용량이 인터프리터당 10-30 MB로, Lua 프로세스보다 몇 자릿수 더 크기 때문입니다. Python의 import 시스템은 코드에 파일 시스템, 네트워크, OS에 대한 주변 접근 권한을 부여합니다. Python을 샌드박싱하려면 WASM 컴파일(대부분의 라이브러리가 깨집니다) 또는 인터프리터에 대한 대대적인 패치가 필요합니다. Python의 동시성 모델(GIL)도 우리의 프로세스별 격리 모델과 충돌합니다. 생태계는 독립 실행 스크립트에는 강점이지만, 코드가 접근할 수 있는 대상을 결정적으로 통제해야 하는 샌드박스 런타임에는 부담입니다.

JavaScript (V8/QuickJS)

V8은 빠르지만 거대합니다(아이솔레이트당 수십 MB). QuickJS는 임베드할 수 있을 만큼 작지만, JavaScript의 프로토타입 체인과 동적 모듈 시스템은 겉보기보다 샌드박싱을 어렵게 만듭니다. importrequire는 파일 시스템에 접근하려 합니다. 생태계는 npm을 전제하며, 이는 네트워크 접근과 쓰기 가능한 파일 시스템을 가정하는데 Wippy 프로세스 안에는 둘 다 존재하지 않습니다. 제품을 만드는 시간보다 언어의 전제와 싸우는 시간이 더 길어질 것입니다.

Go

Wippy의 코어는 Go로 작성되어 있으므로 이 선택은 매력적이었습니다. 하지만 Go는 임베드되지 않습니다. 다른 Go 프로그램 안에서 Go 런타임을 라이브러리로 인스턴스화할 수 없습니다. Go 플러그인이 존재하지만 취약하고, 호스트 프로세스와 메모리를 공유하며, 샌드박싱할 수 없습니다. Go는 런타임 자체에는 적합하지만 사용자 코드에는 적합하지 않습니다.

WASM

샌드박싱에는 정말로 강력하며, 우리는 이를 Wippy의 두 번째 런타임으로 구축했습니다(아래 참조). 하지만 WASM만으로는 에이전트 개발의 주 언어로 충분하지 않습니다. WASM을 직접 작성하고 디버깅하는 개발자 경험은 아직 거칠고, LLM은 Lua보다 WASM 대상 코드를 덜 안정적으로 생성합니다. WASM은 다른 언어로 컴파일된 코드를 Wippy 샌드박스 안에서 실행해야 할 때 올바른 선택입니다. Lua는 주된 개발 및 에이전트 작성 경험에 올바른 선택입니다.

Lua가 다섯 가지 요구사항을 모두 충족하는 이유

Lua는 정확히 이 사용 사례를 위해 만들어졌습니다. 프로덕션에서 가장 많이 임베드된 스크립팅 언어로, World of Warcraft, Roblox, Redis, Nginx/OpenResty, Cisco와 Juniper 네트워크 장비, Adobe Lightroom, 그리고 수백 개의 게임 엔진 안에서 실행됩니다. 25년 넘게 적대적인 환경(사용자가 신뢰할 수 없는 코드를 실행하는 게임)에 임베드되어 왔습니다.

메모리

Wippy Lua 프로세스의 기본 오버헤드는 약 13 KB입니다. 동시 프로세스 10,000개 기준으로 대략 130 MB의 기본 프로세스 오버헤드입니다. Python에서는 같은 개수에 100-300 GB가 필요합니다. 이는 이론적인 우려가 아니라, 단일 머신에서 실행할 수 있느냐 클러스터가 필요하냐의 차이입니다.

샌드박싱

Lua의 모듈 시스템은 호스트가 완전히 통제하는 단일 함수(require)입니다. 이를 프로세스에 허용된 것만 해석하는 커스텀 로더로 교체하면, 프로세스는 여러분이 허용한 것만 보게 됩니다. import os도, subprocess도, 주변 파일 시스템 접근도 없습니다. 그런 함수들은 프로세스 환경에 존재하지 않습니다. 샌드박스는 개방된 시스템 위에 덧붙인 패치가 아니라 기본 상태입니다.

임베딩

Lua의 인터페이스는 작기로 유명합니다. 표준 C API는 약 60개 함수이며, 순수 Go 구현 덕분에 cgo 없이 Wippy의 Go 코어에 임베드하는 일이 간단합니다. 프로세스의 Lua 환경을 생성하고 해제하는 비용은 저렴하며, Wippy는 프로세스가 시작될 때마다 측정 가능한 오버헤드 없이 이를 수행합니다.

결정적 모듈 제어

Wippy에서 프로세스가 로드할 수 있는 코드는 그 프로세스의 레지스트리 범위로 결정됩니다. Lua 로더는 파일 시스템이 아니라 레지스트리에서 모듈을 해석합니다. 프로세스에 모듈이 허용되지 않았다면, 그 프로세스의 관점에서 해당 모듈은 존재하지 않습니다. 이것이 코드 수준에서 멀티테넌트 격리가 동작하는 방식입니다. 테넌트마다 사용 가능한 모듈이 다를 수 있고, 이는 애플리케이션 로직이 아니라 런타임이 강제합니다.

LLM 친화성

Lua의 문법은 최소한입니다. 클래스도, 데코레이터도, 언어에 내장된 타입 어노테이션도, async/await도, 복잡한 모듈 해석도 없습니다. Lua를 학습한 LLM은 Python(데코레이터 패턴, 컨텍스트 매니저, 타입 시스템)이나 JavaScript(프로토타입 체인, this 바인딩, 모듈 방식들)보다 훨씬 안정적으로 첫 시도에 올바른 Lua를 생성할 수 있습니다. 에이전트가 자신의 도구를 작성하고 수정하는 플랫폼에서는 이 점이 중요합니다. Wippy는 Lua를 타입 어노테이션 시스템(제네릭, 유니온, 채널 타입)과 내장 린터로 확장하므로, 문법 복잡성 없이 타입 안전성을 얻을 수 있습니다.

코루틴

Lua는 네이티브 코루틴을 지원하며, 이는 Wippy의 동시 프로세스 모델에 직접 대응됩니다. 각 프로세스는 스케줄러에 양보하는 코루틴에서 실행됩니다. 스레드도, 락도, 프로세스 간 경쟁 상태도 없습니다. 수천 개의 동시 프로세스가 스레드 기반 동시성의 복잡성 없이 협력합니다.

잃는 것

Lua의 생태계는 작습니다. 수만 개의 패키지를 가진 pip나 npm에 해당하는 것이 없습니다. 이는 의도된 것입니다. Wippy에서 의존성은 인터넷에서 가져오는 임의의 패키지가 아니라, 선언된 기능과 보안 정책을 갖춘 레지스트리 항목입니다. 하지만 그 말은 Wippy 프로세스 안에서 pip install pandas를 할 수 없다는 뜻이기도 합니다. 무거운 라이브러리 지원이 필요한 데이터 처리(ML 모델 추론, 복잡한 수치 연산)는 Wippy 에이전트가 도구를 통해 호출하는 외부 서비스로 실행하거나, Wippy 샌드박스 안의 WASM 함수로 실행해야 합니다.

Lua는 또한 대부분의 개발자에게 익숙하지 않습니다. 학습 곡선은 실재하지만 짧습니다. Lua의 전체 언어 레퍼런스는 약 30페이지입니다. 어떤 프로그래밍 언어든 아는 개발자 대부분은 하루 안에 Lua를 작성할 수 있습니다. 낯섦은 마찰 비용이지만, 대부분의 사용자 코드가 짧고 도구 지향적이며 점점 더 AI가 생성하는 런타임 플랫폼에서는 아키텍처적 이점(샌드박싱, 메모리, 임베딩)이 그 비용을 능가합니다.

Lua + WASM: 전체 그림

Wippy는 Lua 전용 플랫폼이 아닙니다. 두 개의 런타임을 제공합니다:

Lua는 에이전트 개발, 도구 작성, 애플리케이션 로직을 위한 주 런타임입니다. 대부분의 Wippy 코드가 작성되는 곳이며 에이전트가 코드를 생성하는 곳입니다. 작은 사용량, 완전한 샌드박싱 가능성, LLM 친화적인 문법이 이를 올바른 기본값으로 만듭니다.

WASM은 컴파일된 워크로드를 위한 보조 런타임입니다. Rust, Go, C 또는 WebAssembly로 컴파일되는 어떤 언어로든 기존 코드가 있다면, Lua와 동일한 프로세스 격리 및 레지스트리 통합으로 Wippy 안에서 실행할 수 있습니다. WASM 함수와 프로세스는 클럭, I/O, 파일 시스템(마운트된 Wippy 파일 시스템 항목을 통해), 환경 접근을 위해 WASI와 통합됩니다. 즉, 기존 비즈니스 로직을 Lua로 다시 작성하지 않고 Wippy 샌드박스로 가져올 수 있습니다.

두 런타임은 동일한 프로세스 모델, 동일한 레지스트리, 동일한 보안 정책을 공유합니다. Lua 에이전트가 WASM 함수를 호출할 수 있습니다. WASM 프로세스가 레지스트리를 통해 Lua 함수를 호출할 수 있습니다. 둘은 같은 시스템 안의 동등한 구성원입니다.

참고