4P by GN⁺ | ★ favorite | 댓글 1개
  • Spot은 Go에서 크로스플랫폼 데스크톱 GUI를 만들기 위한 단순한 반응형 툴킷이며, 가능한 경우 네이티브 위젯을 사용하고 플랫폼별 API를 일관되게 제공함
  • 애플리케이션 상태가 바뀌면 불변 컴포넌트 트리를 다시 만들고 이전 상태와 비교해 업데이트할 UI 컨트롤을 결정하는 방식으로 동작함
  • 현재 백엔드는 macOS의 Cocoa와 그 외 플랫폼의 FLTK 기반 구현이며, macOS에서도 선택적으로 FLTK를 사용할 수 있음
  • spot은 반응형 모델과 렌더링을 제공하는 백엔드 비의존 코어 패키지이고, spot/ui는 미리 만들어진 크로스플랫폼 GUI 컨트롤 모음임
  • 자동 레이아웃, 여러 창, 모달 대화상자, 크기 조절 창, 메뉴 바, 커스텀 위젯, 네이티브 위젯 접근, 드래그 앤 드롭, 국제화는 아직 제공하지 않음

Spot의 목적과 기본 모델

  • Spot은 Go용 반응형 GUI 툴킷으로, 네이티브 위젯을 가능한 곳에서 사용하면서 여러 플랫폼에서 일관된 API를 제공하도록 설계됨
  • 프로젝트에 단순 의존성으로 추가해 Go 코드만 작성하면, 추가 도구나 코드 생성 없이 자체 포함 네이티브 GUI 바이너리를 만들 수 있음
  • 예제는 ui.Init(), spot.MountFn(...), ui.Run() 흐름으로 창과 버튼을 만들고, spot.UseState[int](<https://github.com/roblillack/ctx, 0>)로 클릭 횟수 상태를 관리함
  • 버튼 클릭 핸들러는 setCounter(counter + 1)를 호출하고, 상태가 바뀌면 버튼 제목이 "Clicked %d times!" 형식으로 바뀜

반응형 업데이트 방식

  • Spot에서 reactive는 애플리케이션 상태가 바뀔 때 UI가 자동으로 갱신된다는 뜻임
  • 상태 변경 시 불변 컴포넌트 트리를 다시 만들고, 이전 상태와 빠르게 비교해 어떤 UI 컨트롤을 업데이트할지 결정함
  • 웹에서 이 아이디어는 흔히 virtual DOM이라고 불리며, Spot은 이 개념을 Go 데스크톱 환경에 가져와 React와 비슷한 GUI 라이브러리를 구현하는 실험에서 시작됨
  • 개발자는 UI를 수동으로 업데이트하는 대신, 부작용 없는 렌더링 함수와 UseState 같은 훅으로 애플리케이션 로직과 상태를 관리함

백엔드와 패키지 구성

  • Spot은 컴파일 시 실행 플랫폼에 맞는 백엔드를 자동 선택함
  • 현재 제공되는 백엔드는 두 가지임
  • macOS에서는 Cocoa 백엔드를 사용하고, 그 외 플랫폼에서는 FLTK 기반 백엔드를 사용함
  • macOS에서도 선택적으로 FLTK를 사용할 수 있으며, Windows 지원 개선은 향후 계획으로 남아 있음
  • spot반응형 모델과 렌더링 기능을 제공하는 코어 패키지이며, spot.Control 인터페이스를 구현하는 어떤 컨트롤 집합과도 사용할 수 있음
  • spot/ui는 Spot과 함께 사용할 수 있는 미리 만들어진 크로스플랫폼 GUI 컨트롤 패키지임

컴포넌트, 컨트롤, 훅

  • React처럼 커스텀 훅을 만들 수 있음
    • 첫 번째 인자로 *spot.RenderContext를 받는 함수를 만들고 spot.UseState, spot.UseEffect 등을 호출해 Spot 생명주기에 연결함
    • 관례적으로 함수 이름은 Use… 접두사를 붙임
  • 커스텀 컴포넌트는 spot.Component 인터페이스를 구현하는 구조체로 만들 수 있음
    • 이 인터페이스는 Render(ctx *spot.RenderContext) spot.Component 단일 메서드를 가짐
    • 이렇게 만든 컴포넌트는 내장 컴포넌트와 같은 방식으로 사용할 수 있음
  • Spot에서 component는 비즈니스 로직과 상태를 담는 논리 단위임
    • 컴포넌트는 다른 컴포넌트로 구성되며, 최종적으로 하나 이상의 컨트롤로 렌더링됨
  • control은 UI 트리에 마운트되는 특수한 컴포넌트이며, 화면의 시각 요소를 나타냄
    • 일반적으로 버튼, 라벨, 텍스트 입력처럼 GUI 백엔드의 네이티브 구현을 기반으로 함
  • 제공된 위젯 라이브러리와 완전히 다른 위젯 라이브러리도 사용할 수 있음
    • spot.Component 인터페이스를 구현하고 네이티브 위젯을 관리하는 구조체를 만들면 됨
  • spot/ui를 Cocoa나 FLTK 이외의 백엔드와 함께 쓰는 것은 현재 지원되지 않음

렌더링 생명주기 용어

  • Make: spot.Component 인터페이스를 구현하는 구조체 인스턴스를 만들거나, 렌더 함수와 함께 spot.Make를 호출해 새 컴포넌트 인스턴스를 만드는 과정
  • Render: 컴포넌트 상태를 구성 요소에 적용하고 다른 컴포넌트 인스턴스를 반환하는 과정
  • Build: 컴포넌트를 재귀적으로 렌더링해 컨트롤 트리를 만드는 과정
    • spot.Build에 컴포넌트 인스턴스를 넘기거나, spot.BuildFn에 렌더 함수를 넘겨 실행함
  • Mount: 가상 컨트롤 트리에서 실제 UI 컨트롤을 만드는 과정
    • 트리 노드의 Mount를 호출하거나, spot.Mount, spot.MountFn을 사용할 수 있음
  • Update: 마운트된 컨트롤 트리를 업데이트하는 과정
    • 트리 노드의 Update를 호출해 수행함

현재 제공하지 않는 기능

  • Spot은 현재 다음 기능을 제공하지 않음
    • 자동 레이아웃

      • 여러 창
      • 모달 대화상자
      • 크기 조절 가능한 창
      • 메뉴 바
      • 커스텀 위젯
      • 네이티브 위젯 접근
      • 드래그 앤 드롭
      • 국제화

지원 UI 컨트롤

  • Spot은 버튼, 라벨, 텍스트 입력, 슬라이더, 드롭다운 등 여러 UI 컨트롤을 기본 제공함
  • 지원 상태 표기는 ❓ 미구현, 🚧 작업 중, ⚠️ 부분 구현, ✅ 완료로 나뉨
  • 완료된 주요 컨트롤은 다음과 같음
    • Button: 단순 액션 실행 버튼, Fl_ButtonNSButton 사용
    • Checkbox: 두 선택지 중 하나를 고르는 컨트롤, Fl_Check_ButtonNSButton 사용
    • Dropdown: 여러 항목 중 하나를 고르는 드롭다운, Fl_ChoiceNSComboBox 사용
    • Image: 비트맵 이미지 표시 컨트롤, Fl_Box와 커스텀 NSButton 사용
    • Label: 편집 불가 텍스트 라벨, Fl_BoxNSTextField 사용
    • ListBox: 단일 또는 다중 선택 목록 컨트롤, Fl_Select_Browser/Fl_Multi_BrowserNSTableView 사용
    • ProgressBar: 장기 작업 진행률 표시, Fl_ProgressNSProgressIndicator 사용
    • Slider: 수평 슬라이더 입력, Fl_SliderNSSlider 사용
    • Spinner: 위/아래 버튼이 있는 숫자 입력, Fl_SpinnerNSTextField+NSStepper 사용
    • TextField: 한 줄 텍스트 입력, Fl_InputNSTextField 사용
    • TextEditor: 여러 줄 텍스트 편집, Fl_Text_EditorNSTextView 사용
    • Window: 최상위 창 컨트롤, Fl_WindowNSWindow 사용
  • 부분 구현 또는 미구현 컨트롤도 있음
    • Dial: 원형 상태 컨트롤이며 ⚠️ 부분 구현 상태
    • ComboBox: 텍스트 입력이 결합된 드롭다운 메뉴이며 아직 시작되지 않음
  • 잠재적 향후 백엔드 후보로 네이티브 Windows 컨트롤 라이브러리 https://github.com/rodrigocfd/windigo가 제시됨

댓글과 토론

Hacker News 의견들
  • 이건 꼭 봐야겠음. Go로 내부 개발 도구를 만드는 단순한 방법을 찾고 있었는데, 사실상 버튼과 텍스트 필드가 있는 폼 수준임
    Gio도 써봤지만 이해하기가 어려웠고, 지금은 wails를 쓰는데 훨씬 마음에 듦. 이 프로젝트도 흥미롭고 살펴볼 가치가 있어 보임

  • “크로스 플랫폼: FLTK[1]와 Cocoa[2]를 활용해 Spot은 Mac, Linux, BSD에서 동작하며, 향후 네이티브 Windows 지원 계획이 있음”이라는 방향은 진지하게 줄이는 걸 권함
    배운 점은 남겨서 나중의 유연성은 확보하더라도, 먼저 하나의 툴킷에서 잘하는 게 좋음. GUI 툴킷, GUI 바인딩, GUI 자체가 원래 세부사항에 빠져 죽기 쉬운데, 여러 기반 툴킷의 세부사항까지 자청해서 떠안으면 결국 어떤 툴킷도 제대로 못 하게 될 가능성이 큼
    “처음 90%가 작업의 90%이고, 남은 10%가 또 다른 90%”라는 말이 있지만, GUI에서는 그마저 낙관적으로 보일 정도임. 처음 10%가 작업의 90%이고, 다음 10%는 그 10배, 그다음 10%도 또 10배가 됨. 크로스 플랫폼을 시도하면 목이 조일 수 있음
    당장 이 주장에 동의하리라고 기대하진 않지만, 나중에 세 툴킷이 리치 텍스트 처리 같은 걸 서로 모순되는 세 방식으로 강제하는 상황을 마주하면, 가장 잘 지원되거나 가장 인기 있는 기반 툴킷만 남기고 나머지를 버려도 된다고 스스로 허락하길 바람

  • Go에서 이런 걸 한동안 찾고 있었음. Go는 빌드 과정이 단순해서 크로스 플랫폼 UI에서 훌륭한 개발자 경험을 제공할 기회가 크다고 봄
    경험상 크로스 플랫폼 개발 고통의 절반은 빌드 복잡도 관리인데, Go는 그걸 거의 없애줌
    다만 네이티브 컨트롤의 기본 크기가 플랫폼마다 다른데, 크로스 플랫폼 레이아웃을 어떻게 해결할지 궁금함. 이 부분은 다른 크로스 플랫폼 툴킷에서도 아주 잘 풀린 걸 보지 못했음. 그래도 행운을 빎

  • 몇 년 전에 이런 걸 찾고 있었음. 다만 Windows 지원도 필요했음. 결국 wxWidgets를 쓰려고 C++로 전환했고, 작은 자체 포함 바이너리를 얻을 수 있었음

    • go-fltk는 Windows에서도 빌드되고 실행되며, 실제로 꽤 잘 동작함
      네이티브 툴킷인데 FLTK가 브라우저처럼 Ctrl-+와 Ctrl+-로 앱 전체 확대/축소를 지원하는 걸 보고 인상적이었음. 그리고 https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#wid... 덕분에 FLTK를 얼마나 “네이티브”처럼 보이게 만들 수 있는지에 대한 인상이 좋아졌음
      관련해서 최근 GoVCL https://z-kit.cc/en/도 발견했고 한번 써보고 싶음
    • WxWindows를 아주 좋아하지만, 요즘은 Go에 너무 깊이 들어와 있음
      자체 포함 Spot “Hello World”는 내 Mac에서 2.3MiB임. 예쁘진 않지만 내겐 충분히 동작함
    • wxGo가 있긴 한데, 아쉽게도 프로젝트가 유지보수되지 않고 있음
  • 사용자에게 표시되는 컨트롤을 직접 갱신하는 방식과 비교해, 가상 컨트롤 트리 접근의 장점이 뭔지 궁금함

    • 복잡한 상황, 예를 들어 사용자가 UI와 상호작용하는 동시에 백그라운드에서 오래 걸리는 작업도 UI 상태를 바꾸는 경우에는 상태 관리가 금방 감당하기 어려워짐
      여기저기 콜백 코드를 써야 하고, 각 콜백은 다른 모든 활동의 현재 상태를 조심스럽게 검사한 뒤 수십 개 위젯을 갱신해야 할 수 있음
      반응형 접근에서는 주어진 상태에 대한 인터페이스를 설명하는 단일 렌더링 함수를 작성하면, 프레임워크가 언제 호출할지와 어떤 입력을 줄지를 처리함. 이게 훨씬 이해하기 쉽고, React를 써본 뒤에는 되돌아가기 어렵다 보니 Go에서도 비슷한 게 가능한지 실험하게 됨
  • 좋아 보임. README에 지원 플랫폼을 적어줄 수 있을지 궁금함
    Windows, Linux, macOS, *BSD, Android, iOS, Web, Tizen 같은 정보는 꽤 흥미로울 것 같음
    Flutter 문서처럼 적어도 좋을 듯함: https://docs.flutter.dev/reference/supported-platforms

  • 노력은 박수칠 만하지만, Windows 지원 없는 크로스 플랫폼이라니?

    • Windows에서는 FLTK를 쓰는 것 같고, 단지 네이티브 지원이 아직 없는 듯함
    • WSL을 쓰면 됨
  • 3주 전에 이걸 알았더라면, 혹은 커밋 이력상 그때 이미 존재했더라면 좋았을 텐데. 오래전부터 Go로 포팅된 React나 Go용 React 유사 프레임워크가 있으면 개발 경험이 엄청날 거라고 말해왔고, 이건 딱 맞아 보임
    React.lua로 깨닫기 전까지는 React.js를 꽤 싫어했음

    • 나도 같은 문제가 있었음. React에서 하던 컴포넌트 합성 방식이 정말 좋았고, 되돌아가기 어려웠음
      결국 Go 표준 html/template만으로 비슷한 걸 하는 방법을 찾았고, 구현 내용을 여기 적었음: https://www.sheshbabu.com/posts/react-like-composition-using...
    • 3주 전에는 대신 뭘 썼는지 궁금함
  • 데스크톱으로 출시하면 보통 웹 버전도 원하게 된다는 게 큰 문제임. 운영체제와 많이 상호작용하는 아주 틈새 앱이 아니라면 특히 그렇다
    또는 모바일까지 포함해 여러 플랫폼을 대상으로 하는 무언가가 필요함
    꽤 오래 찾아봤지만 가장 가까운 건 QtReact Native였고, 둘 다 여러 이유로 고통스러운 선택지였음

  • FLTK는 Windows를 지원함. 다른 해결책을 쓰려는 계획이라서 아직 Windows를 지원하지 않는 건지 궁금함

    • 적절한 C 컴파일러 등 환경이 갖춰져 있다면 Spot은 Windows에서도 변경 없이 동작하고 FLTK 백엔드를 선택해야 함
      다만 내 목표는 우선순위는 낮지만 Win32 기반 백엔드를 구현하는 것이고, 첫 단계는 끝났음: https://github.com/roblillack/spot/pull/4