반응형

[PART1.C# 런타임과 .NET 플랫폼 기초(11/11)] 파일 기반 앱 — 단일 .cs 실행 (C# 14)

.csproj 없이 dotnet run app.cs 한 줄 / 파일 상단 #: 디렉티브로 패키지·SDK·빌드 속성을 선언 / 정식 프로젝트와 IL이 동일한 "임시 프로젝트 자동 생성" 모델


1. 문제 제기 — "Hello, World"가 세 파일짜리인 언어

Unity 모바일 클라이언트 주니어 개발자가 사내에서 작은 유틸리티를 만들어야 할 때를 상상해 봅니다. 예를 들어 Resources/Localization/ko.csv 파일을 읽어서 ScriptableObject 자산으로 변환해 주는 CLI 도구가 필요합니다. 한 200줄짜리 스크립트로 끝날 일입니다. 그런데 C#으로 시작하려면 매번 이 절차를 거쳐야 했습니다.

# 기존 C#의 "최소 시작 절차"
mkdir csv-to-asset
cd csv-to-asset
dotnet new console            # .csproj + Program.cs + obj/ 생성
code .                        # IDE 열기
# ... Program.cs 편집 ...
dotnet run

Python이면 python convert.py 한 줄, Node.js면 node convert.js 한 줄, Go면 go run convert.go 한 줄로 끝나는 일입니다. C#만 "프로젝트 파일 + 폴더 구조 + 빌드 시스템"이라는 무거운 갑옷을 벗을 수 없었습니다. 학습용 예제, GitHub Gist 공유, CI 스크립트, 일회성 데이터 변환 — 어떤 목적이든 C# 소스 한 토막을 돌리려면 dotnet new 에서 시작해야 했습니다.

.NET 10 (C# 14)의 파일 기반 앱(File-based apps) 은 이 한 가지 고통을 정면으로 해결합니다. 이제는 .cs 파일 하나만 있으면 됩니다.

# C# 14의 새로운 시작 절차
echo 'Console.WriteLine("Hello!");' > app.cs
dotnet run app.cs
# Hello!

이 글에서 다룰 질문은 세 가지입니다.

  1. .csproj 가 없는데 컴파일러는 어떻게 알고 빌드하는가? (내부 임시 프로젝트)
  2. 파일 상단 #:package Newtonsoft.Json@13.0.3 같은 한 줄이 어떻게 NuGet 패키지로 변하는가? (디렉티브 매핑)
  3. 파일 기반 앱과 정식 dotnet new console 프로젝트의 런타임 동작은 정말 같은가? (IL 비교)

Unity 주니어 관점에서는 "언제 파일 기반 앱을 쓰고, 언제 정식 프로젝트로 승격시켜야 하는가" 라는 판단 기준이 가장 중요합니다. 파일 기반 앱은 Unity 게임 로직 자체를 대체하지 않습니다 — Unity 외부의 보조 툴CI 스크립트에서 빛을 발합니다.


2. 개념 정의 — "한 파일 = 한 앱"

비유: 메모장 스크립트와 정식 소프트웨어

사무실에서 쓰는 엑셀 매크로를 생각해 봅시다. 단순한 매크로는 셀 한 줄에 "수식 한 개"만 있으면 충분합니다. 반면 진짜 업무 자동화는 .xlsm 파일 + 모듈 + 폼 + 참조 라이브러리 구조로 확장됩니다. 엑셀은 두 경험을 같은 엔진으로 돌립니다 — 수식 한 줄을 위해 전체 매크로 프로젝트를 만들라고 강요하지 않습니다.

파일 기반 앱이 C#에 도입하는 것이 이와 정확히 같은 선택지입니다. 같은 C# 컴파일러(Roslyn), 같은 빌드 엔진(MSBuild), 같은 런타임(CLR, Common Language Runtime — .NET 프로그램을 실행하는 가상 머신) 을 쓰지만, 프로젝트 파일을 사용자가 들고 있느냐, SDK가 임시로 만들어 주느냐 만 다릅니다.

개념 다이어그램

정식 프로젝트 vs 파일 기반 앱 — 같은 엔진, 다른 입구

가장 작은 파일 기반 앱

C#
// app.cs — 이 파일 하나가 전부
Console.WriteLine("Hello, File-based App!");
int n = 42;
Console.WriteLine($"n = {n}");

실행:

$ dotnet run app.cs
Hello, File-based App!
n = 42

.csproj 도 없고, 폴더도 하나의 .cs 파일만 들어있습니다. 그런데도 정식 C# 프로그램처럼 동작합니다.

실제로 생성된 IL

이 단순한 앱을 dotnet run app.cs 로 실행한 뒤 임시 폴더에서 .dll 을 꺼내 ilspycmd -il 로 디컴파일한 결과입니다.

IL
// app.dll (파일 기반 앱)
.class private auto ansi beforefieldinit Program
    extends [System.Runtime]System.Object
{
    .method private hidebysig static
        void '<Main>$' (string[] args) cil managed
    {
        .entrypoint
        .locals init (
            [0] int32,
            [1] valuetype [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler
        )

        IL_0000: ldstr "Hello, File-based App!"
        IL_0005: call void [System.Console]System.Console::WriteLine(string)
        IL_000b: ldc.i4.s 42
        IL_000d: stloc.0
        IL_000e: ldloca.s 1
        IL_0010: ldc.i4.4
        IL_0011: ldc.i4.1
        IL_0012: call instance void DefaultInterpolatedStringHandler::.ctor(int32, int32)
        IL_0019: ldstr "n = "
        IL_001e: call instance void DefaultInterpolatedStringHandler::AppendLiteral(string)
        IL_0026: ldloc.0
        IL_0027: call instance void DefaultInterpolatedStringHandler::AppendFormatted<int32>(!!0)
        IL_002f: call instance string DefaultInterpolatedStringHandler::ToStringAndClear()
        IL_0034: call void [System.Console]System.Console::WriteLine(string)
        IL_003a: ret
    }
}

여기서 주목할 것은 Top-level statements가 자동으로 Program 클래스의 <Main>$ 메서드로 포장된다는 점입니다. class, namespace, static void Main 을 쓰지 않았는데도 컴파일러가 모두 만들어줍니다. 이것은 정식 dotnet new console 프로젝트에서도 동일하게 적용되는 C# 9 이후의 규칙이며, 파일 기반 앱만의 특별한 변환이 아닙니다. 즉 파일 기반 앱은 새로운 언어 기능이 아니라 새로운 "실행 입구" 입니다.

<Main>$ — 컴파일러가 생성한 엔트리 포인트 Top-level statements를 쓰면 컴파일러는 소스를 Program 클래스의 <Main>$ 라는 메서드로 감싼다. 이름에 < 가 들어간 것은 C# 코드에서 직접 호출할 수 없다는 표시다. 런타임의 CLR 만이 엔트리 포인트로 인식하고 호출한다.

3. 내부 동작 — SDK가 임시 .csproj를 만드는 순서

비유: "자동 조립" 주방 가구

IKEA에서 가구를 사면 조립 설명서와 부품이 박스로 옵니다. 반면 "조립 대행" 서비스를 쓰면 기사가 도착해서 현장에서 부품을 펼치고 조립한 뒤, 완성품만 남기고 상자는 가져갑니다. 사용자는 "완성된 가구"만 봅니다. 파일 기반 앱의 SDK 동작이 정확히 이 조립 대행 기사와 같습니다.

실행 흐름 다이어그램

dotnet run app.cs — 내부 5단계

실제 임시 폴더 확인

앞서 실행한 dotnet run app.cs 가 남긴 실제 경로는 다음과 같았습니다.

~/Library/Application Support/dotnet/runfile/app-e1267ad5...345f/
├── bin/debug/
│   ├── app.dll              ← 실제 컴파일 결과물
│   ├── app.pdb
│   ├── app.deps.json
│   └── app.runtimeconfig.json
└── obj/
    ├── debug/
    └── app.csproj.nuget.g.props

폴더 이름의 해시(app-e1267ad5...) 는 소스 파일 내용 + 경로로 계산됩니다. 같은 .cs 를 다시 실행하면 재빌드 없이 이 캐시를 재사용하기 때문에 두 번째 실행부터는 기동이 빨라집니다. 소스를 한 글자라도 고치면 해시가 바뀌어 새 폴더에서 다시 빌드합니다.

Windows 에서는 %LOCALAPPDATA%\Temp\dotnet\runfile\... 이 같은 역할을 합니다. 두 위치 모두 OS 임시 폴더로, 디스크 공간이 부족해지면 OS가 청소해도 문제가 없는 계산된 결과물입니다.

정식 프로젝트와 IL 비교 — "정말 같은가?"

증명해 보겠습니다. 동일한 소스를 (1) 파일 기반 앱으로 실행, (2) dotnet new console 정식 프로젝트로 빌드한 뒤 두 .dll 의 IL을 뽑아 비교합니다.

C#
// 동일한 소스 (양쪽 공통)
Console.WriteLine("Hello, File-based App!");
int n = 42;
Console.WriteLine($"n = {n}");

양쪽 IL의 <Main>$ 메서드 본문만 비교한 결과입니다.

IL
// (파일 기반 앱) ~/Library/.../runfile/.../app.dll
IL_0000: ldstr "Hello, File-based App!"
IL_0005: call void [System.Console]System.Console::WriteLine(string)
IL_000b: ldc.i4.s 42
IL_000d: stloc.0
IL_0012: call instance void DefaultInterpolatedStringHandler::.ctor(int32, int32)
IL_0034: call void [System.Console]System.Console::WriteLine(string)
IL_003a: ret
IL
// (정식 프로젝트) classic/bin/Debug/net10.0/classic.dll
IL_0000: ldstr "Hello, File-based App!"
IL_0005: call void [System.Console]System.Console::WriteLine(string)
IL_000b: ldc.i4.s 42
IL_000d: stloc.0
IL_0012: call instance void DefaultInterpolatedStringHandler::.ctor(int32, int32)
IL_0034: call void [System.Console]System.Console::WriteLine(string)
IL_003a: ret

IL이 완전히 동일합니다. 어셈블리 이름(app.dll vs classic.dll)만 다를 뿐 코드 본문은 바이트 단위로 같습니다. 이것이 "파일 기반 앱은 새로운 런타임이 아니다"의 증거입니다. 런타임 성능·GC 동작·JIT 최적화 전부 정식 프로젝트와 동일합니다. 변하는 것은 개발자 경험(DX)뿐이고, 실행물은 같습니다.

이 사실이 Unity 주니어에게 중요한 이유: 파일 기반 앱으로 쓴 CI 스크립트가 "가볍다"고 해서 성능이 떨어지거나 동작이 다를까 봐 걱정할 필요가 없다는 뜻입니다. 같은 Roslyn, 같은 CLR 입니다.


4. 실전 적용 — #: 디렉티브로 빌드 제어

비유: 이메일 헤더처럼 파일 위에 "메타데이터" 붙이기

이메일에는 본문 위에 To:, From:, Subject: 같은 헤더가 붙습니다. 독자(메일 클라이언트)는 헤더를 먼저 읽고 라우팅에 사용한 뒤 본문을 렌더링합니다. 파일 기반 앱의 #: 디렉티브가 이와 같습니다 — 파일 맨 위에 빌드 시스템이 먼저 읽을 메타데이터를 적고, 그 아래가 실제 C# 본문입니다.

디렉티브 세 종류

#:package — NuGet 패키지 참조 빌드 시 해당 NuGet 패키지를 복원(restore)하여 참조에 포함한다. 버전은 @ 또는 공백으로 구분한다.
예시: #:package Newtonsoft.Json@13.0.3 JSON 라이브러리를 끌어와 using Newtonsoft.Json; 으로 사용 가능
#:property — MSBuild 속성 설정 .csproj<PropertyGroup> 안에 들어가는 모든 설정을 파일 상단에서 선언한다. Key=Value 형식이다.
예시: #:property LangVersion=preview 프리뷰 C# 문법 허용
#:sdk — 사용할 .NET SDK 지정 기본값은 Microsoft.NET.Sdk (콘솔 앱). 웹 앱을 만들고 싶다면 Microsoft.NET.Sdk.Web 으로 변경한다.
예시: #:sdk Microsoft.NET.Sdk.Web ASP.NET Core 최소 API 호스팅 가능

Before — .csproj 한 번 열고 싶지도 않은 단일 스크립트

Unity 팀에서 자주 등장하는 유틸리티 코드입니다. Localization/ko.csv 를 JSON 으로 변환해서 CDN 에 올리기 전에 검증하는 일회성 스크립트입니다.

C#
// 기존 방식: 정식 프로젝트로 만들면 딸려오는 것
// convert/
// ├─ convert.csproj         <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
// ├─ Program.cs
// └─ obj/, bin/ 등

// Program.cs
using Newtonsoft.Json;

string csv = File.ReadAllText("ko.csv");
var rows = csv.Split('\n').Select(l => l.Split(','));
var json = JsonConvert.SerializeObject(rows, Formatting.Indented);
File.WriteAllText("ko.json", json);
Console.WriteLine($"변환 완료: {rows.Count()}행");

200줄짜리 프로젝트를 위해 폴더·csproj·bin·obj 를 전부 만들어야 합니다. 팀원에게 공유하려면 폴더를 압축해서 보내거나 git 저장소를 따로 파야 합니다.

After — 단일 .cs + #:package

C#
// convert.cs — 이 파일 하나만 Slack 에 올리면 팀원이 바로 실행 가능
#:package Newtonsoft.Json@13.0.3

using Newtonsoft.Json;

string csv = File.ReadAllText("ko.csv");
var rows = csv.Split('\n').Select(l => l.Split(','));
var json = JsonConvert.SerializeObject(rows, Formatting.Indented);
File.WriteAllText("ko.json", json);
Console.WriteLine($"변환 완료: {rows.Count()}행");

실행 한 줄:

dotnet run convert.cs

첫 실행 시 SDK 가 백그라운드에서 Newtonsoft.Json 13.0.3 을 NuGet에서 다운로드해 ~/.nuget/packages/ 캐시에 넣고 임시 프로젝트에 PackageReference 로 연결합니다. 두 번째 실행부터는 캐시 덕분에 즉시 시작합니다.

#:package 가 실제로 어떤 csproj로 풀리는가

이것이 "임시 .csproj"가 실제로 어떻게 생겼는지 확인할 수 있는 결정적 증거입니다. dotnet run convert.cs 실행 후 임시 폴더의 내부 스펙 파일을 열어 보면 다음과 같습니다.

// ~/Library/Application Support/dotnet/runfile/greet-.../obj/greet.csproj.nuget.dgspec.json
{
  "projects": {
    ".../greet.csproj": {
      "frameworks": {
        "net10.0": {
          "dependencies": {
            "Newtonsoft.Json": {
              "target": "Package",
              "version": "[13.0.3, )"
            }
          }
        }
      }
    }
  }
}

#:package Newtonsoft.Json@13.0.3 한 줄이 내부적으로 <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> 로 풀리고, NuGet 은 이를 "최소 13.0.3 이상" 이라는 버전 제약([13.0.3, ))으로 해석합니다. .csproj 에서 <PackageReference> 를 쓸 때와 완전히 동일한 해석 규칙입니다.

실전 예시 — Unity 외부 CLI 툴

실제로 Unity 주니어가 써먹을 수 있는 스크립트 예시입니다. 개발 중 크래시 리포트 폴더에서 JSON 만 추출해 요약해 주는 도구입니다.

C#
// crash_summary.cs — 팀 Slack 에 그대로 붙여 넣어도 됨
#:package System.CommandLine@2.0.0-beta4.22272.1
#:property LangVersion=preview

using System.CommandLine;

var folderOpt = new Option<DirectoryInfo>("--folder") { IsRequired = true };
var root = new RootCommand { folderOpt };
root.SetHandler((DirectoryInfo dir) =>
{
    var jsons = dir.EnumerateFiles("*.json", SearchOption.AllDirectories);
    Console.WriteLine($"[요약] {dir.FullName}: {jsons.Count()}개 크래시 리포트");
}, folderOpt);
return await root.InvokeAsync(args);

실행:

dotnet run crash_summary.cs -- --folder /tmp/crashes

디렉티브 두 줄로 NuGet 패키지 두 개를 가져오고, CLI 파싱·비동기 실행까지 완성된 도구가 됩니다. Python 스크립트에 argparse 를 쓰는 감각과 비슷하지만, 정적 타입 + BCL 전체가 따라옵니다.


5. 함정과 주의사항

함정 1: #:property정확한 문법은 Key=Value

여러 초기 블로그 글이 #:property LangVersion preview 처럼 공백으로 구분하는 예시를 보여주지만, 실제 도구(특히 dotnet project convert)는 = 를 요구합니다. 실수하면 변환이 실패합니다.

C#
// ❌ 잘못된 문법 — dotnet project convert 가 거부한다
#:property LangVersion preview

// error: The directive should contain a name without special characters
// and an optional value separated by '=' like '#:property Name=Value'.
C#
// ✅ 올바른 문법
#:property LangVersion=preview

dotnet run 만 쓸 때는 너그럽게 통과할 수 있지만, 나중에 정식 프로젝트로 승격할 때 dotnet project convert 가 실패합니다. 처음부터 = 형식을 쓰는 것이 안전합니다.

함정 2: 임시 폴더는 캐시일 뿐 — 소스가 유일한 진실

파일 기반 앱을 디버깅하다 보면 "컴파일은 성공했는데 동작이 이상하다"는 상황이 생깁니다. 임시 폴더(~/Library/.../runfile/app-.../bin/debug/app.dll) 를 직접 열어서 분석해 봤자 의미가 없습니다.

# ❌ 임시 DLL을 강제로 다시 실행? — 캐시가 최신이 아닐 수 있다
dotnet ~/Library/Application\ Support/dotnet/runfile/app-xxxx/bin/debug/app.dll
# ✅ 언제나 소스를 다시 실행 — SDK가 해시 비교 후 필요하면 재빌드
dotnet run app.cs

캐시 경로는 SDK가 관리하는 내부 구현입니다. 사용자가 직접 손대면 소스와 DLL 이 어긋나서 혼란이 생깁니다. 소스가 유일한 "진실의 근원(source of truth)"입니다.

함정 3: Unity 프로젝트 내부 코드는 파일 기반 앱으로 대체할 수 없다

Unity 주니어가 가장 혼동하기 쉬운 부분입니다. 결론부터 말하면:

  • Unity 프로젝트 외부에서 빌드 파이프라인·에셋 변환·데이터 검증 도구로 쓰는 것은 이상적입니다.
  • Unity 프로젝트 내부MonoBehaviour, ScriptableObject, Editor 스크립트를 파일 기반 앱으로 대체할 수 없습니다.

이유: Unity는 UnityEngine.dll, UnityEditor.dll 참조와 함께 자체 컴파일 파이프라인(Assembly-CSharp, Asmdef, IL2CPP 등)을 돌립니다. dotnet run 이 만드는 임시 프로젝트는 Microsoft.NET.Sdk 기반이라 UnityEngine을 참조할 수 없습니다.

C#
// ❌ 이런 코드를 .cs 로 만들어 dotnet run 할 수 없다
using UnityEngine;

public class Player : MonoBehaviour       // UnityEngine 참조 불가
{
    void Update() { transform.Translate(Vector3.forward); }
}
C#
// ✅ 반대로, Unity 빌드 파이프라인을 보조하는 외부 툴은 완벽하게 적합
#:package SixLabors.ImageSharp@3.1.5

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

// Resources/Sprites/ 를 훑어서 모두 512x512 로 리사이즈하는 사전 처리 툴
foreach (var f in Directory.EnumerateFiles("Resources/Sprites", "*.png"))
{
    using var img = Image.Load(f);
    img.Mutate(x => x.Resize(512, 512));
    img.Save(f);
}

Unity 프로젝트 자체가 아니라 Unity 프로젝트를 둘러싼 자동화 작업에만 쓴다고 기억하면 됩니다.

함정 4: 규모가 커지면 정식 프로젝트로 승격해야 한다

파일 기반 앱의 매력은 "한 파일에 모두 담긴" 간결함입니다. 그런데 팀이 공동 유지보수하는 도구로 자라나면 그 매력이 부담으로 바뀝니다.

상황 권장 조치
파일이 500줄 초과 여러 .cs 로 분리 — 정식 프로젝트로 전환
2명 이상이 동시 수정 Git 충돌 빈발 — 정식 프로젝트로 전환
단위 테스트 필요 xunit, nunit 참조 복잡 — 정식 프로젝트로 전환
빌드 출력 커스터마이징 필요 .csproj 직접 편집 — 정식 프로젝트로 전환
일회성 스크립트 (100줄 미만) 파일 기반 유지

전환은 명령 한 줄입니다.

dotnet project convert convert.cs

실행하면 convert/ 폴더가 생기고 그 안에 디렉티브가 .csproj 로 옮겨진 convert.csproj + 디렉티브가 제거된 convert.cs 가 들어갑니다. 실제 실행 결과입니다.

<!-- convert/convert.csproj (자동 생성됨) -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <LangVersion>preview</LangVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
  </ItemGroup>
</Project>
C#
// convert/convert.cs (디렉티브 제거됨)
using Newtonsoft.Json;

string csv = File.ReadAllText("ko.csv");
// ... (나머지 본문 그대로)

주의할 점은 원본 convert.cs 는 그대로 남고 새 폴더에 복사본이 생긴다는 것입니다. Git 을 쓰고 있다면 원본을 지우고 새 폴더로 이동한 뒤 커밋하면 깔끔한 전환 히스토리가 됩니다.


6. C# 버전별 변화

파일 기반 앱은 C# 14 (.NET 10 SDK) 에서 처음 도입되는 신기능입니다. 이전 버전에서는 존재하지 않았으므로 "Before/After" 쌍보다는 "무엇이 어떤 순서로 추가되었는가"가 더 적절합니다.

버전 변화
C# 1.0 ~ C# 8 .csproj + Program.cs + static void Main(string[] args) 필수
C# 9 (.NET 5) Top-level statements 도입 — class, Main 생략 가능. 다만 여전히 .csproj 는 필수
C# 10 (.NET 6) 전역 using(global using) + 암묵적 using(implicit using)using System; 을 SDK가 자동 주입
C# 14 (.NET 10) 파일 기반 앱.csproj 까지 선택 사항으로. dotnet run app.cs 로 단일 .cs 실행. #:sdk, #:property, #:package 디렉티브와 dotnet project convert 마이그레이션 지원

즉 "Hello, World"를 쓰는 데 필요한 보일러플레이트가 C# 버전을 따라 단계적으로 줄어들어 왔다는 관점에서 보면 더 이해하기 쉽습니다.

C#
// C# 8 이하 — .csproj + 전체 구조 필요
using System;
namespace Hello
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello");
        }
    }
}
C#
// C# 9 ~ C# 13 — .csproj 는 필수, 본문은 한 줄
Console.WriteLine("Hello");
C#
// C# 14 — .csproj 도 필요 없음
Console.WriteLine("Hello");
// 단, 저장 파일명은 .cs 하나만 있으면 되고 `dotnet run app.cs` 로 실행

언어 문법 자체는 C# 9 이후로 변함이 없다는 점에 주목합니다. 파일 기반 앱은 언어 확장이 아니라 SDK·툴체인 확장입니다. IL 이 동일하다는 것과 일관됩니다.


7. 정리

핵심 체크리스트

  • [ ] 파일 기반 앱은 .csproj 없이 단일 .cs 를 실행하는 .NET 10 (C# 14) 신기능이다.
  • [ ] 내부적으로 SDK 가 임시 폴더에 .csproj 를 만들고 Roslyn + MSBuild 로 빌드한 뒤 실행한다. 언어 확장이 아닌 툴체인 확장이다.
  • [ ] #:package Name@Version, #:property Key=Value, #:sdk SdkName 세 가지 디렉티브로 빌드를 제어한다. #:property반드시 = 로 구분한다.
  • [ ] 파일 기반 앱과 정식 dotnet new console 프로젝트의 IL 은 완전히 동일하다 — 실행 경로만 다르다.
  • [ ] 임시 결과물은 ~/Library/Application Support/dotnet/runfile/{해시}/ (macOS/Linux) 또는 %LOCALAPPDATA%\Temp\dotnet\runfile\ (Windows) 에 캐싱되며, 소스가 바뀌지 않으면 재실행이 빠르다.
  • [ ] 규모가 커지면 dotnet project convert app.cs 로 정식 프로젝트로 승격한다. 원본 .cs 는 남고, 새 폴더에 디렉티브가 제거된 사본과 .csproj 가 생긴다.
  • [ ] Unity 주니어 기준: Unity 프로젝트 내부 코드(MonoBehaviour 등)는 대체 불가, Unity 외부 보조 도구(에셋 변환, CI 스크립트, 데이터 검증)에는 이상적이다.
  • [ ] 100줄 미만 1회성 스크립트는 파일 기반, 500줄 이상 또는 팀 공동 유지보수 대상은 정식 프로젝트가 기준이다.

한 문장 요약

파일 기반 앱은 C# 에 "스크립트 언어의 입구"를 달아주는 기능이다 — 런타임·IL·성능은 정식 프로젝트와 동일하지만, 단 한 개의 .cs 파일만 있으면 즉시 실행되므로 학습·프로토타입·CI 스크립트·Unity 외부 툴에서 빛을 발한다.
반응형

+ Recent posts