Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

.NET SDK 10 - 단일 소스 코드 파일을 빌드/실행하는 기능을 "dotnet" 명령어에 추가

실행까지는 아니었지만, 과거에도 단일 소스 코드 파일을 컴파일하는 것이 가능했습니다. 일례로, .NET Framework 시절에는 고정된 위치에 프레임워크가 설치됐고, 그 구성 요소에는 C# 컴파일러(csc.exe)도 함께 있었습니다. (msbuild.exe가 프로젝트 단위의 빌드를 담당했다면,) csc.exe는 소스 코드 파일을 컴파일하는 역할을 했는데요, 당연히 소스 코드 파일 1개도 빌드가 가능했습니다.

이후 .NET Core가 등장하면서 초기에 잠깐 csc.exe로 제공되다가 .NET 2.2.2 SDK부터는 csc.exe가 누락되고 아예 csc.dll로 제공되기 시작했습니다. 따라서 단일 소스 코드 파일을 컴파일하려면 dotnet.exe + csc.dll의 조합으로 컴파일해야 했는데요, 관련해서는 다음의 글에 자세하게 설명했으니 참고하시고.

.NET Core/5+ 환경에서 (프로젝트가 아닌) C# 코드 파일을 입력으로 컴파일하는 방법 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/13105

어찌 보면, 사실상 .NET 9 환경까지는 단일 소스 코드 파일의 빌드를 공식적으로 지원한다고 보기에는 다소 무리가 있었는데요, 이런 애매함이 .NET 10부터 사라지게 되었습니다. 게다가 방법도 간단해졌는데요, 기존의 dotnet 실행 파일에 그 기능을 통합시켰기 때문입니다.

간단하게 실습을 해볼까요? ^^ 어떤 식으로든 단일 C# 코드 파일을 하나 만든 후, 다음과 같이 "dotnet build"로 전달만 하면,

c:\temp> type test.cs
Console.WriteLine("Hello world");

c:\temp> dotnet build test.cs

컴파일이 됩니다. 문제가 하나 있다면, 저렇게 빌드한 결과물의 위치가 매우 복잡하다는 점입니다.

%USERPROFILE%\AppData\Local\Temp\dotnet\runfile\test-6dee3fbcf5d3d00ed309fd6226803f671db092c17093e603ca20d6880b18d12c\bin\debug\test.dll

// 위의 경로는 빌드 시 -v 옵션을 주면 쉽게 확인할 수 있습니다.
c:\temp> dotnet build -v d test.cs
Restore complete (0.4s)
    All projects are up-to-date for restore.
    info NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  test succeeded (0.2s) → C:\Users\testusr\AppData\Local\Temp\dotnet\runfile\test-6dee3fbcf5d3d00ed309fd6226803f671db092c17093e603ca20d6880b18d12c\bin\debug\test.dll

Build succeeded in 1.4s

그래서 보통은 output 디렉터리를 지정해서 빌드하게 될 텐데요,

c:\temp> dotnet build test.cs -o .

c:\temp> dotnet test.dll
Hello world

여기서 더 재미있는 점은, 단일 소스 코드 파일에 대한 빌드뿐만 아니라 실행까지도 dotnet 명령어가 지원한다는 점입니다. 그래서 위의 명령어처럼 2개로 나뉠 필요 없이 곧바로 "dotnet run"으로 실행할 수 있습니다.

c:\temp> dotnet run test.cs
Hello world

이게 뭘 의미하냐면, 컴파일 언어에 속한 C#을 마치 스크립트 언어처럼 다룰 수 있게 되었다는 점입니다. ^^ 심지어 *NIX 환경에서는 이에 대한 특징이 더욱더 잘 드러나는데요, 왜냐하면 소스 코드 파일에 shebang 지시자를 담을 수 있기 때문입니다.

$ cat test.cs
#!/usr/bin/dotnet run

Console.WriteLine("Hello world");

이 상태에서 저 텍스트 파일에 실행 권한만 있다면, 단독으로 실행하는 것도 가능합니다.

$ chmode +x test.cs
$ ./test.cs
Hello world

오호~~~ 나름 괜찮은 기능이 추가된 것 같죠? 이로써 그동안 C#을 스크립트 언어처럼 사용하기 위한 부가 프로젝트들이 더 이상 필요 없게 되었습니다. ^^




그나저나, 기본적인 내용은 저렇게 컴파일과 실행이 가능하다는 건데요, 이게 또 들어가 보면 곁가지들이 좀 나옵니다. 왜냐하면, 그동안 프로젝트 파일(csproj)을 통해 관리를 했던 부분들이 날아갔기 때문에 그 빈자리가 아쉬울 수밖에 없고, 그래서 이에 대한 보완으로 5개 정도의 지시자가 추가됐습니다. (향후, 더 나올 수도 있습니다.)

  • #! (shebang)
  • #:sdk (SDK 설정)
  • #:property (속성 설정)
  • #:package (nuget 패키지 참조)
  • #:project (외부 프로젝트 참조)

첫 번째 shebang 지시자는 이미 위에서 설명했으니 넘어가고, 두 번째 지시자부터 하나씩 알아보겠습니다.


#:sdk 지시자

예를 들어, Console 응용 프로그램과 ASP.NET Core 웹 응용 프로그램의 경우 SDK 유형부터가 다릅니다.

<Project Sdk="Microsoft.NET.Sdk">
    <!-- ...[생략]... -->
</Project>

<Project Sdk="Microsoft.NET.Sdk.Web">
    <!-- ...[생략]... -->
</Project>

따라서, 단일 파일의 경우 저 SDK 유형을 지정할 방법이 필요했고, 그 용도로 "#:sdk" 지시자가 도입된 것입니다. 이를 응용하면 ASP.NET Core 웹 응용 프로그램도 단일 소스 코드 파일로 작성할 수 있습니다.

$ cat test.cs
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk.Web

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

$ dotnet run test.cs
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/testusr


#:property 지시자

sdk 유형 외에 프로젝트 파일에서 거의 반드시 등장하는 것이 바로 속성(property) 설정인데요,

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

이것을 소스 코드 파일에 지정이 가능하도록 다음의 유형으로 "#:property" 지시자를 추가할 수 있습니다.

#:property [속성]=[값]

이에 대한 예제로, WinRT API를 사용하는 단일 소스 코드 파일을 이렇게 작성할 수 있습니다.

#:sdk Microsoft.NET.Sdk.WindowsDesktop
#:property TargetFramework=net10.0-windows10.0.17763.0

using Windows.Devices.Enumeration;

// 이 코드는 WinRT를 사용하므로 *NIX 환경에선 동작하지 않습니다.

using Windows.Devices.Enumeration;

// https://www.sysnet.pe.kr/2/0/11284
await ListDevices();

static async Task ListDevices()
{
    var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

    foreach (var item in devices)
    {
        Console.WriteLine($"{item.Id}: {item.Name}");
    }
}


#:package 지시자

그다음 프로젝트 파일의 역할 중 가장 중요한 것으로는 패키지 참조가 있는데요, 이에 대응해서는 "#:package" 지시자를 제공합니다.

#:package [패키지_이름]@[버전]

nuget에서 제공하는 패키지를 참조할 수 있게 해주는데, 아래의 예제는 Newtonsoft.Json 13.0.3 버전을 참조하는 단일 소스 코드 파일을 보여줍니다.

$ cat test.cs
#:package Newtonsoft.Json@13.0.3

using Newtonsoft.Json;

Product product = new Product();

product.Name = "Apple";
product.Price = 3.99M;

string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);

class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

$ dotnet run test.cs
{"Name":"Apple","Price":3.99}


#:project 지시자

마지막으로, nuget 패키지뿐만 아니라 로컬의 프로젝트 참조도 가능하게 해주는 "#:project" 지시자가 있습니다. 예를 들어, 파일/디렉터리 구조가 다음과 같을 때,

c:\temp> tree /F
Folder PATH listing
Volume serial number is 2E94-0194
C:.
│   test.cs
│
└───ClassLibrary1
        Class1.cs
        ClassLibrary1.csproj

test.cs 파일에서 ClassLibrary1.csproj의 경로를 project 지시자로 참조해 실행할 수 있습니다.

c:\temp> type test.cs
#:project .\ClassLibrary1\ClassLibrary1.csproj

ClassLibrary1.Class1.HelloWorld();

c:\temp> type .\ClassLibrary1\Class1.cs
namespace ClassLibrary1;

public class Class1
{
    public static void HelloWorld()
    {
        System.Console.WriteLine("Hello world");
    }
}

c:\temp> dotnet run test.cs
Hello world




정리하면, "dotnet build", "dotnet run" 명령어의 인자로 단일 C# 소스 코드 파일을 지정할 수 있게 되었고, 이로 인해 5개의 지시자("#:", "#:sdk", "#:property", "#:package", "#:project")를 추가해 프로젝트 파일의 기능을 일부 대체했습니다.

그런데 혹시나, 프로젝트 파일의 특정 기능이 필요한 상황이 있을까요? 그런 경우의 대안으로 Directory.Build.props 파일 역시 유효하므로 현재는 아쉬운 대로 이것을 활용하시면 됩니다.

(참고로 글을 쓰면서 느낀 건데, 이제 블로그에서의 웬만한 예제는 단일 파일 기준으로 작성해도 될 것 같습니다. ^^)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 8/19/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...
NoWriterDateCnt.TitleFile(s)
13702정성태7/31/202410756닷넷: 2288. Collection 식을 지원하는 사용자 정의 타입을 CollectionBuilder 특성으로 성능 보완파일 다운로드1
13701정성태7/30/202411399닷넷: 2287. C# 13 - (4) Indexer를 이용한 개체 초기화 구문에서 System.Index 연산자 허용파일 다운로드1
13700정성태7/29/202411604디버깅 기술: 200. DLL Export/Import의 Hint 의미
13699정성태7/27/202411291닷넷: 2286. C# 13 - (3) Monitor를 대체할 Lock 타입파일 다운로드1
13698정성태7/27/202410852닷넷: 2285. C# - async 메서드에서의 System.Threading.Lock 잠금 처리파일 다운로드1
13697정성태7/26/20249662닷넷: 2284. C# - async 메서드에서의 lock/Monitor.Enter/Exit 잠금 처리파일 다운로드1
13696정성태7/26/20249816오류 유형: 920. dotnet publish - error NETSDK1047: Assets file '...\obj\project.assets.json' doesn't have a target for '...'
13695정성태7/25/202410099닷넷: 2283. C# - Lock / Wait 상태에서도 STA COM 메서드 호출 처리파일 다운로드1
13694정성태7/25/202410065닷넷: 2282. C# - ASP.NET Core Web App의 Request 용량 상한값 (Kestrel, IIS)
13693정성태7/24/20249223개발 환경 구성: 717. Visual Studio - C# 프로젝트에서 레지스트리에 등록하지 않은 COM 개체 참조 및 사용 방법파일 다운로드1
13692정성태7/24/202410792디버깅 기술: 199. Windbg - 리눅스에서 뜬 닷넷 응용 프로그램 덤프 파일에 포함된 DLL의 Export Directory 탐색
13691정성태7/23/20249467디버깅 기술: 198. Windbg - 스레드의 Win32 Message Queue 정보 조회
13690정성태7/23/20248745오류 유형: 919. Visual C++ 리눅스 프로젝트 - error : ‘u8’ was not declared in this scope
13689정성태7/22/202412071디버깅 기술: 197. Windbg - PE 포맷의 Export Directory 탐색
13688정성태7/21/20249693닷넷: 2281. C# - Lock / Wait 상태에서도 일부 Win32 메시지 처리파일 다운로드1
13687정성태7/19/202411034닷넷: 2280. C# - PostThreadMessage로 보낸 메시지를 Windows Forms에서 수신하는 방법파일 다운로드1
13686정성태7/19/202410418오류 유형: 918. Visual Studio - ATL Simple Object 추가 시 error C2065: 'IDR_...': undeclared identifier
13685정성태7/19/202410009스크립트: 66. Windows 디렉터리 경로를 WSL의 /mnt 포맷으로 구하는 방법 - 두 번째 이야기
13684정성태7/19/202411190닷넷: 2279. C# - 문자열 보간식 사례 (예: 조건 연산자 사용)
13683정성태7/18/20249668오류 유형: 917. ClrMD - Linux 환경의 .NET 5 덤프 분석 시 hang 현상
13682정성태7/18/202410102닷넷: 2278. WPF - 스레드에 종속되는 DependencyObject파일 다운로드1
13681정성태7/17/20249292닷넷: 2277. C# 13 - (2) 메서드 그룹의 자연 타입 개선 (메서드 추론 개선)파일 다운로드1
13680정성태7/16/202411535닷넷: 2276. C# - Method Group, Natural Type, function_type파일 다운로드1
13679정성태7/16/20248604Linux: 76. Linux - C++ (getaddrinfo 등을 담고 있는) libnss 정적 링크
13678정성태7/15/20249800VS.NET IDE: 191. Visual Studio 2022 - .NET 5 프로젝트를 Docker Support로 실행했을 때 오류
13677정성태7/15/202410895오류 유형: 916. MSBuild - CheckEolTargetFramework (warning NETSDK1138)
1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...