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

비밀번호

댓글 작성자
 




... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13625정성태5/14/202411220닷넷: 2262. C# - Exception Filter 조건(when)을 갖는 catch 절의 IL 구조
13624정성태5/12/202410823Phone: 14. C# - MAUI에서 MediaElement 사용파일 다운로드1
13623정성태5/11/202410353닷넷: 2261. C# - 구글 OAuth의 JWT (JSON Web Tokens) 해석파일 다운로드1
13622정성태5/10/202413190닷넷: 2260. C# - Google 로그인 연동 (ASP.NET 예제)파일 다운로드1
13621정성태5/10/202411999오류 유형: 903. IISExpress - Failed to register URL "..." for site "..." application "/". Error description: Cannot create a file when that file already exists. (0x800700b7)
13620정성태5/9/202411080VS.NET IDE: 190. Visual Studio가 node.exe를 경유해 Edge.exe를 띄우는 경우
13619정성태5/7/202411890닷넷: 2259. C# - decimal 저장소의 비트 구조 [2]파일 다운로드1
13618정성태5/6/202410462닷넷: 2258. C# - double (배정도 실수) 저장소의 비트 구조파일 다운로드1
13617정성태5/5/202413109닷넷: 2257. C# - float (단정도 실수) 저장소의 비트 구조파일 다운로드1
13616정성태5/3/202410438닷넷: 2256. ASP.NET Core 웹 사이트의 HTTP/HTTPS + Dual mode Socket (IPv4/IPv6) 지원 방법파일 다운로드1
13615정성태5/3/202413204닷넷: 2255. C# 배열을 Numpy ndarray 배열과 상호 변환
13614정성태5/2/202413043닷넷: 2254. C# - COM 인터페이스의 상속 시 중복으로 메서드를 선언
13613정성태5/1/202410673닷넷: 2253. C# - Video Capture 장치(Camera) 열거 및 지원 포맷 조회파일 다운로드1
13612정성태4/30/202412467오류 유형: 902. Visual Studio - error MSB3021: Unable to copy file
13611정성태4/29/202410958닷넷: 2252. C# - GUID 타입 전용의 UnmanagedType.LPStruct - 두 번째 이야기파일 다운로드1
13610정성태4/28/202412500닷넷: 2251. C# - 제네릭 인자를 가진 타입을 생성하는 방법 - 두 번째 이야기
13609정성태4/27/202412202닷넷: 2250. PInvoke 호출 시 참조 타입(class)을 마샬링하는 [IN], [OUT] 특성파일 다운로드1
13608정성태4/26/202413092닷넷: 2249. C# - 부모의 필드/프로퍼티에 대해 서로 다른 자식 클래스 간에 Reflection 접근이 동작할까요?파일 다운로드1
13607정성태4/25/202413117닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/202413101닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/202414524닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/202410608오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/202413536닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/202411765닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/202412199닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/202413651닷넷: 2242. C# - 관리 스레드와 비관리 스레드
... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...