- Dagger Cloud v3를 출시하면서 새로운 UI는 WebAssembly(WASM)와 Go로 작성
- Go는 일반적으로 웹 UI 개발에 사용되지 않지만, "코드베이스 통합 및 성능 최적화"를 위해 이 방식을 선택
- 이 글에서는 "선택의 배경, 구현 과정에서의 도전 과제 및 최종 결과"를 공유
기존 문제: 두 개의 코드베이스로 인한 비효율
- Dagger는 DAG(Directed Acyclic Graph) 기반으로 동작하며, 이를 TUI(터미널 UI) 와 웹 대시보드(Dagger Cloud) 에서 시각화 제공
- 기존에 TUI는 Go, 웹 UI는 React/TypeScript로 구현됨
- 하지만 두 UI 간의 동기화가 어려웠고, 특히 웹 UI는 대량의 데이터를 실시간으로 처리하는 데 성능 문제가 발생함
- 복잡한 OpenTelemetry 이벤트 스트림(수십만 개의 스팬)을 처리하면서 React UI의 성능 저하 및 속도 문제가 두드러짐
- 동일한 기능을 두 번 구현해야 했으며, 이는 작은 팀에게 큰 개발 부담이었음
- 따라서, 코드베이스 통합 및 성능 최적화를 목표로 새로운 접근 방식을 고려
선택한 해결책: Go + WebAssembly
-
Go로 코드베이스 통합
- TUI가 이미 Go로 구현되어 있어 웹 UI도 Go로 구현하면 코드 재사용 가능
- 팀 내에 Go 개발자가 많아 팀 생산성 향상 및 유지보수 용이
-
WebAssembly(WASM) 활용
- Go 코드를 직접 브라우저에서 실행할 수 있도록 WebAssembly 도입
- 그러나 Go + WASM은 아직 성숙한 생태계를 갖추지 못해 몇 가지 도전 과제가 존재:
- 컴포넌트 라이브러리 부족 → UI를 직접 구현해야 함
- 브라우저의 WASM 메모리 제한 (2GB) → 대량 데이터를 다룰 때 최적화 필요
- 하지만, 메모리 최적화는 TUI와 Web UI 모두에 이점을 제공할 수 있음
프로젝트 리스크 최소화 전략
-
Go-app 프레임워크 사용
- PWA(Progressive Web App) 개발을 위한 Go 기반 프레임워크 선택
- React와 유사한 컴포넌트 기반 모델을 제공하여 전환이 용이함
-
프로토타입 제작 및 검증
- 기존 UI를 최대한 Go-app으로 재구현하며 주요 이슈 파악
- WASM은 이미 오픈 표준으로 문서화되어 있고, 주요 질문은 Go-app 문서에서 해결 가능
- 가장 큰 문제는 메모리 사용량 제한, 이를 해결하기 위한 설계 및 최적화가 필요
프로토타입 → 프로덕션 전환 과정
성능 최적화 전략
-
대규모 로그 렌더링 최적화
- 20만 줄 이상의 로그 데이터를 처리할 때 렌더링 성능 개선 필요
- 이를 위해 가상 터미널 렌더링 라이브러리(midterm) 최적화,
→ TUI와 Web UI 모두의 성능 향상
-
JSON 파싱 속도 개선
- Go WASM은 JSON 파싱 속도가 느림 → WebSocket 기반 스마트 백엔드 설계
- Go의
encoding/gob
을 사용해 데이터 전송 최적화
-
WASM 파일 크기 최적화
- 초기 WASM 파일 크기: 32MB
- Brotli 압축 적용 → 4.6MB로 감소
- CDN에서 압축을 처리하기 어려워 빌드 프로세스에서 직접 압축 적용
기타 개선 사항
- 예상했던 메모리 문제 외에는 대부분의 걱정이 기우였음
- UI 컴포넌트 작성이 어렵지 않았으며, 다른 서비스(Tailwind, Auth0 등)와의 통합도 문제없음
- WebAssembly에서 NPM 패키지 활용 가능 → JavaScript와의 상호 운용성 확보
- Go-app이 React보다 컴포넌트 업데이트 방식이 유연하여 최적화 자유도가 높음
- Go 프로파일링 도구(pprof) 및 브라우저 내장 프로파일러로 성능 분석 가능
- PWA 지원 덕분에 데스크탑/모바일 앱으로 실행 가능, 브라우저를 열지 않고도 앱을 실행할 수 있음
전환 후 얻은 이점
-
UI 일관성 향상
- TUI와 웹 UI가 동일한 코드베이스를 사용하면서 더 일관된 UX 제공
-
성능 및 메모리 사용량 개선
- 대량의 데이터를 다룰 때 렌더링 속도 개선 및 메모리 사용량 감소
-
팀 생산성 향상
- 기존에는 Web UI와 TUI를 각각 최적화해야 했으나,
이제는 한 번의 최적화로 두 인터페이스에 동시에 적용 가능 - 덕분에 새로운 기능 개발에 더 집중 가능
- 기존에는 Web UI와 TUI를 각각 최적화해야 했으나,
Go + WASM으로 전환해야 할까?
-
일반적으로는 추천하지 않음, 하지만 특정 조건에서는 유용할 수 있음:
- Go 개발자가 많은 팀
- TypeScript/React가 성능 한계를 보이는 복잡한 UI
- TUI 및 Web UI 간 코드 공유 필요
- 개발 속도를 극대화해야 하는 환경
-
위 조건에 부합한다면 Go + WASM이 좋은 대안이 될 수 있음
하지만, 대부분의 경우 기존 웹 기술(React, TypeScript 등)이 더 적합
Hacker News 의견
-
작은 팀으로서 빠르게 배포해야 한다는 의견이 있음
- 하지만 한 달 가까이 프로토타입을 만들고, Go-app UI 컴포넌트를 직접 작성해야 했으며, Go WASM이 JSON을 파싱하는 데 느리다는 문제로 인해 아키텍처가 크게 변경되었다고 함
- 이는 이력서 중심의 프로그래밍처럼 들린다는 의견이 있음
-
강력한 Go 엔지니어 팀과 TypeScript/React로는 확장하기 어려운 복잡한 UI가 있었음
- 데모를 통해 웹 프론트엔드에 대한 경험이 부족한 팀이라는 인상을 받았음
- 회원가입 페이지가 화면에 맞지 않고, 대시보드 내 클릭 시 전체 화면에 스피너가 나타나며 페이지가 다시 로드됨
- 아이콘이 로딩 중일 때 alt-text가 표시됨
- React가 확장되지 않았다는 점이 다행이라는 의견이 있음
-
"캔버스로 렌더링"하는 프레임워크일까 걱정했지만, 그렇지 않음
- WASM과 DOM의 상호 운용성이 충분히 빨라졌다는 점이 긍정적임
- 그러나 32MB 바이너리는 큰 단점임
-
그들은 <a href="https://go-app.dev/" rel="nofollow">https://go-app.dev/</a>를 사용하여 프론트엔드를 만들기로 결정했음
- Go-app은 Golang과 WebAssembly를 사용하여 PWA를 구축하는 패키지임
-
Go는 이런 종류의 작업에 적합하지 않다는 의견이 있음
- Rust를 사용하여 더 나은 결과를 얻었다고 함
- Rust로 만든 wasm 바이너리는 평균 240kb이며, 전송 시 잘 압축됨
-
몇 달 후에 더 무거운 스택에서 더 성능이 좋지만 다소 특이한 스택으로의 전환이 긍정적인 결과를 가져오는지에 대한 후속 보고가 흥미로울 것임
- 이런 프로젝트에 프론트엔드 개발자를 고용하는 것이 React 개발자를 찾는 것보다 쉽지 않을 것임
-
go-app의 창시자가 이 게시물을 발견하고 놀랐으며, 제품의 성공을 기원함
-
Go WASM이 JSON을 파싱하는 데 느려서 아키텍처 변경과 WebSockets를 통한 점진적 데이터 로딩을 위한 "스마트 백엔드"를 만들게 되었음
- gob 데이터의 보안 문제에 대한 우려가 있음
-
WASM은 특정한 틈새 용도에 적합하지만 일반적인 웹 앱을 만드는 데는 부적절하다는 의견이 있음
- go-app으로 만든 앱이 3.6MB의 WASM 코드를 로드함
- Blazor도 마찬가지로 간단한 앱이라도 초기 로드에 최소 1MB 이상이 필요함
-
모든 구성 요소(프론트엔드/백엔드/앱)를 동일한 언어로 사용하는 것이 큰 가치가 있다고 생각함
- 반대로 "Typescript everywhere" 접근 방식을 사용하고 있으며, 프론트엔드에 React, 백엔드에 NodeJS, 앱에 Capacitor를 사용함