๐Ÿ˜บ MewUI โ€“ Native AOT ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๊ฒฝ๋Ÿ‰ .NET UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๊ฐ„๋‹จํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋‚˜ ์†Œํ˜• ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ฐฐํฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ .NET ๋Ÿฐํƒ€์ž„ ์„ค์น˜ ์ž์ฒด๊ฐ€ ๋ถ€๋‹ด์ด ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.
Native AOT๋Š” ํ•˜๋‚˜์˜ ๋Œ€์•ˆ์ด์ง€๋งŒ ํ˜„์‹ค์ ์œผ๋กœ๋Š” ์„ ํƒ์ง€๊ฐ€ ์ œํ•œ์ ์ž…๋‹ˆ๋‹ค.

  • WPF / WinForms๋Š” Native AOT๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • Avalonia๋Š” Native AOT์™€ ํŠธ๋ฆฌ๋ฐ์„ ์ ์šฉํ•˜๋”๋ผ๋„ Skia ๋ฐฑ์—”๋“œ ๋“ฑ์˜ ์˜ํ–ฅ์œผ๋กœ ๊ฒฐ๊ณผ๋ฌผ์ด ์ˆ˜์‹ญ MB์— ๋‹ฌํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ„๋‹จํ•œ ๊ณ„์‚ฐ๊ธฐ๋‚˜ ์„ค์ • ๋„๊ตฌ ์ˆ˜์ค€์˜ ํ”„๋กœ๊ทธ๋žจ์— ์ ์šฉํ•˜๊ธฐ์—๋Š” ๋ฐฐํฌ ํฌ๊ธฐ ๋Œ€๋น„ ํšจ์šฉ์ด ๋งž์ง€ ์•Š๋Š” ์ƒํ™ฉ์ด ์ž์ฃผ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด์†Œํ•˜๊ธฐ ์œ„ํ•ด :grinning_cat: MewUI๋ผ๋Š” ๊ฒฝ๋Ÿ‰ .NET UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ GPT์™€ ํ•จ๊ป˜ ๋งŒ๋“ค์–ด๋ดค์Šต๋‹ˆ๋‹ค.


ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ

MewUI์˜ ๋ชฉํ‘œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Native AOT ๋ฐฐํฌ๋ฅผ ์ „์ œ๋กœ ํ•œ ์ตœ์†Œ ๊ตฌ์„ฑ์˜ UI ํ”„๋ ˆ์ž„์›Œํฌ
  • ๋Ÿฐํƒ€์ž„ ์„ค์น˜ ์—†์ด ๋‹จ์ผ ์‹คํ–‰ ํŒŒ์ผ ๋ฐฐํฌ
  • โ€œํ’€ ์ŠคํŽ™ UI ํ”„๋ ˆ์ž„์›Œํฌโ€๊ฐ€ ์•„๋‹Œ ๊ฐ„๋‹จํ•œ ํˆด ์ œ์ž‘์— ํ•„์š”ํ•œ ์ตœ์†Œ ๊ธฐ๋Šฅ์— ์ง‘์ค‘

์ดˆ๊ธฐ ๋‹จ๊ณ„์—์„œ๋Š” ๋‹ค์Œ ๋ฒ”์œ„๋ฅผ ์šฐ์„  ์ง€์›ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

  • ๊ธฐ๋ณธ์ ์ธ ์ปจํŠธ๋กค ๋ฐฐ์น˜
  • ๋‹จ์ˆœํ•œ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์„ฑ
  • ์†Œํ˜• ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฐ ๋‚ด๋ถ€ ๋„๊ตฌ์— ์ ํ•ฉํ•œ UI ํ‘œํ˜„
  • ๊ฐ„๋‹จํ•œ ๋ฐ”์ธ๋”ฉ๊ณผ ์ปจ๋ฒ„ํ„ฐ

๋ณต์žกํ•œ ์Šคํƒ€์ผ๋ง, ๊ณ ๊ธ‰ ์• ๋‹ˆ๋ฉ”์ด์…˜, ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ๋“ฑ์€ ํ˜„์žฌ ๋ฒ”์œ„์— ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


๊ฐœ๋ฐœ ๋ฐฉ์‹

์ด ํ”„๋กœ์ ํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • GPT Plus ํ”Œ๋žœ / GPT-5.2 (CODEX ์•„๋‹˜) / Medium ์ถ”๋ก  / 150K ํ† ํฐ
  • ์‹ค์ œ ์ฝ”๋“œ ์ง์ ‘ ์ˆ˜์ • ์—†์ด ํ”„๋กฌํ”„ํŠธ ์ดํ„ฐ๋ ˆ์ด์…˜๋งŒ์œผ๋กœ ์„ค๊ณ„ ๋ฐ ๊ตฌํ˜„

์„ค๊ณ„ ๋ณด์™„, API ๊ตฌ์กฐ ์กฐ์ •, ๋‚ด๋ถ€ ๊ตฌ์„ฑ ๊ฐœ์„ ์„ ๋ชจ๋‘ ๋Œ€ํ™” ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ˜๋ณตํ•˜๋ฉฐ ํ˜„์žฌ ํ˜•ํƒœ๊นŒ์ง€ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.


ํ–ฅํ›„ ๊ณ„ํš

์‹œ๊ฐ„์ด ํ—ˆ๋ฝ๋œ๋‹ค๋ฉด ๋‹ค์Œ ๊ธฐ๋Šฅ๋„ ์ถ”๊ฐ€๋ฅผ ๊ฒ€ํ† ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

  • ๋””์ž์ธ ํ”„๋ฆฌ๋ทฐ ๋„๊ตฌ
  • ๋Ÿฐํƒ€์ž„ ํ•ซ ๋ฆฌ๋กœ๋“œ
  • Native AOT ์นœํ™”์ ์ธ ์ถ”๊ฐ€ ์ปจํŠธ๋กค ํ™•์žฅ

์ด ์—ญ์‹œ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.


๋งˆ๋ฌด๋ฆฌ

MewUI๋Š” ์•„์ง ์ดˆ๊ธฐ ๋‹จ๊ณ„์ด๋ฉฐ ๋ฒ”์šฉ UI ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋ชฉํ‘œ๋กœ ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
๋‹ค๋งŒ โ€œ์ด ์ •๋„ ๊ทœ๋ชจ์˜ UI์— ์ˆ˜์‹ญ MB๊ฐ€ ํ•„์š”ํ•œ๊ฐ€โ€๋ผ๋Š” ๋ฌธ์ œ์˜์‹์—์„œ ์ถœ๋ฐœํ•œ ํ•˜๋‚˜์˜ ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ .NET ๋„๊ตฌ ๋ฐฐํฌ, Native AOT ๊ธฐ๋ฐ˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ, ์ดˆ๊ฒฝ๋Ÿ‰ UI์— ๊ด€์‹ฌ ์žˆ๋Š” ๋ถ„๋“ค์˜ ์˜๊ฒฌ๊ณผ ํ”ผ๋“œ๋ฐฑ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

16๊ฐœ์˜ ์ข‹์•„์š”

.NET Windows NativeAOT License: MIT

:grinning_cat: MewUI ๋Š” NativeAOT + Trim ์•ฑ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋Š”, ์ฝ”๋“œ ๊ธฐ๋ฐ˜(code-first) ๊ฒฝ๋Ÿ‰ ํฌ๋กœ์Šค ํ”Œ๋žซํผ .NET GUI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

  • ์ƒํƒœ: :test_tube: ์‹คํ—˜์  ํ”„๋กœํ† ํƒ€์ž… ๋ฒ„์ „์ž…๋‹ˆ๋‹ค(๊ธฐ๋Šฅ/๋™์ž‘/API๋Š” ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค).
  • ์ฐธ๊ณ : :robot: ์ด ์ €์žฅ์†Œ์˜ ๋Œ€๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋Š” GPT์˜ ๋„์›€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์Šคํฌ๋ฆฐ์ƒท


:sparkles: ํ•˜์ด๋ผ์ดํŠธ

  • :package: NativeAOT + Trim ์šฐ์„ 
  • :feather: ๋น ๋ฅด๊ณ  ๊ฐ€๋ณ๊ฒŒ (๊ฐ€๋ฒผ์šด ์‹คํ–‰ ํŒŒ์ผ ํฌ๊ธฐ, ๋‚ฎ์€ ๋ฉ”๋ชจ๋ฆฌ ํ’‹ํ”„๋ฆฐํŠธ, ๋น ๋ฅธ ์‹œ์ž‘)
  • :puzzle_piece: Fluent C# ๋งˆํฌ์—…

:package: ๊ฒฝ๋Ÿ‰(Lightweight)

  • ์‹คํ–‰ ํŒŒ์ผ ํฌ๊ธฐ: NativeAOT + Trim ์ค‘์‹ฌ(์ƒ˜ํ”Œ win-x64-trimmed ์•ฝ 2.2 MB)
  • ์ƒ˜ํ”Œ ๋Ÿฐํƒ€์ž„ ๋ฒค์น˜๋งˆํฌ (NativeAOT + Trimmed, 50ํšŒ ์‹คํ–‰):
๋ฐฑ์—”๋“œ Loaded avg/p95 (ms) FirstFrame avg/p95 (ms) WS avg/p95 (MB) PS avg/p95 (MB)
Direct2D 10 / 11 178 / 190 40.0 / 40.1 54.8 / 55.8
GDI 15 / 21 54 / 67 15.2 / 15.3 4.6 / 4.8

:test_tube: C# ๋งˆํฌ์—…

var window = new Window()
    .Title("Hello MewUI")
    .Size(520, 360)
    .Padding(12)
    .Content(
        new StackPanel()
            .Spacing(8)
            .Children(
                new Label()
                    .Text("Hello, Aprillz.MewUI")
                    .FontSize(18)
                    .Bold(),
                new Button()
                    .Content("Quit")
                    .OnClick(() => Application.Quit())
            )
    );

Application.Run(window);

:bullseye: ์ปจ์…‰

MewUI๋Š” ์•„๋ž˜ 3๊ฐ€์ง€๋ฅผ ์ตœ์šฐ์„ ์œผ๋กœ ๋‘” code-first UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค:

  • :package: NativeAOT + Trim ์นœํ™”
  • :feather: ๋น ๋ฅด๊ณ  ๊ฐ€๋ณ๊ฒŒ (๋‹จ์ผ EXE ~2.2 MB, ๋‚ฎ์€ ๋ฉ”๋ชจ๋ฆฌ ํ’‹ํ”„๋ฆฐํŠธ, ๋น ๋ฅธ ์ฒซ ๋ Œ๋” โ€” ์•„๋ž˜ ๋ฒค์น˜๋งˆํฌ ์ฐธ๊ณ )
  • :puzzle_piece: XAML ์—†์ด Fluentํ•œ C# ๋งˆํฌ์—…์œผ๋กœ UI ํŠธ๋ฆฌ ๊ตฌ์„ฑ

์ง€ํ–ฅํ•˜์ง€ ์•Š๋Š” ๊ฒƒ:

  • WPF์ฒ˜๋Ÿผ ์• ๋‹ˆ๋ฉ”์ด์…˜, ํ™”๋ คํ•œ ์ดํŽ™ํŠธ, ๋ฌด๊ฑฐ์šด ์ปดํฌ์ง€์…˜ ํŒŒ์ดํ”„๋ผ์ธ
  • โ€œ๋‹ค ๋“ค์–ด์žˆ๋Š”โ€ ๋ฆฌ์น˜ ์ปจํŠธ๋กค ์นดํƒˆ๋กœ๊ทธ
  • XAML/WPF ์™„์ „ ํ˜ธํ™˜์ด๋‚˜ ๋””์ž์ด๋„ˆ ์ค‘์‹ฌ ์›Œํฌํ”Œ๋กœ์šฐ

:paintbrush: ๋ Œ๋”๋ง ๋ฐฑ์—”๋“œ

๋ Œ๋”๋ง์€ ์•„๋ž˜ ์ถ”์ƒํ™”๋กœ ๋ถ„๋ฆฌ๋ฉ๋‹ˆ๋‹ค:

  • IGraphicsFactory / IGraphicsContext

์ƒ˜ํ”Œ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Direct2D๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, GDI์™€ OpenGL๋„ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.


:window: ํ”Œ๋žซํผ ์ถ”์ƒํ™”

์œˆ๋„์šฐ/๋ฉ”์‹œ์ง€ ๋ฃจํ”„๋Š” ํ”Œ๋žซํผ ๊ณ„์ธต์œผ๋กœ ์ถ”์ƒํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ˜„์žฌ๋Š” Windows ๊ตฌํ˜„(Win32PlatformHost)์ œ์™€ Linux/X11 ๊ตฌํ˜„์ฒด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • ์ถ”ํ›„ macOS๋„ ์ œ๊ณต์˜ˆ์ •.

:scissors: NativeAOT / Trim

  • ๊ธฐ๋ณธ์ ์œผ๋กœ trimming-safe๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค.(๋ฆฌํ”Œ๋ ‰์…˜ ๊ธฐ๋ฐ˜ ๋ฐ”์ธ๋”ฉ ์ง€์–‘)
  • Windows interop์€ NativeAOT ํ˜ธํ™˜์„ ์œ„ํ•ด ์†Œ์Šค ์ƒ์„ฑ P/Invoke(LibraryImport)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

:link: ์ƒํƒœ/๋ฐ”์ธ๋”ฉ(AOT ์นœํ™”)

๋ฐ”์ธ๋”ฉ์€ ๋ฆฌํ”Œ๋ ‰์…˜ ์—†์ด, ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๊ธฐ๋ฐ˜์ž…๋‹ˆ๋‹ค:

var percent = new ObservableValue<double>(0.25);

var slider = new Slider()
                 .BindValue(percent);
var label  = new Label()
                 .BindText(
                     percent, 
                     convert: v => $"Percent ({v:P0})");

:brick: ์ปจํŠธ๋กค / ํŒจ๋„

์ปจํŠธ๋กค:

  • Label, Button, TextBox
  • CheckBox, RadioButton
  • ListBox, ComboBox
  • Slider, ProgressBar
  • Window

ํŒจ๋„:

  • Grid (row/column: Auto, *, pixel)
  • StackPanel (๊ฐ€๋กœ/์„ธ๋กœ + Spacing)
  • DockPanel (๋„ํ‚น + ๋งˆ์ง€๋ง‰ ์ž์‹ ์ฑ„์šฐ๊ธฐ)
  • UniformGrid (๊ท ๋“ฑ ์…€)
  • WrapPanel (์ค„๋ฐ”๊ฟˆ + Item size + Spacing)

:artist_palette: ํ…Œ๋งˆ(Theme)

  • ๋ผ์ดํŠธ ํ…Œ๋งˆ์™€ ๋‹คํฌ ํ…Œ๋งˆ ๋‘ ๊ฐ€์ง€๋ฅผ ์ง€์›ํ•˜๋ฉฐ Accent ์ƒ‰์ƒ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Accent ๋ณ€๊ฒฝ:

    Theme.Current = Theme.Current.WithAccent(Color.FromRgb(214, 176, 82));
    
  • ์ƒ‰์ƒ ์ด์™ธ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€๊ฒฝ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. (ํ…Œ๋‘๋ฆฌ ๋ผ์šด๋”ฉ, ๊ธฐ๋ณธ ํฐํŠธ ๋“ฑ )


:compass: ๋กœ๋“œ๋งต (TODO)

์ปจํŠธ๋กค

  • Image, GroupBox, TabControl, ScrollViewer

๋ Œ๋”๋ง

  • OpenGL ๋ฐฑ์—”๋“œ

ํ”Œ๋žซํผ

  • Linux
  • macOS

ํˆด๋ง

  • Hot Reload (๋””์ž์ธ ํƒ€์ž„ ์ค‘์‹ฌ)
5๊ฐœ์˜ ์ข‹์•„์š”

์™„์ „ ๋Œ€๋ฐ•!!!

4๊ฐœ์˜ ์ข‹์•„์š”

์•„!! ์ •๋ง ๊ธฐ๋Œ€๋˜๋Š”๊ตฐ์š”. :grinning_face:

์ด๊ฑธ ๋ณด๋‹ˆ LLM์œผ๋กœ ๋งˆ์†Œ๊ฐ€ ์œ ๊ธฐํ•œ **ATL(Active Template Library)**๋ฅผ ํ˜„๋Œ€ํ™” ํ•ด๋ณด๋ฉด ์ข‹๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. :sweat_smile:

4๊ฐœ์˜ ์ข‹์•„์š”

์˜ค.. ATL์„ ์•„์‹œ๋Š”๊ตฐ์š”! ์žŠ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ์˜ค๋žซ๋งŒ์— ์ƒ๊ฐ๋‚ฌ์Šต๋‹ˆ๋‹ค. :smiley: (WTL์ด๋ž€๊ฒƒ๋„ ์žˆ์—ˆ์ฃ . ใ…‹ใ…‹ใ…‹)

4๊ฐœ์˜ ์ข‹์•„์š”

ํ—.. ์ € ์ปจํŠธ๋กค ์‚ฌ์šฉ ํ•˜๋ฉด EXE ํ•˜๋‚˜๋งŒ ๋ฐฐํฌ ๊ฐ€๋Šฅ ํ•œ๊ฑด๊ฐ€์š” ??? ๊ทธ๋ผ๋ชจ ๋Œ€๋ฐ• ์ธ๋””

2๊ฐœ์˜ ์ข‹์•„์š”

MewUI๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ”„๋กœ์ ํŠธ๊ฐ€ ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ๋“ฑ NativeAOT ์ปดํŒŒ์ผ ์ œ์•ฝ์„ ์œ„๋ฐ˜ํ•˜๋Š” ๋กœ์ง์ด๋‚˜ ์ถ”๊ฐ€ ํŒจํ‚ค์ง€ ์ฐธ์กฐ ์—†์ด ๊ตฌ์„ฑ๋  ์ˆ˜ ์žˆ๋‹ค๋ฉด, EXE ๋‹จ์ผ ํŒŒ์ผ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ FBA๋กœ ์ด๋ ‡๊ฒŒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹คใ…Ž

https://github.com/aprillz/MewUI - FBA ์ƒ˜ํ”Œ์•ฑ

https://github.com/aprillz/MewUI - FBA ๊ณ„์‚ฐ๊ธฐ


์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ถ™์—ฌ๋„ฃ๊ธฐ ํ•˜๋ฉด ์ฆ‰์‹œ ์‹คํ–‰ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (.NET 10 SDK ํ•„์š”)

Windows ์ปค๋งจ๋“œ๋ผ์ธ

์ƒ˜ํ”Œ์•ฑ

curl -sL https://raw.githubusercontent.com/aprillz/MewUI/refs/heads/main/samples/FBASample/fba_sample.cs -o - | dotnet run -

๊ณ„์‚ฐ๊ธฐ

curl -sL https://raw.githubusercontent.com/aprillz/MewUI/refs/heads/main/samples/FBASample/fba_calculator.cs -o - | dotnet run -

Powershell

์ƒ˜ํ”Œ์•ฑ

irm https://raw.githubusercontent.com/aprillz/MewUI/refs/heads/main/samples/FBASample/fba_sample.cs | dotnet run -

๊ณ„์‚ฐ๊ธฐ

irm https://raw.githubusercontent.com/aprillz/MewUI/refs/heads/main/samples/FBASample/fba_calculator.cs | dotnet run -

NativeAOT Trim ๋นŒ๋“œ์‹œ ์•ฝ 1.8๋ฉ”๊ฐ€
image

5๊ฐœ์˜ ์ข‹์•„์š”

image
์šฐ์˜ค์˜ค์˜ค์˜ค์˜ค์˜ค!!

6๊ฐœ์˜ ์ข‹์•„์š”

์ด์ œ Linux (X11)์—์„œ๋„ ์ž˜ ๋™์ž‘ํ•˜๋Š” ํฌ๋กœ์Šคํ”Œ๋žซํผ UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! :grin:

8๊ฐœ์˜ ์ข‹์•„์š”