# 마조히스트를 위한 웹 개발 가이드

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

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=21337](https://news.hada.io/topic?id=21337)
- GeekNews Markdown: [https://news.hada.io/topic/21337.md](https://news.hada.io/topic/21337.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-06-08T09:51:54+09:00
- Updated: 2025-06-08T09:51:54+09:00
- Original source: [sebastiano.tronto.net](https://sebastiano.tronto.net/blog/2025-06-06-webdev/)
- Points: 23
- Comments: 1

## Summary

이 글은 **Emscripten, WebAssembly**를 활용해 대규모 **C/C++ 코드베이스**를 브라우저에서 고성능으로 동작시키는 실제 포팅 과정을 Rubik’s Cube 솔버 예제로 상세히 다룹니다. 개발자는 **멀티스레딩, 콜백, 영속 스토리지, 비동기 초기화** 등 웹 이식의 주요 난관과 이를 해결하는 각종 **트러블슈팅 기법**을 단계별로 익힐 수 있습니다. **프론트엔드 경험이 적은 개발자**도 제공된 실습 예제와 안내를 통해 기존 코드를 효과적으로 웹 환경에 적용할 수 있습니다.

## Topic Body

- **C/C++ 코드**를 Emscripten으로 **WebAssembly**로 포팅하여 브라우저에서 동작하는 웹앱 제작 과정을 실제 Rubik’s Cube 솔버 예제 기반으로 상세히 설명함  
- **Hello World부터 멀티스레딩, 콜백, 영구 스토리지, 모듈화** 등 브라우저/WebAssembly 환경에서 마주치는 **구체적 난관과 문제 해결법**을 단계별로 다룸  
- **JavaScript 비동기 초기화, 함수 내보내기, Web Worker 및 Spectre 이슈, IDBFS 통한 IndexedDB 영구 저장** 등 **실전 트러블슈팅**에 초점  
- **Emscripten의 추상화가 실제로는 자주 '새는' 현상**(leaky abstractions)임을 반복적으로 강조하며, 웹 플랫폼의 한계와 내부 구조를 알 필요성을 강조함  
- **프론트엔드의 최소한의 JavaScript/HTML 지식**만으로 복잡한 C 코드 베이스를 웹으로 옮기는 실전 경험을 통해, **기존 C/C++ 라이브러리를 웹으로 이식하려는 개발자**에게 실질적인 도움과 노하우를 제공하는 **경험 기반 가이드**임  
  
---  
  
### 소개  
  
- 최근 **Rubik’s Cube 최적해 알고리듬**을 웹 앱으로 구현하는 프로젝트를 진행하였음  
- C로 개발한 Rubik’s Cube 최적화 솔버를 **Emscripten**으로 컴파일해 **WebAssembly**로 웹 브라우저에서 구동하는 과정을 기록함  
- WebAssembly를 쓰는 주된 이유는 **자바스크립트 대비 거의 네이티브에 가까운 성능**을 웹에서 확보할 수 있기 때문임  
- 본 글은 전통적인 웹 개발 튜토리얼이 아닌, **기존 C/C++ 코드를 웹으로 이식하고자 하는 개발자**를 위한 ‘고통의 여정’임  
- 웹 개발 경험이 많지 않아도 HTML, JavaScript의 기본 구조와 브라우저 개발자 도구 활용법만 알면 따라갈 수 있음  
  
### 환경 구축  
  
- 모든 예제 코드는 [git 저장소](https://git.tronto.net/emscripten-tutorial/file/README.md.html), [github](https://github.com/sebastianotronto/emscripten-tutorial)에서 확인 가능함  
- **Emscripten** 설치 필요(설치법은 공식 사이트 참고), 웹서버는 darkhttpd 또는 Python `http.server` 등 사용  
- 튜토리얼 코드 예제는 **Linux 및 UNIX 계열**에서 테스트되었음. Windows 사용자는 **WSL(Windows Subsystem for Linux)** 을 추천함  
  
### Hello World  
  
- C 코드의 Hello World를 `emcc -o index.html hello.c` 명령으로 컴파일하면 **index.html(웹 페이지), index.wasm(WebAssembly 바이트코드), index.js(JavaScript glue code)** 세 파일이 생성됨  
- 브라우저나 Node.js에서 동작 가능하며, 각각의 환경에서 다른 활용법이 존재함  
- `.wasm`만 생성하려면 `-sSTANDALONE_WASM` 옵션 사용  
- Emscripten에서 `.wasm`만 생성도 가능하지만, 대부분의 경우 **JavaScript glue code**가 필수적임  
  
### Intermezzo I: WebAssembly란?  
  
- **WebAssembly(WASM)** 은 웹 브라우저 내 **고성능 가상 머신**에서 실행되는 저수준 언어임  
- WASM은 **2017년 이후 모든 주요 브라우저**에서 지원됨  
- 원래 Emscripten은 C/C++ 코드를 **asm.js**라는 JavaScript 하위 집합으로 변환했으나, WASM의 등장으로 전환됨  
- 텍스트 표현식도 존재하며, 스택 기반 구조임. 최근까지 32비트 아키텍처만 지원해 4GB 이상의 메모리를 못 썼으나, **WASM64**가 점진적으로 브라우저에 도입되고 있음  
  
### 라이브러리 빌드  
  
- **C 함수 multiply()** 를 WASM으로 빌드 후 JavaScript에서 호출하는 기본 예시 진행  
- 기본 빌드시 Emscripten은 함수 이름에 **언더스코어(_)** 를 붙임(예: `_multiply`)  
- 함수 외부 노출은 **-sEXPORTED_FUNCTIONS** 옵션 지정 필요함  
- 라이브러리 로딩 시 초기화가 비동기적이므로, **onRuntimeInitialized**나 `await` 등 비동기 처리 필요함  
- 실습 코드는 저장소 `01_library` 폴더에 있음  
  
### Intermezzo II: JavaScript와 DOM  
  
- JavaScript에서 HTML의 구성요소에 접근 및 수정하려면 **Document Object Model(DOM)** 을 활용해야 함  
- **이벤트 리스너(addEventListener)** , **내장 연산자/함수** 등으로 동적 UI 구현 가능  
- 예제를 위해 입력, 버튼, 결과 표시가 있는 기본적인 HTML/JavaScript 연동 구조 설명  
- script 분리/병합의 실전적 방법과 이슈(예: `defer`의 사용, DOM 요소 로드 순서)도 안내함  
  
### 라이브러리 모듈화 및 로딩  
  
- WASM 라이브러리를 다중 포함하거나 **Node.js/웹 양쪽에서 재사용하기 위해** **MODULARIZE**, **EXPORT_NAME** 옵션으로 모듈 형태로 빌드할 수 있음  
- `.mjs` (ES6 모듈) 확장자가 Node.js 호환성을 위해 추천됨  
- 웹/Node 양쪽에서 **import MyLibrary from ...** 식 모듈 사용 가능  
  
### 멀티스레딩  
  
- WebAssembly에서 **성능 강화를 위해 pthreads 기반** 멀티스레드 코드 포팅 가능함  
- 함수 내 다수의 스레드를 생성해 **병렬로 계산 작업(예: 소수 개수 세기)** 을 실행함  
- 빌드시 **-pthread**, **-sPTHREAD_POOL_SIZE=** 옵션 필요함  
- 실제 브라우저에서는 **Cross-Origin-Opener-Policy: same-origin**, **Cross-Origin-Embedder-Policy: require-corp**와 같은 HTTP 헤더 추가 필요  
- 모든 예제는 저장소 `03_threads` 폴더에서 확인 가능함  
  
### Intermezzo III: Web Workers 및 Spectre  
  
- **Emscripten 멀티스레드는 Web Workers**로 구현됨(Web Workers는 별도의 프로세스이자 메시지 기반 통신 구조)  
- **공유 메모리(SharedArrayBuffer)** 사용에는 **보안 상의 제약**이 존재  
- 2018년 Spectre 취약점 발생 후, **크로스 오리진 격리(cross-origin isolated)** 요구사항 및 관련 헤더 필수화됨  
  
### 메인 스레드 블로킹 주의  
  
- 긴 작업이 브라우저의 **메인 UI 스레드를 BLOCK**할 경우 사용자 경험이 급격히 저하됨  
- 이를 피하기 위해 웹 워커(Worker)를 도입: UI/입력 처리와 연산 처리를 명확히 분리  
- **postMessage, onmessage**로 메인-워커 간 이벤트 기반 통신 구현  
- 웹 워커 내에서 Emscripten-WASM 모듈을 불러 비동기 연산만 전담  
  
### 콜백 함수  
  
- **C 함수의 파라미터로 함수 포인터**(콜백) 전달 시, 자바스크립트의 함수 객체와 자동 연동이 불가능함  
- Emscripten 제공 **addFunction(), UTF8ToString()** 등을 활용해야 하며, 빌드시 **-sEXPORTED_RUNTIME_METHODS, -sALLOW_TABLE_GROWTH** 옵션 추가 필요  
- 콜백은 반드시 **메인 스레드**에서만 호출되어야 안정적으로 동작함(웹 워커에서는 접근 불가)  
  
### 영속적 스토리지  
  
- 사용자의 브라우저에 데이터를 영구 저장하기 위해 Emscripten의 **IDBFS(IndexedDB 기반 파일 시스템)** 를 사용함  
- 빌드시 **--lidbfs.js** 플래그와 **--pre-js ** 등으로 초기 세팅 필요  
- C 코드에서는 파일 입출력 함수(`fopen`, `fread`, `fwrite`)를 그대로 사용할 수 있으나, 실제 데이터 반영/동기화 처리는 반드시 **JavaScript에서 명시적 매핑 및 싱크 처리**가 필요함  
- 브라우저의 Sandbox/보안 정책 특성상, 로컬 파일 시스템 직접 접근은 Node.js에서만 가능하며, 브라우저에서는 IDBFS와 같은 백엔드를 활용해야 안전하게 영구 데이터 저장 가능함  
  
### 결론  
  
- 본 튜토리얼 전 과정을 통해 **복잡한 네이티브 C/C++ 코드**를 **최소한의 JavaScript와 HTML**만으로도 안전하고 성능 저하 없이 **브라우저 상에서 실행**할 수 있는 실질적인 방법을 자세히 배울 수 있음  
- 실전 환경에서 **멀티스레드, 콜백, 비동기 처리, 스토리지 연동**까지 모든 핵심 트랙의 난관/해결책을 경험하고, 관련 설정 및 브라우저 제약사항 등 최신 트렌드도 익힐 수 있음  
- 제공되는 **Git 저장소 예제**를 참고하여 자체 프로젝트에 적용 및 확장 가능함

## Comments



### Comment 39837

- Author: neo
- Created: 2025-06-08T09:51:54+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=44200895) 
* .js에서 .mjs로 확장자를 바꿨다는 점을 주목해줬으면 하는 바람, 사실 어느 확장자를 쓰든 문제에 부딪히는 현실에 대한 체감형 공감, dojo부터 CommonJS, AMD, ESM, webpack, esbuild, rollup 등 다양한 모듈 시스템을 써 왔던 입장에서 이 말에 정말 공감 100%라는 느낌
  * commonjs에서 esm으로의 전환이 마치 python2에서 python3로 넘어갈 때처럼 엄청난 변환이었지만, 기대에 비해 얻는 이점은 적고 번거로움만 가중된 인상, 많은 라이브러리들이 esm만 지원하는 경우가 많아져서 요즘은 npm의 'versions' 탭에서 최근 한 달 가장 다운로드 많이 된 버전을 골라 그게 마지막 commonjs 버전일 가능성이 높다는 현실, 분명 esm이 진보된 모듈 시스템이라 할 만하지만 tc39가 거의 의도적으로 commonjs와 호환이 안 되게(top-level await 등) 만든 부분은 정말 이해불가라는 솔직한 의견
  * js에서 모듈의 역사가 그야말로 트라우마에 가깝다는 생각, 이제 브라우저에 import maps까지 도입되어 앞으로 또 무슨 재미있는(?) 문제들이 생길지 궁금한 마음
  * 최근에 Function 객체가 런타임에 아무 JS 코드나 컴파일할 수 있음을 알게 되어서, 'import'도 쓸 수 없는 내 환경에서 일종의 생명줄 역할로 매우 유용하게 활용 중, JS 생태계에선 별로 필요 없을 수도 있지만 내겐 큰 도움이 됨을 강조
  * 그래서 모두가 bun.sh를 써야 한다는 주장
  * .esm.js도 쓸 수 있지 않느냐는 질문
* 이 글에서 장기적으로 문제를 일으킬 수 있는 부분을 더 지적하자면, var 키워드 대신 let이나 const를 쓸 것을 추천, var는 여전히 동작하지만 요즘 JS 개발자들은 대부분 linter로 var 사용을 금지하는 분위기, var는 함수 스코프만 지원해서 대부분의 타 언어 개발자들이 언젠가 혼란을 겪는 포인트임, 네이티브앱 포팅 이슈로는 컴파일 타임에 Ctrl-C, Ctrl-V로 복사/붙여넣기를 하드코딩해서 리눅스, 윈도우에선 되지만 맥에서는 먹히지 않는 예시를 언급, 웹에서는 copy, paste 이벤트를 감지하는 식으로 처리해야 하며 Unity 같은 프레임워크도 하드코딩된 키 때문에 맥에서는 복붙이 안 되는 것을 목격, 대부분 게임에선 필요 없지만 복붙이 필요한 기능을 웹으로 내보낼 때는 꼭 문제가 되는 사례
* 웹/NodeJS에서 멀티스레딩 너무 싫다는 푸념, mutex나 rwlock 같은 동기화 프리미티브로 값 자체를 context간(예:v8 isolates) 전송 가능하게 만드는 게 아니라, 실제론 거의 쓸 데 없는 SharedArrayBuffer만 도입된 점이 아쉬움, 스레드간 동기화는 결국 thunking과 데이터 복사를 RPC 레이어를 통해 하게 되는 구조, 우리 회사 프로덕션 앱은 70~100GB RAM을 쓰는 대형 앱(내가 만들기 전부터 그랬음)이라서, 네이티브코드 기반으로 메모리 페이지와 커스텀 데이터 구조를 직접 관리하면서 직렬화/해제 최소화하는 희한한 해결책을 찾고 있는데, v8은 문자열 인코딩이 utf16이라 네이티브 레이어에서 JS 값 다루기가 비쌈
  * 100GB RAM을 쓰는 이 앱이 진짜 웹앱이어야만 했는지 궁금, C# 같은 언어로 작성된 내부 툴이어야만 할 이유가 있는 것처럼 들리는 의문
* 이 생태계가 혼돈에 가까워서 차라리 '마조히스트' 소리도 더 정상적으로 느껴지는 현실
  * 이미 혼돈이 내포돼 있다고 봐도 된다는 한 마디
* 글 자체가 잘 쓰여진 점, 더군다나 어렵고 복잡한 루트로 시작한 선택에 놀라움, 프로젝트 세팅이 가장 힘든 부분임을 실감, 바로 보안/헤더 문제에 부딪힌 걸 칭찬하지만 종종 예상되는 문제는 CORS라는 의견, 우리 회사에서도 emscripten/C++으로 빌드 중이며 WebGPU/셰이더와 WebAudio까지 추가해 앞으로 더 빡센 여정이 예상
* 예전엔 브라우저에서 코드 컴파일은 '느릴 것'이라 막연히 생각했지만 OP가 그렇지 않음을 잘 설명, Emscripten 프로젝트도 "LLVM, Emscripten, Binaryen, WebAssembly의 조합 덕분에 출력물이 작고 거의 네이티브와 같은 속도로 동작"한다고 강조 ([emscripten.org](https://emscripten.org/))
  * 오늘 나에겐 '옐로우버스 증후군'같은 하루, 지난 주까지만 해도 Emscripten을 몰랐는데, 프로젝트에 SDL을 붙이며 CMake에 APPLE, MSVC, EMSCRIPTEN 타겟이란 주석을 만났고, 바로 오늘 hn에서 Emscripten 이야기를 또 마주쳐서, 이제는 본격적으로 시간 내서 깊이 파봐야 할 시점이라는 다짐
  * "거의 네이티브 속도"라는 표현이 상당히 주관적이라는 의문, 실제로 얼마나 빠른 건지 문서에서 수치 데이터를 못 찾음
* 글이 유익했으며 본인도 C로 작성된 컴파일러를 웹어셈블리로 컴파일해 웹플레이그라운드로 만들고 싶다는 계획, 참고로 최신 브라우저는 자바스크립트를 통해 SQLite를 사용할 수 있는데, 이게 wasm에서도 가능한지 궁금, 만약 emscripten이 C 코드의 sqlite API 호출을 브라우저 sqlite db로 다리 연결해주면 너무 이상적일 것 같아 더 알아볼 가치가 있음
* SSL에 왜 48번 포트를 썼는지 궁금, 특별한 이유가 있는지 질문
  * H48이라는 이름에서 따와 무작위로 정한 포트라는 답변, 이 웹앱은 추가 HTTP 헤더가 필요해서 사이트 전체에 영향 없이 구현하려고 간단히 다른 포트를 쓴 것이 원인, [https://h48.tronto.net](https://h48.tronto.net)로 리다이렉트도 되고, 나중에 OpenBSD의 httpd와 relayd 세팅을 더 개선하거나 아예 별도의 도메인으로 옮기는 방안도 고민 중이라는 설명
