FbaTemplates 1.0.0 출시 - 닷넷 개발 환경의 새로운 장 열기

이전에 dotnet-fba-tool 프로젝트를 만들면서 받았던 피드백을 기반으로, 실제 닷넷 생태계에 좀 더 최적화된 형태의 새로운 File-based App 템플릿 팩을 만들었습니다.

dotnet new install fbatemplates

.NET 10 SDK 설치 후 위의 명령어로 템플릿 팩을 설치하면 위의 그림과 같이 FBA 특화 템플릿이 여러개가 설치됩니다. 그리고 다음과 같이 템플릿을 이용하여 새 코드를 빠르게 만들 수 있습니다. 예를 들어, .NET Aspire AppHost 템플릿을 다음과 같이 사용할 수 있을 것입니다.

dotnet new aspire-fba -n aspire

dotnet run aspire/aspire.cs

혹은 AWS CDK 템플릿을 다음과 같이 이용할 수도 있습니다.

dotnet new awscdk-fba -n awscdk

cd awscdk/

cdklocal bootstrap

cdklocal deploy

이렇게 만든 FBA 코드를 C# Extension을 설치한 뒤 VS Code에서 열면, Visual Studio 라이선스가 요구되는 C# Dev Kit Extension 없이 무료로 nuget 패키지까지 인식 가능한 인텔리센스 기능까지 활성화하여 아래 그림처럼 쓸 수 있게 됩니다. (Win32 전용 템플릿을 제외하면, 당연히 아래 기능은 Linux, macOS에서도 잘 지원됩니다.)

code awscdk/awscdk.cs

이번 버전에서는 FBA 템플릿 팩의 기술적 가능성을 확인해보는데 초점을 맞추었는데, 여기에 더해 바이브코딩을 좀 더 잘 수행할 수 있도록 instruction markdown 파일도 템플릿에 포함시켜보려 합니다. (Github Copilot, Amazon Kiro, Cursor 등의 주요 AI 에디터 대응용으로)

많이 사용해보시고, 컨트리뷰션도 부탁드리며, 많이 공유해주시면 감사하겠습니다! 이번 시도를 계기로 국내에 닷넷 개발 환경에 대한 고착된 인식을 깨는 첫 시작이 되었으면 좋겠습니다. :blush:

12개의 좋아요

최근에 꽤 많은 글들을 올리시는 것을 보니, FBA 에 많이 꽂히셨나 봅니다.

혹시, 실무 프로젝트에 대한 적용 대책 같은 것이 있을까요?
예를 들면, 패키지나 DLL 참조가 아닌, 코드 파일 참조 같은 것들요.

3개의 좋아요

네, 개인적으로 많이 연구하고 있습니다. :smiley:

기존에 개발하던 워크로드를 굳이 C#으로 갈아타야 한다든지, 도입을 진지하게 검토해야 한다면 그건 불필요하다고 생각합니다. FBA는 어디까지나 C# 프로그래밍 언어에 대한 저변 확대나 Quick 프로토타이핑을 돕기 위한 취지에 포커스를 맞춘 도구라고 생각합니다. 실제로 순서 상 FBA로 시작한 다음 프로젝트를 변환하는 것이 이 기능의 기획 의도이기도 하고요. (dotnet project convert 명령)

다만 FBA 코드에서 기존 프로젝트를 참조하는 것은 #:project 지시자 문법을 이용해서 프로젝트 참조하는 것이 가능하고, 기존 프로젝트에서 사용하던 MSBuild SDK 설정은 Directory.Build.props 파일을 FBA 파일과 나란히 놓아두면 자동으로 인식하기 때문에 기존 프로젝트와 호환성도 보장됩니다.

프로젝트가 아닌 개별 코드 파일 참조가 필요한 경우에도, Directory.Build.props 파일 내에 <ItemGroup> 태그 안에 <Compile Include=”…" />등의 태그를 써서 참조할 수 있게 되어있습니다. 다만 이 부분은 FBA 자체가 아직 프리뷰 단계여서, 다음에 나올 .NET 11이나 .NET 12에서 더 확장될 부분이 있지 않을까 예상됩니다.

7개의 좋아요

@suwoo 님과

같이 재미삼아 만들고 있던 프로젝트를
@rkttu 님이 만드신 fba 템플릿을 보고 이거다! 싶어

바로 출시해서 적용시켜보았습니다.

레포지토리

5개의 좋아요

간만에 F#을 꺼내고싶은 느낌이에요 ㅎㅎ

3개의 좋아요

저도 비슷한 것을 생각해보고 있었습니다. FBA 코드에서 필요하면 Xaml을 포함시킬 수도 있지만, 보여주신것처럼 Swift UI 스타일의 코드가 더 자연스럽지요. :smiling_face_with_three_hearts:

fba 템플릿으로도 올려보시면 어떨까요?

3개의 좋아요

네 조금씩 나아가볼게요~

3개의 좋아요

@이광석 님의 LinqUI.WPF nuget 패키지를 베이스로 하는 wpf-linqui-fba 템플릿, 그리고 Cursor/VSCode용 MCP 서버 설정과 AGENTS.md 파일을 포함시켜 바이브코딩 대응이 가능하도록 보강한 FbaTemplates 1.1.0 버전을 출시했습니다.

기존에 템플릿 팩을 설치하셨다면 dotnet new update 명령으로 업데이트하실 수 있고, 새로 설치하신다면 dotnet new install fbatemplates 명령으로 쉽게 설치하실 수 있습니다. :smiley:

ps. @이광석 님 컨트리뷰션 감사합니다! :person_bowing:

다음은 코드 예시입니다.

#:sdk Microsoft.NET.Sdk

#:package CommunityToolkit.Mvvm@8.4.0
#:package LinqUI.WPF@1.0.6

#:property OutputType=WinExe
#:property TargetFramework=net10.0-windows
#:property UseWPF=True
#:property UseWindowsForms=False

// WPF cannot use AOT compilation.
#:property PublishAot=False

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LinqUI.WPF;

// https://github.com/dotnet/winforms/issues/5071#issuecomment-908789632
Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown);
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);

new Application()
        .ShutdownMode(ShutdownMode.OnMainWindowClose)
        .OnDispatcherUnhandledException((_, e) =>
        {
            MessageBox.Show(e.Exception.ToString(), "Unhandled", MessageBoxButton.OK, MessageBoxImage.Error);
            e.Handled = true;
        })
        .Run(new Window()
                .WindowStartupLocation(WindowStartupLocation.CenterScreen)
                .Title($"Hello, World! - {Thread.CurrentThread.GetApartmentState()}")
                .Size(640, 240)
                .DataContext(new CounterViewModel())
                .Content(
                    new StackPanel()
                        .Children(
                            new TextBlock()
                                .FontSize(24)
                                .Margin(8)
                                .HCenter()
                                .Bind(TextBlock.TextProperty, new Binding(nameof(CounterViewModel.Count))),
                            new Button()
                                .Content("Increment (+1)")
                                .Margin(8)
                                .Bind(Button.CommandProperty, new Binding(nameof(CounterViewModel.IncrementCountCommand))),
                            new Button()
                                .Content("Decrement (-1)")
                                .Margin(8)
                                .Bind(Button.CommandProperty, new Binding(nameof(CounterViewModel.DecrementCountCommand)))
                        )
                )
    );

public sealed partial class CounterViewModel : ObservableObject
{
    [ObservableProperty]
    private int _count = 0;

    [RelayCommand]
    private void IncrementCount()
        => Count++;

    [RelayCommand]
    private void DecrementCount()
        => Count--;
}

8개의 좋아요