본문 바로가기
카테고리 없음

WASM with Go

by a little good developer 2025. 2. 27.

최근 기술 커뮤니티에서 “WASM은 컨테이너를 대체하게 될 것!" 이라는 제목의 글을 읽었습니다.

흥미로운 주제에 매료된 저는 직접 WASM 실습을 진행해보기로 했습니다. 특히, 최근 AI 도구의 발전과 함께 생산성을 극대화할 수 있는 방법을 모색하던 중, Perplexity 의 Deep Search 기능이 새로 출시되어 Deep Search 기능을 활용하여 관련 자료를 조사하고 실습 과정을 디자인했습니다.

본 글에서는 WASM 이 Docker 컨테이너를 대체할 가능성을 탐구하기 위해 진행한 실습 과정과 그 결과를 공유합니다.

기술적 통찰력을 제공하는 동시에, 새로운 도구와 기술을 활용하는 방법에 대한 영감을 드릴 수 있기를 바랍니다.

 

Perplexity 의 Deep Search 를 활용하여 만든 생성된 실습 가이드입니다.

전체 프로세스 요약

  1. 개발 환경 구성 - Go 설치 및 웹 어셈블리 컴파일 도구 준비
  2. Go 코드 작성 - DOM 조작과 브라우저 API 호출 기능 구현
  3. WASM 컴파일 - Go 코드를 '.wasm' 바이너리로 변환
  4. 웹 페이지 구성 - HTML/JS 로 WASM 모듈 로드 및 실행
  5. 로컬 서버 실행 - 웹 서버를 통해 실제 활경 테스트
  6. 최적화 - 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 을 찾아서 실행하는 것을 확인할 수 있다.

Alert 실행
화면 확인

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