# JavaScript 디블로팅

> Clean Markdown view of GeekNews topic #29675. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=29675](https://news.hada.io/topic?id=29675)
- GeekNews Markdown: [https://news.hada.io/topic/29675.md](https://news.hada.io/topic/29675.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-05-20T08:04:54+09:00
- Updated: 2026-05-20T08:04:54+09:00
- Original source: [github.com/naver](https://github.com/naver/lispe/wiki/6.23-De%E2%80%90bloating-Javascript)
- Points: 2
- Comments: 2

## Topic Body

- **JavaScript 문법**은 중첩된 괄호와 콜백으로 쉽게 복잡해지고, 작은 UI에도 많은 라이브러리를 끌어오는 비대화가 생김
- **WebAssembly**는 브라우저에서 다른 언어를 실행할 길을 열지만, Pyodide처럼 JavaScript 이벤트 루프와의 비동기 연결 비용이 큼
- 브라우저 자원과 WebAssembly 메모리는 제한적이어서, JavaScript를 대체하려 하기보다 **협상**하는 접근이 필요함
- **LispE**는 3.3MB WASM 바이너리 하나에 450개 이상 함수를 담고, 문자열·계산·행렬·정규표현식을 함께 제공함
- `evaljs`와 `asyncjs`로 JavaScript 함수와 DOM을 활용하면서, 여러 외부 라이브러리 대신 감사 가능한 단일 바이너리로 **코드 비대화**를 줄임

---

### JavaScript 비대화와 브라우저 제약
- **JavaScript 문법**은 괄호, 중괄호, 대괄호가 겹치고 닫는 순서를 맞춰야 해서 코드가 쉽게 복잡해짐
  - `forEach` 콜백 안에서 여러 객체에 값을 넣고 조건부 캐시를 갱신하는 예시가 나옴
```js
newNames.forEach((name, i) => {
    allAgentContents[name] = contents[i];
    agentModes[name] = modes[i];
    if (compiled[i]) agentCompiledCache[name] = compiled[i];
    agentViewingCompiled[name] = viewing[i];
});
```
- 기본 프로그래밍 언어라면 흔히 포함하는 처리를 위해 JavaScript에서는 많은 **라이브러리**를 내려받게 됨
  - C나 C++도 작업을 하려면 `include`가 필요하지만, JavaScript 라이브러리는 누가 구현했고 누가 유지보수하는지 알기 어려운 경우가 많음
  - 작은 창에 _hello_를 표시하려고 “인터넷의 절반”을 로드하는 듯한 페이지도 생김
- 인터넷은 JavaScript에 의존하며, TypeScript로 감추더라도 브라우저에서 페이지를 열 때마다 거쳐야 하는 문지기로 남아 있음
- 브라우저가 궁극의 운영체제가 되어 Windows나 Mac OS를 불필요하게 만들 것이라는 꿈이 있었고, 그 꿈을 구현할 언어로 JavaScript가 선택됨

### WebAssembly와 JavaScript의 관계
- **WebAssembly**는 자체 기계어를 가진 가상 머신에 가까워, 브라우저 안에서 다른 방식으로 코딩할 가능성을 열어줌
- Pyodide는 브라우저에서 Python을 실행하는 인상적인 엔지니어링 성과지만, JavaScript 영역 안에서 움직이는 비용도 드러냄
  - Python의 `asyncio`와 JavaScript 이벤트 루프라는 두 비동기 세계가 서로 대화해야 함
  - 두 세계 사이의 다리는 취약하며, 모든 `await`가 어느 세계에 속하는지 기억해야 함
- 브라우저 자원은 제한적이어서, 초기 컴퓨터 과학 시절처럼 매 명령과 구조를 비트 단위로 살펴보는 사고방식이 필요함
  - 1981년에 정확히 **15772 bytes**만 남은 컴퓨터에서 프로그래밍을 시작했다는 기준이 제시됨
  - 현대 브라우저 메모리가 그 첫 컴퓨터만큼 제한적인 것은 아니지만, WebAssembly는 일반 프로그램처럼 메모리를 자유롭게 소유하지 못하고 제한된 방식으로 먼저 허락을 받아야 함
- 핵심 태도는 JavaScript와 싸우는 것이 아니라 **협상**하는 것임

### LispE가 제시하는 대안
- **LispE**는 단일 바이너리 안에 [450개 이상의 함수](https://github.com/naver/lispe/wiki/Index-of-functions)를 제공함
  - WASM 바이너리 크기는 **3.3 MB**임
  - 문자열, 계산, 행렬, 정규표현식 처리 기능이 한곳에 묶여 있음
  - WASM 바이너리는 [binaries/wasm](https://github.com/naver/lispe/tree/master/binaries/wasm)에 있음
- 작은 인터프리터지만 반환값으로 문자열, `Float64Array`, 정수, 실수, 문자열 배열을 다룰 수 있음
- LispE는 Lisp 기반이라 **AST**가 살아 있는 구조를 가짐
- Lisp 문법이 맞지 않으면 Python이나 Basic과 구분하기 어려운 언어를 위한 트랜스파일 문법을 사용할 수 있음
  - [grammar](https://github.com/naver/lispe/tree/master/Pythonic)를 수정해 원하는 스타일의 언어를 만들 수 있음
  - 그리스어로도 가능하며, 이미 [그리스어 예시](https://github.com/naver/lispe/wiki/6.15-Programming-is-Greek-to-me...-Literally)가 있음
- LispE는 JavaScript와 싸우지 않고 JavaScript 함수와 DOM 기능을 활용하는 방식으로 협력함

### JavaScript 호출: evaljs와 asyncjs
- LispE 안에서 JavaScript 코드를 실행하려면 **`evaljs`** 와 **`asyncjs`** 를 사용할 수 있음
  - `evaljs`는 JavaScript 코드를 실행해 값을 받음
  - `asyncjs`는 페이지에 정의된 사용자 JavaScript 함수와 비동기 콜백을 연결함
```commonlisp
(setq a (evaljs "10 + 20 + 30")) ; execute some JS code

; call_llm is a user-defined JS function in the page
(asyncjs `call_llm("Implement a piece of code in Python to sort strings");` 'mycallback)

(defun mycallback(theresult) ...)
```
- `asyncjs`는 작업이 끝나면 돌아오겠다는 **Promise**로 동작함

### 코드 비대화 줄이기
- 1995년 Wirth 인용은 더 빠른 컴퓨터와 더 큰 모델이 문제를 해결하지 않으며, 코드를 날씬하게 유지해야 한다는 결론으로 이어짐
- LispE는 브라우저에 노출되는 [450개 함수](https://github.com/naver/lispe/wiki/Index-of-functions)를 단일 감사 가능 바이너리로 제공함
  - 각 기능을 따로 구현하려면 수치 계산용 `mathjs`, 컬렉션용 `lodash`, 문자열 조작용 `voca`, 통계 분포용 `simple-statistics` 같은 라이브러리를 로드해야 함
  - 이런 방식은 각자 다른 작성자, 버그, 유지보수 일정을 가진 수백 MB의 코드로 커질 수 있음
- LispE는 하나의 유지보수되는 코드로 이 기능들을 제공하며, 오픈소스라 전체 코드 감사를 할 수 있음
- **Garbage Collector**가 최악의 순간에 코드 성능을 무너뜨리지 않는다고 봄
- JavaScript와는 단순 API 호출로 투명하게 통신하며, 미리 정의된 구조를 반환할 수 있음
```js
// floats here is a Float64Array
const floats = callEvalLispEToFloats(0, `(normal_distribution 100)`);
```
- LispE와 JavaScript가 함께 동작하는 예시는 [데모 페이지](https://naver.github.io/lispe/)에서 확인 가능함
- [Here Are The Files](https://github.com/naver/lispe/tree/master/binaries/wasm): JavaScript 애플리케이션에 `lispe`를 통합하는 파일
- [Here Are The Demonstrator Files](https://github.com/naver/lispe/tree/master/docs): 데모 파일

## Comments



### Comment 57873

- Author: crawler
- Created: 2026-05-20T09:04:18+09:00
- Points: 1

글이 뭔가 뜬금없어서 출처를 의심했는데 네이버였다니 ㄷㄷ  
근데 역시 반응은 안 좋네요... 솔직히 자바스크립트가 과장돼서 3.3MB짜리 WASM까지 올려서 리습을 쓰겠다는 게 쉽게 이해되지 않는 오버 엔지니어링이죠 ㅋㅋㅋ  
  
네이버 계정인 이유에 대해 Claudius라는 프로젝트 개발자가 댓글을 남겼는데 본인이 Naver Labs Europe에서 일하고 있고 네이버가 오픈소스 프로젝트로 승인해서 올라갔다고 하네요.  
네이버랑 크게 상관은 없는 거 같고 그냥 리습을 진짜 사랑하는 분들인듯...

### Comment 57867

- Author: neo
- Created: 2026-05-20T08:04:54+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/lynqdm/de_bloating_javascript) 
- JavaScript를 덜 부풀리겠다면서 **불투명한 3.3MB WASM 덩어리**를 추가하고 앱은 Lisp로 쓰라는 건가 싶음  
  순수 JavaScript와 추가 의존성 0개만으로도 그 크기의 10분의 1에 닿기 전까지 꽤 많은 걸 만들 수 있음
  - 문서의 작은 한 부분만 보고 dismiss하지는 않았으면 함  
    이 활용 방식에는 공감하지 않지만, 요즘 보기 드문 기묘함이 있는 재미있는 **Lisp 열정 프로젝트**이고, 모든 문서가 사람 손으로 쓴 티가 나는 독특한 문장이라 좋음

- 표준 라이브러리에 새로 넣을 만한 건 언제나 제안받고 있음  
  최근 추가된 것으로는 **집합 합집합/교집합**, `sum`, `base64` 등이 있음  
  다만 미적분 함수 요청은 거의 못 들었고, 문자열 관련으로 꾸준히 요청되는 건 `.reverse`나 `.titleCase` 정도임  
  `.reverse`는 장난감 문제 말고 설득력 있는 쓰임새가 많지 않고, `.titleCase`는 [가능은 하지만 국제화 데이터가 필요해서 논의가 진행 중임](https://github.com/tc39/ecma402/issues/294)  
  게다가 [둘 다 이 라이브러리에는 없음](https://github.com/naver/lispe/wiki/5.8-Strings)
  - 많은 사람이 **tc39** 구성원을 직접 알지 못하거나, 그들이 우리 주변에 있다는 걸 모르는 듯함  
    제안을 해도 실제 반영까지 3년 걸릴 테니 시도할 가치가 없다고 느끼고, `utils/` 폴더는 별로지만 당장 만들 수 있다고 생각하는 것도 있음
  - 표준 라이브러리가 내가 추적한 것보다 많이 움직였으니 글을 조금 고쳐야겠음  
    내 주장은 JavaScript에 함수가 부족하다는 것보다는 **배포 모델**에 더 가까움  
    표준 라이브러리가 꽉 차 있어도 평균적인 앱은 여전히 전이적 npm 의존성을 메가바이트 단위로 싣고 나감  
    LispE-as-WASM은 3.3MB, 문서화된 함수 약 450개, C++ 코어, GitHub 공개 소스로, 감사 가능한 런타임이 어떤 모습일 수 있는지 보는 실험임  
    실제로 그 위에 에이전트 하네스를 만들었고, LispE 규칙을 브라우저 안에서 즉석 실행하고 있음  
    JavaScript 이벤트 루프는 아주 좋아함

- 누군가 **문법**을 부풀었다고 표현하면 긴장하게 됨  
  최소 문법은 시각적 구분을 위해 서로 다른 문자를 쓰는 문법보다 훨씬 읽기 어려울 수 있음  
  LispE가 대안으로 내놓는 걸 보면 이렇다:  
  ```  
  (defun gol8(⍵) ((λ(⍺) (| (& (== ⍺ 4) r) (== ⍺ 3))) (⌿ '+ (⌿  '+ (° (λ (x ⍺) (⊖ ⍺ x)) '(1 0 -1) (↑ (λ (x) (⌽ ⍵ x)) '(1 0 -1)))))))  
  ```  
  ... !?  
  https://github.com/naver/lispe/wiki/5.3-A-la-APL
  - 이건 당연히 유명하게 짧은 **APL 예제**를 포팅한 것임 https://aplwiki.com/wiki/Conway's_Game_of_Life
  - 와, 이런 건 어떤 프로덕션에도 허용하지 않을 듯함

- 이 프로젝트로 들어가는 진입점이 좀 이상하고, https://github.com/naver/lispe/wiki/1.-Introduction 쪽이 더 나아 보임  
  WASM을 대상으로도 제공하는 **네이티브 Lisp 프로젝트**이고, 프로그래밍 언어 덕후들이 좋아할 만한 재미있는 요소가 있음

- 첫인상은 꽤 억지스러운 JavaScript 예제라는 느낌이었음  
  JavaScript 혐오의 상당 부분은 초기 브라우저 비호환성, 고통스러운 DOM API, 문법상의 함정에서 나온 것 같고, 이런 것들은 요즘 내 일상에는 사실상 영향이 없음  
  현대 JavaScript, 특히 **TypeScript**와 합리적인 린트 규칙을 쓰면 언어로서는 충분히 괜찮음  
  여전히 CJS 대 ESM, 공급망 위험, 생태계 변동 같은 문제는 있지만, 대부분은 언어 자체 바깥에 있음

- 괄호 균형 맞추기가 너무 성가셔서 사람들이 편집기 확장까지 만든 바로 그 경험이 훌륭하다는 건가  
  이런 스타일의 프로그래밍, 즉 **낮은 문법 밀도와 함수형 방식**을 원한다면 방향을 뒤집어서 연결형(concatenative) 언어로 가는 편이 나음

- Lisp 해석기가 컴파일된 결과물로 어떻게 **3.3MB**까지 갈 수 있는지 궁금함  
  이게 덜 부풀리는 건가?
  - 큰 **표준 라이브러리**가 포함돼 있어서 그럴 가능성이 큼  
    그렇다 해도 쓰지 않는 코드를 포함한 큰 표준 라이브러리를 함께 배포하는 건 덜 부풀리는 것과 거리가 멀다는 데 동의함

- 충격! 공포!  
  닫는 괄호를 올바른 순서로 닫아야 한다니 놀라움
  - 올바른 순서로 닫지 않아도 되는 언어를 원한다면 [J](https://code.jsoftware.com/wiki/NuVoc)가 있음  
    JavaScript 같은 언어의 장황함에 질색하는 Lisp 애호가들도 APL 계열 언어 앞에서는 돌아서서, 최대한 짧은 것보다 **명확성**과 **가독성**이 훨씬 중요하다고 논쟁하기 시작하곤 함  
    LispE 작성자는 APL 기본 연산에 어느 정도 애정이 있어 보이지만, 그렇게 간결한 표기법의 언어를 접하고도 Conway의 _Life_를 APL, J, K, 심지어 Q 버전보다 길고 성긴 깊은 중첩 s-표현식 버전으로 선호하는 이유는 이해하기 어려움
