Minimal APIs in .NET 6 ํˆฌ์–ด - slog

Feather Http API๋ผ๊ณ ๋„ ํ•˜๋Š” Minimal APIs์— ์ต์ˆ™ํ•ด์ง€๊ธฐ ์œ„ํ•ด ์œ— ๊ธ€์˜ ๋‚ด์šฉ์„ ๋”ฐ๋ผํ•ด๋ณด๋ฉด์„œ ๊ฐ๊ฐ์„ ์ตํ˜€๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์—ญ์‹œ ์ฝ”๋”ฉ์œผ๋กœ ์ต์ˆ™ํ•ด์ ธ์•ผ ํ•˜๋‹ˆ๊นŒ์š”.

์ฒ˜์Œ ์†Œ์‹์„ ์ ‘ํ–ˆ์„ ๋•Œ ํ•œ์ฐธ ํ•™์ƒ๋“ค์—๊ฒŒ ํŒŒ์ด์ฌ์˜ Flask๋ฅผ ์ด์šฉํ•ด REST API๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•˜๋Š” ๊ณผ์ •์ค‘์— ์žˆ์—ˆ๋Š”๋ฐ, ์œ ์‚ฌํ•œ ์ฝ”๋“œ๋Ÿ‰์œผ๋กœ ์ด์ œ C#์œผ๋กœ๋„ REST API๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด๋ณด์—ฌ ์ธ์ƒ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ ํ•˜๋Š” ๊ณผ์ •์€ ๋Œ“๊ธ€๋กœ ์ง„ํ–‰ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ข‹์•„์š” 2

๋จผ์ € .NET 6 Preview 6 SDK๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  Visual Studio 2019 Preview๋กœ ์ง„ํ–‰ํ•ด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

๋จผ์ € ์ฝ˜์†” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•œ ํ›„, csproj๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ณ€๊ฒฝ ํ•ฉ๋‹ˆ๋‹ค.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
</Project>

์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด๋„ ๋˜์ง€๋งŒ ํˆฌ์–ด์— ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์ด๋ ‡๊ฒŒ ์ง„ํ–‰์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
await app.RunAsync();

using๋„ ์—†๊ณ  ๋ญ”๊ฐ€ ์ด์ƒํ•˜์ฃ ? C# 9์—์„œ๋Š” ์•„์ง์€ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ณ€๊ฒฝํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

using System;
using Microsoft.AspNetCore.Builder;

var app = WebApplication.Create(args);
app.MapGet("/", (Func<string>)(() => "Hello World!"));
await app.RunAsync();

์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์ž˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

(Func<string>)์œผ๋กœ์˜ ๋ช…์‹œ์  ์บ์ŠคํŠธ๋Š” C# 10์˜ Lambda improvements์— ์˜ํ•ด์„œ ์—†์–ด์งˆ ์˜ˆ์ •์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ , global using์„ ํ†ตํ•ด ๋งˆ์น˜ Blazor์˜ _Imports.razor์™€ ์œ ์‚ฌํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋  ์˜ˆ์ •์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ข‹์•„์š” 2

C# 10๋ฅผ ํ™œ์„ฑํ™” ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. .NET 6 Preview 4 SDK๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๊ณ  Visual Studio 2019 Preview์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

csprojํŒŒ์ผ์— <LangVersion>preview</LangVersion>์„ ์ถ”๊ฐ€ ํ•ฉ๋‹ˆ๋‹ค.

<Project Sdk="Microsoft.NET.Sdk.Web">
	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<LangVersion>preview</LangVersion>
	</PropertyGroup>
</Project>

usings.csํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ์ฒ˜๋Ÿผ ํ•ฉ๋‹ˆ๋‹ค.

global using Microsoft.AspNetCore.Builder;

์ด์ œ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
await app.RunAsync();

^^;

์ข‹์•„์š” 2

usings.cs

global using System.Threading.Tasks;
global using Microsoft.AspNetCore.Builder;
global using System.Net.Http;
var app = WebApplication.Create(args);
app.MapGet("/quote", async () =>
    await new HttpClient().GetStringAsync("https://ron-swanson-quotes.herokuapp.com/v2/quotes"));
await app.RunAsync();

์ข‹์•„์š” 2
var app = WebApplication.Create(args);
app.MapGet("/person", () => new Person("๋ชจ์ด", "๋””"));
await app.RunAsync();

public record struct Person(string FirstName, string LastName);

์ข‹์•„์š” 2
var app = WebApplication.Create(args);
app.MapPost("/person", (Person p) => $"์ƒˆ๋กœ์šด ์‚ฌ๋žŒ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. : {p.LastName}{p.FirstName}");
await app.RunAsync();
public record struct Person(string FirstName, string LastName);

Windows 10 ํ„ฐ๋ฏธ๋„์—์„œ UTF-8์„ ์›ํ• ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, ๋‹ค์Œ์˜ ์„ค์ •์„ ์ฒดํฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ข‹์•„์š” 2

์Šฌ๋กœ๊ทธ๋ฅผ ๋งˆ๋ฌด๋ฆฌ ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํžˆ ์ฝ”๋”ฉ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ConcurrentDictionary<string, string> dict = new(StringComparer.OrdinalIgnoreCase)
{
["Apple"] = "์‚ฌ๊ณผ",
["Piano"] = "ํ”ผ์•„๋…ธ"
};

var app = WebApplication.Create(args);

app.MapGet("/dict", () => dict);
app.MapPost("/dict", (Word word) => dict[word.Eng] = word.Kor);
app.MapGet("/dict/{eng}", (string eng) => new { Response = dict[eng] });
app.MapDelete("/dict/{eng}", (string eng) => dict.TryRemove(eng, out var _));

await app.RunAsync();

record Word(string Eng, string Kor);

์ข‹์•„์š” 2