최근 기술 커뮤니티에서 “WASM은 컨테이너를 대체하게 될 것!" 이라는 제목의 글을 읽었습니다.
흥미로운 주제에 매료된 저는 직접 WASM 실습을 진행해보기로 했습니다. 특히, 최근 AI 도구의 발전과 함께 생산성을 극대화할 수 있는 방법을 모색하던 중, Perplexity 의 Deep Search 기능이 새로 출시되어 Deep Search 기능을 활용하여 관련 자료를 조사하고 실습 과정을 디자인했습니다.
본 글에서는 WASM 이 Docker 컨테이너를 대체할 가능성을 탐구하기 위해 진행한 실습 과정과 그 결과를 공유합니다.
기술적 통찰력을 제공하는 동시에, 새로운 도구와 기술을 활용하는 방법에 대한 영감을 드릴 수 있기를 바랍니다.
Perplexity 의 Deep Search 를 활용하여 만든 생성된 실습 가이드입니다.
전체 프로세스 요약
- 개발 환경 구성 - Go 설치 및 웹 어셈블리 컴파일 도구 준비
- Go 코드 작성 - DOM 조작과 브라우저 API 호출 기능 구현
- WASM 컴파일 - Go 코드를 '.wasm' 바이너리로 변환
- 웹 페이지 구성 - HTML/JS 로 WASM 모듈 로드 및 실행
- 로컬 서버 실행 - 웹 서버를 통해 실제 활경 테스트
- 최적화 - TinyGo 를 활용한 바이너리 크기 축소
1. 개발 환경 구성
Go 설치 및 버전 확인
$ go version
go version go1.22.5 darwin/arm64
Go 1.11 부터 공식 WASM 지원 시작
웹 어셈블리 컴파일 설정
export GOOS=js
export GOARCH=wasm
GOOS=js - 자바스크립트 호환 환경 지정
GOARCH=wasm - 웹 어셈블리 아키텍처 타겟팅
환경변수를 설정하면 모든 go 실행환경에 영향을 미치기 때문에 Makefile 을 작성하여 실습을 진행
go mod init
go mod init example.com/web-assembly-go
// go.mod
module example.com/web-assembly-go
go 1.22.5
위와 같은 파일이 생성된 것을 확인할 수 있습니다.
2. Go 코드 작성
기본 템플릿 (main.go)
// main/main.go
package main
import (
"syscall/js"
)
func main() {
// 1. 브라우저 콘솔 출력
println("WASM 샌드박스 초기화 완료")
// 2. DOM 요소 제어
doc := js.Global().Get("document")
title := doc.Call("createElement", "h1")
title.Set("innerHTML", "Go WASM 작동 확인!")
blueArea := doc.Call("getElementById", "wasm-output")
blueArea.Call("appendChild", title)
// 3. 자바스크립트 alert 호출
alert := js.Global().Get("alert")
alert.Invoke("브라우저 API 정상 호출")
// 4. 이벤트 루프 유지
select {}
}
syscall/js - Go ↔ JS 상호작용 핵심 패키지
select {} - Go 루틴이 종료되지 않도록 유지
3. WASM 컴파일 - Go 코드를 '.wasm' 바이너리로 변환
기본 컴파일
go build -o main.wasm
Output
- main.wasm (1.5MB)
Makefile 작성
컴파일을 위해 다음과 같이 Makefile 을 작성하고, 명령어를 실행합니다.
GOOS=js
GOARCH=wasm
OUTPUT=main.wasm
MAIN_PATH=main
MAIN_SRC=$(MAIN_PATH)/main.go
SERVER_PATH=server
SERVER_SRC=$(SERVER_PATH)/server.go
compile:
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(OUTPUT)
build:
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(OUTPUT) $(MAIN_SRC)
run:
GOOS=$(GOOS) GOARCH=$(GOARCH) go run $(MAIN_SRC)
clean:
rm -f $(OUTPUT)
tinygo:
tinygo build -o $(OUTPUT) -target wasm $(MAIN_SRC)
server-start:
go run $(SERVER_SRC)
go-start:
$(MAKE) build
$(MAKE) server-start
tinygo-start:
$(MAKE) tinygo
$(MAKE) server-start
.PHONY: compile build run clean tinygo server-start go-start tinygo-start
$ make compile
GOOS=js GOARCH=wasm go build -o main.wasm
main.wasm 파일이 생성된 것을 확인할 수 있습니다.
Glue 코드 복사
$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
Go 런타임과 브라우저 간의 브리지
4. 웹 페이지 구성 - HTML/JS 로 WASM 모듈 로드 및 실행
HTML 템플릿 (index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go WASM 샌드박스</title>
<script src="wasm_exec.js"></script>
</head>
<body>
<div id="wasm-output" style="border: 2px solid blue; padding: 8rem;">
<!-- GO 에서 DOM 조작할 영역 -->
</div>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then((result) => {
go.run(result.instance);
console.log("Go WASM 샌드박스 실행 완료");
})
.catch((err) => {
console.error("WASM 초기화 실패", err);
});
</script>
</body>
</html>
5. 로컬 서버 실행 - 웹 서버를 통해 실제 활경 테스트
Go HTTP 서버
// server/server.go
package main
import (
"log"
"net/http"
)
func main() {
log.Println("Server is starting...")
go func() {
err := http.ListenAndServe(":12345", http.FileServer(http.Dir(".")))
if err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}()
log.Println("Server is running at <http://localhost:12345>")
// Keep the main function running
select {}
}
$ go run server.go
접속확인 localhost:8080
index.html 을 찾아서 실행하는 것을 확인할 수 있다.
6. 최적화 - TinyGo 를 활용한 바이너리 크기 축소
TinyGo 설치
brew tap tinygo-org/tools
brew install tinygo
컴파일
tinygo build --target wasm -o main.wasm main.go
cp $(tinygo env TINYGOROOT)/target/wasm_exec.js .
Output
- main.wasm (146KB)
- 15MB → 146KB 로 용량이 매우 줄어든 것을 확인할 수 있습니다. (약 97% 감소)
참고
소스코드 - https://github.com/mg5566/WebAssembly-Go
GitHub - mg5566/WebAssembly-Go: WebAssembly with Go
WebAssembly with Go. Contribute to mg5566/WebAssembly-Go development by creating an account on GitHub.
github.com
GeekNews WASM은 컨테이너를 대체하게 될 것 - https://news.hada.io/topic?id=19201