2P by GN⁺ | ★ favorite | 댓글 1개
  • Go 개발자가 여러 운영체제와 WebAssembly를 대상으로 즉시 모드 GUI를 만들 수 있게 하는 라이브러리임
  • Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD, WebAssembly까지 지원해 플랫폼 범위가 넓음
  • 의존성을 줄이고 창 관리·입력·GPU 그리기는 각 플랫폼 라이브러리를 활용하는 설계임
  • 렌더링은 OpenGL ES와 Direct3D 11 기반의 Pathfinder 벡터 렌더러를 포함하며, piet-gpu 기반 컴퓨트 셰이더 렌더러로 이동 중임
  • 텍스트와 도형을 텍스처로 굽지 않고 외곽선으로 렌더링해 애니메이션, 변환 그리기, 픽셀 해상도 독립성을 지원함

Gio의 목적과 지원 범위

  • Gio는 Go로 효율적이고 부드럽고 이식 가능한 GUI를 만들기 위한 라이브러리임
  • 지원 플랫폼은 Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD, WebAssembly
  • 빠른 시연용 WebAssembly 데모가 있으며, 실행하려면 WebAssembly 지원 브라우저가 필요함
  • 예제 소스는 Kitchen project에서 확인할 수 있음

설치와 학습 경로

  • Gio는 적은 의존성을 목표로 설계됨
  • 플랫폼별 설치 문서에서 필요한 의존성을 확인할 수 있음
  • 설치 후에는 Learn 문서와 Hello World로 시작할 수 있음
  • 쇼케이스에는 godcr, Tailscale, gotraceui, Sointu, Protonet 등이 포함됨

렌더링 기술

  • Gio는 즉시 모드 그래픽 패러다임의 유연성과 최신 2D 그래픽 기술을 결합함
  • 벡터 렌더러는 Pathfinder project를 기반으로 하며 OpenGL ESDirect3D 11 위에 구현됨
  • 렌더러는 piet-gpu 위에 구축되는 더 효율적인 컴퓨트 셰이더 기반 렌더러로 이동 중임
  • 텍스트와 도형은 텍스처 이미지로 미리 굽지 않고 외곽선만 사용해 렌더링
    • 효율적인 애니메이션을 지원함
    • 변환된 그리기에 적합함
    • 픽셀 해상도 독립성을 유지할 수 있음

후원 모델

댓글과 토론

Hacker News 의견들
  • 실제 사용해 보니 이걸로 진지한 복잡한 앱을 만들기는 불가능했음
    다른 플랫폼에서는 기본으로 주는 동영상, 지도, 리치 텍스트 같은 컴포넌트가 없고, 직접 추가할 명확하고 쉬운 길도 없음
    몇 달마다 API가 깨지고, 테마를 입히는 방법도 없음
    즉시 모드 그래픽은 복잡한 상태를 관리하기 전까지는 좋지만, 그때부터는 결국 자체 보존 모드 그래픽을 구현해야 해서 오래전에 해결된 문제를 다시 끌어옴
    piet-gpu 기반의 화려한 렌더러도 베지어 곡선 제어점만 입력으로 받아 전부 테셀레이션하는 방식이라 개념은 멋지지만, 실제 원을 그리려면 베지어 4개짜리 근사에 의존하게 됨
    Wasm은 컴파일 팀이 몇 년은 공학적으로 다듬어야 제품 수준이 될 개념 증명에 가깝고, 전체적으로 Go 개발자가 목록과 입력 필드 정도의 단순 UI를 만들 때는 괜찮아 보임

    • 이걸로 뭐든 만들 수 있고, v0.6에서 이런 문제들이 훨씬 덜 까다로워졌음
      Material Design 테마도 있고 라이트/다크 모드도 있음
      라이트/다크와 커스텀 테마를 가진 훌륭한 gioui 앱 예시는 https://github.com/chapar-rest/chapar
      Mac이나 Windows에서는 go run .이면 됨
      텍스트 커닝, 원호를 따라 흐르는 텍스트, RTL/LTR도 github.com/go-text/typesetting 덕분에 가능함
      달력 스피너나 다이어그램 같은 복잡한 위젯도 GitHub에 있지만, 이것들을 한데 모으는 노력이 부족함
      이런 것들이 모이면 더 많은 개발자가 뛰어들 유인이 충분히 커질 것 같음
    • 몇 달마다 깨지는 API 변경은 Google 코드에서 우울할 정도로 자주 보임
      사용자 코드가 깨지지 않는 것을 중시하는 문화가 없어 보임
    • Wasm이 중요하다면 Uno Platform은 꽤 괜찮게 지원하고, AvaloniaUI도 있음
      둘 다 지금은 안정적이고 비교적 풍부한 컨트롤 모음과 라이브러리를 갖췄음
  • 웹에서는 Flutter처럼 모든 것을 캔버스에 렌더링하는 듯한데, 이 방식은 접근성과 네이티브 느낌에서 문제가 있는 것으로 알려져 있음

    • 웹에서는 확실히 네이티브처럼 느껴지지도 않고 접근성도 좋지 않음
      라디오 버튼 사이를 탭으로 이동할 수 없고, macOS에서 CMD+A가 텍스트 필드 전체 선택을 하지 않으며 CTRL+A는 됨
    • 접근성을 위해 보이지 않는 DOM을 캔버스 옆에 같이 두는 변형이 있는지 궁금함
      가능은 할 것 같지만 작업량도 꽤 클 듯함
    • iPhone에서는 복사나 붙여넣기조차 전혀 안 됨
    • 이건 애초에 웹 앱 프레임워크가 아니라 네이티브 GUI 툴킷이고, 웹 백엔드를 가진 것에 가까움
  • 약간 주제에서 벗어나지만, 요즘 크로스플랫폼 모바일·웹 앱을 만드는 최선의 방법이 뭔지 궁금함
    비즈니스 로직과 UI를 모두 공유하는 경우든, 비즈니스 로직만 공유하는 경우든 말임
    gomobile, Rust, TypeScript 같은 선택지 중에서 고민했음
    한때는 TypeScript가 가장 이식성 좋은 기술처럼 보여 모든 비즈니스 로직에 쓰려 했지만, iOS에서 JavaScript를 괜찮은 성능으로 돌릴 좋은 방법이 없다는 걸 알게 됨

    • 지금은 아마 Flutter가 최선일 가능성이 큼: https://flutter.dev/
      UI를 네이티브로 작성하고 비즈니스 로직만 공유해도 괜찮다면 Kotlin도 선택지임: https://kotlinlang.org/docs/multiplatform.html#kotlin-multip...
      Compose를 쓰면 Kotlin으로 UI도 만들 수 있음: https://www.jetbrains.com/lp/compose-multiplatform/
      다만 iOS 지원은 아직 알파이고 웹은 “experimental”이라, 프레임워크가 발전하면서 코드를 바꿔야 하는 상황을 감수하기 싫다면 모든 플랫폼에서 이미 꽤 안정적인 Flutter가 맞음
      TypeScript와 React를 이미 안다면 React Native도 고려할 수 있지만, iOS나 다른 곳에서의 성능은 보장해서 말하긴 어려움: https://reactnative.dev/
    • 완성도가 필요한 앱은 네이티브 UI가 맞음
      모든 걸 해결해 준다고 약속하고 못 지킨 프레임워크를 너무 많이 겪었음
      처음에는 더 빨리 시작하지만, 곧 OS 업데이트 뒤에 FPS를 네이티브에 가깝게 만들거나 시스템 애니메이션을 더 비슷하게 맞추려고 핵심 라이브러리를 패치하게 됨
      UI를 다듬지 않을 때만 시간이 절약됨
      핵심 로직은 공유할 수 있음
      gomobile을 쓰고 있고 대체로 좋아하지만 런타임 오버헤드가 3MB라 웹에는 맞지 않음
      Kotlin Multiplatform은 좋아 보였지만 기본적인 라이브러리가 빠져 있었고, Kotlin Android에는 이미 있어서인지 멀티플랫폼 대응물을 만드는 사람이 별로 없었음
      Rust와 Mozilla의 언어 바인딩 계층도 좋아 보이지만 아직 써보진 않았음
    • React Native와 Expo 조합을 추천함
      Flutter도 나쁘진 않지만 웹은 캔버스에 렌더링해서 느낌과 접근성 면에서 별로임
      iOS도 Impeller 렌더링 엔진을 도입한 뒤에도 여전히 지연 문제가 있음
      Bluesky 클라이언트를 보면 지원 플랫폼 전반에서 성능이 괜찮고 단일 코드베이스를 씀
      https://github.com/bluesky-social/social-app
    • Uno(https://platform.uno)가 있음
      오픈소스이고 Android, iOS, Windows, Mac, Linux를 대상으로 함: https://platform.uno/platforms/
      C#을 쓰며, 각 플랫폼의 네이티브 UI 프레임워크로 뷰와 컨트롤을 자동 구현함
      Visual Studio, VS Code, Rider 같은 IDE 지원도 좋고 그 외 도구로도 제한되지 않음
      디자인 협업용 Figma 플러그인도 있음
    • 우리는 Tauri를 쓰지만, 내부 도구에만 쓴다는 점이 중요함
      소비자용 제품에도 좋은 도구인지는 모르겠음
      우리가 쓰는 용도에는 충분히 잘 맞는데, 주로 태양광 발전소 기술자용 도구이고, 열악한 인터넷 환경에서 크로스플랫폼 TypeScript를 쓰는 일이 작은 팀에는 너무 부담스러워졌기 때문임
  • gioui로 스트리밍 앱을 만들고 있는데, 매우 쉽고 업그레이드도 늘 수월함
    Go이고 핵심 개발자들이 변경을 꽤 진지하게 다루기 때문임
    웹 GUI가 필요할 때는 이 gioui 플러그인 시스템을 씀: https://github.com/gioui-plugins/gio-plugins
    WebView가 웹, 데스크톱, 모바일에서 동작해서 놀라움
    딥링크도 되어 이메일이나 Monike 알림 링크를 보내면 사용자 앱이 GUI의 정확한 위치로 열림
    모든 OS용 알림과 공유 확장도 있어서 정말 완성된 시스템에 가깝다고 봄
    모든 OS를 지원하는 게 어렵다는 점에는 공감하지만, 지금 세상은 다양성이 기본임
    여러 기술을 오가지 않고 Go만으로 이걸 다 할 수 있는 점이 좋음
    Go 백엔드는 항상 gio와 HTML 양쪽에서 동작하도록 작성함
    SEO나 동영상 재생이 필요하면 WebView에서 처리하고, gio 웹 쪽의 Google SEO도 만족시킬 수 있음
    Hugo에 Markdown을 넣어 Google SEO가 보게 만듦

  • Go 초보 입장에서 문서의 이 부분이 궁금함
    ops.Add(ColorOp{Color: red}) 대신 op.ColorOp{Color: red}.Add(ops)를 쓰는 이유가, Add 메서드가 인터페이스 타입 인자를 받지 않게 해서 호출 시 할당을 피하기 위한 것이며 Gio의 “zero allocation” 설계에서 핵심이라고 되어 있음
    왜 할당이 생기는지, 무엇이 할당되는지, 어떻게 절약되는지 알고 싶음

    • 이건 Go가 인터페이스로 동적 타입을 처리하는 방식과 구조체가 거기에 맞물리는 방식 때문임
      함수가 인터페이스 타입 인자를 받고 거기에 순수 구조체를 넘기면, Go는 그 주변에 래퍼를 만드는데 이게 인용문에서 말한 할당임
      이 래퍼는 타입/vtable 포인터와 구조체 데이터 포인터로 된 포인터 쌍임
      이렇게 하면 런타임 타입 추론과 암묵적 인터페이스 확장이 가능함
      즉 인터페이스 구현은 메서드를 구현하면 되는 것이고, ByteReader extends Reader처럼 명시적으로 타입을 적을 필요가 없음
      이 비용은 사용할 때만 내므로, 많은 빠른 경로 코드는 가능하면 구조체만 사용함
    • Go에서 v := interfaceType(concreteTypeValue)를 하면 낮은 수준에서는 대략 이런 일이 일어남
      dataPtr := &concreteTypeValue, typePtr := typeData[concreteType](), 그리고 데이터 포인터와 타입 포인터를 가진 인터페이스 값을 만드는 식임
      여기서 첫 줄이 할당인데, 내가 기억하는 규칙으로는 Go에서 포인터는 스택 값들을 가리키지 않으므로 concreteTypeValue가 힙에 할당되어야 하기 때문임
      포인터가 스택을 가리키지 않는다는 규칙은 고루틴 스택이 동적으로 커지기 쉽게 만들기 위한 것임
      https://go.dev/doc/faq#stack_or_heap 참고
    • 첫 번째 방식에서는 ColorOp{Color: red}박싱되어 힙에 할당되어야 함
      ops.Add가 일반적으로 구체 타입 값이 아니라 특정 인터페이스를 구현한 값에 대한 굵은 포인터를 받기 때문임
    • 질문 자체에는 다른 답들이 답했지만, 별개로 op.ColorOp{Color: red}.Add(ops)는 읽기가 이상함
      내게는 “op.ColorOp{Color: red}의 결과에 ops를 더한다”처럼 읽힘
      그래서 함수 이름을 AddTo로 짓고 싶음: op.ColorOp{Color: red}.AddTo(ops)
      여전히 관례적이진 않지만, 적어도 함수 인자가 수정된다는 신호는 줌
    • https://stackoverflow.com/questions/39492539/go-implicit-con...
  • Windows 10과 Chrome을 쓰는 꽤 평범한 PC에서 첫 페이지의 WASM 데모가 텍스트가 있어야 할 곳에 검은 사각형만 렌더링하는 게 흥미로움
    Android 휴대폰의 Chrome에서는 제대로 렌더링됐음

    • 나만 그런 게 아니었음
      게다가 엄청 느리게 실행됐음
    • Windows 11의 Edge에서도 똑같음
  • Go로 Fyne을 써서 작은 앱을 만들었는데 다시는 안 쓸 생각임
    Gio와 Fyne 모두 Flutter가 제공하는 다듬어진 완성도와 기능이 크게 부족함
    핵심 로직은 Golang으로 만들고 Android 앱으로 감싸기로 했는데, GUI가 2003년에서 나온 것 같고 고칠 방법도 제한적이었음

    • 다른 선택지로 Wails가 있음
      모든 로직은 Go로 쓰고 UI는 HTML로 작성할 수 있으며, 웹 프레임워크를 써도 되고 안 써도 됨
      Electron과 비슷하지만 Chrome을 같이 배포하지 않고 시스템 웹 뷰어를 쓰기 때문에 더 가벼움
      [1] https://github.com/wailsapp/wails
    • 흥미롭다
      Android 앱으로 어떻게 감쌌는지 설명해 줄 수 있으면 좋겠음
  • 왜 이런 크로스플랫폼 GUI들은 전부 50년 전에 디자인된 것처럼 보일까

    • 1974년에 도대체 뭘 쓰고 있었길래 저게 그때처럼 보였는지 알고 싶음
  • 데모가 나한테는 동작하지 않음
    Win 11의 Chromium에서 버튼 몇 개는 보이지만 대부분이 전부 검은색임

  • Fyne과 달리 이 라이브러리는 내가 처음 던진 테스트인 CJK 텍스트 렌더링을 통과했다는 점이 좋은 신호임
    Fyne은 모든 것을 렌더링할 커스텀 폰트 하나를 주지 않으면 이걸 못 함
    전 세계에서 흔히 쓰이는 모든 문자 체계는 물론 이모지까지 만족스럽게 포함하는 폰트 하나를 찾는 건 행운을 빌어야 할 일임
    그래서 사용자 생성 콘텐츠, 웹 콘텐츠, 현지화 가능성이 조금이라도 있는 걸 만들 때 Fyne은 내게 바로 탈락임

    • 이런 면에서는 Noto Sans가 꽤 좋음