C# 코드로 UI 라이브러리를 다시 만들기 시작했습니다

빠른 피드백 대응 다시 한번 감사드립니다

1개의 좋아요

GPU 기반이어도 가상화는 필요했습니다

Duxel은 GPU 기반으로 빠르게 화면을 그리고, 매 프레임 UI를 구성하기 때문에 처음에는 별도의 Virtual List가 필요하지 않을 것이라고 생각했습니다.

하지만 항목이 많아지면 화면에 그리는 비용뿐 아니라, 각 항목의 레이아웃 계산과 위젯 명령 생성, 이벤트 상태 처리도 함께 증가했습니다. 보이지 않는 항목까지 매 프레임 처리하는 방식으로는 한계가 있었습니다.

그래서 WPF에서처럼 생성된 컨트롤을 재활용하는 방식 대신, 현재 화면에 보이는 항목과 위아래 일정 개수의 항목만 투영하도록 구현했습니다.

현재 보이는 항목
+ 위쪽 N개
+ 아래쪽 N개
→ 레이아웃 및 위젯 명령 생성
→ Viewport 영역으로 Clip

스크롤 위치가 변경되면 이 범위도 함께 이동합니다.

Immediate Mode에서는 유지할 네이티브 컨트롤 자체가 없기 때문에, 컨트롤 재활용보다는 매 프레임 처리할 데이터와 명령의 범위를 제한하는 방식 이 더 자연스러웠습니다.

개선 전

개선 후

1개의 좋아요

Duxel에서 발견된 Router 문제

같은 Router를 사용했지만 WPF와 Duxel의 상태 변경 감지 방식이 달라, Duxel에서는 화면 전환 후 State가 변경되어도 화면이 갱신되지 않는 문제가 있었습니다.

원인은 Router가 라우트 키를 적용하는 과정에서 실제 Component가 아닌 가상 요소 ID가 중간에 만들어졌고, 이로 인해 Component의 부모 관계가 완전히 연결되지 않았기 때문입니다.

WPF는 갱신 대상을 느슨하게 확인하고, 필요하면 전체 화면을 다시 그리는 방식이라 문제가 드러나지 않았습니다. 반면 Duxel은 실제 Component 부모 관계를 엄격하게 확인하면서 해당 갱신을 무시했습니다.

Duxel 연동을 통해 기존 Router의 숨겨진 문제가 발견된 셈이며, renderer에 관계없이 상태 변경과 화면 갱신이 정상적으로 이어지도록 Router 구조를 보완했습니다.

Router

AnimatedRouter

1개의 좋아요

DevTools를 Nuri.Duxel로 다시 작성했습니다

v0.3.0부터 Duxel 백엔드를 함께 배포하기에 앞서, 실제 프로그램에서 어느 정도 사용할 수 있는지 확인하기 위해 DevTools를 Nuri.Duxel 기반으로 다시 작성했습니다.

단순한 샘플이 아니라 기존 DevTools를 옮겨보면서 Duxel과 Nuri.Duxel 양쪽에서 개선이 필요한 부분도 확인할 수 있었습니다.

Duxel 샘플 실행 시 기본적으로 Duxel 전용 아이콘이 표시되는 부분은 애플리케이션별 아이콘을 사용할 수 있도록 개선을 요청했습니다.

Nuri.Duxel에서는 Div의 일부 그리기 속성이 제대로 반영되지 않는 문제도 발견했습니다.

  • Background
  • BorderThickness
  • 테두리와 배경 관련 일부 속성

이번 DevTools 재작성은 단순한 UI 변경이라기보다, v0.3.0부터 Duxel을 실제 배포 대상에 포함하기 전 진행한 시범 적용에 가깝습니다.

샘플 수준에서는 보이지 않던 문제를 실제 프로그램을 옮기는 과정에서 확인했고, Duxel과 Nuri.Duxel을 함께 보완할 수 있었습니다.

1개의 좋아요

Nuri.Duxel Preview 성능 개선

Nuri.Duxel 기반 Preview를 실제로 사용해보니, 기대했던 것보다 초기 렌더링과 다시 불러오는 속도가 느리게 느껴졌습니다.

막연히 최적화하기보다 먼저 병목을 확인할 수 있도록 Duxel 성능 계측 API를 추가했습니다.

프레임별로 다음 항목을 선택적으로 기록할 수 있습니다.

  • Runtime 처리 시간
  • VirtualEntry Projection 시간
  • Commit 및 Effect 처리 시간
  • 전체 프레임 소요 시간
  • Patch와 입력 이벤트 수
  • 창 크기 변경 후 Projection까지의 지연

초기 화면이 완성되기 전에 창이 먼저 표시되던 부분도 개선했습니다. 첫 Projection과 Effect 처리가 끝날 때까지 창을 숨기고, 준비 신호가 오지 않을 경우에는 5초 후 자동으로 표시하도록 했습니다.

Virtual DOM에서는 빈 Property, Event, Animation 컬렉션을 공유하고, Leaf 노드의 불필요한 Children 할당을 제거했습니다. 문서에 사용한 1,000개 Text 테스트 기준으로는 할당량이 약 12.2% 감소했습니다.

Preview의 코드 변경 반영 속도도 함께 개선했습니다.

저장 감지 시간을 500ms에서 150ms로 줄였고, Roslyn Compilation이 프로젝트의 OutputType과 빌드 출력 폴더의 Dependency Assembly를 반영하도록 수정해 dotnet build로 다시 넘어가는 경우를 줄였습니다.

이번 작업을 통해 단순히 “느리다”는 느낌에서 끝내지 않고, 순수 Duxel 렌더링과 Nuri Projection, 전체 첫 프레임을 각각 나누어 측정할 수 있는 기반도 마련했습니다.

2개의 좋아요

Row와 Column은 결국 같은 의미였습니다

Flutter나 React 계열에서 RowColumn 을 처음 보면 XAML의 Grid.Row , Grid.Column 과 방향이 반대인 것처럼 느껴질 수 있습니다.

하지만 조금 더 깊게 보면 같은 의미였습니다.

Row 는 하나의 입니다. 하나의 행 안에 자식들을 배치하므로 요소가 좌에서 우로 놓입니다.

Column 은 하나의 입니다. 하나의 열 안에 자식들을 배치하므로 요소가 위에서 아래로 놓입니다.

Row    → 하나의 행 → 좌에서 우
Column → 하나의 열 → 위에서 아래

XAML의 RowDefinition , ColumnDefinition 도 의미는 같습니다.

다만 Definition 은 Grid 안에 행이나 열 하나를 정의한다는 뜻이고, Rows , Columns 는 여러 행과 열을 정의한다는 복수의 의미가 담겨 있습니다.

Row              → 하나의 행에 자식을 배치
Rows              → 여러 행을 정의
RowDefinition     → 행 하나의 크기와 규칙을 정의

Column            → 하나의 열에 자식을 배치
Columns           → 여러 열을 정의
ColumnDefinition  → 열 하나의 크기와 규칙을 정의

겉으로는 서로 다른 방향을 표현하는 것처럼 보였지만, 결국 Row 는 행이고 Column 은 열이라는 같은 개념을 서로 다른 문맥에서 사용하고 있었던 셈입니다.

이제야 왜 계속 헷갈렸는지, 그리고 왜 둘 다 맞는 표현인지 이해하게 됐습니다.

1개의 좋아요

Nuri v0.2.1을 업데이트했습니다

Nuri v0.2.1을 업데이트했습니다.

이번 버전에서는 Core DSL과 WPF 렌더링, 개발 도구, 런타임 안정성을 전반적으로 개선했습니다.

원래는 Nuri.Duxel도 함께 배포하고 싶었지만, 아직 성능상 다듬어야 할 부분이 남아 있어 다음 버전으로 미뤘습니다.

이번에는 현재 안정적으로 사용할 수 있는 WPF와 Core 개선 사항을 먼저 배포했습니다.

자세한 변경 내용과 호환성 안내는 아래 링크에서 확인할 수 있습니다.

Release v0.2.1

3개의 좋아요

@이광석 님 안녕하세요! 꾸준히 Nuri 소식을 업데이트해주셔서 감사합니다. 혹시 전용 게시판을 만드는 것은 어떠실지 건의드려봅니다. :smiley:

2개의 좋아요

@rkttu 전용 게시판까지 말씀해주셔서 정말 감사합니다.

감사한 제안이라 바로 답을 드리기보다는, 어제부터 조금 고민해봤습니다.

다만 아직은 별도의 게시판을 만들어주시는 것이 제게 조금 부담스럽기도 하고, 기존에 전용 게시판을 사용하고 계신 분들을 생각하면 지금 단계에서는 이른 것 같다는 생각이 들었습니다.

우선은 지금 게시판에서 계속 기록해보고, 관련 글이 100개 정도 쌓여 정말 별도의 공간이 필요해지는 시점이 오면 그때 다시 조심스럽게 말씀드려도 괜찮을까요?

좋게 봐주시고 먼저 제안해주신 것만으로도 정말 감사드립니다. :slight_smile:

4개의 좋아요

넵, 좋습니다. 언제든 필요하시면 말씀주세요!

1개의 좋아요

저장 시 자동으로 동작하는 Render 포매터

이번 추가된 버전에서는 Visual Studio Preview와 함께 C# 기반 UI 코드를 위한 자동 포매터도 추가했습니다.

Nuri처럼 메서드 호출을 중첩해 화면을 구성하다 보면 코드가 조금만 길어져도 괄호와 들여쓰기가 쉽게 흐트러집니다. 일반적인 C# 포매터만으로는 UI 구조가 기대한 형태로 정리되지 않는 경우도 있어, 항상 이런 기능이 있었으면 좋겠다고 생각했습니다.

현재는 파일을 저장할 때 자동으로 포매터가 동작하며, Component를 상속한 클래스의 다음 메서드 안에서만 적용됩니다.

public override IElement Render()
{
    // 이 영역의 Nuri UI 코드만 자동 정렬
}

파일 전체를 임의로 변경하지 않고, Nuri UI를 선언하는 Render() 영역만 찾아 정리하도록 범위를 제한했습니다.

아직 모든 C# UI 작성 방식을 지원하는 범용 포매터는 아니지만, Preview에서 화면을 확인하며 Nuri 코드를 작성하는 흐름은 조금 더 편해졌습니다.
녹음 2026-08-06 123158

2개의 좋아요