<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>CLIEL LAB</title>
    <link>https://cliel.tistory.com/</link>
    <description>누구냐 넌?</description>
    <language>ko</language>
    <pubDate>Sun, 24 May 2026 16:55:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>클리엘</managingEditor>
    <image>
      <title>CLIEL LAB</title>
      <url>https://tistory1.daumcdn.net/tistory/3198881/attach/b54ab3ddabe146d0a8078b8317eed036</url>
      <link>https://cliel.tistory.com</link>
    </image>
    <item>
      <title>[C# 14 / .NET 10] 흐름제어, Type 변환, 예외 처리</title>
      <link>https://cliel.tistory.com/entry/C-14-NET-10-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;# 14에 대한 3번째 순서에서는 변수와 array를 통한 data다루기와 함께, 의사결정/반복수행과 같은 실제 program의 동작방식을 살펴보고, 서로 다른 type간 형변환과 예외처리를 포함하여 application작성에 관한 전반적인 사항을 알아보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 연산자&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산자라함은 실제값 또는 피연산자를 대상으로 사칙연산과 같은 동작을 수행하도록 하는 것을 말합니다. 이때 연산자는 연산결과에 대한 값을 반환할 수 있고 이 값은 다시 다른 변수에 할당할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 산술연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 이항(binary) 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 보편적인 연산자이며 2개의 피연산자를 필요로 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 21px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 21px;&quot;&gt;var resultVariable = firstVariable [연산자] secodVariable;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 이항연산자의 일반적인 사용방법을 나타내고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768291537465&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int x = 10;
int y = 20;

int resultAdd = x + y;
int resultMultiple = x * y;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 일반적인 이항연산자는 +, -, *, / 정도로 볼 수 있고 다른 하나는 % 인데 이는 나머지로 생각할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768456212451&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
int j = 3;

Console.WriteLine($&quot;{i % j}&quot;); //결과 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 단항(unary) 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단항 연산자는 하나의 피연산자를 필요로 하는 것으로 피연산자의 앞이나 뒤에서 연산을 수행할 수 있습니다. 단항 연산자를 사용하는 가장 대표적인 사례로 증감/감소연산자를 예로 들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768291565724&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
int a = ++i;
int b = i++;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 typeof나 nameof, sizeof도 단한 연산자에 해당할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768291578766&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Type resultType = typeof(int);
string resultName = nameof(i);
int resultByteSize = sizeof(int);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++와 --에서 ++는 1씩 증가시키는 증감연산자, --를 1씩 감소시키는 감소연산자라고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768377871855&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
int j = i++;

Console.WriteLine($&quot;{i} / {j}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 증감연산자를 사용하여 변수 i의 값을 1만큼 증기시키고 있습니다. 위 예제를 실행하면 결과값으로 '11 / 10'을 나타내게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++는 1만큼 증가시킨다고 했는데 왜 결과에서 j의 값이 10이라고 할까? 그 이유는 ++연산자가 i의 값을 j에 할당하고 난 뒤에 실행되었기 때문입니다. 이런것을 후위연산자라고 하며 만약 i변수의 값이 j에 할당되기 전에 먼저 ++가 실행되어야 한다면 이를 '++i'와 같이 변수앞에 단항연산자를 붙여줘야 하고 이를 선행연산자라고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768378357707&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
int j = ++i;

Console.WriteLine($&quot;{i} / {j}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;위 예제는 ++를 선행연산자로 적용한 것이며 예제를 실행하면 '11 / 11'이라는 결과를 표시할 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;증감, 감소연산자를 할당연산자와 결합하여 사용할때 자칫 code를 읽는데서 잘못된 예상결과의 판단을 불러올 수 있습니다. 이러한 우려 때문에 Swift언어의 경우 version 3부터 단항연산자를 지원하지 않기도 합니다. 따라서 증감, 감소연산자를 사용하더라도 되도록이면 할당연산자와 결합하는 것을 피하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 삼항(ternary) 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼항 연산자는 아래와 같이 3개의 피연산자를 필요로 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;var result = first ? second : third&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서는 삼항 연산자의 가장 흔한 것으로 if문과 유사한 '?:'를 사용하는 조건부 연산자를 나타내고 있습니다. 여기서 first는 boolean값을 표현하는 피연산자가 와야하며 second는 first가 true인 경우 반환할 값을, third는 first가 false인 경우 반환할 값을 나타냅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768378469428&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;

string result = i &amp;gt; 10 ? &quot;i는 10보다 큽니다.&quot; : &quot;i는 10과 같거나 작습니다.&quot;;

Console.WriteLine(result);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 예제는 실제 삼항연산자를 사용해 조건부 연산을 수행한 것으로 이것을 if문으로 바꿔보면 다음과 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768378512654&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (i &amp;gt; 10)
{
    result = &quot;i는 10보다 큽니다.&quot;;
}
else
{
    result = &quot;i는 10과 같거나 작습니다.&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 많은 C#개발자가 가능한 경우 if문을 사용하기 보다는 삼항연산자를 사용하는 경우가 많습니다. Code를 더욱 간소화할 수 있고 읽기에도 익숙해진다면 더 명확한 code가 될 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 할당 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 예제를 통해 사용되어온 '='문자가 바로 할당연산자입니다. 할당연산자는 다른연산자와 결합이 가능하므로 아래와 같이 code를 간소화하여 사용하는 경우도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768460953188&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 1;
i += 1 // i = i + 1;
i -= 1 // i = i - 1;
i *= 1 // i = i * 1;
i /= 1 // i = i / 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) NULL 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당계열의 연산자로서 기본적으로 값을 활당하지만 만약 값이 null인 경우 이를 대체하는 다른 값으로 대신 할당하도록 하는 연산자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NULL 병합 연산자는 아래와 같이 3가지를 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 118px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 7.28679%; height: 21px;&quot;&gt;연산자&lt;/td&gt;
&lt;td style=&quot;width: 31.124%; height: 21px;&quot;&gt;명칭&lt;/td&gt;
&lt;td style=&quot;width: 61.5891%; height: 21px;&quot;&gt;역활&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 63px;&quot;&gt;
&lt;td style=&quot;width: 7.28679%; height: 63px;&quot;&gt;?.&lt;/td&gt;
&lt;td style=&quot;width: 31.124%; height: 63px;&quot;&gt;Null&amp;nbsp;조건부&amp;nbsp;연산자&amp;nbsp;(Null-conditional)&lt;/td&gt;
&lt;td style=&quot;width: 61.5891%; height: 63px;&quot;&gt;개체가&amp;nbsp;null이라면&amp;nbsp;null을&amp;nbsp;반환하며&amp;nbsp;그렇지&amp;nbsp;않으면&amp;nbsp;개체를&amp;nbsp;통해&amp;nbsp;지정한&amp;nbsp;해당&amp;nbsp;속성의&amp;nbsp;값을&amp;nbsp;대신&amp;nbsp;반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 7.28679%; height: 17px;&quot;&gt;??&lt;/td&gt;
&lt;td style=&quot;width: 31.124%; height: 17px;&quot;&gt;Null&amp;nbsp;병합&amp;nbsp;연산자&amp;nbsp;(Null-coalescing)&lt;/td&gt;
&lt;td style=&quot;width: 61.5891%; height: 17px;&quot;&gt;왼쪽&amp;nbsp;표현식이&amp;nbsp;null이라면&amp;nbsp;오른쪽&amp;nbsp;값을&amp;nbsp;반환하지만&amp;nbsp;그렇지&amp;nbsp;않으면&amp;nbsp;아무것도&amp;nbsp;동작도&amp;nbsp;수행하지&amp;nbsp;않습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 7.28679%; height: 17px;&quot;&gt;??=&lt;/td&gt;
&lt;td style=&quot;width: 31.124%; height: 17px;&quot;&gt;Null&amp;nbsp;병합&amp;nbsp;할당&amp;nbsp;연산자&amp;nbsp;(Null-coalescing&amp;nbsp;assignment)&lt;/td&gt;
&lt;td style=&quot;width: 61.5891%; height: 17px;&quot;&gt;왼쪽&amp;nbsp;표현식이&amp;nbsp;null이라면&amp;nbsp;오른쪽&amp;nbsp;값을&amp;nbsp;반환하지만&amp;nbsp;그렇지&amp;nbsp;않으면&amp;nbsp;아무것도&amp;nbsp;동작도&amp;nbsp;수행하지&amp;nbsp;않습니다.&amp;nbsp;단순히&amp;nbsp;표현식을&amp;nbsp;평가하는&amp;nbsp;??과는&amp;nbsp;달리&amp;nbsp;'??='연산자는&amp;nbsp;변수가&amp;nbsp;null인&amp;nbsp;경우&amp;nbsp;즉각&amp;nbsp;값을&amp;nbsp;할당합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 위 연산자에 대한 사용방식을 간단히 표현하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768461057407&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int age = Employee?.Age ?? 0;
EmployeeName ??= &quot;unknown&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 첫번째 구문의 '?.'연산자는 Age속성의 값을 할당하기 전에 Employee가 null인지를 우선 확안하게 됩니다. 만약 Employee가 null이라면 null자체를 반환하게 되고 그 상태에서 ?? 연산자를 통해 null이 확인되어 0을 할당하게 됩니다. 만약 Employee가 null이 아니라면 그때는 Employee?.Age값을 할당할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 구문은 EmployeeName이 null이라면 '??='연산자에 의해 여기에 &quot;unknown&quot;이라는 문자열값을 할당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 null연산자는 null이 발생하는 경우 null에 대한 예외를 일으키는 대신 대안으로 제시한 값을 대신 할당하게 함으로서 code를 간소하고 효휼적으로 작성할 수 있도록 해줍니다. 만약 위와 같은 null 연산자를 사용하지 않는다면 우리는 아래와 같이 null에 대한 예외사항을 직접 작성해줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768461100288&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int age;
if (Employee is not null)
{
    age = Employee.Age;    
}
else
{
    age = 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) NULL 조건부 할당 연산자 (Null-conditional assignment operator)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 14 부터 Null 조건부 할당 연산자는 instance를 포함하는 경우에만 속성이나 field에 값을 할당할 수 있습니다. 예를 들어 Employee라는 class와 함께&lt;/p&gt;
&lt;pre id=&quot;code_1768461863767&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Employee
{
    public string Name { get; set; } = string.Empty;
    public int Age { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Employee의 instance를 변경하는 method를 정의해야 한다면&lt;/p&gt;
&lt;pre id=&quot;code_1768461875478&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void UpdateAge(Employee? emp, int age)
{
    if (emp is not null)
        emp.Age = age;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Null 조건 할당 연산자를 사용해 위와 동일한 구현을 아래와 같이 간소하게 표현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768461886318&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void UpdateAge(Employee? emp, int age)
{
    emp?.Age = age;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, emp가 null이 아니라면 emp의 Age속성은 age값으로 변경되지만 그렇지 않다면 아무런 후속동작도 수행되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Null 조건부 할당 연산자에 관해서는 아래 link를 참고해 더 자세히 알아볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/discussions/8676&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/dotnet/csharplang/discussions/8676&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768461908706&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Null-conditional assignment &amp;middot; dotnet csharplang &amp;middot; Discussion #8676&quot; data-og-description=&quot;Summary Permits assignment to occur conditionally within a a?.b or a?[b] expression. using System; class C { public object obj; } void M(C? c) { c?.obj = new object(); } using System; class C { pub...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/dotnet/csharplang/discussions/8676&quot; data-og-url=&quot;https://github.com/dotnet/csharplang/discussions/8676&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KCdPH/dJMb85WMHtF/iAY755gjBEIeiANlzKZjn1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dfGdKJ/dJMb88FYlf6/yFC9H2ia5kjsn7lKkUcsHk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/discussions/8676&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/dotnet/csharplang/discussions/8676&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KCdPH/dJMb85WMHtF/iAY755gjBEIeiANlzKZjn1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dfGdKJ/dJMb88FYlf6/yFC9H2ia5kjsn7lKkUcsHk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Null-conditional assignment &amp;middot; dotnet csharplang &amp;middot; Discussion #8676&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Summary Permits assignment to occur conditionally within a a?.b or a?[b] expression. using System; class C { public object obj; } void M(C? c) { c?.obj = new object(); } using System; class C { pub...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 논리 연산자(Logical operators)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논리연산자는 대게 boolean값을 평가하게 되므로 그 결과로 true나 false를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논리연산자중 '이진 논리 연산자(binary logical operator)'는 2개 이상의 boolean값을 평가하는 것으로 연산방식에 따라 AND, OR, XOR(exclusive OR)로 나눌 수 있으며 아래와 같이 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768543928454&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool a = true;
bool b = false;

Console.WriteLine($&quot;AND a : {a &amp;amp; a}&quot;);
Console.WriteLine($&quot;AND b : {b &amp;amp; b}&quot;);
Console.WriteLine($&quot;AND a/b : {a &amp;amp; b}&quot;);
Console.WriteLine();
Console.WriteLine($&quot;OR a : {a | a}&quot;);
Console.WriteLine($&quot;OR b : {b | b}&quot;);
Console.WriteLine($&quot;OR a/b : {a | b}&quot;);
Console.WriteLine();
Console.WriteLine($&quot;XOR a : {a ^ a}&quot;);
Console.WriteLine($&quot;XOR b : {b ^ b}&quot;);
Console.WriteLine($&quot;XOR a/b : {a ^ b}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;118&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o0EXe/dJMcab30HKI/ma8wzIIl4Fyvx66DNoy1Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o0EXe/dJMcab30HKI/ma8wzIIl4Fyvx66DNoy1Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o0EXe/dJMcab30HKI/ma8wzIIl4Fyvx66DNoy1Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo0EXe%2FdJMcab30HKI%2Fma8wzIIl4Fyvx66DNoy1Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;118&quot; height=&quot;186&quot; data-origin-width=&quot;118&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 사용된 '&amp;amp;'는 AND 논리연산자로, 피연산자 모두 true여야만 결과로 true를 표시합니다. '|'문자는 OR 연산자로 이 경우에는 피연산자중 단 하나만 true여도 그 결과로 true를 반환하며, '^'문자는 XOR 연산자로 피연산자중 하나면 true여도 그 결과로 true를 반환하지만 피연산자 모두 true여서는 안됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 조건부 논리 연산자(Conditional logical operators)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건부 논리 연산자는 위에서 소개한 논리연산자를 이중으로 표현(&amp;amp;&amp;amp;, ||)한 것을 말하는 것으로 조건에 따라 연산처리의 수행여부를 결정합니다. 예제를 위해 우선 아래와 같은 간단한 함수 하나를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768543991814&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static bool DoSomething()
{
    Console.WriteLine(&quot;함수 실행됨.&quot;);
    return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 예제는 top-level program 기능을 사용하는 경우라면 Program.cs의 어디에 만들어도 문제가 되지 않지만 가급적 가장 아래에 배치하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수는 호출되면 간단한 message를 표시한 뒤 그 결과로 true값을 반환합니다. 함수나 method의 작성에 관한 자세한 사항은 추후에 자세히 다룰 것이므로 지금은 예제의 함수가 어떤동작을 하고 어떤 것을 반환하는지만 알아두면 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 만들고 나면 그 위에 아래의 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768544027731&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool a = true;
bool b = false;

Console.WriteLine($&quot;a &amp;amp; DoSomething() : {a &amp;amp; DoSomething()}&quot;);
Console.WriteLine($&quot;b &amp;amp; DoSomething() : {b &amp;amp; DoSomething()}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;181&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OgxHF/dJMcajt9VF9/iCcr31KwXjxu6KL3ieapkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OgxHF/dJMcajt9VF9/iCcr31KwXjxu6KL3ieapkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OgxHF/dJMcajt9VF9/iCcr31KwXjxu6KL3ieapkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOgxHF%2FdJMcajt9VF9%2FiCcr31KwXjxu6KL3ieapkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;181&quot; height=&quot;69&quot; data-origin-width=&quot;181&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 각각의 연산작업에 2번씩 실행되었음을 알 수 있습니다. 여기서 어떤 분들은 위 결과에 대해 a는 납득이 되지만 b는 그렇지 않다라는 생각을 가질 수도 있습니다. 왜냐하면 '&amp;amp;'는 AND연산으로 피연산자 모두가 true여야만 그 결과가 true가 되는데 b는 처음부터 false로 시작하기 때문에 굳이 함수를 실행하지 않아도, 즉, 함수의 실행여부와는 관계없이 그 결과가 false가 되기 때문입니다. 다시 말해 b를 피연산자로 하는 경우 함수를 실행할 필요가 없다는 결론에 이르게 되는 것입니다. 하지만 단순한 논리연산자는 이러한 경우를 감안할 수 없으므로 무엇인가 다른방식을 적용해야 하고 그 결과로 조건부 논리 연산자가 등장하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 위 예제를 아래와 같이 바꿔보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768544100329&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;a &amp;amp;&amp;amp; DoSomething() : {a &amp;amp;&amp;amp; DoSomething()}&quot;);
Console.WriteLine($&quot;b &amp;amp;&amp;amp; DoSomething() : {b &amp;amp;&amp;amp; DoSomething()}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 변경하고 다시 실행하면 이번에는 다른 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;187&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0AWfk/dJMcaaxg31U/7u1qL16RnFiGgLNKGGJgqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0AWfk/dJMcaaxg31U/7u1qL16RnFiGgLNKGGJgqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0AWfk/dJMcaaxg31U/7u1qL16RnFiGgLNKGGJgqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0AWfk%2FdJMcaaxg31U%2F7u1qL16RnFiGgLNKGGJgqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;187&quot; height=&quot;55&quot; data-origin-width=&quot;187&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 a변수에 대해서만 함수가 두번 실행되었고 b에서는 한번만 실행되었음을 알 수 있습니다. b는 false이므로 그 뒤 어떤 값이 오더라도 결과는 false가 될것이고 따라서 함수를 실행하는게 무의합니다. '&amp;amp;&amp;amp;'연산자는 바로 이러한 조건의 논리를 그대로 적용하기 때문에 위와 같은 결과를 만들어낼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위에서 설명한 조건부 논리 연산자의 특징 때문에 이를 단락(Short-circuiting)으로 표현하기도 합니다. 조건부 논리 연산자는 적절히 사용하면 Application을 더욱 효휼적으로 작성할 수 있지만 자칫 예기치 않은 bug를 만들어낼 가능성도 있습니다. 특히 함수가 항상 실행되어야 한다거나 내지는 실행된다라고 예상하는 경우 Application의 흐름을 전혀 다른 방향으로 이끌 수 있으므로 함수와의 결합하여 사용할때는 특히 주의해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) Bit 연산자 및 이진 shift 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bit연산자는 2개 이상의 bit로 표현된 값에서 동일한 column의 각 bit를 비교하는 연산자이며, 이진 shift 연산자는 bit값을 이동시키는 연산자로서 이러한 특징을 이용하면 일반적인 산술연산자보다 더 빠른 속도로 산술연산을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 연산자의 예제를 만들어보기 위해 우선 아래와 같이 2개의 int형 변수를 선언합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768813551257&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
int j = 4;

Console.WriteLine($&quot;i : {i, 3} | {i:B8}&quot;);
Console.WriteLine($&quot;j : {j, 3} | {j:B8}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 숫자 3은 3개 column에 대한 오른쪽 정렬을, B8은 8자리 binary표시를 의미하므로 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;135&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clrTia/dJMcadHvSbc/AgJJVX58sn9DLVXVizaer1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clrTia/dJMcadHvSbc/AgJJVX58sn9DLVXVizaer1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clrTia/dJMcadHvSbc/AgJJVX58sn9DLVXVizaer1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclrTia%2FdJMcadHvSbc%2FAgJJVX58sn9DLVXVizaer1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;135&quot; height=&quot;35&quot; data-origin-width=&quot;135&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태에서 bit연산자를 사용한 아래 예제를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768813576867&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine();
Console.WriteLine($&quot;i &amp;amp; j : {i &amp;amp; j, 3} | {i &amp;amp; j:B8}&quot;);
Console.WriteLine($&quot;i | j : {i | j, 3} | {i | j:B8}&quot;);
Console.WriteLine($&quot;i ^ j : {i ^ j, 3} | {i ^ j:B8}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bit연산자는 &amp;amp;, |, ^ 를 사용할 수 있으며 연산방식은 이전에 설명한 논리연산자와 동일합니다. 즉, 각 값의 bit를 비교해 &amp;amp;인 경우 AND이므로 동일한 값에 1을, |는 OR이므로 둘중 하나라도 1이면 1을, ^는 XOR이므로 둘중 하나만 1인 경우 1을 표시합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;amp;, | 연산자를 산술값에 적용하면 bit연산자가 되지만 boolean값에 적용하면 논리연산자가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;161&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckQWfr/dJMcahiSU4W/zdaUyLCwv0vu5NxutTKPG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckQWfr/dJMcahiSU4W/zdaUyLCwv0vu5NxutTKPG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckQWfr/dJMcahiSU4W/zdaUyLCwv0vu5NxutTKPG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckQWfr%2FdJMcahiSU4W%2FzdaUyLCwv0vu5NxutTKPG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;161&quot; height=&quot;107&quot; data-origin-width=&quot;161&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음으로 Shift 연산자를 사용한 아래의 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768813638023&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine();
Console.WriteLine($&quot;i &amp;lt;&amp;lt; 3 : {i &amp;lt;&amp;lt; 3} | {i &amp;lt;&amp;lt; 3:B8}&quot;);
Console.WriteLine($&quot;i *  8 : {i * 8} | {i * 8:B8}&quot;);
Console.WriteLine($&quot;j &amp;gt;&amp;gt; 1 : {j &amp;gt;&amp;gt; 1} | {j &amp;gt;&amp;gt; 1:B8}&quot;);
Console.WriteLine($&quot;j - 2 : {j - 2} | {j - 2:B8}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;162&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4UNyE/dJMcac2UnOS/KtaHeBI9QyetkNhEGM3XZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4UNyE/dJMcac2UnOS/KtaHeBI9QyetkNhEGM3XZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4UNyE/dJMcac2UnOS/KtaHeBI9QyetkNhEGM3XZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4UNyE%2FdJMcac2UnOS%2FKtaHeBI9QyetkNhEGM3XZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;162&quot; height=&quot;192&quot; data-origin-width=&quot;162&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 'i &amp;lt;&amp;lt; 3'은 왼쪽 i값의 bit를 왼쪽으로 3column 이동하도록 합니다. 이동하면서 왼쪽으로 밀려난 값은 버려지며 오른쪽은 0으로 채워집니다. 그런데 예제의 결과에서 나타난것 처럼 이러한 동작은 값에 곱하기 8(* 8)을 한것과 동일한 결과를 만들어냅니다. 왼쪽부터 첫번째 1값은 64이고 두번째 1값은 16이기 때문에 결과는 80이 되기 때문입니다. 변수 j에 대한것도 마찬가지인데 'j &amp;lt;&amp;lt; 1'은 오른쪽으로 1column이동하도록 하며 그 결과는 산술연산에서 -2와 동일한 결과를 나타냅니다. shift연산자는 산술연산과 동일한 결과를 만들어내면서도 더 빠른 동작을 수행할 수 있다는 특징이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 기타 연산자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nameof은 변수나 type, member에 대해 약식이름을 반환하는 연산자이며 sizeof는 type에서 필요로 하는 memory size를 byte단위로 반환하는 연산자입니다. 기술적으로 sizeof는 unsafe code block이 필요하지만 type을 지정할때 int나 double와 같은 C# 별칭을 사용하면 unsafe를 사용하지 않아도 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768813736426&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
Console.WriteLine($&quot;변수 {nameof(i)}는 약 {sizeof(int)} byte의 memory를 필요로 합니다.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 밖에도 type의 member를 가져오기 위해 사용하는 dot(.)도 'member 접근 연산자'라고 하는 연산자이며 method의 뒤에 붙이는 괄호(())역시 호출 연산자(Invocation operator)라고 하는 연산자에 해당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768813747876&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
string iname = i.ToString();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 선택문(분기문)&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택문은 서로 다른 code간의 분기를 위한 구문을 말합니다. C#에서 사용가능한 선택문으로는 if와 switch문이 있는데 이 둘은 '선택'이라는 측면에서 비슷하지만 swicth는 일부 일반적인 상황에서 if보다 더 code를 단순화시키면서도 효휼적인 선택처리가 가능합니다. 곧 보게 되겠지만 switch는 특히, 하나의 변수에 대해 각기 다른 처리를 필요로 하는 다양한 값을 가질 수 있는 경우에 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) if를 사용한 분기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문은 boolean 형식의 문을 평가한 뒤 그 결과를 토대로 분기를 결정하는 것으로서 만약 평가 결과가 true라면 내부의 block문을 실행하게 됩니다. else문은 평가 결과가 false인 경우 실행하는데 이는 선택적이라 필요한 경우에만 추가할 수 있습니다. 또한 if문은 충첩될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문은 아래와 같은 형식으로 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768898656142&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (조건식1)
{
    //조건1이 true인 경우 실행
}
else if (조건식2)
{
    //상기 조건1이 false이고 조건2가 true인 경우 실행
}
else
{
    //상기 모든 조건이 false인 경우 실행
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 if문의 조건식은 독립적으로 구현할 수 있으며 switch와는 달리 단일값을 사용할 필요는 없습니다. 또한 else if와 else는 선택적으로 필요한 경우에만 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 if문을 사용해 비밀번호를 학인하고 그에 따른 처리를 수행하는 방법을 나타내고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768898685715&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string password = &quot;abc123&quot;;
string userInput = Console.ReadLine();

if (userInput == password)
{
    Console.WriteLine(&quot;비밀번호가 일치합니다.&quot;);
}
else
{
    Console.WriteLine(&quot;비밀번호가 일치하지 않습니다.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 결과는 입력한 값에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;211&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pjrEw/dJMcaiWmztS/tc0JZdIWhedgGhDJF9AsKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pjrEw/dJMcaiWmztS/tc0JZdIWhedgGhDJF9AsKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pjrEw/dJMcaiWmztS/tc0JZdIWhedgGhDJF9AsKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpjrEw%2FdJMcaiWmztS%2Ftc0JZdIWhedgGhDJF9AsKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;211&quot; height=&quot;34&quot; data-origin-width=&quot;211&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 괄호사용에 관해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Block은 기본적으로 괄호를 통해 표현되며 C#명세에서는 block을 복합문(compound statement)으로 언급하고 있습니다. 본래 block안의 실행문은 단일문이 와야하지만 이를 중괄호를 사용해 여러 구문으로 묶어 놓을 수 있다는 것을 나타내는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if, else, while 및 for와 같은 C#의 모든 제어문에서는 괄호를 사용하지 않고 단일문을 사용하되 그 이상의 문이 필요하다면 괄호를 사용해 하나의 덩어리로 묶어놓아야 합니다. 이러한 것을 삽입문(embedded statement)이라고 하는데 block이나 복합문은 이 삽입문중 하나이며 괄호를 사용하지 않는 단일문 역시 또 다른 style의 삽입문에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;띠라서 위 예제를 단일구문으로 변경하는 경우 괄호를 제거하고 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768898799098&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string password = &quot;abc123&quot;;
string userInput = Console.ReadLine();

if (userInput == password)
    Console.WriteLine(&quot;비밀번호가 일치합니다.&quot;);
else
    Console.WriteLine(&quot;비밀번호가 일치하지 않습니다.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보기에는 언뜻 code가 깔끔해 보일 수 있지만 사실 위와 같은 coding style은 중대한 결함을 만들 수 있기에 피하는 것이 좋습니다. 단지 예제에서의 if문뿐만 아니라 단일문을 허용하는 모든 제어문에서 괄호를 사용하지 않는 단일문의 사용은 권장되지 않습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;괄호를 사용하지 않아도 된다고 해서 꼭 그렇게 해야하는 것은 아닙니다. 괄호를 사용하지 않음으로인해 code가 효휼적으로 변하는 것도 아닙니다. 오히려 code를 읽기 어렵게 하고 관리성도 떨어지며 잠재적으로 위험한 code를 만들어낼 수도 있습니다. 예제에서는 공간의 활용을 위해 괄호를 생략할 수 있지만 실제 그러한 code를 작성하지 않도록 해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) if문에서의 조기반환 또는 guard절(Early return or guard clause)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'조기반환 또는 guard절'은 값을 반환하는 method안에서 if문을 사용하는 경우 상황에 따라 else를 사용하기 보다 if문의 조건이 만족하면(또는 만족하지 않으면) 곧장 값을 반환함으로서 code의 중첩이 깊어지는 문제를 해결하고자 하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 Method가 있다면&lt;/p&gt;
&lt;pre id=&quot;code_1768898973846&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public bool IsSchoolZoneSpeeding(int speed)
{
    if (speed &amp;gt; 30)
    {
        return true;
    }
    else
    {
        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 조기반환 또는 guard절로 변경하게 되면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768898985561&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public bool IsSchoolZoneSpeeding(int speed)
{
    if (speed &amp;gt; 30)
    {
        return true;
    }
    
    return false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식은 가급적 중첩을 줄임으로서 code의 가독성을 향상시킬 수 있고 잘못된경우 혹은 경계조건에서 조기에 단락을 실행함으로서 code실행의 효휼성을 가져올 수 있습니다. 또한 code상에서 가장 우선적이거나 중요한점을 부각할 수 있다는 이점도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 이러한 방식은 ASP.NET Core와 같은 framework에서 조기에 입력값이나 상태의 유효성을 검증하고 사전에 값을 반환함으로서 code의 흐름을 간소화하고자 하는 방법으로 아래와 같이 광범위하게 사용되고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768899066676&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public IActionResult ProcessOrder(Order order)
{
    if (order == null)
    {
        return BadRequest();
    }
    
    if (!order.IsValid())
    {
        return UnprocessableEntity();
    }
    
    // Normal processing
    _orderService.Save(order);
    
    return Ok();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 code에서 볼 수 있는 것처럼 else를 사용하지 않으면서 각각 잘못된 경우에 도달했을때 이를 처리하여 즉각적으로 method를 빠져나오도록 code를 설계하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 조건의 분리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문에서 조건문을 사용할때 조건문 자체가 너무 길어지는 경우 이를 전부 if문안에 넣기 보다 따로 분리하여 지정하는 편이 좋습니다. 예를 들어&lt;/p&gt;
&lt;pre id=&quot;code_1768899107319&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string password = Console.ReadLine();

if (password.Length &amp;gt;= 8 &amp;amp;&amp;amp;
    char.IsLetterOrDigit(password[0]) == false &amp;amp;&amp;amp;
    password.Any(char.IsDigit))
{
    //
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 if문에서는 입력된 비밀번호가 8자 이상이면서 첫글자가 특수문자이고 숫자가 포함되어 있는지 여부를 확인하고 있습니다. 이렇게 복작한 조건의 경우는 아래와 같이 분리하면 code의 가독성을 훨씬 향상시킬 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768899128572&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string password = Console.ReadLine();

bool isPasswordVaild = password.Length &amp;gt;= 8 &amp;amp;&amp;amp; //8자이상
    char.IsLetterOrDigit(password[0]) == false &amp;amp;&amp;amp; //첫글자의 특수문자여부
    password.Any(char.IsDigit); //숫자포함여부

if (isPasswordVaild) //비밀번호의 유효성 검증
{
    ///
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) if문에서의 pattern matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pattern matching은 C#7에서 도입된 기능으로 if문에서는 is keyword를 사용해 변수의 선언과 결합함으로서 code를 더 안전하게 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768981110868&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;object s = &quot;2&quot;;
int i = 1;

if (s is int j)
{
    Console.WriteLine($&quot;{i + j}&quot;);
}
else
{
    Console.WriteLine(&quot;변수 s는 int type이 아닙니다.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 if문의 조건식에서 's is int j'를 통해 is keyword를 사용하고 있습니다. 이렇게 하면 변수 s의 type을 확인하고 만약 type이 int라면 s의 값을 변수 j에 할당하게 됩니다. 그리고 이렇게 할당된 j는 if문 내부에서 자유롭게 사용할 수 있습니다. 여기서 중요한점은 이러한 방식으로 지역변수 j를 사용하면 최소한 j가 int형이라는 점을 보장할 수 있기 때문에 int에서 수행할 수 있는 작업을 더 안전하게 수행할 수 있다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;23&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLwuO5/dJMcabbUa3z/ocE8a2Bb27oxDXViR1lkBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLwuO5/dJMcabbUa3z/ocE8a2Bb27oxDXViR1lkBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLwuO5/dJMcabbUa3z/ocE8a2Bb27oxDXViR1lkBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLwuO5%2FdJMcabbUa3z%2FocE8a2Bb27oxDXViR1lkBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;207&quot; height=&quot;23&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;23&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;object s에 들어가는 값이 처음부터 &quot;2&quot;와 같은 방법을 통해 문자열로 저장하고 있으므로 s는 문자열 type이 되고 따라서 위와 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 &quot;2&quot; 대신 아래와 같이 변경하여 s변수에 2의 값이 int으로 저장되도록 하고&lt;/p&gt;
&lt;pre id=&quot;code_1768981204769&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;object s = 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 다시 실행하면 이번에는 다른 결과를 표시하게 될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;17&quot; data-origin-height=&quot;19&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2jGrb/dJMcaaRBRpR/D2WSYKdW8bxruUaEZ5rVz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2jGrb/dJMcaaRBRpR/D2WSYKdW8bxruUaEZ5rVz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2jGrb/dJMcaaRBRpR/D2WSYKdW8bxruUaEZ5rVz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2jGrb%2FdJMcaaRBRpR%2FD2WSYKdW8bxruUaEZ5rVz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;17&quot; height=&quot;19&quot; data-origin-width=&quot;17&quot; data-origin-height=&quot;19&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;is 연산자에 대한 더 자세한 내용은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768981225564&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;The &amp;#96;is&amp;#96; operator - Match an expression against a type or constant pattern - C# reference&quot; data-og-description=&quot;Learn about the C# &amp;#96;is&amp;#96; operator that matches an expression against a pattern. The &amp;#96;is&amp;#96; operator returns true when the expression matches the pattern.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cr88pb/dJMb9dHh2p5/mjns1GhevNg2ok15DEWmj1/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cr88pb/dJMb9dHh2p5/mjns1GhevNg2ok15DEWmj1/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;The `is` operator - Match an expression against a type or constant pattern - C# reference&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the C# `is` operator that matches an expression against a pattern. The `is` operator returns true when the expression matches the pattern.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2)&amp;nbsp;Switch&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch문은 하나의 단일값을 여러개의 가능한 다른 값으로 비교하고 해당 값의 case문으로 처리를 분기하는 분기문입니다. 이때 각 case문은 아래의 방법중 하나로 끝을 맺어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;break keyword 사용&lt;/li&gt;
&lt;li&gt;goto case keyword 사용&lt;/li&gt;
&lt;li&gt;어떠한 구문도 없음&lt;/li&gt;
&lt;li&gt;다른 label을 참조하는 goto keyword 사용&lt;/li&gt;
&lt;li&gt;현재 처리를 벗어나는 return keyword 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 일반적인 switch의 사용법을 나타내는 예제입니다. 각 case문에서 위와 같은 규칙이 적용되어 있음에 주목하시기 바랍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768981293513&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 2;

switch(i)
{
    case 1:
        Console.WriteLine(&quot;1&quot;);
        break;
    case 2:
        Console.WriteLine(&quot;2&quot;);
        goto case 1;
    case 3:
    case 4:
        goto Jump;
    default:
        Console.WriteLine(&quot;etc...&quot;);
        break;
}

Jump:
    Console.WriteLine(&quot;jumped!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 goto keyword는 다른 case나 지정한 label로 code의 흐름을 이동시키기서 사용됩니다. 그런데 대부분의 개발자들사이에서 goto문은 소위 'spaghetti code'를 만들 수 있다는 인식때문에 피해야할 대상으로 여겨지고 있습니다. 하지만 일부 scenario에서는 최선의 해결책이 될 수도 있습니다. 실제 Microsoft에서도 .NET Class library에 goto문을 사용하곤 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/search?q=%22goto%20%22+repo%3Adotnet%2Fruntime+language%3AC%23&amp;amp;type=code&amp;amp;ref=advsearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/search?q=%22goto%20%22+repo%3Adotnet%2Fruntime+language%3AC%23&amp;amp;type=code&amp;amp;ref=advsearch&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768981347806&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub &amp;middot; Change is constant. GitHub keeps you ahead.&quot; data-og-description=&quot;Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/search?q=%22goto%20%22+repo%3Adotnet%2Fruntime+language%3AC%23&amp;amp;type=code&amp;amp;ref=advsearch&quot; data-og-url=&quot;https://github.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/L9gy6/dJMb84p2K5Q/nRoIPwTlG3uTYkre8HpW10/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/dYm7zm/dJMb82Mw2A4/XpE0KwJPgfkSJ1IW0bML1k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://github.com/search?q=%22goto%20%22+repo%3Adotnet%2Fruntime+language%3AC%23&amp;amp;type=code&amp;amp;ref=advsearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/search?q=%22goto%20%22+repo%3Adotnet%2Fruntime+language%3AC%23&amp;amp;type=code&amp;amp;ref=advsearch&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/L9gy6/dJMb84p2K5Q/nRoIPwTlG3uTYkre8HpW10/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/dYm7zm/dJMb82Mw2A4/XpE0KwJPgfkSJ1IW0bML1k/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub &amp;middot; Change is constant. GitHub keeps you ahead.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;goto는 편리한 만큼 code를 복잡하게 만들 수 있다는 양면성을 가지고 있으므로 남발하지 말고 꼭 필요한 곳에만 쓰도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;55&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zVMR0/dJMcahpFvni/1Vk4Tzd9nKB08Eqd3poiLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zVMR0/dJMcahpFvni/1Vk4Tzd9nKB08Eqd3poiLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zVMR0/dJMcahpFvni/1Vk4Tzd9nKB08Eqd3poiLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzVMR0%2FdJMcahpFvni%2F1Vk4Tzd9nKB08Eqd3poiLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;55&quot; height=&quot;51&quot; data-origin-width=&quot;55&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 결과는 i변수에 설정되는 값에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;'Jump:' Label문은 goto문의 의해 이동되어 실행될 뿐만 아니라 위에서 아래로 흐르는 code의 흐름을 통해 실행될 수도 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Switch문에서의 pattern matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문과 마찬가지로 switch문에서도 pattern matching을 지원합니다. 따라서 case문에서는 기존 literal값대신 pattern을 대신 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#7 부터 class의 하위 type에 기반하여 code를 더 간결하게 분기할 수 있게 되었으며 지역변수의 선언과 할당을 통해 이를 더 안전하게 사용할 수 있습니다. 또한 case에서는 when keyword를 도입함으로서 더 세부적인 pattern matching을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 switch에서의 pattern matching을 사용해 보기 위해 아래와 같은 class를 정의합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;class에 관한 자세한 사항은 추후에 자세히 알아볼 것입니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1769074661903&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car
{
    public string ModelName;
    public int MaxSpeed;
    public DateTime Release;
}

class Sedan : Car
{
    public bool IsGasoline;
}

class Truck : Car
{
    public bool IsDiesel;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 Sedan과 Truck은 Car의 하위 class(sub type)이며 Car는 최상위 부모 class에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 아래와 같이 Cars 배열을 선언하고 각 type에 따라 분기하여 type과 속성명을 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769074684896&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var cars = new Car?[] {
    new Sedan {
        ModelName = &quot;Sonta&quot;, MaxSpeed = 200, Release = new( year: 2019, month: 12, day: 04 )
    },
    null,
    new Sedan {
        ModelName = &quot;Avan&quot;, MaxSpeed = 180, Release = new ( year: 2022, month: 05, day: 15 ), IsGasoline = false
    },
    new Truck {
        ModelName = &quot;Manbo&quot;, MaxSpeed = 150, Release = new ( year: 2012, month: 03, day: 29 )
    }
};

foreach (Car? car in cars)
{
    switch (car) {
        case Sedan speedCar when speedCar.MaxSpeed == 200:
            Console.WriteLine($&quot;{speedCar.ModelName}이 가장 빠른 차량임&quot;);
            break;
        case Sedan gCar when gCar.IsGasoline = false:
            Console.WriteLine($&quot;{gCar.ModelName}차량은 휘발유를 사용하지 않음&quot;);
            break;
        default:
            Console.WriteLine(&quot;그 외&quot;);
            break;
        case null:
            Console.WriteLine(&quot;개체가 null상태임&quot;);
            break;
        case Truck truck:
            Console.WriteLine($&quot;{truck.ModelName}은 {truck.Release.Year}년에 출시된 truck임.&quot;);
            break;
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제를 보면 default를 switch가운데 정의하였는데 이는 이런것도 가능하다는 것을 보여주기 위해 고의적으로 배치한 것이므로 실제 switch문을 작성할때는 default를 항상 마지막에 정의하여야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;'case Sedan speedCar when speedCar.MaxSpeed == 200;' 해당 구문은 속성 pattern matching을 통해 아래와 같이 더 간소하게 구현할 수 있습니다.&lt;br /&gt;'case&amp;nbsp;Sedan&amp;nbsp;{&amp;nbsp;MaxSpeed&amp;nbsp;:&amp;nbsp;200&amp;nbsp;}&amp;nbsp;speedCar:'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 Car? type의 cars array를 정의하였으므로 Car type을 상속받은 모든 하위 type이 올 수 있습니다. 또한 '?'문자를 통해 null가능한 type이 되었으므로 null역시 수용할 수 있습니다. 따라서 위에서 정의한 array의 순회하면서 각각의 조건에 맞는 type에 따라 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;229&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVIZfX/dJMcagYzDKx/5Mqv38CCQGOK8JS5Jlex10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVIZfX/dJMcagYzDKx/5Mqv38CCQGOK8JS5Jlex10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVIZfX/dJMcagYzDKx/5Mqv38CCQGOK8JS5Jlex10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVIZfX%2FdJMcagYzDKx%2F5Mqv38CCQGOK8JS5Jlex10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;229&quot; height=&quot;69&quot; data-origin-width=&quot;229&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2)&amp;nbsp;switch&amp;nbsp;표현식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;switch 표현식은 C#8부터 사용가능한 것으로 일반적인 switch문을 더욱 간소화할 수 있습니다. &lt;br /&gt;&lt;br /&gt;위 예제와 같은 switch문이 그리 복잡한 것은 아니지만 지금 소개할 switch 표현식은 각 case문에서 값을 반환하는 기존 방식과 달리 lambda(=&amp;gt;)를 사용하여 반환값을 나타냄으로서 구문자체를 간소화하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 이전 switch 예제에서 switch 표현식을 적용한 결과를 나타내고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769150488887&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string msg;
foreach (Car? car in cars)
{
    msg = car switch
    {
        Sedan speedCar when speedCar.MaxSpeed == 200 =&amp;gt; $&quot;{speedCar.ModelName}이 가장 빠른 차량임&quot;,
        Sedan gCar when gCar.IsGasoline == false =&amp;gt; $&quot;{gCar.ModelName}차량은 휘발유를 사용하지 않음&quot;,
        Truck truck =&amp;gt; $&quot;{truck.ModelName}은 {truck.Release.Year}년에 출시된 truck임.&quot;,
        null =&amp;gt; &quot;개체가 null상태임&quot;,
        _ =&amp;gt; &quot;그 외&quot;
    };

    Console.WriteLine(msg);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 예제와 이전의 swicth와의 가장 큰 차이는 case와 break가 존재하지 않는다는 것입니다. 또한 예제에서 underscore(_)문자는 이전 case에서의 default에 해당하며 이를 'discard'라고 합니다. 관련된 더 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards&quot;&gt;Discards - unassigned discardable variables - C# | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769150534070&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Discards - unassigned discardable variables - C#&quot; data-og-description=&quot;Describes C#'s support for discards, which are unassigned, discardable variables, and the ways in which discards can be used.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bX8La0/dJMb8TB3Jf0/wx9dqKKUWxsv12VNEjFPg0/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bX8La0/dJMb8TB3Jf0/wx9dqKKUWxsv12VNEjFPg0/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Discards - unassigned discardable variables - C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Describes C#'s support for discards, which are unassigned, discardable variables, and the ways in which discards can be used.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 반복문&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문은 구문의 block을 반복하는 구문으로서 while이나 for문에서 조건이 true면 반복을 실행하며, foreach문에서는 collecction안의 각 item을 순회하는 목적으로 사용됩니다. for, while 또는 foreach를 선택하는 기준은 현재 문제를 해결하기에 가장 적합한 것과 개인적인 선호도에 따라 달라질 수 있으며 딱히 정재진 규칙은 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) while&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while문은 boolean식을 평가한 뒤 그 결과가 true면 계속해서 해당 block을 반복하는 구문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 while을 사용하는 간단한 예제를 나타낸 것입니다. 주어진 조건(i &amp;lt; 10)이 false를 반환할때까지 내부 {}사이의 code를 계속 반복하게 되며 결과적으로 0부터 9까지 숫자를 표시하고 동작을 종료할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769153382382&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 0;

while (i &amp;lt; 10)
{
    Console.WriteLine(i);
    ++i;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;20&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTwxb7/dJMcadAKCQD/WtJTe8waKnMAhktxUUdEKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTwxb7/dJMcadAKCQD/WtJTe8waKnMAhktxUUdEKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTwxb7/dJMcadAKCQD/WtJTe8waKnMAhktxUUdEKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTwxb7%2FdJMcadAKCQD%2FWtJTe8waKnMAhktxUUdEKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;20&quot; height=&quot;169&quot; data-origin-width=&quot;20&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) do&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한 while문은 시작할때부터 boolean조건을 평가하지만 do는 일단 한번 실행한 뒤에 그 다음 boolean조건을 평가한다는 차이가 있습니다. 이것 외에는 동작방식이 거의 while과 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 do를 사용하는 간단한 예제를 나타낸 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769153440230&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int right = 5;
int answer = 0;
do
{
    Console.WriteLine(&quot;2 + 3 = ?&quot;);
    answer = int.Parse(Console.ReadLine());
}
while(right != answer);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;do문은 일단 내부의 구문을 최소 한번은 실행하게 됩니다. 따라서 boolean식 조건도 위가 아닌 하단에 위치하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. Application은 사용자에게 질문의 답을 요청하게 되고 그 정답이 입력될때까지 같은 질문을 반복하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;70&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ELLT9/dJMcab33kQO/XBiD9aIDcRF1IWn5hvs1P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ELLT9/dJMcab33kQO/XBiD9aIDcRF1IWn5hvs1P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ELLT9/dJMcab33kQO/XBiD9aIDcRF1IWn5hvs1P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FELLT9%2FdJMcab33kQO%2FXBiD9aIDcRF1IWn5hvs1P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;70&quot; height=&quot;106&quot; data-origin-width=&quot;70&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) for&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문은 아래와 같은 조합으로 만들어집니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;초기화 표현식&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;loop를 시작할때 한번은 실행되는 부분으로 반복문의 초기값을 설정합니다. 별다른 초기화가 필요하지 않으면 생략할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;조건&amp;nbsp;표현식&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;loop가 실행될때 마다 실행되는 부분으로 반복문의 실행여부를 확인하는 부분입니다. 만약 해당 표현식에서 true를 반환한다면 loop는 다시 재실행됩니다. 경우에 따라 생략이 가능한데 자칫 무한 loop가 될 수 있으므로 내부실행부분에서 loop를 빠져나가기 위한 처리를 추가해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;반복&amp;nbsp;표현식&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;loop의 실행이 종료될때마다 실행되는 부분으로 대부분 값의 증가나 감소처리를 진행합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 for문을 사용하여 1부터 10까지 숫자를 나타내도록 하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769415596071&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int i = 1; i &amp;lt;= 10; i++)
{
    Console.WriteLine(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;23&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oCM38/dJMcah4iHDj/x5XZ2TkNb1u5KQDNYgYTu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oCM38/dJMcah4iHDj/x5XZ2TkNb1u5KQDNYgYTu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oCM38/dJMcah4iHDj/x5XZ2TkNb1u5KQDNYgYTu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoCM38%2FdJMcah4iHDj%2Fx5XZ2TkNb1u5KQDNYgYTu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;23&quot; height=&quot;170&quot; data-origin-width=&quot;23&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 'int i = 1'이 초기화, 'i &amp;lt;= 10'이 조건, 'i++'이 반복 표현식에 해당합니다. 만약 이 모두를 생략하고 for문을 작성해야 한다면 다음과 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769415621225&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (;;)
{
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) foreach&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 for문은 값의 변화에 따른 반복을 수행하지만 foreach문은 array나 List와 같이 배열을 열거하는데 사용됩니다. 이때 열거대상인 배열은 읽기전용상태가 되므로 만약 기존 item을 삭제하거나 추가하려고 시도하면 예외를 발생시키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 foreach문을 통해 문자열값의 List를 대상으로 열거하며, 각각의 item을 표시하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769415651325&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; fruits = [&quot;Apple&quot;, &quot;Banana&quot;, &quot;Strawberry&quot;];

foreach (string fruit in fruits)
{
    Console.WriteLine(fruit);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;83&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM7B8J/dJMcafyDQ7T/Nk3pf1FXTk2D7WmtSDgzKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM7B8J/dJMcafyDQ7T/Nk3pf1FXTk2D7WmtSDgzKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM7B8J/dJMcafyDQ7T/Nk3pf1FXTk2D7WmtSDgzKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM7B8J%2FdJMcafyDQ7T%2FNk3pf1FXTk2D7WmtSDgzKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;83&quot; height=&quot;56&quot; data-origin-width=&quot;83&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;List및 관련 내용에 관한 더 자세한 사항은 추후에 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) foreach문의 동작방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 array나 collection과 같이 다수의 item을 표현하는 type을 정의하고자 한다면 해당 type이 foreach문을 통해 열거가능함을 알려줄 필요가 있고 따라서 아래와 같은 규칙이 준수되어야 합니다. 아무리 foreach라도 주어진 type이 열거가능함을 foreach스스로가 알 수 있는 방법이 없기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ⓐ type은 object를 반환하는 GetEnumerator라는 이름의 method를 가지고 있어야 합니다. &lt;br /&gt;ⓑ&amp;nbsp;ⓐ에서&amp;nbsp;반환된&amp;nbsp;개체는&amp;nbsp;반드시&amp;nbsp;Current라는&amp;nbsp;속성과&amp;nbsp;MoveNext라는&amp;nbsp;method를&amp;nbsp;가지고&amp;nbsp;있어야&amp;nbsp;합니다. &lt;br /&gt;ⓒ MoveNext method는 반드시 Current속성값을 변경해야 하며 열거 가능한 item이 존재하는 경우 true를 그렇지 않으면 false를 반환해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET에서는 위와 같은 규칙을 어떤 type이든 일관성있게 적용하기 위해 IEnumerable과 IEnumerable&amp;lt;T&amp;gt; interface를 제공하고 있으며 해당 interface를 통해 상기 규칙을 정의하고 있습니다.(다만 해당 interface의 규칙을 강제하지는 않습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 compiler는 이전 예제와 같은 code를 아래와 같은 형식으로 처리하고 있는데&lt;/p&gt;
&lt;pre id=&quot;code_1769415748525&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IEnumerator e = fruits.GetEnumerator();

while (e.MoveNext())
{
    string fruit = (string)e.Current;
    Console.WriteLine(fruit);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시는 바와 같이 iterator와 해당 읽기전용의 Current속성을 사용하기 때문에 foreach문안에 선언된 변수는 현재 item의 값을 변경하는데 사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 예제에서는 fruits의 변수에 List&amp;lt;string&amp;gt; type을 사용하였는데 만약 이 type이 아래와 같이 단순 배열(array)이라면&lt;/p&gt;
&lt;pre id=&quot;code_1769415778979&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] fruits = [&quot;Apple&quot;, &quot;Banana&quot;, &quot;Strawberry&quot;];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compiler는 배열이 IEnumerable&amp;lt;T&amp;gt; interface를 구현한다는 것을 무시하고 이를 사용하는 대신 조금 더 효휼적으로 array의 Length속성을 사용하는 for문을 작성하여 아래와 같이 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769415839757&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int len = 0; len &amp;lt; fruits.Length; len++)
{
    Console.WriteLine(fruits[len]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. 배열(Array)&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 Type이면서 다수의 값을 한꺼번에 저장해야 한다면 array를 사용할 수 있습니다. 이전 예제에서도 문자열 type의 과일 이름을 다수로 저장하는데 array를 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 1차원 Array&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다수의 값을 한꺼번에 저장하는 array는 보통 내부 요소를 저장할때 최초 0부터 시작하는 zero 하한선(lower bound of zero)을 사용하므로 각 요소는 0부터 3까지 index값을 가지고 저장됩니다. 따라서 마지막 item의 index는 array의 크기의 -1값을 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 이미지는 1차원 array가 값을 저장할때 상태를 나타낸 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjOknZ/dJMcaac2H4F/YzDv6zK5tsi2rNkNtbWGq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjOknZ/dJMcaac2H4F/YzDv6zK5tsi2rNkNtbWGq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjOknZ/dJMcaac2H4F/YzDv6zK5tsi2rNkNtbWGq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjOknZ%2FdJMcaac2H4F%2FYzDv6zK5tsi2rNkNtbWGq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;288&quot; height=&quot;49&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Array가 item을 저장할때 항상 0부터 시작된다고 단정지어서는 안됩니다. .NET에서 array의 가장 일반적인 type은 []문법을 사용하는 szArray(Single-dimensional Zero-indexed array)이지만 다차원 array인 mdArray도 제공하고 있으며 이는 'zero 하한선'을 사용하지 않습니다. 물론 흔하게 사용되는건 아니지만 이런것도 있다는 것을 알려드리고자 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 1차원 array에 4개의 문자열값을 저장하고 이를 for문으로 순회하면서 해당 요소를 화면으로 표시하도록 하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769504683505&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] fruits;

fruits = new string[4];

fruits[0] = &quot;Apple&quot;;
fruits[1] = &quot;Banana&quot;;
fruits[2] = &quot;Watermelon&quot;;
fruits[3] = &quot;Orange&quot;;

for (int i = 0; i &amp;lt; fruits.Length; i++)
{
    Console.WriteLine($&quot;{fruits[len]} of {i}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;115&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbqKJx/dJMb996g4ue/MyhxkXIRD8sA3OcZKHR47K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbqKJx/dJMb996g4ue/MyhxkXIRD8sA3OcZKHR47K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbqKJx/dJMb996g4ue/MyhxkXIRD8sA3OcZKHR47K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbqKJx%2FdJMb996g4ue%2FMyhxkXIRD8sA3OcZKHR47K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;115&quot; height=&quot;72&quot; data-origin-width=&quot;115&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보면 array를 선언한 뒤에 아래와 같이 array의 instance를 생성하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769504719312&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fruits = new string[4];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array는 memory에 할당되는 시점에 항상 고정된 값의 크기를 지정해야 합니다. 따라서 array를 선언하고 할당하기 전에는 해당 array에 얼마나 많은 item을 담을 것인지 예상하고 그에 맞는 크기를 지정하는 것이 필요합니다. 또는 array를 선언하고 지정된 item을 아래와 같이 저장하면 자동으로 해당 item의 수만큼 array의 size를 확보하기 때문에 좀더 간결하게 array를 초기화 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769504741081&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] fruits = { &quot;Apple&quot;, &quot;Banana&quot;, &quot;Watermelon&quot;, &quot;Orange&quot;};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 다차원 Array&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string이나 다른 data type을 하나의 줄로만 계속 연결해서 저장하려면 1차원 array만으로 충분하지만 array를 통해 table형태로 값을 저장하려면 다차원 array가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 array를 2차원으로 구성한다면 아래와 같은 그림으로 표현할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b00eBR/dJMcahJ1NCz/ChSoZtNdZ43Gva712kq010/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b00eBR/dJMcahJ1NCz/ChSoZtNdZ43Gva712kq010/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b00eBR/dJMcahJ1NCz/ChSoZtNdZ43Gva712kq010/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb00eBR%2FdJMcahJ1NCz%2FChSoZtNdZ43Gva712kq010%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;368&quot; height=&quot;96&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 다차원중에서도 array를 2개 사용하는 2차원을 표현한 것인데 이를 code를 바꾸면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585410236&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[,] twoArray =
{
    { &quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot; },
    { &quot;aa&quot;, &quot;bb&quot;, &quot;cc&quot;, &quot;dd&quot; },
    { &quot;aaa&quot;, &quot;bbbb&quot;, &quot;ccc&quot;, &quot;ddd&quot; }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'string[,]'구문에서 comma가 사용되었는데 이를 통해 string type의 array를 2차원으로 선언한 것이 됩니다. 혹은 다음과 같은 방법이 사용되기도 하는데&lt;/p&gt;
&lt;pre id=&quot;code_1769585429466&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[,] twoArray = new string[3, 4];
twoArray[0, 0] = &quot;a&quot;;
twoArray[0, 1] = &quot;b&quot;;
twoArray[0, 2] = &quot;c&quot;;
twoArray[0, 3] = &quot;d&quot;;
twoArray[1, 0] = &quot;aa&quot;;
twoArray[1, 1] = &quot;bb&quot;;
twoArray[1, 2] = &quot;cc&quot;;
twoArray[1, 3] = &quot;dd&quot;;
twoArray[2, 0] = &quot;aaa&quot;;
twoArray[2, 1] = &quot;bbb&quot;;
twoArray[2, 2] = &quot;ccc&quot;;
twoArray[2, 3] = &quot;ddd&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방법은 array의 선언과 할당을 분리하는 것으로, 사실 array의 차원을 이해하는데는 오히려 위와 같은 code가 더 도움이 될 수 있습니다. 또한 database나 file과 같이 외부에서 runtime에 값을 설정해야 하는 경우라면 이러한 방법을 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;array를 선언할때 []안에서는 comma(,)를 기준으로 첫번째 숫자가 첫번째 차원의 길이를, 두번째 숫자가 두번째 차원의 길이를 나타냅니다. 요소를 가져올때 붙이는 최대상한선의 index를 의미하는 것이 아니므로 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;array에서 각 차원의 하한선과 상한선은 다음과 같이 해당 array의 GetLowerBound와 GetUpperBound라는 2개의 method를 통해 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585534644&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;1 dimension lower bound : {twoArray.GetLowerBound(0)}&quot;);
Console.WriteLine($&quot;1 dimension uppper bound : {twoArray.GetUpperBound(0)}&quot;);
Console.WriteLine($&quot;2 dimension lower bound : {twoArray.GetLowerBound(1)}&quot;);
Console.WriteLine($&quot;2 dimension uppper bound : {twoArray.GetUpperBound(1)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1HK6M/dJMcagqMNX9/Idl1c4aDxKID3M429cdiR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1HK6M/dJMcagqMNX9/Idl1c4aDxKID3M429cdiR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1HK6M/dJMcagqMNX9/Idl1c4aDxKID3M429cdiR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1HK6M%2FdJMcagqMNX9%2FIdl1c4aDxKID3M429cdiR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;208&quot; height=&quot;73&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위 array에 대한 값을 어떻게 확인할 수 있을지 생각해 봅시다. 1차원 array에서는 단 하나의 for문만 있으면 되지만 위 예제처럼 2차원이라면 for문을 하나 더 추가해야 하며, 이렇게 만들어진 2개의 중첩된 for문을 통해 array를 순회하여 값을 표시할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585576156&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int row = 0; row &amp;lt;= twoArray.GetUpperBound(0); row++)
{
    for (int col = 0; col &amp;lt;= twoArray.GetUpperBound(1); col++)
    {
        Console.WriteLine($&quot;{row} Row, {col} Column : {twoArray[row, col]}&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 바깥쪽 for문은 첫번째 차원을, 그 다음 for문은 두번째 차원을 순회합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;array를 선언할때의 예제를 보면 array를 초기화 할때 각 row와 column에 들어갈 모든 값을 지정하고 있습니다. 이렇게 하지 않으면 오류가 발생하는데 만약 중간에 값을 비워둬야 한다면 string type의 경우 string.Empty로 이를 대신할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585601286&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[,] twoArray =
{
    { string.Empty, &quot;b&quot;, &quot;c&quot;, &quot;d&quot; },
    { &quot;aa&quot;, &quot;bb&quot;, string.Empty, &quot;dd&quot; },
    { &quot;aaa&quot;, &quot;bbbb&quot;, &quot;ccc&quot;, string.Empty }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string.Empty는 말그대로 공백을 의미합니다. 또는 null처럼 공백이 아닌 아예 없는 것으로 만들어야 한다면 'string?[,]'과 같이 null을 허용하게끔 array를 선언하고 null keyword로 값을 비워두면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585631818&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string?[,] twoArray =
{
    { null, &quot;b&quot;, &quot;c&quot;, &quot;d&quot; },
    { &quot;aa&quot;, &quot;bb&quot;, null, &quot;dd&quot; },
    { &quot;aaa&quot;, &quot;bbbb&quot;, &quot;ccc&quot;, null }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 가변배열(jagged arrays)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가변배열은 다차원배열과 비슷하지만 각각 차원이 다른 요소를 저장할 수 있는 배열을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가변배열을 시각화하면 다음과 같이 나타낼 수 있으며&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2lANE/dJMcacooPGk/oILPwFkAvEKs7p10NhlokK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2lANE/dJMcacooPGk/oILPwFkAvEKs7p10NhlokK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2lANE/dJMcacooPGk/oILPwFkAvEKs7p10NhlokK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2lANE%2FdJMcacooPGk%2FoILPwFkAvEKs7p10NhlokK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;139&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;139&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 code화 하면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585693634&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[][] jaggedArray =
[
    [ &quot;a&quot;, &quot;b&quot;, &quot;c&quot; ],
    [ &quot;aa&quot;, &quot;bb&quot;, &quot;cc&quot;, &quot;dd&quot; ],
    [ &quot;aaa&quot;, &quot;bbb&quot; ],
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 C#11 이전의 version을 사용한다면 위의 대괄호를 사용할 수 없고 아래와 같이 중괄호와 함께 new keyword를 사용하여 instance화 해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585714918&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[][] jaggedArray =
{
    new[] { &quot;a&quot;, &quot;b&quot;, &quot;c&quot; },
    new[] { &quot;aa&quot;, &quot;bb&quot;, &quot;cc&quot;, &quot;dd&quot; },
    new[] { &quot;aaa&quot;, &quot;bbb&quot; }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가변배열역시 GetLowerBound()와 GetUpperBound() method를 통해 하산선과 상한선값을 가져올 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585728506&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int arr = 0; arr &amp;lt;= jaggedArray.GetUpperBound(0); arr++)
{
    Console.WriteLine($&quot;Upper bound of array {arr} : {jaggedArray[arr].GetUpperBound(0)}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;191&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lnJGa/dJMcahJ1NP3/BK97lUVUkmMuGfaojKBgN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lnJGa/dJMcahJ1NP3/BK97lUVUkmMuGfaojKBgN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lnJGa/dJMcahJ1NP3/BK97lUVUkmMuGfaojKBgN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlnJGa%2FdJMcahJ1NP3%2FBK97lUVUkmMuGfaojKBgN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;191&quot; height=&quot;53&quot; data-origin-width=&quot;191&quot; data-origin-height=&quot;53&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 순회하는 방식역시 for문과 같은 반복문을 사용하면 됩니다. 다만 가변배열은 각각의 row마다 새로운 array가 들어가 잇는 셈이므로 각 array에 대한 상한선을 구해 요소를 순회하여야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769585759564&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int row = 0; row &amp;lt;= jaggedArray.GetUpperBound(0); row++)
{
    for (int col = 0; col &amp;lt;= jaggedArray[row].GetUpperBound(0); col++)
    {
        Console.WriteLine($&quot;{row} Row, {col} Column : {jaggedArray[row][col]}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4dVzs/dJMcagK7ebs/43FyaXSQcY5XhYAKeFia4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4dVzs/dJMcagK7ebs/43FyaXSQcY5XhYAKeFia4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4dVzs/dJMcagK7ebs/43FyaXSQcY5XhYAKeFia4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4dVzs%2FdJMcagK7ebs%2F43FyaXSQcY5XhYAKeFia4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;158&quot; height=&quot;157&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) Array의 List Pattern Matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 어떠한 개체에 대해서 pattern matching을 사용해 어떤식으로 개체의 속성과 type을 비교할 수 있는지 살펴봤었습니다. 이러한 pattern matching은 array나 기타 collection에서도 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11에서 도입된 pattern matching은 public으로 정의된 Length나 Count 속성과 함께 int 혹은 System.Index 매개변수를 사용하는 indexer를 가진 type이라면 어떤 것에도 pattern matching을 적용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;indexer에 관한 자세한 사항은 추후에 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 switch문안에서 다수의 list pattern을 정의할때는 가장 상세한 조건이 가장 먼저올 수 있도록 해야 합니다. 자세하지 않은 일반적인 pattern이 우선으로 오면 해당 pattern이 사전에 먼저 조건에 부합될 수 있고 결국 다음순서에 존재하는 상세 pattern까지 도달할 수 없게 될 것이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래&amp;nbsp;표는&amp;nbsp;int값을&amp;nbsp;갖는&amp;nbsp;list로&amp;nbsp;가정할&amp;nbsp;경우&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;list&amp;nbsp;pattern의&amp;nbsp;예제를&amp;nbsp;나열하고&amp;nbsp;있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 266px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 21px;&quot;&gt;[]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 21px;&quot;&gt;빈&amp;nbsp;array&amp;nbsp;또는&amp;nbsp;collection과&amp;nbsp;일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 63px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 63px;&quot;&gt;[..]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 63px;&quot;&gt;Array나&amp;nbsp;collection의&amp;nbsp;항목갯수가&amp;nbsp;0개이상일때와&amp;nbsp;일치합니다.&amp;nbsp;만약&amp;nbsp;'[]'와&amp;nbsp;'[..]'모두에서&amp;nbsp;조건이&amp;nbsp;거쳐가도록&amp;nbsp;하려면&amp;nbsp;이론상&amp;nbsp;'[..]'이&amp;nbsp;'[]'이후에&amp;nbsp;와야&amp;nbsp;합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 63px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 63px;&quot;&gt;[int item1] or [var&amp;nbsp;item1]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 63px;&quot;&gt;지정된&amp;nbsp;단일&amp;nbsp;항목을&amp;nbsp;가진&amp;nbsp;list와&amp;nbsp;일치합니다.&amp;nbsp;또한&amp;nbsp;반환표현식을&amp;nbsp;통해&amp;nbsp;일치하는&amp;nbsp;항목의&amp;nbsp;값을&amp;nbsp;반환하므로&amp;nbsp;item1을&amp;nbsp;참조해&amp;nbsp;값을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[1,&amp;nbsp;2]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;정확히&amp;nbsp;지정한&amp;nbsp;2개의&amp;nbsp;항목을&amp;nbsp;지정한&amp;nbsp;순서대로&amp;nbsp;가진&amp;nbsp;list와&amp;nbsp;일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[_,&amp;nbsp;_]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;2개의&amp;nbsp;item을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치합니다.&amp;nbsp;underscore(_)는&amp;nbsp;단일값을&amp;nbsp;의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[var&amp;nbsp;item1,&amp;nbsp;var&amp;nbsp;item2]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;2개의&amp;nbsp;item을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치하며&amp;nbsp;반환표현식에&amp;nbsp;의해&amp;nbsp;반환되는&amp;nbsp;값을&amp;nbsp;item1과&amp;nbsp;item2를&amp;nbsp;참조하여&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[_,&amp;nbsp;_,&amp;nbsp;_]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;3개의&amp;nbsp;item을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[var&amp;nbsp;item1,&amp;nbsp;..]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;하나&amp;nbsp;이상의&amp;nbsp;item을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치하며&amp;nbsp;반환표현식에&amp;nbsp;의해&amp;nbsp;반환되는&amp;nbsp;첫번째&amp;nbsp;item값을&amp;nbsp;item1을&amp;nbsp;참조하여&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;'..'은&amp;nbsp;범위값을&amp;nbsp;의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[var&amp;nbsp;firstItem,&amp;nbsp;..,&amp;nbsp;var&amp;nbsp;lastItem]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;2개의&amp;nbsp;이상을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치하며&amp;nbsp;반환표현식에&amp;nbsp;의해&amp;nbsp;반환되는&amp;nbsp;첫번째&amp;nbsp;item과&amp;nbsp;마지막&amp;nbsp;item값을&amp;nbsp;firstItem과&amp;nbsp;lastItem을&amp;nbsp;참조해&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 26.6279%; height: 17px;&quot;&gt;[..,&amp;nbsp;var&amp;nbsp;lastItem]&lt;/td&gt;
&lt;td style=&quot;width: 73.3721%; height: 17px;&quot;&gt;하나&amp;nbsp;이상의&amp;nbsp;item을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치하며&amp;nbsp;반환표현식에&amp;nbsp;의해&amp;nbsp;반환되는&amp;nbsp;마지막&amp;nbsp;item값을&amp;nbsp;lastItem을&amp;nbsp;참조하여&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 우선 아래와 같은 int형 type의 array를 선언하고&lt;/p&gt;
&lt;pre id=&quot;code_1769676029632&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] _1Numbers = {};
int[] _2Numbers = { 1, 2, 5, 6 };
int[] _3Numbers = { 1, 2, 3, 4 };
int[] _4Numbers = { 1, 2, 8 };
int[] _5Numbers = { 1, 2 };
int[] _6Numbers = { 9, 10, 11, 12, 13 };
int[] _7Numbers = { 99 };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;patterm matching을 사용할 switch 표현식을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769676040725&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static string filtering(int[] numbers) =&amp;gt; numbers switch
{
    [] =&amp;gt; &quot;_1NNumbers&quot;,
    [1, 2, _, 6] =&amp;gt; &quot;_2Numbers&quot;,
    [1, .., 4] =&amp;gt; &quot;_3Numbers&quot;,
    [1, 2, 8] =&amp;gt; &quot;_4Numbers&quot;,
    [int item1, int item2] =&amp;gt; $&quot;_5Numbers with {item1}, {item2}&quot;,
    [9, .. int[] nums] =&amp;gt; $&quot;_6Numbers with {nums.Length}&quot;,
    [..] =&amp;gt; &quot;etc.. _7Numbers&quot;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음과 같이 각 array에 대해 filtering을 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769676051978&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(filtering(_1Numbers));
Console.WriteLine(filtering(_2Numbers));
Console.WriteLine(filtering(_3Numbers));
Console.WriteLine(filtering(_4Numbers));
Console.WriteLine(filtering(_5Numbers));
Console.WriteLine(filtering(_6Numbers));
Console.WriteLine(filtering(_7Numbers));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;141&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9iDaB/dJMb99LXOEd/xI8Ty6AHMiK4E3ZbzltG00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9iDaB/dJMb99LXOEd/xI8Ty6AHMiK4E3ZbzltG00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9iDaB/dJMb99LXOEd/xI8Ty6AHMiK4E3ZbzltG00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9iDaB%2FdJMb99LXOEd%2FxI8Ty6AHMiK4E3ZbzltG00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;141&quot; height=&quot;121&quot; data-origin-width=&quot;141&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#6 부터는 code안에서 함수를 정의할 수 있는 식본문member(Expression-bodied function members)를 지원하고 있으며 예제에서는 이를 사용해 filtering method를 구현하고 있습니다. 또한 '=&amp;gt;'문자를 사용하는 lambda를 통해 함수로 부터의 값반환을 표현하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 Pattern matching에 대한 모든 것을 설명하지 않았습니다. 더 자세한 사항은 아래 link를 참고해 주시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769676088269&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Patterns - Pattern matching using the is and switch expressions. - C# reference&quot; data-og-description=&quot;Learn about the patterns supported by the &amp;#96;is&amp;#96; and &amp;#96;switch&amp;#96; expressions. Combine multiple patterns using the &amp;#96;and&amp;#96;, &amp;#96;or&amp;#96;, and &amp;#96;not&amp;#96; operators.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KKwoL/dJMb8VNp5F9/j8FVx8KA1PA72Jv2ymXGAk/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KKwoL/dJMb8VNp5F9/j8FVx8KA1PA72Jv2ymXGAk/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Patterns - Pattern matching using the is and switch expressions. - C# reference&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the patterns supported by the `is` and `switch` expressions. Combine multiple patterns using the `and`, `or`, and `not` operators.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) Array의 List Pattern Matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 pattern matching을 사용해 어떤식으로 개체의 속성과 type을 비교할 수 있는지 살펴봤었습니다. 이러한 pattern matching은 array나 기타 collection에서도 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11에서 도입된 pattern matching은 public으로 정의된 Length나 Count 속성과 함께 int 혹은 System.Index 매개변수를 사용하는 indexer를 가진 type이라면 어떤 것에도 pattern matching을 적용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;indexer에 관한 자세한 사항은 추후에 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 switch문안에서 다수의 list pattern을 정의할때는 가장 상세한 조건이 가장 먼저올 수 있도록 해야 합니다. 상세적이지 않은 일반적인 pattern이 우선으로 오면 해당 pattern이 사전에 먼저 조건에 부합될 수 있고 결국 다음순서에 존재하는 상세 pattern까지는&amp;nbsp; 도달할 수 없을지 모르기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래&amp;nbsp;표는&amp;nbsp;int값을&amp;nbsp;갖는&amp;nbsp;list로&amp;nbsp;가정할&amp;nbsp;경우&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;list&amp;nbsp;pattern의&amp;nbsp;예제를&amp;nbsp;나열하고&amp;nbsp;있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;빈&amp;nbsp;array&amp;nbsp;또는&amp;nbsp;collection과&amp;nbsp;일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[..]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;Array나 collection의 항목갯수가 0개이상일때와 일치합니다. 만약 '[]'와 '[..]'모두에서 조건이 거쳐가도록 하려면 이론상 '[..]'이 '[]'이후에 와야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[int item1] or [var&amp;nbsp;item1]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;지정된&amp;nbsp;단일&amp;nbsp;항목을&amp;nbsp;가진&amp;nbsp;list와&amp;nbsp;일치합니다.&amp;nbsp;또한&amp;nbsp;반환표현식을&amp;nbsp;통해&amp;nbsp;일치하는&amp;nbsp;항목의&amp;nbsp;값을&amp;nbsp;반환하므로&amp;nbsp;item1을&amp;nbsp;참조해&amp;nbsp;값을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[1, 2]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;정확히&amp;nbsp;지정한&amp;nbsp;2개의&amp;nbsp;항목을&amp;nbsp;지정한&amp;nbsp;순서대로&amp;nbsp;가진&amp;nbsp;list와&amp;nbsp;일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[_, _]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;2개의&amp;nbsp;item을&amp;nbsp;가진&amp;nbsp;모든&amp;nbsp;list와&amp;nbsp;일치합니다.&amp;nbsp;underscore(_)는&amp;nbsp;단일값을&amp;nbsp;의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[var item1, var item2]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;2개의 item을 가진 모든 list와 일치하며 반환표현식에 의해 반환되는 값을 item1과 item2를 참조하여 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[_, _, _]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;3개의 item을 가진 모든 list와 일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[var&amp;nbsp;item1,&amp;nbsp;..]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;하나 이상의 item을 가진 모든 list와 일치하며 반환표현식에 의해 반환되는 첫번째 item값을 item1을 참조하여 사용할 수 있습니다. '..'은 범위값을 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[var firstItem, .., var lastItem]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;2개의 이상을 가진 모든 list와 일치하며 반환표현식에 의해 반환되는 첫번째 item과 마지막 item값을 firstItem과 lastItem을 참조해 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;[.., var lastItem]&lt;/td&gt;
&lt;td style=&quot;width: 74.4186%;&quot;&gt;하나 이상의 item을 가진 모든 list와 일치하며 반환표현식에 의해 반환되는 마지막 item값을 lastItem을 참조하여 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 우선 아래와 같은 int형 type의 array를 선언하고&lt;/p&gt;
&lt;pre id=&quot;code_1769753614389&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] _1Numbers = {};
int[] _2Numbers = { 1, 2, 5, 6 };
int[] _3Numbers = { 1, 2, 3, 4 };
int[] _4Numbers = { 1, 2, 8 };
int[] _5Numbers = { 1, 2 };
int[] _6Numbers = { 9, 10, 11, 12, 13 };
int[] _7Numbers = { 99 };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;patterm matching을 사용할 switch 표현식을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769753625537&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static string filtering(int[] numbers) =&amp;gt; numbers switch
{
    [] =&amp;gt; &quot;_1NNumbers&quot;,
    [1, 2, _, 6] =&amp;gt; &quot;_2Numbers&quot;,
    [1, .., 4] =&amp;gt; &quot;_3Numbers&quot;,
    [1, 2, 8] =&amp;gt; &quot;_4Numbers&quot;,
    [int item1, int item2] =&amp;gt; $&quot;_5Numbers with {item1}, {item2}&quot;,
    [9, .. int[] nums] =&amp;gt; $&quot;_6Numbers with {nums.Length}&quot;,
    [..] =&amp;gt; &quot;etc.. _7Numbers&quot;,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음과 같이 각 array에 대해 filtering을 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769753636349&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(filtering(_1Numbers));
Console.WriteLine(filtering(_2Numbers));
Console.WriteLine(filtering(_3Numbers));
Console.WriteLine(filtering(_4Numbers));
Console.WriteLine(filtering(_5Numbers));
Console.WriteLine(filtering(_6Numbers));
Console.WriteLine(filtering(_7Numbers));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;139&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4M6FW/dJMcacBXiRW/MnKeLew3SnKlBQ6m6U7EY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4M6FW/dJMcacBXiRW/MnKeLew3SnKlBQ6m6U7EY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4M6FW/dJMcacBXiRW/MnKeLew3SnKlBQ6m6U7EY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4M6FW%2FdJMcacBXiRW%2FMnKeLew3SnKlBQ6m6U7EY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;139&quot; height=&quot;120&quot; data-origin-width=&quot;139&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C#6 부터는 code안에서 함수를 정의할 수 있는 식본문member(Expression-bodied function members)를 지원하고 있으며 예제에서는 이를 사용해 filtering method를 구현하고 있습니다. 또한 '=&amp;gt;'문자를 사용하는 lambda를 통해 함수로 부터의 값반환을 표현하고 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pattern matching에 대한 보다 더 자세한 사항은 아래 link를 참고해 주시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#list-patterns&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) Trailing comma&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 다시 보면 마지막 '[..] =&amp;gt; &quot;etc.. _7Numbers&quot;'끝에 comma가 붙어 있는데 이를 trailing comma라고 하며 comma뒤에 다른 요소가 더 나오지 않아도 compiler는 별다른 오류를 표시하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#과 같은 대부분의 언어에서는 이러한 방식의 comma를 허용하는데 위 예제처럼 다수의 item을 comma로 분리하는 경우(array나 collection초기화, enum 또는 switch표현식등)에 사용하며 향후에 comma를 추가하거나 제거하지 않아도 item순서를 쉽게 재정렬하거나 추가/삭제할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2008년 swicth표현식에서 trailing comma를 허용하는것에 대한 논의가 있었는데 관련 내용에 대해서는 아래 link를 참고 하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/issues/2098&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/dotnet/csharplang/issues/2098&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769753806425&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Switch expressions currently disallow trailing comma &amp;middot; Issue #2098 &amp;middot; dotnet/csharplang&quot; data-og-description=&quot;Update: LDM (1/9) decided to allow such trailing comma. @jcouv commented on Thu Dec 20 2018 Trailing commas are currently disallowed, which is inconvenient when copy/pasting a line. public class C ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/dotnet/csharplang/issues/2098&quot; data-og-url=&quot;https://github.com/dotnet/csharplang/issues/2098&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QNRbv/dJMb82eHSOK/KjIgyfav2kfDlMcknqLnm1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/eyho0N/dJMb83knM07/t27jVE7xN85vrw9sId1IA0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang/issues/2098&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/dotnet/csharplang/issues/2098&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QNRbv/dJMb82eHSOK/KjIgyfav2kfDlMcknqLnm1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/eyho0N/dJMb83knM07/t27jVE7xN85vrw9sId1IA0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Switch expressions currently disallow trailing comma &amp;middot; Issue #2098 &amp;middot; dotnet/csharplang&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Update: LDM (1/9) decided to allow such trailing comma. @jcouv commented on Thu Dec 20 2018 Trailing commas are currently disallowed, which is inconvenient when copy/pasting a line. public class C ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐만 아니라 JSON serializer도 이러한 방식을 사용할 수 있는 option을 가지고 있습니다.&lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.allowtrailingcommas?view=net-10.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.allowtrailingcommas?view=net-10.0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769753802576&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;JsonSerializerOptions.AllowTrailingCommas Property (System.Text.Json)&quot; data-og-description=&quot;Get or sets a value that indicates whether an extra comma at the end of a list of JSON values in an object or array is allowed (and ignored) within the JSON payload being deserialized.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.allowtrailingcommas?view=net-10.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.allowtrailingcommas?view=net-10.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ceRPf8/dJMb85WN6nA/CjkYi3Olp6j8FFAhnbOKK1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.allowtrailingcommas?view=net-10.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.allowtrailingcommas?view=net-10.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ceRPf8/dJMb85WN6nA/CjkYi3Olp6j8FFAhnbOKK1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JsonSerializerOptions.AllowTrailingCommas Property (System.Text.Json)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Get or sets a value that indicates whether an extra comma at the end of a list of JSON values in an object or array is allowed (and ignored) within the JSON payload being deserialized.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) inline array&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#12에 도입된 inline array는 .NET Runtime team에서 성능향상을 위해 사용한 기능으로 아래와 같이 struct에 inline array특성을 지정하여 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769755428360&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[System.Runtime.CompilerServices.InlineArray(5)]
public struct InArr
{
    private int _item;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 InlineArray를 통해 전달한 인수만큼 연속적인 메모리공간을 할당하여 배열을 생성하게 되며 일반적인 배열과 동일하게 다음과 같이 값을 설정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769755438750&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;InArr ia = new InArr();
for(int i = 0; i &amp;lt; 5; i++)
{
    ia[i] = i;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에 들어간 값을 가져오는 방식도 크게 다르지 않지만 대신 InlineArray는 Length속성이 없기 때문에 자체적으로는 배열의 길이를 확인할 방법이 없습니다. 따라서 동적으로 배열의 길이를 확인해야 한다면 여러가지 방법이 있겠지만 아래와 같이 Span을 경유하는 방식이 가장 편리할 것으로 보입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1769755466662&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Span&amp;lt;int&amp;gt; si = ia;
for(int i = 0; i &amp;lt; si.Length; i++)
{
    Console.WriteLine(si[i]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inline array에 관한 좀더 자세한 사항은 아래 link를 참고 하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769755474568&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Inline arrays, or fixed sized buffers - C# feature specifications&quot; data-og-description=&quot;Inline arrays provide a general-purpose and safe mechanism for declaring inline arrays within C# classes, structs, and interfaces.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/m3TZV/dJMb88FZKGv/AV5inJqjH6uVX1lyTV0AyK/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/inline-arrays&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/m3TZV/dJMb88FZKGv/AV5inJqjH6uVX1lyTV0AyK/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Inline arrays, or fixed sized buffers - C# feature specifications&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Inline arrays provide a general-purpose and safe mechanism for declaring inline arrays within C# classes, structs, and interfaces.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7) Array와 collection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array는 다수의 item을 임시적으로 저장하는데 유용하게 사용할 수 있지만 만약 item을 동적으로 추가하거나 삭제하는 경우가 있다면 array보다는 collection을 사용하는편이 효휼적일 수 있습니다. 그런데 반대로 item을 추가하거나 삭제할 일이 전혀 없다면 collection보다는 array를 사용하는 편이 훨씬 더 나은 선택입니다. Array는 내부의 item을 인접하게 저장함으로서 memory를 훨씬 더 효휼적으로 사용하며 따라서 collection보다 나은 성능을 낼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;collection에 관한 자세한 내용은 추후에 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. 형변환과 type변환&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Programming에서 서로다른 type간 변수의 값을 바꾸는 경우는 매우 흔한 경우입니다. 예컨데 문자열형식으로 들어온 data는 처음에는 string type으로 저장되지만 곧 필요에 따라 숫자나 날짜등으로 얼마든지 변환하여 취급되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자역시 비슷하지만 엄밀히 다른 type인 경우에도 변환이 필요합니다. int에서 double로 변환하는 경우처럼 계산하고자 하는 목적에 따라 그 data역시 달라져야 하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형변환은 곧 type변환을 의미하기도 하며 결국 같은 의미로 통용됩니다. 그리고 이러한 변환은 그 변환의 방식에 따라 암시적(implicit)변환과 명시적(explicit)변환으로 나눌 수 있는데 암시적 변환은 자동적으로 발생하는 것으로서 비교적 안전하게 변환이 이루어지며 변환결과에 따른 data손실이나 변경이 발생하지 않습니다. 반대로 명시적 변환은 개발자가 의도적으로 변환하는 것으로서 특히 숫자에서 소수의 정밀도에 따라 data의 손실이 발생할 수 있는데 이는 compiler에게 이러한 손실이 발생할 수 있음을 알고있고 그걸 감수할 준비가 되었음을 알려주는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) &lt;b&gt;Number type&lt;/b&gt; 에서의 암시적 형변환과 명시적 형변환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int값을 double값으로 변환하는 암시적형변환은 어떠한 data의 손실이나 변형도 발생하지 않기 때문에 안전하게 이루어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제가 int형에서 double형으로 암시적 변환이 수행되는 사례를 나타는 것인데, int형 변수 i를 double형 변수 d에 할당하는 순간 암시적인 형변환이 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770020379316&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
double d = i; //암시적 형변환

Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 반대는 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 double형 변수 d를 먼저 값을 할당하여 선언한 뒤 int형 변수 i로 암시적형변환이 이루어지도록 아래와 같이 code를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770021315656&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double d = 10.99;
int i = d;

Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태에서 예제를 실행하려 하면 아래와 같은 오류가 발생할 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;error CS0266: Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론만 얘기하면 double형에서 int형으로의 암시적형변환은 불가능합니다. int보다 double자체가 더 넓은 범위의 값을 가질 수 있을 뿐만 아니라 int는 점이하의 소수부분 정보를 담을 수 없기 때문에 형변환과정에서 data의 손실이 발생할 수 밖에 없기 때문입니다. 따라서 이런 경우에는 개발자가 형변환을 직접 지정하는 명시적형변환을 수행해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적형변환은 변환하고자하는 type을 괄호를 사용해 아래와 같이 직접 지정해 주는 방식입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770021371214&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double d = 10.99;
int i = (int)d;

Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명히 알아야할건 위와 같은 경우는 어떻게든 작동은 하겠지만 data의 손실과 그에 따른 결과를 개발자가 직접 책임져야 한다는 것입니다. 특히 큰값을 가진 type의 변수를 작은 값을 가지는 type의 변수로 변환할때는 전혀 예상치 못한 엉뚱한 값으로 변환이 수행될 수도 있음을 염두해 두어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770021382861&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long l = 100;
int i = (int)l;

Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 long값에 100을 할당한 뒤 이를 int변수로 형변환을 수행하고 있습니다. 이런 경우에는 long type이라 하더다도 int에 충분히 수용할 수 있는 100의 값을 변환하는 것이기 때문에 아무런 문제가 되지 않지만&lt;/p&gt;
&lt;pre id=&quot;code_1770021398116&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long l = long.MaxValue;
int i = (int)l;

Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 int범위를 넘어서는 매우 큰값에 대해 형변환을 수행하면 아마도 아래와 유사한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;22&quot; data-origin-height=&quot;18&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXwHBG/dJMb996jWzv/XC3yKjzkK66aQhMKswW7v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXwHBG/dJMb996jWzv/XC3yKjzkK66aQhMKswW7v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXwHBG/dJMb996jWzv/XC3yKjzkK66aQhMKswW7v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXwHBG%2FdJMb996jWzv%2FXC3yKjzkK66aQhMKswW7v1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;22&quot; height=&quot;18&quot; data-origin-width=&quot;22&quot; data-origin-height=&quot;18&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 아래와 같이 overflow를 발생시켜 엉뚱한 값을 표시할 수도 있는데&lt;/p&gt;
&lt;pre id=&quot;code_1770021420997&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long l = 5_000_000_000;
int i = (int)l;

Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;71&quot; data-origin-height=&quot;19&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfSMd8/dJMcafyIans/1K7MgfJkrKMfHmu0XwtqB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfSMd8/dJMcafyIans/1K7MgfJkrKMfHmu0XwtqB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfSMd8/dJMcafyIans/1K7MgfJkrKMfHmu0XwtqB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfSMd8%2FdJMcafyIans%2F1K7MgfJkrKMfHmu0XwtqB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;71&quot; height=&quot;19&quot; data-origin-width=&quot;71&quot; data-origin-height=&quot;19&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 int형 값을 binary로 표현하는 과정에서 발생하는 결과입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Binary에서 음수를 표현하는 방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'long.MaxValue'를 사용한 예제의 결과에서 i의 값은 -1이 됩니다. 이렇게 되는 이유는 음수를 표현할때 binary에서 첫번째 bit를 부호를 나타내는데 사용하기 때문입니다. 첫번재 bit가 0이면 양수, 1이면 음수를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 이를 확인하기 위한 것으로 i의 값을 양수5부터 음수5까지 순회하면서 각각의 값을 binary로 표시하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770021473227&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (int i = 5; i &amp;gt;= -5; i--)
{
    Console.WriteLine($&quot;{i} to bit =&amp;gt; {i:B32}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgGjdk/dJMcaiB9Bhs/Tz8orRMB1YvzqIjgFRIzI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgGjdk/dJMcaiB9Bhs/Tz8orRMB1YvzqIjgFRIzI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgGjdk/dJMcaiB9Bhs/Tz8orRMB1YvzqIjgFRIzI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgGjdk%2FdJMcaiB9Bhs%2FTz8orRMB1YvzqIjgFRIzI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;321&quot; height=&quot;185&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용한 :B32는 32자리의 크기로 binary를 나타내기 위한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 모든 양수값에서 bit는 0으로 시작하며 모든 음수값에서 bit는 1로 시작함을 알 수 있습니다. 특히 -1값을 보면 모든 bit가 1임을 알 수 있는데 상기 예제에서 long의 Max값을 binary로 표현하면&lt;/p&gt;
&lt;pre id=&quot;code_1770021524195&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long l = long.MaxValue;
Console.WriteLine($&quot;{l} to bit =&amp;gt; {l:B64}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 되고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;18&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ktb51/dJMcafMfmc3/YN7CswKyGY9xG4j7qlpO51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ktb51/dJMcafMfmc3/YN7CswKyGY9xG4j7qlpO51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ktb51/dJMcafMfmc3/YN7CswKyGY9xG4j7qlpO51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKtb51%2FdJMcafMfmc3%2FYN7CswKyGY9xG4j7qlpO51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;18&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;18&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 int값으로 변환하게 되면 뒤의 1부분만 32bit로 들어가게 되므로 결국 예제의 i값은 -1이 됩니다. 즉, 큰값에서 작은값으로의 형변환시에 큰값의 bit가 버려지게 되는 것인데, 예를 들어 32bit int값을 16bit int값으로 형변환하게 되면 앞의 16bit에 해당하는 모든 bit가 버려지게 되고 나머지 16bit만 사용됩니다. 그리고 결국 여기에 해당하는 만큼의 값만이 남게 되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5,000,000,000의 long값을 int로 변환하는 예제에서도 마찬가지입니다. 이러한 원리가 적용되면 결과적으로 707949866이라는 값을 표시하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770021593042&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long l = 5_000_000_000;
int i = (int)l;

Console.WriteLine($&quot;{l} to bit =&amp;gt; {l:B64}&quot;);
Console.WriteLine($&quot;{i:10} to bit =&amp;gt; {i:B64}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;36&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaKDoJ/dJMcaaqCy95/du8oG0wnMsaleoYkWaKls1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaKDoJ/dJMcaaqCy95/du8oG0wnMsaleoYkWaKls1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaKDoJ/dJMcaaqCy95/du8oG0wnMsaleoYkWaKls1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaKDoJ%2FdJMcaaqCy95%2Fdu8oG0wnMsaleoYkWaKls1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;36&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;36&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부호화된 수를 computer에서 표시하는 것과 관련해 더 자세한 사항을 알아보려면 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Signed_number_repr%EF%BB%BFesentations&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Signed_number_repr%EF%BB%BFesentations&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) System.Convert로 type변환하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래 type간 형변환은 비슷한류의 type안에서만 수행할 수 있습니다. 예를 들어 int는 byte나 long사이에서만 가능하며 class역시 동급이나 해당 class의 하위 class사이에만 가능합니다. 따라서 long을 문자열로 하거나 byte를 DateTime으로 변환하는 것은 암시적이든 명시적이든 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 서로 다른 type간 변환을 수행하기 위해서는 조금 특별한 방법을 써야 하는데 그것이 바로 System.Convert를 사용하는 것입니다. System.Convert는 bool, DateTime, string등 뿐만 아니라 숫자관련 모든 type으로 부터 변환을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 System.Convert를 사용하여 변환을 수행한 것으로&lt;/p&gt;
&lt;pre id=&quot;code_1770277230539&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long l = 1_000_000_000;
string s = System.Convert.ToString(l);

Console.WriteLine(s);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Convert의 ToString method를 통해 지정한 값을 문자열로 변환하고 있습니다. System.Convert는 이외 ToInt32나 ToBoolean등 다양한 변환 method를 가지고 있으므로 필요시에 적절한 method를 호출하여 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환을 말할때 흔히 Cast와 Convert가 많이 언급되는데 기본적인 개념은 동일하지만 Convert는 변환과정에서 overflow가 발생하면 이에 대한 예외를 발생시키지만 Cast는 예외대신 overflow를 허용한다는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 반올림 규칙&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실수를 정수형태로 형변환하는 경우는 소수점 이하 값을 어떻게 할지에 대한 결정이 필요한데, 이를 위해 적용되는 규칙이 존재합니다. 우리가 초등학생때 배운것을 생각해 보면 소수이하값이 .5이상이면 올림을, 그 보다 더 작은 값이면 내람을 하면 된다고 판단할 수도 있습니다. 물론 이치에 틀린말은 아니지만 이러한 방식은 경우에 따라 한쪽으로 올림이 몰리는 현상이 발생할 소지가 많습니다. 따라서 C#에서는 약간의 규칙을 적용하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 C#에서 소수점이하값을 처리하기 위해 사용하는 규칙을 정확히 알아보기 위한 것으로서 몇개의 소수에 대한 변환을 수행하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770277455572&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double[] values = { 2.49, 3.5, 4.51, -5.49, -6.5, -7.51 };

foreach(var item in values)
{
    Console.WriteLine(Convert.ToInt32(item));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;22&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/odfPg/dJMcafyJxBG/47TKTEvPaJCshXeCEcAQEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/odfPg/dJMcafyJxBG/47TKTEvPaJCshXeCEcAQEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/odfPg/dJMcafyJxBG/47TKTEvPaJCshXeCEcAQEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FodfPg%2FdJMcafyJxBG%2F47TKTEvPaJCshXeCEcAQEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;22&quot; height=&quot;104&quot; data-origin-width=&quot;22&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 C#에서는 위에서 언급한 단순한 규칙을 따르고 있지 않다는 것을 알 수 있습니다. 예제에서 적용된 규칙을 살펴보면 우선 소수점값이 .5보다 낮으면 0으로 내림을 수행하고 .5보다 더 높으면 올림을 수행합니다. 여기까지는 별 다를바 없지만 만약 소수점이하값이 .5이고 정수부분이 음수라면 반올림을 수행하지만 정수부분이 양수라면 내림을 수행한다는 것을 알 수 있습니다. 이러한 규칙을 흔히 '은행가의 반올림'이라고 하며 반올림과 내림을 균등하게 교차시킬 수 있다는 이유로 선호되는 방식이기도 합니다. 다만 JavaScript와 같은 언어에서 처럼 '은행가의 반올림'규칙을 따르지 않는 것도 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 반올림 규칙 조정하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 반드시 위와 같은 규칙만을 적용해야 하는 것은 아닙니다. 필요하다면 Math class의 Round method를 사용하여 이러한 규칙을 조정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770363887680&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foreach(var item in values)
{
    Console.WriteLine(Math.Round(item, 0, MidpointRounding.AwayFromZero));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 위 예제에서 Round method를 통해 MidpointRounding.AwayFromZero option으로 반올림규칙을 조정한 것입니다. 위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;25&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAfeuc/dJMcadHCYlS/DsEkYPxg0J8dztSmcdh1mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAfeuc/dJMcadHCYlS/DsEkYPxg0J8dztSmcdh1mK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAfeuc/dJMcadHCYlS/DsEkYPxg0J8dztSmcdh1mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAfeuc%2FdJMcadHCYlS%2FDsEkYPxg0J8dztSmcdh1mK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;25&quot; height=&quot;102&quot; data-origin-width=&quot;25&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과에서 보듯 소수점이하 .5를 포함해 그 이상인 값은 모두 반올림되었음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 특정 type에서 string으로 형변환 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 type에서 string으로 변환하는 경우의 대부분은 읽을 수 있는 형태로 type을 나타내기 위한 것입니다. 이를 위해 모든 type은 System.Object class에서 파생된 ToString이라는 method를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에서 ToString은 해당 변수의 값을 문자열로 나타내지만 어떤 type의 경우에는 ToString으로 나타낼만한 정보가 없기 때문에 대신 자신이 속한 namespace와 이름을 표시하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 몇가지 변수를 선언해두고 각각 자신의 ToString method를 호출하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770363944770&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10;
bool b = true;
object o = new();

Console.WriteLine(i.ToString());
Console.WriteLine(b.ToString());
Console.WriteLine(o.ToString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;97&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4qwCb/dJMcabQBRqV/SNd2BYema1lONdA8fJ7zk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4qwCb/dJMcabQBRqV/SNd2BYema1lONdA8fJ7zk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4qwCb/dJMcabQBRqV/SNd2BYema1lONdA8fJ7zk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4qwCb%2FdJMcabQBRqV%2FSNd2BYema1lONdA8fJ7zk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;97&quot; height=&quot;56&quot; data-origin-width=&quot;97&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위 예제는 사실 아래와 같이 ToString method를 생략할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770363967536&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(i);
Console.WriteLine(b);
Console.WriteLine(o);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 개체를 WriteLine method에 전달하면 암시적으로 string type의 형변환이 이루어지기 때문이며 따라서 명시적으로 ToString을 호출할 필요가 없습니다. 그러나 ToString method를 명시적으로 호출하면 string으로 발생하는 형변환의 boxing동작을 피할 수 있기 때문에 method를 호출하는 쪽이 성능면에서는 더 도움이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Binary에서 string으로의 변환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Image나 동영상과 같은 media file은 그 자체가 binary로 이루어져 있는데 이러한 파일에 대한 전송과 저장은 아주 흔하게 발생합니다. 예를 들어 특정 image를 전송하고자 할때 binary자체의 bit를 그대로 보낼 수 있지만 bit를 있는 그대로 다룬다는건 생각보다 절차가 복잡하고 어려울 수 있습니다. 게다가 이기종간의 전송이라면 동일한 bit를 다르게 해석할 수 있다는 문제도 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결하기 위한 가장 안정적인 방식은 binary개체를 안정적인 문자열로 변환하여 처리하는 것이며 이때 가장 많이 사용되는 것이 바로 Base64 encoding입니다. Base64는 64개의 특정 문자집합을 사용해 임의의 bit data를 문자열로 변환하는 encoding scheme로서 data전송에 광범위하게 사용되는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64를 사용하기 위해서는 Convert type을 사용해야 하며 Convert는 이를 위한 ToBase64String과 FromBase64String이라는 2개의 method를 갖고 있고 이 method를 통해 변환과 복원을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 우선 아래와 같이 byte배열 변수를 선언하고 이를 임의의 값으로 채워넣습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770364047904&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;byte [] b = new byte[128];
Random.Shared.NextBytes(b);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런다음 각각의 개별값을 그대로 표시합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770364058357&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for(int i = 0; i &amp;lt; b.Length; i++)
    Console.Write(b[i] + &quot; &quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 예제를 실행하면 아래와 같은 결과를 볼 수 있습니다. 결과는 채워진 값에 따라 다르게 보일 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvzD1T/dJMcacWhjtJ/HIWhK7aolzN0f6vVigw93k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvzD1T/dJMcacWhjtJ/HIWhK7aolzN0f6vVigw93k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvzD1T/dJMcacWhjtJ/HIWhK7aolzN0f6vVigw93k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvzD1T%2FdJMcacWhjtJ%2FHIWhK7aolzN0f6vVigw93k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1882&quot; height=&quot;35&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;기본적으로 위 결과는 int값을 10진수로 표시합니다. 따라서 만약 이를 16진수로 표시하고자 한다면 :X2와 같은 서식 code를 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위와 같은 byte배열 data가 image binary data라고 가정한다면 다음의 방법을 통해 base64 문자열로 변환할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770364106105&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s = Convert.ToBase64String(b);
Console.WriteLine(s);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;19&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGECD2/dJMcaia9R1Q/zoslTdnC5lIDFCVsnJtSa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGECD2/dJMcaia9R1Q/zoslTdnC5lIDFCVsnJtSa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGECD2/dJMcaia9R1Q/zoslTdnC5lIDFCVsnJtSa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGECD2%2FdJMcaia9R1Q%2FzoslTdnC5lIDFCVsnJtSa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;593&quot; height=&quot;19&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;19&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) URL에서 Base64 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64는 위와 같은 상황에서는 매우 유용하게 사용될 수 있지만 특정한 상황에서는 문제를 일으킬 수 있습니다. 특히 query string을 갖고 있는 URL을 처리하는 경우에는 +나 -같은 문자가 문제가 될 수 있는데 Base64에서 이러한 문자는 조금 특수하게 취급되고 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 URL의 경우 Base64Url이라고 하는 전용 scheme를 사용해야 합니다. 기본적으로는 Base64와 비슷하지만 URL에서 문제될 수 있는 몇가지 문자를 약간 다르게 사용함으로서 URL을 좀더 안전하게 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET9 에서는 새로운 Base64Url class를 도입하였고 여기에서 URL data를 encoding하고 decoding하기 위한 최적화된 method를 제공하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 특정 URL을 통해 Base64Url을 사용하여 URL을 encode하고 다시 decode한뒤 그 결과를 확인하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770621757059&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Buffers.Text;
using System.Text;

string originalURL = &quot;http://...url?find=name+age&quot;;
byte[] urlBytes = Encoding.UTF8.GetBytes(originalURL);
string encodedURL = Base64Url.EncodeToString(urlBytes);
Console.WriteLine(&quot;Encode : &quot; + encodedURL);

byte[] urlEncodeBytes = Base64Url.DecodeFromChars(encodedURL);
string encodedUrl = Encoding.UTF8.GetString(urlEncodeBytes);
Console.WriteLine(&quot;Decode : &quot; + encodedUrl);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dwifk/dJMb996mEHS/FUzodvlH86HR7pnKSIMz9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dwifk/dJMb996mEHS/FUzodvlH86HR7pnKSIMz9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dwifk/dJMb996mEHS/FUzodvlH86HR7pnKSIMz9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDwifk%2FdJMb996mEHS%2FFUzodvlH86HR7pnKSIMz9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;322&quot; height=&quot;33&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64와 관련된 더 자세한 사항은 아래 URL을 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://base64.guru/standards/base64url&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://base64.guru/standards/base64url&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1770621797005&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bM9S2P/dJMb86nTh3r/THKaI3Ek65Vm5fqdf9Ql81/img.jpg?width=512&amp;amp;height=256&amp;amp;face=0_0_512_256,https://scrap.kakaocdn.net/dn/Enteq/dJMb82MyNML/VWF5O94Xaj5f0aGpHTk4DK/img.png?width=512&amp;amp;height=256&amp;amp;face=0_0_512_256,https://scrap.kakaocdn.net/dn/ZpTUM/dJMb89x9iLW/9o9gED343pSPvsIlvioQGK/img.jpg?width=512&amp;amp;height=256&amp;amp;face=0_0_512_256&quot; data-og-url=&quot;https://base64.guru/standards/base64url&quot; data-og-source-url=&quot;https://base64.guru/standards/base64url&quot; data-og-host=&quot;base64.guru&quot; data-og-description=&quot;Base64URL Base64URL is a modification of the main Base64 standard, the purpose of which is the ability to use the encoding result as filename or URL address. The Base64URL is described in RFC 4648 &amp;sect; 5, where it is mentioned that the standard Base64 alphab&quot; data-og-title=&quot;Base64URL | Base64 Standards | Base64&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://base64.guru/standards/base64url&quot; data-source-url=&quot;https://base64.guru/standards/base64url&quot;&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Base64URL | Base64 Standards | Base64&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Base64URL Base64URL is a modification of the main Base64 standard, the purpose of which is the ability to use the encoding result as filename or URL address. The Base64URL is described in RFC 4648 &amp;sect; 5, where it is mentioned that the standard Base64 alphab&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;base64.guru&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 숫자나 날짜/시간 parsing하기위한 Parse 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열값으로 부터 숫자나 날짜 혹은 시간으로 변환하여 가져오는 경우는 아주 흔하게 발생하는 일입니다. 어떤 type에서 ToString method를 호출하는 것은 개체를 문자열로 나타내기 위한 것인데 이것과 반대되는 것이 바로 Parse입니다. 다만 Parse는 모든 type에 존재하지 않고 숫자나 DateTime과 같은 특정 type에만 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 문자열에서 각각의 Parse method를 통해 int값과 DateTime값을 가져오고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770621844709&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = int.Parse(&quot;100&quot;);
DateTime dt = DateTime.Parse(&quot;2026-02-02 10:30:20&quot;);

Console.WriteLine(i);
Console.WriteLine(dt);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;146&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baQIsI/dJMcaibaWWE/B4BqzB7U3jtf8ddVUY0eQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baQIsI/dJMcaibaWWE/B4BqzB7U3jtf8ddVUY0eQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baQIsI/dJMcaibaWWE/B4BqzB7U3jtf8ddVUY0eQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaQIsI%2FdJMcaibaWWE%2FB4BqzB7U3jtf8ddVUY0eQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;146&quot; height=&quot;39&quot; data-origin-width=&quot;146&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;DateTime형식의 값을 사용할때는 표준 날짜/시간 서식지정자를 사용할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#table-of-format-specifiers&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#table-of-format-specifiers&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) TryParse method를 통해 예외 처리하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parse method를 편리하기는 하지만 만약 지정한 문자열에서 원하는 값으로 변환할 수 없는 경우에는 예외를 발생시키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 경우입니다. int의 Parse를 통해 문자열을 int형으로 변환하려 하고 있지만 지정한 문자열은 숫자가 안니 문자열을 갖고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770621935303&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = int.Parse(&quot;abc&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먄약 위의 예제를 실행하려 하면 다음과 같은 예외를 보게 될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RzsA4/dJMcaiWuSZE/3ge8nbpKFQGhSw2h1TVea1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RzsA4/dJMcaiWuSZE/3ge8nbpKFQGhSw2h1TVea1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RzsA4/dJMcaiWuSZE/3ge8nbpKFQGhSw2h1TVea1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRzsA4%2FdJMcaiWuSZE%2F3ge8nbpKFQGhSw2h1TVea1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;55&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Parse에서 발생할 수 있는 예외를 적절하게 처리하려면 try ~ catch문을 사용하거나 더 간단하게는 TryParse method를 대신 사용할 수 있습니다. TryParse는 주어진 문자열을 원한는 type으로 변환을 시도하고 변환이 가능하다면 true를, 그렇지 않으면 false를 반환합니다. 예외를 발생시키는 것은 비교적 비용이 많이 드는 동작이므로 되도록이면 이를 피하고자 TryParse를 사용하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 TryParse를 사용해 문자열값으로부터 int type으로 변환을 수행하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770621975060&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (int.TryParse(&quot;abc&quot;, out int i))
{
    Console.WriteLine(i);
}
else
{
    Console.WriteLine(&quot;failed!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 사용된 out keyword는 TryParse method가 정상적으로 변환하는 작업을 수행했을때 그 결과값을 담아둘 변수를 지정하기 위한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;59&quot; data-origin-height=&quot;22&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by3lll/dJMcafyK6Hz/4fGcDibqS6i0A4Og1rqKsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by3lll/dJMcafyK6Hz/4fGcDibqS6i0A4Og1rqKsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by3lll/dJMcafyK6Hz/4fGcDibqS6i0A4Og1rqKsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby3lll%2FdJMcafyK6Hz%2F4fGcDibqS6i0A4Og1rqKsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;59&quot; height=&quot;22&quot; data-origin-width=&quot;59&quot; data-origin-height=&quot;22&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 문자열값을 다른 type으로 변환하기 위해 System.Convert method를 사용할 수 있지만 변환에 실패하는 경우 예외를 발생시킵니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;모든 TryParse method는 변환의 성공과 실패를 나타내기 위해 bool type의 값을 반환하며, 변환에 성공하는 경우 값을 담기위한 out 매개변수를 가집니다. 이러한 규칙은 모든 try method에 동일하게 적용됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. 예외처리&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Application에서는 어떤 예기치 않은 문제가 발생하면 '예외(Exception)'을 발생시킬 수 있습니다. 이때 발생한 예외를 적절히 처리하는 것을 예외처리라고 하며, 어떤 언어에서는 error가 발생하면 그에 해당하는 error code를 대신 반환하기도 합니다. .NET은 error가 발생한 경우에 예외라는걸 통해서 문제점을 좀더 잘 살펴볼 수 있도록 지원하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Error code를 사용하는 경우 해당 값에 따라 여러가지로 판단할 수 있을 것입니다. 예를 들어 값이 음수인 경우 무엇인가 잘못되었음을 알리는 것이지만 양수값인 경우 정상처리되었음을 알려주는 의미가 될 수도 있습니다. .NET에서도 일부 third-party library의 경우 result type을 자체적으로 정의하여 이러한 값의 의미를 더 잘 이해할 수 있도록 해주는 경우도 있는데 예외를 사용하는 것 보다 이러한 방식을 더 선호하는 경우도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application에서 예외가 발생하면 thread는 중지됩니다. 이때 예외를 발생시킨 code를 try~catch문안에서 구현했다면 자체적으로 예외를 처리할 수 있습니다. 만약 그렇지 않은경우 해당 code를 호출한 상위 stack으로 예외를 던지게 되며 그 상위에서도 적절한 예외처리가 구현되어 있지 않으면 또 다시 그 상위로 예외를 던지는 동작을 계속 진행하게 됩니다. 그러다가 더이상 거슬로 올라갈 수 있는 상위 stack이 존재하지 않으면 그때 사용자에게 stack trace정보를 포함해 예외 message를 표시하고 Application전체를 종료시키게 됩니다. 잠재적으로 잘못된 상태의 Application을 불안하게 유지하는 것보다 이러한 방식으로 처리하는 것이 더 좋은 선택일 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;if문과 같이 조건을 확인하는 방식을 통해 예외를 발생시킬 수 있는 code의 작성을 최소화하는 것이 좋습니다. 물론 문제가 되는 code를 호출하는 상위 component에서 예외를 처리하도록 하는 것이 더 나은 선택일 수 있으므로 상황에 따라 잘 판단해야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET9 부터 예외는 NativeAOT 예외처리 model을 기반으로한 새로운 방식을 사용하고 있으며 이를 통해 이전보다 더 나은 성능을 그대할 수 있게 되었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) try ~ catch&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 code가 error를 유발할 가능성이 있으면 이를 try~catch문으로 감싸둘 수 있습니다. 예를 들어 문자열에서 숫자값을 읽어들이는 위의 예제의 경우에도 Parse method를 사용하면서 try~catch문을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try~catch는 try와 catch 2개의 부분으로 구성되는데 try문안에 있는 code가 기본적으로 실행되면서 예외가 발생한 경우에만 catch문안에 있는 code를 실행하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770708772124&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;start&quot;);

string? s = Console.ReadLine();

try
{
    int i = int.Parse(s!);
    Console.WriteLine($&quot;input value : {i}&quot;);
}
catch (System.Exception)
{
}

Console.WriteLine(&quot;end&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 문자열값을 int값으로 읽어들이는 시도를 하고 있는데 이 구문을 try~catch로 감싸고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 예제는 'warning CS8600: Converting null literal or possible null value to non-nullable type.'이나 'warning CS8604: Possible null reference argument for parameter 's' in 'int int.Parse(string s)'.'과 같은 경고 message를 표시할 수 있는데 이는 .NET6 부터 nullable 참조 type을 허용하고 있기 때문입니다. 물론 위 code가 실제 사용자에게 배포되는 application수준의 code라면 이러한 경고를 무시해선 안되고 null인 경우에 대한 적절한 처리를 추가해야 합니다. 그러나 예제는 사용자가 아무런 값을 입력하지 않는 경우에도 공백을 반환하므로 실제 null이 되지 않을 뿐더러 null을 확인하는 추가적인 code를 추가하면 code를 더 복잡하게 만들 수 있어 이를 생략하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) null-forgiving 연산자를 통해 경고 message억제하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서의 compiler 경고를 억제하려면 s문자뒤에 느낌표(!)문자를 붙여주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770708839324&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string? s = Console.ReadLine();

try
{
    int i = int.Parse(s!);
    Console.WriteLine($&quot;input value : {i}&quot;);
}
catch (System.Exception)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 !를 'null-forgiving 연산자'라고 합니다. null-forgiving 연산자는 runtime에서 아무런 영향도 주지 않지만 만약 null이 발생한다면 예외를 발생시킬 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 숫자를 입력하면 아래와 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;127&quot; data-origin-height=&quot;68&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDijX2/dJMcagj6Irv/SZolCSxQ5qCkWstIgwrRPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDijX2/dJMcagj6Irv/SZolCSxQ5qCkWstIgwrRPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDijX2/dJMcagj6Irv/SZolCSxQ5qCkWstIgwrRPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDijX2%2FdJMcagj6Irv%2FSZolCSxQ5qCkWstIgwrRPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;127&quot; height=&quot;68&quot; data-origin-width=&quot;127&quot; data-origin-height=&quot;68&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 다시 예제를 실행하고 숫자가 아닌 문자를 입력합니다. 그러면 결과를 약간 다르게 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;40&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chTVYH/dJMcaiWvh7X/lfobTxjWycUKIzz5i85xoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chTVYH/dJMcaiWvh7X/lfobTxjWycUKIzz5i85xoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chTVYH/dJMcaiWvh7X/lfobTxjWycUKIzz5i85xoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchTVYH%2FdJMcaiWvh7X%2FlfobTxjWycUKIzz5i85xoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;40&quot; height=&quot;50&quot; data-origin-width=&quot;40&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제가 실행된 후 try안에 구문에서 예외가 발생하면 별다른 message를 표시하지 않고 application이 정상적으로 종료된다는 것을 알 수 있습니다. 물론 이러한 동작도 나쁘지 않지만 예외가 발생하면 어떤 문제가 발생했지는 사용자에게 알려주는 것이 더 유용할 수 있습니다. 실제 사용자에게 배포되는 application의 경우라면 예외를 조용히 처리하는건 잠재적인 문제점을 숨기는 것에 불과하므로 당장에 예외를 사용자에게 보여주길 원하지 않거나 적절한 처리를 하지 않을 것이라면 log를 남겨두는것도 방법이 될 수 있습니다. 또는 예외를 상위로 던져 상위수준에서 발생한 예외를 처리하도록 하는 것도 좋은 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 모든 예외 처리하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발생할 수 있는 모든 type의 예외에 대한 정보를 가져오기 위해서는 catch에서 System.Exception type의 변수를 선언해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770797410617&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try
{
    int i = int.Parse(s!);
    Console.WriteLine($&quot;input value : {i}&quot;);
}
catch (System.Exception ex)
{
    Console.WriteLine($&quot;{ex.GetType()} - {ex.Message}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 예외가 발생한경우 해당 예외 type과 예외에 대한 message를 표시하도록 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 후 abc와 같이 숫자가 아닌 문자를 입력하면 아래와 같은 예외 message를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wNYVL/dJMcad1Xa1O/EJFXVIXnekeWqwtUYircZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wNYVL/dJMcad1Xa1O/EJFXVIXnekeWqwtUYircZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wNYVL/dJMcad1Xa1O/EJFXVIXnekeWqwtUYircZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwNYVL%2FdJMcad1Xa1O%2FEJFXVIXnekeWqwtUYircZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;33&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 특정 예외만 처리하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 통해 우리는 숫자가 아닌 값을 입력하면 어떤 type의 예외가 발생할 수 있는지 알 수 있습니다. 발생가능한 예외의 type을 알면 해당 하는 예외가 발생했을때 사용자에게 좀더 도움이 될만한 정보를 제공해 줄 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 기존 catch (System.Exception ex)는 그대로 남겨두고 그 위에서 이전에 발생한 FormatException 예외를 아래와 같이 추가합니다. FormatException이 어떤 경우에 발생할 수 있는지 알고 있으므로 사용자에게 그에 맞는 안내 message를 다음과 같이 남겨 둘 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770797491096&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try
{
    int i = int.Parse(s!);
    Console.WriteLine($&quot;input value : {i}&quot;);
}
catch (System.FormatException)
{
    Console.WriteLine(&quot;The you entered is not a valid number.&quot;);
}
catch (System.Exception ex)
{
    Console.WriteLine($&quot;{ex.GetType()} - {ex.Message}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 이전 예제와 동일한 값을 입력하면 이번에는 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmPeMh/dJMcafyL2oV/asLJatq4J5adkNNxomaD71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmPeMh/dJMcafyL2oV/asLJatq4J5adkNNxomaD71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmPeMh/dJMcafyL2oV/asLJatq4J5adkNNxomaD71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmPeMh%2FdJMcafyL2oV%2FasLJatq4J5adkNNxomaD71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;33&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 System.Exception에 대한 catch를 제거하지 않고 남겨 두는 이유는 FormatException이외에 다른 예외가 발생하는 경우에 대한 대비입니다. 이때 위와 같이 여러 catch를 사용하고자 하는 경우에는 순서가 중요한데, 가장 구체적이며 발생할 가능성이 많은 예외를 위에 두고 System.Exception처럼 보편적인 예외는 가장마지막에 두어야 합니다. 그렇다고 이 순서를 크게 걱정할 필요는 없습니다. 어차피 순서가 잘못되면 compiler는 error를 표시하고 Application의 compile을 중단할 것이기 때문입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;모든 예외 type은 System.Exception으로 부터 상속되므로 System.Exception은 전체 예외 type에 대한 최상위 type이 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 너무 많은 예외를 잡지 않도록 주의해야 합니다. 예외를 처리할 수 있는 더 많은 정보를 가진 상위의 call stack으로 예외를 전파시키는 것이 당장 예외를 처리하여 전파를 중단시키는 것 보다 더 나은 상황을 만들 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Catch에서 filter적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;catch부분에서는 when keyword를 사용하여 특정 조건에 대한 예외를 filter할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770877020858&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;catch (System.FormatException) when (s.Contains(&quot;abc&quot;))
{
    Console.WriteLine(&quot;Please enter a number, not a letter.&quot;);
}
catch (System.FormatException)
{
    Console.WriteLine(&quot;The you entered is not a valid number.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 동일한 FormatException에 대한 catch를 추가하면서도 when을 사용해 입력된 내용중에서 'abc'라는 문자열을 포함하는 경우로 한정하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) Overflow&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 형변환에 대해 언급할때 number type의 형변환시 예를 들면 long에서 int형으로 변환시에 본래 값에서 일부 data가 손실될 수 있음을 알 수 있었습니다. 그런데 변환대상이 되는 값이 저장될 type에 비해 너무 크다면 이때는 overflow가 발생하게 되고 예상치 못한 결과를 만들게 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) checked&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;checked 구문을 사용하면 overflow 예외를 발생시킬 수 있습니다. 본래 overflow상황이 발생하면 성능적인 문제 때문에 일부 data가 손실되는 상황이 발생해도 결과값을 저장함으로서 별다른 오류없이 조용히 처리하지만 checked문은 overflow가 발생하면 이에 대한 예외를 던져주게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 에제를 보겠습니다. 예제는 int형변수에 int가 담을 수 있는 최대값을 저장한 후 +1을 하여 그 결과를 확인하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770880591768&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = int.MaxValue;

Console.WriteLine(i);
++i;
Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;89&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsx36N/dJMcadOp1gV/r6s2MvyetiSxaHEzKDD5vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsx36N/dJMcadOp1gV/r6s2MvyetiSxaHEzKDD5vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsx36N/dJMcadOp1gV/r6s2MvyetiSxaHEzKDD5vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdsx36N%2FdJMcadOp1gV%2Fr6s2MvyetiSxaHEzKDD5vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;89&quot; height=&quot;35&quot; data-origin-width=&quot;89&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행결과를 보면 int최대값에서 +1을 하게 됨으로서 overflow가 발생하였고 그 결과 i의 값은 -값이 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 에제는 위 예제에서 checked문만 추가한 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770880637340&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;checked {
    int i = int.MaxValue;

    Console.WriteLine(i);
    ++i;
    Console.WriteLine(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이번에는 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KabLI/dJMcadnmXRR/6n8SilLioyGjywKcJphSuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KabLI/dJMcadnmXRR/6n8SilLioyGjywKcJphSuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KabLI/dJMcadnmXRR/6n8SilLioyGjywKcJphSuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKabLI%2FdJMcadnmXRR%2F6n8SilLioyGjywKcJphSuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;51&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 예외를 통해 overflow가 발생하였음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하지만 overflow예외의 경우에도 catch를 통해 잡아낼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770880671302&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try
{
    checked {
        int i = int.MaxValue;

        Console.WriteLine(i);
        ++i;
        Console.WriteLine(i);
    }   
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PEJRP/dJMcabwlvf0/JCSo7QEuRoJJk1x0O3992k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PEJRP/dJMcabwlvf0/JCSo7QEuRoJJk1x0O3992k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PEJRP/dJMcabwlvf0/JCSo7QEuRoJJk1x0O3992k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPEJRP%2FdJMcabwlvf0%2FJCSo7QEuRoJJk1x0O3992k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;322&quot; height=&quot;34&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) unchecked&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;위에서 사용한 checked는 runtime에서 overflow가 발생한 경우 기본동작을 바꿔 예외를 발생시키도록 하기 위한 것입니다. 그런데 overflow는 compile단계에서 오류를 발생시키는 경우도 있습니다. 특히 VS2026과 같은 IDE에서 아래와 같이 overflow가 예상되는 구문을 작성했을때&lt;/p&gt;
&lt;pre id=&quot;code_1770880716838&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = int.MaxValue + 1;

Console.WriteLine(i);
++i;
Console.WriteLine(i);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compiler는 compile을 시도하기 전에 다음과 같이 해당 구문에 문제가 있음을 알려주게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWvBTr/dJMcabQEhit/0jVapLfJgXQ0fML2H0Tx0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWvBTr/dJMcabQEhit/0jVapLfJgXQ0fML2H0Tx0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWvBTr/dJMcabQEhit/0jVapLfJgXQ0fML2H0Tx0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWvBTr%2FdJMcabQEhit%2F0jVapLfJgXQ0fML2H0Tx0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;138&quot; data-origin-width=&quot;542&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 피하려면 unchecked라는 구문을 checked와 동일한 방식으로 다음과 같이 사용해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1770880748324&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;unchecked
{
    int i = int.MaxValue + 1;

    Console.WriteLine(i);
    ++i;
    Console.WriteLine(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 더이상 compiler는 overflow에 대한 오류를 발생시키지 않을것입니다. 하지만 이러한 방법은 오류를 억제할 뿐 overflow는 여전히 발생할 수 있다는 점을 기억해야 합니다.&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>.NET</category>
      <category>c#</category>
      <category>반목문</category>
      <category>배열</category>
      <category>분기문</category>
      <category>선택문</category>
      <category>연산자</category>
      <category>예외</category>
      <category>형변환</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/645</guid>
      <comments>https://cliel.tistory.com/entry/C-14-NET-10-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4-Type-%EB%B3%80%ED%99%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC#entry645comment</comments>
      <pubDate>Thu, 12 Feb 2026 16:21:00 +0900</pubDate>
    </item>
    <item>
      <title>무차별 대입 공격 방어 도구 (Brute Force Attack Depend Tool) - Ban_Server</title>
      <link>https://cliel.tistory.com/entry/%EB%AC%B4%EC%B0%A8%EB%B3%84-%EB%8C%80%EC%9E%85-%EA%B3%B5%EA%B2%A9-%EB%B0%A9%EC%96%B4-%EB%8F%84%EA%B5%AC-Brute-Force-Attack-Depend-Tool-BanServer</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Remote Desktop의 경우 원격 port는 3389, MS-SQL Server의 경우 1433이 기본 port입니다. 그런데 server를 외부에서 접속 가능하도록 두면 이 2개 port에 대해서 무차별 대입 공격이 발생함을 알 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;For Remote Desktop, the default remote port is 3389, and for MS-SQL Server, it is 1433. However, if you leave the server accessible from outside, you can see that brute-force attacks occur on these two ports.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/puuo0/btrJ8JnwA0j/HaIdA5kkH0Qbw7FD6sDXBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/puuo0/btrJ8JnwA0j/HaIdA5kkH0Qbw7FD6sDXBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/puuo0/btrJ8JnwA0j/HaIdA5kkH0Qbw7FD6sDXBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpuuo0%2FbtrJ8JnwA0j%2FHaIdA5kkH0Qbw7FD6sDXBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;764&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;port번호를 바꾸면 일시적으로는 공격 빈도가 줄지만 시간이 지나면 다시 공격이 되살아나서 별 의미가 없습니다. 때문에 접속 가능한 IP만 제한적으로 열어두는 것이 제일 좋은데 만약 접속을 허용하는 IP만 특정할 수 없는 경우라면 해결 방법을 달리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Changing&amp;nbsp;the&amp;nbsp;port&amp;nbsp;number&amp;nbsp;may&amp;nbsp;temporarily&amp;nbsp;reduce&amp;nbsp;the&amp;nbsp;frequency&amp;nbsp;of&amp;nbsp;attacks,&amp;nbsp;but&amp;nbsp;over&amp;nbsp;time,&amp;nbsp;the&amp;nbsp;attacks&amp;nbsp;will&amp;nbsp;resume,&amp;nbsp;making&amp;nbsp;it&amp;nbsp;largely&amp;nbsp;ineffective.&amp;nbsp;Therefore,&amp;nbsp;the&amp;nbsp;best&amp;nbsp;approach&amp;nbsp;is&amp;nbsp;to&amp;nbsp;restrict&amp;nbsp;access&amp;nbsp;to&amp;nbsp;only&amp;nbsp;specific&amp;nbsp;IP&amp;nbsp;addresses.&amp;nbsp;If&amp;nbsp;it&amp;nbsp;is&amp;nbsp;not&amp;nbsp;possible&amp;nbsp;to&amp;nbsp;specify&amp;nbsp;the&amp;nbsp;exact&amp;nbsp;IP&amp;nbsp;addresses&amp;nbsp;allowed&amp;nbsp;for&amp;nbsp;access,&amp;nbsp;an&amp;nbsp;alternative&amp;nbsp;solution&amp;nbsp;must&amp;nbsp;be&amp;nbsp;implemented.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무차별 대입 공격에 대응할 수 있는 방법이야 여러가지로 존재할테지만 그 방법중의 하나로 Ban_Server와 Ban_MSSQL을 소개하고자 합니다. Ban_Server는 Windows Server에서 원격제어를, Ban_MSSQL은 MSSQL Server의 전용port로 무단 접속을 시도하면 이와 관련된 Log를 만들때 이를 감지하고 공격자의 IP를 차단하는 역활을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There are various methods to counter indiscriminate brute-force attacks, and among them, we introduce Ban_Server and Ban_MSSQL. Ban_Server detects unauthorized attempts to remotely control Windows Server and Ban_MSSQL detects unauthorized attempts to connect to the dedicated port of MSSQL Server. When creating logs related to these attempts, they perform the role of detecting them and blocking the attacker's IP address.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 설치방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&amp;nbsp;Installation&amp;nbsp;Method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Microsoft의 아래 homepage에서 Console Application용으로 .NET 10 Runtime을 내려받아 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First,&amp;nbsp;download&amp;nbsp;and&amp;nbsp;install&amp;nbsp;the&amp;nbsp;.NET&amp;nbsp;10&amp;nbsp;Runtime&amp;nbsp;for&amp;nbsp;Console&amp;nbsp;Applications&amp;nbsp;from&amp;nbsp;the&amp;nbsp;Microsoft&amp;nbsp;homepage&amp;nbsp;below.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot;&gt;.NET 10.0 런타임 다운로드&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768523242457&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;.NET 10.0 런타임 다운로드&quot; data-og-description=&quot;.NET 10.0 런타임에는 기존 .NET 및 ASP.NET Core 애플리케이션을 실행하는 데 필요한 모든 것이 포함됩니다.&quot; data-og-host=&quot;dotnet.microsoft.com&quot; data-og-source-url=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot; data-og-url=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lLcGi/dJMb9dHhycy/5j1QmLiNJwEVSd170QlRq0/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/cuhpAM/dJMb8PGpFQa/eduHkMhhebEa7aFnfjkVD1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dotnet.microsoft.com/ko-kr/download/dotnet/10.0/runtime&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lLcGi/dJMb9dHhycy/5j1QmLiNJwEVSd170QlRq0/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/cuhpAM/dJMb8PGpFQa/eduHkMhhebEa7aFnfjkVD1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;.NET 10.0 런타임 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 10.0 런타임에는 기존 .NET 및 ASP.NET Core 애플리케이션을 실행하는 데 필요한 모든 것이 포함됩니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dotnet.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;.NET 10 Runtime 설치를 하고 나면 아래 file을 내려 받고 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;file을 server의 임의폴더에 풀어줍니다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;After&amp;nbsp;installing&amp;nbsp;the&amp;nbsp;.NET&amp;nbsp;10&amp;nbsp;Runtime,&amp;nbsp;download&amp;nbsp;the&amp;nbsp;file&amp;nbsp;below&amp;nbsp;and&amp;nbsp;extract&amp;nbsp;it&amp;nbsp;to&amp;nbsp;any&amp;nbsp;folder&amp;nbsp;on&amp;nbsp;the&amp;nbsp;server.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/zE4MN/dJMcaivf7Wr/vKQ1KmuHqPF4427QwTFWSk/Ban.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;Ban.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.40MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;압축을 풀고나면 Ban_Server와 Ban_MSSQL 2개의 folder가 있는지 확인하고 각각의 폴더를 원하는 위치로 옮겨줍니다. 만약 REMOTE와 MSSQL을 모두 사용할 필요가 없다면 필요한 것만 사용하고 다른건 삭제해도 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;After&amp;nbsp;unzipping,&amp;nbsp;verify&amp;nbsp;that&amp;nbsp;the&amp;nbsp;Ban_Server&amp;nbsp;and&amp;nbsp;Ban_MSSQL&amp;nbsp;folders&amp;nbsp;are&amp;nbsp;present,&amp;nbsp;then&amp;nbsp;move&amp;nbsp;each&amp;nbsp;folder&amp;nbsp;to&amp;nbsp;your&amp;nbsp;desired&amp;nbsp;location.&amp;nbsp;If&amp;nbsp;you&amp;nbsp;do&amp;nbsp;not&amp;nbsp;need&amp;nbsp;to&amp;nbsp;use&amp;nbsp;both&amp;nbsp;REMOTE&amp;nbsp;and&amp;nbsp;MSSQL,&amp;nbsp;you&amp;nbsp;may&amp;nbsp;use&amp;nbsp;only&amp;nbsp;the&amp;nbsp;necessary&amp;nbsp;one&amp;nbsp;and&amp;nbsp;delete&amp;nbsp;the&amp;nbsp;other.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이제 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;ask Scheduler를 등록해야 하는데 우선 &lt;/span&gt;Remote Desktop에 대응할 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler부터 등록합니다. 이를 위해&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Control Panel -&amp;gt; Administrative Tools -&amp;gt; Task Scheduler를 실행하여 아래 제시된 방법으로 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler를 등록합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;You&amp;nbsp;must&amp;nbsp;now&amp;nbsp;register&amp;nbsp;the&amp;nbsp;Scheduler.&amp;nbsp;Begin&amp;nbsp;by&amp;nbsp;registering&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;for&amp;nbsp;Remote&amp;nbsp;Desktop.&amp;nbsp;To&amp;nbsp;do&amp;nbsp;this,&amp;nbsp;run&amp;nbsp;Control&amp;nbsp;Panel&amp;nbsp;-&amp;gt;&amp;nbsp;Administrative&amp;nbsp;Tools&amp;nbsp;-&amp;gt;&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;and&amp;nbsp;register&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;using&amp;nbsp;the&amp;nbsp;method&amp;nbsp;described&amp;nbsp;below.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLbVG/btrJ8HXvuec/AERiDLuX7oDTctUcS9AcNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLbVG/btrJ8HXvuec/AERiDLuX7oDTctUcS9AcNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLbVG/btrJ8HXvuec/AERiDLuX7oDTctUcS9AcNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLbVG%2FbtrJ8HXvuec%2FAERiDLuX7oDTctUcS9AcNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;오른쪽의 Create Task를 선택합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Select&amp;nbsp;Create&amp;nbsp;Task&amp;nbsp;on&amp;nbsp;the&amp;nbsp;right.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EWjMA/btrJ8rfmJGM/9zvopuMy3RdaSxgw9fcid1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EWjMA/btrJ8rfmJGM/9zvopuMy3RdaSxgw9fcid1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EWjMA/btrJ8rfmJGM/9zvopuMy3RdaSxgw9fcid1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEWjMA%2FbtrJ8rfmJGM%2F9zvopuMy3RdaSxgw9fcid1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;800&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Name에 Ban_REMOTE를 입력하고 아래 Security Options에는 'Run whether user is logged on or not'을 선택합니다. 이때 Name은 해당&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler가 어떠한 목적으로 만들어졌는지를 구분할 수 있을 정도면 다른 이름을 지정할 수도 있습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Enter&amp;nbsp;Ban_REMOTE&amp;nbsp;in&amp;nbsp;the&amp;nbsp;Name&amp;nbsp;field&amp;nbsp;and&amp;nbsp;select&amp;nbsp;&amp;lsquo;Run&amp;nbsp;whether&amp;nbsp;user&amp;nbsp;is&amp;nbsp;logged&amp;nbsp;on&amp;nbsp;or&amp;nbsp;not&amp;rsquo;&amp;nbsp;under&amp;nbsp;Security&amp;nbsp;Options.&amp;nbsp;At&amp;nbsp;this&amp;nbsp;point,&amp;nbsp;you&amp;nbsp;can&amp;nbsp;assign&amp;nbsp;a&amp;nbsp;different&amp;nbsp;name&amp;nbsp;to&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;as&amp;nbsp;long&amp;nbsp;as&amp;nbsp;it&amp;nbsp;clearly&amp;nbsp;indicates&amp;nbsp;the&amp;nbsp;purpose&amp;nbsp;for&amp;nbsp;which&amp;nbsp;it&amp;nbsp;was&amp;nbsp;created.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1Fhzd/btrKcwm9DON/9jJ1NAH7KyPT0bB6dbk6TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1Fhzd/btrKcwm9DON/9jJ1NAH7KyPT0bB6dbk6TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1Fhzd/btrKcwm9DON/9jJ1NAH7KyPT0bB6dbk6TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1Fhzd%2FbtrKcwm9DON%2F9jJ1NAH7KyPT0bB6dbk6TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;480&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 Trigger Tab으로 이동해 Begin the task에 'On an event'를 선택하고 Log에는 Security를 Event ID로는 4625를 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next,&amp;nbsp;go&amp;nbsp;to&amp;nbsp;the&amp;nbsp;Trigger&amp;nbsp;tab,&amp;nbsp;select&amp;nbsp;&amp;lsquo;On&amp;nbsp;an&amp;nbsp;event&amp;rsquo;&amp;nbsp;for&amp;nbsp;Begin&amp;nbsp;the&amp;nbsp;task,&amp;nbsp;and&amp;nbsp;enter&amp;nbsp;Security&amp;nbsp;for&amp;nbsp;Log&amp;nbsp;and&amp;nbsp;4625&amp;nbsp;for&amp;nbsp;Event&amp;nbsp;ID.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wkHqc/btrKeiIrNqA/NWQeYXStVgKdlCgZX0MKgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wkHqc/btrKeiIrNqA/NWQeYXStVgKdlCgZX0MKgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wkHqc/btrKeiIrNqA/NWQeYXStVgKdlCgZX0MKgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwkHqc%2FbtrKeiIrNqA%2FNWQeYXStVgKdlCgZX0MKgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;516&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actions tab에서는 Action에 'Start a program'을 선택하고 Program/script에 Ban_Server.exe를 명시한 뒤 Start in (optional)에 Ban_Server.exe가 존재하는 folder를 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In&amp;nbsp;the&amp;nbsp;Actions&amp;nbsp;tab,&amp;nbsp;select&amp;nbsp;&amp;lsquo;Start&amp;nbsp;a&amp;nbsp;program&amp;rsquo;&amp;nbsp;for&amp;nbsp;Action,&amp;nbsp;specify&amp;nbsp;Ban_Server.exe&amp;nbsp;in&amp;nbsp;Program/script,&amp;nbsp;and&amp;nbsp;then&amp;nbsp;designate&amp;nbsp;the&amp;nbsp;folder&amp;nbsp;where&amp;nbsp;Ban_Server.exe&amp;nbsp;is&amp;nbsp;located&amp;nbsp;in&amp;nbsp;Start&amp;nbsp;in&amp;nbsp;(optional).&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qvxYd/btrKbsFkaOI/xizlHdfkeyudrmin1Cxkkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qvxYd/btrKbsFkaOI/xizlHdfkeyudrmin1Cxkkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qvxYd/btrKbsFkaOI/xizlHdfkeyudrmin1Cxkkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqvxYd%2FbtrKbsFkaOI%2FxizlHdfkeyudrmin1Cxkkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;500&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것으로 REMOTE(Ban_Server)에 대한&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Task Scheduler을 완료하였습니다. 같은 방법으로 MSSQL에 관한 event로 등록해 줘야 하는데 위의 설정과정 대부분이 동일합니다. 다만 MSSQL의 경우 Trigger에서 Log를 Application을 잡고 Event ID를 18456으로 해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;This&amp;nbsp;completes&amp;nbsp;the&amp;nbsp;Task&amp;nbsp;Scheduler&amp;nbsp;setup&amp;nbsp;for&amp;nbsp;REMOTE(Ban_Server).&amp;nbsp;You&amp;nbsp;must&amp;nbsp;register&amp;nbsp;the&amp;nbsp;event&amp;nbsp;for&amp;nbsp;MSSQL&amp;nbsp;using&amp;nbsp;the&amp;nbsp;same&amp;nbsp;method;&amp;nbsp;most&amp;nbsp;of&amp;nbsp;the&amp;nbsp;configuration&amp;nbsp;steps&amp;nbsp;above&amp;nbsp;are&amp;nbsp;identical.&amp;nbsp;However,&amp;nbsp;for&amp;nbsp;MSSQL,&amp;nbsp;you&amp;nbsp;must&amp;nbsp;set&amp;nbsp;the&amp;nbsp;Application&amp;nbsp;in&amp;nbsp;the&amp;nbsp;Trigger&amp;nbsp;to&amp;nbsp;Log&amp;nbsp;and&amp;nbsp;the&amp;nbsp;Event&amp;nbsp;ID&amp;nbsp;to&amp;nbsp;18456.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;2. 사용방법&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;2.&amp;nbsp;How&amp;nbsp;to&amp;nbsp;Use&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;압축을 풀어놓은 folder를 보면 jsconfig.json이라는 이름의 file을 볼 수 있습니다. 해당 file을 아래 내용으로 구성되어 있습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;When you unzip the folder, you will find a file named jsconfig.json. This file is structured as follows:&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;{ &lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;FailedCount&quot;: 1, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;WhiteLists&quot;:&amp;nbsp;[] &lt;br /&gt;}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FailedCount는 실패한 건수에 대한 대응입니다. 동일한 IP에 대하여 설정한 횟수 이상의 접속실패건이 발생하는 경우에만 해당 IP를 방화벽에 등록하여 차단을 수행하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FailedCount&amp;nbsp;corresponds&amp;nbsp;to&amp;nbsp;the&amp;nbsp;number&amp;nbsp;of&amp;nbsp;failures.&amp;nbsp;Only&amp;nbsp;when&amp;nbsp;the&amp;nbsp;number&amp;nbsp;of&amp;nbsp;connection&amp;nbsp;failures&amp;nbsp;exceeds&amp;nbsp;the&amp;nbsp;set&amp;nbsp;limit&amp;nbsp;for&amp;nbsp;the&amp;nbsp;same&amp;nbsp;IP&amp;nbsp;address&amp;nbsp;will&amp;nbsp;that&amp;nbsp;IP&amp;nbsp;be&amp;nbsp;registered&amp;nbsp;in&amp;nbsp;the&amp;nbsp;firewall&amp;nbsp;and&amp;nbsp;blocked.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WhiteLists는 Ban_Server.exe가 예외로 둘 IP를 지정하는 곳입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WhiteLists&amp;nbsp;is&amp;nbsp;where&amp;nbsp;Ban_Server.exe&amp;nbsp;specifies&amp;nbsp;the&amp;nbsp;IP&amp;nbsp;addresses&amp;nbsp;to&amp;nbsp;be&amp;nbsp;treated&amp;nbsp;as&amp;nbsp;exceptions.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;FailedCount&quot;: 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;WhiteLists&quot;: [&quot;192.168.10.1&quot;, &quot;192.168.10.2&quot;]&lt;br /&gt;}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 위와 같이 '192.168.10.1'과 '192.168.10.2'의 IP를 등록했다면 해당 IP에 대해서만 접속실패가 발생하더라도 차단방화벽의 IP로 등록하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Therefore,&amp;nbsp;if&amp;nbsp;you&amp;nbsp;have&amp;nbsp;registered&amp;nbsp;the&amp;nbsp;IP&amp;nbsp;addresses&amp;nbsp;&amp;lsquo;192.168.10.1&amp;rsquo;&amp;nbsp;and&amp;nbsp;&amp;lsquo;192.168.10.2&amp;rsquo;&amp;nbsp;as&amp;nbsp;described&amp;nbsp;above,&amp;nbsp;even&amp;nbsp;if&amp;nbsp;access&amp;nbsp;failures&amp;nbsp;occur&amp;nbsp;only&amp;nbsp;for&amp;nbsp;those&amp;nbsp;specific&amp;nbsp;IPs,&amp;nbsp;they&amp;nbsp;will&amp;nbsp;not&amp;nbsp;be&amp;nbsp;added&amp;nbsp;to&amp;nbsp;the&amp;nbsp;firewall's&amp;nbsp;blocked&amp;nbsp;list.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 기타&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.&amp;nbsp;Other&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Ban_Server는 Open source project이며 Git을 통해 source를 살펴보고 변경할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Ban_Server&amp;nbsp;is&amp;nbsp;an&amp;nbsp;open-source&amp;nbsp;project,&amp;nbsp;and&amp;nbsp;you&amp;nbsp;can&amp;nbsp;view&amp;nbsp;and&amp;nbsp;modify&amp;nbsp;the&amp;nbsp;source&amp;nbsp;code&amp;nbsp;via&amp;nbsp;Git.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/cliel/Ban_Server&quot;&gt;cliel/Ban_Server: 무차별 대입 공격 방어(Brute Force Attack Defend) (github.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661113307982&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - cliel/Ban_Server: 무차별 대입 공격 방어(Brute Force Attack Defend)&quot; data-og-description=&quot;무차별 대입 공격 방어(Brute Force Attack Defend). Contribute to cliel/Ban_Server development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/cliel/Ban_Server&quot; data-og-url=&quot;https://github.com/cliel/Ban_Server&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kaKeV/hyPv0QLp7d/8bWdsYsBQ4Rq4uHJkgLAFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/cliel/Ban_Server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/cliel/Ban_Server&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kaKeV/hyPv0QLp7d/8bWdsYsBQ4Rq4uHJkgLAFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - cliel/Ban_Server: 무차별 대입 공격 방어(Brute Force Attack Defend)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;무차별 대입 공격 방어(Brute Force Attack Defend). Contribute to cliel/Ban_Server development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;※ 해당 프로그램 사용으로 인한 부작용에 대해서는 책임지지 않습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;※&amp;nbsp;We&amp;nbsp;are&amp;nbsp;not&amp;nbsp;responsible&amp;nbsp;for&amp;nbsp;any&amp;nbsp;adverse&amp;nbsp;effects&amp;nbsp;resulting&amp;nbsp;from&amp;nbsp;the&amp;nbsp;use&amp;nbsp;of&amp;nbsp;this&amp;nbsp;program.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ json에서 FailedCount는 정확성을 보장하지 않습니다. FailCount를 3으로 하면 &quot;3번 실패 시 작동한다.&quot;가 아니라 &quot;3번 이상 실패 시 작동한다&quot;쯤으로 해석해야 합니다. LOG를 읽고 program이 동작하는 시점에 다시 공격이 들어올 수 있는 등 여러 가지 예외 사항이 발생하므로 IP가 Block 완료될 때쯤 실제 공격은 3회 이상으로 이루어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※&amp;nbsp;The&amp;nbsp;FailedCount&amp;nbsp;in&amp;nbsp;JSON&amp;nbsp;does&amp;nbsp;not&amp;nbsp;guarantee&amp;nbsp;accuracy.&amp;nbsp;Setting&amp;nbsp;FailCount&amp;nbsp;to&amp;nbsp;3&amp;nbsp;should&amp;nbsp;be&amp;nbsp;interpreted&amp;nbsp;as&amp;nbsp;&amp;ldquo;activates&amp;nbsp;after&amp;nbsp;3&amp;nbsp;or&amp;nbsp;more&amp;nbsp;failures,&amp;rdquo;&amp;nbsp;not&amp;nbsp;&amp;ldquo;activates&amp;nbsp;after&amp;nbsp;exactly&amp;nbsp;3&amp;nbsp;failures.&amp;rdquo;&amp;nbsp;Various&amp;nbsp;exceptions&amp;nbsp;can&amp;nbsp;occur,&amp;nbsp;such&amp;nbsp;as&amp;nbsp;attacks&amp;nbsp;resuming&amp;nbsp;while&amp;nbsp;the&amp;nbsp;program&amp;nbsp;is&amp;nbsp;processing&amp;nbsp;logs.&amp;nbsp;Therefore,&amp;nbsp;by&amp;nbsp;the&amp;nbsp;time&amp;nbsp;the&amp;nbsp;IP&amp;nbsp;is&amp;nbsp;fully&amp;nbsp;blocked,&amp;nbsp;the&amp;nbsp;actual&amp;nbsp;attack&amp;nbsp;may&amp;nbsp;have&amp;nbsp;occurred&amp;nbsp;more&amp;nbsp;than&amp;nbsp;three&amp;nbsp;times.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;※ Ban_Server는 Windows Server상의 EventLog를 기반으로 동작합니다. Log가 많이 쌓아두면 그 만큼 처리하는데 시간이 오래 걸릴 수 있으며 Log의 관리특성에 따라 원하는 방향으로 프로그램이 동작하지 않을 수 있기 때문에 적절한 관리가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;※&amp;nbsp;Ban_Server&amp;nbsp;operates&amp;nbsp;based&amp;nbsp;on&amp;nbsp;the&amp;nbsp;EventLog&amp;nbsp;on&amp;nbsp;Windows&amp;nbsp;Server.&amp;nbsp;If&amp;nbsp;logs&amp;nbsp;accumulate&amp;nbsp;excessively,&amp;nbsp;processing&amp;nbsp;may&amp;nbsp;take&amp;nbsp;longer,&amp;nbsp;and&amp;nbsp;depending&amp;nbsp;on&amp;nbsp;log&amp;nbsp;management&amp;nbsp;characteristics,&amp;nbsp;the&amp;nbsp;program&amp;nbsp;may&amp;nbsp;not&amp;nbsp;function&amp;nbsp;as&amp;nbsp;intended.&amp;nbsp;Therefore,&amp;nbsp;proper&amp;nbsp;management&amp;nbsp;is&amp;nbsp;required.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Server/Windows Server</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/534</guid>
      <comments>https://cliel.tistory.com/entry/%EB%AC%B4%EC%B0%A8%EB%B3%84-%EB%8C%80%EC%9E%85-%EA%B3%B5%EA%B2%A9-%EB%B0%A9%EC%96%B4-%EB%8F%84%EA%B5%AC-Brute-Force-Attack-Depend-Tool-BanServer#entry534comment</comments>
      <pubDate>Fri, 16 Jan 2026 09:48:42 +0900</pubDate>
    </item>
    <item>
      <title>[C# 14 / .NET 10] C# 14</title>
      <link>https://cliel.tistory.com/entry/C-14-NET-10-C-14</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 C#에 관한 전반적인 관한 내용을 다뤄보고자 합니다. C#에 대한 여러 기초 사항들을 살펴보고 문법을 사용해 필요한 구문을 작성하며 일반적으로 사용되는 여러 어휘들도 같이 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. C#의 표준&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 해에 걸쳐 Microsoft는 C#의 몇 가지 Version을 ECMA 표준 기관에 제출하였고 2014년 C#을 Open Source화 하였습니다. 이와 관련된 표준 문서는 아래 link에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/specification/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/specification/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764578225662&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Specifications - ECMA specification and latest features.&quot; data-og-description=&quot;Read the detailed specifications for the C# language and the latest features. The specifications are the definitive source for the behavior of the C# language.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/specification/&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/specification/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dNBFKw/hyZO48wy7O/0YXBPdrhJD8RryPCUQToJK/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/specification/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/specification/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dNBFKw/hyZO48wy7O/0YXBPdrhJD8RryPCUQToJK/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Specifications - ECMA specification and latest features.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Read the detailed specifications for the C# language and the latest features. The specifications are the definitive source for the behavior of the C# language.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMA표준 이외에&amp;nbsp;GitHub&amp;nbsp;Repository에서도&amp;nbsp;C#을&amp;nbsp;공개적으로&amp;nbsp;만들기&amp;nbsp;위한&amp;nbsp;여러 가지 들을&amp;nbsp;살펴볼&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%;&quot;&gt;C#&amp;nbsp;언어&amp;nbsp;설계&lt;/td&gt;
&lt;td style=&quot;width: 80%;&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/csharplang&quot;&gt;https://github.com/dotnet/csharplang&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%;&quot;&gt;Compiler 구현&lt;/td&gt;
&lt;td style=&quot;width: 80%;&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn&quot;&gt;https://github.com/dotnet/roslyn&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%;&quot;&gt;언어표준&lt;/td&gt;
&lt;td style=&quot;width: 80%;&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/csharpstandard&quot;&gt;https://github.com/dotnet/csharpstandard&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. C# Compiler Version&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Roslyn은 C#과 VB.NET을 위한 Compiler를 말하며 .NET SDK의 일부분으로 배포되었고 F#에는 적용되지 않습니다. 일반적으로는 가장 최신의 Compiler를 사용하게 되지만 만약 다른 Version의 Compiler를 사용하고자 한다면 그에 해당하는 .NET SDK가 반드시 설치되어 있어야 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 123px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;.NET SDK&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;Compiler Version&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;C# 언어 Version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;1.0.4&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;2.0-2.2&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;1.1.4&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;2.3-2.4&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;7.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;2.1.2&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;2.6-2.7&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;7.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;2.1.200&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;2.8-2.10&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;7.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;3.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;3.0-3.4&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;5.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;3.8&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;9.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;6.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;4.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;7.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;4.4&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;11.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;8.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;4.8&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;12.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;9.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;4.12&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;13.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;5.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;14.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Application을 생성할 때 일반적인 .NET Version말고도 .NET Standard를 지정할 수 있는데 이런 경우 각 .NET Standard의 Version에 따라 기본 C#언어 Version도 달라집니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;.NET&amp;nbsp;Standard&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C#&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;2.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;7.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;2.1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상술했듯이 특정 Compiler Version을 사용하려면 해당 Compiler를 포함하는 .NET SDK가 설치되어 있어야 합니다. 다만 이전 .NET Version을 Target으로 하는 Project를 생성한 경우 필요하다면 최신 Version의 Compiler를 사용하는 것은 가능합니다. 예를 들어 .NET 10 SDK를 설치했다면 .NET 8을 대상으로 하는 Project라도 C# 14의 언어기능을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 설치된 SDK Version 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 PC에서 사용가능한 .NET SDK와 C# Compiler Version을 확인하고자 한다면 아래 명령을 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet --version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lUk0M/dJMcadNYc1a/ACKdk3JeLl8Q13oHr0Owr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lUk0M/dJMcadNYc1a/ACKdk3JeLl8Q13oHr0Owr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lUk0M/dJMcadNYc1a/ACKdk3JeLl8Q13oHr0Owr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlUk0M%2FdJMcadNYc1a%2FACKdk3JeLl8Q13oHr0Owr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과로 나온 10.0.100을 보면 아직 초기 .NET SDK Version이며 어떠한 Bug수정이나 기능적인 변화가 없음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 원하는 Compiler Version 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 경우 사용되는 Compiler Version은 현재 설치된 .NET SDK의 Compiler Version이 사용됩니다. 만약 .NET 14 SDK를 설치한 상태에서 이 후 .NET 14.1 SDK를 설치했다면 Compiler 역시 자체 Version으로 교체될 것입니다. 다만 언어 Version의 경우에는 SDK가 정한 기본 Version이 사용될 수 있는데 만약 특정 언어 Version을 사용해야 하는 경우라면 Project file에 아래와 같이 LangVersion요소를 추가해 줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764662275460&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;LangVersion&amp;gt;14.1&amp;lt;/LangVersion&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 LangVersion에서는 상황에 따라 다음과 같은 값을 지정해 줄 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;&amp;lt;LangVersion&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.2093%;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;9,&amp;nbsp;10,&amp;nbsp;11,&amp;nbsp;12,&amp;nbsp;13,&amp;nbsp;14..등등&lt;/td&gt;
&lt;td style=&quot;width: 77.2093%;&quot;&gt;지정한 언어 Version이 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;latestmajor&lt;/td&gt;
&lt;td style=&quot;width: 77.2093%;&quot;&gt;가장 높은 major 번호의 Version이 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;latest&lt;/td&gt;
&lt;td style=&quot;width: 77.2093%;&quot;&gt;가장 높은 major와 minor 번호의 Version이 사용됩니다. 만약 2026년에 14.1이 Release된다면 해당 Version이 사용될 것입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;preview&lt;/td&gt;
&lt;td style=&quot;width: 77.2093%;&quot;&gt;가능한한 가장 높은 version의 preview를 사용합니다. 예를 들어 2026년말 .NET 11 Preview 6를 설치했다면 해당 Version이 사용될 것입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 아마도 2026년 초에 .NET 11의 첫 번째 Preview version을 C# 15 Compiler와 함께 Release 할 수도 있습니다.&amp;nbsp; 이 시기에 .NET 11 SDK Preview를 설치하게 되면 이를 통해 Project를 생성하고 C# 15의 새로운 기능들을 사용해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이런 경우 위에서 설명드린 &amp;lt;LangVersion&amp;gt;을 Project file(.csproj) 아래와 같이 설정하여야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764744558840&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net11.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Preview는 어디까지나 Preview이므로 이를 Product환경에서 사용해서는 안됩니다. Microsoft는 Preview를 개발자를 통해 feedback을 얻기 위한 목적으로 공개할 뿐 공식적인&amp;nbsp;지원을&amp;nbsp;제공하지&amp;nbsp;않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) Compiler 판올림하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK 10을 설치했다면 C# 14 Compiler만을 사용할 수 있지만 다른 Version의 .NET SDK를 추가로 설치한다면 해당 SDK의 C# Compiler 역시 사용할 수 있을 것입니다. 따라서 만약 2026년에 .NET SDK 11이 나오고 이를 설치한다면 C# 14와 15 Compiler를 사용할 수 있는 환경이 갖춰지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Project를 생성하는 것은 물론이고 기존 Project의 Version을 올리는 것 역시 가능한데 예를 들어 .NET 10을 대상으로 한 Project가 있고 이 상태에서 .NET SDK 11을 설치했다면 Project file의 아래와 같은 설정을 통해 해당 C# Version을 올려줄 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764748839726&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;LangVersion&amp;gt;15&amp;lt;/LangVersion&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보면 TargetFramework에서 .NET의 Version을 .NET 10로 유지하고 있고, LangVersion을 통해 C#언어의 Version만 15로 설정해 주는 걸 확인할 수 있습니다. .NET 10은 꾸준히 Update를 지속한다면 2028년 11월까지 지원되므로 C#언어의 Version만 변경하고 SDK자체의 Version은 기존상태를 계속 유지할 수 있습니다. 향후 .NET SDK 11을 사용해 Project를 Build 하고자 하는 경우에도 LangVersion설정은 변경해 줄 필요가 없습니다.(.NET SDK 11을 대상으로 하는 신규 Project의 경우에는 당연히 최신 Version이 기본적으로 사용되므로 따로 별도의 필요하지 않습니다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일부 C# 언어기능은 .NET SDK의 Library에 의존성을 갖고 있을 수 있습니다. 따라서 위 예제에서처럼 TargetFramework의 값을 이전상태로 유지하면서 LangVersion만 최신 C# 언어 Version으로 변경하는 경우 해당 Version의 새로운 언어기능을 사용하지 못하는 경우가 생길 수 있습니다. 예를 들어 C# 11에서 도임된 required keyword는 .NET 7에서만 가능한 새로운 Attribute를 필요로 하기 때문에 하위 SDK Version인 .NET 6에서는 사용할 수 없습니다. 이런 경우 Compiler는 지원하지 않는 기능을 사용하는 것에 대한 경고 알림을 표시할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. C# 문법 익히기&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의 문법과 어휘에 대한 기본을 익혀보기 위해 간단한 예제를 만드는 것으로 시작해 보겠습니다. C#의 문법은 크게 구문과 block으로 나눌 수 있으며 Code에 대한 설명을 위해 주석문을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 구문&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 일상적인 언어에서 문장은 여러 단어와 문구로 이루어지며 마침표로 문장의 끝을 맺습니다. 단어가 나열되는 순서 또한 문장에서 중요하게 작용하는데 한국어와 달리 대부분의 Programming언어는 영어권의 영향을 받으므로 영어와 비슷한 순서가 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 문장의 끝은 semicolon으로 나타내며 단어처럼 여러 Type과 변수, 그리고 표현식으로 이루어집니다. 예를 들어 아래 예제에서 int는 Type이며 i는 변수, '10 + 20'은 표현식에 해당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764832534389&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10 + 20;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기서 표현식은 다시 10과 20이라는 피연산자와 +라는 기호의 연산자로 나누어 볼 수 있습니다. 이때 연산자와 피연산자의 순서는 그 의미와 결과가 달라질 수 있으므로 매우 중요한 부분에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 주석 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석은 Code를 설명하기 위한 가장 주된 방법이자 오래전부터 사용되어 온 고전적인 방법으로서 제삼자가 Code를 읽을 때 Code의 흐름을 이해하기 위한 주요 수단으로 활용됩니다. 심지어 Code를 작성한 본인도 시간이 흐르면 자신의 Code를 잊어버리게 되는데 '주석'은 여기서 기억을 상기시키는데 필요한 History로도 사용될 수 있습니다. 자신의 Code를 잊어버린다는 말은 선뜻 이해하기 어려울 수 있지만 Code를 작성한 개발자 본인아라고 하더라도 시간이 지나면 그때의 의도와 기억을 되찾지 못하는 경우가 대부분입니다. 자신의 기억력을 자신하지 마십시오.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;주석이 Code를 설명하기 위한 주된 방법이기는 하지만 유일한 방법은 아닙니다. 의미가 부여된 변수나 Method명, 단위 Test작성, Project과정에서의 여러 가지 산출물등 Code를 설명하는 데 사용되는 방법은 여러 가지가 있을 수 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;주석중에는 slash 3개로 시작하는 XML주석이라는 것이 존재합니다. 이러한 주석은 Code를 설명하기 위한 다양한 방법으로 활용될 수 있는데 XML주석에 관한 자세한 사항은 추후에 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석중 한 줄 주석은 아래와 같이 slash 2개로 시작하며 Compiler는 //를 만나면 그 줄 전체를 무시하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764832734087&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//덧셈 계산
int i = 10 + 20;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Line에 걸쳐진 주석을 작성하려면 주석의 처음을 '/*'로 시작하고 끝을 '*/'로 끝내야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764832744568&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*
    아래 Code는
    덧셈 계산을 수행합니다.
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔한 경우는 아니지만 위와 같은 주석방식은 Code의 가운데에 주석을 끼워 넣는 경우에도 사용될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764832756062&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = /* 표현식 부분 */ 10 + 20;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Method나 변수의 의미론적인 명명방식과 class의 Encapsulation 등은 그것 자체로 문서화의 역할을 수행할 수 있습니다. 실제 주석을 사용하는 경우에도 너무 많은 주석은 오히려 Code를 이해하는데 방해가 되는 경우도 있으니 적당한 선을 유지하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명하는 목적 이외에 Code자체를 주석화하여 작동하지 않도록 하거나 주석된 Code를 다시 원복 시키는 경우도 있습니다. 이럴 때는 주석문으로 지정할 부분을 Code Editor에서 선택한 뒤 VS2026에서 Edit -&amp;gt; Advanced -&amp;gt; Comment Selection을 선택해 주석화 하거나 Uncomment Selection을 선택해 주석을 취소할 수 있습니다. VSCode라면 Edit -&amp;gt; Toggle Line Comment나 Toggle Block Comment로 동일한 동작을 수행할 수 있습니다. Menu사용할 통한 주석동작은 //을 추가하는 것과 //을 제거하는 것이 전부입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Block&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리말에서 문단은 새로운 행을 기점으로 나타내지만 C#에서는 중괄호({})를 사용한 Code의 Block을 통해 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 C#은 뭔가에 대한 시작점을 정의하는 것으로 사용되는데 예를 들어 구조체, Namespace, Class, Method는 물론 foreach와 같은 구문에서도 사용될 수 있습니다. Namespace나 Class, Method에 대한 자세한 사항은 추후에 자세히 알아볼 테지만 이들에 대해 간략히 설명하자면 다음과 같이 정의할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;관련있는&amp;nbsp;여러&amp;nbsp;Class&amp;nbsp;및&amp;nbsp;Type들을&amp;nbsp;Group화하기&amp;nbsp;위해&amp;nbsp;사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;Method를 포함해 개체의 Member들을 포함한 것으로 Type을 나타내는데 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;개체가&amp;nbsp;가질&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;동작을&amp;nbsp;구현하는데&amp;nbsp;사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026이나 VSCode와 같은 Editor에서는 왼쪽여백에 화살표모양을 Click 함으로써 하나의 Code Block전체를 접거나 펼 수 있는 편리한 기능을 제공하고 있습니다. 화살표가 내려가면 Block이 펼쳐진 상태를 의미하고 오른쪽으로 향하고 있으면 Block이 접혀 있는 상태를 의미하는데 해당 화살표모양은 평소에는 숨겨져 있지만 mouse pointer를 여백에 가져대 대면 자동으로 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pzYwm/dJMcag44xpu/YYKzNwSgnENbwQDdlm1Lt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pzYwm/dJMcag44xpu/YYKzNwSgnENbwQDdlm1Lt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pzYwm/dJMcag44xpu/YYKzNwSgnENbwQDdlm1Lt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpzYwm%2FdJMcag44xpu%2FYYKzNwSgnENbwQDdlm1Lt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1032&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Region&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 Code의 영역을 묶어 이를 지역화할 수도 있습니다. 이때 의미 있는 이름을 붙여줄 수 있는데 상술했던 Code Block의 접고 펴는 기능은 Region에서도 동일하게 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역화는 #Region으로 시작해 #endregion으로 끝나며 Editor는 이 사이에 Code를 지역화로 묶어주게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRdeGu/dJMcafyj1vx/8ZIOzL8FBOQEACXgk1Cxl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRdeGu/dJMcafyj1vx/8ZIOzL8FBOQEACXgk1Cxl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRdeGu/dJMcafyj1vx/8ZIOzL8FBOQEACXgk1Cxl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRdeGu%2FdJMcafyj1vx%2F8ZIOzL8FBOQEACXgk1Cxl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;281&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckPUBF/dJMcahXdEux/8ne5OVke0DaP4bvqBdAfm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckPUBF/dJMcahXdEux/8ne5OVke0DaP4bvqBdAfm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckPUBF/dJMcahXdEux/8ne5OVke0DaP4bvqBdAfm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckPUBF%2FdJMcahXdEux%2F8ne5OVke0DaP4bvqBdAfm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;157&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Block Style&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 Block는 여는 중괄호와 닫는 중괄호를 사용하는 괄호 Style을 사용하며 각각 독립적인 Line에 위치하면서 동일한 들여 쓰기 수준을 갖습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764916196824&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Main(string[] args)
{
    Console.WriteLine(&quot;Hello, World!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 개발자의 Style에 따라 아래와 같이 구문의 정의가 끝나는 지점에 여는 중괄호를 배치하는 약간 다른 Style을 사용하기도 합니다. Method나 Class를 정의하는 경우에는 잘 없지만 하위의 특정 구문을 작성하는 경우 아래의 Style이 많이 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764916225659&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void Main(string[] args)
{
    if (1 == 1) {
        Console.WriteLine(&quot;Hello, World!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 Style을 사용하든 전혀 문제 될 것은 없으므로 선호하는 방식을 선택해 사용하면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Microsoft에서 제시하는 공식적인 Coding Style은 아래 Link에서 찾아볼 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 team단위개발에서 도입하는 특정 Style이 있다면 그걸 따라가는 방식도 필요합니다. 조직의 규칙이 적용되는 경우가 아니라면 어떤 식으로든 일관된 방식을 고수하는 것이 좋습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Microsoft의 공식문서를 살펴보변 대부분 위 규칙 중 전자의 규칙을 사용한다는 것을 알 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 공백을 사용한 Code 서식화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백의 개념은 Space를 포함해 Tab이나 개행문자등을 포함합니다. 그리고 Compiler는 이러한 공백문자를 무시하기 때문에 Code작성 시 서식화에 공백을 자유롭게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 예제에서의 3개 구문은 모두 동일합니다. Code에서 공백을 사용한 Style만 다를 뿐 변수명과 표현식이 모두 동일하기 때문입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765172942575&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int i = 10 + 20;

int i=10+20;

int
i
=
10
+
20
;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 예제는 모두 동일한 변수명인 i를 사용하고 있기 때문에 변수병을 변경하지 않는 이상 같은 Block안에 해당 구문이 모두 존재하는 것은 불가능합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실상 위 예제에서 공백이 들어가야 할 부분은 int라는 type명과 변수명 안 i사이뿐입니다. 만약 이 부분을 아래와 같이 작성한다면&lt;/p&gt;
&lt;pre id=&quot;code_1765172964508&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;inti...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compiler는 어디가 Type이고 어디가 변수명인지를 구분할 수 없기 때문에 오류를 발생시킬 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의&amp;nbsp;공백에&amp;nbsp;대한&amp;nbsp;더&amp;nbsp;자세한&amp;nbsp;사항은&amp;nbsp;아래&amp;nbsp;link를&amp;nbsp;참고하시기&amp;nbsp;바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#634-white-space&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#634-white-space&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765172976297&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Lexical structure - C# language specification&quot; data-og-description=&quot;This chapter explains the lexical grammar, and the syntactic grammar of the C# language.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#634-white-space&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tll3y/hyZOLojolL/SHz38GGkIkWZObHAiQA3kK/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#634-white-space&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#634-white-space&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tll3y/hyZOLojolL/SHz38GGkIkWZObHAiQA3kK/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Lexical structure - C# language specification&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This chapter explains the lexical grammar, and the syntactic grammar of the C# language.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 어휘&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의 어휘는 keyword, 상징문자, type으로 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 keyword라 함은 사전에 특정 용도로 정의된 것을 말하는 것으로 이들 keyword는 변수명이나 Method이름과 같은 용도로는 사용할 수 없습니다. C# Code작성 시 자주 사용되는 keyword로는 using, namespace, class, static, int, string, double, bool, if, switch, break, shile, do , for, foreach, this, true, false 등이 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;this Keyword는 현재 개체의 instance를 참조하거나 개체의 생성자를 호출하고 indexer를 정의하는 등 여러 가지 용도로 사용될 수 있습니다. 더 자세한 사항은 추후에 자세히 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상징문자의 경우 &quot;, ', +, -, *, /, %, @, $등이 있으며 괄호문자도 포함됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호의 경우에는 아래 4가지 문자가 사용되며 각각의 용도가 정해져 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.09302%;&quot;&gt;()&lt;/td&gt;
&lt;td style=&quot;width: 92.907%;&quot;&gt;Method를&amp;nbsp;호출하고&amp;nbsp;표현식이나&amp;nbsp;조건을&amp;nbsp;정의하며&amp;nbsp;type간&amp;nbsp;변환을&amp;nbsp;수행하는데도&amp;nbsp;사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.09302%;&quot;&gt;{}&lt;/td&gt;
&lt;td style=&quot;width: 92.907%;&quot;&gt;Block을&amp;nbsp;정의하거나&amp;nbsp;개체&amp;nbsp;또는&amp;nbsp;Collection의&amp;nbsp;초기화를&amp;nbsp;수행합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.09302%;&quot;&gt;[]&lt;/td&gt;
&lt;td style=&quot;width: 92.907%;&quot;&gt;Array나&amp;nbsp;Collection상에서&amp;nbsp;특정&amp;nbsp;Item에&amp;nbsp;접근하거나&amp;nbsp;Code에&amp;nbsp;Attribute를&amp;nbsp;적용하는데도&amp;nbsp;사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 7.09302%;&quot;&gt;&amp;lt;&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 92.907%;&quot;&gt;Generic에서 Type을 정의하는데 사용되며 XML이나 HTML에서도 사용됩니다. 또한 표현식에서 작다(&amp;lt;), 크다(&amp;gt;)를 나타내는 용도로도 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에 특정한 상황에서만 특별한 의미로 사용되는 like, or, not, record, init와 같은 keyword도 존재합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C# keyword는 모두 소문자입니다. 자신만의 Type을 만드는 경우에도 모두 소문자를 사용할 수 있지만 실제로 그렇게 하는 것은 권장하지 않습니다. 아마다 향후에 새롭게 만들어지게 될 keyword 때문에 그런 것으로 보입니다. 실제로 자체 Type에 소문자를 사용하는 경우 Compiler는 'Warning CS8981'경고 Message를 표시할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keyword는&amp;nbsp;이미&amp;nbsp;예약된&amp;nbsp;것으로&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;없지만&amp;nbsp;꼭&amp;nbsp;그렇게&amp;nbsp;해야&amp;nbsp;한다면&amp;nbsp;keyword앞에&amp;nbsp;'@'문자를&amp;nbsp;접두사로&amp;nbsp;붙여&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765175593191&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int @class = 10;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 위와 같은 방식은 code를 읽는 것에도 혼란을 초래할 수 있기 때문에 여전히 권장되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) Namespace&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 namespace는 서로 관련 있는 class, interface, struct, enum, delegate 등을 group 하여 모은 것으로 이를 통해 동일한 명칭에 대한 충돌을 방지하고 code를 더욱 간결하게 작성할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace는 또한 일종의 type에 대한 주소의 역할을 수행하기도 하는데, 예를 들어 System.Console.WriteLine이라면 Compiler는 WriteLine이라는 method를 System namespace안에 있는 Console type에서 찾게 됩니다. 하지만 WriteLine method를 작성하기 위해 매번 System.Console.WriteLine Code를 작성할 수는 없으므로 .NET 6.0까지는 cs file상단에&lt;/p&gt;
&lt;pre id=&quot;code_1765266898642&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같이 namespace를 선언함으로써 compiler에게 자신의 namespace가 존재하지 않는 type에 대해서는 System namespace를 찾아볼 수 있도록 하였습니다. 따라서 Code작성 시&lt;/p&gt;
&lt;pre id=&quot;code_1765266915862&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(....)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만을 작성해 줄 수 있는 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위에서 처럼 using System을 사용하는 경우 이를 namespace import라고 표현합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 암시적인 전역 namespace import&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 모든 .cs file은 각각 저마다 내부 code에서 필요로 하는 namespace를 import 하기 위한 using구문을 필요로 했습니다. 그런데 System과 System.Linq 같은 어떤 namespace들은 code를 작성하기 위해 필요한 아주 기본적인 namespace들이었고 따라서 project내의 거의 대부분의 cs file이 몇 줄에 걸친 동일한 using문이 작성되어야 했습니다. 심지어 어떤 경우에는 수십 가지의 namespace를 import 하기 위해 그만큼 많은 using문이 작성되는 경우도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 보완하기 위해 C# 10부터는 새로운 keyword를 도입하였고 .NET SDK 6에서는 공통적으로 사용되는 namespace import를 간소화하기 위한 project 설정을 도입하게 되었습니다. 새로운 keyword는 'global using'문으로 해당 keyword를 사용하여 하나의 file에서만 namespace를 import 하면 이 효과가 project 전체 모든 cs file에 적용되기 때문에 각각의 cs file마다 반복적인 using문을 사용할 필요가 없게 되었습니다. 'gloabl using'문을 사용할 수 있는 file에는 제한이 없으므로 극단적으로 Program.cs file에도 사용할 수 있으나 GlobalUsings.cs와 같은 별도의 분리된 file을 생성하여 여기에서 아래와 같이 공통적으로 사용되는 namespace를 import 하는 것이 좋습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765267146004&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;global using System;
global using System.Linq;
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6나 이후를 target으로 하는 모든 project는 C# 10부터의 compiler를 사용하게 됨으로써 기본적으로 obj/Debug/netXX.0 folder안에서 [Project명].GlobalUsings.g.cs file을 생성하게 됩니다. 그리고 이를 통해 System과 같은 일부 namespace의 암시적인 전역 import를 생성하게 됩니다. 이때 대상 namespace는 SDK에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;SDK&lt;/td&gt;
&lt;td style=&quot;width: 75.3488%;&quot;&gt;Implicitly imported namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;Microsoft.NET.Sdk&lt;/td&gt;
&lt;td style=&quot;width: 75.3488%;&quot;&gt;System&lt;br /&gt;System.Collections.Generic&lt;br /&gt;System.IO&lt;br /&gt;System.Linq&lt;br /&gt;System.Net.Http&lt;br /&gt;System.Threading&lt;br /&gt;System.Threading.Tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;Microsoft.NET.Sdk.Web&lt;/td&gt;
&lt;td style=&quot;width: 75.3488%;&quot;&gt;Microsoft.NET.Sdk에&amp;nbsp;추가로&lt;br /&gt;System.Net.Http.Json&lt;br /&gt;Microsoft.AspNetCore.Builder&lt;br /&gt;Microsoft.AspNetCore.Hosting&lt;br /&gt;Microsoft.AspNetCore.Http&lt;br /&gt;Microsoft.AspNetCore.Routing&lt;br /&gt;Microsoft.Extensions.Configuration&lt;br /&gt;Microsoft.Extensions.DependencyInjection&lt;br /&gt;Microsoft.Extensions.Hosting&lt;br /&gt;Microsoft.Extensions.Logging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;Microsoft.NET.Sdk.Worker&lt;/td&gt;
&lt;td style=&quot;width: 75.3488%;&quot;&gt;Microsoft.NET.Sdk에&amp;nbsp;추가로&lt;br /&gt;Microsoft.Extensions.Configuration&lt;br /&gt;Microsoft.Extensions.DependencyInjection&lt;br /&gt;Microsoft.Extensions.Hosting&lt;br /&gt;Microsoft.Extensions.Logging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 작성된 import file을 확인해 보기 위해 이전에 만든 Project를 VS2026으로 열어준 뒤 Solution Explorer에서 'Show All Files'button을 눌러 bin과 object folder가 표시되도록 합니다. 그리고 표시된 obj folder에서 Debug-&amp;gt;net10.0순서로 열어 GlobalUsings.g.cs file을 찾아 해당 file을 열어봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTtcKR/dJMcafZpC7f/oSTCg8GXUvM5vntAks02A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTtcKR/dJMcafZpC7f/oSTCg8GXUvM5vntAks02A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTtcKR/dJMcafZpC7f/oSTCg8GXUvM5vntAks02A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTtcKR%2FdJMcafZpC7f%2FoSTCg8GXUvM5vntAks02A1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1032&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GlobalUsings.g.cs file은 file앞에 Project명이 따라옵니다. 따라서 예제의 경우 'ConsoleApp1.GlobalUsings.g.cs' file이 되는데 설명에서는 'ConsoleApp1.'까지를 생략하였습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GlobalUsings.g.cs file명에서 g는 'generated'를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 file은 .NET 6 이후를 대상으로 한 project를 생성하는 경우 compiler에 의해 자동적으로 생성되는 file로서 System을 포함해 몇 가지 기본적인 namespace의 import를 구현하고 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 상기 namespace에 더해 추가적으로 import 하거나 필요 없는 namespace를 제거하고자 한다면 Solution Explorer에서 Project명을 double click 하여 Project file을 열고 여기에 아래와 같이 조정하고자 하는 namespace를 용도에 맞게 지정해 주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKj1KD/dJMcagcXIAn/xU01p3BdVcIMMxAv0VnW9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKj1KD/dJMcagcXIAn/xU01p3BdVcIMMxAv0VnW9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKj1KD/dJMcagcXIAn/xU01p3BdVcIMMxAv0VnW9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKj1KD%2FdJMcagcXIAn%2FxU01p3BdVcIMMxAv0VnW9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1032&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제의 ItemGroup과 비슷하게 ImportGroup이라는 요소도 존재하지만 정확히 ItemGroup을 사용해야 하므로 ImportGroup과 혼동해서는 안됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 Using Remove는 namespace의 제거를 의미하며, Static은 namespace의 정적 import를, Alias는 별칭을 지정하는 속성입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 처럼 필요한 namespace를 지정한 뒤 file을 저장하고 GlobalUsings.g.cs file을 다시 열어보면 아래와 같이 file의 내용이 변경되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765267698485&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// &amp;lt;auto-generated/&amp;gt;
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading.Tasks;
global using Env = System.Environment;
global using static System.Console;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과에서 처럼 System.Environment는 Env라는 별칭이 지정되었으므로 아래와 같이 Env별칭을 통해 namespace의 type을 바로 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765267711953&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WriteLine(Env.MachineName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WriteLine 역시 상기에서 Sytem.Console을 정적 import 하였으므로 Console을 사용하지 않고 WriteLine method를 호출하는 것이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 생성되는 file을 사용하기보다 직접 전역 import를 수행하는 file을 만들어서 사용하고자 하는 경우 처럼 필요하다면 암시적인 전역 import기능을 사용하지 않도록 설정할 수도 있는데 이렇게 하려면 Project file에서&lt;/p&gt;
&lt;pre id=&quot;code_1765267754323&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부분을 삭제하거나 혹은 아래와 같이 enable대신 disable로 지정해 주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765267764708&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ImplicitUsings&amp;gt;disable&amp;lt;/ImplicitUsings&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) Method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method는 실질적으로 Application의 동작을 구현하는 부분입니다. 예를 들어 예제를 통해 계속 봐왔던 WriteLine도 Console에 Message를 표시하는 동작을 실행하는 것으로서 우리가 목표로 하는 대부분의 기능이 이러한 Method를 통해 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Method는 호출되는 방식에 따라서도 그 동작이 달라질 수 있습니다. 예를 들어 아래와 같은 경우&lt;/p&gt;
&lt;pre id=&quot;code_1765356126246&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine();
Console.WriteLine(&quot;abcdefg&quot;);
Console.WriteLine(&quot;number : {0}&quot;, 100);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 호출은 빈 공백을 표시하고 두 번째는 'abcdefg'라는 message를 표시하며 세 번째는 'number : 100'이라는 message를 표시합니다. 이렇게 Method를 다양하게 호출하는 것을 overloading이라고 하는데 이와 관련해서는 추후에 자세히 다뤄볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 식별자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자라 함은 어떠한 대상의 이름으로서 C#에서는 type, 변수, field, 속성등을 정의할 때 이를 구분하는 특정한 명칭을 부여하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 우리가 'Car'라는 type을 만들면 그 안에 'Engine'이라는 field나 속성을 만들 수 있습니다. Airplan, Train과 같은 것들도 마찬가지로 특정한 개체를 참조하기 위한 식별자로 사용될 수 있을 것입니다. 여기서 중요한 것은 C#자체적으로는 어떠한 type도 정의하지 않는다는 것입니다. 단지 int나 string과 같이 특정 type에 대한 별칭의 keyword만 가지고 있을 뿐이며 이 마저도 현재 platform상에서 제공되는 type을 나타내기 위한 것일 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#이라는 언어는 .NET을 떠나 독립적으로 존재할 수 없는 언어이며 .NET상에서 동작하는 몇 가지 언어 중 하나에 불과합니다. 누군가가 전혀 다른 type을 가진 다른 paltform용으로의 C# compiler를 만들 수도 있겠지만 사실상 C#의 platform은 .NET이라고 할 수 있으며 .NET에서 System.Int32와 같이 수많은 type을 제공하고 있고 C#에서는 이렇게 제공된 System.Int32 type을 참조하기 위해 int라는 별칭을 사용하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type이라 함은 흔히 class로 오해하기 쉬운데 C#에서 말하는 type은 class뿐만 아니라 struct, enum, interface, delegate 등 다양하게 존재할 수 있습니다. 실제로 C#에서 쓰이는 string은 class에 해당하지만 int는 구조체(struct)에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 관련 예제로 Type 살펴보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 Console app을 통해 C#에서 사용되는 type(method를 포함해)이 얼마나 있는지를 알아보도록 하겠습니다. 아래 예제 code는 내부적으로 사용되는 type을 확인하기 위해 reflection이라는 방법을 사용하고 있는데, 시간이 지나면서 모두 이해할 수 있게 될 것이므로 지금은 code자체가 어떻게 동작하는지 정확하게 이해하지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Console App Project를 생성한 뒤 Program.cs의 모든 구문을 삭제하고 reflection을 사용하기 위한 namespace를 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765356307094&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;원한다면 상기 namespace를 global using을 통해 import 할 수도 있겠지만 예제에서는 단 하나의 file에서만 사용할 것이므로 예제와 같이 개별적으로 import 하여 사용할 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아래와 같이 구문을 작성하여 각각의 Assembly가 가진 Method와 type의 수를 확인하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765356340572&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection;

Assembly sampleApp = Assembly.GetEntryAssembly()!;

foreach (AssemblyName assName in sampleApp.GetReferencedAssemblies())
{
    Assembly ass = Assembly.Load(assName);

    int methodCount = 0;

    foreach (TypeInfo t in ass.DefinedTypes)
    {
        methodCount += t.GetMethods().Length;
    }

    Console.WriteLine(&quot;{0:N0} types and {1:N0} methods in {2} assembly.&quot;, ass.DefinedTypes.Count(), methodCount, assName.Name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 code를 작성한 뒤 실행하면 현재 동작중인 OS상에서 Application이 실행될 때 가능한 type과 method의 수를 아래와 같이 표시하게 됩니다. OS에 따라 표시되는 값은 약간씩 다를 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;0 types and 0 methods in System.Runtime assembly.&lt;br /&gt;119&amp;nbsp;types&amp;nbsp;and&amp;nbsp;1,491&amp;nbsp;methods&amp;nbsp;in&amp;nbsp;System.Linq&amp;nbsp;assembly.&lt;br /&gt;44&amp;nbsp;types&amp;nbsp;and&amp;nbsp;656&amp;nbsp;methods&amp;nbsp;in&amp;nbsp;System.Console&amp;nbsp;assembly.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 결과를 보면 System.Runtime은 type과 method가 0인 것을 알 수 있습니다. System.Runtime은 runtime에서 동적으로 type과 method를 결정하는데, 많은 BCL(Base Class Library) assembly들이 reference assembly로만 존재하고 있으며 System.Runtime에서는 metadata만 가지고 있고 실제 구현 type은 다른 곳에 들어 있기 때문입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 가능한 Method와 type이 얼마나 있는지는 가늠할 수 없습니다. Method는 type이 가질 수 있는 member 중 하나에 불과하며 지금도 수많은 개발자가 나름대로의 새로운 type과 member를 정의하고 있기 때문입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 변수&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 application이라도 원하는 결과를 얻기 위해서는 처리할 data를 필요로 합니다. 계산기 application을 만든다고 가정했을 때 산술계산에 필요한 수를 전달해야 하는데 이것이 바로 data인 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data는 대게 사용자가 입력한 것일 수 있고, database나 file과 같은 매체를 통해 전달될 수도 있습니다. 그리고 이렇게 전달된&amp;nbsp; data를 application에서 다루고자 할 때 임시적으로 변수에 담는 처리를 통해 computer의 memory에 저장하게 됩니다. 저장된 data는 나름대로의 처리과정을 거치게 되고 그 결과 data를 생성하게 되는데 일반적으로 Application이 종료되면 관련 memory전체가 소멸되기 때문에 결과 data는 다시 database나 file 등에 저장하거나 또는 사용자에게 직접 표시하여 처리 결과를 확인할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 사용할 때 가장 기본적인 질문은 '다루고자 하는 data에서 얼마나 많은 memory공간을 필요로 하는가?'라는 것입니다. 그리고 우리는 변수의 적절한 type을 지정함으로써 이러한 질문에 응답할 수 있습니다. 여기서 흔히 하는 오해중 하나가 '더 적은 memory type이 더 빨리 수행될 것이다.'라고 추측하는 것입니다. 그러나 항상 그런 것만은 아닙니다. int나 double와 같은 일반적인 type은 각자 차지하는 memory공간이 다르지만 그렇다고 더 빨리 수행되지는 않습니다. 64bit OS에서 16bit 값과 64bit 값은 동일한 조건에 놓여있기 때문에 처리 속도에는 차이가 없습니다. 그리고 일반적인 변수는 서로 근접한 memory공간에 할당될 수 있지만 일부 큰 data의 경우 heap구조를 통해 멀리 떨어진 memory영역에 저장될 수 있습니다. 이러한 memory저장방식은 stack과 heap으로 구분될 수 있는데 이에 대해서는 잠시 후 더 자세히 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 변수의 Naming규칙과 값할당&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 선언할 때는 해당 변수에 대한 이름을 지정해야 합니다. 변수뿐만 아니라 type, method 등 거의 모든 부분에서 개발자의 작명감각이 필요한데, 이때 이름은 직관적이어야 하며 특정한 의미를 연상할 수 있어야 합니다. 또한 다음과 같은 규칙이 존재하므로 가급적이면 해당 규칙을 준수하여 이름을 붙여주는 것이 좋습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;규칙방식&lt;/td&gt;
&lt;td style=&quot;width: 85%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;Camel&amp;nbsp;case&lt;/td&gt;
&lt;td style=&quot;width: 85%;&quot;&gt;소문자로&amp;nbsp;시작해&amp;nbsp;단어&amp;nbsp;사이의&amp;nbsp;구분을&amp;nbsp;대문자로&amp;nbsp;지정합니다.&amp;nbsp;userName,&amp;nbsp;age,&amp;nbsp;addressOfBase와&amp;nbsp;같은&amp;nbsp;형태가&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있으며&amp;nbsp;지역변수와&amp;nbsp;private&amp;nbsp;field에&amp;nbsp;사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;Pascal&amp;nbsp;case&lt;/td&gt;
&lt;td style=&quot;width: 85%;&quot;&gt;Camel&amp;nbsp;case와&amp;nbsp;달리&amp;nbsp;대문자로&amp;nbsp;시작합니다.&amp;nbsp;UserName,&amp;nbsp;Age,&amp;nbsp;AddressOfBase와&amp;nbsp;같은&amp;nbsp;형태가&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있으며&amp;nbsp;type과&amp;nbsp;type의&amp;nbsp;member(method와&amp;nbsp;같은),&amp;nbsp;그리고&amp;nbsp;private이&amp;nbsp;아닌&amp;nbsp;모든&amp;nbsp;field에&amp;nbsp;사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우에는 _age와 같이 private field에 _(underscore) 문자를 접두사로 붙여주는 경우도 있는데 private은 외부에 노출되지 않기 때문에 '_'사용여부에 관해서는 별다른 규칙이 존재하지 않습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Team단위나 조직별로 별도의 naming규칙을 적용할 수도 있습니다. 중요한 건 어떤 규칙이든 일관성 있게 적용해야 한다는 것이며 이를 통해 code를 이해하기에 쉬운 접근성을 제공해 줄 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 지역변수를 선언하고 해당 변수에 값을 할당하고 있습니다. 값의 할당은 '='문자를 사용하며 참고로 변수자체의 이름을 표현할 때는 nameof 연산자를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765523012889&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int userAge = 50;
Console.WriteLine($&quot;변수 nameof(userAge)의 값은 {userAge}입니다.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;nameof 연산자는 C# 6부터 도입된 것이며 C# 12부터는 정적 context의 instance data에도 사용할 수 있습니다. instance와 static에 관해서는 추후에 자세히 알아볼 것입니다. 또한 'nameof(List&amp;lt;&amp;gt;)'와 같이 bound 되지 않은 generic type에도 사용할 수 있게 되었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) Literal&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값을 할당할 때는 대게 고정된 값을 의미하는 literal값을 할당합니다. 이때 data type에 따라 할당 가능한 literal값의 표기방법이 달라질 수 있습니다. 정수라면 10, 20과 같은 값을 할당할 수 있지만 문자열이라면 &quot;abc&quot;나 &quot;가나다라&quot;와 같이 값을 할당해야 합니다. C#언어의 literal에 관한 더 자세한 사항은 아래 link를 참고해 주시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#645-literals&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#645-literals&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765523095626&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Lexical structure - C# language specification&quot; data-og-description=&quot;This chapter explains the lexical grammar, and the syntactic grammar of the C# language.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#645-literals&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cQKyBj/hyZPz1trvx/sKz9ZugCrl5WowUKekKoH1/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#645-literals&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#645-literals&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cQKyBj/hyZPz1trvx/sKz9ZugCrl5WowUKekKoH1/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Lexical structure - C# language specification&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This chapter explains the lexical grammar, and the syntactic grammar of the C# language.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 문자/문자열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 문자를 저장하는 경우라면 char type을 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;대게 하나의 문자를 하나의 char type으로 나타낼 수 있다고 생각하기 쉬운데, 꼭 그런 것만은 아닙니다. 영문자나 숫자의 경우 별 문제가 없고 다른 곳에서의 char에 대한 설명도 편의상 '1개의 문자는 1개의 char를 사용한다.'라고 하지만, 이집트 상형문자의 경우 해당 문자를 표현하기 위해 2개의 char type(이런 것을 surrogate pair라고 합니다.)을 필요로 합니다. 이러한 방식은 일부 아시아국가에서의 문자에서도 마찬가지니 주의하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 한 개의 문자를 literal표 표현하려면 홑따옴표를 사용해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765523191833&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char c = 'a';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 문자가 하나이상 존재하는 '문자열'은 큰따옴표를 사용해 표현하며 string type을 사용해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765523209805&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string userName = &quot;kim sang hyun&quot;;
string doubleA = new ('a', 2); //2개의 a문자 표현
string emoji = char.ConvertFromUtf32(0x1F600);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows Termial에서는 emoji를 표현할 수 있는 기능이 있는데 이 기능을 통해 emoji를 표현하려면 UTF-8로 encoding을 수행한 뒤 char.ConvertFromUtf32 method를 사용해야 합니다. 이러한 방식은 Emoji뿐만 아니라 한글과 같은 일부 아시아국가의 고유문자를 나타내기 위한 방법이기도 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765523250643&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.OutputEncoding = System.Text.Encoding.UTF8;
string emoji = char.ConvertFromUtf32(0x1F603); //Grinning Face with Big Eyes
Console.WriteLine(emoji);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) ESC(Escape) 문자 다루기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;C# 13부터 ESC문자(Unicode U+001B)는 '\e'확장문자열로 나타낼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765790129104&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char esc = '\e';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 13 이전이라면 ESC문자를 \u001b로 표현해야 합니다. 어떤 경우에는 \x1b로 나타내는 경우가 있었으나 1b 뒤에 오는 16진수 문자가 확장문자열(Escape sequence)로 잘못 해석될 수 있기 때문에 사용을 권장하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Verbatim string&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Escape문자는 programming에서 문자열을 처리할 때 확장문자열(Escape sequence)을 도입하기 위해 사용되는 특수한 문자입니다. 확장문자열은 문자열에 직접적으로 삽입이 불가능하거나 그렇게 하기 어려운 문자를 표현할 수 있게 하는 것으로 backslash(\) 문자로 시작하여 처리하고자 하는 문자열값을 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문자열에서 tab문자를 표현하고자 하는 경우 아래와 같이 문자열을 구성하여 변수에 저장할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765790223733&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string tabInName = &quot;Kim\tLee&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약 아래와 같이 file의 경로를 지정하는 경우 특정 folder의 이름이 공교롭게도 t로 시작하게 된다면 어떻게 될까?&lt;/p&gt;
&lt;pre id=&quot;code_1765790273247&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string path = &quot;C:\source\test\mycs.cs&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상태에서 compile을 시도하면 compiler는 '\test'에서 '\t'를 tab문자료 변환하기에 정상적인 경로값이 되지 않을 것입니다. 뿐만 아니라 \s나 \m 역시 유효한 확장문자열이 아닌 것으로 오류를 표현하기 때문에 compile조차 되지 않습니다. 다시 말해 '\'뒤에 모든 문자열은 확장문자열로 우선처리하도록 하고 있으므로 이 가운에 유효하지 않은 확장문자열이 존재한다면 compiler는 오류를 발생시키는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서&amp;nbsp;사용가능한&amp;nbsp;확장문자열은&amp;nbsp;아래&amp;nbsp;link에서&amp;nbsp;확인하실&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#string-escape-sequences&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#string-escape-sequences&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1765790320275&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Strings - C#&quot; data-og-description=&quot;Learn about strings in C# programming. See information on declaring and initializing strings, the immutability of string objects, and string escape sequences.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#string-escape-sequences&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bw4eLx/hyZPyWpqzx/LnWYw6hVKWQ7fqQyRIq3mK/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#string-escape-sequences&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#string-escape-sequences&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bw4eLx/hyZPyWpqzx/LnWYw6hVKWQ7fqQyRIq3mK/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Strings - C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about strings in C# programming. See information on declaring and initializing strings, the immutability of string objects, and string escape sequences.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 아래와 같이 문자열 앞에 '@'문자를 접미사로 붙이면 문자열 안에 모든 '\'문자는 확장문자열의 시작이 아닌 문자 그대로 해석하게 되므로 file이나 folder의 경로를 나타내고자 할 때 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765790336904&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string path = @&quot;C:\source\test\mycs.cs&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 원시 문자열 (Raw string literal)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11부터 사용되어 온 원시문자열 표현방법은 text를 escape 하지 않고도 문자열 그대로 입력할 수 있어서 문자열 안에 XML이나 HTML, JSON과 같은 contnet를 편리하게 포함시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시문자열은 아래와 같이 세 개의 큰따옴표를 문자열의 처음과 끝에 붙여주는 방법으로 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765790369198&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string html = &quot;&quot;&quot;
    &amp;lt;div&amp;gt;
        &amp;lt;span style=&quot;color:red&quot;&amp;gt;Hello!&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &quot;&quot;&quot;;

Console.WriteLine(html);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시문자열표현방법에서 기본 세 개의 큰따옴표가 사용되는 이유는 문자열자체적으로 큰따옴표를 포함하는 경우 때문입니다. 만약 문자열내부에서 3개의 큰따옴표가 포함되어야 한다면 해당 문자열 앞뒤로 4개의 큰따옴표로 감싸야 하며 문자열내부에 4개의 쌍따옴표가 포함된다면 5개의 쌍따옴표로 감싸줘야 합니다. 원시문자열은 이와 같은 방법으로 내부에 포함된 큰따옴표수보다 더 많은 큰따옴표를 통해 문자열을 감싸줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 위 예제를 보면 문자열 끝에 세 개의 쌍따옴표의 들여쓰기가 내부 content와 동일한 수준을 유지하고 있다는 것을 알 수 있습니다. 이와 같은 방법을 통해 compiler는 모든 문자열앞에 들여쓰기 수준을 마지막 세개의 큰따옴표와 동일하게 유지하도록 처리하게 할 수 있습니다. 따라서 위 예제를 실행하면 내부 html의 앞에 들여 쓰기 수준이 모두 제거된 상태로 다음과 같이 문자열을 값을 표현하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbccci/dJMcah31W3C/akKB3boTZSG0B7CbCa6To1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbccci/dJMcah31W3C/akKB3boTZSG0B7CbCa6To1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbccci/dJMcah31W3C/akKB3boTZSG0B7CbCa6To1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbccci%2FdJMcah31W3C%2FakKB3boTZSG0B7CbCa6To1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;286&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래와 같이 문자열 끝에 큰따옴표를 왼쪽에 맞춰 들여 쓰기를 없애면&lt;/p&gt;
&lt;pre id=&quot;code_1765790462882&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string html = &quot;&quot;&quot;
    &amp;lt;div&amp;gt;
        &amp;lt;span style=&quot;color:red&quot;&amp;gt;Hello!&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
&quot;&quot;&quot;;

Console.WriteLine(html);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그만큼 내부 문자열에 들여 쓰기 수준을 그대로 적용하여 표현할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzaG5B/dJMcaihzKmD/5xnSByvKFLcMvFPXRDlZ60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzaG5B/dJMcaihzKmD/5xnSByvKFLcMvFPXRDlZ60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzaG5B/dJMcaihzKmD/5xnSByvKFLcMvFPXRDlZ60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzaG5B%2FdJMcaihzKmD%2F5xnSByvKFLcMvFPXRDlZ60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;324&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 원시 보간 문자열(Raw interpolated string literals)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시 문자열 안에서 보간 문자열을 사용하기 위한 방법은 일반 문자열의 경우와 크게 다르지 않습니다. 다만 문자열 시작지점에 보간 문자열의 표현식으로 나타낼 중괄호({})의 수만큼 '$'문자를 지정하고 문자열 안에서 동일한 수의 중괄호를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문자열안에서 하나의 중괄호를 있는 그대로의 일반적인 문자열로 취급해야 한다면 2개의 중괄호를 사용해 보간 문자열의 표현식으로 사용해야 합니다. 따라서 아래와 같이 $문자를 2개 사용하고 문자열 안에서도 보간 문자열을 위해 중괄호를 2개 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765867004268&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string say = &quot;hello!&quot;;
string html = $$&quot;&quot;&quot;
    &amp;lt;div&amp;gt;
        &amp;lt;span style=&quot;color:red&quot;&amp;gt;{{say}}&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
&quot;&quot;&quot;;

Console.WriteLine(html);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, $문자는 compiler에게 문자열안에서 몇 개의 중괄호가 보간 문자열의 표현식으로 인식되어야 하는가를 알려주는 역할을 하는 것입니다. 이를 바꿔 말하면 중괄호가 사용되지 않는 일반 문자열에서는 $문자를 하나만 붙이고 중괄호 역시 하나만 사용하여 보간 문자열을 나타내도 아무런 문제가 되지 않는다는 것을 의미합니다. 하지만 만약 원시 문자열이 중괄호가 빈번하고 중대한 의미로 사용되는 JSON을 나타내야 하는 경우라면 이와 같은 방법이 매우 중요하고 편리하게 작용할 수 있으므로 꼭 기억하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 숫자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 숫자라 함은 간단히 더하기와 같은 산술계산에 사용되는 값이라 말할 수 있습니다. 그런 의미에서 전화번호는 숫자가 될 수 없습니다. 예를 들어 변수를 사용할 때 숫자로 된 data가 존재하는 경우 이걸 숫자 Type에 넣을지 문자열 Type에 넣을지 고민된다면 그 값이 산술연산에 사용될 수 있는 값인지, 숫자로 구성된 data에서 서식화를 위해 괄호나 hypen과 같은 특수 문자가 사용되었는지를 명확하게 판단하여 결정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 숫자는 31, 32와 같이 셈이 가능한 범자연수(Whole number)가 될 수 있고 또는 -31처럼 음수(Integer)가 될 수 있으며 1.3처럼 실수(단정도 또는 배정도 부동 소수점)가 될 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765867162046&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int num = 31;
uint unum = 31;
float fnum = 1.3f;
double dnum = 1.3;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 uint는 unsigned integer라 하여 음수를 사용하지 않는 대신 int보다 더 넓은 범위의 양수를 표현할 수 있습니다. float은 단정도 부동 소수점으로 F 또는 f를 값의 접미사로 붙여 표현합니다. double은 배정도 부동 소수점으로서 실수에서 기본적으로 표현되는 type이므로 실수에 별도의 접미사를 붙이지 않으면 double로 인식합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Whole number는 범자연수라 하여 음수를 제외한 0부터의 숫자를 의미하며 integer는 자연수로 음수를 포함한 숫자를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 범자연수 (Whole numbers)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Computer는 모든 정보를 0과 1만으로 구성된 bit값으로 저장하며 0과 1, 단 2개의 숫자만 사용하므로 이를 '2진수(2진법)'라고 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 10의 값을 computer가 저장할 때 만들어지는 bit를 표현하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;43&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o3GaM/dJMb99Ze0QA/wc06G6LKFab3GH7xQUomoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o3GaM/dJMb99Ze0QA/wc06G6LKFab3GH7xQUomoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o3GaM/dJMb99Ze0QA/wc06G6LKFab3GH7xQUomoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo3GaM%2FdJMb99Ze0QA%2Fwc06G6LKFab3GH7xQUomoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;43&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;43&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 표에서 8과 2에 1이 표시되어 있음에 주목하시기 바랍니다. 8+2면 10의 값이 성립되는데 결론적으로 2진수 총 8bit로 구성된 00001010은 10진수 10의 값과 같다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 숫자 구분자를 사용한 가독성 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 7부터는 underscore(_) 문자를 사용하여 원하는 만큼의 숫자를 분리해 줄 수 있으며 이러한 방법은 2진수나 16진수와 같은 여러 유형의 값을 표현할 때도 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 분리하는 데 사용되는 '_'문자는 숫자를 표현할 때 해당 숫자의 어디서든 삽입이 가능한데 예를 들어 1백만의 숫자를 표현하는 경우&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;1000000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 표현하는 것보다 천 단위로 분리하여&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;1_000_000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성하면 가독성면에서 훨씬 도움이 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;② 2진수와 16진수 표기법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 2진수 값을 직접적으로 표현하려면 해당 숫자의 앞에 0b를 붙여줄 수 있습니다. 0~9, A~F까지 사용하는 16진수의 경우에는 0x를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1765961117778&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int decimal = 1_000_000; //10진수
int binary = 0b_1111_0100_0010_0100_0000; //2진수
int hexadecimal = 0x_000F_4240; //16진수

Console.WriteLine($&quot;{decimalNotation:N0}&quot;);
Console.WriteLine($&quot;{binaryNotation:N0}&quot;);
Console.WriteLine($&quot;{hexadecimalNotation:N0}&quot;);

Console.WriteLine($&quot;{decimalNotation:X}&quot;);
Console.WriteLine($&quot;{binaryNotation:X}&quot;);
Console.WriteLine($&quot;{hexadecimalNotation:X}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 'N0'은 10진수를, X는 16진수를 나타내므로 실행결과는 다음과 같을 것입니다.\&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bieJWZ/dJMcaaKChnt/3OfNKQ6fGzKPLkVGqu6vKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bieJWZ/dJMcaaKChnt/3OfNKQ6fGzKPLkVGqu6vKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bieJWZ/dJMcaaKChnt/3OfNKQ6fGzKPLkVGqu6vKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbieJWZ%2FdJMcaaKChnt%2F3OfNKQ6fGzKPLkVGqu6vKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;754&quot; height=&quot;267&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 실수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 실수에 사용되는 type 중 float와 double은 각각 단정도 부동 소수점과 배정도 부동 소수점을 사용해 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 Programming언어는 부동 소수점을 다루기 위해 IEEE(Institute of Electrical and Electronics Engineers) 표준을 구현하고 있는데 여기서 IEEE 754가 1985년에 만들어진 부동 소수점을 위한 기술적 표준에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 computer에서 12.75라는 실수값을 표현하기 위한 2진수를 나타내고 있는데, 1이 표시된 부분만 모두 계산하면 8 + 4 + 1/2 + 1/4가 되므로 결과는 12.75가 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wIquV/dJMcagjLBCs/FN6a3Q7iemwOa3fBN92xEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wIquV/dJMcagjLBCs/FN6a3Q7iemwOa3fBN92xEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wIquV/dJMcagjLBCs/FN6a3Q7iemwOa3fBN92xEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwIquV%2FdJMcagjLBCs%2FFN6a3Q7iemwOa3fBN92xEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;807&quot; height=&quot;51&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12.75 값은 이진수 00001100.1100으로 정확하게 표현할 수 있지만 다른 수는 거의 대부분 정확한 표현이 불가능하며 근사치로만 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 예제를 통해 number type의 size 알아보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 sizeof라는 연산자를 통해 type에서 사용하는 memory의 byte값을 확인할 수 있습니다. 또한 대부분의 number type에서는 자체적으로 해당 type의 변수가 가질 수 있는 최솟값과 최댓값을 표현하는 MinValue와 MaxValue를 속성을 가지고 있는데 이들을 조합하여 다음과 같이 type의 수용범위를 알 수 있는 간단한 예제를 만들어 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766048153623&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;int : {sizeof(int)} bytes, range : {int.MinValue:N0} to {int.MaxValue:N0}.&quot;);
Console.WriteLine();
Console.WriteLine($&quot;double : {sizeof(double)} bytes, range {double.MinValue:N0} to {double.MaxValue:N0}.&quot;);
Console.WriteLine();
Console.WriteLine($&quot;decimal : {sizeof(decimal)} bytes, range {decimal.MinValue:N0} to {decimal.MaxValue:N0}.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CKGIl/dJMcafZsOq3/kDZKEPDTALkyPne7XbSSGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CKGIl/dJMcafZsOq3/kDZKEPDTALkyPne7XbSSGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CKGIl/dJMcafZsOq3/kDZKEPDTALkyPne7XbSSGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCKGIl%2FdJMcafZsOq3%2FkDZKEPDTALkyPne7XbSSGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1238&quot; height=&quot;153&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 int는 4byte에 음수부터 양수까지 2십억 정도의 값을 가질 수 있고 double은 8byte에 int보다 더 많은 값을 가질 수 있습니다. Decimal은 16byte를 사용하므로 double보다 더 많은 memory를 사용하지만 표현가능한 값의 범위는 double보다 더 작음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 decimal은 double보다 memory는 더 많이 사용하면서도 값의 범위는 오히려 더 작은 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 대한 답을 알아보기 위해 우선 아래와 같은 에제를 작성해 보았습니다. 예제는 2개의 double변수를 선언하고 이들에 +연산자를 적용한 뒤 그 결괏값을 비교하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766048247376&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double d1 = 0.1;
double d2 = 0.2;

double r = d1 + d2;

if (r == 0.3)
    Console.WriteLine(&quot;result : 0.3&quot;);
else
    Console.WriteLine($&quot;result : {r}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 에제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;199&quot; data-origin-height=&quot;17&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6uMlx/dJMcahQv12o/RzBipnMXdOGajwgb6h5CDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6uMlx/dJMcahQv12o/RzBipnMXdOGajwgb6h5CDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6uMlx/dJMcahQv12o/RzBipnMXdOGajwgb6h5CDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6uMlx%2FdJMcahQv12o%2FRzBipnMXdOGajwgb6h5CDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;199&quot; height=&quot;17&quot; data-origin-width=&quot;199&quot; data-origin-height=&quot;17&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 결과에서 보듯 double은 정확성을 기대할 수 없습니다. 0.1과 0.2, 0.3과 같은 값은 부동 소수점에서는 문자 그대로의 정확한 표현이 불가능하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 예제와 약간 다르게 0.1 + 0.3의 식을 대신 사용해 보면 이번에는 0.4라는 정확한 값이 산출됨을 알 수 있습니다. double type에서 실제로는 수학적으로 같지 않음에도 불구하고 현재 표현식에서 정확히 동일한 값으로 간주되는 일부 부정확한 값이 존재할 수 있습니다. 결론은 너무나 모호하지만 일부는 정확한 비교가 가능하나 일부는 그렇지 않다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 float type으로 실수를 다루는 경우에는 위 예제와 같은 비교에서 더 정확한 결과를 낼 수 있습니다. 그 이유는 double type보다 정확도가 더 떨어지기 때문입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766048347372&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float f1 = 0.1f;
float f2 = 0.2f;

float r = f1 + f2;

if (r == 0.3f)
    Console.WriteLine(&quot;result : 0.3&quot;);
else
    Console.WriteLine($&quot;result : {r}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 값자체의 비교보다는 표현의 정밀도가 더 중요하다면 double을 사용하고 값의 비교가 더 중요한 경우에는 float을 사용해야 합니다. 중요한 건 double에서 절대로 '=='비교연산자를 통해 값을 비교해서는 안된다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Double을 사용한 위 에제에서는 computer가 실수값을 어떻게 저장하는지를 보면 왜 예상한 것과 다른 결과를 내는지 알 수 있습니다. 2진수에서 0.1이라는 값을 표현하기 위해 double에서는 아래표에서와 같이 1/16, 1/32, 1/256, 1/512... 등등에 계속해서 1이라는 값을 저장해야 합니다. 따라서 결국 0.1 값은 2진법으로 0.00011001100110011... 이 되는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;44&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m45lN/dJMcacaBmqC/GPsRSwSJ5pZzjcNkB7AB91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m45lN/dJMcacaBmqC/GPsRSwSJ5pZzjcNkB7AB91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m45lN/dJMcacaBmqC/GPsRSwSJ5pZzjcNkB7AB91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm45lN%2FdJMcacaBmqC%2FGPsRSwSJ5pZzjcNkB7AB91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;44&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;44&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 double예제에서 data type을 decimal로 변경한 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766048424124&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;decimal d1 = 0.1M;
decimal d2 = 0.2M;

decimal r = d1 + d2;

if (r == 0.3M)
    Console.WriteLine(&quot;result : 0.3&quot;);
else
    Console.WriteLine($&quot;result : {r}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행해 보면 이전과 달리 r변수의 값이 0.3을 가지게 된다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decimal의 실수저장방식은 의외로 단순한데, 모든 수를 정수형태로 저장한 뒤 소수 부분의 숫자만 이동시키는 방식을 따르고 있습니다. 예를 들어 0.1의 값은 1로 저장되며 소수점 1자리만큼 이동시켜 0.1을 표현합니다. 12.75도 마찬가지인데 12.75를 1275로 저장한 다음 소수점 자리만큼 뒤의 75를 이동시켜 12.75로 표현합니다. 이러한 저장 방식은 실수 전체를 큰 범위의 정수형태로 저장하게 되므로 많은 정수 부분의 공간을 필요로 하게 됩니다. 따라서 double보다 더 많은 memory공간을 필요로 하게 되는 것입니다. 하지만 이러한 방식은 double에서 거의 무한적으로 반복되는 정밀도를 따라가기에는 한계가 있기 때문에 당연히 소수의 정밀도 면에서는 double을 따라갈 수 없습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일반적인 자연수는 int를 사용합니다. 다른 값과의 정확한 비교가 필요 없으면 double을 사용합니다. double은 정확힌 비교가 아닌 비교대상보다 더 큰가, 작은 가를 판단하기에 좋지만 값의 정확한 비교에는 적절하지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;② 기타 실수 관련 상수와 method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;float과 double은 특별한 경우에 사용가능한 몇 가지 유용한 상수를 갖고 있습니다. 대표적으로 NaN은 not a number로서 0에서 0으로 나눈 결과를 표현하는 것이며 Epsilon은 float과 double에서 저장가능한 가장 작은 양수값을 나타냅니다. PositiveInfinity와 NegativeInfinity는 무한대의 양의실수와 음수값을 표현하며 IsInfinity와 IsNaN과 같이 이들 값을 확인할 수 있는 method도 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 이와 같은 상수를 활용한 것으로 해당 상수값이 어떻게 표현되는지 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766130174357&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;double.Epsilon: {double.Epsilon}&quot;);
Console.WriteLine($&quot;double.Epsilon(decimal places-324): {double.Epsilon:N324}&quot;);
Console.WriteLine($&quot;double.Epsilon(decimal places-330): {double.Epsilon:N330}&quot;);
Console.WriteLine();
Console.WriteLine($&quot;double.NaN: {double.NaN}&quot;);
Console.WriteLine();
Console.WriteLine($&quot;double.PositiveInfinity: {double.PositiveInfinity}&quot;);
Console.WriteLine($&quot;double.PositiveInfinity: {double.NegativeInfinity}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mFYez/dJMcaiV9yt3/Lo802PfYNl49kIU5qw3J61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mFYez/dJMcaiV9yt3/Lo802PfYNl49kIU5qw3J61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mFYez/dJMcaiV9yt3/Lo802PfYNl49kIU5qw3J61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmFYez%2FdJMcaiV9yt3%2FLo802PfYNl49kIU5qw3J61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1051&quot; height=&quot;438&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PositiveInfinity와 NegativeInfinity는 옆으로 누 운모양의 8을 표시하는데 각각 양의 실수와 음의 실수를 0으로 나눈 표현식에서 생성될 수 있습니다. Epsilon은 5E-324보다 약간 작은 값으로 아래 link에서의 표시법으로 표현됩니다. https://en.wikipedia.org/wiki/Scientific_notation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 신규 number type과 unsafe code&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Half type은 .NET 5에서 도입된 것으로 float이나 double처럼 실수를 저장하지만 32bit의 float이나 64bit의 double과 다르게 2byte의 memory만을 사용함으로써 그만큼 memory를 절약하고 cache의 효율성을 증대시킬 수 있습니다. 다만 값의 범위는 더 작아지므로 대규모 data를 처리하는 데는 주의해야 합니다. System.Int128과 System.UInt128은 .NET 7에서 도입되었는데 int와 uint같이 음수와 양수모두를 저장하거나 0을 포함한 양수만을 저장합니다. 차이점은 더 많은 memory를 사용하기에 더 큰 정수 범위를 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 위의 type의 표현범위를 알아보기 위한 것으로 내부에서 sizeof 연산자를 사용하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766130306040&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;unsafe
{
    Console.WriteLine($&quot;Half : {sizeof(Half)} bytes, range {Half.MinValue:N0} ~ {Half.MaxValue:N0}&quot;);
    Console.WriteLine($&quot;Int128 : {sizeof(Int128)} bytes, range {Int128.MinValue:N0} ~ {Int128.MaxValue:N0}.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sizeof 연산자를 위와 같이 사용함으로 인해 code를 unsafe를 통해 구현해하며 이를 build 하려면 Project의 설정에 아래와 같이 AllowUnsafeBlocks를 추가해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766130322288&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;AllowUnsafeBlocks&amp;gt;True&amp;lt;/AllowUnsafeBlocks&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nqWyG/dJMb9951XZE/H3u5e8FrIF3mf87GsrSk91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nqWyG/dJMb9951XZE/H3u5e8FrIF3mf87GsrSk91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nqWyG/dJMb9951XZE/H3u5e8FrIF3mf87GsrSk91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnqWyG%2FdJMb9951XZE%2FH3u5e8FrIF3mf87GsrSk91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;970&quot; height=&quot;305&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sizeof 연산자는 int나 byte와 같이 일반적으로 사용되는 type을 제외하곤 unsafe code block을 사용해야 합니다. sizeof 연산자에 관한 더 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766130352052&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;sizeof operator - determine the storage needs for a type - C# reference&quot; data-og-description=&quot;Learn about the C# &amp;#96;sizeof&amp;#96; operator that returns the memory amount occupied by a variable of a given type.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wYfY2/hyZPTZ2s5k/SSEfr7Wq5Kur1kPh74PrJ0/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wYfY2/hyZPTZ2s5k/SSEfr7Wq5Kur1kPh74PrJ0/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;sizeof operator - determine the storage needs for a type - C# reference&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the C# `sizeof` operator that returns the memory amount occupied by a variable of a given type.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 unsafe code block을 사용하면 그만큼 안정성을 검증할 수 없습니다. 이에 관한 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766130364975&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Unsafe code, pointers to data, and function pointers - C# reference&quot; data-og-description=&quot;Learn about unsafe code, pointers, and function pointers. C# requires you to declare an unsafe context to use these features to directly manipulate memory or function pointers (unmanaged delegates).&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dxf6SC/hyZPpzsQaM/vxcKiaAktNxYZkz5kXICLk/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dxf6SC/hyZPpzsQaM/vxcKiaAktNxYZkz5kXICLk/img.png?width=72&amp;amp;height=72&amp;amp;face=0_0_72_72');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Unsafe code, pointers to data, and function pointers - C# reference&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about unsafe code, pointers, and function pointers. C# requires you to declare an unsafe context to use these features to directly manipulate memory or function pointers (unmanaged delegates).&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) bool&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bool은 true와 false값 중 하나만을 가질 수 있는 type으로서 아래와 같이 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766387202637&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool b1 = true;
bool b2 = false;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bool type자체는 조건을 판단하는데 가장 많이 사용되는 type으로서 분기문이나 loop문에 응용됩니다. 분기문 및 loop문에 관한 자세한 사항은 추후에 자세히 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) object&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서 object type은 모든 type을 저장할 수 있는 아주 특별한 type입니다. 하지만 자칫 이해하기 어려운 code를 만들 수 있고 특히 application의 성능을 떨어뜨리는 주요 원인이 될 수 있으므로 가급적 사용을 피해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 object에 값을 저장하거나 가져올 때 어떤 형태로 사용될 수 있는지를 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766387250184&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;object o1 = 100;
object o2 = &quot;abcdefg&quot;;

Console.WriteLine($&quot;{o1} : {o2}&quot;);

int i = (int)o1;
string s = (string)o2;

Console.WriteLine($&quot;{i} : {s}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제와 같이 object에서 data를 가져올 때는 본래 해당 data가 어떤 type인지 알 수 없기 때문에 적절한 type으로의 형변환이 필요합니다. 따라서 아래와 같이 code를 작성하면 compile error가 발생할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766387270851&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;object s1 = &quot;abcdefg&quot;;
string s2 = s1.Length; //error : s1을 문자열로 취급하려면 형변환이 필요하다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형변환 관한 자세한 사항은 추후에 살펴볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의 초기 version부터 사용되어 온 object는 형변환이 필수적으로 따라와야 하기 때문에 여기에서 발생하는 성능상 부하가 경우에 따라서는 아주 크게 작용할 수 있습니다. C#에서는 이러한 문제를 해결하기 위해 generic을 도입하게 되었는데 generic은 object와는 다르게 충분한 유연성을 제공하면서도 성능저하에 관한 문제를 피할 수 있습니다. Generic에 관한 자세한 사항은 추후에 자세히 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) dynamic&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 4에서 도입된 dynamic은 object와 같이 모든 type의 data를 담을 수 있는 type이며 또한 비슷하게 성능저하를 일으키는 원인이 될 수 있습니다. object와 비슷하지만 한 가지 다른 차이점이라면 저장된 값의 type을 명시적으로 형변환하지 않고도 그대로 사용할 수 있다는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766387395730&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dynamic s = &quot;abcdefg&quot;;

int sLen = s.Length;

Console.WriteLine(sLen);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 어떠한 compile 오류도 발생시키지 않고 정상적으로 7이라는 결괏값을 표시할 것입니다. 할당된 값자체가 문자열값이며 해당 type은 Length라는 속성을 가지고 있기 때문에 아무런 문제가 되지 않습니다. 여기서 중요한 건 dynamic은 항상 마지막에 할당된 값의 type을 유지한다는 것입니다. 따라서&lt;/p&gt;
&lt;pre id=&quot;code_1766387463811&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dynamic s = &quot;abcdefg&quot;;
s = 100;

int sLen = s.Length;

Console.WriteLine(sLen);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 예제는 compile 오류를 발생시킵니다. 마지막으로 할당된 값은 100이며 int type에 해당하므로 Length라는 속성을 가지지 않기 때문입니다. dynamic의 또 한 가지 특징은 code를 작성할 때 IntelliSense의 도움을 받을 수 없다는 것도 있습니다. 왜냐하면 dynamic은 code를 작성하는 중에는 type을 확인하지 않기 때문이며 type의 member 역시 runtime시에 CLR(Common Language Runtime)이 확인하는 과정을 거치게 됩니다. 따라서 만약 runtime에서 해당 member가 확인되지 않으면 예외를 발생시키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외에 관해서는 추후에 자세히 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic type은 주로 비 .NET system과의 상호작용을 위해 사용되는 경우가 많습니다. 대표적으로 F#이나 Python, JavaScript를 예로 들 수 있으며 Excel이나 Word와 같이 COM(Component Object Model)과의 상호작용을 위해서도 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Dynamic과 ExpandoObject&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ExpandoObject는 System.Dynamic namespace안에 존재하는 것으로 runtime에서 임의의 속성을 추가하거나 삭제할 수 있는 dynamic개체이며 추가되는 속성을 위해 내부적으로 dictonary를 사용하여 key와 값을 저장하고 관리합니다. 뿐만 아니라 단순 속성을 벗어나 Action과 같은 delegate를 할당하여 method의 추가를 구현할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ExpandoObject는 주로 고정된 형태의 class가 아닌 동적인 동작을 필요로 하는 경우에 유용하게 사용할 수 있습니다. 다만 성능이 중요시되거나 비교적 안전한 code가 필요한 환경이라면 ExpandoObject의 사용을 피하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 ExpandoObject를 어떠한 형태로 사용할 수 있는지를 확인해 볼 수 있는 간단한 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766479832698&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Dynamic;

dynamic car = new ExpandoObject();

car.MaxSpeed = 200;
car.MaxPassengers = 4;
car.CarType = &quot;Sedan&quot;;
Console.WriteLine($&quot;{car.CarType} Maximun Speed : {car.MaxSpeed}&quot;);

Console.WriteLine(&quot;--------------------------------------------&quot;);

var dic = (IDictionary&amp;lt;string, object&amp;gt;)car;
foreach(var item in dic)
{
    Console.WriteLine($&quot;{item.Key} : {item.Value}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0leV/dJMcaiWa1cB/rp4kcMXWZ9RKiIbuOsip10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0leV/dJMcaiWa1cB/rp4kcMXWZ9RKiIbuOsip10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0leV/dJMcaiWa1cB/rp4kcMXWZ9RKiIbuOsip10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0leV%2FdJMcaiWa1cB%2Frp4kcMXWZ9RKiIbuOsip10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;317&quot; height=&quot;88&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 사용된 data type cast와 dictonary에 관해서는 추후에 자세히 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7) 변수의 선언&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역변수라 함은 method안에서 선언되는 것들을 의미하며 오로지 method가 실행되는 동안에만 유효합니다. 그런데 Method의 실행이 종료되는 순간 memory에 할당된 지역변수는 값 type인 경우 소멸하지만 참조 type인 경우 garbage collection을 대기하게 됩니다. 변수의 값 type과 참조 type의 차이에 대해서는 이제 곧 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 지역변수의 type 지정하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 예제를 통해 꾸준히 봐온 바와 같이 변수는 type과 변수명의 방식으로 아래와 같이 선언됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766565290569&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int age = 20;
double weight = 60.1;
decimal price = 5.99M;
string fruit = &quot;apple&quot;;
char alpabet = 'A';
bool isUse = false;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 변수를 선언만 하고 해당 변수를 사용하는 구문을 따로 작성하지 않는다면 사용하고 있는 editor와 색상 설정에 따라 물결모양 밑줄과 함께 '변수에 값이 할당되었지만 사용되지 않는다.'는 경고를 표시할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 변수의 type 추론&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 3 이후부터는 변수를 선언할 때 var keyword를 사용할 수 있습니다. Compiler가 var가 사용된 변수를 만나면 할당된 값을 통해 type을 추론하게 되는데 이러한 동작은 compile 할 때 실행되므로 runtime에 미치는 성능적인 영향은 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type추론에도 일정한 규칙이 존재하는데 var type변수에 숫자값을 할당할 때는 아래와 같이 값뒤에 붙여주는 접미사를 통해 적절한 type을 추론하게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;접미사&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;추론되는 type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;L&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;long&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;UL&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;ulong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;M&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;decimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;D&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;double&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;F&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;float&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소수점이 없는 정수값을 할당하는 경우에는 int로 추론되며 어떠한 접미사도 없으면서 실수면 double로 추론됩니다. 숫자가 아닌 문자열의 경우 큰따옴표가 사용되면 string으로, 홑따옴표가 사용되면 char type으로 추론되며 true나 false가 사용되면 bool로 추론됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 상기 변수선언의 예제를 var를 사용해 변경하면 아래와 같이 할 수 있으며 각 규칙에 따라 type이 추론됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766565656905&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var age = 20;
var weight = 60.1;
var price = 5.99M;
var fruit = &quot;apple&quot;;
var alpabet = 'A';
var isUse = false;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 code editor에 따라 var keyword에 mouse pointer를 올려두면 추론된 type을 표시하기도 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tBs4P/dJMcahbWJr7/V9qddUK9o4nXSnJUiqQ6W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tBs4P/dJMcahbWJr7/V9qddUK9o4nXSnJUiqQ6W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tBs4P/dJMcahbWJr7/V9qddUK9o4nXSnJUiqQ6W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtBs4P%2FdJMcahbWJr7%2FV9qddUK9o4nXSnJUiqQ6W1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;113&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type추론은 위와 같은 일반적인 변수에만 적용되는 것은 아닙니다. 복합 type에도 적용될 수 있는데, 예를 들어 C# 3 이전에는&lt;/p&gt;
&lt;pre id=&quot;code_1766565715015&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;XmlDocument xml = new XmlDocument();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 해야 했지만 var type을 통해 아래와 같이 XmlDocument type에 대한 변수를 선언해 줄 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1766565729581&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var xml = new XmlDocument();&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;var keyword는 편리하기는 하지만 type을 즉각적으로 파악할 수 없다는 점 때문에 일부 개발자은 var사용을 선호하지 않는 경우도 있습니다. var를 무조건 사용하거나 또는 사용하지 않기로 마음먹기보다는 type이 명확한 경우에는 var를 사용하고 그렇지 않은 경우 메는 해당 type을 명시적으로 지정하여 변수를 선언하는 것도 괜찮은 방법이 될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간혹 var를 dynamic과 혼동하는 경우가 있는데 dynamic은 dynamic자체를 compiler가 변경하는 경우가 없으며 runtime에서 type이 확인된다는 점에서 var와는 동작방식이 다릅니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio에서는 var를 실제 type으로 자동적으로 바꿔주는 refactoring기능을 사용할 수 있습니다. 자세한 사항은 아래 link를 통해 확인할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=visualstudio&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=visualstudio&lt;/a&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1766565832920&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Refactor code to replace var with an explicit type - Visual Studio (Windows)&quot; data-og-description=&quot;Learn how to use Quick Actions to replace var in a local variable expression with an explicit type.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=visualstudio&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=visualstudio&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eYKlW/hyZPDp0JB2/Poc2s6SfxRl2QluN6Kdoz1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=visualstudio&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/convert-var-to-explicit-type?view=visualstudio&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eYKlW/hyZPDp0JB2/Poc2s6SfxRl2QluN6Kdoz1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Refactor code to replace var with an explicit type - Visual Studio (Windows)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to use Quick Actions to replace var in a local variable expression with an explicit type.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) new keyword&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 C# code에 대한 몇 가지 예제에서는 명확한 설명 없이 new keyword를 계속 사용해 왔습니다. new keyword는 기본적으로 memory에 대한 초기화 및 할당에 사용되는 것으로 변수의 참조 type에서 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 type에서는 memory할당을 위한 new keyword가 사용되지 않지만 literal을 사용해 값을 초기화하기 위한 방법이 마땅치 않은 경우 사용될 수도 있습니다. 그러나 대부분 new keyword를 굳이 사용해야 하는 경우는 없으며 사용한다 하더라도 참조 type에서의 new와 동일한 의미를 가지지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 type은 변수를 선언할 때 값을 저장하기 위한 stack memory만을 할당합니다. 이러한 방식은 int나 char처럼 변수가 가져야 할 정해진 memory의 크기가 이미 지정되어 있으므로 가능한 방식입니다. 그러나 참조 type은 해당 변수를 위한 memory공간을 어디서부터 어디까지 할당해야 할지 미리 예견할 수 없으므로 heap이라는 특별한 memory공간을 사용해야 하며 stack memory는 실제 data가 저장된 heap의 memory주소를 기억하기 위한 용도로만 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 type의 경우에도 변수를 선언하는 경우 new를 사용하지 않으면&lt;/p&gt;
&lt;pre id=&quot;code_1767078967192&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Car car;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heap에 존재하는 Car개체의 memory주소를 저장하기 위한 stack memory만을 확보(memory주소만을 가질 것이므로 32bit의 경우 4byte를 64bit system의 경우 8byte를 확보)하게 되며 아직 개체를 위한 heap memory까지 할당된 상태에 있지는 않습니다. 따라서 아직 값이 지정되지 않았으므로 이때 변수 car는 null상태가 됩니다. int와 같은 값 type에서 값이 할당되지 않은 경우 기본값 0을 가지는 것과는 다른 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767079065614&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Car car = new Car();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new keyword를 사용하면 이 순간 car개체를 저장하기 위한 heap memory를 할당합니다. 이때부터 car는 더 이상 null이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 대상형식화된 new(target-typed new)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 9에서는 개체를 초기화하기 위한 새로운 구문으로 target-typed new를 도입하였습니다. 개체를 초기화할 때 변수의 type을 지정하고 나면 new를 사용할 때 이를 반복할 필요 없이 new keyword만을 사용하여 아래와 같이 개체를 초기화하는 방식입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767079193771&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;XmlDocument xml = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 type이 특정 type의 field나 속성인 경우 이에 값을 할당하는 경우에도 type은 추론될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767079215257&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Car car = new();
car.ShipDate = new(2025, 12, 26);

class Car
{
    public DateTime ShipDate;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서는 target-typed new를 사용하도록 기존 code의 refactoring기능을 가지고 있습니다. 자세한 사항은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/use-new?view=visualstudio&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/visualstudio/ide/reference/use-new?view=visualstudio&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767079229626&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Use new() - Visual Studio (Windows)&quot; data-og-description=&quot;Learn how to use &amp;#96;new()&amp;#96; when you can't use &amp;#96;var&amp;#96;.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/use-new?view=visualstudio&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/use-new?view=visualstudio&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nAATL/hyZQ9VMVHG/aJxBRP4lwj98LauKpG8cG0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/Laz7f/hyZQ6dG2V2/E24MDGF1d3qWUGZV3vOPTK/img.png?width=1417&amp;amp;height=550&amp;amp;face=0_0_1417_550&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/use-new?view=visualstudio&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/reference/use-new?view=visualstudio&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nAATL/hyZQ9VMVHG/aJxBRP4lwj98LauKpG8cG0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/Laz7f/hyZQ6dG2V2/E24MDGF1d3qWUGZV3vOPTK/img.png?width=1417&amp;amp;height=550&amp;amp;face=0_0_1417_550');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Use new() - Visual Studio (Windows)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to use `new()` when you can't use `var`.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 값 Type의 기본값&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string을 제외하고 대부분의 원시 type은 값 type에 해당하며 값 type은 null이 될 수 없으므로 반드시 값을 가지게 됩니다. 변수에 기본값을 확인하기 위해서는 default() 연산자를 사용하여 type을 매개변수로 전달할 수 있고 반대로 기본값을 할당하는 경우에는 default keyword를 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;? 접미사를 사용하면 값 type인 경우에도 null을 가질 수 있는 상태로 만들 수 있습니다. 이에 관해서는 추후에 자세히 알아볼 테이지만 지금은 기본적으로 값 type은 null을 가지지 않은 것으로 이해해야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;string type은 new를 사용하지 않아 값 type으로 오인할 수 있지만 엄밀히 말하면 참조 type에 해당합니다. 그러므로 string의 변수는 실제 값을 가지고 있는 것이 아니라 값을 가진 heap memory의 주소값을 가지게 됩니다. string은 참조 type이므로 당연히 null상태가 될 수 있는데 null은 아직 어떤 것도 참조하고 있지 않은 상태임을 나타내며 모든 참조 type의 기본값이기도 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 int와 bool, DateTime과 같은 값 type의 기본값과 string의 기본값을 확인하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767168014659&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;int default : {default(int)}&quot;);
Console.WriteLine($&quot;bool default : {default(bool)}&quot;);
Console.WriteLine($&quot;DateTime default : {default(DateTime)}&quot;);
Console.WriteLine($&quot;string default : {default(string) ?? &quot;&amp;lt;NULL&amp;gt;&quot;}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 사용된?? 연산자는 피연산자가 null인 경우 오른쪽 값을 대신 반환하도록 하는 연산자입니다. 따라서 참조형인 string은 기본이 null상태이므로 'null'이라는 문자열을 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vWAst/dJMcah3750y/LHbbV61MNLwqC4pLfxVYx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vWAst/dJMcah3750y/LHbbV61MNLwqC4pLfxVYx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vWAst/dJMcah3750y/LHbbV61MNLwqC4pLfxVYx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvWAst%2FdJMcah3750y%2FLHbbV61MNLwqC4pLfxVYx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;311&quot; height=&quot;67&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 시간에 대한 기본값은 현재 예제를 실행하는 PC의 문화권에 따라 다른 서식으로 표현될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 값 type에 기본값을 할당하는 방법을 나타내고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767168075713&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int number = 10;
Console.WriteLine($&quot;current value 1 : {number}&quot;);
number = default;
Console.WriteLine($&quot;current value 2 : {number}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 type에 아무런 값도 지정하지 않으면 기본적으로 기본값이 할당되지만 이를 명시적으로 나타내고자 하는 경우 예제에서와 같이 default keyword를 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;149&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDRpa/dJMcafyttq8/OjKQ0JeuTSiMjWaBeDzx90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDRpa/dJMcafyttq8/OjKQ0JeuTSiMjWaBeDzx90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDRpa/dJMcafyttq8/OjKQ0JeuTSiMjWaBeDzx90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDRpa%2FdJMcafyttq8%2FOjKQ0JeuTSiMjWaBeDzx90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;149&quot; height=&quot;33&quot; data-origin-width=&quot;149&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Console App&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 Console App이라 함은 문자기반의 command prompt에서 동작하는 Application을 말합니다. 문자열로 명령을 입력하며 문자열기반의 응답을 받기 때문에 macOS나 Windows의 UI(User Interface)따위는 존재하지 않습니다. 따라서 비교적 단순하며 빠른 응답을 필요로 하는 경우에 종종 사용되는 Application유형이라 할 수 있습니다. 또한 대부분의 경우 이러한 Application의 동작을 제어하기 위해 인수를 전달하는 방법이 많이 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Console App의 한 가지 예로 Command prompt에서 dotnet 명령을 사용하는 경우를 들 수 있는데 새로운 Console App project를 생성하는 경우 기본적으로 folder의 이름을 사용하게 되지만 특정 project의 이름을 지정하고자 한다면 아래와 같이 dotnet명령을 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new console -n myapp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 화면 출력&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Console App에서 가장 빈번하게 수행되는 2가지 작업을 꼽으라면 data를 읽고 쓰는 것을 말할 수 있습니다. 우리는 이미 여러 예제에서 화면 출력을 위해 WriteLine method가 사용되는 것을 봐왔습니다. WriteLine method는 인수로 전달된 내용을 화면에 표시한 뒤 자동으로 carriage return을 수행하는데 만약 이러한 동작이 이루어지는 걸 원하지 않는다면 Write method를 대신 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767600194625&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.Write(&quot;A&quot;);
Console.Write(&quot;B&quot;);
Console.Write(&quot;C&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;30&quot; data-origin-height=&quot;15&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUsWFf/dJMcagjSy6i/oxqPpU2wXx7anK5ybUOEE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUsWFf/dJMcagjSy6i/oxqPpU2wXx7anK5ybUOEE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUsWFf/dJMcagjSy6i/oxqPpU2wXx7anK5ybUOEE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUsWFf%2FdJMcagjSy6i%2FoxqPpU2wXx7anK5ybUOEE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;30&quot; height=&quot;15&quot; data-origin-width=&quot;30&quot; data-origin-height=&quot;15&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Write method는 자동개행(carriage return)을 수행하지 않으므로 인수의 문자열이 나란히 표시됨을 알 수 있습니다. 반면 WriteLine method를 대신 사용한다면 carriage return동작으로 인해 위와 다른 결과를 표시할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767600221359&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;A&quot;);
Console.WriteLine(&quot;B&quot;);
Console.WriteLine(&quot;C&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;20&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Awl4I/dJMcahb08Hy/naRBHJjI4ZwWNmaBzwgxO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Awl4I/dJMcahb08Hy/naRBHJjI4ZwWNmaBzwgxO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Awl4I/dJMcahb08Hy/naRBHJjI4ZwWNmaBzwgxO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAwl4I%2FdJMcahb08Hy%2FnaRBHJjI4ZwWNmaBzwgxO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;20&quot; height=&quot;53&quot; data-origin-width=&quot;20&quot; data-origin-height=&quot;53&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) Numbered positional arguments(번호가 매겨진 위치 인수)를 사용한 서식화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Console App에서 서식화된 문자열을 표시하기 위해 사용되는 가장 흔한 방식으로 numbered positional arguments가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Numbered positional arguments는 Write나 WriteLine와 같은 method에서 지원하는데 문자열에서는 format method를 대신 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 상품의 개수별 단가를 표시하는 것으로 상품의 개수와 단가표시를 위해 WriteLine method와 무너져 열변수에서 numbered positional arguments를 사용하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767600282822&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int goods = 10;
decimal price = 1200;

Console.WriteLine(
    format: &quot;상품 {0}개 : {1}원&quot;,
    arg0: goods,
    arg1: goods * price);

string howmuch = string.Format(
    format: &quot;상품 {0}개 : {1}원&quot;,
    arg0: goods,
    arg1: goods * price);

Console.WriteLine(howmuch);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;139&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QUMvY/dJMcabbN0j2/fOgDWSZ9jVSSs7KvnKV5c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QUMvY/dJMcabbN0j2/fOgDWSZ9jVSSs7KvnKV5c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QUMvY/dJMcabbN0j2/fOgDWSZ9jVSSs7KvnKV5c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQUMvY%2FdJMcabbN0j2%2FfOgDWSZ9jVSSs7KvnKV5c1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;139&quot; height=&quot;35&quot; data-origin-width=&quot;139&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 인수를 지정하기 위해 arg0과 arg1이라는 숫자가 매겨진 이름의 인수를 사용하였습니다. 기본적으로 이러한 인수는 arg2까지 3개를 사용할 수 있으며 그 이상의 인수를 지정해야 한다면 아래와 같이 인수명을 생략하고 인수를 지정해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767600313647&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(
    &quot;Colors : {0}, {1}, {2}, {3}, {4}&quot;,
    &quot;Red&quot;, &quot;Blue&quot;, &quot;Black&quot;, &quot;Yellow&quot;, &quot;Green&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 보간문자열(Interpolated strings)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보간문자열은 C#6부터 도입된 기능으로 사용하고자 하는 문자열의 앞에 아래 예제와 같이 '$'문자를 접두사로 붙이면 문자열 중간에 중괄호를 사용하여 해당 위치에 변수나 표현식의 현재 결괏값을 표현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767685199648&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int goods = 10;
decimal price = 1200;

Console.WriteLine($&quot;상품 수량 : {goods}, 단가 {price}원&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;195&quot; data-origin-height=&quot;23&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EXdxw/dJMcac2PbbD/911nFt6oYPm2S5cHx4I0A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EXdxw/dJMcac2PbbD/911nFt6oYPm2S5cHx4I0A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EXdxw/dJMcac2PbbD/911nFt6oYPm2S5cHx4I0A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEXdxw%2FdJMcac2PbbD%2F911nFt6oYPm2S5cHx4I0A1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;195&quot; height=&quot;23&quot; data-origin-width=&quot;195&quot; data-origin-height=&quot;23&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 문자열에서 보간문자열을 사용하면 해당 문자열을 더 쉽게 읽을 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이러한 특징의 보간문자열을 사용하면 문자열을 서로 결합하는 경우에도 유용하게 사용할 수 있습니다. 예를 들어 C#10 이전에는 문자열 결합을 위해 + 연산자를 사용하여 결합해야 했지만&lt;/p&gt;
&lt;pre id=&quot;code_1767685278064&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s1 = &quot;abcd&quot;;
string s2 = &quot;efgh&quot;;

string result = s1 + s2;

Console.WriteLine(result);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보간문자열을 사용하면 위의 예제를 아래와 같이 동일하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767685399141&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s1 = &quot;abcd&quot;;
string s2 = &quot;efgh&quot;;

string result = $&quot;{s1}{s2}&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) 서식 문자열&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수나 표현식은 comma나 colon을 통해 서식 문자열을 사용함으로써 서식화활 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서식문자열은, 예를 들어 N0은 천 단위 분리와 함께 소수점이 없는 서식을 의미하며 C는 통화를 의미합니다. 통화와 관련된 서식은 현재 PC의 문화권설정에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서식문자열을 사용할 때의 형식은 아래와 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;{ index [, alignment ] [ : formatString ] }&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 형식에서 alignment는 특정 문자수만큼의 간격으로 왼쪽 혹은 오른쪽으로 값을 정렬하고자 할 때 사용되며 정수값이 사용됩니다. 이때 정수값이 양수라면 오른쪽 기준 정렬을, 음수라면 왼쪽 기준 정렬을 수행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767685827820&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;Student Table&quot;);
Console.WriteLine(&quot;{0, -10} {1, -5} {2, 9}&quot;, &quot;Name&quot;, &quot;Class&quot;, &quot;Age&quot;);
Console.WriteLine(&quot;{0, -8} {1, -5} {2, 5:N0}&quot;, &quot;홍길동&quot;, &quot;족구&quot;, 12);
Console.WriteLine(&quot;{0, -8} {1, -5} {2, 5:N0}&quot;, &quot;장아영&quot;, &quot;야구&quot;, 15);
Console.WriteLine(&quot;{0, -8} {1, -5} {2, 5:N0}&quot;, &quot;황민경&quot;, &quot;야구&quot;, 9);
Console.WriteLine(&quot;{0, -8} {1, -5} {2, 5:N0}&quot;, &quot;김필석&quot;, &quot;탁구&quot;, 11);
Console.WriteLine(&quot;{0, -8} {1, -5} {2, 5:N0}&quot;, &quot;김동원&quot;, &quot;축구&quot;, 10);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 Name, Class, Age column을 표시할 때 Name은 10자 기준 왼쪽정렬을, Class는 5자 기준 왼쪽정렬을, Age는 9자 기준 오른쪽 정렬을 수행하며 목록에서는 이름에 8 자 기준 왼쪽, 수업에는 5자 기준 왼쪽, 나이 부분에는 오른쪽 5자 기준 정렬을 수행하여 표시하도록 합니다. 이때 나이는 N0을 적용해 소수점 없이 표시하도록 하였으며 그럴 일이 없겠지만 나잇값이 1000이 넘으면 ,를 사용해 천 단위 분리를 적용할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;190&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FG4fK/dJMcaihHWBD/qMY7v7BLRFassVF4De8YV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FG4fK/dJMcaihHWBD/qMY7v7BLRFassVF4De8YV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FG4fK/dJMcaihHWBD/qMY7v7BLRFassVF4De8YV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFG4fK%2FdJMcaihHWBD%2FqMY7v7BLRFassVF4De8YV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;190&quot; height=&quot;123&quot; data-origin-width=&quot;190&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 숫자 밑 날짜, 시간 서식화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자의&amp;nbsp;서식과&amp;nbsp;관련해서는&amp;nbsp;사용자&amp;nbsp;정의&amp;nbsp;서식&amp;nbsp;code를&amp;nbsp;사용함으로써&amp;nbsp;원하는 대로&amp;nbsp;표현방식을&amp;nbsp;지정할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;아래&amp;nbsp;표에서는&amp;nbsp;사용가능한&amp;nbsp;서식&amp;nbsp;code와&amp;nbsp;그&amp;nbsp;용도를&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;0자리 표시자로 0이 아닌 값이 있다면 해당 값을 표시하지만 그렇지 않다면 0을 대신 표현합니다. 예를 들어 '0000.00'으로 서식화한 경우 '123.4'라는 값이 있으면 '0123.40'으로 표시됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;#&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;자리&amp;nbsp;표시자로&amp;nbsp;0이&amp;nbsp;아닌&amp;nbsp;값이&amp;nbsp;있다면&amp;nbsp;해당&amp;nbsp;값을&amp;nbsp;표시하지만&amp;nbsp;그렇지&amp;nbsp;않다면&amp;nbsp;해당&amp;nbsp;자리수를&amp;nbsp;표현하지&amp;nbsp;않습니다.&amp;nbsp;예를&amp;nbsp;들어&amp;nbsp;'####.##'으로&amp;nbsp;서식화한&amp;nbsp;경우&amp;nbsp;'123.4'라는&amp;nbsp;값이&amp;nbsp;있으면&amp;nbsp;'123.4'로&amp;nbsp;표시됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;.&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;소수점 표시자로 숫자에서 소수점을 나타내기 위해 사용됩니다. 문화권의 영향을 받으므로 소수점의 표현형태가 달라질 수 있습니다. 예를 들어 French 문화권이라면 소수점을 ,(comma)로 표시합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;,&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;그룹단위 분리자입니다. 보통 숫자에서 천단위 분리를 위해 많이 사용됩니다. 예를 들어 '0,000'서식일때 '123456789'라는 숫자가 있으면 '123,456,789'로 표시합니다. 또한 1000단위로 수를 나누어 숫자의 규모를 조정하는데에도 사용됩니다. 예를 들어 '0.00,,'으로 서식화한 경우 ,하나당 1000을 나눈다는 의미가 되므로 '1234567'값은 '1.23'으로 표시됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;%&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;Percentage 자리표시자로 값을 100으로 곱하여 뒤에 percentage문자를 붙여줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;\&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;확장문자로서 서식code뒤의 문자를 그대로 표시합니다. 예를 들어 '\##,###'으로 서식화한 경우 '1234'값이 있다면 '#1,234'로 표시합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.69767%;&quot;&gt;;&lt;/td&gt;
&lt;td style=&quot;width: 94.3023%;&quot;&gt;Selction분리자로서 양수, 음수, zero에 대한 서식문자열의 차이를 정의합니다. 예를 들어 '[0];(0);Zero'로 서식화한 경우 12값은 '[12]'로, -12값은 '(12)'로, 0은 'Zero'로 표시합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 외에 사용가능한 다른 서식 code도 존재합니다. 모든 서식 code에 대한 목록은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings\&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767772013590&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Custom numeric format strings - .NET&quot; data-og-description=&quot;Learn how to create a custom numeric format string to format numeric data in .NET. A custom numeric format string has one or more custom numeric specifiers.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IYTWR/hyZQ51SUq9/UssdWYIkarKZkXYDokQcdK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IYTWR/hyZQ51SUq9/UssdWYIkarKZkXYDokQcdK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Custom numeric format strings - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to create a custom numeric format string to format numeric data in .NET. A custom numeric format string has one or more custom numeric specifiers.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자에 대한 서식은 'C'나 'N'과 같은 표준 서식을 사용하여 좀 더 간단하게 적용할 수도 있습니다. 이때도 'C0, N4'와 같이 정밀도를 조정하여 원하는 만큼의 정밀도를 표현할 수 있는데, C와 N에서는 아무런 숫자도 지정하지 않으면 기본값 2가 적용되지만 다른 서식 code의 영향으로 약간씩 달라질 수 있습니다. 특히 D 같은 경우, 기본정밀도가 필요로 하는 최소수이며 E에서는 6이 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.72093%;&quot;&gt;C, c&lt;/td&gt;
&lt;td style=&quot;width: 91.2791%;&quot;&gt;통화를 의미하며 문화권의 영향에 따라 다르게 표시될 수 있습니다. 예를 들어 대한민국문화권이라면 123.4값을 '￦123.4'로 표현합니다. 소수점을 무시하고자 하는 경우 'C0'으로 사용할 수 있으며 그 결과 '￦123'로 표시될 것입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.72093%;&quot;&gt;N,&amp;nbsp;n&lt;/td&gt;
&lt;td style=&quot;width: 91.2791%;&quot;&gt;선택적 음수기호와 group화문자를 가진 정수자리수입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.72093%;&quot;&gt;D,&amp;nbsp;d&lt;/td&gt;
&lt;td style=&quot;width: 91.2791%;&quot;&gt;10진수로 선택적 음수기호가 있지만 group화문자는 없는 정수자리수입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.72093%;&quot;&gt;B,&amp;nbsp;b&lt;/td&gt;
&lt;td style=&quot;width: 91.2791%;&quot;&gt;2진수로 13과 같은 정수를 1101로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.72093%;&quot;&gt;X,&amp;nbsp;x&lt;/td&gt;
&lt;td style=&quot;width: 91.2791%;&quot;&gt;16진수로 255와 같은 정수를 FF로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.72093%;&quot;&gt;E,&amp;nbsp;e&lt;/td&gt;
&lt;td style=&quot;width: 91.2791%;&quot;&gt;지수표기법으로 1234.567과 같은 수를 1.234567000E+003으로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 이외에 전체 표준서식은 아래 link에서 확인할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767772210918&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Standard numeric format strings - .NET&quot; data-og-description=&quot;In this article, learn to use standard numeric format strings to format common numeric types into text representations in .NET.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cahtG3/hyZQZHpclE/2H6o5I3GTbHAIwR3cwTyN0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cahtG3/hyZQZHpclE/2H6o5I3GTbHAIwR3cwTyN0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Standard numeric format strings - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this article, learn to use standard numeric format strings to format common numeric types into text representations in .NET.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 숫자뿐 아니라 아래와 같이 날짜와 시간에 대한 사용자 정의 서식 code도 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;/&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;날짜 분리자입니다. 문화권의 영향을 받아 다르게 표시할 수 있는데 예를 들어 미국 문화권이라면 '/'로 대한민국 문화권이라면 '-'문자로 표현됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;\&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;확장문자로서 서식code뒤의 문자를 그대로 표시합니다. 예를 들어 'h \h m \m'이라면 오전 10:30분을 '10 h 30 m'으로 표시할 것입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;:&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;시간분리자로서 문화권의 영량을 받아 다르게 표시할 수 있습니다. 대한민국 문화권이라면 ':'이 사용되지만 French라면 '.'이 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;d,&amp;nbsp;dd&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;날짜 표시자입니다. 기본적으로 1에서 31까지 날짜를 표시하는데 'dd'를 사용한다면 숫자가 하나일때 앞에 0을 부텅 '01'처럼 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;ddd,&amp;nbsp;dddd&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;요일을 표시합니다. 예를 들어 월요일인 경우 'ddd'라면 '월'로 표시하지만 'dddd'라면 '월요일'로 표시합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;f,&amp;nbsp;ff,&amp;nbsp;fff&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;10분의 1초, 백분의 1초(밀리초)를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;g&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;'A.D'같은 시대를 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;h,&amp;nbsp;hh&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;시간을 나타냅니다. 1부터 12시를 표현하며 'hh'인 경우 숫자가 하나일때 앞에 0을 붙어 '01'과 같이 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;H,&amp;nbsp;HH&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;시간을 나타냅니다. 1부터 24시를 표현하며 'HH'인 경우 숫자가 하나일때 앞에 0을 붙어 '01'과 같이 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;K&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;표준 시간대를 나타냅니다. 표준 시간대가 특정되지 않으면 null이 되며 UTC는 Z로 나타냅니다. UTC로 부터 조정된 지역 시간대는 -8:00과 같은 값으로 표현됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;m,&amp;nbsp;mm&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;0부터&amp;nbsp;59분까지&amp;nbsp;붙을&amp;nbsp;나타내며&amp;nbsp;'mm'이라면&amp;nbsp;숫자가&amp;nbsp;하나일때&amp;nbsp;앞에&amp;nbsp;0을&amp;nbsp;붙어&amp;nbsp;'01'과&amp;nbsp;같이&amp;nbsp;표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;M,&amp;nbsp;MM&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;1부터 12까지 월을 나타내며 'MM'이라면 숫자가 하나일때 앞에 0을 붙어 '01'과 같이 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;MMM,&amp;nbsp;MMMM&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;월을 문자열로 표현합니다. 'MMM'이라면 'Jan'과 같이 표현하지만 'MMMM'이라면 'January'로 나타냅니다. 문화권의 영향을 받으므로 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;s,&amp;nbsp;ss&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;0부터 59까지 초를 나타내며 'ss'라면 숫자가 하나일때 앞에 0을 붙어 '01'과 같이 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;t, tt&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;오전, 오후를 나타내며 't'라면 'A'로 표현하지만 'tt'라면 'AM'으로 표현됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;y,&amp;nbsp;yy&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;0부터 99까지 세기(century)를 표현하며 'yy'라면 숫자가 하나일때 앞에 0을 붙어 '01'과 같이 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;yyy&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;최소 3자리 숫자만큼의 년도를 표시하며 필요로 하는 만큼의 자리수를 가질 수 있습니다. 예를 들어 '1 A.D'는 '100'으로 표현되며 '2026'과 같은 년도표현도 가능합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;yyyy,&amp;nbsp;yyyyy&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;4자리 혹은 5자리 수의 년도를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;z,&amp;nbsp;zz&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;UTC로 부터의 offset시간을 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7209%;&quot;&gt;zzz&lt;/td&gt;
&lt;td style=&quot;width: 86.2791%;&quot;&gt;UTC로 부터의 offset 시간과 붙을 나타냅니다. 0으로 자리수를 채우므로 '+05:30'과 같이 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜와 시간에 관한 더 자세한 목록은 아래 link에서 확인할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767772485094&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Custom date and time format strings - .NET&quot; data-og-description=&quot;Learn to use custom date and time format strings to convert DateTime or DateTimeOffset values into text representations, or to parse strings for dates &amp;amp; times.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJI7n1/hyZQTTrzPh/nCJkD9DZWROpR89Fpm5lBK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJI7n1/hyZQTTrzPh/nCJkD9DZWROpR89Fpm5lBK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Custom date and time format strings - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn to use custom date and time format strings to convert DateTime or DateTimeOffset values into text representations, or to parse strings for dates &amp;amp; times.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한&amp;nbsp;날짜와&amp;nbsp;시간에도&amp;nbsp;아래와&amp;nbsp;같이&amp;nbsp;표준서식을&amp;nbsp;적용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;d&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'2026-01-07'와 같이 날짜를 표현합니다. 문화권의 영향을 받으므로 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;D&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'2026년 1월 7일 수요일'와 같이 날짜와 요일을 표현합니다. 문화권의 영향을 받으므로 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;f&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'2026년 1월 7일 수요일 오후 4:28'와 같이 날짜와 요일, 시간을 표현합니다. 문화권에 따라 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;F&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'2026년 1월 7일 수요일 오후 4:31:16'와 같이 날짜와 요일, 시간, 초를 표현합니다. 문화권에 따라 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;m,&amp;nbsp;M&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'1월 7일'과 같이 월과 날짜를 표현합니다. 문화권에 따라 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;o,&amp;nbsp;O&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;날짜와 시간을 '026-01-07T16:32:42.9964891+09:00'과 같이 표준 pattern으로 표현합니다. 다른 쪽으로의 data전달을 위해 날짜와 시간값을 직렬화하는데 유용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;r,&amp;nbsp;R&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;RFC1123 pattern으로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;t&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'HH:mm' 형식으로 짧은 시간을 표현합니다. 문화권에 따라 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;T&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'HH:mm:ss' 형식으로 긴 시간을 표현합니다. 문화권에 따라 다르게 표현될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;u&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'2026-01-07 16:38:32Z'와 같이 범용 짧은 날짜/시간 pattern으로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 8.25581%;&quot;&gt;U&lt;/td&gt;
&lt;td style=&quot;width: 91.7442%;&quot;&gt;'2026년 1월 7일 수요일 오전 7:39:19'와 같이 범용 긴 날짜/시간 pattern으로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜와 시간에 관한 더 자세한 표준서식 목록은 아래 link에서 확인할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767772605370&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Standard date and time format strings - .NET&quot; data-og-description=&quot;Learn how to use a standard date and time format string to define the text representation of a date and time value in .NET.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bVhvAl/hyZQY2Nxde/piL5Ejlzi6803k1KCtBq10/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bVhvAl/hyZQY2Nxde/piL5Ejlzi6803k1KCtBq10/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Standard date and time format strings - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to use a standard date and time format string to define the text representation of a date and time value in .NET.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) Console 사용 간소화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#6부터 using문은 namespace를 import 할 뿐만 아니라 정적 class를 import 함으로써 code작성 자체를 간소화하는데도 사용할 수 있습니다. Console class도 마찬가지인데 using문을 사용하면 Console의 입력을 반복할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 단일 file에서 정적 type import 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Program.cs라면 해당 file의 상단에 아래와 같이 using문을 추가하여 System.Console class에 대한 정적 import를 수행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767938326599&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Console;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이후부터는 해당 file에서 아래와 같이 WriteLine와 같은 Console method를 Console 없이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767938337527&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WriteLine(&quot;...&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Project단위 정적 type import 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 import는 단일 file이 아닌 Project단위 모든 file을 위해서도 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 이전에 작성한 using문 전체를 삭제하고 Project의 설정 file(csproj)을 열어 &amp;lt;PropertyGroup&amp;gt; 다음에 &amp;lt;ItemGroup&amp;gt;을 추가하고 그 안에 아래와 같이 Using 요소를 추가함으로써 System.Console을 전역적으로 import 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767938388947&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup Label=&quot;Console static import&quot;&amp;gt;
    &amp;lt;Using Include=&quot;System.Console&quot; Static=&quot;true&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 ItemGroup안에 있는 label속성은 일부 요소에 대한 주석화가 필요할 때 또는 다수의 이 사용되는 경우 각 의 역할을 명확하게 구분하는데 유용하게 사용할 수 있지만 필수속성은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7) 입력받기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면을 표시하기 위해서는 WriteLine method를 사용하지만 반대로 사용자로부터의 입력은 ReadLine method를 사용하며 이를 통해 입력된 문자열값을 받을 수 있습니다. ReadLine method가 호출되면 사용자가 값을 입력할 때까지 대기상태에 있다가 사용자가 입력을 마치면(원하는 입력 후 Enter key를 누르는 순간) 그동안의 입력한 값을 문자열로 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 사용자에게 이름과 나이의 입력을 요청한 뒤 사용자가 값을 입력하고 나면 해당 값을 그대로 화면에 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767855077707&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;이름을 입력하세요.&quot;);
string name = Console.ReadLine();
Console.WriteLine(&quot;나이를 입력하세요.&quot;);
string age = Console.ReadLine();

Console.WriteLine($&quot;귀하의 이름은 {name}이고 나이는 {age}입니다.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET 6부터는 null가능성 확인기능이 기본으로 동작하므로 위 예제라면 ReadLine method가 null을 반환할 수 있다는 compiler의 경고를 표시할 수 있습니다. 그러나 실질적으로 ReadLine method가 null을 반환할 수 있는 상황은 발생하지 않습니다. 사용자가 아무런 값을 입력하지 않는다 하더라도 빈문자열 값을 반환할 뿐 null을 반환하는 경우는 없습니다. 필요하다면 특정한 경우에 이러한 null확인기능이 동작하지 않도록 할 수 있는데 이에 대한 구체적인 방법은 곧 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 결괏값은 입력한 값에 따라 달라집니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSENSe/dJMcacPk3mI/BDqi0OI5mB6YH6pZPmR9hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSENSe/dJMcacPk3mI/BDqi0OI5mB6YH6pZPmR9hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSENSe/dJMcacPk3mI/BDqi0OI5mB6YH6pZPmR9hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSENSe%2FdJMcacPk3mI%2FBDqi0OI5mB6YH6pZPmR9hK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;272&quot; height=&quot;90&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null가능성 경고와 관련하여 예제의 'string name = Console.ReadLine();'부분에서는 아래와 같이 string뒤에 '?'문자를 접미사로 붙여줄 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767855565515&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string? name = Console.ReadLine();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우는 compiler에게 name변수가 null이 될 수 있음을 알고 있다고 명시함으로써 compiler가 null가능성 경고를 표시하지 않도록 합니다. 하지만 이미 상술했듯이 ReadLine이 null을 반환하는 경우는 없으므로 예제는 아무런 문제 없이 작동할 수 있습니다. 그러나 만약 위 상태에서 name변수의 특정 member에 접근을 시도하게 되면 compiler는 다시 null가능성 경고를 표시할 수 있습니다. 상기 처리는 compiler에게 'name변수가 null이 될 수 있음을 알고 있어. 그러니 경고표시는 하지 말아 줘.'라고 compiler에게 요청하는 것일 뿐 실제 name이 null이 되지 못하도록 하는 조치는 아닙니다. 때문에 여전히 name변수가 null이 될 수 있는 상황에서 member의 접근은 적절한 처리가 아니므로 compiler의 경고를 보게 되는 것입니다. 만약 이와 같은 상황에서 'name변수가 어떠한 경우에도 null이 되지 않는다.'라는 걸 명시하고자 한다면 아래와 같이 '!'문자를 ReadLine method뒤에 붙여줄 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767855646254&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string name = Console.ReadLine()!;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 '!'표시는 compiler에게 'ReadLine method는 절대 null을 반환하지 않을 거야.'라고 알려주는 것입니다. 따라서 '그 결과를 받는 name변수도 null 되는 경우는 없다.'라는 의미가 되고 따라서 compiler는 상기 처리에 대해서 null가능성 경고를 표시하지 않게 됩니다. 이를 'null 허용 연산자'라고 하는데 다만, 정말 ReadLine method가 null을 반환하게 되는 상황 즉, name변수가 null이 되는 상황이 발생하는 경우에 대한 모든 책임은 직접 개발자에게 있음을 알고 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 2가지 방법을 통해 compiler가 발생시키는 null가능성 경고를 어떻게 처리할 수 있는지를 간단히 알아봤는데 추후에 null과 관련된 더 자세한 처리방법을 알아볼 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ReadLine method 자체가 null을 반환하는 경우는 있을 수 있습니다.&lt;br /&gt;&lt;br /&gt;ReadLine method는 표준입력 stream으로부터 입력값을 받게 되는데 상술했듯 사용자가 아무런 값도 입력하지 않고 Enter를 누르게 되면 null대신 빈 문자열값을 반환합니다.&lt;br /&gt;&lt;br /&gt;Console App에서 일반적인 경우 ReadLine이 null을 반환하는 건 EOF(end of stream)에 도달한 경우뿐입니다. 그러나 이 경우는 표준입력 stream이 redirect 되거나 EOF에 도달한 경우 또는 EOF를 임의적으로 만들어낼 수 있는 환경에서만 발생가능한 것일 뿐 단순 사용자 입력으로는 '절대!'발생하지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) key 입력 여부 판별하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReadKey method를 사용하면 사용자로부터 특정 key의 입력을 판별할 수 있습니다. 해당 method는 사용자의 key입력을 대기하다가 특정 key나 혹은 여러 key를 누르게 되면 그에 해당하는 ConsoleKeyInfo값을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 사용자에게 key의 입력을 요청하고 사용자가 원하는 key입력을 수행하면 그에 해당하는 key의 정보를 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1767939529675&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;원하는 key를 눌러주세요.&quot;);
ConsoleKeyInfo key = Console.ReadKey();
Console.WriteLine();
Console.WriteLine($&quot;{key.Key} : {key.KeyChar} : {key.Modifiers}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 예제에서는 key입력에 Alt + h key를 누른 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3IGeX/dJMcajgAs06/dKhpOyUL9wKTQrzueH5yHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3IGeX/dJMcajgAs06/dKhpOyUL9wKTQrzueH5yHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3IGeX/dJMcajgAs06/dKhpOyUL9wKTQrzueH5yHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3IGeX%2FdJMcajgAs06%2FdKhpOyUL9wKTQrzueH5yHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;177&quot; height=&quot;54&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConsoleKeyInfo에서 key속성은 입력한 key의 값을, KeyChar는 입력한 key의 문자를 의미합니다. 따라서 만약 F3이나 F12와 같은 key를 누른 경우라면 이 값은 나오지 않을 수 있습니다. 마지막으로 Modifiers는 Alt나 Shift key와 같이 다른 key와의 조합을 의미합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;VSCode안에서 Console App을 실행하는 경우 일부 key의 입력은 실행 중인 Console App에 도달하기도 전에 VSCode안에서 가로채 VSCode의 특정기능으로 실행될 수 있습니다. 예를 들어 Ctrl + Shift + X key는 VSCode에서 Extensions보기기능에 해당합니다. 따라서 Console App에서 제대로 key의 입력을 test 해 보려면 Console App을 Build 하고 Command Prompt 또는 Terminal에서 Compile 된 exe를 실행해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;8) 인수 전달하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Console App의 동작은 대게 필요한 인수를 전달함으로써 제어합니다. 예를 들어 dotnet의 명령줄도구에서 특정 template의 project를 생성하려면 아래와 같이 해당 project template의 이름을 전달합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new Console&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 console ppp이 실행되면 사용자가 전달한 인수값을 application안에서 추출해야 할 필요가 있습니다. .NET6 이전에 console app project template은 기본적으로 아래와 같은 code를 생성했는데&lt;/p&gt;
&lt;pre id=&quot;code_1768201229164&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;

namespace Arguments
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(&quot;Hello World!&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 code에서 Main method에 있는 'string[] args'가 바로 인수를 저장하고 있는 매개변수에 해당하며 array type이므로 해당 매개변수를 통해 인수값을 순서대로 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET6 이후부터는 top-level programs가 사용되므로 상기 예제와 같이 Program class와 Main method가 노출되지 않습니다. 따라서 얼핏 보면 args인수 역시 존재하지 않는 것처럼 보일 수 있으나 이러한 요소들은 눈에만 보이지 않고 감춰져 있을 뿐 여전히 존재하므로 사용되지 않는다고 오해해서는 안됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령줄에서 인수는 오로지 공백으로만 분리되며 이렇게 분리된 인수값이 args배열에 순서대로 들어가게 됩니다. 만약 인수값 자체에 공백이 포함되어야 한다면 이때는 홑따옴표나 큰따옴표로 인수값 자체를 감싸서 표현해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 top-level programs환경에서 전달된 인수의 개수를 확인하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768201962879&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;전달된 인수의 수 : {args.Length}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;40&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5gGyj/dJMcafrMLUd/9Ax2CkWquF5kdXeC4UOpN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5gGyj/dJMcafrMLUd/9Ax2CkWquF5kdXeC4UOpN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5gGyj/dJMcafrMLUd/9Ax2CkWquF5kdXeC4UOpN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5gGyj%2FdJMcafrMLUd%2F9Ax2CkWquF5kdXeC4UOpN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;40&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 예제를 Visual Studio에서 실행하고자 한다면 인수를 조금 다른 방식으로 지정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio의 Solution Explorer에서 project를 mouse오른쪽 click 하고 Properties를 선택합니다. 그런 후 설정화면의 왼쪽에 Debug 항목을 선택하고 'Open debug launch profiles UI'를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'Launch Profiles'화면의 'Command line arguments'부분에 원하는 매개변수를 공백으로 분리하여 순서대로 입력합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/039Qy/dJMcafef8w2/JRvb0pTpGAxwTBzbNCyph1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/039Qy/dJMcafef8w2/JRvb0pTpGAxwTBzbNCyph1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/039Qy/dJMcafef8w2/JRvb0pTpGAxwTBzbNCyph1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F039Qy%2FdJMcafef8w2%2FJRvb0pTpGAxwTBzbNCyph1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;513&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정하고 'Launch Profiles'화면을 닫으면 Solution Explorer에는 'Properties'folder와 함께 하위에 launchSettings.json file이 생성되는데, 해당 file을 선택해 열어보면 위에서 설정한 인수값이 그대로 commandLineArgs로 등록되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768202043314&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;profiles&quot;: {
    &quot;ConsoleApp1&quot;: {
      &quot;commandName&quot;: &quot;Project&quot;,
      &quot;commandLineArgs&quot;: &quot;aaa bbb ccc&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달된 인수의 실제 값을 확인하기 위해 Program.cs에서 Console.WriteLine... 다음에 아래와 같은 구문을 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1768202057015&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foreach (string arg in args)
{
    Console.WriteLine(arg);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 예제를 실행시키면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5zON6/dJMcagK0mcY/KuyJnAZMVEXo6IUCKQAQg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5zON6/dJMcagK0mcY/KuyJnAZMVEXo6IUCKQAQg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5zON6/dJMcagK0mcY/KuyJnAZMVEXo6IUCKQAQg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5zON6%2FdJMcagK0mcY%2FKuyJnAZMVEXo6IUCKQAQg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;87&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 인수 활용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 Console App을 통해 현재 실행중인 terminal의 전경색과 배경색을 변경하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 현재 필요한 인수는 총 2개이므로 만약 사용자가 23개의 인수를 제대로 입력하지 않은 경우 사용자에게 관련 오류를 표시하고 application의 실행을 중지하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768202099384&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (args.Length &amp;lt; 2)
{
    Console.WriteLine(&quot;프로그램 실행에 필요한 충분한 인수가 지정되지 않았습니다.&quot;);
    return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 인수가 2개나 그 이상입력된 경우라면 첫 번째 인수값부터 읽어 terminal의 전경색과 배경색, cursor의 크기를 설정하도록 합니다. 인수가 2개 이상 지정된 경우라면 그 이상은 어차피 무시할 것이므로 문제 되지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768202115396&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), args[0], true);
Console.BackgroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), args[1], true);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용된 ForegroundColor나 BackgroundColor 외에도 CursorSize와 같이 terminal에 관련된 여러 속성이 제공됩니다. 다만 일부 속성의 경우는 Windows전용이므로 다른 OS에서 다른 속성을 사용하고자 하는 경우 compiler경고가 발생할 수 있고 실제 Application이 실행되는 경우 예외가 발생할 것입니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Enum.Parse, typeof와 같은 생소한 구문은 추후에 자세히 알아볼 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하기 위해 아래와 같이 2개의 인수를 지정하여 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J9sh2/dJMcac9CfQC/XVwlpAAKngV4vVyTYV9bXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J9sh2/dJMcac9CfQC/XVwlpAAKngV4vVyTYV9bXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J9sh2/dJMcac9CfQC/XVwlpAAKngV4vVyTYV9bXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ9sh2%2FdJMcac9CfQC%2FXVwlpAAKngV4vVyTYV9bXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>.NET 10</category>
      <category>c#</category>
      <category>C# 14</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/644</guid>
      <comments>https://cliel.tistory.com/entry/C-14-NET-10-C-14#entry644comment</comments>
      <pubDate>Mon, 1 Dec 2025 17:38:04 +0900</pubDate>
    </item>
    <item>
      <title>[C# 14 / .NET 10] 2025년 11월 C# 14 탄생</title>
      <link>https://cliel.tistory.com/entry/%EC%97%B4%EB%84%A4%EB%B2%88%EC%A7%B8C-2025%EB%85%84-11%EC%9B%94-C%EC%9D%98-14%EB%B2%88%EC%A7%B8-%EB%B2%84%EC%A0%84%EC%9D%98-%ED%83%84%EC%83%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 11월 기다리던 C#의 14번째 version과 .NET 10이 출시되었습니다. 새로움은 늘 기대로 가득한 채 변화를 맞이하게 하지만 그만큼 새롭게 알아야 할 것들이 생겨나게 마련이고 그런 이유로 새로운 것을 찾아보기 위한 여정을 지금부터 시작하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 언어도 모두 마찬가지 겠지만 Software개발을 위해서는 그 맞는 개발환경을 갖추어야 할 것입니다. C# 역시 이 과정이 필요하므로 우선 Visual Studo 2026과 Visual Studio Code를 사용해 개발환경을 만들어볼 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;향후 개발 도구의 명칭은 Visual Studio 2026를 VS2026으로 Visual Studio Code를 VSCode로 지칭하고자 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 개발환경을 갖춘다고 아무것도 모른채로 바로 시작할 수 있는 것은 아니니 .NET에 대한 기본적인 개념을 잡고 가야 할 것입니다. 사실 .NET이란걸 단편적으로는 설명할 수 없고 대신 여러 가지 얘기가 나와야 하는데, 그렇다고 해서 복잡한 얘기를 할 필요는 전혀 없습니다. 단순히 기본적인 개념을 잡는 것으로 시작해 VS2026과 VSCode를 사용하여 C# 14와 .NET 10을 대상으로 한 예제를 만들어 봄으로서 한 걸음씩 나아갈 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 개발환경 설정&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Programming을 하려면 당연히 그에 맞는 개발도구를 갖추고 있어야 합니다. 물론 notepad와 같은것만으로도 불가능한 것은 아니지만 작업의 효율성은 극히 떨어질 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#을 위해 사용할 수 있는 대표적인 개발도구로는 당연 VS2026이나 VSCode를 그 예로 들 수 있습니다. 물론 이외에도 jetBrains사의 Rider등을 사용할 수 있지만 VS2026와 VSCode 외에 다른 개발도구에 관해서는 따로 언급하지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026은 이것 하나만으로 정말 많은 작업을 수행할 수 있으며 대부분의 .NET개발자가 사용하는 개발도구이기도 합니다. 하지만 많은 것을 수행할 수 있는 만큼 VSCode에 비해 크고 복잡한 구조를 가진다는 단점이 있습니다. 뿐만 아니라 대부분의 작업에서 자체적인 mechanism을 제공함으로써 뒷단에 발생하는 많은 동작을 감추고 있고, 그 결과로 드러나는 것만 개발자에게 보여주는 경향이 있습니다. 이러한 동작방식은 숙련된 개발자에게는 상당한 편리함을 제공해 주지만 학습을 시작하고자 하는 초보개발자에게는 오히려 세부적인 동작원리를 감추게 됨으로써 학습효과를 저해하는 결과를 가져다줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;극단적으로 설명하자면 사실 project의 설정이나 code file을 편집하기 위해서는 반드시 VS2026과 같은 개발도구가 필요한 것은 아닙니다. 이미 언급했듯 notepad만으로도 code를 작성할 수 있고 dotnet command-line 도구를 사용하여 해당 file을 compile하고 실행할 수 있습니다. 개발도구는 단지 이러한 과정에서 직접 처리해야만 하는 수많은 작업과정을 대신해 주는 도구의 역할만을 해줄 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 학습을 위한 적절한 도구와 project type&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Programming을 처음 시작하는 경우, 사용할 가장 좋은 개발도구는 code의 입력과 설정과정을 적절하게 도와주면서도 뒷편에서 발생하는 동작을 감추지 않는 것입니다. 개인적으로 현재 시점에서 이러한 조건에 가장 부합해 있는 개발도구가 바로 VSCode라고 판단됩니다. 아직 어떤 개발도구를 사용할지 결정하지 않았다면 VSCode를 강력히 권장합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;VSCode는 권장사항일뿐 이미 익숙하게 사용하고 있거나 소속되어 있는 Team에서 사용 중인 별도의 개발도구가 있는 경우 해당 도구를 계속할 수 있습니다. Programming에서 개발도구의 선택범위는 제한이 없으므로 특정 개발도구의 사용을 고집할 필요는 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET에서 생성할 수 있는 Application유형은 Desktop수준부터 Web에 이르기까지 매우 다양합니다. 단순히 C# Programming을 경험하기 위한 것이라 해도 예제를 위한 Project를 특정 Application 유형으로 생성해야 하는데, 이를 위해 특별한 경우가 아니면 모두 Console Application Project Teamplate을 사용할 것입니다. Console Application은 학습에 불필요한 Code를 수반하지 않으면서 학습에만 집중할 수 있게 하는 가장 좋은 Application유형이라 말할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Visual Studio Code&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode는 Microsoft에서 개발된 가벼우면서도 훌륭한 성능을 제공하는 code editor이며 무엇보다 cross-platform에 해당하므로 Windows는 물론 macOS나 Ubuntu, Red Hat과 같은 Linux에서도 실행할 수 있습니다. 이렇게 다양한 platform을 지원하게 됨으로써 자연스럽게 cross-platform app을 개발하는 데 사용되고 있으며 Hardware 역시 기존 x86 processor뿐만 아나라 ARM까지도 지원함으로써 Apple Silicon 계열 및 Surface Laptop, 심지어 Raspberry Pi 등 ARM을 기반으로 한 다양한 장치에서도 실행하고 관련 App을 개발할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐만 아니라 .NET/C#차원을 넘어 다양한 언어와 개발보조를 위한 수많은 확장기능까지 원하는 대로 설치하여 활용할 수 있습니다. 이중에서 .NET/C#개발자에게 가장 중요한 C# Dev Kit이 존재하는데 이 확장도구는 일반적인 VSCode를 .NET/C#개발을 위한 최적의 도구로 전환시켜 줌으로써 VSCode를 훌륭한 .NET/C#개발을 위한 도구로서 활용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C# Dev Kit에 관한 더 자세한 사항은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://devblogs.microsoft.com/visualstudio/announcing-csharp-dev-kit-for-visual-studio-code/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devblogs.microsoft.com/visualstudio/announcing-csharp-dev-kit-for-visual-studio-code/&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode는 엄밀히 말해 범용으로 다양하게 활용될 수 있지만 현재 개발분야에서 가장 강력한 지원체계를 갖고 있는 분야가 바로 Web개발입니다. 아직까지는 web분야외에 mobile이나 desktop분야에서 해당 platform의 전용개발도구에 밀리는 경향이 있지만&amp;nbsp; 그럼에도 불구하고 전 세계에서 가장 인기 있는 code editor로 자리 잡고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) GitHub Codespaces&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Codespaces는 VSCode에 기반한 개발환경으로서 겉으로 보이에는 이 둘 사이에 거의 차이점이 없어 보일 수 있습니다. 그러나 Web상에서 구동된다는 근본적인 이점을 가지고 있으므로 web browser가 있고 internet에 접속할 수만 있다면 어디서든 접근가능한 Cloud개발을 경험해볼 수 있습니다. Git repository와 내장된 command-line interface 및 확장기능 역시 그대로 지원하므로 VSCode와 동일하게 code를 편집하고 실행할 수 있으며 다양한 device상에서 test 역시 가능합니다. 하지만 무료인 VSCode와는 달리 정말 쓸모 있는 code editor로서 유용하게 사용하려면 license비용을 지불해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GitHub Codespaces에 관한 더 자세한 사항은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://github.com/features/codespaces&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/features/codespaces&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3)&amp;nbsp;Visual&amp;nbsp;Studio&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio는 desktop뿐만 아니라 Web분야까지 다양한 유형의 application을 개발할 수 있는 IDE라고 할 수 있습니다. 그러나 현재는 Windows운영체제에 한정되어 있으므로 macOS나 Linux상에서는 사용할 수 없다는 단점이 있습니다. 물론 VS2026을 사용해 cross-platform을 위한 Android와 iOS의 mobile app개발을 시작할 수 있지만 각각의 platform을 위한 compile을 위해서는 여전히 해당 platform상에서의 개발환경을 필요로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래 macOS에서 .NET을 위한 Visual Studio가 존재했었습니다. 하지만 .NET 8부터 더 이상 공식적으로 지웒하지 않게 되었으며 현재로서는 Visual Studio가 아닌 macOS용 VSCode를 사용하는 것이 대안으로 제시되고 있습니다. 또는 Paralles와 같은 가상화를 통해 Windows용 Visual Studio를 사용하는 방법도 있습니다. macOS용 Visual Studio의 지원종료에 관한 내용은 아래 link에서 확인할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://devblogs.microsoft.com/visualstudio/visual-studio-for-mac-retirement-announcement/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devblogs.microsoft.com/visualstudio/visual-studio-for-mac-retirement-announcement/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) Windows에서의 Linux Application개발&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서 Linux를 대상으로한대상으로 한 Application개발에는 WSL를 빼놓을 수 없습니다. WSL(Windows Subsystem for Linux)은 개발자들이 cross-platform개발을 위해 사용할 수 있는 가장 강력한 도구라 할 수 있습니다. 가상화나 별도의 OS를 설치할 필요 없이 Windows안에서 일반적인 Linux환경을 직접 구동시킬 수 있는 것인데 이를 통해 VS2026과 같은 Windows작업환경을 벗어나지 않으면서 Linux를 대상으로한 .NET Application을 직접 test 하고 build 하고 debuging 할 수 있습니다. 즉, Windows상에서 filesystem이나 대소문자구분, 권한, 경로문자와 같은 Linux의 고유한 특징 및 기능 등을 사전에 검토하여 Linux호환 .NET Code를 작성하는 것이 가능한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL자체가 Linux kernel을 기반으로 하는 Linux를 구동시키는 것이기 때문에 .NET Application을 실제 Linux상에서 실행시키는 것이라 볼 수 있으며 이는 Linux를 대상으로 배포하고자 하거나 Linux Shell에서 Application의 동작을 직접 확인해 보고자 할 때 유용하게 사용할 수 있습니다. 실제 Linux와 동일한 환경이므로 curl, grep, awk, sed, bash과 같은 Linux개발도구를 직접 사용할 수 있고 심지어 다른 개발도구나 의존성을 설치하기 위한 apt 등의 package manager를 사용할 수도 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Windows상에서 WSL을 설치하는 자세한 방법은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows/wsl/install&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/windows/wsl/install&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) .NET 10의 지원 Platform&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 10은 아래 Platform을 지원하고 있으므로 해당 Platform을 대상으로 한 Application을 개발하고 배포할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.7209%;&quot;&gt;OS&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%;&quot;&gt;Version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.7209%;&quot;&gt;Windows&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%;&quot;&gt;Windows&amp;nbsp;10(1607&amp;nbsp;이후),&amp;nbsp;Windows&amp;nbsp;11(22000&amp;nbsp;이후),&amp;nbsp;Windows&amp;nbsp;Server(2012R2&amp;nbsp;SP1&amp;nbsp;이후),&amp;nbsp;Nano&amp;nbsp;Server(2019&amp;nbsp;이후),&amp;nbsp;Microsoft&amp;nbsp;Surface&amp;nbsp;Pro&amp;nbsp;11,&amp;nbsp;Surface&amp;nbsp;Laptop&amp;nbsp;7과&amp;nbsp;같은&amp;nbsp;Windows&amp;nbsp;ARM기기&amp;nbsp;지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.7209%;&quot;&gt;Linux&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%;&quot;&gt;Alpine&amp;nbsp;Linux&amp;nbsp;3.22,&amp;nbsp;Azure&amp;nbsp;Linux&amp;nbsp;3.0,&amp;nbsp;CentOS&amp;nbsp;Stram&amp;nbsp;9/10,&amp;nbsp;Debian&amp;nbsp;12/13,&amp;nbsp;Fedora&amp;nbsp;42,&amp;nbsp;openSUSE&amp;nbsp;Leap&amp;nbsp;15.6,&amp;nbsp;Redhat&amp;nbsp;Linux&amp;nbsp;9/10,&amp;nbsp;SUSE&amp;nbsp;Enterprise&amp;nbsp;Linux&amp;nbsp;15.6,&amp;nbsp;Ubuntu&amp;nbsp;22.04/24.04/25.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.7209%;&quot;&gt;Android&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%;&quot;&gt;최소&amp;nbsp;API&amp;nbsp;21&amp;nbsp;이상,&amp;nbsp;Version&amp;nbsp;12,&amp;nbsp;12.1,&amp;nbsp;13,&amp;nbsp;14,&amp;nbsp;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.7209%;&quot;&gt;iOS&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%;&quot;&gt;iOS&amp;nbsp;12.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.7209%;&quot;&gt;Mac&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%;&quot;&gt;macOS&amp;nbsp;14/15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;지원가능한 OS 및 Version에 관한 가장 최신의 정보는 아래 link를 참고해 주시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://github.com/dotnet/core/blob/main/release-notes/10.0/supported-os.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/dotnet/core/blob/main/release-notes/10.0/supported-os.md&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한&amp;nbsp;.NET의&amp;nbsp;모든&amp;nbsp;version은&amp;nbsp;Windows의&amp;nbsp;자동&amp;nbsp;Update를&amp;nbsp;통해&amp;nbsp;최신상태를&amp;nbsp;유지할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) VS2026 내려받고 설치하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 .NET개발자들이 사용하는 가장 주된 개발도구라 함은 단연코 Visual Studio라고 할 수 있습니다. 같이 사용하게 될 VSCode는 범용이지만 Visual Studio는 오로지 .NET을 위한 전용의 개발도구라 할 수 있습니다. 향후 만들게 될 많은 예제는 VSCode만으로도 작성이 가능하겠지만 가능하다면 Visual Studio도 같이 사용해 볼 것을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio중에서 가장 최신의 version은 2025년 11월 출시한 Visual Studio 2026(이하 VS2026)이며 .NET10을 공식적으로 지원하는 유일한 개발도구이기도 합니다. Visual Studio는 Professional, Enterprise와 같이 여러 version이 존재하는데 2014년 10월부터 학생이나 open source 기여자, 개인에 한해 무료로 제공되고 있는 Community Edition도 존재합니다. Community는 Professional과 거의 비슷한 기능을 제공하면서도 무료로 배포되고 있으므로 학습을 위한 목적으로 가장 알맞은 Visual Studio version이라 할 수 있습니다. 향후 예제를 만드는 경우에도 Community Edition을 사용할 것이며 그중에서 가장 최신 version인 Visual Studio 2026를 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026은 아래 link를 통해 내려받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://visualstudio.microsoft.com/downloads/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://visualstudio.microsoft.com/downloads/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내려받은 file을 실행하고 잠시 기다리면 다음과 같은 화면을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8qM55/dJMcagKCF0r/6QI2vWHld2MfYXfK2Zew9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8qM55/dJMcagKCF0r/6QI2vWHld2MfYXfK2Zew9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8qM55/dJMcagKCF0r/6QI2vWHld2MfYXfK2Zew9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8qM55%2FdJMcagKCF0r%2F6QI2vWHld2MfYXfK2Zew9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 화면의 Workloads에서 학습에 필요한 아래 항목을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;① ASP.NET and web development &lt;br /&gt;②&amp;nbsp;.NET&amp;nbsp;disktop&amp;nbsp;development&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBBIWc/dJMcaawSbXq/wnIOOuymtSPn0GQSBkazzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBBIWc/dJMcaawSbXq/wnIOOuymtSPn0GQSBkazzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBBIWc/dJMcaawSbXq/wnIOOuymtSPn0GQSBkazzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBBIWc%2FdJMcaawSbXq%2FwnIOOuymtSPn0GQSBkazzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 기본적인 web개발을 위한 것이며 2번은 console app을 포함한 일반적인 응용 program개발을 위한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 항목을 선택한 뒤 Install button을 click 하고 관련항목이 설치될 때까지 잠시 기다려 줍니다. 설치가 완료되고 처음 VS2026을 실행하면 다음과 같이 login화면이 나오게 되는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJt1Y/dJMcaaDDICM/DDk5MtiRdku1akRYGstF01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJt1Y/dJMcaaDDICM/DDk5MtiRdku1akRYGstF01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJt1Y/dJMcaaDDICM/DDk5MtiRdku1akRYGstF01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJt1Y%2FdJMcaaDDICM%2FDDk5MtiRdku1akRYGstF01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;971&quot; height=&quot;495&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Microsoft계정이 있다면 해당 계정을 통해 login하면 되고 없다면 새로운 계정을 생성할 수 있습니다.(굳이 login을 하지 않더라도 하단의 'Skip and add accounts later.'를 선택하면 별도의 login 없이 VS2026을 사용할 수는 있으나 기능의 완전한 사용을 위해 login을 권장합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 과정을 넘기면 그 뒤로 기본적인 환경설정화면이 나오게 되는데 여기서는 Development Settings에 C#이나 General을 선택하고 Color theme부분에는 원하는 색상의 theme를 선택합니다. 모든 설정이 완료되고 나면 'Start Visual Studio'를 click 해 VS2026을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) VSCode 내려받고 설치하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode는 지난 몇년동안 가장 빠르게 발전한 code editor일 것입니다. 만약 자신의 Coding작업에 VS2026을 사용하기로 결정했다 하더라도 VSCode를 반드시 사용해 보길 권장합니다. 실제 어떤 걸 사용할지는 Visual Studio나 VSCode를 모두 사용해 본 다음에 결정해도 충분할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode를 설치하고 사용하기 위해서는 몇가지 사전 절차가 필요한데 가장 중요한 VSCode부터 내려받고 설치하는 것으로 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode는 아래 link에서 내려받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://code.visualstudio.com/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode는 내려받고 설치가능한 version으로 안정화 version과 insider version을 제공하고 있는데 insider는 다음 version에 대한 미리 보기로 update주기가 잦으며 최신의 VSCode를 사용할 수 있지만 대신 안정성은 안정화 version에 비해 떨어질 것입니다. 특별한 경우가 아니라면 안정화 version을 내려받는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode의 설치에 대한 자세한 guide는 아래 link에서 찾아 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/setup/setup-overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://code.visualstudio.com/docs/setup/setup-overview&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode와 함께 .NET SDK도 설치할 필요가 있습니다. 가장 최신 version은 .NET 10으로 만약 위에서 VS2026을 설치했다면 이미 .NET 10 SDK가 설치되어 있을 것이므로 이 과정은 넘어가도 좋습니다. 만약 그렇지 않다면 아래 link에서 .NET 10 SDK를 내려받아 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/en-us/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dotnet.microsoft.com/en-us/download&lt;/a&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;경우에 따라 .NET8, .NET9, .NET10등 다수의 .NET version이 설치되어 있을 수 있습니다. 이러한 경우는 아무런 문제가 되지 않으며 project를 build 할대 어떤 걸 사용할지 직접 정할 수 있습니다. 참고로 .NET8, .NET9, .NET10 version이 2025 이후로도 계속 지원되는 version에 해당합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK를 설치하고 나면 VSCode를 위해 'C# Dev Kit'확장기능을 설치합니다. 이를 위해 VSCode를 실행하고 Extensions를 선택하거나 View -&amp;gt; Extensions menu를 선택하고 'C#'을 입력하면 확장기능목록에 C# Dev Kit이 나오게 됨을 알 수 있습니다. 여기서 해당 항목을 선택해 확장기능을 설치합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AaQlT/dJMcab3COjw/Ks21wzI954YB7xjK87wKWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AaQlT/dJMcab3COjw/Ks21wzI954YB7xjK87wKWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AaQlT/dJMcab3COjw/Ks21wzI954YB7xjK87wKWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAaQlT%2FdJMcab3COjw%2FKs21wzI954YB7xjK87wKWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 다른 확장기능 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode에서 사용가능한 수많은 확장기능 중에는 'C# Dev Kit' 이외에 C#개발에 도움을 줄 수 있는 다른 확장기능이 존재합니다. 기본적으로 아래와 같은 확장기능을 추가로 설치하면 C#을 위한 VSCode로서 그 효율성을 향상할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;C#&amp;nbsp;Dev&amp;nbsp;Kit&amp;nbsp;(ms-dotnettools.csdevkit)&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;Microsoft의&amp;nbsp;공식&amp;nbsp;C#확장기능으로서&amp;nbsp;C#/.NET개발에서는&amp;nbsp;필수입니다.&amp;nbsp;Solution&amp;nbsp;Explorer를&amp;nbsp;통해&amp;nbsp;Code를&amp;nbsp;관리를&amp;nbsp;지원하며&amp;nbsp;통합된&amp;nbsp;단위&amp;nbsp;TEST&amp;nbsp;탐색&amp;nbsp;및&amp;nbsp;실행기능으로&amp;nbsp;Code를&amp;nbsp;test할&amp;nbsp;수도&amp;nbsp;있습니다.&amp;nbsp;이&amp;nbsp;밖에&amp;nbsp;C#개발에&amp;nbsp;필요한&amp;nbsp;다양한&amp;nbsp;기능을&amp;nbsp;지원하고&amp;nbsp;있으므로&amp;nbsp;C#개발경험을&amp;nbsp;향상시킬&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;C#&amp;nbsp;(ms-dotnettools.csharp)&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;C#에 특화된 기능을 제공하기 위한 것으로 IntalliSense와 code탐색, debugging기능 지원등을 통해 더 빠르고 안정적인 C#개발경험을 제공합니다. 'C# Dev Kit'을 설치했다면 해당 확장기능은 따로 설치할 필요 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;IntelliCode&amp;nbsp;for&amp;nbsp;C#&amp;nbsp;Dev&amp;nbsp;Kit&amp;nbsp;(ms-dotnettools.vscodeintellicode-csharp)&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;C#(TypeScript나&amp;nbsp;JavaScript등&amp;nbsp;기타&amp;nbsp;다른&amp;nbsp;언어&amp;nbsp;포함)언어를&amp;nbsp;위한&amp;nbsp;AI&amp;nbsp;지원&amp;nbsp;개발기능을&amp;nbsp;제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;ilspy-vscode&amp;nbsp;(icsharpcode.ilspy-vscode)&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;MSIL로&amp;nbsp;구성된&amp;nbsp;assembly를&amp;nbsp;decompile하기&amp;nbsp;위한&amp;nbsp;것으로&amp;nbsp;가장&amp;nbsp;최신의&amp;nbsp;.NET뿐만&amp;nbsp;아니라&amp;nbsp;.NET&amp;nbsp;Framework,&amp;nbsp;,NET&amp;nbsp;Core,&amp;nbsp;.NET&amp;nbsp;Standard등에서도&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 VS확장기능은 VSCode안에서 뿐만 아니라 Terminal에서도 아래 명령을 통해 설치할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.3023%;&quot;&gt;명령&lt;/td&gt;
&lt;td style=&quot;width: 65.6977%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.3023%;&quot;&gt;code&amp;nbsp;--list-extensions&lt;/td&gt;
&lt;td style=&quot;width: 65.6977%;&quot;&gt;설치된&amp;nbsp;확장기능&amp;nbsp;나열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.3023%;&quot;&gt;code&amp;nbsp;--install-extension&amp;nbsp;&amp;lt;/extension&amp;nbsp;id&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 65.6977%;&quot;&gt;지정한&amp;nbsp;id의&amp;nbsp;확장기능&amp;nbsp;설치&amp;nbsp;(ex&amp;nbsp;:&amp;nbsp;code&amp;nbsp;--install-extension&amp;nbsp;ms-dotnettools.csdevkit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.3023%;&quot;&gt;code --uninstall-extension&lt;/td&gt;
&lt;td style=&quot;width: 65.6977%;&quot;&gt;지정한&amp;nbsp;id의&amp;nbsp;확장기능&amp;nbsp;삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. NET&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 개발초기 단계에서부터 현재에 이르기까지 .NET Framework나 .NET Standard, .NET Core와 같은 platform의 등장을 통해 발전해 왔었음을 알 수 있습니다. .NET Framework시기부터 함께해 온 개발자라면 .NET의 역사에 익숙하겠지만 그렇지 않은 개발자도 있을 것입니다. 과거가 중요하지 않다는 말은 아니지만 굳이 여기서 .NET의 역사를 되짚어 볼 생각은 없습니다. 만약 이러한 것이 관심이 있다면 아래 Link를 통해 간략히 확인해 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 2. C#&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) .NET의 지원체계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET의 생명주기와 관련된 version은 크게 3가지로 나누어 볼 수 있습니다. 하나는 LTS(Long-Term Support)이고 다른 하나는 STS(Standard-Term Support)이며 나머지 하나는 Preview입니다. 그리고 이들 각각은 아래와 같은 지원가능한 주기를 갖고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;Release&lt;/td&gt;
&lt;td style=&quot;width: 82.2093%;&quot;&gt;Support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;LTS&lt;/td&gt;
&lt;td style=&quot;width: 82.2093%;&quot;&gt;GA(General&amp;nbsp;Availability)이후&amp;nbsp;3년간&amp;nbsp;또는&amp;nbsp;다음&amp;nbsp;LTS가&amp;nbsp;발표된&amp;nbsp;이&amp;nbsp;후&amp;nbsp;1년중&amp;nbsp;더&amp;nbsp;긴쪽으로&amp;nbsp;지원되는&amp;nbsp;version입니다.&amp;nbsp;지원기간&amp;nbsp;동안&amp;nbsp;보안과&amp;nbsp;안정성에&amp;nbsp;관한&amp;nbsp;patch가&amp;nbsp;지속적으로&amp;nbsp;이루어집니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;STS&lt;/td&gt;
&lt;td style=&quot;width: 82.2093%;&quot;&gt;GA이후 2년간 또는 다음 STS 나 LTS가 발표된 이 후 1년중 더 긴쪽으로 지원되는 version입니다. 지원기간 동안 보안과 안정성에 관한 patch가 지속적으로 이루어집니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;Preview&lt;/td&gt;
&lt;td style=&quot;width: 82.2093%;&quot;&gt;공개 TEST용으로 사전에 신규기능이나 기술에 접근해 보고자 하는 개발자를 위한 version입니다. Preview자체가 실제 지원되는 version은 아니지만 어떤 Preview 또는 RC(Release Candidate)의 경우 Go Live로 결정되어 Microsoft에서 정식 제품으로 지원되는 경우도 있습니다. 그러나 만약 Preview를 통해 Application을 개발한 뒤에 바로 Go Live로 전환되었다 하더라도 가능한한 빠른 시간안에 migration을 진행해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;STS는 경우에 따라 'Current'라고 표기되기도 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;2025년 9월 Microsoft는 현재 STS의 지원기간이 18개월에서 24개월로 늘어날 수 있음을 공지한 바 있습니다. 관련된 내용은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/&lt;/a&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1763366144300&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;.NET STS releases supported for 24 months - .NET Blog&quot; data-og-description=&quot;.NET STS releases will be supported for 24 months&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2jodR/hyZNUy7Gsf/xz6ub0srClu736orKwkVN1/img.jpg?width=2500&amp;amp;height=1002&amp;amp;face=0_0_2500_1002,https://scrap.kakaocdn.net/dn/b77dUQ/hyZNRPUCq8/iUWqL6sfH80wKszr9gG4HK/img.jpg?width=2500&amp;amp;height=1002&amp;amp;face=0_0_2500_1002&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/dotnet-sts-releases-supported-for-24-months/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2jodR/hyZNUy7Gsf/xz6ub0srClu736orKwkVN1/img.jpg?width=2500&amp;amp;height=1002&amp;amp;face=0_0_2500_1002,https://scrap.kakaocdn.net/dn/b77dUQ/hyZNRPUCq8/iUWqL6sfH80wKszr9gG4HK/img.jpg?width=2500&amp;amp;height=1002&amp;amp;face=0_0_2500_1002');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;.NET STS releases supported for 24 months - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET STS releases will be supported for 24 months&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;가능한한 주기적인 Patch를 통해 .NET의 최신상태를 유지하시기 바랍니다. 예를 들어 .NET Runtime 10.0.0 version이 설치되어 있고 이후 10.0.1 version이 release 되었다면 지속적인 지원을 받기 위해 10.0.1을 설치하는 것이 좋습니다. 이러한 update는 매달 둘째 주 화요일인 Patch Tuesday에 release 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STS와 LTS 간 지원기간에 대한 구분을 좀 더 명확히 확인하기 위해 아래 표를 참고해 주시기 바랍니다. 표에서 붉은색은 3년 녹색은 2년간의 지원기간을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AoNPK/dJMcafSv1Oc/WjtndVhbq5QfTsg3Bjcpu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AoNPK/dJMcafSv1Oc/WjtndVhbq5QfTsg3Bjcpu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AoNPK/dJMcafSv1Oc/WjtndVhbq5QfTsg3Bjcpu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAoNPK%2FdJMcafSv1Oc%2FWjtndVhbq5QfTsg3Bjcpu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1298&quot; height=&quot;137&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 10의 지원기간 동안 .NET 8과 .NET 9는 한동안 지원이 계속 이루어질 수 있습니다. 그리고 .NET 10의 지원기간안에 .NET 11이 release 될 수 있습니다. 특별한 경우가 아니면 상위 .NET version은 하위 .NET version에 대한 호환성을 갖고 있으므로 만약 .NET 8이나 .NET 9로 개발된 Application이 있다면 Microsoft로부터의 지속적인 지원을 받을 수 있게 이를 현재 가장 최신 version의 .NET으로 전환할 것을 권장합니다. 그리고 이러한 전환방식은 향후 .NET 11이나 .NET 12가 발표되는 경우도 마찬가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) .NET의 지원체계와 관련된 용어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET의 생명주기는 사용되는 용어를 통해 그 지원 수준을 파악할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;Release&lt;/td&gt;
&lt;td style=&quot;width: 87.3256%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;Preview&lt;/td&gt;
&lt;td style=&quot;width: 87.3256%;&quot;&gt;Preview라는 말이 붙으면 공식적으로 지원되지 않는 version으로 이해해야 합니다. 따라서 실제 Product수준의 Application개발대신 TEST나 사전 검토용으로만 사용되어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;Go&amp;nbsp;Live&lt;/td&gt;
&lt;td style=&quot;width: 87.3256%;&quot;&gt;GA동안에만&amp;nbsp;지원되는&amp;nbsp;것으로&amp;nbsp;그&amp;nbsp;이후에는&amp;nbsp;미지원상태로&amp;nbsp;전환됩니다.&amp;nbsp;따라서&amp;nbsp;가능한한&amp;nbsp;최신의&amp;nbsp;version으로&amp;nbsp;update해야&amp;nbsp;합니다.&amp;nbsp;보통&amp;nbsp;Release&amp;nbsp;Candidate가&amp;nbsp;GA에&amp;nbsp;해당합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;Active&lt;/td&gt;
&lt;td style=&quot;width: 87.3256%;&quot;&gt;현재 .NET 10이 2025년 11월에서 2028년 5월까지 Active상태에 해당합니다. 이 기간동안 매달 bug와 보안관련 수정을 포함한 release가 이루어지므로 그에 맞춰 update를 유지해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;Maintenance&lt;/td&gt;
&lt;td style=&quot;width: 87.3256%;&quot;&gt;생명주기의 마지막 6개월동안의 기간을 말하는 것으로 이때는 오로지 보안관련 수정만 이루어집니다. 본래 .NET 10의 지원기간은 2028년 11월까지인데 이 중 마지막 6개월을 제외한 기간만 Active로 잡히는 이유입니다. 또한 .NET 8과 .NET 9역시 조만간 maintenance상태로 진입할 것이므로 관련 Application Project가 존재한다면 이 기간동안 최신의 .NET Version으로 전환해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;EOL&lt;/td&gt;
&lt;td style=&quot;width: 87.3256%;&quot;&gt;End of life로 지원이 더이상 이루어지지 않는 시기입니다. .NET 10은 2028년 11월이 넘어가게 되면 EOL에 도달하게 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) EOL&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;End of life 또는 End of support라고 하는 EOL은 bug나 보안 patch, 기술지원등이 Microsoft에서 더 이상 지원되지 않는 것을 말합니다. .NET 10이 2025년 11월에 발표되면서 .NET 8을 제외한 이하 모든 .NET은 EOL에 도달하게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88372%;&quot;&gt;.NET&amp;nbsp;8&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;2025년&amp;nbsp;11월&amp;nbsp;기준&amp;nbsp;Active&amp;nbsp;상태이며&amp;nbsp;2026년&amp;nbsp;5월&amp;nbsp;maintenance상태로,&amp;nbsp;2026년&amp;nbsp;11월에&amp;nbsp;EOL에&amp;nbsp;도달하게&amp;nbsp;됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88372%;&quot;&gt;.NET&amp;nbsp;9&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;2025년&amp;nbsp;11월&amp;nbsp;기준&amp;nbsp;Active&amp;nbsp;상태이며&amp;nbsp;2026년&amp;nbsp;5월&amp;nbsp;maintenance상태로,&amp;nbsp;2026년&amp;nbsp;11월에&amp;nbsp;EOL에&amp;nbsp;도달하게&amp;nbsp;됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88372%;&quot;&gt;.NET&amp;nbsp;10&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;2028년&amp;nbsp;5월까지&amp;nbsp;Active&amp;nbsp;상태가&amp;nbsp;되며&amp;nbsp;그&amp;nbsp;다음&amp;nbsp;2028년&amp;nbsp;11월&amp;nbsp;EOL까지&amp;nbsp;maintenance상태에&amp;nbsp;있게&amp;nbsp;됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88372%;&quot;&gt;.NET&amp;nbsp;11&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;2026년&amp;nbsp;2월부터&amp;nbsp;2026년&amp;nbsp;11월까지&amp;nbsp;preview&amp;nbsp;상태에&amp;nbsp;있다가&amp;nbsp;그&amp;nbsp;다음부터&amp;nbsp;Active상태로&amp;nbsp;전환됩니다.&amp;nbsp;그리고&amp;nbsp;2028년&amp;nbsp;11월&amp;nbsp;EOL에&amp;nbsp;도달합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 .NET version의 현재 상태와 EOL도달시기는 아래 link를 통해 더 자세히 확인할 수 있습니다. &lt;br /&gt;&lt;a href=&quot;https://github.com/dotnet/core/blob/main/releases.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/dotnet/core/blob/main/releases.md&lt;/a&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET 8과 .NET 9의 경우에는 2026년 11월 EOL에 도달하게 되는데 해당 version을 사용하는 project자체는 계속 운영할 수 있지만 보안 update가 더 이상 제공되지 않고 기술지원도 불가능하므로 신속히 새로운 .NET version으로 전환해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) .NET Runtime과 SDK&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application을 standalone으로 build 하지 않는 한 .NET Runtime은 .NET Application을 실행하기 위해 필수로 OS에 설치되어 있어야 하는 최소한의 요구사항에 해당합니다. 반면 .NET SDK는 .NET Runtime과 함께 .NET Application을 build하기 위해 필요한 compiler 및 기타 다른 도구를 포함하고 있는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Rumtime의 versioning은 semantic versioning의 규칙을 따르고 있어서 어떠한 대규모 변화의 발생 시 주 version이 증가하며 새로운 기능 및 기타 bug 수정의 경우에는 하위 version이 증가하게 됩니다. 하지만 .NET SDK의 versioning은 semantic versioning의 규칙을 따르고 있지 않으며 주와 부 version은 항상 .NET Runtime과 동일하게 유지됩니다. 다만 그 하위에 존재하는 부수적인 version번호만 SDK의 patch에 따른 증가값을 표시하고 있는데, 해당 version번호는 초기에 100부터 시작해서 첫 번째 숫자는 새로운 기능에 대한 변화를, 나머지 2개의 숫자는 bug나 보안 patch와 같은 변화를 나타냅니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;.NET Runtime&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;.NET SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;초기상태&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SDK&amp;nbsp;Bug&amp;nbsp;수정&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.0&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Runtime&amp;nbsp;Bug&amp;nbsp;수정&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SDK&amp;nbsp;기눙&amp;nbsp;수정&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;10.0.200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) .NET 설치 관리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Runtime의 경우 주 version에 대해서는 하위 version과 무관하게 호환성을 유지합니다. 예를 들어 .NET 10.0.0을 대상으로 개발된 project라 하더라도 향후 10.0.1이나 10.0.2와 같은 version이 나오는 경우 별다른 문제없이 무난하게 version을 update할 수 있습니다. 물론 단순 version상의 문제뿐 아니라 지속적인 관리를 위해서라도 꾸준히 update를 유지해야 할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK의 경우도 마찬가지인데 초기 이전 version의 .NET SDK로 build한 project라 하더라도 향후 bug수정된 version의 SDK를 사용해 이전 project를 여전히 build할 수 있으며 심지어 주 version이 update된 11.0.100과 같은 경우에도 대부분의 경우 무리없이 판올림을 진행하여 build를 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 하위 호환성유지는 새로운 version의 .NET SDK가 설치된 경우라면 이전 version의 .NET SDK는 안전하게 삭제할 수 있다는 의미가 됩니다. 새로운 .NET SDK를 통해서도 이전 version의 모든 project를 build 할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 설치된 .NET SDK와 runtime을 보려면 아래 명령을 사용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet --info&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 SDK만 확인하려면 아래 명령을 사용하고&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet --list-sdks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Runtime이라면 아래 명령을 사용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet --list-runtimes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 특정 SDK를 삭제하려 한다면 Windows 제어판의 'Programs and Features'에서 삭제할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) .NET과 C#&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 개발 Platform이고 C#은 개발언어에 해당합니다. 또한 C#은 .NET을 위한, .NET만을 위한언어로서 .NET과 아주 밀접하게 연결되어 있다 말할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#언어가 Application으로 실행되는 과정을 보면 우선 C# code를 C# Compiler가 CIL(Common Intermediate Language) 혹은 IL Code라고 하는 형태로 compile 합니다. 그 다음 .NET Runtime의 구성요소 중 하나인 CLR(Common Language Runtime)에서 해당 IL Code를 load 한 뒤 이를 다시 JIT(Just In Time) Compiler가 실제 동작중인 Machine의 OS와 CPU가 이해할 수 있는 기계어 code로 변환하여 code를 실행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET세계에서 많이 쓰이는 VB.NET이나 F#도 마찬가지로 각각의 언어에 맞는 compiler를 가지고 있고 이들도 compile 하게 되면 공통적으로 IL Code가 만들어지는데 그다음 실행까지의 과정은 어떤 언어든지 모두 동일한 순서와 방식으로 처리됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beDmSO/dJMcaiobpiQ/L3v6Vx2Sj1NkYEBt1EdkO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beDmSO/dJMcaiobpiQ/L3v6Vx2Sj1NkYEBt1EdkO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beDmSO/dJMcaiobpiQ/L3v6Vx2Sj1NkYEBt1EdkO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeDmSO%2FdJMcaiobpiQ%2FL3v6Vx2Sj1NkYEBt1EdkO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1364&quot; height=&quot;338&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#은 언어일 뿐이므로 실제 C#없는 .NET Project는 존재할 수 있지만 .NET아닌 C#은 존재할 수 없습니다. 이론적으로 현재 C#은 개방형 표준이기 때문에 누구나 다른 platform을 위한 C# Compiler를 만들 수 있지만 실제 그렇게 만들어진 사례는 아직 존재하지 않으며 사실 그렇게 해야 할 이유도 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄밀하 말하면 .NET세계에서 C#을 꼭 사용해야 하는 것은 아닙니다. 이미 언급했듯이 C#, F#, VB.NET은 Programming언어에 물과 하며 Microsoft 역시 이들 언어 모두를 지속적으로 지원하고 있습니다. 다만 C#은 이 중에서 가장 우세한 지원쳬계에 속해 있으며 관련 자료 역시 방대하게 존재하므로 오히려 다른 언어를 사용하는 것보다 C#이 훨씬 접근성이 높다고 말할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) IL&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상술했듯 .NET은 2번의 compiler과정을 거치고 있습니다. 이 중 첫 번째가 Roslyn이라고 하는 C# compiler에서 C# Source code를 compile 하여 IL로 변환한 뒤 이를 EXE나 DLL의 Assembly형태로 저장하는 것입니다. IL code는 Assembly언어와 비슷한 구조를 가지지만 오로지 CoreCLR이라고 하는 .NET Virtual Machine상에서만 실행될 수 있습니다. 두 번째 Compile은 Runtime때 실행되는데, CoreCLR이 Assembly에서 IL Code를 load 하면 JIT Compiler가 이를 현재 Machine의 OS와 CPU가 해석할 수 있는 구조의 기계어로 compile 하게 됩니다. 그리고 이렇게 두 번의 Compile과정을 거치고 나면 비로소 Code를 실행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번의 Compile과정(C# -&amp;gt; IL -&amp;gt; Machine Code)은 언뜻 생각해 보면 불합리해 보일 수 있지만 동일한 IL Code를 가지고 Windows는 물론 Linux나 macOS든 어떠한 OS platform상에서도 실행시킬 수 있다는 아주 큰 장점을 제공해 줄 수 있습니다. Microsoft는 그저 각 OS에 맞는 CoreCLR을 만들어두기만 하면 되고, 해당 OS의 CoreCLR에서 두 번째 Compile과정을 거쳐 실행가능한 기계어로 변환될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#뿐만 아니라 기타 다른 어떠한 언어든지 관계없이 첫 단계의 Compile에서 IL Code를 생성하게 되는데 중간언어라는 특성상 해당 Code의 구조를 역해석하면 본래 언어로 만들어진 Source code로 되돌려 보는 것이 가능합니다. 실제 이것을 가능하게 만드는 여러 가지 tool도 존재하는데 JetBranins사의 DotPeek나 Microsoft사의 ILSpy 등이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 Compile은 Runtime때에 이루어지는데 이러한 방식은 OS 간 높은 실행률을 보장할 수 있지만 단점은 역시 성능입니다. 한 번의 Compile로 곧장 실행가능한 형태의 file을 만들어내는 다른 언어의 Application과는 아무래도 비교될 수밖에 없습니다. Microsoft도 이러한 문제를 인식하고 있고 따라서 이에 대한 대안으로 AOT(Ahead of time) Compile을 제시하고 있습니다. AOT에 관해서는 추후에 다시 언급하도록 하겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CoreCLR의 원래 이름은 CLR이지만 이는 .NET Framework때 Windows전용으로 사용되는 것으로 .NET이 Cross-platform인 Core시대로 접어들면서 CoreCLR로 명칭이 변경되었습니다. 전체 모든 CLR을 지칭하는 경우는 CLRs이라고 표현하기도 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET Core는 Cross-platform으로 처음 등장한때에 사용되던 용어로 현재는 .NET만 공식적인 명칭으로 사용되고 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Runtime은 Application실행되는 시점을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) .NET과 .NET Framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Framework는 2002년부터 시작한 .NET Platform의 공식명칭이었으며 그 밖에 다른 관련 Platform이 등장하였지만 현재는 이 명칭이 .NET 이라는 것으로 통일되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Framework와 .NET은 명칭이 비슷하다 보니 이를 혼재해 사용하는 경우도 있는데 이 둘은 엄밀히 다르며 다음과 같은 특징으로 나누어 볼 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6279%;&quot;&gt;.NET Framework&lt;/td&gt;
&lt;td style=&quot;width: 83.3721%;&quot;&gt;.NET Framework 4.8 / C# 8.0까지만 지원되는 것으로 향후 그 어떠한 지원계획도 존재하지 않습니다. 사실상 Windows전용의 Platform이며 오로지 기존 .NET Framework를 대상으로 한 Application을 유지관리하기 위한 목적으로만 사용해합니다. 따라서 신규 Project에 적용해서는 안됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6279%;&quot;&gt;.NET&lt;/td&gt;
&lt;td style=&quot;width: 83.3721%;&quot;&gt;C# 8.0부터 현재의 C# 14까지 지원하는 것으로 향후 유지관리동안에 신규 Project에 사용할 수 있는 Platform입니다. Windows뿐만 아니라 Linux, macOS, Android등 다양한 OS를 지원하며 기존의 .NET Framework Application을 최신 version인 .NET으로 전환하고자 하는 경우에도 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 C#과 .NET에 대한 간단한 개념들을 살펴봤습니다. 이론적인 부분은 여기까지 하기로 하고 이제 예제를 통해 실제 Application을 만들기 위한 과정을 하나씩 짚어가도록 하겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 예제 따라 하기&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 VS2026을 사용해 Console Appp정도의 간단한 Application을 만들어 보는 것으로 시작할까 합니다. 하지만 만약 Windows OS를 사용하지 않거나 VSCode만을 사용하기로 결정했다면 사실 이 과정은 건너뛰어도 괜찮습니다. 사실 앞으로의 예제는 대부분 VSCode를 사용하는 경우가 많을 테지만 기본적인 개념은 거의 동일하므로 VS2026에 대한 설명을 지나친다고 하더라도 크게 문제가 될 건 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) VS2026을 사용해 .NET Project 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 예제를 빠르게 만들어 보는 것을 목표로 하므로 일부 세부적인 설명은 생략할 수 있습니다. 그러나 생략된 설명은 추후에 자세히 설명될 것이므로 일단 설명대로 따라해 .NET Project를 생성해 보시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026을 실행한 후 첫 화면에서 왼쪽에 'Create a new project'를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tm7jl/dJMcacIhmHh/HfbMMkQPyOabLGUo0DJgn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tm7jl/dJMcacIhmHh/HfbMMkQPyOabLGUo0DJgn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tm7jl/dJMcacIhmHh/HfbMMkQPyOabLGUo0DJgn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTm7jl%2FdJMcacIhmHh%2FHfbMMkQPyOabLGUo0DJgn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;575&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'All languages'에서 C#을 선택하고 'Search for templates'에서 'console'을 입력합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Titwo/dJMb99Lyjik/koNF6ktnrEc7Eulofdc701/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Titwo/dJMb99Lyjik/koNF6ktnrEc7Eulofdc701/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Titwo/dJMb99Lyjik/koNF6ktnrEc7Eulofdc701/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTitwo%2FdJMb99Lyjik%2FkoNF6ktnrEc7Eulofdc701%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;575&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하단 template목록에서 가장 처음항목인 'Console App'을 선택하고 'Next'를 Click 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pRBRg/dJMcadAmEAk/KSKmKVJD6roT3Oma5r47kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pRBRg/dJMcadAmEAk/KSKmKVJD6roT3Oma5r47kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pRBRg/dJMcadAmEAk/KSKmKVJD6roT3Oma5r47kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpRBRg%2FdJMcadAmEAk%2FKSKmKVJD6roT3Oma5r47kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;575&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project name에서 임의의 Project명을 입력하고 해당 Soluction(Project)가 저장될 경로를 Location에서 지정한 뒤 'Next'를 Click합니다.(저장위치와 Project명, Solution명은 자유롭게 설명할 수 있습니다. 예제에서는 기본값 그대로 사용하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Framework에서 '.NET 10 (Long Term Support)'를 설정합니다. 아마 대부분 기본적으로 선택가능한 '.NET 10'항목 하나만 존재할 테지만 만약 다른 version의 .NET SDK가 설치되어 있다면 .NET 9.0과 같이 다른 선택가능한 항목이 존재할 수 있습니다. 여기서 Enable container support, Do not use top-level statements 등 다른 설정은 그대로 남겨두고 'Create'를 Click한뒤 잠시 기다리면 다음과 같은 화면을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pByER/dJMcaihqq7f/yfKPutG48CqUCatzJInyRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pByER/dJMcaihqq7f/yfKPutG48CqUCatzJInyRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pByER/dJMcaihqq7f/yfKPutG48CqUCatzJInyRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpByER%2FdJMcaihqq7f%2FyfKPutG48CqUCatzJInyRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1032&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 표시되는 code는 Program.cs file의 code로서 현재는 주석문 한 줄과 code 한 줄만이 존재하고 있습니다. Project생성 시 다른 option사항을 선택하지 않았다면 C# 9.0에서 도입된 top-level program기능을 사용하게 되므로 아주 간소화된 code만이 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Code를 아래와 같이 간단히 수정해 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1763629617802&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// See https://aka.ms/new-console-template for more information
Console.WriteLine(&quot;안녕, C#&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 예제를 Compile 하고 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기와 같이 수정한 뒤 예제를 실행하기 위해 VS2026의 상단 Menu에서 Debug -&amp;gt; Start Without Debugging을 선택합니다. 그런 뒤 잠시 기다리면 다음과 같은 Console화면이 열리면서 예제가 실행될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QQsAh/dJMcaiIsbd3/OyYWy8xKwL85w8sh0QwtA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QQsAh/dJMcaiIsbd3/OyYWy8xKwL85w8sh0QwtA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QQsAh/dJMcaiIsbd3/OyYWy8xKwL85w8sh0QwtA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQQsAh%2FdJMcaiIsbd3%2FOyYWy8xKwL85w8sh0QwtA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Console화면의 색상이나 형태는 각 PC의 설정에 따라 조금씩 다르게 표시될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태에서 임의의 key를 누르면 Console화면이 닫히고 VS2026 화면으로 되돌아갈 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Solution Explorer에서 Project(예제에서는 ConsoleApp1)를 double click 하거나 Mouse오른쪽 button을 눌러 'Edit Project File'을 선택하면 아래와 같이 Project file(*csproj)의 내용이 표시될 것입니다. 아래 설정에 따라 현재 예제 Project는 .NET 10을 target으로 하고 있음을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1763713657858&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;대게 Project file의 명칭은 Project명을 따라가므로 예제와 같은 경우라면 Project file명은 'ConsoleApp1.csproj'가 될 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Visual Studio에서 Debugger사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026에서 Project를 실행하는 경우 상황에 따라 Debugger를 사용할지 여부를 선택할 수 있습니다. 만약 debugging이 필요하지 않은 경우라면 Debugger를 사용하지 않는 편이 훨씬 더 나을 것입니다. 왜냐하면 Debugger를 사용하게 되면 더 많은 Resource를 사용하면서 속도도 Debugger를 사용하지 않는 것보다 더 느리게 실행될 것이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Debugger를 사용하게 되면 단 하나의 Project만 실행할 수 있다는 단점도 존재합니다. 만약 하나이상의 Project를 Debugger를 사용하면서 동시에 실행해야 한다면 그만큼 VS2026을 중복으로 실행해 각각의 Project를 개별적으로 실행해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 현재 Project에서 Debugger사용하도록 Project를 실행하면 Console화면이 표시되었다가 결과를 확인하기도 전에 금방 닫혀버리는 경우가 있습니다. 이런 경우 VS2026의 Tools -&amp;gt; Options -&amp;gt; Debugging -&amp;gt; General Menu를 선택하고 오른쪽 설정항목에서 'Automatically close the console when debugging stops'부분이 설정되어 있는지 확인합니다. 만약 이 설정이 설정상태에 있다면 Check표시를 지워 설정을 해제하도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btropG/dJMcacIhK1U/0uVigl5KgQaCwHc7D3ARCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btropG/dJMcacIhK1U/0uVigl5KgQaCwHc7D3ARCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btropG/dJMcacIhK1U/0uVigl5KgQaCwHc7D3ARCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtropG%2FdJMcacIhK1U%2F0uVigl5KgQaCwHc7D3ARCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;671&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) VS2026에서 Solution에 Project추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대다수 Project는 단일 Solution에 단일 Project만이 존재하는 경우가 거의 없습니다. 필요에 따라 또는 용도에 따라 여러 가지 Project로 나뉘는 경우가 많이 때문에 Solution에 Project가 추가되는 경우는 흔하다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026에서 Solution에 Project를 추가하기 위해 File -&amp;gt; Add -&amp;gt; New Project...를 선택합니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;비슷한 Menu로 File -&amp;gt; New -&amp;gt; Project/Solution이 있으나 이 기능은 새로운 Project와 Solution을 추가하는 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Add a new project 화면에서 Recent project templates밑에 Console App을 선택하고 Next를 눌러줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD92Ah/dJMcabWV5dK/fWS0k6d9SDJYFXrW3iVzOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD92Ah/dJMcabWV5dK/fWS0k6d9SDJYFXrW3iVzOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD92Ah/dJMcabWV5dK/fWS0k6d9SDJYFXrW3iVzOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD92Ah%2FdJMcabWV5dK%2FfWS0k6d9SDJYFXrW3iVzOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;575&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Configure your new project 화면에서는 사용할 Project이름과 Project가 생성될 위치를 지정합니다. (예제에서는 기본값 그대로 사용하였습니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0oMbc/dJMcaiuW2ZI/IRjnMCIN6bTf5h3iVKNN41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0oMbc/dJMcaiuW2ZI/IRjnMCIN6bTf5h3iVKNN41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0oMbc/dJMcaiuW2ZI/IRjnMCIN6bTf5h3iVKNN41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0oMbc%2FdJMcaiuW2ZI%2FIRjnMCIN6bTf5h3iVKNN41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;575&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Additional information 화면에서는 현재 표시된 상태 그대로 사용하되 'Do net use top-level statements'만 check 해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v8ele/dJMcahbMwaq/J95teTneEIGW3DSxkUxzk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v8ele/dJMcahbMwaq/J95teTneEIGW3DSxkUxzk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v8ele/dJMcahbMwaq/J95teTneEIGW3DSxkUxzk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv8ele%2FdJMcahbMwaq%2FJ95teTneEIGW3DSxkUxzk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;575&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이제 Create를 눌러 Project를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Project의 Program.cs를 열어보면 이번에는 아래와 같이 Project명과 동일한 Namespace가 선언되었고 Program internal class 및 내부에 args 매개변수를 가진 정적 Main method가 포함되어 있는 걸 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764226378565&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(&quot;Hello, World!&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태에서 Project의 Namespace를 확인할 수 있도록 이전과 동일한 구문을 아래와 같이 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764226378566&quot; class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;static void Main(string[] args)
{
    string namespaceName = typeof(Program).Namespace ?? &quot;이름없음&quot;;
    Console.WriteLine(namespaceName);
    Console.WriteLine(&quot;Hello, World!&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026의 Solution Explorer에서 ConsoleApp1 Solution을 선택한 뒤 mouse오른쪽 button을 눌러 'Configure Startup Projects...'항목을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 'Solution 'ConsoleApp1' Property Pages화면에서 Startup Project를 Current selection으로 변경한 뒤 OK를 눌러 설정을 반영합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI81xI/dJMcaaqb6jO/W8a3YMcm7rJ1DXA4ZXEKtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI81xI/dJMcaaqb6jO/W8a3YMcm7rJ1DXA4ZXEKtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI81xI/dJMcaaqb6jO/W8a3YMcm7rJ1DXA4ZXEKtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI81xI%2FdJMcaaqb6jO%2FW8a3YMcm7rJ1DXA4ZXEKtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;544&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 뒤 다시 Solution Explorer에서 ConsoleApp2 project를 선택(또는 Project내부에서 임의의 folder나 file을 선택해도 동일)하면 해당 Project명이 진하게 변경됨으로써 시작 Project로 설정되었음을 확인할 수 있습니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;Solution Explorer에서 시작 Project로 바꾸고자 하는 Project명을 Mouse오른쪽 button으로 눌러 'Set as Startup Project'항목을 선택해도 동일한 기능을 수행할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 Project를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDCvUJ/dJMcaawXMyd/DkBkamiyRXrNnAEs3mVNU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDCvUJ/dJMcaawXMyd/DkBkamiyRXrNnAEs3mVNU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDCvUJ/dJMcaawXMyd/DkBkamiyRXrNnAEs3mVNU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDCvUJ%2FdJMcaawXMyd%2FDkBkamiyRXrNnAEs3mVNU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 Project를 실행하는 경우 해당 Application은 [Project명]\bin\Debug\net10.0 folder에 존재하는 Project file을 실행하게 됩니다. 하지만 VSCode(dotnet CLI)에서 Project를 실행하는 경우에는 VS20265과 다른 동작을 취하게 되므로 주의해야 합니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;Solution을 생성한 뒤 Solution file확장자를 보면 기존의 sln에서 slnx로 변경되어 있음을 알 수 있습니다. sln은 해당 Solution만의 고유한 방식으로 장황하며 읽기 어려운 구조로 되어 있고 GUID(Globally Unique Identifiers)를 사용해 Solution에 포함된 다른 Project나 Component를 참조하고 있습니다. 하지만 slnx는 XML을 사용하는 방식으로 간단하면서도 상대적으로 읽기 쉬운 구조를 갖고 있습니다. 아마도 VS2026에서는 slnx가 기본 Solution file로 설정되어 있을 테지만 그렇지 않거나 다른 file을 기본 Solution file로 설정해야 한다면 Tools -&amp;gt; Options -&amp;gt; Projects and Solutions의 설정화면에서 General부분의 설정을 바꿔줄 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) bin과 obj folder&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bin과 obj는 compiler에 의해 자동으로 생성되는 folder로서 obj는 각 source file을 compile 한 하나의 object file을 포함하고 있으며 bin은 App이나 class library로서 실행가능한 binary file을 포함하고 있는 folder입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 해당 folder의 구조나 내부에 포함하고 있는 file들을 세부적으로 모두 알아야 할 필요는 없지만 어쨌건 상기 2개의 folder와 file은 Compiler가 Project를 build 할 때마다 임시적으로 생성한다는 점을 기억하시기 바랍니다. 원한다면 이들 folder를 삭제할 수 있지만 Project를 build 하는 과정을 거칠 때마다 folder는 다시 생성됩니다. 대개의 경우 bin과 obj를 가지고 뭔가를 해야 하는 경우는 거의 없기 때문에 현재는 이런 것이 있다는 정도만 알고 넘어가면 충분합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;br /&gt;위 folder안에 여러 file 중 '.g.'와 같이 중간에 g가 붙은 file을 볼 수 있는데 여기서 'g'는 generated를 의미합니다. 해당 파일은 build과정에서 새롭게 만들어지므로 file을 삭제하거나 file명을 변경하는 것은 무의미하며 그렇게 해야 할 이유도 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bin과 obj는 수동적으로 삭제할 수 있지만 VS2026의 'Solution Explorer'에서 'clean'을 통해 삭제할 수도 있습니다. 이렇게 하려면 Project를 mouse오른쪽 button으로 눌러 'Clean'항목을 선택하기만 하면 됩니다. 이러한 동작은 dotnet CLI에서 'dotnet clean'명령한 것과 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) Top-level Programs&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 .NET Project를 신규로 생성하는 경우 처음 시작되는 code는 다소 장황한 느낌이 있었습니다. 심지어 비교적 단순한 Console App을 생성하는 경우에도 한줄짜리 간단한 message를 출력하는것이 전부인데 간단한 동작과는 다르게 몇줄짜리 code가 기본으로 생성되곤 했습니다. 하지만 이러한 방식은 .NET 6이후부터 compiler가 필수적인 code를 대신 처리해 줌으로서 기본으로 시작되는 code의 양이 크게 간소화되기 시작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5 또는 그전에서나 혹은 그 이후의 .NET Version이라 하더라도 Project생성 시 'Do net use top-level statements'항목을 선택했다면 Program.cs는 다음과 같은 형태로 만들어지게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1763965714476&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;

namespace ConsoleApp1
{
    class Program
    {
        staic void Main(string[] args)
        {
            Console.WriteLine(&quot;Hello, World!&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 .NET 6부터 Program class와 Main method는 compiler에 의해 자동으로 생성되어 개발자가 작성한 code를 감싸는 형태로 처리하게 되므로 주석과 code구문을 포함한 단 2줄만 Program.cs에 표시하게 되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pByER/dJMcaihqq7f/yfKPutG48CqUCatzJInyRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pByER/dJMcaihqq7f/yfKPutG48CqUCatzJInyRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pByER/dJMcaihqq7f/yfKPutG48CqUCatzJInyRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpByER%2FdJMcaihqq7f%2FyfKPutG48CqUCatzJInyRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1032&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 top-level programs라는 기능은 .NET 5부터 도입된 것인데 이걸 Microsoft는 .NET 6에 와서야 Console App project의 template에 기본으로 적용하도록 하였으며 .NET 7부터 개발자의 선호도에 따라 직접 어떤 style로 시작할지 선택할 수 있도록 하였습니다. 따라서 top-level programs기능을 사용하고 싶지 않다면 VS2026의 경우 Project생성 시 'Do net use top-level statements'를 선택하면 되고 dotnet CLI를 사용하는 경우 '--use-program-main'이라는 option을 붙여주면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일반적인 경우라면 namespace는 project명과 동일하게 생성되지만 top-level programs기능을 사용하는 경우 namespace가 정의되지 않으므로 Program class는 암시적으로 이름이 없는 빈 namespace상에서 정의됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Top-level&amp;nbsp;Programs은&amp;nbsp;compile과정에서&amp;nbsp;필연적으로&amp;nbsp;code가&amp;nbsp;수정되어야&amp;nbsp;하므로&amp;nbsp;다음&amp;nbsp;사항을&amp;nbsp;기억해야&amp;nbsp;합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Project에서 단 하나의 파일에만 적용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Namespace import를 위한 모든 using문은 file의 상단에 위치해야 합니다.&lt;/li&gt;
&lt;li&gt;Class나 기타 다른 type을 선언하는 경우 file의 하단에 위치해야 합니다.&lt;/li&gt;
&lt;li&gt;주 진입함수의 Method이름인 Main은 직접적으로 사용할 수 없습니다. Main함수를 명시적으로 정의하고자 하는 경우에도 compile과정에서 &amp;lt;Main&amp;gt;$로 변경됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) Global Namespace Import&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 System namespace를 import 하고자 한다면 우리는 해당 file 상단에 다음과 같이 구문을 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764055800437&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 구현하면 System namespace에 속해있는 Console을 바로 사용할 수 있고 따라서&lt;/p&gt;
&lt;pre id=&quot;code_1764055814920&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 구문작성이 가능하게 됩니다. 하지만 이전에 만든 예제의 경우 'using System;'문을 사용하지 않았음에도 불구하고 Console.WriteLine...구문을 그대로 사용되었음을 볼 수 있습니다. 이런 것이 가능한 이유는 Project가 C# 10/.NET 6에서 도입된 Global Namespace Import라는 기능을 사용하고 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026의 Solution Explorer에서 obj -&amp;gt; Debug -&amp;gt; net10.0 folder를 순서대로 열어보면 '[Project명].globalUsings.g.cs'라는 file을 볼 수 있습니다.(예제에서는 'ConsoleApp1.GlobalUsings.g.cs') 이 file은 .NET 6부터 compiler에 의해 자동으로 생성되는 file로 Global Namespace Import의 기능을 수행하는 file입니다. 해당 file을 열어보면 다음과 같이 구현되어 있는데&lt;/p&gt;
&lt;pre id=&quot;code_1764055904741&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// &amp;lt;auto-generated/&amp;gt;
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using Systehttp://m.Net.Http;
global using System.Threading;
global using Systehttp://m.Threading.Tasks;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System과 같이 어느 Project에서나 적용되는 공통적인 일부 Namespace가 Impport 되어 있음을 알 수 있습니다. 이러한 구현은 Project내의 모든 code file에 적용되는 것으로 이 기능덕에 각 code file에서는 저마다 공통적으로 import 하는 반복적인 작업을 생략할 수 있게 되었습니다. 이 기능에 관한 더 자세한 사항은 추후에 다시 다룰 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5) 예외를 통해 자동으로 생성된 Code 살펴보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Top-level Programs기능은 본래 필요한 Code를 생략하는 것이 아니라 Compile과정에서 생성해 주는 방식으로 동작합니다. 다시 말해 Compile을 거쳐지 않으면 볼 수 없다는 얘기인데 대신 Runtime에서 예외를 일으키게 되면 숨겨진 Code를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Project의 Program.cs에서 다음과 같이 예외를 일으키는 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764055976918&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// See https://aka.ms/new-console-template for more information
Console.WriteLine(&quot;안녕, C#&quot;);
throw new Exception();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 VS2026의 Debug -&amp;gt; Start Without Debugging를 선택해 Project를 실행합니다. 이때 Debugging으로 Project를 시작하지 않도록 주의해야 합니다. 예외가 발생하면 Debugger에 의해 예외가 다뤄지기 때문에 보려고 하는 결과를 제대로 살펴볼 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project가 실행되면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOB2uC/dJMcahQnGtM/E0Simm8kDkxP8AARLhSjAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOB2uC/dJMcahQnGtM/E0Simm8kDkxP8AARLhSjAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOB2uC/dJMcahQnGtM/E0Simm8kDkxP8AARLhSjAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOB2uC%2FdJMcahQnGtM%2FE0Simm8kDkxP8AARLhSjAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 인수의 전달을 위해 args라는 매개변수를 사용하고 있는 &amp;lt;Main&amp;gt;$이름의 Main method를 볼 수 있으며 해당 Mehtod는 Program Class안에 정의되었음을 알 수 있습니다. 이러한 부분은 Compiler에 의해 자동으로 생성된 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6) Program class의 Namespace확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 top-level program에서의 Namespace는 이름이 없다고 언급한 적이 있는데 그것이 사실인지 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 예외처리한 구문 위에 아래의 구문을 추가합니다. 해당 구문은 Program class의 Namespace이름을 가져와 이를 화면에 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764143636357&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text;

Console.OutputEncoding = Encoding.UTF8;
Console.InputEncoding = Encoding.UTF8;

Console.WriteLine(&quot;안녕, C#&quot;);

string namespaceName = typeof(Program).Namespace ?? &quot;이름없음&quot;;
Console.WriteLine(namespaceName);

throw new Exception();&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용된 ??은 null 조건부 연산자로서 왼쪽 피연산자가 null인 경우 반환값을 오른쪽 값으로 대체하도록 하는 연산자입니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;VS2026의 Code Editor는 code snippets라는 기능이 있어 일반적으로 사용되는 긴 문장을 짧은 글로 대체하여 입력할 수 있습니다. 예를 들어 'Console.WriteLine()'을 입력하고자 하는 경우 cw를 입력한 뒤 Tab이나 Enter key를 누르게 되면 자동으로 'Console.WriteLine()'가 입력된 뒤 괄호 안에 Cursor를 이동시켜 곧바로 화면에 표시하고자 하는 내용을 입력할 수 있는 상태가 됩니다. 좀 더 자세한 사항은 아래 link를 참고하시기 바랍니다. &lt;br /&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/ide/code-snippets?view=visualstudio&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://learn.microsoft.com/en-us/visualstudio/ide/code-snippets?view=visualstudio&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용된 Console.OutputEncoding과 Console.InputEncoding은 Console에 한글이 표시될 때 글자가 깨지는 현상을 방지하기 위해서 추가한 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMgeSk/dJMcacatlcy/rkwDssuedmfVdelVLS82f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMgeSk/dJMcacatlcy/rkwDssuedmfVdelVLS82f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMgeSk/dJMcacatlcy/rkwDssuedmfVdelVLS82f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMgeSk%2FdJMcacatlcy%2FrkwDssuedmfVdelVLS82f1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;229&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7) C# Code를 Project file 없이 실행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS2026에서 Project를 생성하고 실행해 본 지금까지 예와 같이 C# Code file(.cs)은 물론 기타 여러 가지 file들이 하나의 Project안에 포함되어 있고 이를 관리하는 Project file이 필수적으로 필요하다는 것을 알 수 있습니다. 이런 Project file은 내부에서 target이 되는 .NET Runtime Version이나 nullability언어기능, global namespace import와 같은 어려가지 설정들을 포함하고 있으며 다른 Project나 Package의 참조정보를 가지고 있기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 예제처럼 복잡하지 않은 아주 단순한 Console App만을 필요로 한다면, 게다가 기본적으로 적용되는 Project규칙의 대부분을 굳이 따로 설정하거나 바꿀필요가 없다면 Project단위의 관리는 오히려 Application을 만드는데 복잡한 절차만 더해지는 꼴이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 언어, 예를 들면 Python과 같은 언어의 경우 Project단위가 아니라 하더라도 아래와 같이 단일 file에 대한 실행방식을 지원하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;python app.py&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) File-based apps&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 이러한 문제를 해결하고자 C# 14에서 file-based apps라는 기능을 도입하게 되었습니다. 이 기능은 python과 비슷하게 단일 cs file로만 직접적으로 실행가능하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet run mycsapp.cs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 file로 Application을 만드는 경우에도 top-level 구문을 사용할 수 있기 때문에 Namespace와 Main을 따로 정의해야 하는 번거로움이 없으며 복잡한 Project구조를 따르지 않기 때문에 Application을 더욱 간단하고 빠르게 만들어낼 수 있습니다. 특히 C# 언어에 대한 학습이나 간단한 TEST가 필요할 때 손쉽게 사용가능한 유용한 방식이라 할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;File-based apps기능을 사용할 때 file명은 굳이 Program.cs일 필요가 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) File-based apps 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 알아둬야 할 건 File-based app은 dotnet CLI 기능이므로 VS2026에서 만들 수 없다는 것입니다. 따라서 적당한 folder를 하나 만들고 여기서 VSCode와 같은 Code편집기로 임의의 cs file을 하나 생성한 뒤 필요한 file내용을 직접 작성합니다. 예제에서는 FileBasedApps folder에 test.cs라는 file을 만들어보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nngYp/dJMcaaKvn6x/UEbT8vuOLidL7k2piV9YYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nngYp/dJMcaaKvn6x/UEbT8vuOLidL7k2piV9YYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nngYp/dJMcaaKvn6x/UEbT8vuOLidL7k2piV9YYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnngYp%2FdJMcaaKvn6x%2FUEbT8vuOLidL7k2piV9YYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1032&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File을 저장한 뒤 Windows Terminal이나 VSCode의 Terminal에서 FileBasedApps folder로 위치한 뒤 아래와 같은 명령을 사용해 file을 실행합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;run&amp;nbsp;test.cs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 기다리면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;69&quot; data-origin-height=&quot;18&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yiVEG/dJMcacuM65C/hKvCoN1GtqmPA5XNuaZYrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yiVEG/dJMcacuM65C/hKvCoN1GtqmPA5XNuaZYrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yiVEG/dJMcacuM65C/hKvCoN1GtqmPA5XNuaZYrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyiVEG%2FdJMcacuM65C%2FhKvCoN1GtqmPA5XNuaZYrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;69&quot; height=&quot;18&quot; data-origin-width=&quot;69&quot; data-origin-height=&quot;18&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재까지 File-based apps는 하나의 file만을 지원합니다. 다수의 file에 대한 지원은 .NET 11에서 지원될 예정입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) File-based apps에 대한 추가지원 기능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project 없는 단일 cs file이라고 하더라도 외적인 기능을 지원해야 하는 경우가 있을 수 있습니다. 이럴 때는 특별한 지시자문자인 '#:'을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 특정 NuGet Package를 필요로 하는 경우 아래와 같이 NuGet참조를 추가할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764312680849&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#:package Newtonsoft.Json@13.0.3

using Newtonsoft.Json;

var obj = new { Name = &quot;홍길동&quot;, Age = 30 };
string json = JsonConvert.SerializeObject(obj);
Console.WriteLine(json);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 외부의 Class Library를 참조해야 한다면 아래와 같이 사용할 수 있고&lt;/p&gt;
&lt;pre id=&quot;code_1764312691992&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#:project ../MyLibrary/MyLibrary.csproj&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 SDK를 지정해야 한다면 #:sdk를 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764312703777&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#:sdk Microsoft.NET.Sdk.Web

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;

Console.WriteLine(&quot;ASP.NET Core SDK 로드 완료!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;SDK의 Version을 구분해야 한다면 @를 사용합니다. (#:sdk Microsoft.NET.Sdk.Web@11.1.0)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 MS Build 속성의 설정도 가능해서 C#언어의 Version을 다음과 같이 지정할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764312724610&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#:property LangVersion=9.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 만들어진 cs file은 추후 필요하다면 아래 명령을 통해 완벽한 Project형태로 재생성할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet project convert test.cs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포가 필요하다면 그것 역시 가능한데&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet publish test.cs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 위와 같이 하는 경우 기본적으로 AOT Compile이 적용되므로 이를 끄고자 한다면 #:property를 통해 다음과 같이 설정해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764312760897&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#:property PublishAot=false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File-based apps에 관한 더 자세한 사항은 아래 link를 참고해 보시기 바랍니다.&lt;br /&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764312774959&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Announcing dotnet run app.cs - A simpler way to start with C# and .NET 10 - .NET Blog&quot; data-og-description=&quot;Run C# files instantly with dotnet run app.cs, no project file needed! Coming to .NET 10, try it out today in Preview 4.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/m0Tty/hyZOOEq6m1/GMySGqcsKNlIlM4aYn4An1/img.jpg?width=1257&amp;amp;height=703&amp;amp;face=0_0_1257_703&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/m0Tty/hyZOOEq6m1/GMySGqcsKNlIlM4aYn4An1/img.jpg?width=1257&amp;amp;height=703&amp;amp;face=0_0_1257_703');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Announcing dotnet run app.cs - A simpler way to start with C# and .NET 10 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Run C# files instantly with dotnet run app.cs, no project file needed! Coming to .NET 10, try it out today in Preview 4.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련하여 동영상도 시청할 수 있습니다.&lt;br /&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=98MizuB7i-w&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=98MizuB7i-w&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=98MizuB7i-w&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/NB9ID/hyZOotmjyH/xkjkA06ryHcTH4iPpLtwtk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=838_196_904_270,https://scrap.kakaocdn.net/dn/SoBfX/hyZOAGaHo3/EZZUJIUZhDMYthBQjPPWRk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=838_196_904_270&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;No projects just C# with &amp;#96;dotnet run app.cs&amp;#96; | DEM518&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/98MizuB7i-w&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다.&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>.NET 10</category>
      <category>C# 14</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/643</guid>
      <comments>https://cliel.tistory.com/entry/%EC%97%B4%EB%84%A4%EB%B2%88%EC%A7%B8C-2025%EB%85%84-11%EC%9B%94-C%EC%9D%98-14%EB%B2%88%EC%A7%B8-%EB%B2%84%EC%A0%84%EC%9D%98-%ED%83%84%EC%83%9D#entry643comment</comments>
      <pubDate>Thu, 6 Nov 2025 16:31:36 +0900</pubDate>
    </item>
    <item>
      <title>Hyper-V Virtual Machine상에서 Docker실행하기</title>
      <link>https://cliel.tistory.com/entry/Hyper-V-Virtual-Machine%EC%83%81%EC%97%90%EC%84%9C-Docker%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Virtual Machine상에서 Docker를 설치할 때 설치 option에서 Hyper-V대신 WSL(Windows Subsystem Linux)을 사용한다는 설정이 있습니다. 이 설정을 제외하고 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 뒤 Docker를 실행하면 다음과 같은 오류를 볼 수 있는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jnsyv/btsHaVRwgN9/NIkEqGRkNkULxYGywXqypk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jnsyv/btsHaVRwgN9/NIkEqGRkNkULxYGywXqypk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jnsyv/btsHaVRwgN9/NIkEqGRkNkULxYGywXqypk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJnsyv%2FbtsHaVRwgN9%2FNIkEqGRkNkULxYGywXqypk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1270&quot; height=&quot;721&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말이 길지만 내용은 BIOS상에서 가상화를 설정해야 한다는 것입니다. 하지만 Hyper-V의 Virtual Machine에서 BIOS를 제한적일 수밖에 없습니다. 이 문제를 해결하려면 Virtual Machine를 끈뒤 Host Machine상에서 관리자 권한으로 PowerShell를 실행하고 아래 명령을 통해 Virtual Machine의 가상화를 설정해 줘야 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Set-VMProcessor&amp;nbsp;-VMName&amp;nbsp;Windows11&amp;nbsp;-ExposeVirtualizationExtensions&amp;nbsp;$true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 명령에서 Windows11은 Virtual Machine의 이름입니다. Virtual Machine의 이름에 'Windows 11'처럼 공백이 있으면 오류가 날 수 있으므로 공백을 없애주는 것이 좋습니다.&lt;/blockquote&gt;</description>
      <category>ETC</category>
      <category>docker</category>
      <category>hyper-v</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/613</guid>
      <comments>https://cliel.tistory.com/entry/Hyper-V-Virtual-Machine%EC%83%81%EC%97%90%EC%84%9C-Docker%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0#entry613comment</comments>
      <pubDate>Sat, 4 May 2024 16:53:25 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 11. LINQ</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-11-LINQ</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ(Language INtegrated Query)는 일련의 data를 대상으로 filtering 및 sorting 하고 다른 형태로 결과를 투영할 수 있는 언어확장 도구입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 왜 LINQ인가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 명령형및 선언형 언어의 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 2008년 .NET 3.0과 .NET Framework 3.0과 함께 도입되었습니다. 그전에 C#및 .NET개발자는 명령형이라고 하는 절차적 code문을 사용해 예를 들어 loop처럼 일련의 item들을 처리하곤 했습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;첫 번째 item에 대한 현재 위치를 설정합니다.&lt;/li&gt;
&lt;li&gt;지정한 값과 하나 또는 그 이상의 속성을 비교 비교하여 예를 들어 가격이 50 이상이어야 한다거나 수량이 동일한지등과 같은 경우처럼 처리해야 하는 item인지를 확인합니다.&lt;/li&gt;
&lt;li&gt;2번에서 조건과 일치하는 경우라면 item을 처리합니다. 예를 들어 사용자에게 하나 또는 그 이상의 속성을 표시하거나 새로운 값으로 update 할 수 있고 또는 item을 제거하거나 counting과 summing처럼 값을 집계할 수도 있을 것입니다.&lt;/li&gt;
&lt;li&gt;다음 item으로 이동합니다. 모든 item이 처리될때까지 이 과정을 반복합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령형이라고 하는 절차적 code는 compiler에게 '이렇게 하라. 저렇게 하라'와 같이 주어진 목표를 어떻게 달성할 수 있는지를 말해줍니다. Compiler는 우리가 달성하고자 하는 목표자체를 알 수 없기 때문에 그 자체로 우리를 도울 수는 없습니다. 모든 방법적 절차가 정확하게 맞다는 것에 대한 모든 책임은 100% 개발자가에게 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 이러한 일반적인 작업들을 오류를 최소화하면서 더 쉽게 code를 만들 수 있도록 해줍니다. Move나 read, update 기타 등등 각각의 동작에 대한 상태를 명시적으로 설명하는 대신 LINQ는 개발자가 선언적 혹은 기능적인 style의 문을 작성할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언적 code는 compiler에게 달성하기 위한 목표가 무엇인지를 말해주며 compiler는 가장 최적의 방법을 통해 목표를 달성하게 됩니다. 또한 해당 문은 기존보다 더 간소하면서도 깔끔한 상태를 유지할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;LINQ를 제대로 이해하지 않으면 작성한 code는 약간의 자체적인 bug를 갖게 될 수 있습니다. 아래 글을 읽어보세요.(&lt;a href=&quot;https://twitter.com/amantinband/status/1559187912218099714&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://twitter.com/amantinband/status/1559187912218099714&lt;/a&gt;). 해당 부분과 관련해서 솔직히 말하자면 이것은 LINQ의 동작과 multi-threading 동작이 결합된 것으로 사람들이 가장 혼란스러워 하는 것이 이 부분이므로 code가 위험해 질 수 있는 부분을 잘 이해해야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. LINQ 표현식&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글을 통해 우리는 이미 간단하게나마 LINQ를 사용해 본적이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 위의 글에 대한 핵심은 LINQ가 아니므로 LINQ가 어떻게 동작하는지에 대해서는 생략했습니다. 이제 LINQ의 표현식을 이해하기 위한 시간을 가져보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 무엇이 LINQ를 만드는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 몇가지 부분으로 나뉠 수 있는데 일부는 필수적이지만 일부는 선택적입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Extension method(필수적) : 여기에는 Where나 OrderBy 그리고 Select를 포함한 LINQ의 기능을 제공합니다.&lt;/li&gt;
&lt;li&gt;LINQ provider(필수적) : 여기에는 memory상에서 개체를 표현하기 위한 LINQ to Objects와 외부 database에 저장된 data와 EF Core를 통한 model을 처리하기 위한 LINQ to Entities, 그리고 XML로 저장된 data를 처리하기 위한 LINQ to XML을 포함합니다. 이들 provider는 다양한 유형의 data에 대해 특정한 방식으로 LINQ표현식을 실행합니다.&lt;/li&gt;
&lt;li&gt;Lambda 표현식(선택적) : LINQ query를 간소화하기 위해 명명된 method대신 예를 들어 filtering를 위한 Where method를 조건부 논리와 같이 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;LINQ query comprehension 구문(선택적) : 여기에는 from, in, where, orderby, descending, select와 같은 C# keyword가 존재합니다. 이들은 몇몇 LINQ 표현 method에 대한 별칭에 해당하며 특히 이미 SQL(Structured Query Language)과 같이 다른 query언어를 경험해 본 적이 있다면 이들을 더욱 친숙하게 사용할 수 있을 것입니다. 처음 LINQ를 사용할 때는 종종 LINQ query comprehension 구문을 LINQ라고 오해하는 경우도 있는데 이것은 선택적으로 사용하는 LINQ의 여러 부분 중 하나에 해당할 뿐입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 열거 가능한 class에 LINQ 표현식 적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Where나 Select와 같은 LINQ 확장 method는 Enumerable static class에 의해 IEnumerable&amp;lt;T&amp;gt;를 구현하는 모든 sequence type에 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 모든 type의 array는 IEnumerable&amp;lt;T&amp;gt; class를 구현하며 여기서 T는 array에 속하는 item에 대한 type을 의미합니다. 다시 말해 모든 array는 질의와 이들의 조작하는데 대한 LINQ를 지원한다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt;, Dictionary&amp;lt;TKey, TValue&amp;gt;, Stack&amp;lt;T&amp;gt;, Queue&amp;lt;T&amp;gt;와 같은 모든 generic collection은 IEnumerable&amp;lt;T&amp;gt;를 구현하므로 이들도 역시 LINQ를 통해 data질의하고 변경할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enumerable은 아래 표에서 요약된것처럼 50여 개 이상의 method를 정의하고 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 표는 향후 참고용으로 유용할 수 있지만 지금은 간단하게 어떤 method가 존재하고 나중에 어떤 method를 사용하는 것이 적절한지 확인하는 차원에서 살펴볼 수 있습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;First, FirstOrDefault, Last, LastOrDefault&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Sequence에서 첫번째 혹은 마지막 item을 가져오며 예외 혹은 type에 대한 기본값을 반환할 수 있습니다. 예를 들어 첫번째 혹은 마지막 item이 존재하지 않는 경우 int type이면 0을 참조 type이라면 null을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Where&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 filter와 일치하는 item에 대한 sequence를 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Single, SingleOrDefault&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 filter와 일치하는 item을 반환하거나 아니면 예외 혹은 일치하는 것이 존재하지 않는다면 type에 대한 기본값을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;ElementAt, ElementAtOrDefault&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 index위치의 item을 반환하거나 아니면 예외 혹은 position에 해당하는 item이 존재하지 않는 경우 type의 기본값을 반환합니다. .NET 6에서 도입된것은 int대신 Index를 전달할 수 있게 overload되어 Span&amp;lt;T&amp;gt; sequences에서 더 효휼적입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Select, SelectMany&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item을 다른 type으로 투영하며 item의 중첩된 계층을 평탄화합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;OrderBy, OrderByDescending, ThenBy, ThenByDescending&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정된 field또는 속성을 통해 item을 정렬합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Order, OrderDescending&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;.NET 7에 도입된 것으로 item을 item자체별로 정렬합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Reverse&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item의 순서를 뒤집습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;GroupBy, GroupJoin, Join&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;두개의 sequences를 group화 하거나 join합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Skip, SkipWhile&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item 특정 수만큼 skip하거나 표현식이 true일때까지 skip합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Take, TakeWhile&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item을 특정 수만큼 가져오거나 표현식이 true일땨까지 가져옵니다. .NET 6에서 도입된 것은 Range를 전달할 수 있도록 overload되었는데 예를 들어 Take(range: 3..^5)은 3번째 item부터 시작하여 5번째 item에서 끝나는 부분집합을 가져온다는 것을 의미합니다. 또는 Skip(4)대신 Take(4..)와 같이 사용할 수도 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Aggregate, Average, Count, LongCount, Max, Min, Sum&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;전체값 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;TryGetNonEnumeratedCount&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Count()는 Count속성이 sequence하에서 구현되었는지를 확인하고 그 값을 반환하거나 item을 count하기 위해 전체 sequence를 열거합니다. .NET 6에서 도입된 것은 해당 method로 오로지 Count만을 확인하여 없는 경우 잠재적인 저성능 운용을 동작을 방지하기 위해 false를 반환하며 out매개변수를 0으로 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;All, Any, Contains&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item이 filter에 대해 전체 혹은 일부와 일치하거나 sequence가 지정한 item을 포함하고 있다면 true를 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Cast&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;지정한 type으로 item을 형변환합니다. 이것은 비 generic개체를 generic type으로 변환할때 유용하게 사용될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Distinct&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;중복 item을 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;DistinctBy, ExceptBy, IntersectBy, UnionBy, MinBy, MaxBy&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Item의 전체가 아닌 일부만을 사용해 비교를 수행할 수 있습니다. 예를 들어 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;전체&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Person개체를 비교함으로서 Distinct를 통해 중복 item을 제거하는 대신 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;LastName과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;DateOfBirth만 비교함으로서 DistinctBy를 통해 중복 item을 제거할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Chunk&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;크기가 지정된 batch로 sequence를 분할합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Append, Concat, Prepend&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Sequence별 결합동작을 수행합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;Zip&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Items의 위치를 기반으로 2개 혹은 3개의 sequence에 대한 일치 작업을 수행합니다. 예를 들어 첫번째 sequence에서 position이 1인 item은 두번째 sequence의 position 1인 item과 일치합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 48.256%;&quot;&gt;ToArray, ToList, ToDictionary, ToHashSet, ToLookup&lt;/td&gt;
&lt;td style=&quot;width: 51.744%;&quot;&gt;Sequence를 array나 collection으로 변환합니다. 이들은 지연된 실행을 기다라기 보다는 LINQ표현식의 즉각적인 실행을 강제하는 유일한 method입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;As와 To로 시작하는 확장 method의 차이에 대해 확실히 해둘 필요가 있습니다. As로 시작하는, 예를 들어 AsEnumerable method는 sequence를 다른 type으로 변환하기는 하지만 memory를 할당하지 않으므로 빠르게 동작합니다. 하지만 To로 시작하는, 예를 들어 ToList와 같은 method는 새로운 item의 sequence에 대한 memory를 할당하게 되므로 상대적으로 느릴 수 있으며 또한 더 많은 memory resource를 사용하게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enumerable class는 또한 확장 method가 아닌 아래 표에서 설명한 바와 같이 다른 몇몇 method들도 가지고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 111px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 17px;&quot;&gt;Method&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 60px;&quot;&gt;Empty&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 60px;&quot;&gt;지정된 type T에 대한 빈 sequence를 반환합니다. 대게 IEnumerable&amp;lt;T&amp;gt;를 필요로 하는 method에서 빈 sequence를 전달할때 유용하게 사용될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 17px;&quot;&gt;Range&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 17px;&quot;&gt;Count item을 통한 시작값으로 부터 정수 sequence를 반환합니다. 예를 들어 Enumerable.Range(start: 5, count: 3)는 정수 5, 6 그리고 7을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.9535%; height: 17px;&quot;&gt;Repeat&lt;/td&gt;
&lt;td style=&quot;width: 86.0465%; height: 17px;&quot;&gt;Count횟수만큼 반복되는 같은 요소를 포함하는 sequence를 반환합니다. 예를 들어 Enumerable.Repeat(element: &quot;5&quot;, count: 3)은 문자열값 &quot;5&quot;, &quot;5&quot;, &quot;5&quot;를 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;(3) 지연된 실행(deferred execution)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ는 지연된 실행을 사용합니다. 이는 위에서 언급한 확장 method 대부분을 호출하면 query를 실행하지 않고 결과를 가져온다는 것을 이해하는데 중요한 부분이며 이때 이들 확장 method의 대부분은 답이 아닌 질문을 나타내는 LINQ 표현식을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022에서 csStudy11 solution을 생성하고 여기에 LinqWithObjects이름의 console app project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Program.cs에서 기존의 문을 모두 제거하고 아래와 같이 특정 인물에 대한 문자열값의 sequence를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691048680529&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] names = new[] { &quot;이순신&quot;, &quot;이도&quot;, &quot;신사임당&quot;, &quot;홍길동&quot;, &quot;김종서&quot;, &quot;박혁거세&quot;, &quot;김좌진&quot;, &quot;윤봉길&quot; };

// 질문 : 이름이 '이'자로 시작하는 사람은 누구인가?

//확장 method 사용
var query1 = names.Where(name =&amp;gt; name.StartsWith(&quot;이&quot;));

//LINQ query comprehension syntax 사용
var query2 = from name in names where name.StartsWith(&quot;이&quot;) select name;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문에 대한 답을 얻으려면 즉, query를 실행하려면 다음과 같이 ToArray나 ToLookup과 같은 To method 중 하나를 호출하거나 query를 열거해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691049178009&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//질문의 답을 포함하는 문자열 arrary를 반환
string[] result1 = query1.ToArray();
//질문의 답을 포함하는 문자열의 list를 반환
List&amp;lt;string&amp;gt; result2 = query2.ToList();
//결과를 열거함으로서 질문의 답을 반환
foreach (string name in query1)
{
	Console.WriteLine(name);

	names[1] = &quot;유관순&quot;; //이도를 유관순으로 변경 -&amp;gt; 유관순은 '이'자로 시작하지 않는다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;77&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8HTZ2/btsFEw4QxVN/BaRQ21krNiMzSxJaFGHp91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8HTZ2/btsFEw4QxVN/BaRQ21krNiMzSxJaFGHp91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8HTZ2/btsFEw4QxVN/BaRQ21krNiMzSxJaFGHp91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8HTZ2%2FbtsFEw4QxVN%2FBaRQ21krNiMzSxJaFGHp91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;77&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;77&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연된 실행때문에 첫 번째 결과인 '이순신'을 출력한 이후 본래 배열의 값이 바뀌게 되면 loop로 다시 돌아갈 때쯤 '이도'가 '유관순'으로 변경되었으므로 더 이상 일치하는 것이 없게 되고 따라서 오로지 '이순신'만 표시되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Where를 통한 entity filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ를 사용하는 가장 일반적인 이유는 Where확장 method를 사용해 sequence안에서 item을 filtering하기 위한 것입니다. 예제를 통해 sequence를 정의하고 여기에 LINQ동작을 적용함으로써 filtering을 사용해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Project file(*.csproj)에서 아래와 같이 global로 자동으로 import 되는 System.Linq를 제거하기 위한 요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691115147839&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;Using Remove=&quot;System.Linq&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 names에 Where 확장 method를 호출하도록 합니다. 이때 Where를 입력하기 위해 typing울 시도하면 아래와 같이 IntelliSense에 의해 array에서 사용가능한 member들이 표시되는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4ahAJ/btsFDB6EMih/1EATk7ZqUVzze5AO1lxHP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4ahAJ/btsFDB6EMih/1EATk7ZqUVzze5AO1lxHP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4ahAJ/btsFDB6EMih/1EATk7ZqUVzze5AO1lxHP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4ahAJ%2FbtsFDB6EMih%2F1EATk7ZqUVzze5AO1lxHP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;859&quot; height=&quot;302&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상한 점은 여기에 표시되는 member들 중에서는 Where가 존재하지 않는다는 것입니다. 이는 Where가 확장 method이기 때문이며 array type에는 기본적으로 존재하지 않는 member이기 때문입니다. 따라서 Where 확장 method를 사용하려면 System.Linq namespace를 import 해야 합니다. .NET 6부터는 암시적으로 import 되는 것이 기본이지만 위에서 우리는 이미 System.Linq가 import 되지 않도록 Remove 했기 때문에 사용할 수 없게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Project file에서 위에서 추가한 System.Linq Remove요소를 삭제하고 names에서 Where method를 호출하도록 시도하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w9IT2/btsFDDi6kmg/E4Zn3ykZEVTqsJVsqpZl51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w9IT2/btsFDDi6kmg/E4Zn3ykZEVTqsJVsqpZl51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w9IT2/btsFDDi6kmg/E4Zn3ykZEVTqsJVsqpZl51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw9IT2%2FbtsFDDi6kmg%2FE4Zn3ykZEVTqsJVsqpZl51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;949&quot; height=&quot;189&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntelliSense는 Enumerable class에 의해 추가된 extension method를 추가한 member들을 표시할 것입니다. 또한&amp;nbsp;Where method에서 IntelliSense는 Where method를 호출하기 위해 Func&amp;lt;string, bool&amp;gt; delegate의 instance를 전달해야 함을 말해주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Func&amp;lt;string, bool&amp;gt; delegate의 새로운 instance를 생성하기 위한 표현식을 입력합니다. 하지만 다음 단계에서 method의 이름을 정의할 것이기 때문에 아직까지는 method이름을 제공하지 않았습니다. (오류발생)&lt;/p&gt;
&lt;pre id=&quot;code_1691116238908&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.Where(new Func&amp;lt;string, bool&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Func&amp;lt;string, bool&amp;gt; delegate는 우리에게 각 string변수에 method로 전달되고 method는 반드시 bool값을 반환해야 함을 말해주고 있습니다. Method가 true를 반환한다면 이것은 결과에서 해당 string을 포함하고 있음을 의미하며 그렇지 않으면 포함하고 있지 않음을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 명명된 method 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에서 Function.cs 이름의 class file을 추가하고 여기에 partial Program class를 정의한 뒤 아래와 같이 3글자 이상의 이름만을 포함시킬 method를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691136823919&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	static bool NameLongerThanFour(string name)
	{
		return name.Length &amp;gt; 3;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs로 돌아와 위에서 정의했던 Func&amp;lt;string, bool&amp;gt; delegate로 method의 이름을 전달하고&lt;/p&gt;
&lt;pre id=&quot;code_1691136905230&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 query를 열거하도록 하면&lt;/p&gt;
&lt;pre id=&quot;code_1691137019214&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foreach (var name in query)
{
	Console.WriteLine(name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TrnsA/btsFD5zEaEe/4mjvwtg2ucOKASMWIU8NKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TrnsA/btsFD5zEaEe/4mjvwtg2ucOKASMWIU8NKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TrnsA/btsFD5zEaEe/4mjvwtg2ucOKASMWIU8NKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTrnsA%2FbtsFD5zEaEe%2F4mjvwtg2ucOKASMWIU8NKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Delegate를 제거하여 code 간소화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Func&amp;lt;string, bool&amp;gt; delegate의 instance는 명시적으로 제거할 수 있습니다. 그러면 code를 더욱 간소화할 수 있는데 이는 C# compiler가 편의상 delegate를 instance화 할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전의 문을 복사한 뒤 그대로 주석처리하고 복사된 code를 다시 넣습니다. 그런 뒤 붙여 넣은 code에서 아래와 같이&amp;nbsp; method이름만 남겨두고 delegate의 instance를 제거합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691137433886&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));
var query = names.Where(NameLongerThanFour);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Lambda 표현식 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 code를 더 간소화할 수 있는 방법으로 명명된 method를 사용하는 대신 lambda expression을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 이러한 방법이 처음에는 복잡하고 어려워 보일 수 있으나 lambda expression은 이름이 없는 함수로서 =&amp;gt; 기호(goes to라고 함)를 사용해 반환값을 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 다시 복사하여 기존의 문을 주석처리한 뒤 복사한 code를 붙여 넣어 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691208102145&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));
//var query = names.Where(NameLongerThanFour);
var query = names.Where(name =&amp;gt; name.Length &amp;gt; 3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보면 NameLongerThanFour method의 모든 중요한 부분이 lambda 식에 포함되어 있는 것을 알 수 있고 또 이것이 전부입니다. lambda 식은 크게 아래 2가지를 정의하고 있는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 매개변수의 이름 : name&lt;/li&gt;
&lt;li&gt;반환 값 표현식 : name.length &amp;gt; 3&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 name 입력 매개변수의 type은 sequence가 포함하고 있는 문자열값으로 추론되며 Where에서 가능한 delegate의 정의에 따라 반환값은 bool이 됩니다. 따라서 =&amp;gt; 기호 이후 표현식에서는 반드시 bool값을 반환하도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compiler는 우리를 위해 대부분의 작업을 수행하므로 code는 가능한 한 간소화할 수 있습니다. 예제를 실행하면 이전과 동일한 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) 매개변수에 기본값 적용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 12에서는 lambda 표현식에서 기본값을 제공할 수 있게 되었습니다. 따라서 아래 예제와 같이 표현식을 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709864753458&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//var query = names.Where(new Func&amp;lt;string, bool&amp;gt;(NameLongerThanFour));
//var query = names.Where(NameLongerThanFour);
//var query = names.Where(name =&amp;gt; name.Length &amp;gt; 3);
var query = names.Where((string name = &quot;홍&quot;) =&amp;gt; name.Length &amp;gt; 3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) Entity 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 일반적으로 사용되는 method로 OrderBy와 ThenBy 등이 있으며 이들은 Sequence를 정렬하기 위해 사용됩니다. 또한 확장 method는 이전 method가 IEnumerable&amp;lt;T&amp;gt; interface를 구현하는 type인 다른 sequence를 반환하는 경우라면 연결(method chain)하여 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● OrderBy를 사용한 단일 속성으로 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제와 같이 OrderBy method를 query의 마지막에 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691209003327&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
	.Where(name =&amp;gt; name.Length &amp;gt; 1)
	.OrderBy(name =&amp;gt; name.Length);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;각각의 확장 method마다 line을 분리하여 형식화하면 좀 더 code를 읽기 쉽게 만들 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이름이 짧은 순서대로 정렬된 결과를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOLMuQ/btsFCZ0T4u7/QDiq34sXufSDeC9mtqcrRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOLMuQ/btsFCZ0T4u7/QDiq34sXufSDeC9mtqcrRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOLMuQ/btsFCZ0T4u7/QDiq34sXufSDeC9mtqcrRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOLMuQ%2FbtsFCZ0T4u7%2FQDiq34sXufSDeC9mtqcrRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;229&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 name이 긴 것 순으로 정렬하고자 한다면 OrderByDescending method를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● ThenBy를 사용한 다수의 속성으로 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 이상의 속성을 통해 정렬하고자 한다면 예를 들어 alphabet이나 가나다순에서 같은 길이의 name으로 정렬하고자 한다면 아래 예제와 같이 ThenBy를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691216999339&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
	.Where(name =&amp;gt; name.Length &amp;gt; 1)
	.OrderBy(name =&amp;gt; name.Length)
	.ThenBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 약간 다른 차이를 알 수 있을 것입니다. 같은 길이의 name으로 정렬된 상태에서 가나다순으로 정렬되었으므로 이도를 제외한 김종서와 김좌진이 상위에 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbYLV/btsFDCdp53v/KpGMa1AUo0KxW9uhMxHdkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbYLV/btsFDCdp53v/KpGMa1AUo0KxW9uhMxHdkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbYLV/btsFDCdp53v/KpGMa1AUo0KxW9uhMxHdkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbYLV%2FbtsFDCdp53v%2FKpGMa1AUo0KxW9uhMxHdkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;210&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) Item 자체에서 정렬하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서는 Order와 OrderDescending라는 확장 method가 도입되었으며 간단하게 item자체에서 졍렬을 수행할 수 있습니다. 예를 들어 string값의 sequence가 있다면 .NET 7이전에 OrderBy method를 호출하고 item자체를 선택하는 lambda식을 전달해야 했지만&lt;/p&gt;
&lt;pre id=&quot;code_1691217351046&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.OrderBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7부터는 아래와 같이 간단하게 정렬을 수행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691217387019&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names.Order();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderDescending 역시 역순으로 정렬한다는 점을 제외하면 Order와 동일하게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) var 또는 특정 type을 사용해 query 선언하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ 표현식을 작성하는 동안 query개체를 선언하기 위해 var keyword를 사용하는 것은 LINQ 표현식이 동작하는 것마다 type이 자주 바뀔 수 있으므로 매우 일반적인 방법입니다. 예를 들어 예제에서의 query는 IEnumerable&amp;lt;string&amp;gt;으로서 시작되었으나 현재는 IOrderedEnumerable&amp;lt;string&amp;gt;이 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mouse를 var keyword에 올려두면 type이 IOrderedEnumerable&amp;lt;string&amp;gt;이라는 것을 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W7Aom/btsFABfItjL/JEctL6bjL9dJONu9adVyCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W7Aom/btsFABfItjL/JEctL6bjL9dJONu9adVyCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W7Aom/btsFABfItjL/JEctL6bjL9dJONu9adVyCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW7Aom%2FbtsFABfItjL%2FJEctL6bjL9dJONu9adVyCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;86&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 var는 실제 type으로 아래와 같이 바꾸는 것이 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691217895246&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IOrderedEnumerable&amp;lt;string&amp;gt; query = names
	.Where(name =&amp;gt; name.Length &amp;gt; 1)
	.OrderBy(name =&amp;gt; name.Length)
	.ThenBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일단 query에 대한 작업을 끝내고 나면 var에서부터 선언된 type을 실제 type으로 바꿀 수 있고 이는 type이 정확히 어떤 것인지를 명확히 이해하는데 도움이 될 수 있습니다. 또한 compile시 var가 실제 type으로 바뀌게 되므로 성능에는 영향을 주지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) Type filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Where 확장 method는 text나 숫자와 같이 값을 통해 filtering 할 수 있지만 sequence가 여러 type을 가지고 있고 이때 상속 계정을 따르면서 특정 type으로 filtering 해야 한다면 다른 방법을 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 예외에 대한 sequence가 있다고 가정하면 아래 그림과 같이 복잡한 계층구조로 부터 수백 가지의 예외 type이 형성될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nzYCq/btsFDyWnBF2/KLwMunmi3RObVeMqMQGJI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nzYCq/btsFDyWnBF2/KLwMunmi3RObVeMqMQGJI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nzYCq/btsFDyWnBF2/KLwMunmi3RObVeMqMQGJI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnzYCq%2FbtsFDyWnBF2%2FKLwMunmi3RObVeMqMQGJI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1041&quot; height=&quot;388&quot; data-origin-width=&quot;1041&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 실제 type에 대한 filtering을 예제를 통해 구현해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Progam.cs에서 아래와 같이 예외별 파생 개체를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691285029526&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Exception&amp;gt; exceptions = new()
{
		new ArgumentException(),
		new SystemException(),
		new IndexOutOfRangeException(),
		new InvalidOperationException(),
		new NullReferenceException(),
		new InvalidCastException(),
		new OverflowException(),
		new DivideByZeroException(),
		new ApplicationException()
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 OfType&amp;lt;T&amp;gt; 확장 method를 사용해 arithmetic exception이 아닌 예외를 삭제하고 해당 예외만 console에 표시하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691285217877&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IEnumerable&amp;lt;ArithmeticException&amp;gt; arithmeticExceptionsQuery = exceptions.OfType&amp;lt;ArithmeticException&amp;gt;();
foreach (ArithmeticException exception in arithmeticExceptionsQuery)
{
	Console.WriteLine(exception);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 ArithmeticException 혹은 ArithmeticException으로부터 파생된 type만이 표시됨을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWBJlb/btsFCfQxoXP/zl8nT1rIVAg0gDPzHmvzOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWBJlb/btsFCfQxoXP/zl8nT1rIVAg0gDPzHmvzOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWBJlb/btsFCfQxoXP/zl8nT1rIVAg0gDPzHmvzOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWBJlb%2FbtsFCfQxoXP%2Fzl8nT1rIVAg0gDPzHmvzOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(13) LINQ를 사용한 set과 bag&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 수학에서 가장 기본적인 개념 중의 하나이며 set은 하나 또는 그 이상의 중복되지 않은 개체의 collection입니다. 반면 Bag라고 하는 multiset(중복집합)은 중복되는 하나 또는 그 이상의 개체에 대한 collection에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 관련하여 학교에서 우리는 이미 Venn diagram이라는 것을 배운 적이 있는데 공통집합 연산에는 집합 간의 교집합 또는 합집합을 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 취미별 학생들에 대한 3개의 문자열 값의 array를 정의하고 이들에 대한 공통집합과 합집합을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 project에서 Helpers.cs이름의 class file을 partial class Program으로 추가하고 여기에 아래와 같이 method를 정의합니다. 해당 method는 문자열 변수의 모든 sequence를 comma(,)로 분리한 단일 문자열을 선택적으로 전달하는 설명과 함께 console에 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691286902614&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	static void Output(IEnumerable&amp;lt;string&amp;gt; cohort, string description = &quot;&quot;)
	{
		if (!string.IsNullOrEmpty(description))
		{
			Console.WriteLine(description);
		}

		Console.Write(&quot; &quot;);
		Console.WriteLine(string.Join(&quot;, &quot;, cohort.ToArray()));
		Console.WriteLine();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 학생이름에 대한 3개의 문자열 array를 정의하고 이들을 출력한 뒤 다양한 집합연산을 수행하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691287405006&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] reading = new[] { &quot;홍길동&quot;, &quot;박남일&quot;, &quot;김신영&quot;, &quot;조정석&quot; };
string[] music = new[] { &quot;윤태진&quot;, &quot;박길영&quot;, &quot;서만진&quot;, &quot;윤태진&quot;, &quot;김영수&quot; };
string[] exercise = new[] { &quot;최동만&quot;, &quot;이해영&quot;, &quot;이해영&quot;, &quot;윤태진&quot;, &quot;한봉남&quot; };

Output(reading, &quot;독서&quot;);
Output(music, &quot;음암감상&quot;);
Output(exercise, &quot;운동&quot;);

Output(music.Distinct(), &quot;music.Distinct()&quot;);
Output(music.DistinctBy(name =&amp;gt; name.Substring(0, 2)), &quot;music.DistinctBy(name =&amp;gt; name.Substring(0, 2)):&quot;);
Output(music.Union(exercise), &quot;music.Union(exercise)&quot;);
Output(music.Concat(exercise), &quot;music.Union(exercise)&quot;);
Output(music.Intersect(exercise), &quot;music.Intersect(exercise)&quot;);
Output(music.Except(exercise), &quot;music.Union(exercise)&quot;);
Output(reading.Zip(music, (c1, c2) =&amp;gt; $&quot;{c1} matched with {c2}&quot;), &quot;reading.Zip(music)&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5VztW/btsFB4Is5rO/RM8tskkiltKGS4ZGlAIhYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5VztW/btsFB4Is5rO/RM8tskkiltKGS4ZGlAIhYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5VztW/btsFB4Is5rO/RM8tskkiltKGS4ZGlAIhYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5VztW%2FbtsFB4Is5rO%2FRM8tskkiltKGS4ZGlAIhYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;609&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 Zip은 2개의 sequences에서 item의 수가 동일하지 않다면 일부 item에서 mathing 되지 않을 수 있습니다. 따라서 music에 '김영수'는 결과에 포함되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DistinctBy는 전체이름을 비교하여 중복을 제거하는 대신 이름에서 처음 2글자만 비교하여 제거하기 위해 lambda key selector를 정의하였고 따라서 2명의 '윤태진'중 하나는 제거되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 우리는 LINQ to Objects 공급자를 사용하여 in-memory 개체를 대상으로 예제를 구현하였습니다. 이후부터는 LINQ to Entities 공급자를 사용하여 database에 저장된 entity를 대상으로 예제를 구현해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. EF Core와 LINQ&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 LINQ query를 통해 filter와 sort를 수행해 보았습니다. 하지만 sequence의 item 형태는 어떤 것도 변함이 없습니다. 이것은 projection이라고 하는 것으로 어떤 형태의 item을 다른 형태로 projecting 하는 것이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Projection에 관해 더 알아보려면 좀 더 복잡한 type을 가지고 해 보는 것도 좋은 방법이므로 다음 project에서는 string sequences를 사용하는 대신 이전에 설치했었던 Northwind sample database의 entity sequences를 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) EF Core model build&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 작업의 대상이 될 database와 table을 표현하기 위해 EF Core model을 정의해야 합니다. 따라서 우선은 Categories와 Categories table사이에 자동적으로 관계가 정의되는 것을 막고 완전한 제어권을 가져오기 위해 model을 수동적으로 정의할 것입니다. 그런 다음 LINQ를 사용하여 이 둘의 entity를 join 해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy11 solution에서 LinqWithEFCore이름의 console app project를 생성합니다. 그리고 LinqWithEFCore project에서 SQL Server에 대한 EF Core provider package를 설치합니다. 실제 database는 아래 글에서 이미 설치하였으므로 database를 생성하는 과정은 다루지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-10-Entity-Framework-Core&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 10. Entity Framework Core&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 project에 Northwind.cs, Category.cs, Product.cs라는 3개의 class file을 추가할 것입니다. 우선 Northwind.cs file은 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691304440261&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Northwind : DbContext
{
	public DbSet&amp;lt;Category&amp;gt; Categories { get; set; } = null!;

	public DbSet&amp;lt;Product&amp;gt; Products { get; set; } = null!;

	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		string connection = &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;MultipleActiveResultSets=true;TrustServerCertificate=true&quot;;
		optionsBuilder.UseSqlServer(connection);
	}

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		if ((Database.ProviderName is not null))
		{
			modelBuilder.Entity&amp;lt;Product&amp;gt;().Property(product =&amp;gt; product.UnitPrice).HasConversion&amp;lt;double&amp;gt;();
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Category.cs는 아래와 같이 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1691304557572&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Category
{
	public int CategoryId { get; set; }

	[Required]
	[StringLength(15)]
	public string CategoryName { get; set; } = null!;

	public string? Description { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product.cs는 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691304655532&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Product
{
	public int ProductId { get; set; }

	[Required]
	[StringLength(40)]
	public string ProductName { get; set; } = null!;
	public int? SupplierId { get; set; }
	public int? CategoryId { get; set; }

	[StringLength(20)]
	public string? QuantityPerUnit { get; set; }

	[Column(TypeName = &quot;money&quot;)]
	public decimal? UnitPrice { get; set; }
	public short? UnitsInStock { get; set; }
	public short? UnitsOnOrder { get; set; }
	public short? ReorderLevel { get; set; }
	public bool Discontinued { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Sequence filtering 하고 sorting 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table로부터 sequence를 filtering 하고 sorting 하기 위해 project에 Functions.cs이름의 class를 file을 추가하고 partial Program class를 정의한 뒤 product를 filter 하고 sort 하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691305023712&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void FilterAndSort()
{
	using (Northwind db = new())
	{
		DbSet&amp;lt;Product&amp;gt; allProducts = db.Products;

		IQueryable&amp;lt;Product&amp;gt; filteredProducts = allProducts.Where(product =&amp;gt; product.UnitPrice &amp;lt; 10M);

		IOrderedQueryable&amp;lt;Product&amp;gt; sortedAndFilteredProducts = filteredProducts.OrderByDescending(product =&amp;gt; product.UnitPrice);

		Console.WriteLine(&quot;Products that cost less than $10:&quot;);

		foreach (Product p in sortedAndFilteredProducts)
		{
			Console.WriteLine(&quot;{0}: {1} costs {2:$#,##0.00}&quot;, p.ProductId, p.ProductName, p.UnitPrice);
		}

		Console.WriteLine();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 DbSet&amp;lt;T&amp;gt;는 IEnumerable&amp;lt;T&amp;gt;를 구현하고 있으므로 LINQ는 EF Core를 위해 만들어진 model의 entity를 query 하고 조작하기 위해 사용할 수 있습니다. (실제 T대신 TEntity라고 할 수 있지만 해당 generic type의 이름은 기능적인 면을 가지고 있지 않으며 단지 type이 class라는 것만 필요할 뿐입니다. 이름은 그저 class가 entity model이 될 것이라는 예상을 나타낼 뿐입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence는 또한 IEnumerable&amp;lt;T&amp;gt;혹은 IOrderedEnumerable&amp;lt;T&amp;gt;대신 IQueryable&amp;lt;T&amp;gt; (또는 정렬 LINQ method를 호출하고 나면 IOrderedQueryable&amp;lt;T&amp;gt;)를 구현하고 있습니다. 이는 표현식 tree를 사용해 query를 build 하는 LINQ provider를 사용한다는 것을 나타냅니다. 이들은 tree와 같은 data구조를 나타내고 있으며 동적 query의 생성을 가능하게 하는데 SQLite와 같은 외부 data 공급자를 위해 LINQ query를 build 하는데 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ표현식은 SQL과 같은 다른 query언어로 변환될 수 있습니다. foreach나 ToArray와 같은 method를 호출하여 query를 열거하게 되면 query를 실행하게 되고 결과를 구체화하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 FilterAndSort method를 아래와 같이 호출합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsQOIg/btsFz6HcgoE/5QhT0GqQp1d6qNGU3pO1Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsQOIg/btsFz6HcgoE/5QhT0GqQp1d6qNGU3pO1Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsQOIg/btsFz6HcgoE/5QhT0GqQp1d6qNGU3pO1Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsQOIg%2FbtsFz6HcgoE%2F5QhT0GqQp1d6qNGU3pO1Sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;324&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 해당 query가 목적한 바에 대한 결과를 표시하고 있기는 하지만 우리가 필요한 3개의 column대신 product table로부터 모든 column들을 가져오고 있기 때문에 상당히 비효휼적이라고 할 수 있습니다. 실제 생성된 SQL문을 확인해 보기 위해 FilterAndSort method를 아래와 같이 수정하고&lt;/p&gt;
&lt;pre id=&quot;code_1691306248348&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IOrderedQueryable&amp;lt;Product&amp;gt; sortedAndFilteredProducts = filteredProducts.OrderByDescending(product =&amp;gt; product.UnitPrice);

Console.WriteLine(sortedAndFilteredProducts.ToQueryString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 다시 실행해 보면 product에 대한 filtering결과를 표시하기 전에 실행된 SQL을 다음과 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;343&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CPlX1/btsFEgOCqQC/YkVw8DOixQpFWpt2KXeku1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CPlX1/btsFEgOCqQC/YkVw8DOixQpFWpt2KXeku1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CPlX1/btsFEgOCqQC/YkVw8DOixQpFWpt2KXeku1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCPlX1%2FbtsFEgOCqQC%2FYkVw8DOixQpFWpt2KXeku1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;343&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Sequence를 새로운 type으로 projecting 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 projection을 구현하기 전에 우선은 개체 초기화 구문을 다시 검토해 볼 필요가 있습니다. 정의된 class가 있다면 class명이나 new()를 사용해 해당 개체의 instance를 생성할 수 있으며 괄호를 통해 field나 속성의 초기값을 설정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691371584371&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person
{
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
}

Person knownTypeObject = new()
{
    Name = &quot;dongjunkim&quot;,
    DateOfBirth = new(year: 1976, month: 11, day: 14)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 3.0 이부터는 instance에서 익명 type을 허용하고 있으므로 var keyword를 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691371664242&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var knownTypeObject = new()
{
    Name = &quot;dongjunkim&quot;,
    DateOfBirth = new(year: 1976, month: 11, day: 14)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 type을 명시하지는 않았지만 compiler는 Name과 DateOfBirth라는 이름의 2개의 속성에 대한 설정으로부터 익명 type을 추론할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 기법은 특히 새로운 type을 명시적으로 정의하지 않고 기존 type에서 새로운 type으로 project 하기 위해 LINQ query를 작성할 때 도움이 될 수 있습니다. Type은 익명이기에 var 선언된 지역변수에서만 작동할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 Product class의 instance를 새로운 익명 type으로 투영하기 위한 select method의 호출을 추가함으로써 database table을 상대로 SQL명령을 더 효휼적으로 실행하는 방식을 보여주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs의 FilterAndSort method에서 실제 필요로 하는 세 개의 속성(table의 column에 해당하는)만을 반환하는 Select method를 사용하기 위해 LINQ query를 확장하는 문을 아래와 같이 추가하고 var keyword와 projection LINQ 표현식을 사용하도록 아래와 같이 foreach문을 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691372465730&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IOrderedQueryable&amp;lt;Product&amp;gt; sortedAndFilteredProducts = filteredProducts.OrderByDescending(product =&amp;gt; product.UnitPrice);

var projectedProducts = sortedAndFilteredProducts.Select(product =&amp;gt; new { product.ProductId, product.ProductName, product.UnitPrice });

Console.WriteLine(projectedProducts.ToQueryString());

Console.WriteLine(&quot;Products that cost less than $10:&quot;);

foreach (var p in projectedProducts)
{
	Console.WriteLine(&quot;{0}: {1} costs {2:$#,##0.00}&quot;, p.ProductId, p.ProductName, p.UnitPrice);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Select method에서 new keyword와 foreach문의 var keyword에 mouse를 올려두면 이것이 익명 type임을 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ak9Xm/btsFB8D0VgK/0BFj9c7GKdMCZSt09eirv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ak9Xm/btsFB8D0VgK/0BFj9c7GKdMCZSt09eirv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ak9Xm/btsFB8D0VgK/0BFj9c7GKdMCZSt09eirv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAk9Xm%2FbtsFB8D0VgK%2F0BFj9c7GKdMCZSt09eirv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;103&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;103&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 이전과 같은 결과를 표시하지만 생성된 SQL은 더 효휼적으로 바뀌었음을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwpFn9/btsFBdZZ3oh/a2NdDA3uU9Oi3yBY7ZLQqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwpFn9/btsFBdZZ3oh/a2NdDA3uU9Oi3yBY7ZLQqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwpFn9/btsFBdZZ3oh/a2NdDA3uU9Oi3yBY7ZLQqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwpFn9%2FbtsFBdZZ3oh%2Fa2NdDA3uU9Oi3yBY7ZLQqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;311&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Sequence에 대한 join과 grouping&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Joining과 grouping을 위해 사용할 수 있는 확장 method로 아래와 같은 3가지가 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Join : 해당 method는 join 하고자 하는 sequence 및 연결시킬 left sequence와 right sequence에 해당하는 속성, 그리고 projection인 4개의 매개변수를 필요로 합니다.&lt;/li&gt;
&lt;li&gt;GroupJoin : 해당 method 역시 같은 매개변수를 필요로 하지만 일치하는 항목들을 일치하는 값에 대한 key속성과 다중 일치항목에 대한 IEnumerable&amp;lt;T&amp;gt; type을 가진 group개체로 결합합니다.&lt;/li&gt;
&lt;li&gt;ToLookup : 해당 method는 기존의 sequence에서 key를 통해 group화된 것을 토대로 새로운 data구조를 생성합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Sequence Join&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Categories와 Products라는 2개의 table을 join 하고자 하는 경우 Functions.cs에서 categories와 products를 select 하고 이들을 join 하여 출력하는 method를 아래와 같이 추가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691374125937&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void JoinCategoriesAndProducts()
{
	using (Northwind db = new())
	{
		//Products를 Categories와 join함
        var queryJoin = db.Categories.Join(
			inner: db.Products,
			outerKeySelector: category =&amp;gt; category.CategoryId,
			innerKeySelector: product =&amp;gt; product.CategoryId,
			resultSelector: (c, p) =&amp;gt; new { c.CategoryName, p.ProductName, p.ProductId }
		);
        foreach (var item in queryJoin)
        {
            Console.WriteLine(&quot;{0}: {1} is in {2}.&quot;, arg0: item.ProductId, arg1: item.ProductName, arg2: item.CategoryName);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Join에서는 outer와 inner라는 2개의 sequence가 존재합니다. 위의 예제에서 categories는 outer sequence가 되고 products는 inner sequence가 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 JoinCategoriesAndProducts method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wDxn8/btsFAhu4bm5/ikUFHU5BaIOdgAj2vKhE30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wDxn8/btsFAhu4bm5/ikUFHU5BaIOdgAj2vKhE30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wDxn8/btsFAhu4bm5/ikUFHU5BaIOdgAj2vKhE30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwDxn8%2FbtsFAhu4bm5%2FikUFHU5BaIOdgAj2vKhE30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;query의 끝에서 CategoryName에 대한 정렬을 위해 OrderBy method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691374427041&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var queryJoin = db.Categories.Join(
	inner: db.Products,
	outerKeySelector: category =&amp;gt; category.CategoryId,
	innerKeySelector: product =&amp;gt; product.CategoryId,
	resultSelector: (c, p) =&amp;gt; new { c.CategoryName, p.ProductName, p.ProductId }
).OrderBy(cp =&amp;gt; cp.CategoryName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 실행해 보면 처음에는 Beverages category에 대한 모든 product들이 나오고 그다음 Condiments category에 대한 것들 등.. 의 순서로 결과가 표시됨을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAy242/btsFz8SxppW/eSNf71oMkR7fh9dZMak4cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAy242/btsFz8SxppW/eSNf71oMkR7fh9dZMak4cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAy242/btsFz8SxppW/eSNf71oMkR7fh9dZMak4cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAy242%2FbtsFz8SxppW%2FeSNf71oMkR7fh9dZMak4cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Sequence group-Joining&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 join 하기에 사용했던 Categories와 Products 이 2개의 table에 대해 group-joining을 해봄으로써 약간의 차이를 비교해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 group과 join을 수행하고 group name과 각 group에서의 모든 item을 표시하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691386474731&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void GroupJoinCategoriesAndProducts()
{
	using (Northwind db = new())
	{
        var queryGroup = db.Categories.AsEnumerable().GroupJoin(
			inner: db.Products,
			outerKeySelector: category =&amp;gt; category.CategoryId,
			innerKeySelector: product =&amp;gt; product.CategoryId,
			resultSelector: (c, matchingProducts) =&amp;gt; new
			{
				c.CategoryName,
				Products = matchingProducts.OrderBy(p =&amp;gt; p.ProductName)
			});

        foreach (var category in queryGroup)
        {
            Console.WriteLine(&quot;{0} has {1} products.&quot;, arg0: category.CategoryName, arg1: category.Products.Count());
            foreach (var product in category.Products)
            {
                Console.WriteLine($&quot; {product.ProductName}&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 예제에서처럼 AsEnumerable method를 호출하지 않는다면 다음과 같은 runtime 예외가 발생할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s9VGD/btsFCZT9otf/hitYZ9alKfwIdsnosIDKd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s9VGD/btsFCZT9otf/hitYZ9alKfwIdsnosIDKd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s9VGD/btsFCZT9otf/hitYZ9alKfwIdsnosIDKd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs9VGD%2FbtsFCZT9otf%2FhitYZ9alKfwIdsnosIDKd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 모든 LINQ 확장 method가 표현식 tree로부터 SQL과 같은 일부 다른 query문으로 변환되지 않기 때문입니다. 이 경우 예제는 application으로 data를 가져오기 위한 LINQ to EF Core사용을 위해 query처리를 강제하고 memory에서 더 복잡한 처리를 실행하기 위한 LINQ to Objects를 사용하는 AsEnumerable method를 호출함으로써 IQueryable&amp;lt;T&amp;gt;에서 IEnumerable&amp;lt;T&amp;gt;로 변환하고 있습니다. 하지만 대게 이러한 처리는 효율성면에서 더 떨어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 GroupJoinCategoriesAndProducts method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/okVQG/btsFD6yyGO0/jwyTyTGd83c0Efk8hadHB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/okVQG/btsFD6yyGO0/jwyTyTGd83c0Efk8hadHB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/okVQG/btsFD6yyGO0/jwyTyTGd83c0Efk8hadHB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FokVQG%2FbtsFD6yyGO0%2FjwyTyTGd83c0Efk8hadHB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 각 category에 포함된 product들이 query에서 정의된 것처럼 이름순으로 정렬되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● lookup을 위한 group화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ query 표현식을 통해 join과 group를 작성하는 대신 lookup 확장 method를 통해 entity를 group화한 개체를 생성하고 memory에 개체를 저장할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind의 database의 Products table을 보면 category를 위한 일부 column이 포함되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Product&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chai&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chang&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Aniseed Syrup&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chef Anton&amp;rsquo;s Cajun Seasoning&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Chef Anton&amp;rsquo;s Gumbo Mix&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 category별로 product의 entity를 그룹화한 data구조를 생성하고자 한다면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1710117865950&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ILookup&amp;lt;int, Product&amp;gt; productsByCategoryId = db.Products.ToLookup(keySelector: category =&amp;gt; category.CategoryId);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ToLookup method에서는 group화에 사용될 값으로서 key selector를 지정해야 합니다. 이를 통해 key와 값을 가진 dictionary형태의 data를 생성하게 되며 이때 key는 category ID가 되고 값은 Product 개체의 collection이 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;key&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;Chai, Chang...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;Aniseed Syrup, Chef Anton&amp;rsquo;s Cajun Seasoning, Chef Anton&amp;rsquo;s Gumbo Mix, ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;...&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Sequences 집계하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ 확장 method 중에는 집계기능을 수행하는 Average나 Sum과 같은 것들이 존재합니다. 아래 예제를 통해 이들 method를 사용하여 Products에 대한 집계정보를 확인하는 방법을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 다음과 같이 다양한 집계 확장 method를 사용하는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691454545650&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void AggregateProducts()
{
	using (Northwind db = new())
	{
        if (db.Products.TryGetNonEnumeratedCount(out int countDbSet))
        {
            Console.WriteLine(&quot;{0,-25} {1,10}&quot;, arg0: &quot;Product count from DbSet:&quot;, arg1: countDbSet);
        }
        else
        {
            Console.WriteLine(&quot;Products DbSet does not have a Count property.&quot;);
        }

        List&amp;lt;Product&amp;gt; products = db.Products.ToList();

        if (products.TryGetNonEnumeratedCount(out int countList))
        {
            Console.WriteLine(&quot;{0,-25} {1,10}&quot;, arg0: &quot;Product count from list:&quot;, arg1: countList);
        }
        else
        {
            Console.WriteLine(&quot;Products list does not have a Count property.&quot;);
        }

        //aggregation extension methods
        Console.WriteLine(&quot;{0,-25} {1,10}&quot;, arg0: &quot;Product count:&quot;, arg1: db.Products.Count());
        Console.WriteLine(&quot;{0,-27} {1,8}&quot;, arg0: &quot;Discontinued product count:&quot;, arg1: db.Products.Count(product =&amp;gt; product.Discontinued));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, arg0: &quot;Highest product price:&quot;, arg1: db.Products.Max(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, arg0: &quot;Sum of units in stock:&quot;, arg1: db.Products.Sum(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, arg0: &quot;Sum of units on order:&quot;, arg1: db.Products.Sum(p =&amp;gt; p.UnitsOnOrder));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, arg0: &quot;Average unit price:&quot;, arg1: db.Products.Average(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, arg0: &quot;Value of units in stock:&quot;, arg1: db.Products.Sum(p =&amp;gt; p.UnitPrice * p.UnitsInStock));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Count를 확인하는 것은 단순한 동작처럼 보이지만 처리비용이 많이 들 수 있습니다. Products와 같은 DbSet&amp;lt;T&amp;gt;는 Count 속성을 가지고 있지 않으므로 TryGetNonEnumeratedCount는 false를 반환합니다. 하지만 products 같은 List&amp;lt;T&amp;gt;는 ICollection을 구현하므로 Count속성을 가지고 있고 따라서 TryGetNonEnumeratedCount는 true를 반환합니다. (이 경우 그 자체로 비용이 많이 드는 list를 instance화 해야 했지만 이미 list가 있고 item의 수를 알아야 한다면 이것이 더 효율적일 수 있습니다.) DbSet&amp;lt;T&amp;gt;에서 언제나 Count() 호출할 수 있지만 이는 sequence를 열거해야 하므로 느릴 수 있으며 lambda 식을 Count()에 전달하여 sequence의 count되어야할 item을 filter할 수 있지만 Count나 Length는 사용할 수 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 AggregateProducts method를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byawGN/btsFCv6P1t2/PYd1hcVryjpMirH6KRO6HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byawGN/btsFCv6P1t2/PYd1hcVryjpMirH6KRO6HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byawGN/btsFCv6P1t2/PYd1hcVryjpMirH6KRO6HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyawGN%2FbtsFCv6P1t2%2FPYd1hcVryjpMirH6KRO6HK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;192&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 빈 sequence를 확인하는 방법&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 sequence를 확인하는데는 아래와 같은 방법이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Count method&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ의 Count method를 호출하여 결과가 0이상인지를 확인합니다. 이 방법은 sequence items 전체를 열거해야 하므로 좋은 방법은 아닙니다. Sequence가 ICollection 혹은 ICollection&amp;lt;T&amp;gt;를 구현하고 있으면 Count 속성이 더 효휼적인 방법일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Any method&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ의 Any method를 호출하여 true여부를 확인합니다. Count method보다는 괜찮지만 아래 2가지 방법을 사용할 수 있다면 추천하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Count 속성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence의 Count 속성을 사용하여 0이상인지를 확인합니다. ICollection 혹은 ICollection&amp;lt;T&amp;gt;를 구현하는 모든 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;sequence는 Count속성을 가집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;● Length 속성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence의 Length속성을 사용하여 0이상인지를 확인합니다. 기본적으로 모든 array는 Length속성을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Sequence가 Count나 Length속성을 가지고 있다면 해당 속성을 가장 우선적으로 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Count에서의 주의점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amichai Mantinband은 Microsoft의 software engineer로서 C#과 .NET 개발자 stack의 흥미로운 부분에 대한 주목할만한 일을 하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 그는 Twitter, LinkedIn, YouTube에 개발자들이 이 code가 무엇을 할 것이라고 생각하는지를 알아보기 위한 설문과 함께 code teaser를 post 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그의 code는 이렇습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691474166123&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IEnumerable&amp;lt;Task&amp;gt; tasks = Enumerable.Range(0, 2).Select(_ =&amp;gt; Task.Run(() =&amp;gt; Console.WriteLine(&quot;*&quot;)));
await Task.WhenAll(tasks);
Console.WriteLine($&quot;{tasks.Count()} stars!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 code의 결과는 무엇일까요?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;**2 stars!&lt;/li&gt;
&lt;li&gt;**2 stars!**&lt;/li&gt;
&lt;li&gt;****2 stars!&lt;/li&gt;
&lt;li&gt;이외 다른 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 설문결과 대부분 틀린 것을 골랐습니다. 여기서 말하고자 하는 핵심이 바로 이것이며 위와 같은 까다로운 질문에서 task와 함께 multi-threadiing의 세부적인 부분 까지는 아니더라도 LINQ부분을 살펴볼 수 있습니다. 위 code에서는 LINQ부분을 이해하기 위해 아래와 같이 code를 분리해 보겠습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;Enumerable.Range(0, 2)&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;0과 1의 두 정수에 대한 sequence를 반환합니다. code를 좀더 명확히 하기위해 이 부분에서 명명된 매개변수를 아래와 같이 추가할 수도 있을 것입니다.&lt;br /&gt;Enumerable.Range(start: 0, count: 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;Select(_ =&amp;gt; Task.Run(...)&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;두 숫자 각각에서 자체 thread를 가진 task를 생성합니다. 이때 _ 매개변수는 숫자값을 버리게 되며 각각의 task는 console로 *을 표시하게 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;await Task.WhenAll(tasks);&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;두 task가 완료될때까지 main thread를 block하게 되며 2개의 *문자가 console에 표시됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;tasks.Count()&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;해당 scenario에서 LINQ Count() method가 작동하려면 sequence를 열거해야 하며 이렇게 하면 2개의 task를 다시 실행시킬 수 있습니다. 다만 이 2개의 task가 실행될 시점을 알 수 없고 2의 값이 호출한 method로 부터 반환됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;Console.WriteLine( $&quot;... stars!&quot;);&lt;/td&gt;
&lt;td style=&quot;width: 70.5814%;&quot;&gt;'2 stars!'가 console에 출력됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우리는 **이 console에 먼저 출력되고 그다음 하나 또는 2개의 task가 *을 출력하고 그 뒤 '2 stars!'가 출력된다는 것을 알 수 있습니다. 마지막으로 하나 또는 2개의 task가 이전에 수행할 시간이 없거나 main thread가 종료되어 task가 *을 출력하기 전에 console app을 종료할 수 있는 경우 *을 출력할 수 있으므로 결과를 다음과 같을 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;**[각 task는 여기에 *을 출력할 수도 있음]2 starts![각 task는 여기에 *을 출력할 수 있음]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Amichai의 teaser에 대한 가장 정확한 답은 'Something else'일 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반환값을 계산하기 위해 sequence전체를 열거해야 하는 Count 등의 LINQ 확장 method를 호출할 때는 주의가 필요합니다. Task와 같이 실행가능한 개체의 sequence를 사용하지 않는다 하더라도 sequence를 다시 열거하는 것은 비효율적일 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) LINQ를 사용한 paging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skip과 Take 확장 method를 사용하면 paging을 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서 아래와 같이 array로 전달한 products의 table을 console에 출력하는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691481143796&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputTableOfProducts(Product[] products, int currentPage, int totalPages)
{
    string line = new('-', count: 73);
    string lineHalf = new('-', count: 30);

    Console.WriteLine(line);
    Console.WriteLine(&quot;{0,4} {1,-40} {2,12} {3,-15}&quot;, &quot;ID&quot;, &quot;Product Name&quot;, &quot;Unit Price&quot;, &quot;Discontinued&quot;);
    Console.WriteLine(line);

    foreach (Product p in products)
    {
        Console.WriteLine(&quot;{0,4} {1,-40} {2,12:C} {3,-15}&quot;, p.ProductId, p.ProductName, p.UnitPrice, p.Discontinued);
    }

    Console.WriteLine(&quot;{0} Page {1} of {2} {3}&quot;, lineHalf, currentPage + 1, totalPages + 1, lineHalf);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;일반적인 programming세계에서 순서는 0부터 시작합니다. 따라서 currentPage count와 totalPages count에 대한 값을 사용자에게 표시하기 전 1을 더해줄 필요가 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 products에 대한 page를 만드는 LINQ query를 생성하고 여기서 만들어진 SQL을 출력한 뒤 products의 table을 표시하는 method에서 products의 array로 해당 결과를 전달하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691481629651&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputPageOfProducts(IQueryable&amp;lt;Product&amp;gt; products, int pageSize, int currentPage, int totalPages)
{
    // skip과 take전에 data를 정렬하여 각 page에서 data가 임의의 순서로 나오지 않도록 해야 합니다.
    var pagingQuery = products.OrderBy(p =&amp;gt; p.ProductId).Skip(currentPage * pageSize).Take(pageSize);

    OutputTableOfProducts(pagingQuery.ToArray(), currentPage, totalPages);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 사용자가 왼쪽(left) 혹은 오른쪽(right) 화살표를 눌러 database의 products를 한 번에 한 page씩 표시하도록 순환하는 method를 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691482077339&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void PagingProducts()
{
	using (Northwind db = new())
	{
		int pageSize = 10;
		int currentPage = 0;
		int productCount = db.Products.Count();
		int totalPages = productCount / pageSize;

		while (true)
		{
            OutputPageOfProducts(db.Products, pageSize, currentPage, totalPages);
            Console.Write(&quot;Press &amp;lt;- to page back, press -&amp;gt; to page forward, any key to exit.&quot;);

            ConsoleKey key = Console.ReadKey().Key;

            if (key == ConsoleKey.LeftArrow)
                if (currentPage == 0)
                    currentPage = totalPages;
                else
                    currentPage--;
            else if (key == ConsoleKey.RightArrow)
                if (currentPage == totalPages)
                    currentPage = 0;
                else
                    currentPage++;
            else
                break; // loop 탈출

            Console.WriteLine();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 PagingProducts method를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzuW7I/btsFDG7XGg8/MIo8vl31rZcr0TdMkO2Vzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzuW7I/btsFDG7XGg8/MIo8vl31rZcr0TdMkO2Vzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzuW7I/btsFDG7XGg8/MIo8vl31rZcr0TdMkO2Vzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzuW7I%2FbtsFDG7XGg8%2FMIo8vl31rZcr0TdMkO2Vzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;480&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 예제의 결과에서 ORDER BY, LIMIT, OFFSET 등을 사용해 products의 page를 효휼적으로 가져오기 위해 사용된 SQL문의 표시는 제외하였습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽, 오른쪽 key를 누르면 page를 전환할 수 있고 그 외 다른 key를 누르면 program을 빠져나오게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Paging을 위해 Skip과 Take를 호출하는 경우라면 그전에 data를 정렬해야 합니다. 이는 query가 실행될 때마다 ProductId순이나 UnitPrice순 혹은 심지어 random처럼 특정 기준으로의 정렬이 요청될 수 있으므로 LINQ 공급자가 같은 순서의 data가 반환된다는 것을 보증하지 않기 때문입니다. 관계형 database에서 대게는 primary key상의 index순으로 사용하는 경우가 많습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. LINQ 구문의 향상기법&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 3.0에서는 2008년에 SQL경험을 가진 개발자가 LINQ query를 더 쉽게 만들기 위한 몇 가지 새로운 언어 keyword를 도입하였으며 이를 LINQ query 이해(comprehension) 구문이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 문자열형식의 array가 존재할 때&lt;/p&gt;
&lt;pre id=&quot;code_1691546895595&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string[] names = new[] { &quot;이순신&quot;, &quot;이도&quot;, &quot;신사임당&quot;, &quot;홍길동&quot;, &quot;김종서&quot;, &quot;박혁거세&quot;, &quot;김좌진&quot;, &quot;윤봉길&quot; };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름별로 filter 하고 정렬하기 위해 아래와 같이 확장 method와 lambda 식을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691547000212&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
    .Where(name =&amp;gt; name.Length &amp;gt; 3)
    .OrderBy(name =&amp;gt; name.Length)
    .ThenBy(name =&amp;gt; name);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 같은 결과를 만들어내기 위해 아래와 같이 query 이해 문을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691547094651&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = from name in names
            where name.Length &amp;gt; 3
            orderby name.Length, name
            select name;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식이 작동할 수 있는 이유는 compiler가 query 이해 문을 이전예제와 동일하게 확장 method와 lambda 식으로 변환하기 때문입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;select keyword는 LINQ query 이해 구문에서 반드시 필요한 존재합니다. 하지만 Select 확장 method는 확장 method와 lambda 식을 사용할 때 Select를 호출하지 않으면 item전체가 암시적으로 선택되므로 선택적으로 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 확장 method가 C# keyword와 동일하게 연결되지는 않습니다. 예를 들어 Skip과 Take 확장 method는 일반적으로 많은 data에서 paging을 구현하는 데 사용되곤 합니다. Query comprehense만으로 작성될 수 없는 skip과 take query는 모두 확장 method를 사용해 다음과 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691548849066&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = names
    .Where(name =&amp;gt; name.Length &amp;gt; 3)
    .Skip(80)
    .Take(10);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 괄호를 통해 query 이해 문을 감싸고 난 후 이를 확장 method로 연결하여 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691548937922&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var query = (from name in names
                where name.Length &amp;gt; 4
                select name)
                .Skip(80)
                .Take(10);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. 사용자 LINQ 확장 method 정의하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글을 통해서 우리는 이미 자신만의 확장 method를 생성하는 방법을 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.02.20 - [.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ 확장 method를 만들기 위해 해야 할 것은 IEnumerable&amp;lt;T&amp;gt; type을 확장하는 것이 전부입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;직접 정의한 method는 별도의 class file을 만들어 분리해 두면 자체 assembly나 NuGet package 등으로 배포하기 수훨해 질 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 우리는 평균을 아래 3가지 중 하나가 될 수 있음을 말할 수 있는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mean : 수를 합산하고 건수로 나눈 것&lt;/li&gt;
&lt;li&gt;Mode : 가장 일반적인 숫자&lt;/li&gt;
&lt;li&gt;Median : 정렬되고 난 후의 숫자 중에서 중간값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 Average 확장 method를 mean으로 계산되도록 구현하였으므로 여기서 Average method를 위에서 언급한 Mode와 Median계산이 가능하도록 해야 한다면 다음과 같은 방법으로 method를 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에 MyLinqExtensions.cs file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691627431010&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace LinqWithEFCore
{
    public static class MyLinqExtensions
    {
        // chain LINQ extension method
        public static IEnumerable&amp;lt;T&amp;gt; ProcessSequence&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence)
        {
            // you could do some processing here
            return sequence;
        }

        public static IQueryable&amp;lt;T&amp;gt; ProcessSequence&amp;lt;T&amp;gt;(this IQueryable&amp;lt;T&amp;gt; sequence)
        {
            // you could do some processing here
            return sequence;
        }

        // scalar LINQ extension methods
        public static int? Median(this IEnumerable&amp;lt;int?&amp;gt; sequence)
        {
            var ordered = sequence.OrderBy(item =&amp;gt; item);
            int middlePosition = ordered.Count() / 2;

            return ordered.ElementAt(middlePosition);
        }

        public static int? Median&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, int?&amp;gt; selector)
        {
            return sequence.Select(selector).Median();
        }

        public static decimal? Median(this IEnumerable&amp;lt;decimal?&amp;gt; sequence)
        {
            var ordered = sequence.OrderBy(item =&amp;gt; item);
            int middlePosition = ordered.Count() / 2;

            return ordered.ElementAt(middlePosition);
        }

        public static decimal? Median&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, decimal?&amp;gt; selector)
        {
            return sequence.Select(selector).Median();
        }

        public static int? Mode(this IEnumerable&amp;lt;int?&amp;gt; sequence)
        {
            var grouped = sequence.GroupBy(item =&amp;gt; item);
            var orderedGroups = grouped.OrderByDescending(group =&amp;gt; group.Count());

            return orderedGroups.FirstOrDefault()?.Key;
        }

        public static int? Mode&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, int?&amp;gt; selector)
        {
            return sequence.Select(selector)?.Mode();
        }

        public static decimal? Mode(this IEnumerable&amp;lt;decimal?&amp;gt; sequence)
        {
            var grouped = sequence.GroupBy(item =&amp;gt; item);
            var orderedGroups = grouped.OrderByDescending(group =&amp;gt; group.Count());

            return orderedGroups.FirstOrDefault()?.Key;
        }

        public static decimal? Mode&amp;lt;T&amp;gt;(this IEnumerable&amp;lt;T&amp;gt; sequence, Func&amp;lt;T, decimal?&amp;gt; selector)
        {
            return sequence.Select(selector).Mode();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class가 별도의 class library로 분리되어 있다면 LINQ 확장 method를 사용하기 위해서는 그저 class library assembly를 참조하기만 하면 됩니다. 참고로 System.Linq namespace는 이미 암시적으로 import 되는 것이 기본이기 때문에 대부분 별다른 추가작업이 필요 없지만 그렇지 않다면 System.Linq도 import 되어야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;상기 예제의 모든 method 중 하나를 제외하고 그 외 다른 method는 SQL과 같은 query언어로 변환하는 것까지 구현하지 않았기 때문에 LINQ to SQLite 또는 LINQ to SQL Server에서 사용된 것과 같은&amp;nbsp; IQueryable sequence와 함께 사용할 수 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Chainable extension method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 예제에서 처음 2개의 method는 ProcessSequence method와의 chaining이 가능한 method입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs의 FilterAndSort method에서 Products에 대한 LINQ query를 아래와 같이 변경하여 예제의 확장 method를 호출하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691630113324&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DbSet&amp;lt;Product&amp;gt; allProducts = db.Products;

IQueryable&amp;lt;Product&amp;gt; processedProducts = allProducts.ProcessSequence();
				
IQueryable&amp;lt;Product&amp;gt; filteredProducts = processedProducts.Where(product =&amp;gt; product.UnitPrice &amp;lt; 10M);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 FilterAndSort method를 호출하도록 하고 예제를 실행하면 이전과 같은 결과를 볼 수 있을 것입니다. 여기서는 예제의 ProcessSequence method가 실제 sequence를 변경하는 것은 아니지만 이러한 구현을 통해 LINQ표현식을 위 method처럼 확장할 수 있다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Mode와 median method 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 종류의 평균을 계산하기 위한 Mode and Median method를 사용하기 위해 Functions.cs에서 products의 UnitPrice와 UnitsInStock에 대한 mean, median, mode를 이전 예제에서 구현한 확장 method와 기본 내장 Average extension method를 사용해 출력하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691630717396&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void CustomExtensionMethods()
{
	using (Northwind db = new())
	{
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, &quot;Mean units in stock:&quot;, db.Products.Average(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, &quot;Mean unit price:&quot;, db.Products.Average(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, &quot;Median units in stock:&quot;, db.Products.Median(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, &quot;Median unit price:&quot;, db.Products.Median(p =&amp;gt; p.UnitPrice));
        Console.WriteLine(&quot;{0,-25} {1,10:N0}&quot;, &quot;Mode units in stock:&quot;, db.Products.Mode(p =&amp;gt; p.UnitsInStock));
        Console.WriteLine(&quot;{0,-25} {1,10:$#,##0.00}&quot;, &quot;Mode unit price:&quot;, db.Products.Mode(p =&amp;gt; p.UnitPrice));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 CustomExtensionMethods를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4LGD8/btsFDANrGTW/2DhcOKi26merdcFyJlHFt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4LGD8/btsFDANrGTW/2DhcOKi26merdcFyJlHFt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4LGD8/btsFDANrGTW/2DhcOKi26merdcFyJlHFt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4LGD8%2FbtsFDANrGTW%2F2DhcOKi26merdcFyJlHFt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;136&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 $18.00인 products는 4개이고 재고가 0개인 products는 5개입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. LINQ to XML&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ to XML은 query에 대한 결과를 XML로 가져올 수 있도록 하는 LINQ 공급자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) LINQ to XML을 사용한 XML 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Products table을 XML로 변환하고자 한다면 products를 XML형식으로 출력하는 method를 다음과 같이 추가할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691653369153&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputProductsAsXml()
{
	using (Northwind db = new())
	{
        Product[] productsArray = db.Products.ToArray();

        XElement xml = new(&quot;products&quot;,
			from p in productsArray
			select new XElement(&quot;product&quot;, new XAttribute(&quot;id&quot;, p.ProductId), new XAttribute(&quot;price&quot;, p.UnitPrice), new XElement(&quot;name&quot;, p.ProductName)));
                
		Console.WriteLine(xml.ToString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 OutputProductsAsXml method를 호출하도록 한 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HzOZj/btsFEjxQmkA/cEom8wF9ELaRAKHnZYcCmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HzOZj/btsFEjxQmkA/cEom8wF9ELaRAKHnZYcCmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HzOZj/btsFEjxQmkA/cEom8wF9ELaRAKHnZYcCmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHzOZj%2FbtsFEjxQmkA%2FcEom8wF9ELaRAKHnZYcCmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 XML의 구조는 XML 요소와 위 예제에서 선언적으로 서술된 LINQ to XML문의 attribute와 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) LINQ to XML을 사용한 XML parsing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ to XML을 사용하면 XML file을 쉽게 질의하거나 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에 settings.xml이름의 XML file을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691654877066&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;memberSettings&amp;gt;
	&amp;lt;add key=&quot;Name&quot; value=&quot;hong&quot; /&amp;gt;
	&amp;lt;add key=&quot;Age&quot; value=&quot;30&quot; /&amp;gt;
	&amp;lt;add key=&quot;WorkGroup&quot; value=&quot;IT&quot; /&amp;gt;
&amp;lt;/memberSettings&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio 2022는 compile 된 application이 bin\Debug\net7.0 folder에서 실행되므로 추가한 XML을 해당 folder로 복사되도록 해야 합니다. 따라서 XML file을 선택하고 'Copy to Output Directory'의 속성을 'Copy always'로 변경해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Functions.cs에서는 아래와 같은 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1691655196289&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void ProcessSettings()
{
	string path = Path.Combine(Environment.CurrentDirectory, &quot;settings.xml&quot;);

	Console.WriteLine($&quot;Settings file path: {path}&quot;);

	XDocument doc = XDocument.Load(path);
	var memberSettings = doc.Descendants(&quot;memberSettings&quot;).Descendants(&quot;add&quot;)
	.Select(node =&amp;gt; new
	{
		Key = node.Attribute(&quot;key&quot;)?.Value,
		Value = node.Attribute(&quot;value&quot;)?.Value
	}).ToArray();
	foreach (var item in memberSettings)
	{
		Console.WriteLine($&quot;{item.Key}: {item.Value}&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 method는 XML file을 load 하여 LINQ to XML을 사용해 memberSettings이름의 요소와 하위 add요소를 찾습니다. 그런 다음 XML을 Key와 Value 속성을 가진 익명 type의 array로 구체화한뒤 이를 열거하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 ProcessSettings method를 호출하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdAw85/btsFAFvGkGW/Rp0fMcfUnRqRzZMzQbiMkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdAw85/btsFAFvGkGW/Rp0fMcfUnRqRzZMzQbiMkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdAw85/btsFAFvGkGW/Rp0fMcfUnRqRzZMzQbiMkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdAw85%2FbtsFAFvGkGW%2FRp0fMcfUnRqRzZMzQbiMkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;109&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>c#</category>
      <category>LINQ</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/601</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-11-LINQ#entry601comment</comments>
      <pubDate>Mon, 11 Mar 2024 12:04:55 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 10. Entity Framework Core</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-10-Entity-Framework-Core</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Entity Framework Core (이하 EF Core)는 SQLite나 SQL Server와 같은 관계형 database에 data를 읽고 쓰기 위한 객체-데이터 저장 mapping 기술입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. Database&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database에는 크게 2가지 종류가 있는데 하나는 RDBMS(Relational Database Management System)으로 SQL Server, PostgreSQL, MySQL, SQLite 등이 있고 다른 하나는 NoSQL로서 Azure Cosmos DB, Redis, MongoDB, Apache Cassandra 등이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 database는 1970년대 개발된 것으로 SQL(Structured Query Language)을 통해 data를 질의합니다. 그 당시 data를 저장하는데는 많은 비용이 소모되었으므로 가능한 한 data의 중복을 최소화해야 했습니다. Data는 행(Row)과 열(Column)로 이루어진 table구조에 저장되었는데 일단 완성되고 나면 refactor 하기에는 까다로운 구조였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 단순히 SQL이 없음을 의미하지는 않습니다. 다른 뜻으로는 굳이 SQL일 필요가 없다는 의미가 될 수도 있습니다. 2000년대부터 개발되어온 NoSQL은 당시 Internet과 Web이 대중화되고 software에 관한 많은 학습이 이루어지던 시기였으며 또한 막대한 확장성과 고성능을 위해 설계되었고 최대한의 유연성을 제공함으로써 개발자들의 집입장벽을 낮추었습니다. 또한 구조를 강제하지 않음으로써 이들 사이에서는 언제든 schema를 변경할 수 있는 특징이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 과거 Entity Framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity Framework는 2008년 말 Service pack 1과 함께 .NET Framework 3.5의 일부로 처음 출시되었습니다. 그런 후 Entity Framework는 지금까지 계속해서 발전을 거듭하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORM은 mapping정의를 사용하여 class의 속성을 table의 column으로 연결합니다. 그러면 개발자는 관계형 table 혹은 NoSQL data store에 의해 제공된 다른 구조로 값을 어떻게 저장해야 할지 알아야 하는 대신 자신에게 친숙한 방식으로 다른 type 간 개체를 통해 상호작용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Framework에 포함된 EF의 version은 Entity Framework 6 (EF6)로서 성숙되고 안정적이며 복잡한 상속 model뿐 아니라 model을 정의하는 EDMX(XML file)와 그 외 몇 가지 향상된 기능을 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF 6.3부터는 .NET Framework에서 별도의 package로 분리되어 .NET Core 3.0부터 지원이 가능하게 되었습니다. Web application이나 service와 같은 기존 project에서 cross-platform으로 이식하고 동작할 수 있으나 EF6는 cross-platform에서 동작시 일부 제한적이며 더 이상 새로운 기능이 추가되지 않으므로 legacy technology로 간주되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Entity Framework의 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Legacy technology인 Entity Framework를 .NET Core 3.0이나 이후 project에서 사용하려면 project file에서 아래와 같이 package 참조를 추가해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689562026849&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PackageReference Include=&quot;EntityFramework&quot; Version=&quot;6.4.4&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예전 WPF app을 migration하는 경우와 같은 상황만 필요하다면 EF6를 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Entity Framework Core&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진정한 cross-platform version인 EF Core는 Entity Framework와의 가장 큰 차이라고 할 수 있습니다. 비록 EF Core가 이름이 비슷하기는 하지만 EF6와는 분명히 다른 것입니다. 가장 최신의 EF Core version은 8이며 .NET8과 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 5부터는 .NET5와 그 이후만 지원하며 그 이하 EF Core 3은 .NET Standard 2.1을 지원하는 platform에서만 동작합니다. EF Core 3부터는 .NET Framework 4.8과 같은 .NET Standard 2.0 platform을 지원하지 않습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;EF Core 8은 .NET 8부터의 version을 target으로 합니다. 다시 말해 EF Core 8에서의 모든 새로운 기능은 .NET 8혹은 .NET 9에서만 사용할 수 있습니다. 만약 현재 .NET 7을 사용 중이라면 version 8에서 참조하는 EF Core package를 upgrade 할 것을 권장합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 RDBMS뿐만 아니라 EF Core는 cloud기반의 비관계형 Database인 Azure Cosmos DB와 MongoDB등도 지원하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core는 많은 개선점을 가지고 있지만 이 모두를 다루지는 않을 것입니다. 예를 들어 EF Core 7과 함께 도입된 새로운 기능에는 JSON 문서를 저장하는 column을 가질 수 있는 database가 해당 문서를 질의하고 filter와 정렬 표현식에서 문서의 요소를 사용하는 JSON column을 지원하지만 EF Core 7에서 JSON column 기능은 단지 SQL Server를 위해서만 구현됩니다. 향후 EF Core version은 SQLite와 같은 다른 database도 추가될 테고 그 시점에 해당 내용을 다루게 될지도 모르지만 현재는 개발자가 알아야 할 기본적인 사항과 일부 유용한 기능을 살펴보는데만 집중할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Database First와 Code First&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF core를 사용하기 위해서는 아래 2가지 방법을 선택할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database First : Database가 이미 존재하는 상태에서 구조및 특징과 일치하는 model을 build 합니다.&lt;/li&gt;
&lt;li&gt;Code First : Database가 존재하지 않는다면 model을 build 한 뒤 해당 구조 및 특징과 일치하는 database를 EF Core를 사용해 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) EF Core에서의 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core team은 EF Core의 성능향상을 위해 많은 노력을 기울여 왔습니다. 예를 들어 EF Core 7이 SaveChanges가 호출될 때 database에 대해 단일문만이 실행된다고 식별하는 경우 이전 version과 마찬가지로 명시적인 transaction을 생성하지 않습니다. 이와 같은 방식은 일반적인 경우에서 약 25% 정도의 성능향상을 기대할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 내부적으로 적용된 최신의 개선사항들은 이들이 어떻게 동작하는가에 대한 이해가 없이도 사용하는 것만으로 그에 대한 혜택을 얻을 수 있습니다. 만약 이러한 세부적인 사항에 관심이 있다면(특히 일부 멋진 SQL Server 기능을 활용한 방식에 대해) EF Core team이 작성한 아래 글을 읽어보시길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot;&gt;Announcing Entity Framework Core 7 Preview 6: Performance Edition (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689580629937&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Announcing Entity Framework Core 7 Preview 6: Performance Edition - .NET Blog&quot; data-og-description=&quot;Announcing Entity Framework Core 7 Preview 6 with update performance optimizations.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-ef-core-7-preview6-performance-optimizations/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Announcing Entity Framework Core 7 Preview 6: Performance Edition - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Announcing Entity Framework Core 7 Preview 6 with update performance optimizations.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot;&gt;Announcing Entity Framework Core 6.0 Preview 4: Performance Edition (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689580636669&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Announcing Entity Framework Core 6.0 Preview 4: Performance Edition - .NET Blog&quot; data-og-description=&quot;EF Core 6.0 Preview 4 is here and has the best performance ever. Read this post to learn how the team boosted speed by 70% over EF Core 5.0!&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sCgvV/hyTleD3YqP/PzJ3b4mEMJF1TkyKcAedpk/img.png?width=1605&amp;amp;height=552&amp;amp;face=0_0_1605_552,https://scrap.kakaocdn.net/dn/bbwevS/hyTmoSAjeD/E9PwrgPZgCk3zqeJsntUN0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=109_131_349_393,https://scrap.kakaocdn.net/dn/F2Af2/hyTk60iSUo/cisreKRM244C1xFjPphq5K/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-performance-edition/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sCgvV/hyTleD3YqP/PzJ3b4mEMJF1TkyKcAedpk/img.png?width=1605&amp;amp;height=552&amp;amp;face=0_0_1605_552,https://scrap.kakaocdn.net/dn/bbwevS/hyTmoSAjeD/E9PwrgPZgCk3zqeJsntUN0/img.jpg?width=460&amp;amp;height=460&amp;amp;face=109_131_349_393,https://scrap.kakaocdn.net/dn/F2Af2/hyTk60iSUo/cisreKRM244C1xFjPphq5K/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Announcing Entity Framework Core 6.0 Preview 4: Performance Edition - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;EF Core 6.0 Preview 4 is here and has the best performance ever. Read this post to learn how the team boosted speed by 70% over EF Core 5.0!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) EF Core를 사용한 console app 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 console app을 만들기 위해 csStudy10 solution을 생성하고 그 안에 WorkingWithEFCore이름의 Console App project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Sample database&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET을 통해 RDBMS를 어떻게 사용할 수 있을지를 알아보기 위해서는 적당한 수의 record와 적당한 수준의 복잡도를 가진 sample database가 필요합니다. 이를 위해 Microsoft는 몇 가지 sample database를 제공하고 있는데 너무 복잡한 것을 제외하고 사용가능한 가장 적합한 database로 1990년대 가장 먼저 생성된 database인 Northwind를 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격지나 로컬에 MSSQL Server를 설치하고 Northwind Database를 아래 script file로 생성합니다. MSSQL에 관한 제사한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Server/SQL Server] - [MSSQL] MS SQL Server 다운로드 및 설치/설정&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689648100728&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[MSSQL] MS SQL Server 다운로드및 설치/설정&quot; data-og-description=&quot;해당 포스팅에서 설명하는 MS SQL Server는 2019 Developer Edtion입니다. 인터넷에서 아래 주소로 이동합니다. SQL Server 다운로드 | Microsoft SQL Server 다운로드 | Microsoft 지금 Microsoft SQL Server 다운로드를 시&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; data-og-url=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/doAEh3/hyTk9pBH0B/25F1Xb51nkn3Ey3g9t99GK/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/9Yjv7/hyTk6NdivL/kKQZcjbDZJZHUKgxRfKQwk/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/q83Yn/hyTmyg06Gr/ekUfGE7W82TRVWwBs6nM6K/img.png?width=2048&amp;amp;height=1256&amp;amp;face=0_0_2048_1256&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/MSSQL-MS-SQL-Server-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EB%B0%8F-%EC%84%A4%EC%B9%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/doAEh3/hyTk9pBH0B/25F1Xb51nkn3Ey3g9t99GK/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/9Yjv7/hyTk6NdivL/kKQZcjbDZJZHUKgxRfKQwk/img.png?width=800&amp;amp;height=490&amp;amp;face=0_0_800_490,https://scrap.kakaocdn.net/dn/q83Yn/hyTmyg06Gr/ekUfGE7W82TRVWwBs6nM6K/img.png?width=2048&amp;amp;height=1256&amp;amp;face=0_0_2048_1256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[MSSQL] MS SQL Server 다운로드및 설치/설정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 포스팅에서 설명하는 MS SQL Server는 2019 Developer Edtion입니다. 인터넷에서 아래 주소로 이동합니다. SQL Server 다운로드 | Microsoft SQL Server 다운로드 | Microsoft 지금 Microsoft SQL Server 다운로드를 시&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 해당 Database의 전체 구조는 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;993&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0vuNG/btsFipf2hko/ZoFBWVkHH6KBDn7sbZ1ls0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0vuNG/btsFipf2hko/ZoFBWVkHH6KBDn7sbZ1ls0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0vuNG/btsFipf2hko/ZoFBWVkHH6KBDn7sbZ1ls0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0vuNG%2FbtsFipf2hko%2FZoFBWVkHH6KBDn7sbZ1ls0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;993&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;993&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조를 보면 다음과 같은 사항을 알 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 category는 고유한 식별자, 이름, 설명, 이미지를 갖고 있습니다.&lt;/li&gt;
&lt;li&gt;각 product는 고유한 식별자, 이름, 단위 단가, 재고 단위 및 기타 field를 갖고 있습니다.&lt;/li&gt;
&lt;li&gt;각 product는 category의 고유한 식별자를 통해 특정 category에 할당됩니다.&lt;/li&gt;
&lt;li&gt;Category와 product의 관계는 각 category가 product를 0개 이상 가질 수 있는 1대다의 관계입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/P6Y0O/btsFkVk7RYC/ok44lV6uOEzTchU0gRDYSk/northwnd.sql?attach=1&amp;amp;knm=tfile.sql&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;northwnd.sql&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;1.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. EF Core 설정&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core를 사용하기 전 간단히 어떤 EF Core database 공급자를 선택할 수 있는지를 알아보도록 하겠습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) ADO.NET database provider&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity Framework이전에는 ADO.NET이 사용되었습니다. ADO.NET은 EF와 비교해 database를 대상으로한 더 단순하면서도 더 효휼적인 API인데 DbConnection, DbCommand, and DbReader같은 추상화 class와 SqlConnection과&amp;nbsp; SqlCommand같은 공급자별 구현을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 application에 대한 database로 MS SQL Server를 사용하기로 했다면 SqlConnectionStringBuilder class를 통해 정확한 연결문자열을 사용해야 합니다. 해당 class에는 database 연결 문자열에 대한 모든 가능한 속성을 가지고 있고 이를 개별적으로 설정하여 완성된 연결문자열을 얻을 수 있습니다. 또한 비밀번호와 같은 민감한 정보의 경우에는 source code에 직접 작성하는 대신 환경변수나 별도의 관리 system에서 가져올 수 있도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 SQLite라면 연결문자열은 굳이 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SqlConnectionStringBuilder class를 사용하지 않아도 될만큼 간단하므로 굳이 class를 사용할 필요는 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite와 SQL Server에 대한 EF Core database 공급자는 항상 ADO.NET Library위에 구현되므로 본질적으로 ADO.NET보다 더 느립니다. 게다가 ADO.NET은 더 나은 성능을 위해 독립적으로 사용될 수 있지만 EF Core database 공급자는 ORM의 특징에 더 중심을 두기 때문에 성능면에서 ADO.NET이 유리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 EF Core은 아직 AOT 배포를 지원하지 않으므로 만약 AOT를 사용하기로 했다면 EF Core대신 ADO.NET Library를 사용해야 합니다. 물론 EF Core team은 EF Core를 native AOT에 어떻게 지원할지를 살펴보고 있지만 시간적으로 꽤 걸릴것으로 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ADO.NET에 관한 더 자세한 내용은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sql/connect/ado-net/microsoft-ado-net-sql-server?view=sql-server-ver16&quot;&gt;Microsoft ADO.NET - ADO.NET Provider for SQL Server | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) EF Core database provider (공급자)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 database의 data에 접근하기 위해서는 어떻게 database와 효휼적으로 data를 주고받을지를 알고 있는 class를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core database 공급자는 이러한 일련의 class들을 말하며 특정한 database에 최적화되어 있습니다. 이러한 공급자들 중에는 심지어 현재 process의 memory에 data를 저장하는 공급자도 존재하며 특히 외부 system과의 충돌을 피할 수 있기 때문에 고성능 단위 test에서 사용하기 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래표는 저장소별 NuGet package에 배포된 package를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;용도&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;NuGet Package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;SQL Server 2012 부터&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.SqlServer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;SQLite 3.7 부터&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.SQLite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;In-memory&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.InMemory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;Azure Cosmos DB SQL API&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Microsoft.EntityFrameworkCore.Cosmos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;MySQL&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;MySQL.EntityFrameworkCore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;Oracle DB 11.2&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Oracle.EntityFrameworkCore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.186%;&quot;&gt;PostgreSQL&lt;/td&gt;
&lt;td style=&quot;width: 65.814%;&quot;&gt;Npgsql.EntityFrameworkCore.PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 하나의 project에서 다수의 EF Core database 공급자를 설치할 수 있으며 각 package는 공급자별 type뿐만 아니라 필요한 여러 공유 type을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Database 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database연결을 위해서는 연결을 위한 연결문자열을 설정해야 합니다. 위에서 MSSQL Server를 설치했다는 가정하에 해당 server IP 및 login 계정 등의 정보를 묶어 하나의 연결문자열을 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Northwind database context class 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind class는 database를 표현하는 데 사용될 것입니다. 여기에 EF core를 사용하기 위해서 class는 DbContext로부터 상속받아야 합니다. 해당 class는 database와 어떻게 소통할지를 알고 있으며 data의 질의와 수정/삭제등의 조작을 위해 동적으로 SQL문을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext로 부터 파생된 class는 OnConfiguring라는 이름의 override 된 method를 갖게 되는데 해당 method를 통해 database 연결 문자열을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 project에서 MSSQL을 위한 EF Core 공급자인 Microsoft.EntityFrameworkCore.SqlServer NuGet package를 설치하고 Northwind.cs 이름의 class file을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs file에서는 EF Core에 대한 namespace를 import한뒤 Northwind class를 정의하고 DbContext부터 상속받도록 합니다. 그리고 OnConfiguring method에서 MSSQL Server를 사용하기 위한 option builder를 아래와 같이 구성해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689662682446&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Northwind : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string connection = &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;MultipleActiveResultSets=true&quot;;
        Console.WriteLine($&quot;Connection: {connection}&quot;);
        optionsBuilder.UseSqlServer(connection);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 사용하는 database 공급자를 확인할 수 있는 구문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689662808467&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Northwind db = new();
Console.WriteLine($&quot;Provider: {db.Database.ProviderName}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 이를 통해 database 연결문자열과 사용 중인 database 공급자를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L8LbA/btsFiq0lOwW/Wsk5DSlu2eslkHlKmDanmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L8LbA/btsFiq0lOwW/Wsk5DSlu2eslkHlKmDanmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L8LbA/btsFiq0lOwW/Wsk5DSlu2eslkHlKmDanmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL8LbA%2FbtsFiq0lOwW%2FWsk5DSlu2eslkHlKmDanmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. EF Core model 정의&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core는 규칙과 annotation attribute의 결합 및 Fluent API 문을 사용하여 runtime에서 entity model을 build 합니다. 따라서 class상에서 수행되는 모든 동작은 나중에 자동적으로 실제 database에서 수행되는 동작으로 변환될 수 있습니다. entity class는 table의 구조를 나타내며 class의 instance는 table의 row로서 표현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 예제를 통해 model을 정의하기 위한 3가지 방식을 검토하고 이러한 기술을 구현하는 몇 가지 class를 만들어볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) model을 정의하기 위한 EF Core 규칙&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로 작성될 code는 아래 규칙을 사용할 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Table의 이름은 예를 들어 Products와 같이 DbContext class안에 있는 DbSet&amp;lt;T&amp;gt; 속성의 이름과 연결된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;Column의 이름은 예를 들어 ProductId와 같이 entity model class에 있는 속성의 이름과 연결된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;string .NET type은 database의 nvarchar type이 된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;int .NET type은 database의 int type이 된다고 간주합니다.&lt;/li&gt;
&lt;li&gt;Primary key는 Id 혹은 ID이름의 속성이 된다고 간주합니다. 그런데 속성의 이름은 entity model class가 Product로 명명될 때 ProductId 혹은 ProductID로 명명될 수 있습니다. 해당 속성이 integer type 이거나 Guid type이라면 이것은 또한 IDENTITY column(insert시 자동적으로 값이 부여됨)이 될 수 있다고 간주합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에 알아야 할 많은 규칙들이 존해하며 자신만의 것을 정의할 수도 있습니다. 이와 관련해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot;&gt;Creating and Configuring a Model - EF Core | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689664291146&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Creating and Configuring a Model - EF Core&quot; data-og-description=&quot;Overview of creating and configuring a Entity Framework Core model via Fluent API, Data Annotations and conventions.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dz7bmr/hyTlc05tWy/LspbpkUNrgEzMlBqh5b37K/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/kg0a0/hyTmrCkJsL/51muDfXMvLCvpt4fCEVQ0K/img.png?width=1324&amp;amp;height=814&amp;amp;face=0_0_1324_814&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dz7bmr/hyTlc05tWy/LspbpkUNrgEzMlBqh5b37K/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/kg0a0/hyTmrCkJsL/51muDfXMvLCvpt4fCEVQ0K/img.png?width=1324&amp;amp;height=814&amp;amp;face=0_0_1324_814');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Creating and Configuring a Model - EF Core&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Overview of creating and configuring a Entity Framework Core model via Fluent API, Data Annotations and conventions.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Model을 정의하기 위한 EF Core annotation attribute 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 위에서 언급한 규칙만으로는 database개체와 class를 완전히 연결시키에 충분하지 못할 수 있습니다. 이때 model을 더욱 database에 근접시키기 위한 가장 간단한 방법은 annotation attribute를 적용하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 일반적으로 사용되는 몇 가지 attribute를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Attribute&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[Required]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값은 null이 될 수 없습니다. .NET 8에서는 DisallowAllDefaultValues 매개변수를 통해 예를 들어 int가 0이 되는 것처럼 type이 그에 따른 기본값이 되는것을 방지할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[StringLength(50)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값은 50문자길이를 넘을 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[Range(1, 10)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값이 지정된 범위에 있도록 합니다. 이와 관련하여 .NET 8에서는 MinimumIsExclusive와 MaximumIsExclusive라는 매개변수가 존재합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[RegularExpression(표현식)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;값은 지정한 정규표현식과 일치해야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;[Column(TypeName = &quot;money&quot;, Name = &quot;UnitPrice&quot;)]&lt;/td&gt;
&lt;td style=&quot;width: 54.3023%;&quot;&gt;Column의 이름(Name)과 Type(TypeName)을 지정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 database에서 product name의 최대 길이는 40이며 값은 null일 수 없습니다. 이와 같은 사실은 어떻게 Products라는 table이 지정한 data type, key와 기타 제약사항을 가진 column과 함께 생성될지를 정의하는 DDL(Data Definition Language) 문을 통해 알 수 있는데&lt;/p&gt;
&lt;pre id=&quot;code_1689731574142&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE [dbo].[Products](
	[ProductId] [int] IDENTITY(1,1) NOT NULL,
	[ProductName] [nvarchar](40) NOT NULL,
	[SupplierId] [int] NULL,
	[CategoryId] [int] NULL,
	[QuantityPerUnit] [nvarchar](20) NULL,
	[UnitPrice] [money] NULL,
	[UnitsInStock] [smallint] NULL,
	[UnitsOnOrder] [smallint] NULL,
	[ReorderLevel] [smallint] NULL,
	[Discontinued] [bit] NOT NULL,
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
(
	[ProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 사항을 Product class에 반영하려면 아래와 같이 attribute를 적용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689731680506&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Required]
[StringLength(40)]
public string ProductName { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우에는 .NET type과 database type사이를 연결시키는 데 사용가능한 attribute가 분명하지 않을 수 있습니다. 예를 들어 database에서 Products의 UnitPrice column type이 money인데 .NET은 money라는 type이 존재하지 않으므로 이런 경우에는 아래와 같이 data범위가 유사한 decimal을 대신 사용하고 Column attribute를 통해 database의 type을 명시합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689732042362&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Column(TypeName = &quot;money&quot;)]
public decimal? UnitPrice { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Model을 정의하기 위한 EF Core Fluent API 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model을 정의할 수 있는 또 다른 방법은 Fluent API를 사용하는 것입니다. API는 attribute대신 사용할 수 있으며 뿐만 아니라 속성 외에도 사용할 수 있습니다. 예를 들어 ProductName속성을 정의하기 위해 속성에 2개의 attribute를 적용하는 대신 동등한 Fluent API문을 database context class의 OnModelCreating method에 아래와 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689732450857&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;modelBuilder.Entity&amp;lt;Product&amp;gt;()
 .Property(product =&amp;gt; product.ProductName)
 .IsRequired()
 .HasMaxLength(40);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 entity model class를 단순하게 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Fluent API를 사용한 data 제공&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fluent API를 사용함으로써 생기는 또 다른 이점은 database를 채우기 위해 초기 data를 제공할 수 있다는 것입니다. EF Core는 반드시 실행되어야 하는 insert, update, delete와 같은 동작을 자동으로 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 새로운 database에서 Product table은 최소 하나의 행을 가져야 한다면 HasData method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689732856913&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;modelBuilder.Entity&amp;lt;Product&amp;gt;().HasData(new Product {
    ProductId = 1,
    ProductName = &quot;Chai&quot;,
    UnitPrice = 8.99M
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 사용할 model은 이미 data가 존재하는 database와 연결하게 될 것이므로 위와 같은 방법은 사용하지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Northwind table에 대한 EF Core model 구축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 EF Core model을 정의하는 방법을 알게 되었으므로 이를 통해 Northwind database에 대한 2개 table을 표현하는 model을 만들 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개 entity class는 서로를 참조할 것이므로 compiler error를 피하기 위해 처음에는 어떠한 member도 없이 class를 만들 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithEFCore project에서 Category.cs와 Product.cs이름의 class file을 추가합니다. 그리고 Category.cs에서 Category class를 정의하고 Product.cs에서 Product class를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689751762608&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace WorkingWithEFCore
{
    public class Category
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1689751775224&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace WorkingWithEFCore
{
    public class Product
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Category와 Product entity class정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity model인 Category class는 Categories table의 row를 나타내는 데 사용될 것입니다. 이 table은 아래와 같이 4개의 column을 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689751960880&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE [dbo].[Categories](
	[CategoryId] [int] IDENTITY(1,1) NOT NULL,
	[CategoryName] [nvarchar](15) NOT NULL,
	[Description] [ntext] NULL,
	[Picture] [image] NULL,
 CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED 
(
	[CategoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 model을 정의하기 위한 다음의 규칙을 감안해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3~4개의 속성 (Picture column은 사용하지 않을 것입니다.)&lt;/li&gt;
&lt;li&gt;Primary key&lt;/li&gt;
&lt;li&gt;Products table과의 1:N 관계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Description column을 database type과 정확히 연결시키기 위해서는 string 속성에 Column attribute에 대한 적용이 필요합니다. 나중에는 Fluent API를 사용하여 CategoryName이 null일 수 없고 최대 15자로 제한됨을 정의할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Category entity model class를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689752586814&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.ComponentModel.DataAnnotations.Schema; // [Column] attribute

namespace WorkingWithEFCore
{
    public class Category
    {
        public Category()
        {
            // products를 Cateogry로 추가시킬 수 있도록 하기 위해서는 navigation 속성을 빈 collection으로 초기화 해야 합니다.
            Products = new HashSet&amp;lt;Product&amp;gt;();
        }

        //아래 속성은 database의 column과 일치합니다.
        public int CategoryId { get; set; }

        public string? CategoryName { get; set; }

        [Column(TypeName = &quot;ntext&quot;)]
        public string? Description { get; set; }

        //Table간의 관계를 나타내기 위해 정의하는 navigation속성입니다.
        public virtual ICollection&amp;lt;Product&amp;gt; Products { get; set; }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product class도 역시 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689752905654&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.ComponentModel.DataAnnotations; // [Required], [StringLength]
using System.ComponentModel.DataAnnotations.Schema; // [Column]

namespace WorkingWithEFCore
{
    public class Product
    {
        public int ProductId { get; set; } // primary key

        [Required]
        [StringLength(40)]
        public string ProductName { get; set; } = null!;

        [Column(&quot;UnitPrice&quot;, TypeName = &quot;money&quot;)]
        public decimal? Cost { get; set; } // Column attribute를 적용함으로서 여기서 속성의 이름은 column의 이름으로 사용되지 않습니다.

        [Column(&quot;UnitsInStock&quot;)]
        public short? Stock { get; set; }

        public bool Discontinued { get; set; }

        // 이 2개의 속성은 Categories table에 대한 외래key를 정의합니다.
        public int CategoryId { get; set; }
        public virtual Category Category { get; set; } = null!;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 다음 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Product class는 Products table의 row를 표현하는 데 사용되며 10개 정도의 column을 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;Table로부터의 모든 column을 class의 속성으로 포함시킬 필요는 없습니다. 예제에서도 단지 ProductId, ProductName, UnitPrice, UnitsInStock, Discontinued, CategoryId에 대한 6개의 속성만을 일치시키고 있습니다.&lt;/li&gt;
&lt;li&gt;속성으로 일치되지 않는 column은 class를 통해 읽거나 설정할 수 없습니다. 새로운 개체를 생성하는데 class를 사용한다면 table의 새로운 row에서 일치되지 않는 column은 NULL이 되거나 다른 기본값이 됩니다. 누락된 column이 생기는 경우 이들 column은 선택적이거나 database에서 기본값을 설정했거나 runtime에서 예외가 발생하는지를 확인해야 합니다. 지금 예제의 경우에 row는 이미 data값을 가지고 있으며 application에서 해당 값들을 읽을 필요가 없으므로 무시하기로 합니다.&lt;/li&gt;
&lt;li&gt;예제에서 Cost속성처럼 Column의 이름과는 다른 이름으로 속성을 정의할 수도 있습니다. 이런 경우 속성에 [Column] attribute를 적용하고 여기에 UnitPrice처럼 column의 이름을 지정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;마지막 속성인 CategoryId는 각 product에서 부모 category와 일치시키는 데 사용되는 Category 속성과 연결됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 entity를 연결시키는 2개의 속성인 Category.Products와 Product.Category는 둘 다 virtual로 수식되었습니다. 이는 EF Core가 lazy loading과 같이 추가 기능을 제공하기 위해 해당 속성을 상속하고 override 할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Northwind database context class에 table추가하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext로부터 파생된 class에서는 최소 하나의 DbSet&amp;lt;T&amp;gt; type에 대한 속성을 정의해야 합니다. 이 속성은 table을 의미합니다. EF Core에게 table의 가진 각 column이 어떤 것인지를 말해주기 위해 DbSet&amp;lt;T&amp;gt;속성은 generic을 사용하여 table의 row를 나타내는 class를 지정하고 해당 class는 column을 나타내는 속성을 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext 파생 class는 선택적으로 OnModelCreating이름의 override method를 가질 수 있습니다. 여기에 Fluent API를 class에 attribute를 적용하는 대신 작성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind class에서 Categories와 Products에 대한 2개의 table과 OnModelCreating method를 정의하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689823022563&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Northwind : DbContext
{
    public DbSet&amp;lt;Category&amp;gt;? Categories { get; set; }
    public DbSet&amp;lt;Product&amp;gt;? Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string connection = &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;MultipleActiveResultSets=true&quot;;
        Console.WriteLine($&quot;Connection: {connection}&quot;);
        optionsBuilder.UseSqlServer(connection);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Fluent API를 사용해 CategoryName의 최대 길이를 15자로 제한합니다.
        // 이는 attribute를 적용하는 것으로도 가능합니다.
        modelBuilder.Entity&amp;lt;Category&amp;gt;()
        .Property(category =&amp;gt; category.CategoryName)
        .IsRequired() // NOT NULL
        .HasMaxLength(15);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) dotnet-ef 도구 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotnet이라는 .NET CLI 도구는 EF Core사용에 유용한 기능으로 확장될 수 있습니다. 이 기능은 이전 model에서 새로운 model로 migration을 생성하고 적용하는 것과 같은 design-time작업을 수행할 수 있으며 기존 database로부터 code를 생성할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotnet ef 명령줄 도구는 자동적으로 설치되지 않으므로 global이나 local도구를 통해 package를 설치해야 합니다. 만약 해당 도구의 이전 version이 이미 설치된 경우라면 기존 version을 모두 삭제해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal에서 우선 dotnet-ef가 global도구로 설치되어 있는지를 확인합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool list --global&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgVdtv/btsFh8FtT0R/ON3wl0YP52RlMIjgaKy860/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgVdtv/btsFh8FtT0R/ON3wl0YP52RlMIjgaKy860/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgVdtv/btsFh8FtT0R/ON3wl0YP52RlMIjgaKy860/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgVdtv%2FbtsFh8FtT0R%2FON3wl0YP52RlMIjgaKy860%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;506&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 이전 version이 이미 설치되어 있다면 아래와 같은 방식으로 도구를 제거할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool uninstall --global dotnet-ef&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y2DDg/btsFiuayCfo/XqOXPVSdWdmsKHl7FW1i4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y2DDg/btsFiuayCfo/XqOXPVSdWdmsKHl7FW1i4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y2DDg/btsFiuayCfo/XqOXPVSdWdmsKHl7FW1i4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy2DDg%2FbtsFiuayCfo%2FXqOXPVSdWdmsKHl7FW1i4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;90&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제의 화면상으로 version은 7.0.4인데 아래 명령을 통해 최신 version으로 update할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool update --global dotnet-ef&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치는 uninstall대신 install을 사용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool install --global dotnet-ef&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A6nfQ/btsFkGVQAsy/SJQiRL9pravaOlhLLLJVEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A6nfQ/btsFkGVQAsy/SJQiRL9pravaOlhLLLJVEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A6nfQ/btsFkGVQAsy/SJQiRL9pravaOlhLLLJVEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA6nfQ%2FbtsFkGVQAsy%2FSJQiRL9pravaOlhLLLJVEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;106&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 OS별 지침에 따라 PATH 환경 변수에 dotnet tools directory를 추가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 기존 database를 사용한 Scaffolding model&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 database에서 model을 표현하기 위한 class를 생성하는 것을 reverse engineering이라고 하며 이때 도구를 사용하는 처리를 Scaffolding이라고 합니다. 좋은 Scaffolding 도구는 자동적으로 생성된 class를 확장하고 확장된 class의 손실 없이 이들 class를 재생성할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구를 사용해 class를 재생성할 일이 없다고 하더라도 원하는 만큼 자동적으로 생성된 class에서 code를 자유롭게 변경할 수 있습니다. 도구에 의해 생성된 code는 그저 최상의 근사치일 뿐입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;도구를 사용하지 않고도 최상의 결과를 얻을 수 있다면 굳이 도구사용을 시도할 필요는 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 수동적으로 생성했던 것처럼 도구가 같은 model을 생성하는지 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 WorkingWithEFCore project에서 최신의 Microsoft.EntityFrameworkCore.Design package를 설치하고 project를 build 합니다. 그런 뒤 terminal에서 WorkingWithEFCore folder(csproj project file이 있는)로 찾아 들어가고 아래 명령을 통해 AutoGenModels라는 folder에 Categories와 Products에 대한 model을 생성하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet ef dbcontext scaffold &quot;Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true&quot; Microsoft.EntityFrameworkCore.SqlServer --table Categories --table Products --output-dir AutoGenModels --namespace WorkingWithEFCore.AutoGen --data-annotations --context Northwind&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 명령줄에 사용된 것 중 server주소와 id 및 암호는 귀하의 server에 따라 다릅니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령줄에 사용된 각 항목의 의미는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령어는 dbcontext scaffold입니다.&lt;/li&gt;
&lt;li&gt;연결문자열은 Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true 입니다.&lt;/li&gt;
&lt;li&gt;Database 공급자는 Microsoft.EntityFrameworkCore.SqlServer 입니다.&lt;/li&gt;
&lt;li&gt;Model로 생성하기 위한 table은 --table Categories --table Products로 지정합니다.&lt;/li&gt;
&lt;li&gt;Model은 --output-dir AutoGenModels로 지정한 경로에 생성합니다.&lt;/li&gt;
&lt;li&gt;Model의 namespace는 --namespace WorkingWithEFCore로 지정합니다.&lt;/li&gt;
&lt;li&gt;Fluent API와 data annotation 사용을 위해 --data-annotations를 지정합니다.&lt;/li&gt;
&lt;li&gt;context명칭을 --context Northwind로 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 실행하면 정상적으로 처리되는 경우 아래와 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bssmEl/btsFitQbtBz/XT066DsBO8H9YTEKoUQzP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bssmEl/btsFitQbtBz/XT066DsBO8H9YTEKoUQzP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bssmEl/btsFitQbtBz/XT066DsBO8H9YTEKoUQzP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbssmEl%2FbtsFitQbtBz%2FXT066DsBO8H9YTEKoUQzP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1155&quot; height=&quot;186&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 완료되면 AutoGenModels folder를 열어 아래 3개 file이 생성되어 있는지를 확인합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Northwind.cs&lt;/li&gt;
&lt;li&gt;Product.cs&lt;/li&gt;
&lt;li&gt;Category.cs&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 Category file을 열어 이전에 수동으로 작성한 file과의 차이점을 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689839188515&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace WorkingWithEFCore.AutoGen;

[Index(&quot;CategoryName&quot;, Name = &quot;CategoryName&quot;)]
public partial class Category
{
    [Key]
    public int CategoryId { get; set; }

    [StringLength(15)]
    public string CategoryName { get; set; } = null!;

    [Column(TypeName = &quot;ntext&quot;)]
    public string? Description { get; set; }

    [Column(TypeName = &quot;image&quot;)]
    public byte[]? Picture { get; set; }

    [InverseProperty(&quot;Category&quot;)]
    public virtual ICollection&amp;lt;Product&amp;gt; Products { get; set; } = new List&amp;lt;Product&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Entity class에는 [Index] attribute가 적용되어 있습니다. 이는 EF Core 5.0에서 도입된 것으로 index가 있어야 하는 속성을 나타냅니다. 이전 version에서는 단지 Fluent API를 통해서만 index를 정의할 수 있었습니다. 기존 database로의 작업에서는 이것이 필요하지 않았지만 code를 통해 초기 database를 재생성하고자 한다면 해당 정보가 필요할 것입니다.&lt;/li&gt;
&lt;li&gt;Database에서 table의 이름은 Categories이지만 dotnet-ef 도구는 Humanizer third-party library를 사용하여 class의 이름을 Category로 단수화 하였습니다. 이는 단일 entity를 생성할 때 이름을 더욱 자연스럽게 만들기 위한 것입니다.&lt;/li&gt;
&lt;li&gt;Entity class는 partial keyword를 사용하여 선언되었으므로 여기에 추가적인 code가 필요할 때 일치하는 partial class를 생성할 수 있습니다. 이를 통해 신규로 추가한 code의 손실 없이 도구를 사용해 entity class를 재생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;CategoryId속성은 [Key] attribute가 적용되었습니다. 이는 해당 entity가 primary key임을 나타냅니다. 속성의 data type은 SQL Server에서의 int입니다.&lt;/li&gt;
&lt;li&gt;Products 속성은 [InverseProperty] attribute를 사용하여 Product entity class상에서 Category 속성으로 외래 key의 관계를 정의하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product.cs file도 열어 이전에 수동적으로 생성한 것과의 차이를 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs file도 역시 이전에 수동적으로 생성한 것과의 차이를 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689841219409&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace WorkingWithEFCore.AutoGen;

public partial class Northwind : DbContext
{
    public Northwind()
    {
    }

    public Northwind(DbContextOptions&amp;lt;Northwind&amp;gt; options)
        : base(options)
    {
    }

    public virtual DbSet&amp;lt;Category&amp;gt; Categories { get; set; }

    public virtual DbSet&amp;lt;Product&amp;gt; Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
        =&amp;gt; optionsBuilder.UseSqlServer(&quot;Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true&quot;);

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;
        {
            entity.Property(e =&amp;gt; e.ReorderLevel).HasDefaultValueSql(&quot;((0))&quot;);
            entity.Property(e =&amp;gt; e.UnitPrice).HasDefaultValueSql(&quot;((0))&quot;);
            entity.Property(e =&amp;gt; e.UnitsInStock).HasDefaultValueSql(&quot;((0))&quot;);
            entity.Property(e =&amp;gt; e.UnitsOnOrder).HasDefaultValueSql(&quot;((0))&quot;);

            entity.HasOne(d =&amp;gt; d.Category).WithMany(p =&amp;gt; p.Products).HasConstraintName(&quot;FK_Products_Categories&quot;);
        });

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Northwind data context class는 parital이므로 code를 확장하고 이후에 재생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;위의 결과를 보면 2개의 생성자를 갖고 있음을 알 수 있는데 매개변수가 없는 기본 생성자와 options 매개변수를 가진 생성자가 그것입니다. 이는 runtime에서 연결문자열을 지정하고자 할 때 유용하게 사용될 수 있습니다.&lt;/li&gt;
&lt;li&gt;위 결과에는 나타나지 않았지만 Categories와 Products table을 나타내는 2개의 DbSet&amp;lt;T&amp;gt;속성이 null-forgiving연산자값으로 설정될 수 있습니다. 이는 static compiler analysis가 compile time에서 경고가 발생되는 것을 방지하기 위함이며 runtime에서는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;위 결과에는 나타나지 않았지만 OnConfiguring method안에서는 options가 생성자에서 지정되지 않은 경우를 대비한 다음과 같은 code가 만들어질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1689843874891&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        //#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
        optionsBuilder.UseSqlServer(&quot;Server=localhost;user id=sa;password=!123;Database=Northwind;TrustServerCertificate=true&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또한 연결 문자열에 민감한 정보를 hardcode 하지 말 것을 알리기 위한 compiler 경고를 포함하고 있습니다.&lt;/li&gt;
&lt;li&gt;OnModelCreating method안에서는 Fluent API가 Product에 대한 entity class를 구성하기 위해 사용되었으며 OnModelCreatingPartial이름의 partial method를 호출합니다. 이를 통해 별도의 Fluent API configuration을 추가하기 위해 partial Northwind class를 따로 만들어&amp;nbsp; partial method method를 구현할 수 있고 model class를 재생성한다고 하더라도 추가한 code에는 영향을 주지 않을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Reverse engineering template 사용자 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 7에서 새롭게 도입된 기능 중 하나이며 dotnet-ef scaffolding tool에 의해 자동적으로 생성되는 code를 개별적으로 정의할 수 있습니다. 해당 내용에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot;&gt;Custom Reverse Engineering Templates - EF Core | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689904937614&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Custom Reverse Engineering Templates - EF Core&quot; data-og-description=&quot;Using T4 text templates to customize the scaffolded code when reverse engineering an Entity Framework Core model from a database&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cX7kiV/hyTnNTzdy3/CRZpdcqIs1rVCx4SQeRQtK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/templates?tabs=dotnet-core-cli&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cX7kiV/hyTnNTzdy3/CRZpdcqIs1rVCx4SQeRQtK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Custom Reverse Engineering Templates - EF Core&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Using T4 text templates to customize the scaffolded code when reverse engineering an Entity Framework Core model from a database&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) 사전 규칙 model 구성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entity type과 그들의 속성에 의존하는 규칙에서 model이 더 복잡해질수록 이들을 table과 column에 성공적으로 연결시키기는 더욱 어려워집니다. 따라서 model을 분석하고 생성하기 전에 스스로에 대한 규칙을 정의할 수 있다면 해당 기능은 매우 유용하게 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 모든 string 속성은 최대 문자길이가 기본적으로 50까지만 허용된다거나 사용자 interface를 구현하는 모든 속성은 연결에서 제외되어야 한다는 규칙을 정의하고자 한다면 다음과 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689908474121&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Properties&amp;lt;string&amp;gt;().HaveMaxLength(50);
    configurationBuilder.IgnoreAny&amp;lt;IUserInterface&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. EF Core model 질의하기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 통해 Northwind database와 2개의 table에 연결되는 model을 만들게 되었습니다. 이로서 이제는 해당 table에서 data를 가져오기 위해 LINQ query를 작성할 수 있습니다. LINQ query를 작성하는 자세한 사항은 추후에 알아보기로 하고 이번예제에서는 간단하게만 사용해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project에 Queries.cs class file을 추가하고 그 안에 QueryingCategories method와 함께 partial Program class를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689924838446&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    static void QueryingCategories()
    {
        using (Northwind db = new())
        {
            IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories?.Include(c =&amp;gt; c.Products);

            if ((categories is null) || (!categories.Any()))
            {
                Console.WriteLine(&quot;Failed&quot;);
                return;
            }
                
            foreach (Category c in categories)
            {
                Console.WriteLine($&quot;{c.CategoryName} has {c.Products.Count} products.&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 다음 사항에 주목합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제에서는 database를 관리할 Northwind class의 instance를 생성하고 있습니다. Database context instance는 작업단위 짧은 생명주기를 위해 설계되었으며 가능한 한 빨리 dispose 되어야 하므로 using문을 사용하였습니다.&lt;/li&gt;
&lt;li&gt;관련된 projects를 포함한 전체 category를 위한 query를 생성합니다. 여기서 Include는 확장 method이며&amp;nbsp; 사용을 위해서는 Microsoft.EntityFrameworkCore namespace를 import 해야 합니다.&lt;/li&gt;
&lt;li&gt;가져온 category를 열거하면서 해당 category의 이름과 포함된 project의 수를 표시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;if 문에서 || 사이의 순서는 중요한 부분입니다. categories가 null인지를 우선적으로 확인해야 하는데 만약 그 결과가 true라면 두 번째 문을 실행되지 않을 것이며 따라서 Any() member에 접근할 때 NullReferenceException 예외가 발생하지 않을 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 Northwind instance를 생성하고 database provider의 이름을 출력하는 두 문을 주석처리하고 QueryingCategories method를 아래와 같이 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689925921829&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;QueryingCategories();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCTY3y/btsFjIsPHRj/kJKQ379z6mXJ7skWcELGW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCTY3y/btsFjIsPHRj/kJKQ379z6mXJ7skWcELGW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCTY3y/btsFjIsPHRj/kJKQ379z6mXJ7skWcELGW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCTY3y%2FbtsFjIsPHRj%2FkJKQ379z6mXJ7skWcELGW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;186&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 포함된 entity filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 5에서는 filtered include를 도입하여 결과에서 반환된 entity를 filter 하기 위해 호출하는 Include method안에서 lambda 식을 명시할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 FilteredIncludes method를 아래와 같이 정의하고 아래 작업을 수행하는 문을 추가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database를 다룰 Northwind class의 instance를 생성합니다.&lt;/li&gt;
&lt;li&gt;사용자에게 재고의 최솟값을 입력받을 수 있도록 요청합니다.&lt;/li&gt;
&lt;li&gt;재고가 입력된 값보다 같거나 그 보다 많은 product를 가진 Category를 가져오기 위해 query를 생성합니다.&lt;/li&gt;
&lt;li&gt;Category와 Product를 열거하면서 각각에 대한 이름과 재고를 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1689926879027&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void FilteredIncludes()
{
    using (Northwind db = new())
    {
        string? input;
        int stock;

        do
        {
            Console.Write(&quot;Enter a minimum for units in stock: &quot;);
            input = Console.ReadLine();

        } while (!int.TryParse(input, out stock));

        IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories?.Include(c =&amp;gt; c.Products.Where(p =&amp;gt; p.Stock &amp;gt;= stock));

        if ((categories is null) || (!categories.Any()))
        {
            Console.WriteLine(&quot;No categories found.&quot;);
            return;
        }

        foreach (Category c in categories)
        {
            Console.WriteLine($&quot;{c.CategoryName} has {c.Products.Count} products with a minimum of {stock} units in stock.&quot;);

            foreach (Product p in c.Products)
            {
                Console.WriteLine($&quot;{p.ProductName} has {p.Stock} units in stock.&quot;);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 위의 FilteredIncludes method를 호출하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFcHxB/btsFm4uWye5/v2WOn5zklgMsSEjDd6bYZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFcHxB/btsFm4uWye5/v2WOn5zklgMsSEjDd6bYZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFcHxB/btsFm4uWye5/v2WOn5zklgMsSEjDd6bYZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFcHxB%2FbtsFm4uWye5%2Fv2WOn5zklgMsSEjDd6bYZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;350&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 10 Fall Creators Update이전 Windows version에서 제공하는 console에는 하나의 제한사항이 있습니다. 기본적으로 console은 Unicode문자를 표시할 수 없습니다. 만약 예제를 실행하는 computer가 그렇다면 임시로 character set으로 알려진 code page를 console에서 아래 명령으로 Unicode UTF-8로 변경함으로써 해결할 수 있습니다.(예제를 실행시키기 전에)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;chcp 65001&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 source에서 unicode문자를 표시하기전 아래 문을 넣어두면 정상적으로 unicode문자를 표시할 수 있게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Console.OutputEncoding&amp;nbsp;=&amp;nbsp;System.Text.Encoding.UTF8;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Product filtering 하고 sorting 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data를 filtering 하고 sorting 하기 위해 Queries.cs에서 QueryingProducts method를 정의하고 그 안에 &amp;nbsp;아래의 동작을 위한 구문을 추가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database를 다룰 Northwind class의 instance를 생성합니다.&lt;/li&gt;
&lt;li&gt;사용자에게 products에 대한 가격을 입력하도록 요청합니다.&lt;/li&gt;
&lt;li&gt;LINQ를 통해 입력한 가격보다 더 높은 products를 가져오는 query를 생성합니다.&lt;/li&gt;
&lt;li&gt;결과를 순회하면서 ID, name, cost(예제에서의 단위는 dollar입니다.)와 재고를 표시하도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690026085470&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void QueryingProducts()
{
    using (Northwind db = new())
    {
        string? input;
        decimal price;
        do
        {
            Console.Write(&quot;Enter a product price: &quot;);
            input = Console.ReadLine();
        } while (!decimal.TryParse(input, out price));

		IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(product =&amp;gt; product.Cost &amp;gt; price).OrderByDescending(product =&amp;gt; product.Cost);

		if ((products is null) || (!products.Any()))
		{
			Console.Write(&quot;No products found.&quot;);
			return;
		}

		foreach (Product p in products)
		{
			Console.WriteLine(&quot;{0}: {1} costs {2:$#,##0.00} and has {3} in stock.&quot;, p.ProductId, p.ProductName, p.Cost, p.Stock);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서의 '!products.Any()'는 'products.Count() == 0'과 같이 count를 확인하는 것보다 더 효휼적으로 작동합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 QueryingProducts method를 호출하면 사용자에게 price입력을 요청하고 그 결과를 다음과 같이 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kMHBO/btsFiN2ca22/AjQkrmjzOKgnhWKuA9P4Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kMHBO/btsFiN2ca22/AjQkrmjzOKgnhWKuA9P4Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kMHBO/btsFiN2ca22/AjQkrmjzOKgnhWKuA9P4Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkMHBO%2FbtsFiN2ca22%2FAjQkrmjzOKgnhWKuA9P4Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;229&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 입력요청에서 price를 500과 같이 입력하면 결과는 다음처음 달라질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctSZMQ/btsFmjeLAMn/mNJoi6KG1ZzlR3w0kLgkV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctSZMQ/btsFmjeLAMn/mNJoi6KG1ZzlR3w0kLgkV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctSZMQ/btsFmjeLAMn/mNJoi6KG1ZzlR3w0kLgkV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctSZMQ%2FbtsFmjeLAMn%2FmNJoi6KG1ZzlR3w0kLgkV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;96&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 생성된 SQL 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황에서 C# code가 실제 어떤 SQL문을 생성하는지 궁금할 수 있습니다. EF Core 5부터는 생성된 SQL문을 보기 위한 빠르고 편리한 다음 방법을 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FilteredIncludes method에서 foreach문을 사용해 query를 열거하기 전에 생성된 SQL문을 출력하는 다음 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690098287132&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if ((categories is null) || (!categories.Any()))
{
    Console.WriteLine(&quot;No categories found.&quot;);
    return;
}

Console.WriteLine($&quot;ToQueryString: {categories.ToQueryString()}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 생성된 SQL문을 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9eRBU/btsFoeYhV4j/xMLmiN1hjvgb13ypoGh341/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9eRBU/btsFoeYhV4j/xMLmiN1hjvgb13ypoGh341/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9eRBU/btsFoeYhV4j/xMLmiN1hjvgb13ypoGh341/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9eRBU%2FbtsFoeYhV4j%2FxMLmiN1hjvgb13ypoGh341%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;590&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) EF Core Logging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core와 database간상호작용을 살펴보기 위해 logging을 사용할 수 있습니다. 이때 logging은 console이나 Debug, Trace 또는 file이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 EF Core logging은 민감한 data는 제외합니다. 만약 이러한 data도 모두 포함하고자 한다면(특히 개발과정 동안에는) EnableSensitiveDataLogging method를 호출할 수 있습니다. 반대로 완성된 Application을 출시하는 경우 해당 기능을 disable 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs의 OnConfiguring method아래에 console로의 log를 위한 아래 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690099001626&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optionsBuilder.UseSqlServer(connection);
optionsBuilder.LogTo(Console.WriteLine).EnableSensitiveDataLogging();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LogTo는 Action&amp;lt;string&amp;gt; delegate를 필요로 하며 EF core는 각 log message에 대한 문자열값을 전달함으로써 해당 delegate를 호출할 것입니다. 따라서 위 예제처럼 Console class의 WriteLine method를 전달하는 것은 logger에게 각 method를 console에 쓰도록 하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 각 log를 상세하게 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;1847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzCzoa/btsFobUQwQo/L6EHaPFJtNg42IF07Tnc51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzCzoa/btsFobUQwQo/L6EHaPFJtNg42IF07Tnc51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzCzoa/btsFobUQwQo/L6EHaPFJtNg42IF07Tnc51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzCzoa%2FbtsFobUQwQo%2FL6EHaPFJtNg42IF07Tnc51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;1847&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;1847&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Log는 사용 중인 database 공급자와 code 편집기 및 향후 EF Core의 개선점에 따라 달라질 수 있습니다. 일반적으로 연결을 열거나 명령 실행과 같은 다른 event마다 다음과 같이 다른 event ID를 가지고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RelationalEventId.ConnectionOpening[20000] : 원격 server 혹은 file 경로를 포함&lt;/li&gt;
&lt;li&gt;RelationalEventId.ConnectionOpened[20001] : 원격 server 혹은 file 경로를 포함&lt;/li&gt;
&lt;li&gt;RelationalEventId.CommandExecuting[20100] : 실행하는 SQL문 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 공급자별 값을 통한 log filtering&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event ID값과 그 의미는 EF Core 공급자에 따라 달라질 수 있습니다. LINQ query가 SQL 문으로 어떻게 변환되고 실행되는지를 알고자 한다면 20100의 Id값을 가진 event ID를 확인하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 아래에서와 같이 Northwind.cs에서 LogTo method를 변경하여 Id가 20100인 event만 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690162401806&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuting }).EnableSensitiveDataLogging();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 log 된 SQL문을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5djcB/btsFlQcS3fz/LgM9FfwkcreFurb0kbMCwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5djcB/btsFlQcS3fz/LgM9FfwkcreFurb0kbMCwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5djcB/btsFlQcS3fz/LgM9FfwkcreFurb0kbMCwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5djcB%2FbtsFlQcS3fz%2FLgM9FfwkcreFurb0kbMCwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;594&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Query tag를 통한 logging&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINQ query를 logging 할 때 복잡한 상황에서 log message를 서로 관련시키기가 어려울 수 있습니다. EF Core 2.2에서는 query tag 기능을 도입하여 SQL 문을 log에 추가시키기 한결 편리해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 TagWith method를 사용해 LINQ query에 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690165111762&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories?.TagWith(&quot;*****Category*****&quot;).Include(c =&amp;gt; c.Products.Where(p =&amp;gt; p.Stock &amp;gt;= stock));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 log에 SQL comment를 다음과 같이 추가하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t1JW1/btsFoe46Ng3/bzyvqJkkHcVPDkh4dxQa10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t1JW1/btsFoe46Ng3/bzyvqJkkHcVPDkh4dxQa10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t1JW1/btsFoe46Ng3/bzyvqJkkHcVPDkh4dxQa10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft1JW1%2FbtsFoe46Ng3%2FbzyvqJkkHcVPDkh4dxQa10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Like를 사용한 pattern matching&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core에서는 pattern matching을 위한 Like와 같이 일반적인 SQL문을 지원하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 다음과 같이 QueryingWithLike이름의 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690165447737&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void QueryingWithLike()
{
    using (Northwind db = new())
    {
        Console.Write(&quot;Enter part of a product name: &quot;);
        string? input = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(input))
        {
            Console.WriteLine(&quot;You did not enter part of a product name.&quot;);
            return;
        }

        IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; EF.Functions.Like(p.ProductName, $&quot;%{input}%&quot;));

        if ((products is null) || (!products.Any()))
        {
            Console.WriteLine(&quot;No products found.&quot;);
            return;
        }

        foreach (Product p in products)
        {
            Console.WriteLine(&quot;{0} has {1} units in stock. Discontinued? {2}&quot;, p.ProductName, p.Stock, p.Discontinued);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 logging을 사용하고 있으며 사용자에게 product에 대한 이름 중 일부에 대한 입력을 요구하고 있습니다. 사용자가 입력을 완료하면 EF.Functions.Like method를 사용해 입력된 내용을 ProductName 속성으로 검색을 시도합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 일치하는 product가 존재하면 각 product에 대한 이름과 재고 그리고 생산여부등을 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 기존의 method 호출을 주석처리하고 위에서 추가한 QueryingWithLike method를 호출하도록 합니다. 그런 뒤 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUcf2i/btsFirLC1aW/KsNgxCfGLQFsJYRgTKDDh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUcf2i/btsFirLC1aW/KsNgxCfGLQFsJYRgTKDDh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUcf2i/btsFirLC1aW/KsNgxCfGLQFsJYRgTKDDh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUcf2i%2FbtsFirLC1aW%2FKsNgxCfGLQFsJYRgTKDDh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;396&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Query를 통한 임의 숫자 생성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 6에서는 한 가지 유용한 기능인 EF.Functions.Random을 도입하여 0과 1 사이에 배타적인 의사난수를 반환하는 database기능과 연결할 수 있습니다. 예를 들어 table에서 임의의 row를 선택하기 위해 전체 row 수에서 임의의 숫자를 곱할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 GetRandomProduct이름의 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690179811322&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void GetRandomProduct()
{
    using (Northwind db = new())
    {
        int? rowCount = db.Products?.Count();

        if (rowCount == null)
        {
            Console.WriteLine(&quot;Products table is empty.&quot;);
            return;
        }

        Product? p = db.Products?.FirstOrDefault(p =&amp;gt; p.ProductId == (int)(EF.Functions.Random() * rowCount));

        if (p == null)
        {
            Console.WriteLine(&quot;Product not found.&quot;);
            return;
        }

        Console.WriteLine($&quot;Random product: {p.ProductId} {p.ProductName}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 GetRandomProduct method를 호출하면 아래와 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHbCwC/btsFkTOnk2q/THoeojkG9NhPZmm7dUrreK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHbCwC/btsFkTOnk2q/THoeojkG9NhPZmm7dUrreK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHbCwC/btsFkTOnk2q/THoeojkG9NhPZmm7dUrreK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHbCwC%2FbtsFkTOnk2q%2FTHoeojkG9NhPZmm7dUrreK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;255&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Global filter 정의하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind의 products, 즉 제품은 단종될 수 있을 것입니다. 따라서 단종된 product는 결과에 반영되지 않도록 하는 것이 합리적일 수 있는데 products를 조회하는 모든 곳에 where를 사용하여 이러한 사항을 filtering 하기보다는 global로 filter를 적용하여 where를 사용하지 않고 해당 filtering을 products를 조회하는 모든 곳에 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind.cs의 OnModelCreating method아래에 아래와 같이 단종된(discontinued) product를 제거하는 global filter를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690180571033&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;modelBuilder.Entity&amp;lt;Category&amp;gt;()
            .Property(category =&amp;gt; category.CategoryName)
            .IsRequired() // NOT NULL
            .HasMaxLength(15);
modelBuilder.Entity&amp;lt;Product&amp;gt;().HasQueryFilter(p =&amp;gt; !p.Discontinued);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 QueryingWithLike method를 호출하고 product name으로 che를 입력합니다. 다음 결과에서 보듯 'Chef Anton&amp;rsquo;s Gumbo Mix'는 반영되지 않았습니다. 생성된 SQL문을 확인해 보면 Discontinued column에 대한 filter를 포함하고 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YzEe8/btsFiOz1Eru/3KADFnNTqeCTAUhimYsy8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YzEe8/btsFiOz1Eru/3KADFnNTqeCTAUhimYsy8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YzEe8/btsFiOz1Eru/3KADFnNTqeCTAUhimYsy8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYzEe8%2FbtsFiOz1Eru%2F3KADFnNTqeCTAUhimYsy8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;389&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. EF Core의 loading pattern&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core에서 일반적으로 사용되는 3가지 loading pattern이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eager loading : 사전 data load&lt;/li&gt;
&lt;li&gt;Lazy loading : 필요하기 직전에 자동적으로 data load&lt;/li&gt;
&lt;li&gt;Explicit loading : 수동으로 data load&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 확장 method 포함을 사용한 eager loading&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QueryingCategories method에서 현재 code는 각 category를 순회하기 위해 Categories 속성을 사용하며 category name과 category에 속한 products 수를 출력하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 이전에 query를 작성할 때 관련된 products에 대한 Include method를 호출함으로써 eager loading를 사용했기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Include를 사용하지 않으면 어떤 현상이 발생하는지 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;QueryingCategories&lt;span&gt; method에서 &lt;/span&gt;&lt;/span&gt;아래와 같이 Include method의 호출 부분을 주석처리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690189711342&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IQueryable&amp;lt;Category&amp;gt;? categories = db.Categories;//?.Include(c =&amp;gt; c.Products);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 QueryingCategories method를 호출하면 다음과 같은 결과가 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3cO2R/btsFod59xpr/i8emNIsz1LkFktfa1iakR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3cO2R/btsFod59xpr/i8emNIsz1LkFktfa1iakR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3cO2R/btsFod59xpr/i8emNIsz1LkFktfa1iakR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3cO2R%2FbtsFod59xpr%2Fi8emNIsz1LkFktfa1iakR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;376&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foreach안에서 각 item은 Products 속성을 갖고 있는 Category class의 instance인데 Products 속성은 category에서의 products list를 의미합니다. 위 예제에서 query는 단지 Categories table만을 select 하고 있으므로 해당 속성은 각 category에서 비어있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Lazy loading 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lazy loading은 EF Core 2.1에서 도입된 것으로 누락된 관계 data를 자동으로 load 할 수 있습니다. lazy loading을 사용하기 위해서는 우선 proxy를 위한 NuGet package를 참조해야 하며 해당 proxy사용을 위한 lazy loading을 구성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022에서 Microsoft.EntityFrameworkCore.Proxies NuGet package를 찾아 이를 참조합니다. 그리고 Northwind.cs의 OnConfiguring method아래에 UseLazyLoadingProxies 확장 method를 호출하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690251859853&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuting }).EnableSensitiveDataLogging();
optionsBuilder.UseLazyLoadingProxies();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 loop를 열거하면서 Products 속성을 읽으려고 시도할 때마다 lazy loading proxy는 data가 load 되었는지를 확인합니다. 그렇지 않다면 SELECT문을 실행하여 현재 category에 대한 products만을 load 하게 됩니다. 다시 말해 속성에 대한 접근이 있으면 그때 data를 database server에서 가져와야 할지를 판단하고 필요하면 그렇게 한다는 것입니다. 이렇게 되면 정확한 count를 출력에 반환할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 다시 실행하면 이번에는 정확한 product count가 표시될 것입니다. 다만 모든 data를 가져오기 위해 database server와 여러 번 통신해야 하는 과정이 필요하다는 단점이 있습니다. 예를 들어 모든 categories를 가져온 다음 첫 번째 category인 Beverages에 대한 products를 가져오기 위해 아래와 같이 두 번의 SQL 명령을 실행하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkqQu4/btsFkKDZyEK/aOBTupt7mkAPNJfvSZNzFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkqQu4/btsFkKDZyEK/aOBTupt7mkAPNJfvSZNzFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkqQu4/btsFkKDZyEK/aOBTupt7mkAPNJfvSZNzFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkqQu4%2FbtsFkKDZyEK%2FaOBTupt7mkAPNJfvSZNzFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;407&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Load method를 사용해 명시적으로 entity loading 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적 loading은 lazy loading과 비슷하지만 관계 data가 load 되는 것과 언제 load 될지를 정확히 제어한다는 차이가 있습니다. lazy loading은 해당 속성에 접근하면 처리되지만 explicit loading은 처리시점과 대상을 개발자가 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queries.cs에서 우선 ChangeTracking namespace를 import 합니다. 해당 namespace는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;관계 entity를 수동적으로 load 하도록 하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;CollectionEntry class를 사용하기 위한 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690253693570&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore.ChangeTracking;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QueryingCategories안에서 아래와 같이 lazy loading을 위해 변경한 부분을 주석처리한 다음 사용자에게 eager loading과 explicit loading 중 어느 것을 사용할지를 선택하도록 요청하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690253952986&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IQueryable&amp;lt;Category&amp;gt;? categories;// = db.Categories;//?.Include(c =&amp;gt; c.Products);

db.ChangeTracker.LazyLoadingEnabled = false;

Console.Write(&quot;Enable eager loading? (Y/N): &quot;);
bool eagerLoading = (Console.ReadKey(intercept: true).Key == ConsoleKey.Y);
bool explicitLoading = false;
Console.WriteLine();

if (eagerLoading)
{
    categories = db.Categories?.Include(c =&amp;gt; c.Products);
}
else
{
    categories = db.Categories;
    Console.Write(&quot;Enable explicit loading? (Y/N): &quot;);
    explicitLoading = (Console.ReadKey(intercept: true).Key == ConsoleKey.Y);
    Console.WriteLine();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foreach loop안에서는 WriteLine method를 호출하기 전 explicit loading을 사용하는지 확인하고 그렇다면 사용자에게 명시적으로 각각의 개별 category를 명시적으로 load 할지를 결정하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 eager loading를 비활성화하기 위해 N을 누릅니다. 그런 다음 explicit loading을 위해 Y를 누르고 각 category에서 products를 load 할지에 대한 선택을 진행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdg0mW/btsFh68K0Ca/ei3GM7Y9WmAJte1VhKy65K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdg0mW/btsFh68K0Ca/ei3GM7Y9WmAJte1VhKy65K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdg0mW/btsFh68K0Ca/ei3GM7Y9WmAJte1VhKy65K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdg0mW%2FbtsFh68K0Ca%2Fei3GM7Y9WmAJte1VhKy65K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 처음 2건에 대한 products만을 load 하도록 하였습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사용하고자 하는 loading pattern을 선택할 때는 주의해야 합니다. loading pattern에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Entity 추적 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 entity의 identity resolution을 정의하는 것으로 시작해야 합니다. EF Core는 entity instance에서 각각의 고유한 primary key값을 읽음으로서 resolve를 수행하는데 이런 과정을 통해 entity 혹은 entity 사이의 관계를 식별하는데 모호함을 제거할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core는 기본적으로 appliction이 실행되는 memory안에서 entity를 추적합니다. 따라서 새로운 entity를 추가하거나 수정하거나 삭제하는등의 변경사항이 발생하면 꼭 SaveChanges method를 호출하여 해당 변경사항이 실제 database에 반영되도록 해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;EF Core는 key가 database에서 entity를 식별할 수 있는 고유값에 해당하므로 이를 통해서만 entity를 추적할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Northwind database에서 Customer table을 보면 여기에 다음과 같은 고객 정보가 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;CustomerId: ALFKI&lt;br /&gt;CompanyName: Alfreds Futterkiste&lt;br /&gt;Country:Germany&lt;br /&gt;Phone: 030-0074321&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 해당 data context에서 Germany에 해당하는 모든 customer를 불러오는 query를 실행한 뒤 같은 data context안에서 이름이 A로 시작하는 모든 customer를 불러오는 또 다른 query를 실행한다면, 해당 context안에서 특정 customer가 이미 존재한다면 성능향상을 이는 다시 load하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이전에 위 설명과 같은 2개의 query를 실행하는 사이 customer의 telephone정보가 update됐다면 data context에서 추적된 entity는 변경된 telephone 정보를 반영하지 않게 되므로 이를 최신의 customer정보로 인정할 수 없게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 지역적 변경사항에 대한 추적이 필요하지 않거나 매번 query를 실행할때 마다 최신의 data값을 가진 entity의 instance를 새롭게 load하고자 한다면 추적기능 자체를 비활성화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 query에서 추적을 끄려면 해당 query에서 AsNoTracking method를 호출하거나&lt;/p&gt;
&lt;pre id=&quot;code_1709694982788&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var products = db.Products
 .AsNoTracking()
 .Where(p =&amp;gt; p.UnitPrice &amp;gt; price)
 .Select(p =&amp;gt; new { p.ProductId, p.ProductName, p.UnitPrice });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data context의 instance자체에서 추적을 끄려면 ChangeTracker의 QueryTrackingBehavior설정을 아래와 같이 NoTracking으로 설정해 주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695092195&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 각각의 query에서 추적을 끄지만 identity resolution을 그대로 유지하고자 한다면 AsNoTrackingWithIdentityResolution method를 대신 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695187044&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var products = db.Products
 .AsNoTrackingWithIdentityResolution()
 .Where(p =&amp;gt; p.UnitPrice &amp;gt; price)
 .Select(p =&amp;gt; new { p.ProductId, p.ProductName, p.UnitPrice });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 같은 원리로 data context의 instance에도 같은 설정을 적용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695277657&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTrackingWithIdentityResolution;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 새로운 data context에서 일관되게 같은 설정을 적용해야 한다면 OnConfiguring method에서 UseQueryTrackingBehavior method를 호출해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709695370667&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(connectionString).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 추적이 발생하는 3가지 사례&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로 identity resolution을 통한 default tracking을 사용하는 경우 입니다. Entity가 일단 data context로 load되면 실제 database에 대한 변경사항은 적용되지 않으며 지역적으로 하나의 복사본이 생성됩니다. Entity는 지역적으로 변경된 사항을 추적하고 SaveChanges method를 호출하여 database를 update합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database Table Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SaveChanges&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째는 변경사항에 대한 추적을 하지 않고 identity resolution을 사용하지 않는 경우 입니다. 모든 질의는 다른 table row의 instance를 load하며 변경과 중복을 허용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database Table Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;br /&gt;B&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SaveChanges&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;br /&gt;B&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 세번째는 변경사항에 대한 추적을 하지 않으면서 identity resolution을 사용하는 경우입니다. 일단 entity가 data context로 load되고 나면 Database에 대한 변경사항은 적용되지 않으며 오로지 복사본만 존재합니다. 변경사항을 추적하지 않으므로 두번째 경우와 마찬가지로 SaveChanges method는 어떠한 동작도 수행하지 않습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database Table Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Database 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;불러오기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Entity 변경&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SaveChanges&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 추적없는 query에 대한 Lazy loading &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 7과 그 이전에서는 추적을 사용하지 않도록 설정하면 lazy loading pattern은 사용할 수 없었고 만약 그러한 시도를 하게 되면 System.InvalidOperationException예외가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 EF Core 8에 들어서 추적을 활성화하지 않더라도 entity에 대한 lazy loading을 지원하기 시작했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709707838666&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static void LazyLoadingWithNoTracking()
{
    using NorthwindDb db = new();
 
    IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.AsNoTracking();
    
    if (products is null || !products.Any())
    {
        Console.WriteLine(&quot;No Products.&quot;);
        return;
    }

    foreach (Product p in products)
    {
        Console.WriteLine(&quot;{0} is in category {1}.&quot;, p.ProductName, p.Category.CategoryName);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 Product에 대한 query에서 추적을 사용하지 않도록 하고 있으며 Product를 열거할때 lazy loading을 사용하여 관련된 Category이름을 가져오도록 하고 있습니다. EF Core 7이나 6에서 위 code를 실행하면 예외가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● AsNoTracking에 대한 오해.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷의 여러 community에서는 간혹 AsNoTracking이 성능향상을 위한 좋은 선택임을 강조하는 경우가 있습니다. 하지만 상상해 보십시오. 수십, 수백개의 entity를 가져오는 query를 AsNoTracking으로 실행한 뒤 다시 같은 data context안에서 동일한 query를 수행하게 되면 memory에는 수백개의 중복된 entity가 저장되는 상황을 맞이하게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 memory낭비를 초래하며 당연히 성능에도 부정적인 영향을 줄 수 밖에 없을 것입니다. 다시 말해 성능향상을 위해 AsNoTracking을 호출하라는 조언은 모든 상황에서 해당되지 않을 수 있으므로 주의가 필요합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. EF Core를 사용한 data변경&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core를 사용해 entity를 Insert, update, delete 하는 데는 큰 어려움이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext는 변화에 대한 추적관리를 자동으로 하게 되므로 local entity에 새로운 entity를 추가하거나 기존 entity를 수정하거나 혹은 삭제하는 것을 포함해 추적된 여러 변화를 가질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이러한 변경된 사항들을 database에 반영하려면 SaveChanges method를 호출하기만 하면 되고 경우에 따라 성공적으로 변경된 entity의 수가 반환될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Entity Insert&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table에 새로운 row를 추가해 보기 위해 Modifications.cs file을 추가하고 여기에 가장 비싼 순으로 정렬된 각 product에 대한 ID, name, cost, stock, 그리고 discontinued속성을 출력하고 method에 선택적으로 전달된 int값의 array와 일치하는 모든 것을 가종하는 ListProducts이름의 method와 함께 partial Program class를 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690267855914&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    static void ListProducts(int[]? productIdsToHighlight = null)
    {
        using (Northwind db = new())
        {
            if ((db.Products is null) || (!db.Products.Any()))
            {
                Console.WriteLine(&quot;There are no products.&quot;);
                return;
            }

            Console.WriteLine(&quot;| {0,-3} | {1,-35} | {2,8} | {3,5} | {4} |&quot;, &quot;Id&quot;, &quot;Product Name&quot;, &quot;Cost&quot;, &quot;Stock&quot;, &quot;Disc.&quot;);

            foreach (Product p in db.Products)
            {
                ConsoleColor previousColor = System.Console.ForegroundColor;

                if ((productIdsToHighlight is not null) &amp;amp;&amp;amp; productIdsToHighlight.Contains(p.ProductId))
                {
                    System.Console.ForegroundColor = ConsoleColor.Green;
                }

                Console.WriteLine(&quot;| {0:000} | {1,-35} | {2,8:$#,##0.00} | {3,5} | {4} |&quot;, p.ProductId, p.ProductName, p.Cost, p.Stock, p.Discontinued);

                System.Console.ForegroundColor = previousColor;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위 예제에서 '{숫자1, 숫자2}'는 숫자 1번째의 인수를 숫자 2만큼의 문자열 넓이로 정렬함을 의미합니다. 이때 숫자 2가 -면 왼쪽 정렬을 양수이면 오른쪽 정렬입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 Modifications.cs에 AddProduct method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690268164449&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static (int affected, int productId) AddProduct(int categoryId, string productName, decimal? price)
{
    using (Northwind db = new())
    {
        if (db.Products is null) return (0, 0);

        Product p = new()
        {
            CategoryId = categoryId,
            ProductName = productName,
            Cost = price,
            Stock = 72
        };

        // Insert 추가
        EntityEntry&amp;lt;Product&amp;gt; entity = db.Products.Add(p);
        Console.WriteLine($&quot;State: {entity.State}, ProductId: {p.ProductId}&quot;);

        // 변경된 추적사항을 database에 반영
        int affected = db.SaveChanges();
        Console.WriteLine($&quot;State: {entity.State}, ProductId: {p.ProductId}&quot;);

        return (affected, p.ProductId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 주석처리하고 위의 AddProduct와 ListProducts를 호출하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690268974544&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var resultAdd = AddProduct(categoryId: 6, productName: &quot;Apple2 Computer&quot;, price: 1200M);

if (resultAdd.affected == 1)
{
    Console.WriteLine($&quot;Add product successful with ID: {resultAdd.productId}.&quot;);
}
                         
ListProducts(productIdsToHighlight: new[] { resultAdd.productId });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 새로운 product를 추가한 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A1k0W/btsFoI5UXPY/wFrTjijxPsVqcj1o0LDJ0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A1k0W/btsFoI5UXPY/wFrTjijxPsVqcj1o0LDJ0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A1k0W/btsFoI5UXPY/wFrTjijxPsVqcj1o0LDJ0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA1k0W%2FbtsFoI5UXPY%2FwFrTjijxPsVqcj1o0LDJ0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;479&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;새로운 product가 처음 memory에 생성되고 EF Core change tracker에 의해 추적되기 시작하면 Added 상태와 0인 ID를 가지게 됩니다. 그런 후 SaveChanges가 호출되면 상태는 UnChanged로 바뀌게 되고 ID값은 78로 database에 의해 할당됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Entity Update&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 존재하는 table의 row를 변경하기 위해 update 할 product를 product name의 시작을 지정하여 검색한 다음 가장 먼저 일치하는 것만 반환하도록 하는 예제를 작성할 것입니다.(실제로는 ProductId와 같이 고유한 식별자를 사용해 update할 product를 지정하는 것이 일반적입니다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Product를 추가할 때는 실제 어떤 product ID가 할당될지 알 수 없습니다. 다만 현재 Northwind database에&amp;nbsp; 'Apple2'로 시작하는 product가 없다는 것만 알고 있을 뿐입니다. name을 사용해 update 할 product를 찾으면 이전에 추가한 product의 ID를 알아내야 하는 것을 피할 수 있습니다. 물론 database에는 77까지의 ID가 존재하므로 새로운 product의 ID가 78이 될 수 있음을 유추할 수 있지만 product를 추가한 뒤 삭제한다고 해도 다음 product의 ID는 79가 될 수 있으며 이때부터 번호순서 예상에 맞지 않을 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서는 name이 지정된 값으로 시작하는 product에 대해 $20과 같이 지정한 만큼 price를 증가시키는 method를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690270991101&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static (int affected, int productId) IncreaseProductPrice(string productNameStartsWith, decimal amount)
{
    using (Northwind db = new())
    {
        if (db.Products is null) return (0, 0);

        Product updateProduct = db.Products.First(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        updateProduct.Cost += amount;

        int affected = db.SaveChanges();

        return (affected, updateProduct.ProductId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 IncreaseProductPrice method를 호출한 다음 다시 ListProducts method를 호출하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690271077782&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var resultUpdate = IncreaseProductPrice(productNameStartsWith: &quot;Apple&quot;, amount: 30M);

if (resultUpdate.affected == 1)
{
    Console.WriteLine(&quot;Increase price success for ID: {resultUpdate.productId}.&quot;);
}

ListProducts(productIdsToHighlight: new[] { resultUpdate.productId });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다. 이전에 추가한 'Apple2 Computer'에 대한 price가 $30만큼 증가되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuNkc6/btsFh4392px/8nD6zH5ScJeV7eDskHR7A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuNkc6/btsFh4392px/8nD6zH5ScJeV7eDskHR7A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuNkc6/btsFh4392px/8nD6zH5ScJeV7eDskHR7A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuNkc6%2FbtsFh4392px%2F8nD6zH5ScJeV7eDskHR7A1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Entity Delete&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 entity들은 Remove method를 사용함으로써 삭제할 수 있습니다. RemoveRange는 여러 entity를 제거하고자 할 때 더 적합한 method입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서 지정한 값으로 시작하는 모든 product를 삭제하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690354744991&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int DeleteProducts(string productNameStartsWith)
{
    using (Northwind db = new())
    {
        IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        if ((products is null) || (!products.Any()))
        {
            Console.WriteLine(&quot;No products found to delete.&quot;);

            return 0;
        }
        else
        {
            if (db.Products is null) return 0;
            db.Products.RemoveRange(products);
        }

        int affected = db.SaveChanges();
        return affected;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 아래와 같이 DeleteProducts method를 호출하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690354848486&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;About to delete all products whose name starts with Apple.&quot;);
Console.Write(&quot;Press Enter to continue or any other key to exit: &quot;);

if (Console.ReadKey(intercept: true).Key == ConsoleKey.Enter)
{
    int deleted = DeleteProducts(productNameStartsWith: &quot;Apple&quot;);

    Console.WriteLine($&quot;{deleted} product(s) were deleted.&quot;);
}
else
{
    Console.WriteLine(&quot;Delete was canceled.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 Enter key를 누르면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYuV1r/btsFqaA5pwP/gCiTw1x8AMJzPiQRydG6k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYuV1r/btsFqaA5pwP/gCiTw1x8AMJzPiQRydG6k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYuV1r/btsFqaA5pwP/gCiTw1x8AMJzPiQRydG6k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYuV1r%2FbtsFqaA5pwP%2FgCiTw1x8AMJzPiQRydG6k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Apple로 시작하는 product가 여러 건이라면 해당되는 모든 product는 삭제될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 더욱 효율적인 Update와 Delete&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 방법은 EF Core를 사용한 비교적 전통적인 data수정 방식으로 과정을 다음과 같이 요약할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Database context를 생성합니다. (변경추적은 기본으로 사용됩니다.)&lt;/li&gt;
&lt;li&gt;Insert를 위해 entity class에 대한 instance를 생성하고 Add method의 매개변수로 collection을 db.Products.Add(product) 처럼 전달합니다.&lt;/li&gt;
&lt;li&gt;Update를 위해서는 update 하고자 하는 entity를 가져와 이들에 대한 속성을 변경합니다.&lt;/li&gt;
&lt;li&gt;Delete를 위해서는 delete 하고자 하는 entity를 가져와 Remove 또는 RemoveRange method의 매개변수로 db.Products.Remove(product) 처럼 전달합니다.&lt;/li&gt;
&lt;li&gt;database context의 SaveChanges method를 호출하면 change tracker는 insert, update, delete수행에 필요한 SQL 문을 생성해 실행하고 적용된 entity의 수를 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core 7에서는 사전에 load 해야 할 entity와 이들에 대한 변경점을 필요로 하지 않음으로써 더 효휼적으로 update와 delete를 수행할 수 있는 ExecuteDelete와 ExecuteUpdate(Async method도 동일함) 2개의 method를 도입하였습니다. 이들은 LINQ query상에서 호출되어 query 결과에 대한 entity에 영향을 주게 됩니다. query가 entity를 검색하는 데 사용되지 않으므로 어떠한 entity도 data context로 load 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 table의 모든 row를 삭제하고자 한다면 ExecuteDelete 혹은 ExecuteDeleteAsync method를 모든 DbSet속성으로 아래와 같이 호출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356649556&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await db.Products.ExecuteDeleteAsync();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 code는 database에서 다음과 같은 SQL문을 실행할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356712029&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE FROM Products&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 50보다 더 높은 UnitPrice를 가진 product를 삭제하고자 한다면 아래와 같이 LINQ를 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356844835&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await db.Products.Where(product =&amp;gt; product.Cost &amp;gt; 50).ExecuteDeleteAsync();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 code는 database에서 다음과 같은 SQL문을 실행할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690356872940&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE FROM Products p WHERE p.Cost &amp;gt; 50&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ExecuteUpdate와 ExecuteDelete는 단지 단일 table상에서만 동작할 수 있습니다. 만약 다수의 table에 걸친 꽤 복잡한 LINQ query를 작성했다고 하더라도 단일 table에서만 update나 delete가 수행됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예로 Discontinued가 false인 모든 product의 UintPrice값을 10% 인상으로 update 하고자 한다면 LINQ는 아래와 같이 작성될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690357536122&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await db.Products.Where(product =&amp;gt; !product.Discontinued).ExecuteUpdateAsync(s =&amp;gt; s.SetProperty(
p =&amp;gt; p.Cost, //update할 속성 선택
p =&amp;gt; p.Cost * 0.1m)); //속성 update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제와 같은 경우 하나의 LINQ안에서 다수의 속성을 update 하기 위해 같은 query에서 다수의 SetProperty를 호출해 연&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서 ExecuteUpdate를 사용해 name이 지정한 값으로 시작하는 모든 product를 update 하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358193962&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static (int affected, int[]? productIds) IncreaseProductPricesBetter(string productNameStartsWith, decimal amount)
{
    using (Northwind db = new())
    {
        if (db.Products is null)
            return (0, null);

        IQueryable&amp;lt;Product&amp;gt;? products = db.Products.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        int affected = products.ExecuteUpdate(s =&amp;gt; s.SetProperty(
        p =&amp;gt; p.Cost,
        p =&amp;gt; p.Cost + amount));

        int[] productIds = products.Select(p =&amp;gt; p.ProductId).ToArray();

        return (affected, productIds);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 IncreaseProductPricesBetter method를 호출하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358303169&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var resultUpdateBetter = IncreaseProductPricesBetter(productNameStartsWith: &quot;Apple&quot;, amount: 20M);

if (resultUpdateBetter.affected &amp;gt; 0)
{
    Console.WriteLine(&quot;Increase product price successful.&quot;);
}

ListProducts(productIdsToHighlight: resultUpdateBetter.productIds);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 위 예제를 실행하기 전 Apple로 시작하는 product를 이전에 삭제하였으므로 위에서 구현했던 새로운 product를 추가하는 문을 먼저 실행하도록 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 현재 'Apple'로 시작하는 product에 대해 다음과 같이 cost를 변경하였음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOwgYU/btsFjI0DVy6/7HRnvrdv00qkrK0XGNh4b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOwgYU/btsFjI0DVy6/7HRnvrdv00qkrK0XGNh4b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOwgYU/btsFjI0DVy6/7HRnvrdv00qkrK0XGNh4b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOwgYU%2FbtsFjI0DVy6%2F7HRnvrdv00qkrK0XGNh4b0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Modifications.cs로 돌아와 ExecuteDelete를 사용해 지정한 값으로 시작되는 모든 product를 삭제하는 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358625569&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int DeleteProductsBetter(string productNameStartsWith)
{
    using (Northwind db = new())
    {
        int affected = 0;
        IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

        if ((products is null) || (!products.Any()))
        {
            Console.WriteLine(&quot;No products found to delete.&quot;);
            return 0;
        }
        else
        {
            affected = products.ExecuteDelete();
        }

        return affected;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 DeleteProductsBetter method를 호출하도록 하고 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690358750148&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;About to delete all products whose name starts with Apple.&quot;);
Console.Write(&quot;Press Enter to continue or any other key to exit: &quot;);

if (Console.ReadKey(intercept: true).Key == ConsoleKey.Enter)
{
    int deleted = DeleteProductsBetter(productNameStartsWith: &quot;Apple&quot;);
    Console.WriteLine($&quot;{deleted} product(s) were deleted.&quot;);
}
else
{
    Console.WriteLine(&quot;Delete was canceled.&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VU21B/btsFocMWWKf/qQFjibMQlxDpRxLcs86CoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VU21B/btsFocMWWKf/qQFjibMQlxDpRxLcs86CoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VU21B/btsFocMWWKf/qQFjibMQlxDpRxLcs86CoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVU21B%2FbtsFocMWWKf%2FqQFjibMQlxDpRxLcs86CoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;403&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 기존 방식인 변경 추적과 ExecuteUpdate, ExecuteDelete method를 섞어서 사용하게 되는 경우라면 이들은 서로 동기화되지 않음에 주의해야 합니다. Change tracker는 이들 method를 통해 update 하고 delete 한 사항을 알지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Database context pooling&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DbContext class는 일회용이며 단일 작업 단위 원칙에 따라 설계되었습니다. 이전 예제에서는 DbContext에서 파생된 Northwind instance를 using안에서 생성했으므로 Dispose속성은 각 작업에서의 종료시점에 호출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EF Core와 관련된 ASP.NET Core의 특징 중 하나는 website와 service를 구축할 때 database context를 pooling 함으로써 code를 더욱 효휼적으로 만든다는 것입니다. 이때 따라 가능한한 더 효휼적으로 원하는 만큼의 DbContext에서 파생된 다수의 개체를 생성하고 소멸할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7. Transaction&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SaveChanges method를 호출할 때마다 암시적으로 transaction이 시작되므로 무엇인가 잘못된 상황이 발생하면 자동적으로 변경사항이 rollback 됩니다. Transaction이내에서 다수의 변경사항이 성공적으로 수행된다면 transaction은 모든 변경사항을 commit 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 일련의 변경사항들을 처리하는 동안 해당 data에 대한 읽기/쓰기를 방지하지 위해 lock을 적용함으로써 database에 대한 무결성을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 아래 설명에 따른 약자로 ACID라고 말하기도 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A는 원자성(atomic)입니다. Transaction에 대한 모든 동작이 commit 되거나 commit 되지 않습니다.&lt;/li&gt;
&lt;li&gt;C는 일관성(consistent)입니다. Transaction전후에 대한 database의 상태는 일관되며 이는 code의 logic에 의존합니다. 예를 들어 은행 계좌 간 돈이 송금될 때 하나의 계정에서 100만 원이 인출되면 다른 계정에서 100만원이 입금되어야 하는데 이를 보장하는 것은 business loginc에 달려있습니다.&lt;/li&gt;
&lt;li&gt;I는 격리성(isolated)입니다. Transaction동안 변경사항은 다른 process로부터는 감춰지는데 여기에 선태가능한 여러 격리 수준이 존재합니다(아래 표 참고). 수준이 높을수록 data의 원자성은 더 나아지지만 더 많은 lock이 적용되고 이는 다른 process에 부정적인 영향을 줄 수 있습니다. Snapshot은 lock을 회피하기 위해 row의 여러 복사본을 생성하므로 좀 더 특별한 경우라고 할 수 있지만 transaction이 발생하는 동안 database의 크기를 증가시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;D는 지속성(durable)입니다. Transaction이 실패하면 원상태로 복구될 수 있습니다. 이는 대게 2단계 commit과 transaction log로 구현됩니다. 일단 transaction이 commit 되면 후속 오류가 발생하더라도 data의 보존을 보증합니다. 지속성의 반대는 휘발성(volatile)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 격리 수준을 통한 transaction 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 격리 수준을 설정함으로써 아래 표의 설명과 같이 제어할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 134px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 20px;&quot;&gt;격리 수준&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 20px;&quot;&gt;잠금&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;허용되는 무결성 문제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 37px;&quot;&gt;ReadUncommitted&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 37px;&quot;&gt;해당없음&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 37px;&quot;&gt;Dirty read, non-repeatable read와 phantom data&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 60px;&quot;&gt;ReadCommitted&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 60px;&quot;&gt;data변경시 data에 접근하는 다른 사용자를 transaction이 종료될때까지 차단하기 위해 읽기 잠금을 적용합니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 60px;&quot;&gt;Non-repeatable read와 phantom data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.6821%; height: 17px;&quot;&gt;RepeatableRead&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%; height: 17px;&quot;&gt;data를 읽을때 data에 접근하는 다른 사용자를 transaction이 종료될때까지 차단하기 위해 변경 잠금을 적용합니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Phantom data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6821%;&quot;&gt;Serializable&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%;&quot;&gt;Insert나 delete를 포함해 결과에 영향을 줄 수 있는 모든 동작을 차단하기 위해 key-range잠금을 적용합니다.&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;해당없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6821%;&quot;&gt;Snapshot&lt;/td&gt;
&lt;td style=&quot;width: 47.9845%;&quot;&gt;해당없음&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;해당없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 명시적인 transaction 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database context에는 Database 속성이 있으며 이를 통해 transaction을 명시적으로 실행시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifications.cs에서 아래와 같이 namespace를 import 합니다. 이는 IDbContextTransaction interface를 사용하기 위한 EF Core storage namespace입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690423530539&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore.Storage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DeleteProducts method에서 db 변수의 instance를 생성한 직후 transaction을 명시적으로 시작하고 격리 수준을 출력하는 문을 추가합니다. Method의 끝에서는 transaction을 commit 하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690425484794&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static int DeleteProducts(string productNameStartsWith)
{
    using (Northwind db = new())
    {
        using (IDbContextTransaction t = db.Database.BeginTransaction())
        {
            Console.WriteLine(&quot;Transaction isolation level: {0}&quot;, arg0: t.GetDbTransaction().IsolationLevel);

            IQueryable&amp;lt;Product&amp;gt;? products = db.Products?.Where(p =&amp;gt; p.ProductName.StartsWith(productNameStartsWith));

            if ((products is null) || (!products.Any()))
            {
                Console.WriteLine(&quot;No products found to delete.&quot;);

                return 0;
            }
            else
            {
                if (db.Products is null) return 0;
                db.Products.RemoveRange(products);
            }

            int affected = db.SaveChanges();

            t.Commit();

            return affected;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc9oAs/btsFlS9wPMf/F7kn6075OkAKKiUC7tF1sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc9oAs/btsFlS9wPMf/F7kn6075OkAKKiUC7tF1sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc9oAs/btsFlS9wPMf/F7kn6075OkAKKiUC7tF1sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc9oAs%2FbtsFlS9wPMf%2FF7kn6075OkAKKiUC7tF1sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;212&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>c#</category>
      <category>ef core</category>
      <category>TRACKING</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/600</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-10-Entity-Framework-Core#entry600comment</comments>
      <pubDate>Thu, 7 Mar 2024 16:46:08 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 9. File, Streams, Serialization</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-9-File-Streams-Serialization</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Data에 대한 입출력의 대상은 file이나 stream이 될 수 있으며 때로는 text를 encoding 하거나 직렬화할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. File System 관리&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application에서는 종종 다른 환경에서 file이나 directory등으로 입출력 동작을 수행해야 할 경우가 있으며 System 및 System.IO namespace에서는 이러한 목적의 class들을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) cross-platform 환경및 filesystem&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 cross-platform환경을 처리하는 방법과 Windows와 Linux 또는 macOS사이의 차이점에 대해 알아보고자 합니다. Windows와 macOS 그리고&amp;nbsp; Linux에서 경로는 다르게 취급되고 있으므로 .NET이 이를 어떻게 처리하는지를 알아둘 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 Solution에서 WorkingWithFileSystems이름의 Console App project를 생성합니다.&amp;nbsp; 그리고 project에 Helpers.cs 이름의 class file을&amp;nbsp; 추가한뒤 아래와 같이 SectionTitle method를 가진 partial Program class를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688795935958&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void SectionTitle(string title)
{
	ConsoleColor previousColor = System.Console.ForegroundColor;
	System.Console.ForegroundColor = ConsoleColor.Yellow;
	Console.WriteLine(&quot;*&quot;);
	Console.WriteLine($&quot;* {title}&quot;);
	Console.WriteLine(&quot;*&quot;);
	System.Console.ForegroundColor = previousColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 System.IO.Directory, System.Environment, System.IO.Path namespace를 정적 import한뒤&lt;/p&gt;
&lt;pre id=&quot;code_1688796036276&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.IO.Directory; 
using static System.IO.Path; 
using static System.Environment;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 경로 및 directory분리문자를 출력하고, 현재 directory에 대한 경로와 system file, 임시 file, document 등 일부 특별한 용도의 directory에 대한 경로를 출력하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688796417325&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Path.PathSeparator&quot;, arg1: PathSeparator);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Path.DirectorySeparatorChar&quot;, arg1: DirectorySeparatorChar);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Directory.GetCurrentDirectory()&quot;, arg1: GetCurrentDirectory());
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Environment.CurrentDirectory&quot;, arg1: CurrentDirectory);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Environment.SystemDirectory&quot;, arg1: SystemDirectory);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot;Path.GetTempPath()&quot;, arg1: GetTempPath());

Console.WriteLine(&quot;GetFolderPath(SpecialFolder)&quot;);
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .System)&quot;, arg1: GetFolderPath(SpecialFolder.System));
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .ApplicationData)&quot;, arg1: GetFolderPath(SpecialFolder.ApplicationData));
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .MyDocuments)&quot;, arg1: GetFolderPath(SpecialFolder.MyDocuments));
Console.WriteLine(&quot;{0,-33} {1}&quot;, arg0: &quot; .Personal)&quot;, arg1: GetFolderPath(SpecialFolder.Personal));&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Environment type에서는 위의 예제에서 사용된 것 이외 GetEnvironmentVariables method와 OSVersion, ProcessorCount속성등 여러 가지 유용한 member들을 가지고 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv574P/btsFhfWLBES/JW8FjSPBXSVro3YJzlk5uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv574P/btsFhfWLBES/JW8FjSPBXSVro3YJzlk5uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv574P/btsFhfWLBES/JW8FjSPBXSVro3YJzlk5uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv574P%2FbtsFhfWLBES%2FJW8FjSPBXSVro3YJzlk5uK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;286&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio Code에서 dotnet run을 사용해 console app을 실행하는 경우 CurrentDirectory는 bin folder 내부가 아닌&amp;nbsp; project folder가 될 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Windows에서는 directory에 대한 구분문자로 backslash(\)문자를 사용하지만 macOS와 Linux는 slash문자를 사용합니다. 경로를 결합할 때 code에서 어떤 문자가 사용되는지를 미리 가정하지 말고 Path.DirectorySeparatorChar를 사용하는 것이 좋습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Drive&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Drive에 관한 것으로는 computer에 연결된 모든 drive에 관한 정보를 반환하는 정적 method를 가진 DriveInfo type을 사용합니다. 이때 각각의 drive는 drive type을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 모든 drive를 가져와 사용가능한 것들에 대해서만 drive의 이름, type, size, 사용 가능한 용량 그리고 format을 출력하는 문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688803703765&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;My drives&quot;);
Console.WriteLine(&quot;{0,-30} | {1,-10} | {2,-7} | {3,18} | {4,18}&quot;, &quot;NAME&quot;, &quot;TYPE&quot;, &quot;FORMAT&quot;, &quot;SIZE (BYTES)&quot;, &quot;FREE SPACE&quot;);
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
	if (drive.IsReady)
	{
		Console.WriteLine(&quot;{0,-30} | {1,-10} | {2,-7} | {3,18:N0} | {4,18:N0}&quot;, drive.Name, drive.DriveType, drive.DriveFormat, drive.TotalSize, drive.AvailableFreeSpace);
	}
	else
	{
		Console.WriteLine(&quot;{0,-30} | {1,-10}&quot;, drive.Name, drive.DriveType);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;TotalSize와 같은 속성을 읽기 전에 drive가 준비상태인지를 확인해야 합니다. 그렇지 않으면 이동식 drive에 관한 예외를 보게 될 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qA4HX/btsFcQdyQre/ihN1kfWxMuH4Ub05ZWLnk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qA4HX/btsFcQdyQre/ihN1kfWxMuH4Ub05ZWLnk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qA4HX/btsFcQdyQre/ihN1kfWxMuH4Ub05ZWLnk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqA4HX%2FbtsFcQdyQre%2FihN1kfWxMuH4Ub05ZWLnk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;172&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Linux에서 일반 사용자 권한으로 예제를 실행하는 경우 Name과 DriveType만 접근이 가능하고 DriveFormat과 TotalSize에서는 UnauthorizedAccessException예외를 일으킬 수 있습니다.이 문제를 해결하기 위해서는 ' sudo dotnet run.'명령을 통해서 관리자 권한으로 예제를 실행해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Directory&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Directory와 관련해서는 Directory, Path 그리고 Environment 적정 class를 사용합니다. 이들 type에는 filesystem에 관한 다수의 member들을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자지정 경로를 구성할 때는 예를 들어 directory분리문자를 사용할 때 platform에 대한 가정을 하지 않도록 code작성에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 directory 이름에 대한 문자열을 생성하고 Path type의 Combine method를 통해 적절히 이들을 결합하여 사용자의 home directory 아래에 사용자지정 경로를 정의하도록 하는 문을 아래와 같이 작성합니다. 이때 사용자지정 directory에 대한 경로는 Directory class의 Exists method를 통해 존재여부를 확인하고 directory를 생성한 뒤 Directory class의 CreateDirectory method와 Delete method를 사용해 이곳에 포함된 모든 file과 하위 directory를 포함해 삭제하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688805326932&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;Custom Directory&quot;);
			
string newFolder = Combine(GetFolderPath(SpecialFolder.Personal), &quot;NewFolder&quot;);

Console.WriteLine($&quot;Working with: {newFolder}&quot;);
Console.WriteLine($&quot;Does it exist? {Path.Exists(newFolder)}&quot;);
Console.WriteLine(&quot;Creating it...&quot;);
			
CreateDirectory(newFolder);

Console.WriteLine($&quot;Does it exist? {Path.Exists(newFolder)}&quot;);
Console.Write(&quot;Confirm the directory exists, and then press ENTER: &quot;); Console.ReadLine();
Console.WriteLine(&quot;Deleting it...&quot;);
			
Delete(newFolder, recursive: true);

Console.WriteLine($&quot;Does it exist? {Path.Exists(newFolder)}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행한 뒤 결과를 확인합니다. directory를 삭제하기 위해 Enter key를 누르기 전 해당 directory가 정상적으로 만들어졌는지 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJmlm/btsFethOBhg/s9Rnm4dSLyEOnKDO3JFpV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJmlm/btsFethOBhg/s9Rnm4dSLyEOnKDO3JFpV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJmlm/btsFethOBhg/s9Rnm4dSLyEOnKDO3JFpV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJmlm%2FbtsFethOBhg%2Fs9Rnm4dSLyEOnKDO3JFpV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;267&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET 6 이전까지는 Directory class에서만 Exists method가 있었으나 .NET 7 이후부터는 Path에도 Exists method가 존재하며 둘의 역활은 동일합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) File&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File과 관련해서는 Directory에서 했던 것처럼 file type을 정적으로 가져올 수 있습니다. 다만 아래 예제에서는 directory type에서와 이름이 같은 method를 일부 가지고 있으며 따라서 서로 충돌할 수 있기 때문에 그런 방식을 사용하지 않았습니다. 또한 이 경우 큰 문제가 되지 않을 만큼 짧은 이름을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 파일존재를 확인한 뒤 text file을 생성하고 해당 file에 text를 기록하는 문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688863268472&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;files&quot;);

string dir = Combine(GetFolderPath(SpecialFolder.Personal), &quot;OutputFiles&quot;);
CreateDirectory(dir);

string textFile = Combine(dir, &quot;Dummy.txt&quot;);
string backupFile = Combine(dir, &quot;Dummy.bak&quot;);

Console.WriteLine($&quot;Working with: {textFile}&quot;);
Console.WriteLine($&quot;Does it exist? {File.Exists(textFile)}&quot;);

StreamWriter textWriter = File.CreateText(textFile);
textWriter.WriteLine(&quot;Hello, C#!&quot;);
textWriter.Close();

Console.WriteLine($&quot;Does it exist? {File.Exists(textFile)}&quot;);

File.Copy(sourceFileName: textFile, destFileName: backupFile, overwrite: true);
Console.WriteLine($&quot;Does {backupFile} exist? {File.Exists(backupFile)}&quot;);

Console.Write(&quot;Confirm the files exist, and then press ENTER: &quot;);
Console.ReadLine();
			
File.Delete(textFile);
Console.WriteLine($&quot;Does it exist? {File.Exists(textFile)}&quot;);
			
Console.WriteLine($&quot;Reading contents of {backupFile}:&quot;);
StreamReader textReader = File.OpenText(backupFile);
Console.WriteLine(textReader.ReadToEnd());
textReader.Close();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 file을 기록하고 나면 system resource와 잠금을 해제하기 위해 file을 닫아줍니다.(이것은 일반적으로 file작성 시 예외가 발생하더라도 file이 닫히는 경우를 확실히 하기 위해 try-finally문에서 이루어집니다.) 그리고 backup을 위해 file을 복사한 뒤 기존의 file은 삭제하고 backup file로부터 내용을 읽어 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH2Lff/btsFcEdgkM6/88UykhSp0xVC9RJ0aKycAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH2Lff/btsFcEdgkM6/88UykhSp0xVC9RJ0aKycAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH2Lff/btsFcEdgkM6/88UykhSp0xVC9RJ0aKycAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH2Lff%2FbtsFcEdgkM6%2F88UykhSp0xVC9RJ0aKycAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;267&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Path&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 경로의 일부만을 확인해야 하는 경우도 있을 것입니다. 예를 들어 경로에서 folder이름이나 file이름, 혹은 확장자와 같은 것만을 추출해야 하는 경우입니다. 또는 임시 folder나 file이름을 생성해야 하는 경우도 있습니다. 이와 같은 작업은 Path class의 정적 method를 사용하면 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 아래와 같은 구문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688868332364&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;Paths&quot;);
string dir = Combine(GetFolderPath(SpecialFolder.Personal), &quot;OutputFiles&quot;);
string textFile = Combine(dir, &quot;Dummy.txt&quot;);

Console.WriteLine($&quot;Folder Name: {GetDirectoryName(textFile)}&quot;);
Console.WriteLine($&quot;File Name: {GetFileName(textFile)}&quot;);
Console.WriteLine(&quot;File Name without Extension: {0}&quot;,

GetFileNameWithoutExtension(textFile));

Console.WriteLine($&quot;File Extension: {GetExtension(textFile)}&quot;);
Console.WriteLine($&quot;Random File Name: {GetRandomFileName()}&quot;);
Console.WriteLine($&quot;Temporary File Name: {GetTempFileName()}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Wb4S/btsFfWKrfEc/DuhQAsAGk3b0dHrKDnCml0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Wb4S/btsFfWKrfEc/DuhQAsAGk3b0dHrKDnCml0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Wb4S/btsFfWKrfEc/DuhQAsAGk3b0dHrKDnCml0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Wb4S%2FbtsFfWKrfEc%2FDuhQAsAGk3b0dHrKDnCml0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;229&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GetTempFileName은 zero-byte file을 생성하여 사용할 수 있도록 준비한 뒤 해당 file의 이름을 반환합니다. 하지만 GetRandomFileName은 file의 이름만 반환할 뿐 실제 생성하지는 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) File 정보 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File이나 directory에 관한 정보, 예를 들어 size나 마지막 접근일자와 같은 정보를 를 확인하려면 FileInfo 혹은 DirectoryInfo class의 instance를 생성해 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FileInfo와 DirectoryInfo는 모두 FileSystemInfo class로부터 상속받은 것으로 따라서 둘 다 자신만의 속성과 더불어&amp;nbsp; LastAccessTime나 Delete와 같은 공통된 member를 가지고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 49px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.0465%; height: 17px;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%; height: 17px;&quot;&gt;Members&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 15px;&quot;&gt;
&lt;td style=&quot;width: 16.0465%; height: 15px;&quot;&gt;FileSystemInfo&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%; height: 15px;&quot;&gt;Field : FullPath, OriginalPath&lt;br /&gt;Property : Attributes, CreationTime, CreationTimeUtc, Exists, Extension, FullName, LastAccessTime, LastAccessTimeUtc, LastWriteTime, LastWriteTimeUtc, Name Methods: Delete, GetObjectData, Refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.0465%; height: 17px;&quot;&gt;DirectoryInfo&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%; height: 17px;&quot;&gt;Property : Parent, Root&lt;br /&gt;Method : Create, CreateSubdirectory, EnumerateDirectories, EnumerateFiles, EnumerateFileSystemInfos, GetAccessControl, GetDirectories, GetFiles, GetFileSystemInfos, MoveTo, SetAccessControl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.0465%;&quot;&gt;FileInfo&lt;/td&gt;
&lt;td style=&quot;width: 83.9535%;&quot;&gt;Property : Directory, DirectoryName, IsReadOnly, Length&lt;br /&gt;Methods: AppendText, CopyTo, Create, CreateText, Decrypt, Encrypt, GetAccessControl, MoveTo, Open, OpenRead, OpenText, OpenWrite, Replace, SetAccessControl&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 하나의 File에 대해 FileInfo instance를 통해 여러 동작을 어떻게 효휼적으로 수행할 수 있는지를 알려주기 위한 것으로 Program.cs에서 backup file에 대한 FileInfo instance를 생성하고 console에 이에 대한 정보를 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688879220506&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SectionTitle(&quot;File information&quot;);

string dir = Combine(GetFolderPath(SpecialFolder.Personal), &quot;OutputFiles&quot;);
string backupFile = Combine(dir, &quot;Dummy.bak&quot;);

FileInfo info = new(backupFile);
Console.WriteLine($&quot;{backupFile}:&quot;);
Console.WriteLine($&quot;Contains {info.Length} bytes&quot;);
Console.WriteLine($&quot;Last accessed {info.LastAccessTime}&quot;);
Console.WriteLine($&quot;Has readonly set to {info.IsReadOnly}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTyhcm/btsFdVllP0E/XQ8ZqayhxHkbKzhK6oqgf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTyhcm/btsFdVllP0E/XQ8ZqayhxHkbKzhK6oqgf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTyhcm/btsFdVllP0E/XQ8ZqayhxHkbKzhK6oqgf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTyhcm%2FbtsFdVllP0E%2FXQ8ZqayhxHkbKzhK6oqgf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;210&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;byte에 대한 숫자는 OS마다 다른 line ending을 사용할 수 있으므로 다르게 표시될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) File에 대한 처리방식 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File에 대한 어떤 처리를 진행하고자 할 때 경우에 따라 어떤 형태로 file을 open 해야 할지 지정하는 경우가 있으며 이런 경우 File.Open method를 사용할 수 있습니다. Open method는 enum값을 통해 추가적인 option을 지정할 수 있는 overload를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용가능한 enum값으로는 다음과 같은 것이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FileMode : CreateNew, OpenOrCreate, Truncate 등 해당 file에 무엇을 할지 지정합니다.&lt;/li&gt;
&lt;li&gt;FileAccess : ReadWrite와 같이 필요한 접근 수준을 지정합니다.&lt;/li&gt;
&lt;li&gt;FileShare : 다른 process에서 Read와 같이 접근가능한 수준을 지정함으로써 file에 대한 잠금을 조정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 file을 열고 해당 file로부터 읽기를 수행하면서 다른 process에서도 역시 읽기를 허용하려면 다음과 같이 지정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688952949476&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FileStream file = File.Open(textFile, FileMode.Open, FileAccess.Read, FileShare.Read);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 file에 대한 속성을 위해 사용가능한 아래와 enum값도 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FileAttributes : FileSystemInfo에서 파생된 type의 attrubute 속성에서 Archive와 Encrypted와 같은 값을 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 file이나 directory의 속성은 아래와 같이 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688953328017&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FileInfo info = new(backupFile);
WriteLine(&quot;Is the backup file compressed? {0}&quot;, info.Attributes.HasFlag(FileAttributes.Compressed));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. Stream 읽기/쓰기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream은 읽고 쓸 수 있는 일련의 byte입니다. File을 array처럼 처리할 수 있지만 file내 byte의 위치를 알면 random access가 제공되므로 순차적으로 접근할 수 있는 stream으로서 file을 처리하는 것이 유용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream은 또한 terminal 입출력 및 random access를 제공하지 않으며 위치를 탐색(즉, 이동)할 수 없는 socket과 port 같은 networking resource를 처리하는 데에도 사용할 수 있습니다. 이에 따라 data stream이 어디서 온 것인지 신경 쓸 필요 없이 임의의 byte를 처리할 수 있는 code를 작성할 수 있으며 이때 code는 단순히 stream을 읽고 쓰기만 하면 다른 한편에서는 실제 byte가 저장된 곳을 처리할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Stream의 추상화 및 구체화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream이라는 추상화 class는 stream에 대한 모든 type을 표현합니다. 추상화 class자체는 new를 사용해 instance를 생성할 수 없으며 상속만이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 해당 기반 class로부터 상속된 다수의 구체화 class가 존재하는데 여기에는 FileStream, MemoryStream, BufferedStream, GZipStream 그리고 SslStream과 같은 것이 존재하며 이들은 모두 같은 방식으로 작동합니다. 모든 stream은 IDisposable interface를 구현하고 있으므로 비관리 resource를 해제하기 위한 Dispose method를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream class에서 가지고 있는 일부 공통 member에 관해서는 아래 표에서 확인할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 174px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 17px;&quot;&gt;Member&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 40px;&quot;&gt;CanRead, CanWrite&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 40px;&quot;&gt;해당 속성은 stream을 읽거나 쓸수 있는지의 여부를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 40px;&quot;&gt;Length, Position&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 40px;&quot;&gt;해당 속성은 총 byte길이와 stream에서의 현재 위치를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 20px;&quot;&gt;Dispose&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 20px;&quot;&gt;해당 method는 stream을 닫고 resource를 해제합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 40px;&quot;&gt;Flush&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 40px;&quot;&gt;Stream이 bufer를 가지고 있다면 해당 method는 buffer의 byte를 stream에 기록하고 buffer를 비웁니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.9767%; height: 17px;&quot;&gt;CanSeek&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%; height: 17px;&quot;&gt;해당 속성은 Seek method가 사용가능한지를 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;Seek&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 현재 위치를 매개변수로 지정된 곳으로 이동합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;Read, ReadAsync&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 stream으로 부터 지정한 수의 byte를 byte배열로 읽고 위치를 앞당깁니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;ReadByte&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 stream으로 부터 다음 byte를 읽고 위치를 앞당깁니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;Write, WriteAsync&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 byte array의 내용을 stream으로 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.9767%;&quot;&gt;WriteByte&lt;/td&gt;
&lt;td style=&quot;width: 78.0233%;&quot;&gt;해당 method는 byte를 stream으로 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Storage stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 byte가 저장될 위치를 나타내는 일부 storage stream을 설명하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;FileStream&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;filesystem에 저장된 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;MemoryStream&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;현재 process의 memory에 저장된 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 23.217%;&quot;&gt;System.Net.Sockets&lt;/td&gt;
&lt;td style=&quot;width: 31.2403%;&quot;&gt;NetworkStream&lt;/td&gt;
&lt;td style=&quot;width: 45.5426%;&quot;&gt;network 위치에 저장된 byte&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;File Stream은&lt;span&gt; Windows상에서&lt;/span&gt;&lt;/span&gt; 성능과 안정성이 더육 향상되도록 .NET 6에서 재작성되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/file-io-improvements-in-dotnet-6/&quot;&gt;File IO improvements in .NET 6 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Function stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 자체적으로는 존재하지 않지만 다른 stream에 연결되어 기능을 추가시키는 function stream을 설명하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Security.Cryptography&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;CryptoStream&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Stream을 암호화/복호화 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.IO.Compression&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;GZipStream, DeflateStream&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Stream을 압축하고 해제합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Net.Security&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;AuthenticatedStream&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Stream전역에 자격증명을 보냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Stream helper&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비록 저수준에서 stream을 사용해야 하는 경우가 있겠지만 대부분은 helper class를 사용하는 것만으로 손쉽게 구현할 수 있습니다. Stream에 대한 모든 helper type은 IDisposable을 구현하고 있으므로 비관리 resource를 해제하기 위한 Dispose method를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 상황에서 사용가능한 몇몇 helper class는 아래 표에 안내되어 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 205px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;Class&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 40px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 40px;&quot;&gt;StreamReader&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 40px;&quot;&gt;기본 stream으로 부터 일반 text를 읽습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 40px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 40px;&quot;&gt;StreamWriter&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 40px;&quot;&gt;기본 stream으로 일반 text를 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 57px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 57px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 57px;&quot;&gt;BinaryReader&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 57px;&quot;&gt;Stream으로 부터 .NET type을 읽습니다. 예를 들어 ReadDecimal method는 기본 stream에서 decimal 값을 다음 16 byte를 읽고 ReadInt32 method는 int값으로 다음 4 byte를 읽습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;System.IO&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;BinaryWriter&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;Stream으로 .NET type을 작성합니다. 예를 들어 decimal 매개변수를 가진 Write method는 기본 stream으로 16byte를 작성하며 int 매개변수를 가진 Write method는 4 byte를 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;System.Xml&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;XmlReader&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;기본 stream으로 부터 XML format을 사용해 읽습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.6124%; height: 17px;&quot;&gt;System.Xml&lt;/td&gt;
&lt;td style=&quot;width: 15.31%; height: 17px;&quot;&gt;XmlWrite&lt;/td&gt;
&lt;td style=&quot;width: 70.0775%; height: 17px;&quot;&gt;기본 stream으로 XML format을 사용해 작성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Stream pipeline 구축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StreamWriter와 같은 helpler와 CryptoStream이나 GZipStream과 같은 여러 기능 Stream을 stream을 저장하는 FileStream과 pipeline으로 결합하는 것은 흔한 경우입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqoGCD/btsFf4pt9JZ/O2JJm67OV9VVzok1OvzKVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqoGCD/btsFf4pt9JZ/O2JJm67OV9VVzok1OvzKVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqoGCD/btsFf4pt9JZ/O2JJm67OV9VVzok1OvzKVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqoGCD%2FbtsFf4pt9JZ%2FO2JJm67OV9VVzok1OvzKVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;95&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WriteLine method를 통해 hello라는 문자열을 pipeline을 통해서 보내면 이것은 암호화와 압축 stream을 거쳐게 되고 최종목적지에 도달하게 되면 'G7x'라고 쓰여진 file의 결과물을 얻게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Text stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream으로 text를 작성하는 예제를 만들기 위해 csStudy09 solution을 생성하고 이어서 WorkingWithStreams이름의 Console app project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project가 생성되면 Viper.cs라는 이름의 class file을 추가하고 Callsigns이름의 문자열배열값을 가진 Viper이름의 정적 class를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 System.Xml namespace와 System.Environment, System.IO.path namespace를 정적 import 합니다. 그리고 Viper call signs를 열거하면서 각각 한 줄씩 text file에 기록하도록 하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688974601496&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string textFile = Combine(CurrentDirectory, &quot;streams.txt&quot;);
            
// text file을 생성한 뒤 helper writer를 반환
StreamWriter text = File.CreateText(textFile);
            

foreach (string item in Viper.Callsigns)
{
    text.WriteLine(item);
}

text.Close(); // release resources

// file의 크기와 내용 출력
Console.WriteLine(&quot;{0} contains {1:N0} bytes.&quot;, arg0: GetFileName(textFile), arg1: new FileInfo(textFile).Length);
Console.WriteLine(File.ReadAllText(textFile));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caN0Wz/btsFcklFklN/Mu3nvUzN6evF4Qx7Nbm7qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caN0Wz/btsFcklFklN/Mu3nvUzN6evF4Qx7Nbm7qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caN0Wz/btsFcklFklN/Mu3nvUzN6evF4Qx7Nbm7qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaN0Wz%2FbtsFcklFklN%2FMu3nvUzN6evF4Qx7Nbm7qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;207&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) XML stream&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML 요소를 작성하는 데는 2가지 방식이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WriteStartElement와 WriteEndElement : 요소가 자식요소를 가지는 경우 같이 사용합니다.&lt;/li&gt;
&lt;li&gt;WriteElementString : 요소가 자식요소를 가지지 않는 경우 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽거나 쓰기 위해 file을 열면 .NET의 외부 resource를 사용하는 것으로 이를 비관리 resouce라고 하며 대상 file에 대한 작업이 종료되면 instance는 반드시 소멸되어야 하며&amp;nbsp;대게는 resource를 소멸할 때 try ~ finally block안에서 Dispose method를 호출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 Viper pilot 호출 sign에 대한 문자열 배열값을 XML file로 저장하도록 하는 것으로 Program.cs에서 호출 sign을 열거하고 각각을 단일 XML file의 요소로 작성하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689033991291&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//작성을 위한 file경로 정의
string xmlFile = Combine(CurrentDirectory, &quot;streams.xml&quot;);

// filestream과 XML writer에 대한 변수 선언
FileStream? xmlFileStream = null;
XmlWriter? xml = null;

try
{
    // file stream 생성
    xmlFileStream = File.Create(xmlFile);
    // file stream을 XML writer helper로 wrapping하고 자동적으로 중첩된 요소간 들여쓰기를 적용합니다.
    xml = XmlWriter.Create(xmlFileStream, new XmlWriterSettings { Indent = true });

    xml.WriteStartDocument();
    // root 요소 작성
    xml.WriteStartElement(&quot;callsigns&quot;);

    //string을 열거하고 각각을 stream에 작성합니다.
    foreach (string item in Viper.Callsigns)
    {
        xml.WriteElementString(&quot;callsign&quot;, item);
    }

    // 종료 root 요소 작성
    xml.WriteEndElement();
    // helper와 stream닫기
    xml.Close();
    xmlFileStream.Close();
}
catch (Exception ex) {
    //해당 경로가 존재하지 않으면 예외발생
    Console.WriteLine($&quot;{ex.GetType()} says {ex.Message}&quot;);
}
finally {
    if (xml != null)
    {
        xml.Dispose();
        Console.WriteLine(&quot;비관리 resource 해제&quot;);
    }

    if (xmlFileStream != null)
    {
        xmlFileStream.Dispose();
        Console.WriteLine(&quot;File stream의 비관리 resource 해제&quot;);
    }
}

//File 내용 출력
Console.WriteLine(&quot;{0} contains {1:N0} bytes.&quot;, arg0: Path.GetFileName(xmlFile), arg1: new FileInfo(xmlFile).Length);
Console.WriteLine(File.ReadAllText(xmlFile));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsyaLu/btsFdqFSxZK/zko2ppqqJERRllYjDr9OKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsyaLu/btsFdqFSxZK/zko2ppqqJERRllYjDr9OKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsyaLu/btsFdqFSxZK/zko2ppqqJERRllYjDr9OKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsyaLu%2FbtsFdqFSxZK%2Fzko2ppqqJERRllYjDr9OKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;282&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Dispose method를 호출하기 전 개체가 null인지를 확인해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) using문을 사용해 소멸을 단순화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 null 개체에 대한 확인 code를 좀 더 단순화한 다음 using 문을 통해 Dispose method를 호출하도록 바꿀 수 있습니다. 일반적으로는 더 높은 수준에서의 제어가 필요하지 않은 이상에는 수동적으로 Dispose method를 호출하기보다는 using문의 사용을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;using keyword자체가 namespace를 import 하고 IDisposable를 구현하는 개체에 Dispose를 호출할 finally문을 생성하는 2가지로 사용된다는 측면에서 약간 혼란스러울 수 있지만 실제 사용해 보면 이 둘은 아주 명확히 구분된다는 것을 알 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서 compiler는 using 문 block을 catch문이 없는 try-finally문으로 변경합니다. 물론 여기에서도 try문을 충첩적으로 사용할 수 있으므로 어떤 예외사항을 잡아내야 한다면 아래와 같이 구현할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689036298231&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using (FileStream file = File.OpenWrite(Path.Combine(CurrentDirectory, &quot;test.txt&quot;)))
{
    using (StreamWriter writer = new StreamWriter(file))
    {
        try
        {
            writer.WriteLine(&quot;.NET!&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;{ex.GetType()} says {ex.Message}&quot;);
        }
    } // 개체가 null이 아니면 자동적으로 Dispose를 호출
} // 개체가 null이 아니면 자동적으로 Dispose를 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 위 예제는 using문을 위한 괄호와 들여 쓰기를 명시하지 않음으로써 아래와 같이 더욱 간소화할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689037248934&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using FileStream file = File.OpenWrite(Path.Combine(CurrentDirectory, &quot;test.txt&quot;));
using StreamWriter writer = new(file);

try
{
    writer.WriteLine(&quot;.NET!&quot;);
}
catch (Exception ex)
{
    Console.WriteLine($&quot;{ex.GetType()} says {ex.Message}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Stream 압축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML은 상대적으로 장황한 편이라 일반적인 text보다 더 많은 byte공간을 차지하게 됩니다. 이제 GZIP이라는 일반적인 압축 algorithm을 사용하여 XML을 얼마나 압축할 수 있는지를 확인해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Core 2.1에서 Microsoft는 Brotli이라는 압축 algorithm의 구현을 도입하였습니다. 성능면에서 Brotli는 DEFLATE와 GZIP에서 사용된 algorithm과 비슷하지만 20% 정도 더 압축력이 높다고 평가되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 GZIP과 Brotli 2개의 압축 algorithm을 비교해 보고자 Project에 Compress.cs라는 class file을 추가합니다. Compress.cs에서는 GZipStream과 BrotliStream에 대한 instance를 사용하여 위와 동일한 XML 요소를 포함하는 압축 file을 생성한 다음 읽는 동안 압축을 풀고 console에 해당 내용을 출력하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689063406581&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.IO.Compression; // BrotliStream, GZipStream, CompressionMode
using System.Xml; // XmlWriter, XmlReader
using static System.Environment; // CurrentDirectory
using static System.IO.Path; // Combine

namespace WorkingWithStreams
{
    partial class Program
    {
        static void Compress(string algorithm = &quot;gzip&quot;)
        {
            //file의 확장자를 통해 algorithm을 사용하는 file 경로 정의
            string filePath = Combine(CurrentDirectory, $&quot;streams.{algorithm}&quot;);

            FileStream file = File.Create(filePath);
            Stream compressor;

            if (algorithm == &quot;gzip&quot;)
            {
                compressor = new GZipStream(file, CompressionMode.Compress);
            }
            else
            {
                compressor = new BrotliStream(file, CompressionMode.Compress);
            }

            using (compressor)
            {
                using (XmlWriter xml = XmlWriter.Create(compressor))
                {
                    xml.WriteStartDocument();
                    xml.WriteStartElement(&quot;callsigns&quot;);
                    foreach (string item in Viper.Callsigns)
                    {
                        xml.WriteElementString(&quot;callsign&quot;, item);
                    }
                }
            }

            Console.WriteLine(&quot;{0} contains {1:N0} bytes.&quot;, Path.GetFileName(filePath), new FileInfo(filePath).Length);
            Console.WriteLine($&quot;The compressed contents:&quot;);
            Console.WriteLine(File.ReadAllText(filePath));
            
            Console.WriteLine(&quot;Reading the compressed XML file:&quot;);
            file = File.Open(filePath, FileMode.Open);
            Stream decompressor;

            if (algorithm == &quot;gzip&quot;)
            {
                decompressor = new GZipStream(file, CompressionMode.Decompress);
            }
            else
            {
                decompressor = new BrotliStream(file, CompressionMode.Decompress);
            }

            using (decompressor)
            {

                using (XmlReader reader = XmlReader.Create(decompressor))
                {

                    while (reader.Read())
                    {
                        //callsign 요소가 있는지 확인
                        if ((reader.NodeType == XmlNodeType.Element) &amp;amp;&amp;amp; (reader.Name == &quot;callsign&quot;))
                        {
                            reader.Read(); //요소 내부 text로 이동
                            Console.WriteLine($&quot;{reader.Value}&quot;); //해당 값 읽기
                        }
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 gzip과 brotli algorithm을 사용하기 위한 매개변수와 함께 Compress method를 호출하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689063497875&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Compress(algorithm: &quot;gzip&quot;);
Compress(algorithm: &quot;brotli&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같이 XML file과 gzip 및 brotli algorithm을 통해 압축된 XML file의 size를 비교하는 결과를 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D6thE/btsFfb8R8Rc/f0PpbPRnUM8KlaDEdRAes0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D6thE/btsFfb8R8Rc/f0PpbPRnUM8KlaDEdRAes0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D6thE/btsFfb8R8Rc/f0PpbPRnUM8KlaDEdRAes0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD6thE%2FbtsFfb8R8Rc%2Ff0PpbPRnUM8KlaDEdRAes0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;466&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연한 이야기지만 압축된 file은 압축되지 않은 동일한 file size의 size보다 훨씬 적음을 알 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;압축 mode와 더불어 압축 level역시 선택할 수 있습니다. 자세한 사항은 아래 link를 참고해 주시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot;&gt;CompressionLevel Enum (System.IO.Compression) | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708932155515&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CompressionLevel Enum (System.IO.Compression)&quot; data-og-description=&quot;Specifies values that indicate whether a compression operation emphasizes speed or compression size.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bq1cAu/hyVqq9wJGg/tPSkeUnq3iGGoO9K7jzvok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.compressionlevel?view=net-8.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bq1cAu/hyVqq9wJGg/tPSkeUnq3iGGoO9K7jzvok/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CompressionLevel Enum (System.IO.Compression)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Specifies values that indicate whether a compression operation emphasizes speed or compression size.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) tar 압축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장자가 .tar인 file은 Unix기반의 압축 application인 tar에 의해 생성된 것입니다. 확장자가 .tag.gz인 file도 존재하는데 이는 tar로 file이 만들어지고 GZIP 압축 algorithm을 사용해 압축되어 생성된 것임을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서는 System.Formats.Tar assembly를 통해 tar 압축 file을 읽고, 생성하며 압축하거나 풀 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 관련해 TarFile class에서는 아래 표에서의 static public member를 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;Member&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;CreateFromDirectory&lt;br /&gt;CreateFromDirectoryAsync&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;지정된 directory로 부터 지정된 모든 filesystem 항목을 포함하는 stream을 생성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;ExtractToDirectory&lt;br /&gt;ExtractToDirectoryAsync&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;지정한 directory에서 tar 압축을 나타내는 stream의 content를 추출합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.3023%;&quot;&gt;DefaultCapacity&lt;/td&gt;
&lt;td style=&quot;width: 75.6977%;&quot;&gt;Windows의 MAX_PATH (260)이 임의 기본 용량으로 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련한 예제 작성을 위해 csStudy09 solution에서 WorkingWithTarArchives이름의 Console app project를 생성합니다. 그리고 Helpers.cs라는 class file을 추가하여 error, 경고, 정보 message를 console에 적절한 색상으로 출력하는 3개의 method를 가진 partial Program class를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689129665953&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Console;

namespace WorkingWithTarArchives
{
    partial class Program
    {
        static void WriteError(string message)
        {
            ConsoleColor previousColor = ForegroundColor;
            ForegroundColor = ConsoleColor.Red;
            WriteLine($&quot;FAIL: {message}&quot;);
            ForegroundColor = previousColor;
        }

        static void WriteWarning(string message)
        {
            ConsoleColor previousColor = ForegroundColor;
            ForegroundColor = ConsoleColor.DarkYellow;
            WriteLine($&quot;WARN: {message}&quot;);
            ForegroundColor = previousColor;
        }
        static void WriteInformation(string message)
        {
            ConsoleColor previousColor = ForegroundColor;
            ForegroundColor = ConsoleColor.Blue;
            WriteLine($&quot;INFO: {message}&quot;);
            ForegroundColor = previousColor;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 project의 실행 file이 동작하는 bin folder에 images라는 folder를 생성하고 아래 image들을 내려받은 뒤 해당 folder로 복사해 놓습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/cBWRs2/btsFdXwJ9c2/Z8Fmaf0kw6BeRGyRxoh22k/images.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;images.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;2.48MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 지정한 folder의 content를 tar로 압축한 다음 이것을 다시 새로운 folder로 추출하는 구문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689131271529&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try
{
    string current = Environment.CurrentDirectory;
    WriteInformation($&quot;Current directory: {current.Split(Path.DirectorySeparatorChar).Last()}&quot;);

    string sourceDirectory = Path.Combine(current, &quot;images&quot;);
    string destinationDirectory = Path.Combine(current, &quot;extracted&quot;);
    string tarFile = Path.Combine(current, &quot;images-archive.tar&quot;);

    if (!Directory.Exists(sourceDirectory))
    {
        WriteError($&quot;The {sourceDirectory.Split(Path.DirectorySeparatorChar).Last()} directory must exist. Please create it and add some files to it.&quot;);
        return;
    }

    //tar file이 존재한다면 삭제
    if (File.Exists(tarFile))
    {
        File.Delete(tarFile);
        WriteWarning($&quot;{Path.GetFileName(tarFile)} already existed so it was deleted.&quot;);
    }

    WriteInformation($&quot;Archiving directory: {sourceDirectory.Split(Path.DirectorySeparatorChar).Last()}\n To .tar file: {Path.GetFileName(tarFile)}&quot;);

    TarFile.CreateFromDirectory(sourceDirectoryName: sourceDirectory, destinationFileName: tarFile, includeBaseDirectory: true);

    WriteInformation($&quot;Does {Path.GetFileName(tarFile)} exist? {File.Exists(tarFile)}.&quot;);

    //destinationDirectory folder가 존재하지 않으면 새로 생성
    if (!Directory.Exists(destinationDirectory))
    {
        Directory.CreateDirectory(destinationDirectory);
        WriteWarning($&quot;{destinationDirectory.Split(Path.DirectorySeparatorChar).Last()} did not exist so it was created.&quot;);
    }

    WriteInformation($&quot;Extracting archive: {Path.GetFileName(tarFile)}\n To directory: {destinationDirectory.Split(Path.DirectorySeparatorChar).Last()}&quot;);

    TarFile.ExtractToDirectory(sourceFileName: tarFile, destinationDirectoryName: destinationDirectory, overwriteFiles: true);

    if (Directory.Exists(destinationDirectory))
    {
        foreach (string dir in Directory.GetDirectories(destinationDirectory))
        {
            WriteInformation($&quot;Extracted directory {dir.Split(Path.DirectorySeparatorChar).Last()} containing these files: &quot; + string.Join(',', Directory.EnumerateFiles(dir).Select(file =&amp;gt; Path.GetFileName(file))));
        }
    }
}
catch (Exception ex)
{
    WriteError(ex.Message);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjqQZs/btsFcQSeQzG/kGwGwdzZGniTA2TKGYka1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjqQZs/btsFcQSeQzG/kGwGwdzZGniTA2TKGYka1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjqQZs/btsFcQSeQzG/kGwGwdzZGniTA2TKGYka1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjqQZs%2FbtsFcQSeQzG%2FkGwGwdzZGniTA2TKGYka1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;188&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 반디집과 같이 tar archive의 content를 볼 수 있는 software가 있다면 이 것을 사용해 images-archive.tar file의 content를 다음과 같이 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H3gYV/btsFcQklRPL/DjQtUciMO0WK5EJXfnNK7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H3gYV/btsFcQklRPL/DjQtUciMO0WK5EJXfnNK7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H3gYV/btsFcQklRPL/DjQtUciMO0WK5EJXfnNK7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH3gYV%2FbtsFcQklRPL%2FDjQtUciMO0WK5EJXfnNK7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;398&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● tar 항목 읽기 및 쓰기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TarFile class 외에도 tar 압축 file에서 개별적인 항목을 읽고 쓰기 위한 TarEntry, TarEntryFormat, TarReader, TarWriter 등의 class가 존재합니다. 이들은 GzipStream과 결합하여 쓰거나 읽을 때 항목을 압축하거나 해제할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET에서 지원하는 tar에 관한 더 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot;&gt;System.Formats.Tar Namespace | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689142814567&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;System.Formats.Tar Namespace&quot; data-og-description=&quot;Contains types used in reading and writing data in the Tape Archive (TAR) file archiving format.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fagHD/hyTimuysbV/D0pOn6MrBPnGK5OskEH9UK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.formats.tar?view=net-7.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fagHD/hyTimuysbV/D0pOn6MrBPnGK5OskEH9UK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.Formats.Tar Namespace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contains types used in reading and writing data in the Tape Archive (TAR) file archiving format.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Random access handle을 사용한 읽고 쓰기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET의 처음 20년 동안은 file을 직접적으로 처리하는 유일한 API는 stream class였습니다. 이들은 data를 순차적으로 처리해야 하는 자동화된 작업에 적합합니다. 하지만 사람이 data와 상호작용하는 경우 종종 같은 위치로 여러번 이동하고 되돌아 와야할 때가 있습니다. 이에 따라 .NET 6부터는 file stream이 필요없이 file에 대해 직접적으로 작동하면서 randome으로 접근할 수 있는 새로운 API를 도입하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예제를 만들어 보기 위해 아래와 같이 code를 작성합니다. 이 예제는 myTxt.txt라는 file에 대한 handle을 가져오도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708934236353&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.Win32.SafeHandles;
using System.Text;

using SafeFileHandle handle = File.OpenHandle(path: &quot;myTxt.txt&quot;, mode: FileMode.OpenOrCreate, access: FileAccess.ReadWrite);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런다음 file에 저장할 내용을 아래와 같이 작성합니다. 이 예제는 abcdefg라는 문자열을 byte array로 encoding하여 읽기전용 memory buffer에 담은 다음 file로 저장하는 동작을 수행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708934493219&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReadOnlyMemory&amp;lt;byte&amp;gt; buffer = new(Encoding.UTF8.GetBytes(&quot;abcdefg&quot;));
await RandomAccess.WriteAsync(handle, buffer, fileOffset: 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file을 읽기위해서는 우선 file의 길이(length)값을 가져온 다음 그 값을 통해 읽은 content를 담아둘 memory buffer를 할당합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708934681136&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long length = RandomAccess.GetLength(handle);
Memory&amp;lt;byte&amp;gt; contentBytes = new(new byte[length]);

await RandomAccess.ReadAsync(handle, contentBytes, fileOffset: 0);
string content = Encoding.UTF8.GetString(contentBytes.ToArray());
WriteLine($&quot;파일내용: {content}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sKwAa/btsFkVqUDF1/H2x69rnAU41iMHgi4ISKEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sKwAa/btsFkVqUDF1/H2x69rnAU41iMHgi4ISKEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sKwAa/btsFkVqUDF1/H2x69rnAU41iMHgi4ISKEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsKwAa%2FbtsFkVqUDF1%2FH2x69rnAU41iMHgi4ISKEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Text의 encoding과 decoding&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text 문자는 다양한 방식으로 표현될 수 있습니다. 예를 들어 alphabet은 전신을 통해 전송할 수 있는 일련의 점과 선으로 구성되는 Morse 부호로 encode 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷하게 computer에서의 text도 code의 공간 안에서 code point를 나타내는 bit(1과 0)로 저장됩니다. 대부분의 code point는 단일 문자로 표현되지만 형식지정과 같은 다른 의미를 가질 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 ASCII는 128 code point를 가진 code공간을 가지고 있습니다. .NET은 Unicode라고 하는 표준을 통해 text를 내부적으로 endcode 합니다. Unicode는 백만 code point이상을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 text를 Unicode를 사용하지 않거나 Unicode의 변형을 사용하는 system에서 사용하기 위해 .NET 외부로 보내야 하는 경우가 있는데 이 때문에 encoding사이에 변환방법을 아는 것은 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 computer에서 일반적으로 사용되는 text ecoding을 나열하고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;Encoding&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;ASCII&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;byte중 하위 7bit만을 사용함으로서 문자의 범위가 제한적입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-8&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;각 Unicode code point를 1~4byte의 sequence로 나타냅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-7&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;UTF-8보다 7bit channel에서 더 효휼적으로 설계되었지만 보안과 견고함에서 문제점을 가지고 있으므로 UTF-7보다는 UTF-8을 더 권장합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-16&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;각 Unicode code point를 1~2개의 16bit integer로 표현합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;UTF-32&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;각 Unicode code point를 32bit integer로 표현하므로 가변 길이 encoding의 다른 Unicode encoding과는 달리 고정된 길이의 encoding입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.7442%;&quot;&gt;ANSI/ISO encodings&lt;/td&gt;
&lt;td style=&quot;width: 78.2558%;&quot;&gt;특정 언어 또는 언어의 group을 지원하기 위해 사용되는 다양한 code page에 대한 지원을 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현시점에 대부분의 system은 UTF-8을 기본으로 사용하고 있으며 .NET에서는 Encoding.Default로 나타낼 수 있습니다. 상술하였듯 Encoding.UTF7은 보안에 취약하므로 사용을 피해야 하며 이러한 이유로 C# compiler는 UTF-7 사용을 시도할 때 경고를 띄우고 있습니다. UTF-7은 다른 system과의 호환성을 위해 어쩔 수 없이 text를 encoding 해야 하는 경우를 위해서 선택적으로 사용할 수 있도록 남아있을 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Byte array로 문자열 encoding 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy09 solution에서 WorkingWithEncodings이름의 project를 생성하고 Program.cs에서는 System.Text namespace를 import 한 뒤 사용자로 부터 선택된 encoding을 사용해 문자열을 encode 하는 문을 아래와 같이 작성합니다. 또한 각 byte는 loop를 통해 문자열로 decode 한 다음 이를 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689213097165&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WriteLine(&quot;Encodings&quot;);
WriteLine(&quot;[1] ASCII&quot;);
WriteLine(&quot;[2] UTF-7&quot;);
WriteLine(&quot;[3] UTF-8&quot;);
WriteLine(&quot;[4] UTF-16 (Unicode)&quot;);
WriteLine(&quot;[5] UTF-32&quot;);
WriteLine(&quot;[6] Latin1&quot;);
WriteLine(&quot;[any other key] Default encoding&quot;);
WriteLine();
            
Write(&quot;Press a number to choose an encoding.&quot;);
ConsoleKey number = ReadKey(intercept: true).Key;
WriteLine(); WriteLine();

Encoding encoder = number switch
{
    ConsoleKey.D1 or ConsoleKey.NumPad1 =&amp;gt; Encoding.ASCII,
    ConsoleKey.D2 or ConsoleKey.NumPad2 =&amp;gt; Encoding.UTF7,
    ConsoleKey.D3 or ConsoleKey.NumPad3 =&amp;gt; Encoding.UTF8,
    ConsoleKey.D4 or ConsoleKey.NumPad4 =&amp;gt; Encoding.Unicode,
    ConsoleKey.D5 or ConsoleKey.NumPㄴad5 =&amp;gt; Encoding.UTF32,
    ConsoleKey.D6 or ConsoleKey.NumPad6 =&amp;gt; Encoding.Latin1,
    _ =&amp;gt; Encoding.Default
};

// encode할 문자열
string message = &quot;Caf&amp;eacute; &amp;pound;4.39&quot;;
WriteLine($&quot;Text to encode: {message} Characters: {message.Length}&quot;);

// 문자열을 byte array로 encode
byte[] encoded = encoder.GetBytes(message);

// encode후 byte 확인
WriteLine(&quot;{0} used {1:N0} bytes.&quot;, encoder.GetType().Name,
encoded.Length);
WriteLine();

// 각 byte 열거
WriteLine($&quot;BYTE | HEX | CHAR&quot;);
foreach (byte b in encoded)
{
    WriteLine($&quot;{b,4} | {b.ToString(&quot;X&quot;),3} | {(char)b,4}&quot;);
}

string decoded = encoder.GetString(encoded);
WriteLine(decoded);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 ASCII선택을 위해 1을 누르면 아래와 같이 결과를 확인할 수 있습니다. 표시된 byte결과를 보면 pound 기호 (&amp;pound;)와 accent e (&amp;eacute;)는 ASCII로 표현되지 않았음을 확인할 수 있습니다. 따라서 이때는 ?문자가 대신 사용되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAzYHZ/btsFfcmrxeX/WzawePkVKWWD2rw4RUX9PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAzYHZ/btsFfcmrxeX/WzawePkVKWWD2rw4RUX9PK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAzYHZ/btsFfcmrxeX/WzawePkVKWWD2rw4RUX9PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAzYHZ%2FbtsFfcmrxeX%2FWzawePkVKWWD2rw4RUX9PK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;474&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 예제를 실행하고 이번에는 UTF-8인 3을 입력합니다. UTF-8에서는 위 두 문자를 위해 추가로 2 byte를 더 필요로 하지만(총 12byte)&amp;nbsp; &amp;eacute; 과 &amp;pound; 문자에 대한 encode와 decode가 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7syeD/btsFfWXXkMP/RaEyk5Zk5PBQWPOpjqzs11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7syeD/btsFfWXXkMP/RaEyk5Zk5PBQWPOpjqzs11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7syeD/btsFfWXXkMP/RaEyk5Zk5PBQWPOpjqzs11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7syeD%2FbtsFfWXXkMP%2FRaEyk5Zk5PBQWPOpjqzs11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;488&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UTF-16은 모든 문자에 대해 2byte를 필요로 하므로 총 20byte가 사용되며 &amp;eacute; 와 &amp;pound;문자 역시 encode와 decode가 가능합니다. 해당 encoding은 .NET에서 내부적으로 char와 string값을 저장하기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) File로 text를 encoding 하고 decoding 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StreamReader와 StreamWriter 같은 stream helper class를 사용할 때 사용하고자 하는 encoding을 지정할 수 있습니다. helper로 작성하면 text는 자동적으로 encoded 되며 helper로부터 읽으면 byte는 자동적으로 decode 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Encoding을 지정하려면 helper type의 생성자로 두 번째 매개변수를 통해 encoding을 다음과 같이 전달합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689214867957&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StreamReader reader = new(stream, Encoding.UTF8); 
StreamWriter writer = new(stream, Encoding.UTF8);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 다른 system에서 사용할 file을 생성하기 때문에 어떤 encoding을 사용할지 선택할 수 없는 경우가 있습니다. 만약 이런 상황이라면 가장 적은 수의 byte를 사용하되 필요한 모든 문자를 저장할 수 있는 것을 선택해 보는 것이 도움이 될 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Random access handle을 가지는 읽기 및 쓰기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6부터는 file stream이 없이도 file을 대상으로 작업이 가능한 새로운 API를 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 우선 아래와 같이 file에 대한 handle을 가져와야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689232689457&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Microsoft.Win32.SafeHandles;
using System.Text;

using SafeFileHandle handle = File.OpenHandle(path: &quot;test.txt&quot;, mode: FileMode.OpenOrCreate, access: FileAccess.ReadWrite);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하고 나면 byte array로 encode 된 text를 읽기 전용 memory buffer에 아래와 같이 작성하고 이를 file로 저장할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689232873056&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReadOnlyMemory&amp;lt;byte&amp;gt; buffer = new(Encoding.UTF8.GetBytes(&quot;test123123&quot;));
await RandomAccess.WriteAsync(handle, buffer, fileOffset: 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;file에서 읽기 위해서는 file의 길이(크기)를 가져와 해당 길이만큼의 공간을 memory buffer에 할당하여 file을 읽어 들입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689233040103&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long length = RandomAccess.GetLength(handle);
Memory&amp;lt;byte&amp;gt; contentBytes = new(new byte[length]);
await RandomAccess.ReadAsync(handle, contentBytes, fileOffset: 0);

string content = Encoding.UTF8.GetString(contentBytes.ToArray());
WriteLine($&quot;Content of file: {content}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QHeWV/btsFeTUWyLL/SrFOP2QcZoX4nJXKkbKao0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QHeWV/btsFeTUWyLL/SrFOP2QcZoX4nJXKkbKao0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QHeWV/btsFeTUWyLL/SrFOP2QcZoX4nJXKkbKao0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQHeWV%2FbtsFeTUWyLL%2FSrFOP2QcZoX4nJXKkbKao0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Object graph 직렬화&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object graph는 직접 참조 혹은 참조 chain을 통해 간접적으로 서로 연결된 다수의 개체입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬화는 지정한 형식을 사용해 실제 object graph를 일련의 byte로 변환하는 처리를 말합니다.(역직렬화는 이런 처리를 반대로 수행합니다.) 직렬화를 통해 우리는 실제 개체의 현재 상태를 저장할 수 있으며 이후에 그 상태 그대로 개체를 다시 재생할 수 있습니다. 예를 들어 대표적으로 game을 들 수 있는데 현재 game의 진행상태를 저장함으로서 우리는 이후에 마지막상태에서 game을 계속 진행할 수 있습니다. 직렬화된 개체는 database나 file로 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정할 수 있는 format(형식)에는 수십 가지가 있지만 대부분의 경우 XML(eXtensible Markup Language)나 JSON(JavaScript Object Notation)이 둘 중 하나를 사용합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JSON은 data를 상당히 소형화할 수 있는 것으로 web이나 mobile application에서 최적화되어 있습니다. XML은 JSON보다 훨씬 장황하며 주로 legacy system에서 지원되는 경우가 많습니다. 따라서 직렬화된 개체 graph의 크기를 최소화하려면 JSON을 사용하는 것이 좋습니다. JSON은 태생적으로 JavaScript를 위한 직렬화형식이므로 개체 graph를 web이나 mobile application으로 전송하기에도 적합한 형식이라 할 수 있습니다. mobile app은 대게 제한된 대역폭환경에서 동작하는 경우가 많으므로 byte의 수는 중요한 요소로 작용합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 XML과 JSON으로 직렬화하고 그 반대를 수행할 수 있는 다수의 class를 가지고 있는데, 그중 XmlSerializer와 JsonSerializer가 가장 대표적인 class에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) XML 직렬화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 가장 많이 사용되는 직렬화 형식 중 하나가 바로 XML입니다. 일반적인 예제를 만들어 보기 위해 csStudy09 solution에서 WorkingWithSerialization이름의 project를 생성하고 자체 혹은 파생된 class에서만 접근할 수 있는 protected속성인 Group으로 Student.cs이름의 class file를 아래와 같이 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689301487187&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Student
{
    public Student(int group)
    {
        Group = group;
    }

    public string? Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public HashSet&amp;lt;Student&amp;gt;? Friends { get; set; }
    protected int Group { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 초기 salary값을 설정하기 위해 단일 매개변수를 가진 생성자를 사용하고 있습니다. Program.cs에서는 기존의 문을 모두 삭제하고 XML 직렬화에 필요한 정적 Environment와 Path namespace를 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689301614963&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using static System.Environment;
using static System.IO.Path;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Student instance의 개체 graph를 생성하는 문을 아래와 같이 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689301910995&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Student&amp;gt; students = new()
{
    new(1)
    {
        Name = &quot;홍길동&quot;,
        DateOfBirth = new(year: 2014, month: 9, day: 14)
    },
    new(2)
    {
        Name = &quot;홍길영&quot;,
        DateOfBirth = new(year: 2014, month: 6, day: 29)
    },
    new(3)
    {
        Name = &quot;장진구&quot;,
        DateOfBirth = new(year: 2014, month: 3, day: 7),
        Friends = new()
        {
            new(4)
            {
                Name = &quot;김말순&quot;,
                DateOfBirth = new(year: 2014, month: 7, day: 7)
            }
        }
    }
};

// Person의 List를 XML로 형식화할 개체 생성
XmlSerializer xs = new(type: students.GetType());
            
// 작성할 file생성
string path = Combine(CurrentDirectory, &quot;students.xml&quot;);
using (FileStream stream = File.Create(path))
{
    // 개체 graph를 stream으로 직렬화
    xs.Serialize(stream, students);
}

Console.WriteLine(&quot;Written {0:N0} bytes of XML to {1}&quot;, arg0: new FileInfo(path).Length, arg1: path);
Console.WriteLine();
            
Console.WriteLine(File.ReadAllText(path));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아마도 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tw8o3/btsFeHfUCvN/53jn2Slljuu1BbKkPAUkKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tw8o3/btsFeHfUCvN/53jn2Slljuu1BbKkPAUkKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tw8o3/btsFeHfUCvN/53jn2Slljuu1BbKkPAUkKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftw8o3%2FbtsFeHfUCvN%2F53jn2Slljuu1BbKkPAUkKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;83&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 오류를 해결하기 위해서는 Student.cs에서 매개변수 없는 생성자를 아래와 같이 추가해줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689302608706&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Student() { }&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사실 위 생성자는 어디에서도 사용되지 않지만 반드시 존재해야 XmlSerializer가 새로운 Student instance를 역직렬화 처리 중에 호출할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 결과를 확인해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehUOS2/btsFdVTcfTM/V8MH873j11s5Bk4b8nXsNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehUOS2/btsFdVTcfTM/V8MH873j11s5Bk4b8nXsNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehUOS2/btsFdVTcfTM/V8MH873j11s5Bk4b8nXsNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehUOS2%2FbtsFdVTcfTM%2FV8MH873j11s5Bk4b8nXsNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;421&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과에서는 '&amp;lt;Name&amp;gt;홍길동&amp;lt;/Name&amp;gt;'과 같이 XML요소로 개체 graph가 직렬화되었으며 Group속성은 public속성이 아니기에 포함되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) XML 경량화하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 일부 field는 요소대신 attribute를 사용해 더욱 경량화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Student.cs에서 System.Xml.Serialization namespace를 import한뒤&lt;/p&gt;
&lt;pre id=&quot;code_1689303015443&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Xml.Serialization;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Name, DateOfBirth속성에 해당 [XmlAttribute] attribute를 적용합니다. 이때 각 attribute에 아래와 같이 줄임말을 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689303192307&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[XmlAttribute(&quot;nm&quot;)]
public string? Name { get; set; }

[XmlAttribute(&quot;dob&quot;)]
public DateTime DateOfBirth { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 적용 후 다시 예제를 실행하면 설정한 줄임말이 사용됨으로써 file공간이 절약됨을 알 수 있습니다. 따라서 생성된 file의 용량이 이전보다 더 줄어들게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lgKYv/btsFf6sFXBE/YPtm1j1qLejwkJr8DS2QaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lgKYv/btsFf6sFXBE/YPtm1j1qLejwkJr8DS2QaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lgKYv/btsFf6sFXBE/YPtm1j1qLejwkJr8DS2QaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlgKYv%2FbtsFf6sFXBE%2FYPtm1j1qLejwkJr8DS2QaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;240&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) XML file 역직렬화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Memory에 실제 개체로 XML file을 역직렬화하기 위해 Program.cs에서 XML file을 열고 역직렬화하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689321412621&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine();
Console.WriteLine(&quot;* Deserializing XML files&quot;);

string path = Combine(CurrentDirectory, &quot;students.xml&quot;);

List&amp;lt;Student&amp;gt; students = new();
XmlSerializer xs = new(type: students.GetType());

using (FileStream xmlLoad = File.Open(path, FileMode.Open))
{
    List&amp;lt;Student&amp;gt;? loadedStudent = xs.Deserialize(xmlLoad) as List&amp;lt;Student&amp;gt;;
    if (loadedStudent is not null)
    {
        foreach (Student p in loadedStudent)
        {
            Console.WriteLine(&quot;{0} has {1} children.&quot;, p.Name, p.Friends?.Count ?? 0);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 XML로부터 student개체를 load 하여 아래와 같이 열거할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSZvVv/btsFdqlAW2N/A57bxYa6lQqo0uc2H9uq4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSZvVv/btsFdqlAW2N/A57bxYa6lQqo0uc2H9uq4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSZvVv/btsFdqlAW2N/A57bxYa6lQqo0uc2H9uq4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSZvVv%2FbtsFdqlAW2N%2FA57bxYa6lQqo0uc2H9uq4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;115&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 XML을 생성하는 데 사용할 수 있는 다른 많은 attribute들도 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 어떠한 annotation도 사용하지 않는다면 XmlSerializer는 역직렬화에서 속성의 이름에 대소문자를 구분하지 않고 수행합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;XmlSerializer를 사용할 때 오로지 public field와 속성만을 포함하며 type은 반드시 매개변수 없는 생성자를 가져야 합니다. 또한 attribute를 사용해 출력을 사용자정의할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) JSON 직렬화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Newtonsoft.Json는 Json.NET으로 알려져 있으며 JSON 직렬화에 사용할 수 있는 가장 인기 있고 강력한 .NET library 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NuGet package manager에서 download를 counter 하는 데 사용되는 단위는 32bit integer인데 Newtonsoft.Json의 인기가 너무 높은 나머지 여기서 수용가능한 숫자의 범위를 넘어설 정도의 download횟수가 기록된 적도 있을 정도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json.NET을 사용하기 위해 우선 Nuget Package에서 아래와 같이 Newtonsoft.Json을 검색해 최신 version을 내려받습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGVjID/btsFg5z03Ob/50POnOU31OHVXIsRzr1wC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGVjID/btsFg5z03Ob/50POnOU31OHVXIsRzr1wC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGVjID/btsFg5z03Ob/50POnOU31OHVXIsRzr1wC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGVjID%2FbtsFg5z03Ob%2F50POnOU31OHVXIsRzr1wC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1518&quot; height=&quot;998&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 text file을 생성하고 file에 JSON으로 student를 직렬화하는 구문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689322402094&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Student&amp;gt; students = new()
{
    new(1)
    {
        Name = &quot;홍길동&quot;,
        DateOfBirth = new(year: 2014, month: 9, day: 14)
    },
    new(2)
    {
        Name = &quot;홍길영&quot;,
        DateOfBirth = new(year: 2014, month: 6, day: 29)
    },
    new(3)
    {
        Name = &quot;장진구&quot;,
        DateOfBirth = new(year: 2014, month: 3, day: 7),
        Friends = new()
        {
            new(4)
            {
                Name = &quot;김말순&quot;,
                DateOfBirth = new(year: 2014, month: 7, day: 7)
            }
        }
    }
};

string jsonPath = Combine(CurrentDirectory, &quot;student.json&quot;);
using (StreamWriter jsonStream = File.CreateText(jsonPath))
{
    Newtonsoft.Json.JsonSerializer jss = new();
    jss.Serialize(jsonStream, students);
}

Console.WriteLine();
Console.WriteLine(&quot;Written {0:N0} bytes of JSON to: {1}&quot;, arg0: new FileInfo(jsonPath).Length, arg1: jsonPath);

Console.WriteLine(File.ReadAllText(jsonPath));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행해 보면 JSON은 요소로 이루어진 XML에 비해 byte의 수가 절반도 안 되는 훨씬 적은 용량만이 필요함을 알 수 있습니다. 심지어 attribute를 사용한 XML에 비해서도 더 작은 용량입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkQA2H/btsFeQDQMAP/uTIEMsOKVB5xJkxgSfjFIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkQA2H/btsFeQDQMAP/uTIEMsOKVB5xJkxgSfjFIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkQA2H/btsFeQDQMAP/uTIEMsOKVB5xJkxgSfjFIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkQA2H%2FbtsFeQDQMAP%2FuTIEMsOKVB5xJkxgSfjFIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;123&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 고성능 JSON 처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Core 3.0은 JSON처리에 필요한 새로운 namespace인 System.Text.Json를 추가하였으며 Span와 같은 API를 사용하여 성능에 최적화하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json.NET과 같은 이전 library는 UTF-16을 읽음으로써 구현되는데 HTTP를 포함한 대부분의 network protocol이 UTF-8을 사용하고 Json.NET의 Unicode 문자열 값으로 부터 UTF-8로 변환하는 것을 피할 수 있기 때문에 UTF-8을 사용하여 JSON문서를 읽고 쓰는 것이 더 성능이 좋다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Micorsoft는 이 새로운 API를 통해 상황에 따라 1.3 ~ 5배까지 성능향상을 달성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래 Json.NET의 제작자인 James Newton-King은 그가 JSON API에 관한 comment에서 'Json.NET은 사라지지 않는다.'라고 언급한 것처럼 Microsoft와 협업하여 새로운 JSON type을 개발하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON type을 역직렬화하는데 새로운 JSON API를 사용하기 위해 WorkingWithSerialization project의 Program.cs상단에서 직렬화를 수행하기 위한 새로운 JSON class를 별칭을 사용해 Import 합니다. 별칭은 위에서 사용한 Json.NET의 이름과 충돌하는 것을 피하기 위한 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689406712245&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using FastJson = System.Text.Json.JsonSerializer;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 JSON file을 열어 역직렬화를 수행한 뒤 Student에 대한 frends의 이름과 수를 출력하도록 하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689406917468&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string jsonPath = Combine(CurrentDirectory, &quot;student.json&quot;);

using (FileStream jsonLoad = File.Open(jsonPath, FileMode.Open))
{
	// deserialize object graph into a List of Person
	List&amp;lt;Student&amp;gt;? loadedStudent = await FastJson.DeserializeAsync(utf8Json: jsonLoad, returnType: typeof(List&amp;lt;Student&amp;gt;)) as List&amp;lt;Student&amp;gt;;

	if (loadedStudent is not null)
	{
		foreach (Student s in loadedStudent)
		{
			Console.WriteLine(&quot;{0} has {1} children.&quot;, s.Name, s.Friends?.Count ?? 0);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KSunF/btsFg3IWSyt/sFeTuqlEtHh9zmg21MkGOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KSunF/btsFg3IWSyt/sFeTuqlEtHh9zmg21MkGOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KSunF/btsFg3IWSyt/sFeTuqlEtHh9zmg21MkGOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKSunF%2FbtsFg3IWSyt%2FsFeTuqlEtHh9zmg21MkGOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;134&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개발 생산성을 위해서는 Json.NET을, 다양한 기능 또는 성능을 위해서는 System.Text.Json을 사용합니다. 아래 link를 통해 이 둘에 대한 차이목록을 확인해 볼 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1689407342019&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Migrate from Newtonsoft.Json to System.Text.Json - .NET&quot; data-og-description=&quot;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jtKgX/hyTk65BcZr/7Xkege2gF5vmKACtUMmdH1/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0#table-of-differences-between-newtonsoftjson-and-systemtextjson&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jtKgX/hyTk65BcZr/7Xkege2gF5vmKACtUMmdH1/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) JSON 처리 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON처리 방식에 관해서는 아래와 같이 사용가능한 많은 option들이 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Field의 포함 또는 제외&lt;/li&gt;
&lt;li&gt;Casing 정책 설정&lt;/li&gt;
&lt;li&gt;대소문자 구분 정책 설정&lt;/li&gt;
&lt;li&gt;공백의 처리방식 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 csStudy09 solution에서 WorkingWithJson이름의 project를 생성한 뒤 Program.cs에서 기존의 문을 모두 삭제하고 고성능 JSON을 위한 주요 namespace와 System. Environment, System.IO.Path namespace를 정적 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689472977408&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text.Json; 

using static System.Environment;
using static System.IO.Path;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Book.cs이라는 새로운 class file을 추가하고 그 안에 Book class를 아래와 같이 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689473150679&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text.Json.Serialization;

namespace WorkingWithJson
{
	public class Book
	{
		public Book(string title)
		{
			Title = title;
		}

		public string Title { get; set; }

		public string? Author { get; set; }
		
		[JsonInclude]
		public DateTime PublishDate;

		[JsonInclude]
		public DateTimeOffset Created;

		public ushort Pages;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Program.cs로 돌아와 Book class의 instance를 생성하고 이를 JSON으로 직렬화하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689473595282&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Book mybook = new(title: &quot;어린왕자&quot;)
{
	Author = &quot;생텍쥐페리&quot;,
	PublishDate = new(year: 1943, month: 4, day: 6),
	Pages = 250,
	Created = DateTimeOffset.UtcNow,
};

JsonSerializerOptions options = new()
{
	IncludeFields = true, // 모든 field를 포함
	PropertyNameCaseInsensitive = true,
	WriteIndented = true,
	PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
	Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};

string filePath = Combine(CurrentDirectory, &quot;mybook.json&quot;);

using (Stream fileStream = File.Create(filePath))
{
	JsonSerializer.Serialize&amp;lt;Book&amp;gt;(utf8Json: fileStream, value: mybook, options);
}

Console.WriteLine(&quot;{0:N0} byte의 JSON file {1}&quot;, arg0: new FileInfo(filePath).Length, arg1: Path.GetFileName(filePath));
Console.WriteLine();
			
Console.WriteLine(File.ReadAllText(filePath));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mhdya/btsFfWXXlRO/uIw3B3k9qnZH86bSK9y5n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mhdya/btsFfWXXlRO/uIw3B3k9qnZH86bSK9y5n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mhdya/btsFfWXXlRO/uIw3B3k9qnZH86bSK9y5n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMhdya%2FbtsFfWXXlRO%2FuIw3B3k9qnZH86bSK9y5n1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;248&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 JSON file은 총 171byte로 member의 이름은 publishDate와 같이 camelCase가 적용되었습니다. 이는 Javascript를 사용하는 browser를 위한 가장 적합한 방식입니다. 또한 option설정에서 pages를 포함하여 모든 field를 포함하도록 했고 결과를 보면 사람이 읽기 쉽도록 하기 위해 JSON자체에 들여 쓰기 등의 서식이 적용되어 있음을 확인할 수 있습니다. DateTime와 DateTimeOffset값은 단일 표준 문자열 값으로 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 JsonSerializerOptions을 설정할 때 PropertNameCaseInsensitive, WriteIndented와 IncludeFields부분을 주서처리하고 예제를 다시 실행해 보면 결과를 다음과 같이 바뀌게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duxXzS/btsFg3oFvmq/Bu7WU9vtXaSfxcCmKOKM11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duxXzS/btsFg3oFvmq/Bu7WU9vtXaSfxcCmKOKM11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duxXzS/btsFg3oFvmq/Bu7WU9vtXaSfxcCmKOKM11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduxXzS%2FbtsFg3oFvmq%2FBu7WU9vtXaSfxcCmKOKM11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;134&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 JSON은 133byte가 되어 이전 171byte보다 용량이 작아졌으며 member의 이름은 normal casing가 사용되어 PublishDate와 같이 되었습니다. Pages field는 포함되지 않았으며 [JsonInclude]와 같은 attribute가 포함된 field는 정상적으로 포함되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) HTTP응답을 위한 새로운 JSON 확장 method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5에서 Microsoft는 HttpResponse의 확장 method와 같이 System.Text.Json namespace type을 개선하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Newtonsoft에서 신규 JSON으로의 변환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Newtonsoft Json.NET library를 사용하는 code를 신규 System.Text.Json namespace로 변환해야 한다면 아래 link에서 Microsoft가 이를 위해 마련해 둔 문서를 찾아볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709089681352&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gwG1C/hyTjPY2Do7/32F9iUPgcPvUKFWML2U0fK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-description=&quot;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&quot; data-og-title=&quot;Migrate from Newtonsoft.Json to System.Text.Json - .NET&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gwG1C/hyTjPY2Do7/32F9iUPgcPvUKFWML2U0fK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Migrate from Newtonsoft.Json to System.Text.Json - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. 환경변수&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수는 system및 사용자 정의가능한 값이며 이는 process의 동작을 실행하는 방식에 영향을 줄 수 있습니다. 일반적으로는 ASP.NET Core project에서 개발과 production 설정사이를 전환하기 위한 설정에 사용하거나 database연결 문자열에서 service key와 password같이 process에서 필요한 값을 전달하는데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서 환경변수는 machine(system), user, process라는 3가지 범위에서 설정할 수 있습니다. 환경변수를 가져오거나 설정하는 method는 기본적으로 process의 범위 수준을 가정하며 process, user, machine의 EnvironmentVariableTarget을 지정하는 override를 가집니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 20px;&quot;&gt;GetEnvironmentVariables&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 20px;&quot;&gt;지정된 수준의 범위 혹은 현재 process에 대한 모든 환경변수의 IDictionary를 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 20px;&quot;&gt;GetEnvironmentVariable&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 20px;&quot;&gt;명몇된 환경변수의 값을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 20px;&quot;&gt;SetEnvironmentVariable&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 20px;&quot;&gt;명명된 환경변수의 값을 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 19.031%; height: 40px;&quot;&gt;ExpandEnvironmentVariables&lt;/td&gt;
&lt;td style=&quot;width: 47.6356%; height: 40px;&quot;&gt;문자열로 구성된 모든 환경변수, 예를 들어 &quot;Computer name :&amp;nbsp; %COMPUTER_NAME%&quot;에서와 같은 값에서 %%를 해당 식별되는 값으로 변환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 모든 환경 변수 가져오기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 다양한 범위 수준에서 현재 환경변수를 어떻게 확인할 수 있는지 확인해 보겠습니다. Program.cs를 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709095273213&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections;
using static System.Environment;

IDictionary evs = GetEnvironmentVariables();
IDictionary evm = GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
IDictionary evu = GetEnvironmentVariables(EnvironmentVariableTarget.User);

foreach (var key in evs.Keys)
{
    Console.WriteLine(&quot;key : {0}, value : {1}&quot;, key, evs[key]!.ToString());
}

Console.WriteLine(&quot;-------------------------------------------------------&quot;);

foreach (var key in evm.Keys)
{
    Console.WriteLine(&quot;key : {0}, value : {1}&quot;, key, evs[key]!.ToString());
}

Console.WriteLine(&quot;-------------------------------------------------------&quot;);

foreach (var key in evu.Keys)
{
    Console.WriteLine(&quot;key : {0}, value : {1}&quot;, key, evs[key]!.ToString());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 각각의 범위에 속한 모든 환경변수를 표시하도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kERiF/btsFm9bNhqM/TBSozh99FkA9Cl2R9EcDSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kERiF/btsFm9bNhqM/TBSozh99FkA9Cl2R9EcDSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kERiF/btsFm9bNhqM/TBSozh99FkA9Cl2R9EcDSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkERiF%2FbtsFm9bNhqM%2FTBSozh99FkA9Cl2R9EcDSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;856&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) 환경변수를 확장하고 설정하고 가져오기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 경우는 내장된 환경변수를 포함하고 있는 문자열을 형식화하여 표시해야 하는 경우가 있을 수 있습니다. 이러한 경우 환경변수는 %문자로 변수를 감싸는 형태로 표시됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;User : %USERNAME%, Computer : %COMPUTER_NAME%&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 환경변수는 Windows상에서 set 혹은 setx 명령을 통해 설정할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;변수 범위&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;예제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;Session/Shell&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;set MY_TMP_VAR=&quot;me&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;User&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;setx MY_TMP_VAR &quot;me&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.8837%;&quot;&gt;Machine&lt;/td&gt;
&lt;td style=&quot;width: 75.1163%;&quot;&gt;setx MY_TMP_VAR &quot;me&quot; /M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sDc2j/btsFp84il0r/sBAseaaxU07l8DgKjehTDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sDc2j/btsFp84il0r/sBAseaaxU07l8DgKjehTDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sDc2j/btsFp84il0r/sBAseaaxU07l8DgKjehTDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsDc2j%2FbtsFp84il0r%2FsBAseaaxU07l8DgKjehTDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;191&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set 명령은 임시 환경변수를 정의하는 것으로 즉시 값을 다시 읽거나 현재 shell 또는 session안에서 읽을 수 있습니다. 다만 해당 범위의 값을 설정할때는 위 표에서 처럼 =문자를 사용해 값을 할당함에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setx 명령은 영구적인 환경변수를 설정하지만 현재 shell 또는 session을 닫고 재시작해야만 해당 환경변수를 읽을 수 있습니다. 또한 이 명령에서는 값의 할당을 위해 =문자를 사용하지 않음에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setx명령에 관한 좀더 자세한 사항은 아래 link를 참고해 주시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot;&gt;setx | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709098054543&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;setx&quot; data-og-description=&quot;Reference article for the setx command, which creates or modifies environment variables in the user or system environment, without requiring programming or scripting.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9mg8A/hyVunKMZMm/mMkATHhvm8ANkVC3nPnjNK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9mg8A/hyVunKMZMm/mMkATHhvm8ANkVC3nPnjNK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;setx&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Reference article for the setx command, which creates or modifies environment variables in the user or system environment, without requiring programming or scripting.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 환경변수는 Windows상에서 Settings -&amp;gt; System -&amp;gt; About -&amp;gt; Advanced system settings -&amp;gt; System Properties 순서로 이동하면 해당 화면을 통해 변수항목과 해당 값을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beAGHv/btsFlSBHxcB/PXk5e29DgeIekYa8SJc39K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beAGHv/btsFlSBHxcB/PXk5e29DgeIekYa8SJc39K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beAGHv/btsFlSBHxcB/PXk5e29DgeIekYa8SJc39K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeAGHv%2FbtsFlSBHxcB%2FPXk5e29DgeIekYa8SJc39K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1700&quot; height=&quot;1261&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임시적으로 환경변수를 설정하는 것은 아래와 같이 export 명령으로도 가능합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;export MY_TMP_VAR=me&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export 명령에 대한 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ss64.com/bash/export.html&quot;&gt;export Man Page - Linux - SS64.com&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709098452905&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;export Man Page - Linux - SS64.com&quot; data-og-description=&quot;Set an environment variable. The supplied names are marked for automatic export to the environment of subsequently executed commands. If no names are supplied, or if the '-p' option is given, a list of exported names is displayed. To view the environment v&quot; data-og-host=&quot;ss64.com&quot; data-og-source-url=&quot;https://ss64.com/bash/export.html&quot; data-og-url=&quot;https://ss64.com/bash/export.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ss64.com/bash/export.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ss64.com/bash/export.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;export Man Page - Linux - SS64.com&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Set an environment variable. The supplied names are marked for automatic export to the environment of subsequently executed commands. If no names are supplied, or if the '-p' option is given, a list of exported names is displayed. To view the environment v&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ss64.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 알아본 방법외에 환경변수를 설정하고 값을 확인하고 확장하는 또 다른 방법이 존재하며 이를 예제를 통해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 환경변수를 포함하고 있는 문자열을 정의한 뒤 이에 대한 값을 출력하도록 아래와 같이 code를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709099236105&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string comUser = &quot;User name : %USERNAME%, Computer name : %COMPUTERNAME%&quot;;

Console.WriteLine(ExpandEnvironmentVariables(comUser));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnAys6/btsFmkSfL6z/MJd5KmS4wtKMutSxzKXqxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnAys6/btsFmkSfL6z/MJd5KmS4wtKMutSxzKXqxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnAys6/btsFmkSfL6z/MJd5KmS4wtKMutSxzKXqxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnAys6%2FbtsFmkSfL6z%2FMJd5KmS4wtKMutSxzKXqxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 MY_TMP_VAR 이름의 환경변수를 process범위로 설정하고 해당 값을 출력하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709099496257&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string tmpKey = &quot;MY_TMP_VAR&quot;;
SetEnvironmentVariable(tmpKey, &quot;cliel&quot;);
string? tmpResult = GetEnvironmentVariable(tmpKey);

Console.WriteLine($&quot;{tmpKey}: {tmpResult}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Rsji/btsFob8kgtC/dklJnXYVbUnG4uKjz9HVfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Rsji/btsFob8kgtC/dklJnXYVbUnG4uKjz9HVfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Rsji/btsFob8kgtC/dklJnXYVbUnG4uKjz9HVfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Rsji%2FbtsFob8kgtC%2FdklJnXYVbUnG4uKjz9HVfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 MY_TMP_VAR 환경변수를 3개의 모든 범위수준에서 읽어오도록 program.cs를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709100116168&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string tmpKey = &quot;MY_TMP_VAR&quot;;

string tmpValue = GetEnvironmentVariable(tmpKey, EnvironmentVariableTarget.Machine);
Console.WriteLine($&quot;Machine - {tmpKey}: {tmpValue}&quot;);

tmpValue = GetEnvironmentVariable(tmpKey, EnvironmentVariableTarget.User);
Console.WriteLine($&quot;User - {tmpKey}: {tmpValue}&quot;);

tmpValue = GetEnvironmentVariable(tmpKey, EnvironmentVariableTarget.Process);
Console.WriteLine($&quot;Process - {tmpKey}: {tmpValue}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런다음 project folder에서 다음과 같이 launchSettings.json file을 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709100745538&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;profiles&quot;: {
        &quot;test&quot;: {
            &quot;commandName&quot;: &quot;Project&quot;,
            &quot;environmentVariables&quot;: 
            {
                &quot;MY_TMP_VAR&quot;: &quot;me&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 test는 project 이름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;terminal이나 command창에서는 다음 명령을 통해 user와 machine 두 범위에서 같은 이름의 환경변수를 설정합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;setx MY_TMP_VAR &quot;me1&quot;&lt;br /&gt;setx MY_TMP_VAR &quot;tmp2&quot; /M&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp5iaW/btsFoEoU1sS/ieIwCL44OUTECEMojDLC1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp5iaW/btsFoEoU1sS/ieIwCL44OUTECEMojDLC1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp5iaW/btsFoEoU1sS/ieIwCL44OUTECEMojDLC1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp5iaW%2FbtsFoEoU1sS%2FieIwCL44OUTECEMojDLC1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;267&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;권한관련 오류가 발생되는 경우 terminal을 관리자 권한으로 실행시켜야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행시켜 다음과 같은 결과를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBwySO/btsFm24S78n/5NvbtiUiJ9TI70lAWyw8Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBwySO/btsFm24S78n/5NvbtiUiJ9TI70lAWyw8Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBwySO/btsFm24S78n/5NvbtiUiJ9TI70lAWyw8Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBwySO%2FbtsFm24S78n%2F5NvbtiUiJ9TI70lAWyw8Y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;210&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 통해 알 수 있듯이 launchSettings. json file에서의 설정은 process 수준으로 설정됩니다.&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>c#</category>
      <category>decoding</category>
      <category>encoding</category>
      <category>filesystem</category>
      <category>Serialization</category>
      <category>Stream</category>
      <category>환경변수</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/599</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-9-File-Streams-Serialization#entry599comment</comments>
      <pubDate>Wed, 28 Feb 2024 15:23:06 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 8. 공용 .NET Type</title>
      <link>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-8-%EA%B3%B5%EC%9A%A9-NET-Type</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;.NET에는 일반적인 개발과정에서 사용할 수 있는 숫자, 문자열, collection등과 span, index, range와의 작업, network access 등 몇 가지 공용 type들을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 숫자 다루기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data에 관한 가장 일반적인 작업중 하나가 바로 숫자입니다. 아래표는 .NET에서 숫자에 관한 가장 일반적인 type을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Example Type&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;SByte, Int16, Int32, Int64&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;정수로서 음수, 양수, 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Byte, UInt16, UInt32, UInt64&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;기수로서 0, 양수 / 부호가 없으므로 U로 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Half, Single, Double&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;실수로서, 부동소수점 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Decimal&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;실수(accurate real)로서 과학, 공학, 금융분야에서 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Numbers&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;BigInteger, Complex, Quaternion&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;임의의 큰 정수, 복소수, 4차원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 .NET Framework 1.0부터 32bit float과 64bit double type을 지원해 왔습니다. IEEE 754 명세는 또한 16bit floating-point 표준을 정의하였는데 Machine learning의 일부 algorithm에서 해당 type을 필요로 하므로 Microsoft는 System.Half type을 .NET 5부터 포함하였으며 .NET 7부터는 System.Int128와 System.UInt128 type을 포함하였습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 C#언어는 half와 Int128, UInt128에 대한 별칭을 따로 정의하지 않았으므로 System.Half와 System.Int128, Sysem.UInt128을 그대로 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Big integer 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 별칭이 있는 .NET type에서 저장가능한 가장 큰 정수는 &lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;18조 5천억으로 unsigned ulong integer입니다. 만약 이것보다 더 큰 수를 저장해야 한다면 BigInteger를 사용해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 csStudy08 solution을 만들고 여기에 WorkingWithNumbers이름의 Console App project를 추가합니다. 그런 뒤 Program.cs에서 기존구문을 모두 삭제하고 ulong type의 최댓값과 BigInteger를 통해 30 자릿수의 표시하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687936759756&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Numerics;

ulong big = ulong.MaxValue;
Console.WriteLine($&quot;{big,40:N0}&quot;);

BigInteger bigger = BigInteger.Parse(&quot;123456789012345678901234567890&quot;);
Console.WriteLine($&quot;{bigger,40:N0}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;예제에서 사용한 format code 40은 오른쪽으로 40자리수 만큼 정렬하고자 하는 것이므로 두 숫자 모두 오른쪽 가장자리로 줄지어 표시하게 되며 N0은 천 단위 분리자와 소수점 0자리 사용을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cR8qv6/btsE0h3bmwe/tkC5P5lkRrGNdp78i1Hmak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cR8qv6/btsE0h3bmwe/tkC5P5lkRrGNdp78i1Hmak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cR8qv6/btsE0h3bmwe/tkC5P5lkRrGNdp78i1Hmak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcR8qv6%2FbtsE0h3bmwe%2FtkC5P5lkRrGNdp78i1Hmak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;84&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 복소수 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복소수는 a + bi로 표현될 수 있으며 이때 a와 b는 실수, i는 허수 단위이고 여기서 i&amp;sup2; = -1입니다. 실수부 a가 0이면 순허수, 허수부 b가 0이면 이것은 실수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복소수는 많은 STEM(science, technology, engineering and mathematics)연구분야에서 실질적으로 응용되고 있으며 가수의 실수와 허수 부분이 별도로 더해짐으로써 추가되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 17px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px;&quot;&gt;(a + bi) + (c + di) = (a + c) + (b + d)i&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithNumbers project의 Program.cs에서 아래와 같이 2개의 복소수를 더하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688007502517&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Complex c1 = new(real: 4, imaginary: 2);
Complex c2 = new(real: 3, imaginary: 7);
Complex c3 = c1 + c2;

Console.WriteLine($&quot;{c1} added to {c2} is {c3}&quot;);
Console.WriteLine(&quot;{0} + {1}i added to {2} + {3}i is {4} + {5}i&quot;, c1.Real, c1.Imaginary, c2.Real, c2.Imaginary, c3.Real, c3.Imaginary);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beCOqs/btsE5Cr0e2g/3E1EMD1GTI4EyGZxmvq80K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beCOqs/btsE5Cr0e2g/3E1EMD1GTI4EyGZxmvq80K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beCOqs/btsE5Cr0e2g/3E1EMD1GTI4EyGZxmvq80K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeCOqs%2FbtsE5Cr0e2g%2F3E1EMD1GTI4EyGZxmvq80K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;92&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;..NET 6이전에는 (4, 2)와 (3, 7)을 더하면 (7, 9)가 되는 다른 형식을 사용했지만 .NET 7이후부터는 꺽쇠 괄호와 semi-colon을 사용하는 다른 기본형식을 사용합니다. 이는 일부 문화권에서 음수를 나타내기 위해 괄호를 사용하고 십진수를 나타내기 위해 commna를 사용하기 때문입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Quaternion(4원수)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quaternions은 복소수를 확장자는 숫자체계로서 실수를 넘어 4차원 연관 규범 분할 대수를 형성하며 따라서 domain도 형성합니다. 사실 실제 활용해야 할 상황이 아니라면 이런 내용들에 대한 자세한 이해할 필요는 없는데, 이들은 공간을 묘사하는 데 사용되며 따라서 다수의 computer simulation과 비행 제어 system, video game engine에서 많이 사용되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 난수 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난수가 필요하지 않다면 Random class의 instance는 아래와 같이 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688021727236&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Random class는 난수 생성의 초기화에 사용되는 seed 값을 지정하는 매개변수를 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688021952372&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = new(Seed: 46378);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글에서 언급하였지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 2. C#&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708393225387&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 2. C#&quot; data-og-description=&quot;이번 글에서는 C# programming 언어에 대한 기본적인 사항을 살펴볼 것입니다. 전반적으로 알아야 할 용어와 C#에 대한 기본적인 문법에 대한 것들입니다. 1. C# 언어 C#을 통해 application에 대한 source co&quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vwcsG/hyVm1PBbMw/Zd6nvHrbh3y2I5hhGTlscK/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/gjfml/hyVmVhxpgI/QOY0V1KGz1s3rC3VaUocJ1/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/blZPRG/hyVmRMY4PF/Mbl1GmNbMlN0XZ8ZholPKk/img.png?width=1066&amp;amp;height=664&amp;amp;face=0_0_1066_664&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vwcsG/hyVm1PBbMw/Zd6nvHrbh3y2I5hhGTlscK/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/gjfml/hyVmVhxpgI/QOY0V1KGz1s3rC3VaUocJ1/img.png?width=681&amp;amp;height=109&amp;amp;face=0_0_681_109,https://scrap.kakaocdn.net/dn/blZPRG/hyVmRMY4PF/Mbl1GmNbMlN0XZ8ZholPKk/img.png?width=1066&amp;amp;height=664&amp;amp;face=0_0_1066_664');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 2. C#&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 C# programming 언어에 대한 기본적인 사항을 살펴볼 것입니다. 전반적으로 알아야 할 용어와 C#에 대한 기본적인 문법에 대한 것들입니다. 1. C# 언어 C#을 통해 application에 대한 source co&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수의 이름은 camel case로 사용하길 권장하고 있습니다. 하지만 예제에서 보듯 해당 class의 생성자를 정의한 개발자가 해당 규칙을 어기는 바람에 seed가 아닌 Seed로 사용되고 있습니다. 다시 말하지만 Seed가 아닌 seed가 되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Memory를 더 할당하지 않기 위해 .NET 6에서는 Random의 공유 정적 instance를 포함하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688023561036&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = Random.Shared;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Random 개체를 생성하게 되면 임의의 숫자를 생성하는 method를 아래와 같이 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688023700785&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int ranInt = r.Next(minValue: 1, maxValue: 7); // 1에서 6사이값
double randomReal = r.NextDouble(); // 0.0에서 1.0보다 작은값
byte[] arrayOfBytes = new byte[256];
r.NextBytes(arrayOfBytes); //256 random byte array&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Random class는 random숫자를 생성하기 위해 일반적으로 사용되는 다음의 method를 갖고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3023%;&quot;&gt;Next&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%;&quot;&gt;Next method는 minValue와 maxValue 2개의 매개변수를 필요로 합니다. 다만 이때 maxValue값은 method가 반환하는 최댓값을 의미하는 것이 아님에 주의해야 합니다. 이는 'exclusive upper bound(배타적 상한선)'라고 하는 것으로 반환하는 최댓값보다 1 더 많아야 함을 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3023%;&quot;&gt;NextDouble&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%;&quot;&gt;0.0보다 더 크거나 같으면서 1.0미만의 수를 반환합니다. float type의 값이 필요하다면 NextSingle method를 대신 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3023%;&quot;&gt;NextBytes&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%;&quot;&gt;random byte (0 to 255)값으로 array를 채우는 method로서 00~FF와 같이 16진수로서 byte값을 형식화 하기 위해 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;암호화와 같이 진정한 random값이 필요한 경우를 위해 RandomNumberGenerator와 같은 특별한 type을 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 8에서는 다음과 같은 2개의 새로운 method를 포함하였습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.1628%;&quot;&gt;GetItems&amp;lt;T, length&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.8372%;&quot;&gt;해당 method에는 Type T에 대한 배열 혹은 읽기 전용 span과 생성하고자 하는 item의 수가 전달되며 이것으로 부터 ramdom하게 선택된 item을 반환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.1628%;&quot;&gt;Shuffle&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.8372%;&quot;&gt;해당 method에는 Type T에 대한 배열 혹은 일기 전용 span이 전달되며 해당 item의 순서가 ramdom하게 변경됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1708395121202&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Random r = Random.Shared;

string[] employees = r.GetItems(
    choices: new[] { &quot;lee&quot;, &quot;choi&quot;, &quot;kim&quot;, &quot;hong&quot; },
    length: 3);

foreach (string employee in employees)
    Console.Write($&quot; {employee}&quot;);

Console.WriteLine();

r.Shuffle(employees);

foreach (string emploee in employees)
    Console.Write($&quot; {emploee}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIXRfn/btsE6VLm9bU/K2cfKznGGDjuM2kV2AUcZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIXRfn/btsE6VLm9bU/K2cfKznGGDjuM2kV2AUcZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIXRfn/btsE6VLm9bU/K2cfKznGGDjuM2kV2AUcZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIXRfn%2FbtsE6VLm9bU%2FK2cfKznGGDjuM2kV2AUcZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;172&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) GUID 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUID (globally unique identifier)는 128bit TEXT 문자열로서 중복되지 않는 고유한 값을 나타냅니다. 따라서 multi-tasking환경에서의 충돌을 방지하는 것과 같은 상황에서 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Guid type은 값 type(구조체)로서 NewGuid method로 새로운 GUID값을 나타내며 기존 값을 문자열로서 나타내기 위한 Parse와 ParseTry method를 갖추고 있고 그 반대로 문자열을 GUID로 변환하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 위해 아래와 같이 NewGuid method로 GUID값을 생성하고 그 값을 byte array로 나타내기 위한 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708398121657&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Guid g = Guid.NewGuid();
Console.WriteLine($&quot;Random GUID: {g}.&quot;);

byte[] guidAsBytes = g.ToByteArray();

Console.Write(&quot;GUID to byte array: &quot;);
for (int i = 0; i &amp;lt; guidAsBytes.Length; i++)
    Console.Write($&quot;{guidAsBytes[i]:X2} &quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 아래와 같이 표시할 것입니다. NewGuid method는 random한 값을 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LGHP1/btsEZnJHQAl/JekidKAgZAdj8qyB7ackb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LGHP1/btsEZnJHQAl/JekidKAgZAdj8qyB7ackb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LGHP1/btsEZnJHQAl/JekidKAgZAdj8qyB7ackb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLGHP1%2FbtsEZnJHQAl%2FJekidKAgZAdj8qyB7ackb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. Text&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text는 변수를 통해 사용되는 가장 일반적인 data type이라고 할 수 있습니다. 아래 표는 text와 관련된 .NET의 일반적인 type을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 91px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Type&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Char&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;단일 한문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;System.Text&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;StringBuilder&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;고성능 문자열 다루기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;System.Text.RegularExpressions&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;Regex&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;고성능 문자열 pattern-matching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 문자열 길이 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text를 다루게 되는 경우 사용되는 몇 가지 일반적인 상황들이 있습니다. 예를 들어 string변수에 저장된 문자열의 길이를 알아내는 경우 등이 그것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 solution에 WorkingWithText console app project를 추가하고 Program.cs에서 기본의 문을 모두 삭제한 뒤 도시 이름을 저장하기 위한 변수를 정의하고 console을 통해 도시이름과 길이를 출력하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688027234134&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string city = &quot;Seoul&quot;;
Console.WriteLine($&quot;{city} is {city.Length} characters long.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/een6bx/btsE8A7MlVJ/wTJ2QuJ2QAJ1uVdGq4wCu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/een6bx/btsE8A7MlVJ/wTJ2QuJ2QAJ1uVdGq4wCu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/een6bx/btsE8A7MlVJ/wTJ2QuJ2QAJ1uVdGq4wCu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feen6bx%2FbtsE8A7MlVJ%2FwTJ2QuJ2QAJ1uVdGq4wCu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;71&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 문자열에서 문자 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String class는 문자열을 저장하기 위해 내부적으로 문자에 대한 array를 사용하며 또한 한문자를 읽기 위해 array 구문을 사용할 수 있는 indexer를 가지고 있습니다. Array indexer는 0부터 시작하므로 세 번째 문자라면 index는 2가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 string변수의 첫 번째 문자와 네 번째 문자를 표시하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688032138696&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;First char is {city[0]} and fourth is {city[3]}.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wHnJf/btsE6QXy7Ou/yreQ16TW295b6hx2nue4iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wHnJf/btsE6QXy7Ou/yreQ16TW295b6hx2nue4iK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wHnJf/btsE6QXy7Ou/yreQ16TW295b6hx2nue4iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwHnJf%2FbtsE6QXy7Ou%2FyreQ16TW295b6hx2nue4iK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;76&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 문자열 분할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 문자열 안에서 특정 문자(이를 테면 comma(,)와 같은)를 기준으로 array형태의 분리가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 comma로 분리된 도시이름을 포함한 단일 문자열 변수를 정의하고 이를 Split method를 통해 분리하는 문을 추가합니다. 이때 분리의 기준을 comma로 지정하며 반환된 문자열값의 array를 열거하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688032466415&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string cities = &quot;Seoul,Busan,Daegu,Daejeon,Jeju&quot;;
string[] citiesArray = cities.Split(',');

Console.WriteLine($&quot;There are {citiesArray.Length} items in the array:&quot;);

foreach (string item in citiesArray)
{
    Console.WriteLine(item);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b43ttZ/btsE8DpTTbJ/wDWF7pGj9gbB1FXd9rQDQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b43ttZ/btsE8DpTTbJ/wDWF7pGj9gbB1FXd9rQDQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b43ttZ/btsE8DpTTbJ/wDWF7pGj9gbB1FXd9rQDQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb43ttZ%2FbtsE8DpTTbJ%2FwDWF7pGj9gbB1FXd9rQDQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;146&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 문자열의 일부 가져오기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 일부를 가져오는 방법 중의 하나로 IndexOf method를 사용할 수 있습니다. 해당 method는&amp;nbsp; 문자열 안에서 지정한 문자 또는 문자열에 대한 index위치를 반환하는 9개의 overload를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Substring method는 2개의 다음과 같은 2개의 overload를 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Substring(startIndex, length) : startIndex로 시작하고 다음 length 문자만큼을 포함하는 문자열의 일부를 반환합니다.&lt;/li&gt;
&lt;li&gt;Substring(startIndex) : startIndex로 시작하고 그다음 끝까지 모든 문자열을 포함하는 문자열의 일부를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 사람의 이름을 저장하는 string변수를 추가합니다. 이때 성과 이름은 공백으로 구분합니다. 그리고 해당 공백의 위치를 찾아 성과 이름을 분리하며 다른 순서로도 재결합될 수 있도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688095497910&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string fullName = &quot;hong seojun&quot;;
int indexOfTheSpace = fullName.IndexOf(' ');

string firstName = fullName.Substring(startIndex: 0, length: indexOfTheSpace);
string lastName = fullName.Substring(startIndex: indexOfTheSpace + 1);

Console.WriteLine($&quot;Original: {fullName}&quot;);
Console.WriteLine($&quot;Swapped: {lastName}, {firstName}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIiKLb/btsE2DLtHBz/DqLsJZDpBptzIU6lhUH4Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIiKLb/btsE2DLtHBz/DqLsJZDpBptzIU6lhUH4Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIiKLb/btsE2DLtHBz/DqLsJZDpBptzIU6lhUH4Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIiKLb%2FbtsE2DLtHBz%2FDqLsJZDpBptzIU6lhUH4Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 내용에서 문자열 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에서는 특정 문자열로 시작하는지 혹은 끝나는지, 아니면 어떤 문자열을 포함하고 있는지를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 문자열 저장을 위한 string변수를 추가하고 특정 문자열로 시작하는지와 포함하는지를 확인하는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688107876663&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string company = &quot;Microsoft&quot;;
bool startsWithM = company.StartsWith(&quot;M&quot;);
bool containsN = company.Contains(&quot;N&quot;);

Console.WriteLine($&quot;Text: {company}&quot;);
Console.WriteLine($&quot;Starts with M: {startsWithM}, contains an N: {containsN}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c94Cip/btsE6EC6jzP/FVNkykO0q0DZLZSdf2XYdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c94Cip/btsE6EC6jzP/FVNkykO0q0DZLZSdf2XYdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c94Cip/btsE6EC6jzP/FVNkykO0q0DZLZSdf2XYdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc94Cip%2FbtsE6EC6jzP%2FFVNkykO0q0DZLZSdf2XYdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;85&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 문자열 비교&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 비교 역시 문자열을 통한 가장 일반적인 작업에 해당합니다. 예를 들어 login해야 하는 사용자가 자신의 ID와 비밀번호를 입력했다면 이것을 저장된 것과 비교해야할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string class는 IComparable interface를 구현하고 있으므로 CompareTo instance method를 통해 문자열을 비교할 수 있고 이때 문자열 값이 더 큰지, 같은지, 아니면 더 작은지에 따라 method는 -1, 0, 1의 값을 반환하게 됩니다. IComparable interface는 이미 아래 글에게 다루어본바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708411623363&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 6. Interface와 Class상속&quot; data-og-description=&quot;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고 &quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dANDYX/hyVm2uhJKN/n45EsLyezwLjuaq6kC1t1K/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bc1t20/hyVmUQAchu/aLROBouOJiQdlZWFlJKPv0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bgnSPn/hyVmQHpODZ/3Ye7TZoAQE6lJeTJcLJ1BK/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dANDYX/hyVm2uhJKN/n45EsLyezwLjuaq6kC1t1K/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bc1t20/hyVmUQAchu/aLROBouOJiQdlZWFlJKPv0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bgnSPn/hyVmQHpODZ/3Ye7TZoAQE6lJeTJcLJ1BK/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 6. Interface와 Class상속&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 영문 대소문자는 순서에 영향을 줄 수 있고 문자열의 순서규칙은 문화권에 영향을 받습니다. 예를 들어 이중 L은&amp;nbsp; Spain어에서 하나의 문자로 취급됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관성을 위해 때로는 문화권에 영향을 받지 않는 방식으로 비교를 수행해야할 필요가 있으며 이때 Compare라는 정적 method를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708412615783&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Globalization;

Console.OutputEncoding = System.Text.Encoding.UTF8;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(&quot;en-US&quot;);

string text1 = &quot;abc&quot;;
string text2 = &quot;ABC&quot;;

Console.WriteLine($&quot;문자열1: {text1}, 문자열2: {text2}&quot;);
Console.WriteLine(&quot;비교: {0}.&quot;, string.Compare(text1, text2));
Console.WriteLine(&quot;비교 (대소문자 무시): {0}.&quot;, string.Compare(text1, text2, ignoreCase: true));
Console.WriteLine(&quot;비교 (문화권및 대소문자 무시): {0}.&quot;, string.Compare(text1, text2, StringComparison.InvariantCultureIgnoreCase));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btgJvT/btsE9Zmjanh/Xs0xp7m5a5i66FSVP2MraK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btgJvT/btsE9Zmjanh/Xs0xp7m5a5i66FSVP2MraK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btgJvT/btsE9Zmjanh/Xs0xp7m5a5i66FSVP2MraK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtgJvT%2FbtsE9Zmjanh%2FXs0xp7m5a5i66FSVP2MraK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;210&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소문자 a는 대문자 A보다 더 작게 판단되므로 비교 결과가 -1이 되지만 대소문자를 무시하도록 하면 또는 더 확실하게 대소문자와 문화권을 무시하도록 설정하면 2개의 문자열을 동등하게 비교할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 비교에 관해서는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot;&gt;Collation, sorting, and string comparison - Globalization | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708412898933&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Collation, sorting, and string comparison - Globalization&quot; data-og-description=&quot;In world-ready applications, the alphabetical order can vary among languages, and the conventions for sequencing items can also be quite different.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pacAo/hyVmUJNTPe/Yx0gOcRHXaKp0oZQILJKYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/globalization/locale/sorting-and-string-comparison&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pacAo/hyVmUJNTPe/Yx0gOcRHXaKp0oZQILJKYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Collation, sorting, and string comparison - Globalization&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In world-ready applications, the alphabetical order can vary among languages, and the conventions for sequencing items can also be quite different.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 문자열 결합, 형식화 그리고 기타&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 string에서 사용할 수 있는 몇 가지 member를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 294px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 17px;&quot;&gt;Member&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;Trim, TrimStart, TrimEnd&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;해당 method는 space나 tab 그리고 carriage return과 같은 공백문자를 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 20px;&quot;&gt;ToUpper, ToLower&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 20px;&quot;&gt;대상 문자열을 전부 대문자 혹은 소문자로 변환합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 20px;&quot;&gt;Insert, Remove&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 20px;&quot;&gt;특정 문자열을 삽입하거나 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 20px;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 20px;&quot;&gt;특정 문자열을 다른 문자열로 대체합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.Empty&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;빈문자열를 표시하기 위해 매번 2중쌍따옴표(&quot;)를 사용하는 대신 공백을 표현하는데 사용될 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.Concat&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;두 문자열 변수를 연결합니다. +연산자를 피연산자인 문자열 사이에 사용해도 동일한 결과를 얻을 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.Join&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;하나 또는 그 이상의 문자열을 각각의 문자열 변수 사이에 문자를 통해 연결합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 40px;&quot;&gt;string.IsNullOrEmpty&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 40px;&quot;&gt;string변수가 null이거나 비어있는지를 확인합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 24.4186%; height: 17px;&quot;&gt;string.IsNullOrWhitespace&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%; height: 17px;&quot;&gt;string변수가 null이거나 공백인지를 확인합니다. 이때 공백은 tab, space, carriage return, line feed등을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.4186%;&quot;&gt;string.Format&lt;/td&gt;
&lt;td style=&quot;width: 75.5814%;&quot;&gt;형식화된 문자열변수를 출력하기 위한 보간문자열의 대체 method로 명명된 매개변수대신 위치를 사용합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상기 method 중 'string.'으로 표시된 method는 static method입니다. 즉, instance변수가 아닌 type에서부터 바로 호출될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 문자열값의 배열을 가져와 Join method를 통해 분리 문자열을 사용하여 단일 문자열로 결하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688109825956&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string cities = &quot;Seoul,Busan,Daegu,Daejeon,Jeju&quot;;
string[] citiesArray = cities.Split(',');
string recombined = string.Join(&quot; =&amp;gt; &quot;, citiesArray);

Console.WriteLine(recombined);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daf6eJ/btsEZCs0QO7/zUO0in8DIPXU2TcMUFzwYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daf6eJ/btsEZCs0QO7/zUO0in8DIPXU2TcMUFzwYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daf6eJ/btsEZCs0QO7/zUO0in8DIPXU2TcMUFzwYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdaf6eJ%2FbtsEZCs0QO7%2FzUO0in8DIPXU2TcMUFzwYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;70&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위치 매개변수와 보간 문자열 서식 구문을 사용해 같은 세 개의 변수를 두 번 표시하도록 하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688110092636&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string fruit = &quot;Apples&quot;;
int price = 2000;

DateTime when = DateTime.Today;

Console.WriteLine($&quot;Interpolated: {fruit} cost {price:C} on {when:dddd}.&quot;);
Console.WriteLine(string.Format(&quot;string.Format: {0} cost {1:C} on {2:dddd}.&quot;, arg0: fruit, arg1: price, arg2: when));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vYNxA/btsE6Gt7BFs/9AFdIZisIejWM9aYxDrrr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vYNxA/btsE6Gt7BFs/9AFdIZisIejWM9aYxDrrr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vYNxA/btsE6Gt7BFs/9AFdIZisIejWM9aYxDrrr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvYNxA%2FbtsE6Gt7BFs%2F9AFdIZisIejWM9aYxDrrr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;82&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JetBrains Rider와 같은 일부 편집기에서는 boxing연산에 대한 경고문구를 표시할 수 있으나 지금 상황에서는 무시해도 딥니다. 만약 이러한 경고문구를 보지 않게 하고자 한다면 price와 when에서 ToString() method를 호출하면 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Console.WriteLine method는 string.Format와 동일한 서식을 지원하고 있기 때문에 좀 더 문을 간소화할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688110340388&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;WriteLine: {0} cost {1:C} on {2:dddd}.&quot;, arg0: fruit, arg1: price, arg2: when);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) 문자열을 효휼적으로 다루기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String.Concat method나 혹은 간단히 + 연산자를 사용하면 2개의 문자열을 결합해 새로운 문자열을 만들 수 있습니다. 하지만 이런 식의 문자열결합은 .NET이 memory에 완전히 새로운 문자열을 생성해야 하므로 좋지 않은 방법이라 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 대게는 단순히 2개의 문자열을 결합하는 경우라면 큰 문제가 없을 테지만 반복문안에서 많은 수의 반복으로 문자열결합을 구현하는 경우라면 성능면에서 부정적인 영향을 줄 수 있으며 memory낭비 역시 초래할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다수의 문자열을 많이 결합해야 하는 경우라면 StringBuilder type을 사용하는 것이 좋습니다. 이에 관한 내용은 아래 link을 참고해 주시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot;&gt;StringBuilder Class (System.Text) | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688110837289&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;StringBuilder Class (System.Text)&quot; data-og-description=&quot;Represents a mutable string of characters. This class cannot be inherited.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chyhQR/hyTa8Jvb85/aWKY2uMJkIgsBI40Kr4KjK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=net-7.0#examples&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chyhQR/hyTa8Jvb85/aWKY2uMJkIgsBI40Kr4KjK/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;StringBuilder Class (System.Text)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Represents a mutable string of characters. This class cannot be inherited.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 정규 표현식을 통한 pattern matching&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 정규 표현식은 사용자로부터의 입력을 검증하는 데 사용되곤 합니다. 정규표현식은 아주 강력하지만 동시에 매우 복잡해질 수 있습니다. 거의 모든 programming언어가 정규표현식을 지원하고 있으며 이를 정의하기 위해 공통적인 특별한 문자들을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표현식의 예를 만들어 보기 위해 csStudy08 solution에서 WorkingWithRegularExpressions이름의 Console App project를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 문자열로서 입력된 숫자 확인하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 입력을 검증하는 일반적인 예를 구현하는 것으로 시작해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithRegularExpressions&amp;nbsp;project의 Program.cs에서 사용자에게 나이입력을 요청하고 입력된 값에서 숫자문자를 찾는 정규표현식을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688192223597&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Text.RegularExpressions;

Console.Write(&quot;Enter your age: &quot;);

string input = Console.ReadLine()!;

Regex ageChecker = new(@&quot;\d&quot;);

if (ageChecker.IsMatch(input))
{
	Console.WriteLine(&quot;Thank you!&quot;);
}
else
{
	Console.WriteLine($&quot;This is not a valid age: {input}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 아래 사항에 주목해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@문자는 문자열 안에서 escape문자를 사용할 수 있는 기능을 끄는데, escape문자 앞에는 backslash가 붙습니다. 예를 들어 \t는 tab을 의미하며 \n은 new line을 의미합니다. 표현식을 사용할 때는 이 기능을 바꿔 사용해야 하므로 disable 해야 합니다.&lt;/li&gt;
&lt;li&gt;Escape문자가 @문자를 통해 비활성화되면 정규표현식으로 해석될 수 있습니다. 예를 들어 \d는 숫자를 의미하게 되는 것입니다. 이 부분에 관해서는 잠시 후 더 자세히 알아볼 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 나이를 묻게 되고 숫자를 입력하면 다음과 같은 결과가 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8TTEE/btsEZAva73c/ei7okK2gyZB2EQ3Zbqd0xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8TTEE/btsEZAva73c/ei7okK2gyZB2EQ3Zbqd0xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8TTEE/btsEZAva73c/ei7okK2gyZB2EQ3Zbqd0xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8TTEE%2FbtsEZAva73c%2Fei7okK2gyZB2EQ3Zbqd0xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;115&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 숫자가 아닌 다른 형태로 값을 입력하게 되면 결과는 달라지게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1942&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bANwuq/btsEZnXcBG6/XprjObB1J1Ux8EMFKWyuyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bANwuq/btsEZnXcBG6/XprjObB1J1Ux8EMFKWyuyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bANwuq/btsEZnXcBG6/XprjObB1J1Ux8EMFKWyuyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbANwuq%2FbtsEZnXcBG6%2FXprjObB1J1Ux8EMFKWyuyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1942&quot; height=&quot;115&quot; data-origin-width=&quot;1942&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 숫자와 다른 문자를 섞어 입력해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BvLna/btsEZBHF4Ae/LqF0yRxWhssVAEzXRNrd6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BvLna/btsEZBHF4Ae/LqF0yRxWhssVAEzXRNrd6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BvLna/btsEZBHF4Ae/LqF0yRxWhssVAEzXRNrd6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBvLna%2FbtsEZBHF4Ae%2FLqF0yRxWhssVAEzXRNrd6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1474&quot; height=&quot;96&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식에서는 \d를 사용했습니다. 이는 숫자 하나를 의미합니다. 그러나 한자리 숫자 앞뒤에 무엇이 입력될 수 있는지는 명시하지 않았습니다. 해당 정규표현식을 풀어서 설명하면 '하나의 숫자를 입력하는 한 어떤 문자를 입력해도 상관없다.'가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식에서는 ^문자를 통해 시작되어야 하는 값을 나타낼 수 있고 $문자를 통해서는 끝나냐 하는 값을 나타낼 수 있습니다. 예제에서는 이들 기호문자를 사용해 숫자를 제외하고 입력된 값 앞뒤에 아무것도 없음을 아래와 같이 표시하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688193941542&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = new(@&quot;^\d+$&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 예제를 다시 실행하면 어떤 길이든지 0과 정수만 허용하게 됨을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 정규 표현식 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식 사용을 위한 .NET type은 .NET platform전역에서 사용될 수 있으며 다수의 app이 이를 내장하고 있습니다. 따라서 이들은 성능에 중요한 영향력을 가지고 있지만 Microsoft로 부터의 최적화에 대한 관심을 받지 못했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5 이후에 들어와서는 System.Text.RegularExpressions namespace가 최대의 성능을 이끌어 내기 위해 내부적으로 재작성되으며 IsMatch와 같은 method를 사용한&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 일반적인 정규표현식 &lt;/span&gt;benchmark에서는 5배 정도 빠르게 측정되었습니다. 게다가 이러한 성능적 혜택을 누리기 위해 기존에 작성되었던 code를 다시 바꿀 필요가 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서 Regex class의 IsMatch method는 성능향상을 위한 ReadOnlySpan&amp;lt;char&amp;gt;을 전달할 수 있는 overload를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 정규 표현식 문법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 정규표현식에서 사용할 수 있는 일반적인 기호문자를 정리한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;^&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;시작값&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;$&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;끝값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\d&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;숫자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\D&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\s&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;공백&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\S&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비공백&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\W&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;단어 문자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\W&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비단어 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[A-Za-z0-9]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;문자의 범위&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\^&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;^(caret) 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[aeiou]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;가능한 문자집합&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[^aeiou]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;비가능한 문자집합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;.&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;모든 단일문자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;\.&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;.(dot) 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 아래는 정규표현식에서 이전 기호에 영향을 주는 일부 정규표현식 한정자를 정리한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;기호&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;+&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;하나 또는 그 이상&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;?&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;하나 아니면 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{3}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;정확히 3개&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{3,5}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;3~5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{3,}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;최소3개&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;{,3}&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;3이상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) 정규 표현식 예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표에서는 정규식을 통한 몇 가지 예를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 220px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;표현식&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;\d&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;한자리 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;a&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;문자 a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;cliel&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;단어 cliel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^cliel&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;cliel로 시작하는 단어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;cliel$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;cliel로 끝나는 단어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^\d{2}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;정확히 2자리 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[0-9]{2}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;위와 동일, 정확히 2자리 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[A-Z]{4,}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;ASCII 문자 set에서의 최소 4개의 영문 대문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[A-Za-z]{4,}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;ASCII 문자 set에서의 최소 4개의 영문 대소문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.8372%; height: 20px;&quot;&gt;^[A-Z]{2}\d{3}$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%; height: 20px;&quot;&gt;ASCII 문자 set에서의 2개 대문자와 3개의 숫자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.8372%;&quot;&gt;^[A-Za-z\u00c0-\ u017e]+$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%;&quot;&gt;ASCII 문자 set에서의 최소 1개의 대소문자 혹은 Unicode 문자 set에서의 European 문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.8372%;&quot;&gt;^d.g$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%;&quot;&gt;문자 d로 시작, 모든 문자, g로 끝나는 경우이므로 dig, dog처럼 d와 g사이에 단일문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.8372%;&quot;&gt;^d\.g$&lt;/td&gt;
&lt;td style=&quot;width: 71.1628%;&quot;&gt;문자 d로 시작, ., g로 끝나는 경우이므로 d.g에서만 일치&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;정규표현식은 거의 공통이므로 한번 사용한 정규표현식은 JavaScript나 Python과 같은 다른 언어에서 재사용이 가능합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) 쉼표(,)로 구분된 문자열의 분리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 '문자열 분리'에서는 comma(,)로 구분된 문자열 변수에서 문자열을 어떻게 분리할 수 있을지에 대한 예제를 만들어본 바 있습니다. 그런데 아래와 같은 경우는 위에서 처럼 단순하게 처리할 수는 없는 형태입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&quot;aaa,bbb&quot;,&quot;ccc,ddd&quot;,&quot;eee,fff&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문자열값은 각각에서 큰따옴표(&quot;)를 사용하고 있으며 해당 값들은 쉼표(,)로 분리할 필요가 있는지 여부를 확인하기 위해 사용할 것입니다. 문자열 분리를 위한 Split method가 존재하지만 해당 예제에서는 맞지 않으므로 정규표현식을 대신 사용해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link는 해당 예제를 작성하는데 영향을 받게 된 Stack Overflow의 article로서 더 자세한 설명을 확인해 볼 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string문자열 안에서 큰따옴표(&quot;)를 포함시키기 위해 이들 앞에 backslash를 붙여주거나 C# 11부터 도입된 'raw string literal'을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 comma로 구분된 문자열값을 저장하는 변수를 추가하고&amp;nbsp; Split method를 통해 문자열을 나누어 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688279031503&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string str = &quot;&quot;&quot;
&quot;aaa,bbb&quot;,&quot;ccc,ddd&quot;,&quot;eee,fff&quot;
&quot;&quot;&quot;;

Console.WriteLine($&quot;Strings to split: {str}&quot;);

string[] attrStr = str.Split(',');

Console.WriteLine(&quot;Splitting with string.Split method:&quot;);
foreach (string s in attrStr)
{
	Console.WriteLine(s);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 해당 문자열을 다시 정규표현식을 통해 나누는 문을 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688281231450&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex csv = new(&quot;(?:^|,)(?=[^\&quot;]|(\&quot;)?)\&quot;?((?(1)[^\&quot;]*|[^,\&quot;]*))\&quot;?(?=,|$)&quot;);
MatchCollection colStr = csv.Matches(str);

Console.WriteLine(&quot;Splitting with regular expression:&quot;);
foreach (Match s in colStr)
{
	Console.WriteLine(s.Groups[2].Value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHO7YI/btsE0I7k0HC/wmMzxMfKd6kylxi5Brqim0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHO7YI/btsE0I7k0HC/wmMzxMfKd6kylxi5Brqim0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHO7YI/btsE0I7k0HC/wmMzxMfKd6kylxi5Brqim0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHO7YI%2FbtsE0I7k0HC%2FwmMzxMfKd6kylxi5Brqim0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2269&quot; height=&quot;286&quot; data-origin-width=&quot;2269&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 정규표현식 구문강조 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022를 사용한다면 위에서 정규표현식을 사용하고자 생성자값으로 문자열을 전달할 때 색상으로 구문이 강조되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;19&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehAPTV/btsEZyD9pDV/Ubk0SpHVA6tiaps2Y6MPnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehAPTV/btsEZyD9pDV/Ubk0SpHVA6tiaps2Y6MPnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehAPTV/btsEZyD9pDV/Ubk0SpHVA6tiaps2Y6MPnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehAPTV%2FbtsEZyD9pDV%2FUbk0SpHVA6tiaps2Y6MPnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;598&quot; height=&quot;19&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;19&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 문자열값을 처리할 때 적용되지 않던 색상 구문 강조가 정규 표현식을 사용할 때 적용된 이유는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유를 알아보기 위해 생성자에서 mouse오른쪽 button을 click 한 뒤 'Go To Implementation'항목을 선택합니다. 그러면 pattern 문자열 매개변수에 Regex 상수값이 전달된 StringSyntax 이름의 attribute가 적용되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293345681&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Creates a regular expression object for the specified regular expression.
/// &amp;lt;/summary&amp;gt;
public Regex([StringSyntax(StringSyntaxAttribute.Regex)] string pattern) :
this(pattern, culture: null)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 다시 StringSyntax attribute에 mouse오른쪽 click한뒤 'Go To Implementation'항목을 선택합니다. 결과를 보면 Regex를 포함해 선택가능한 12개의 인식된 문자열 구문형식이 존재함을 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293553586&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public const string CompositeFormat = nameof(CompositeFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing date format specifiers.&amp;lt;/summary&amp;gt;
public const string DateOnlyFormat = nameof(DateOnlyFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing date and time format specifiers.&amp;lt;/summary&amp;gt;
public const string DateTimeFormat = nameof(DateTimeFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing &amp;lt;see cref=&quot;Enum&quot;/&amp;gt; format specifiers.&amp;lt;/summary&amp;gt;
public const string EnumFormat = nameof(EnumFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing &amp;lt;see cref=&quot;Guid&quot;/&amp;gt; format specifiers.&amp;lt;/summary&amp;gt;
public const string GuidFormat = nameof(GuidFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing JavaScript Object Notation (JSON).&amp;lt;/summary&amp;gt;
public const string Json = nameof(Json);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing numeric format specifiers.&amp;lt;/summary&amp;gt;
public const string NumericFormat = nameof(NumericFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing regular expressions.&amp;lt;/summary&amp;gt;
public const string Regex = nameof(Regex);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing time format specifiers.&amp;lt;/summary&amp;gt;
public const string TimeOnlyFormat = nameof(TimeOnlyFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing &amp;lt;see cref=&quot;TimeSpan&quot;/&amp;gt; format specifiers.&amp;lt;/summary&amp;gt;
public const string TimeSpanFormat = nameof(TimeSpanFormat);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing URIs.&amp;lt;/summary&amp;gt;
public const string Uri = nameof(Uri);

/// &amp;lt;summary&amp;gt;The syntax identifier for strings containing XML.&amp;lt;/summary&amp;gt;
public const string Xml = nameof(Xml);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithRegularExpressions project에서 Strings. cs라는 이름의 class file을 추가한 뒤 2개의 문자열 상수를 정의하는 문을 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688293704379&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	const string digitsOnlyText = @&quot;^\d+$&quot;;
	const string commaSeparatorText = &quot;(?:^|,)(?=[^\&quot;]|(\&quot;)?)\&quot;?((?(1)[^\&quot;]*|[^,\&quot;]*))\&quot;?(?=,|$)&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio 2022에서 위 예제를 작성하면 아직까지는 정규식에 색상 구문 강조가 적용되지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 literal string을 아래와 같이 digits-only 정규 표현식에 대한 string constant로 바꾸고&lt;/p&gt;
&lt;pre id=&quot;code_1688294148019&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = new(digitsOnlyText);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithText project의 comma 분리 정규표현식에서도 아래와 같이 string constant로 바꾸어 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688294497859&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex csv = new(commaSeparatorText);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Strings.cs에서는 StringSyntax attribute를 위한 namespace를 추가하고 string상수 둘다에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;StringSyntax&lt;span&gt; attribute를 적용합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1688294610163&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
	[StringSyntax(StringSyntaxAttribute.Regex)]
	const string digitsOnlyText = @&quot;^\d+$&quot;;

	[StringSyntax(StringSyntaxAttribute.Regex)]
	const string commaSeparatorText = &quot;(?:^|,)(?=[^\&quot;]|(\&quot;)?)\&quot;?((?(1)[^\&quot;]*|[^,\&quot;]*))\&quot;?(?=,|$)&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 날짜의 서식화를 위한 또 다른 상수를 아래와 같이 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1688294680370&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[StringSyntax(StringSyntaxAttribute.DateTimeFormat)]
const string fullDateTime = &quot;&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fullDateTime문자열 안에 'd'를 typing 하면 IntelliSense가 작동됨을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;163&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCpSOD/btsE42Yxi7W/pwsNk5XV8PNp0slp6j1WD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCpSOD/btsE42Yxi7W/pwsNk5XV8PNp0slp6j1WD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCpSOD/btsE42Yxi7W/pwsNk5XV8PNp0slp6j1WD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCpSOD%2FbtsE42Yxi7W%2FpwsNk5XV8PNp0slp6j1WD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;163&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;163&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fullDateTime변수에 'yyyy-MM-dd'형식의 날짜 format을 지정하고 digitsOnlyText의 끝에는 \문자를 추가합니다. 이때 IntelliSense를 통해 올바른 정규표현식을 입력하는데 도움을 받을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1013&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BxXHs/btsE7uGI6kG/lDHgKKtRgzRXZkKuZAKVC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BxXHs/btsE7uGI6kG/lDHgKKtRgzRXZkKuZAKVC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BxXHs/btsE7uGI6kG/lDHgKKtRgzRXZkKuZAKVC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBxXHs%2FbtsE7uGI6kG%2FlDHgKKtRgzRXZkKuZAKVC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1013&quot; height=&quot;236&quot; data-origin-width=&quot;1013&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[StringSyntax] attribute는 .NET 7에서 새롭게 도입된 기능으로 이를 인식하는 것은 code editor의 몫입니다. .NET 7에는 이뿐만 아니라 해당 attribute에 적용할 수 있는 350개 이상의 매개변수, 속성, field를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) Source generator를 통한 정규표현식의 성능향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열값 혹은 문자열상수를 Regex의 생성자로 전달하면 class는 문자열을 분석하여 정규표현식 번역기에 의해 효휼적으로 실행될 수 있도록 최적화된 방식으로 표현식을 나타내는 내부 tree구조로 변환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규 표현식은 RegexOption을 지정함으로써 아래와 같이 compile 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688360143487&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = new(digitsOnlyText, RegexOptions.Compiled);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 compiling은 정규표현식의 초기생성 속도를 늦추는 부정적인 효과를 가지고 있습니다. 번역기가 실행할 tree구조가 생성되고 나면 compiler는 이 tree를 IL code로 변환하고 IL code를 native code로 JIT compile 해야 합니다. 정규표현식을 몇 번만 실행시킬 거라면 이것을 굳이 compile 할 필요가 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서는 [GeneratedRegex] attribute를 Regex를 반환하는 partial method에 적용하면 이를 인식하는 정규표현식에 대한 source generator를 도입하였는데 이는 정규표현식에 대한 logic을 구현하는 method의 구현을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithRegularExpressions project에서 Regexs.cs라는 새로운 class file을 생성하고 아래와 같이 partial method를 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688362457867&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    [GeneratedRegex(digitsOnlyText, RegexOptions.IgnoreCase)]
    private static partial Regex DigitsOnly();
    [GeneratedRegex(commaSeparatorText, RegexOptions.IgnoreCase)]
    private static partial Regex CommaSeparator();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 아래와 같이 digits-only 정규표현식을 반환하는 partial method를 호출하도록 생성자를 바꿔주고&lt;/p&gt;
&lt;pre id=&quot;code_1688362570923&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex ageChecker = DigitsOnly();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;comma 구분자 정규 표현식을 반환하는 partial method를 호출하도록 하는 생성자도 아래와 같이 바꿔줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688363068378&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Regex csv = CommaSeparator();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 각 partial method에 mouse pointer를 올려보면 표현식에 대한 동작을 설명하는 tooltip을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPkbtG/btsE5EQP4Y8/IOYuEmBWYj4q3iMuP3XSG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPkbtG/btsE5EQP4Y8/IOYuEmBWYj4q3iMuP3XSG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPkbtG/btsE5EQP4Y8/IOYuEmBWYj4q3iMuP3XSG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPkbtG%2FbtsE5EQP4Y8%2FIOYuEmBWYj4q3iMuP3XSG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;224&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DigitsOnly partial method를 mouse 오른쪽 click 하여 'Go To Definition'을 click 해 보면 자동으로 생성된 partial method의 구현체를 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLaJbw/btsE3pTYGrt/TZLMu4Nru1SNDKYWpCVW0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLaJbw/btsE3pTYGrt/TZLMu4Nru1SNDKYWpCVW0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLaJbw/btsE3pTYGrt/TZLMu4Nru1SNDKYWpCVW0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLaJbw%2FbtsE3pTYGrt%2FTZLMu4Nru1SNDKYWpCVW0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1146&quot; height=&quot;391&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 기존의 동작과 같은지를 확인합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link에서는 .NET 7에서 정규 표현식의 구현에 대한 더 많은 내용을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot;&gt;Regular Expression Improvements in .NET 7 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688363376173&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Regular Expression Improvements in .NET 7 - .NET Blog&quot; data-og-description=&quot;System.Text.RegularExpressions has improved significantly in .NET 7. In this post, we'll deep-dive into many of its exciting improvements.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uB2JV/hyTcAl2QIW/5ieMkckpL4trcKuUiaeJQK/img.png?width=655&amp;amp;height=223&amp;amp;face=0_0_655_223,https://scrap.kakaocdn.net/dn/Zzyym/hyTcFt64Ae/TwHMJrzehISeL5l35wefCK/img.png?width=1659&amp;amp;height=742&amp;amp;face=0_0_1659_742,https://scrap.kakaocdn.net/dn/bx4uQH/hyTcH6x7tj/LcVIVW8MoX7cLUIEslomtk/img.png?width=857&amp;amp;height=590&amp;amp;face=0_0_857_590&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uB2JV/hyTcAl2QIW/5ieMkckpL4trcKuUiaeJQK/img.png?width=655&amp;amp;height=223&amp;amp;face=0_0_655_223,https://scrap.kakaocdn.net/dn/Zzyym/hyTcFt64Ae/TwHMJrzehISeL5l35wefCK/img.png?width=1659&amp;amp;height=742&amp;amp;face=0_0_1659_742,https://scrap.kakaocdn.net/dn/bx4uQH/hyTcH6x7tj/LcVIVW8MoX7cLUIEslomtk/img.png?width=857&amp;amp;height=590&amp;amp;face=0_0_857_590');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Regular Expression Improvements in .NET 7 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;System.Text.RegularExpressions has improved significantly in .NET 7. In this post, we'll deep-dive into many of its exciting improvements.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Collection에서 다중 개체 저장하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data와 관련된 또 다른 가장 일반적인 type으로는 collection을 들 수 있습니다. Collection은 다중값을 저장하는 일반적인 type입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection은 memory에 있는 data구조로서 모든 collection이 일부 기능을 공유하고 있기는 하지만 나름대로의 방식으로 다중 item을 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 .NET에서 collection을 사용하기 위한 가장 일반적인 type을 나타낸 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;Example type(s)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;IEnumerable, IEnumerable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Collection에서 사용되는 Interface와 기반 class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections.Generic&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;List&amp;lt;T&amp;gt;, Dectionary&amp;lt;T&amp;gt;, Queue&amp;lt;T&amp;gt;, Stack&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;.NET Framework 2.0과 함께 C# 2.0에서 도입된 것으로 generic type 매개변수를 통해 원하는 type을 지정할 수 있는 collection (더 안전하고, 더 빠르며, 더 효휼적인)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections.Concurrent&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;BlockingCollection, ConcurrentDictionary, ConcurrentQueue&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Multithread scenario에서 사용하기에 안전한 collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.8682%;&quot;&gt;System.Collections.Immutable&lt;/td&gt;
&lt;td style=&quot;width: 38.7984%;&quot;&gt;ImmutableArray, ImmutableDictionary, ImmutableList, ImmutableQueue&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;본래 collection의 content가 절대 바뀌지 않는다는 scenario를 위해 설계된 것으로 다만 새로운 instance를 생성함으로서 기존의 collection을 변경할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 모든 collection에서의 공통기능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 collection은 ICollection interface를 구현하고 있고 때문에 얼마나 많은 개체가 존재하는지를 의미하는 Count속성을 가지고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371452448&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICollection : IEnumerable
{
	int Count { get; }
	bool IsSynchronized { get; }
	object SyncRoot { get; }
	void CopyTo(Array array, int index);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 한 학급의 학생에 대한 collection이 존재한다면 다음과 같이 학생의 수를 구할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371230849&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int howMany = students.Count;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 모든 collection은 IEnumerable interface를 구현합니다. 때문에 foreach문을 통해 각 요소를 열거할 수 있습니다. 그리고 이를 위해서는 IEnumerator를 구현하는 개체를 반환하는 GetEnumerator method를 가져야 하며 반환된 개체는 collection을 탐색할 수 있는 MoveNext와 Reset method 그리고 collection에서 현재 item을 가지고 있는 Current 속성을 가져야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371476056&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface IEnumerable
{
	IEnumerator GetEnumerator();
}

public interface IEnumerator
{
	object Current { get; }
	bool MoveNext();
	void Reset();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 students collection에서 각 개체에 대해 아래와 같은 동작을 수행할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371576216&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;foreach (Student s in students)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개체 기반 collection interface뿐만 아니라 collection에 저장되는 type을 정의하는 generic type generic interface와 class도 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688371758609&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICollection&amp;lt;T&amp;gt; : IEnumerable&amp;lt;T&amp;gt;, IEnumerable
{
    int Count { get; }
    bool IsReadOnly { get; }
    void Add(T item);
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int index);
    bool Remove(T item);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Collection의 가용성을 지정하여 성능 향상하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 1.1부터 StringBuilder와 같은 type은 EnsureCapacity라는 이름의 method를 가지고 있어서 이를 통해 array를 저장하는 size를 미리 지정할 수 있었고 따라서 string의 마지막 size를 예상할 수 있었습니다. 이 것은 문자가 추가됨으로서 array의 size를 반복적으로 증가시키지 않아도 되므로 성능에 도움이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6부터 List&amp;lt;T&amp;gt;, Queue&amp;lt;T&amp;gt;, Stack&amp;lt;T&amp;gt;와 같은 collection은 같은 역할로 EnsureCapacity이름의 method를 가지게 되었으며 아래와 같이 사전 size를 지정할 수 있게 되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688376054059&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; names = new();
names.EnsureCapacity(10000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Collection의 종류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection은 사용하고자 하는 목적에 따라 lists, dictionaries, queues, sets 등 선택가능한 여러 가지가 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Lists&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IList&amp;lt;T&amp;gt;를 구현하는 List는 정렬된 collection입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688437311666&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[DefaultMember(&quot;Item&quot;)]
public interface IList&amp;lt;T&amp;gt; : ICollection&amp;lt;T&amp;gt;, IEnumerable&amp;lt;T&amp;gt;, IEnumerable
{
    T this[int index] { get; set; }
    int IndexOf(T item);
    void Insert(int index, T item);
    void RemoveAt(int index);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;DefaultMember attribute는 member의 이름이 지정되지 않았을 경우 기본적으로 접근될 member를 지정합니다. 따라서 만약 IndexOf를 기본 member로 지정하고자 한다면 [DefaultMember(&quot;IndexOf&quot;)]와 같이 사용할 수 있습니다. 예제에서 Item은 Indexer를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IList&amp;lt;T&amp;gt;는 ICollection&amp;lt;T&amp;gt;로 부터 파생되므로 Count속성을 가지고 있으며 collection끝에 item을 추가하기 위한 Add method와 특정한 위치에 item을 추가하는 Insert method, 지정한 위치에 item을 제거하는 RemoveAt method를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 List는 수동적으로 collection의 item 순서를 조정할 수 있습니다. List의 각 item은 자동적으로 할당된 고유한 index(또는 위치값)를 가지고 있으며 또한 T에 의해 어떠한 type으로도 item이 정의될 수 있고 필요하다면 중복으로 item을 저장시킬 수 있습니다. Index는 int type으로서 0부터 시작하므로 list의 첫 번째 item의 index값은 0이 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Index&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;apple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;banana&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;orange&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 상태에서 banana와 orange사이에 lemon이라는 새로운 item이 삽입되면 orange의 index는 자동적으로 증가됩니다. 따라서 item의 index는 다른 item의 삽입이나 제거로 인해 바뀔 수 있음에 주의해야 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Index&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;apple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;banana&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;lemon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;orange&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;때로는 Array가 더 나은 상황임에도 굳이 List&amp;lt;T&amp;gt;나 다른 collection을 사용하는 경우가 있습니다. 그러나 이는 좋지 않은 습관입니다. Data의 size가 instance화 된 이후에 바뀌는 않는 경우라면 array를 사용하는 것이 좋습니다. 먕갸 data의 조작이 필요한 경우라면 처음에는 list를 사용하여 필요한 변경을 수행하고 그 다음 array로 변환하여 사용합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Dictionary&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary는 각 값(또는 개체)이 고유한 하위 값을 가질 수 있으며 이는 collection에서 빠르게 값을 찾는 key로 사용될 수 있습니다. 여기서 key는 고유해야 합니다. 예를 들어 사람에 대한 list를 저장하는 경우라면 주민등록번호를 key로서 사용할 수 있을 것입니다. Dictionary는 Python과 Java와 같은 다른 언어에서는 hashmaps라고도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key는 실제 사전에서 index항목과 같이 생각할 수 있습니다. 단어(즉, key)는 정렬된 상태로 유지되므로 빠르게 단어의 정의를 찾을 수 있게 하며 만약 manatee단어에 대한 정의를 찾는다고 하면 사전의 중간쯤부터 시작할 수 있을 것입니다. 왜냐하면 문자 M은 alphabet의 중간쯤에 위치해 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Programming에서 Dictionary는 무엇인가를 찾을 때 위와 비슷하게 동작할 수 있습니다. Dictionary는 IDictionary&amp;lt;TKey, TValue&amp;gt; interface를 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688439430168&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[DefaultMember(&quot;Item&quot;)] // aka this indexer
public interface IDictionary&amp;lt;TKey, TValue&amp;gt; : ICollection&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;, IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;, IEnumerable
{
    TValue this[TKey key] { get; set; }
    ICollection&amp;lt;TKey&amp;gt; Keys { get; }
    ICollection&amp;lt;TValue&amp;gt; Values { get; }
    void Add(TKey key, TValue value);
    bool ContainsKey(TKey key);
    bool Remove(TKey key);
    bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary의 item은 값 type인 KeyValuePair&amp;lt;TKey, TValue&amp;gt; struct의 instance로서 TKey는 key의 type이며 TValue는 value의 type입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688439609425&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public readonly struct KeyValuePair&amp;lt;TKey, TValue&amp;gt;
{
    public KeyValuePair(TKey key, TValue value);
    public TKey Key { get; }
    public TValue Value { get; }
    
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void Deconstruct(out TKey key, out TValue value);
    public override string ToString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Dictionary&amp;lt;string, Person&amp;gt;는 key로서 string을, 값(value)으로서 Person instance를 사용하는 것입니다. 아래 예제 Dictionary&amp;lt;string, string&amp;gt;은 값과 key로서 둘 다 string을 사용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;key&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;801201-1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;홍길동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;750405-2&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;홍길남&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;820712-1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;홍길석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Stack&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack는 LIFO(last-in, first-out) 동작을 구현하기에 적합합니다. 따라서 stack에서는 전체 item을 열거할 수는 있지만 stack의 가장 상위에 있는 하나의 item에 대한 직접적인 접근 및 제거만이 가능합니다. Stack에 대한 두 번째나 세 번째 등의 접근 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack의 가장 대표적인 사례로는 word process를 들 수 있습니다. Stack을 통해 우리가 한 동작을 순서대로 기억하고 있다가 Ctrl + Z key를 누르게 되면 stack에서 마지막 동작을 읽어 되돌리게 되는 것입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Queue&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue는 FIFO(first-in, first-out)동작을 구현하기에 적합합니다. 따라서 queue에서는 전체 item을 열거할 수는 있지만 queue의 가장 앞에 있는 item에 대한 직접적인 접근및 제거만이 가능하며 두번째나 세번째등의 접근은 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue의 가장 대표적인 사례로는 은행에서 줄을 서 있는 사람들처럼 들어온 순서대로 작업 item을 처리할 때 queue를 사용한 경우를 들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서는 queue의 각 item에 position과 우선순위값이 할당된 PriorityQueue를 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Program.cs에 queue를 정의하고 사용하는 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다. 예제는 Queue를 통해 음식점에서 손님에게 순서대로 주문한 음식을 제공하는 경우를 나타낸 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708569942672&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Queue&amp;lt;string&amp;gt; foods = new();
foods.Enqueue(&quot;제육볶음&quot;);
foods.Enqueue(&quot;된장찌개&quot;);
foods.Enqueue(&quot;김치찌개&quot;);
foods.Enqueue(&quot;백반정식&quot;);
foods.Enqueue(&quot;라면&quot;);

Output(&quot;Initial queue from front to back&quot;, foods);

string served = foods.Dequeue();
Console.WriteLine($&quot;Served: {served}.&quot;);

served = foods.Dequeue();
Console.WriteLine($&quot;Served: {served}.&quot;);
Output(&quot;Current queue from front to back&quot;, foods);

Console.WriteLine($&quot;{foods.Peek()} is next in line.&quot;);
Output(&quot;Current queue from front to back&quot;, foods);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IP2tc/btsEZoaFcvE/xd4JHOwG2nHvQQEl6Jwn6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IP2tc/btsEZoaFcvE/xd4JHOwG2nHvQQEl6Jwn6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IP2tc/btsEZoaFcvE/xd4JHOwG2nHvQQEl6Jwn6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIP2tc%2FbtsEZoaFcvE%2Fxd4JHOwG2nHvQQEl6Jwn6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;326&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Helpers.cs의 partial Program class에서 OutputPQ라는 이름의 정적 method를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708569942674&quot; class=&quot;cs&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void OutputPQ&amp;lt;TElement, TPriority&amp;gt;(string title, IEnumerable&amp;lt;(TElement Element, TPriority Priority)&amp;gt; collection)
{
    Console.WriteLine(title);

    foreach ((TElement, TPriority) item in collection)
    {
        Console.WriteLine($&quot; {item.Item1}: {item.Item2}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;OutputPQ는 generic임에 주목합니다. 이를 통해 collection에 전달되는 tuple에서 사용할 2개의 type을 지정할 수 있습니다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 priority queue를 정의하고 사용하는 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708569942675&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PriorityQueue&amp;lt;string, int&amp;gt; foods = new();
foods.Enqueue(&quot;제육볶음&quot;, 1);
foods.Enqueue(&quot;된장찌개&quot;, 4);
foods.Enqueue(&quot;김치찌개&quot;, 3);
foods.Enqueue(&quot;백반정식&quot;, 2);
foods.Enqueue(&quot;라면&quot;, 1);

OutputPQ(&quot;Initial queue from front to back&quot;, foods.UnorderedItems);
Console.WriteLine($&quot;{foods.Dequeue()} has been served.&quot;);
Console.WriteLine($&quot;{foods.Dequeue()} has been served.&quot;);

OutputPQ(&quot;Current queue :&quot;, foods.UnorderedItems);
Console.WriteLine($&quot;{foods.Dequeue()} has been served.&quot;);
            
Console.WriteLine(&quot;Adding Mark to queue with priority 2&quot;);
foods.Enqueue(&quot;오무라이스&quot;, 2);

Console.WriteLine($&quot;{foods.Peek()} will be next to be served.&quot;);
OutputPQ(&quot;Current queue :&quot;, foods.UnorderedItems);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제에서 Enqueue method의 두 번째 숫자가 우선순위에 해당합니다. 1이 가장 높은 우선순위를 4가 가장 낮은 우선순위가 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmuK90/btsE6Bzz5XW/R3R5RdtY38KColbhq8bQ4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmuK90/btsE6Bzz5XW/R3R5RdtY38KColbhq8bQ4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmuK90/btsE6Bzz5XW/R3R5RdtY38KColbhq8bQ4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmuK90%2FbtsE6Bzz5XW%2FR3R5RdtY38KColbhq8bQ4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;365&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Set&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set은 2개의 collection사이에 집합 연산을 수행합니다. 예를 들어 과일에 대한 2개의 collection을 가지고 있을 때 2개 collection모두에서 공통적으로 속해있는(집합 사이에 교차하는) 과일의 이름을 알고 싶은 경우 set을 사용할 수 있습니다. Set에서 item은 고유해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 사용되는 Set method는 아래와 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;Add&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Item이 존재하지 않는다면 새롭게 추가합니다. Method의 수행결과로 true라면 Item이 추가된 것이고 false라면 해당 Item이 이미 존재하는 경우입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;ExceptWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set의 item을 set에서 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;IntersectWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set이 아닌 item을 set에서 제거합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;IsProperSubsetOf&lt;br /&gt;IsProperSupersetOf&lt;br /&gt;IsSubsetOf&lt;br /&gt;IsSupersetOf&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Subset은 item이 모두 다른 set에 존재하는 set입니다. 적절한 subset은 item이 모두 다른 set에 존재하는 set이지만 다른 set에 없는 item이 최소 하나이상 있는 set입니다. Superset은 모든 item이 다른 set에 있는 set이고 적절한 superset은 다른 set의 모든 item과 다른 set에 존재하지 않는 하나이상의 item을 포함합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;Overlaps&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Set와 다른 set는 하나 이상의 공통 item을 공유합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;SetEquals&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;Set와 다른 set는 정확히 같은 item을 공유합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;SymmetricExceptWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set에 속하지 않은 item을 set에서 제거하고 누락된것을 추가합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.6279%;&quot;&gt;UnionWith&lt;/td&gt;
&lt;td style=&quot;width: 78.3721%;&quot;&gt;매개변수로 전달된 set에 아직 set에 없는 item을 추가합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set에 대한 일반적인 예제를 작성해 보기 위해 Program.cs를 아래와 같이 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708561770243&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HashSet&amp;lt;String&amp;gt; fruits = new();

foreach(string fruit in new[] { &quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;, &quot;banana&quot; })
{
    bool result = fruits.Add(fruit);

    Console.WriteLine($&quot;result : {result}, {fruit}&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biOEjj/btsE8B1QHiP/ouJbNzzX3YetE2HdbhBerk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biOEjj/btsE8B1QHiP/ouJbNzzX3YetE2HdbhBerk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biOEjj/btsE8B1QHiP/ouJbNzzX3YetE2HdbhBerk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiOEjj%2FbtsE8B1QHiP%2FouJbNzzX3YetE2HdbhBerk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;229&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Collection method&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 collection은 item을 추가하거나 삭제할 때 사용하는 일련의 method가 다르며 아래 표는 collection별로 이러한 method를 정리한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Collection&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Add method&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Remove method&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;List&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Add, Insert&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Remove, RemoveAt&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;List는 정렬되어 있으므로 item은 int형식의 index position을 갖고 있습니다. Add는 list의 끝에 새로운 item을 추가하며 Insert는 지정한 위치에 새로운 item을 추가하게 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Dictionary&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Add&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Remove&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Dictionary는 정렬되어 있지 않으므로 item또한 index position을 갖고 있지 않습니다. ContainsKey method를 호출하면 해당 key가 사용되었는지 여부를 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Stack&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Push&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Pop&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Push method는 새로운 item을 stack의 상위에 추가합니다. 따라서 첫번째 item은 맨 마지막에 있게 됩니다. Item이 Pop을 통해 제거될때는 stack의 가장 상위의 것이 제거됩니다. Peek method를 호출하면 해당 값을 삭제하지 않고도 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88373%;&quot;&gt;Queue&lt;/td&gt;
&lt;td style=&quot;width: 13.721%;&quot;&gt;Enqueue&lt;/td&gt;
&lt;td style=&quot;width: 19.0697%;&quot;&gt;Dequeue&lt;/td&gt;
&lt;td style=&quot;width: 57.3256%;&quot;&gt;Enqueue method를 통해 item이 추가될때는 항상 queue의 끝에 추가됩니다. 따라서 첫번째 item은 queue의 앞에 있게 됩니다. Dequeue method를 통해 item이 제거될때는 queue의 가장 앞에 있는 item을 제거합니다. Peek method를 호출하면 해당 값을 삭제하지 않고도 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) List 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 solution에서 WorkingWithCollections이름의 Console App project를 추가하고 Helpers.cs class를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688524462411&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;partial class Program
{
    static void Output(string title, IEnumerable&amp;lt;string&amp;gt; collection)
    {
        Console.WriteLine(title);

        foreach (string item in collection)
        {
            Console.WriteLine($&quot; {item}&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 기존의 문을 모두 삭제하고 list를 정의하고 사용하는 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688524632676&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new();
cities.Add(&quot;Daegu&quot;);
cities.Add(&quot;Seoul&quot;);
cities.Add(&quot;Busan&quot;);

Output(&quot;Initial list&quot;, cities);
Console.WriteLine($&quot;The first city is {cities[0]}.&quot;);
Console.WriteLine($&quot;The last city is {cities[cities.Count - 1]}.&quot;);
            
cities.Insert(0, &quot;Jeju&quot;);
Output(&quot;After inserting Sydney at index 0&quot;, cities);

cities.RemoveAt(1);
cities.Remove(&quot;Seoul&quot;);
Output(&quot;After removing two cities&quot;, cities);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 cities list는 아래 2가지 다른 방식으로도 정의될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688524790235&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new() { &quot;London&quot;, &quot;Paris&quot;, &quot;Milan&quot; };&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688524812315&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new();
cities.AddRange(new[] { &quot;London&quot;, &quot;Paris&quot;, &quot;Milan&quot; });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과가 표시될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AyKnC/btsE8NlKE8c/BuT1ySNXpVEnI0VGKwrODK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AyKnC/btsE8NlKE8c/BuT1ySNXpVEnI0VGKwrODK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AyKnC/btsE8NlKE8c/BuT1ySNXpVEnI0VGKwrODK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAyKnC%2FbtsE8NlKE8c%2FBuT1ySNXpVEnI0VGKwrODK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;285&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Dictionary 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서는 Dictionary를 정의하고 사용하며 단어를 찾는 등의 일반적인 방법을 나타내는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688525225523&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;string, string&amp;gt; keywords = new();

keywords.Add(key: &quot;int&quot;, value: &quot;32-bit integer data type&quot;);
keywords.Add(&quot;long&quot;, &quot;64-bit integer data type&quot;);
keywords.Add(&quot;float&quot;, &quot;Single precision floating point number&quot;);

Output(&quot;Dictionary keys:&quot;, keywords.Keys);
Output(&quot;Dictionary values:&quot;, keywords.Values);

Console.WriteLine(&quot;Keywords and their definitions&quot;);
foreach (KeyValuePair&amp;lt;string, string&amp;gt; item in keywords)
{
    Console.WriteLine($&quot; {item.Key}: {item.Value}&quot;);
}

string key = &quot;long&quot;;
Console.WriteLine($&quot;The definition of {key} is {keywords[key]}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 첫 번째 Add method는 명명된 매개변수를 사용한 추가방식을 나타내고 있습니다. 또한 Dictionary는 아래와 같은 2가지 다른 방식을 통해 정의될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688525331275&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;string, string&amp;gt; keywords = new()
{
    { &quot;int&quot;, &quot;32-bit integer data type&quot; },
    { &quot;long&quot;, &quot;64-bit integer data type&quot; },
    { &quot;float&quot;, &quot;Single precision floating point number&quot; },
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688525369306&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dictionary&amp;lt;string, string&amp;gt; keywords = new()
{
    [&quot;int&quot;] = &quot;32-bit integer data type&quot;,
    [&quot;long&quot;] = &quot;64-bit integer data type&quot;,
    [&quot;float&quot;] = &quot;Single precision floating point number&quot;,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 상기예제에서 마지막 comma(,)는 생략할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cR70MY/btsE2A84tDV/8VTA4btzGEK0cb0TzoJglk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cR70MY/btsE2A84tDV/8VTA4btzGEK0cb0TzoJglk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cR70MY/btsE2A84tDV/8VTA4btzGEK0cb0TzoJglk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcR70MY%2FbtsE2A84tDV%2F8VTA4btzGEK0cb0TzoJglk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;253&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Collection 정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt; class에서는 Sort method를 호출함으로써 수동적으로 정렬할 수 있습니다(단, 각 item의 index는 바뀔 수 있습니다.). 문자열값 혹은 다른 내장 type의 list를 수동적으로 정렬하는 것은 별도의 구현이 필요 없이 작업이 가능하지만 자신만의 type으로 collection을 생성한 경우라면 해당 type은 IComparable이름의 interface를 구현해야 합니다. 이에 대한 자세한 내용은 아래 글에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 6. Interface와 Class상속&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708571031002&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C# 12와 .NET 8] 6. Interface와 Class상속&quot; data-og-description=&quot;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고 &quot; data-og-host=&quot;lab.cliel.com&quot; data-og-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/i1iYS/hyVqpPu2Ag/cLT1YOQk5tofRqupUcRQ91/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bQYHQR/hyVm2uQI2n/dZNmboyKfcXNBKrx50a0r0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/tWgzp/hyVqhjC3Ka/96VPTdN78p9Rrgi3VXEwn0/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://lab.cliel.com/entry/C-12%EC%99%80-NET-8-6-Interface%EC%99%80-Class%EC%83%81%EC%86%8D&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/i1iYS/hyVqpPu2Ag/cLT1YOQk5tofRqupUcRQ91/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/bQYHQR/hyVm2uQI2n/dZNmboyKfcXNBKrx50a0r0/img.png?width=800&amp;amp;height=58&amp;amp;face=0_0_800_58,https://scrap.kakaocdn.net/dn/tWgzp/hyVqhjC3Ka/96VPTdN78p9Rrgi3VXEwn0/img.png?width=360&amp;amp;height=214&amp;amp;face=0_0_360_214');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C# 12와 .NET 8] 6. Interface와 Class상속&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글을 통해서는 OOP(Object-Oriented Programming)을 사용한 하나의 개체에서 다른 새로운 type을 상속하는 기본 개념에 대해 알아볼 것입니다. 또한 generic을 사용하여 어떻게 code를 안전하게 만들고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;lab.cliel.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack&amp;lt;T&amp;gt;또는 Queue&amp;lt;T&amp;gt; collection은 근본적인 특성상 정렬이 불가능하며 Dictionary 혹은 set에서는 정렬이 가능할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 때로는 자동적으로 정렬된 collection, 즉, item을 추가하거나 삭제하면 그것대로 순서대로 정렬된 item을 유지하는 것을 사용하는 것이 유용할 때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 선택가능한 여러 자동정렬 collection들이 존재합니다. 이들 정렬된 collection의 차이는 미묘하지만 application에서 사용될 때 필요한 memory와 성능에는 큰 영향이 있을 수 있습니다. 따라서 필요에 따라 가장 적합한 것을 선택할 수 있도록 신중해질 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택가능한 자동정렬 collection에는 다음과 같은 것들이 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;Collection&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;SortedDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;key에 의해 정렬된 key/값 쌍의 collection입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;SortedList&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;key에 의해 정렬된 key/값 쌍의 collection입니다. SortedDictionary&amp;lt;TKey, TValue&amp;gt;와 비교하여 열거하는 성능은 비슷하지만 더 적은 memory를 사용하며 정렬되어 있지 않은 상태에서는 insert나 remove동작은 느리지만 정렬되어 있다면 더 빠르게 동작합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;SortedSet&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;순서대로 정렬된 상태를 유지하는 고유한 개체의 collection입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) 더 전문화된 collection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 collection 외 더 특별한 상황을 위한 다른 collection들이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Bit 값을 사용하는 compact array&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Collections.BitArray collection은 bool형식의 bit값에 대한 array를 관리하며 true라면(값이 1이라면) bit가 on이고 false라면(값이 0이라면) bit가 off임을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Efficient list&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Collections.Generics.LinkedList&amp;lt;T&amp;gt; collection은 이중연결 list를 나타내는 것으로 모든 item은 전의 item과 후의 item에 대한 참조를 갖고 있습니다. 이러한 구조로 list의 중간쯤에 자주 item에 대한 insert나 remove가 이루어지는 상황이라면 List&amp;lt;T&amp;gt;와 비교해 더 우월한 성능을 나타내며 구조의 특성상 LinkedList&amp;lt;T&amp;gt;에서 item은 memory에서 재정렬될 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) Read-only , Immutable, frozen collection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generic collection interface를 보면 IsReadOnly라는 속성이 존재함을 알 수 있습니다. 이 속성은 method에 collection을 전달할때 해당 collection에 어떠한 변경도 허용하지 말아야 할 경우 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;T&amp;gt;와 Dictionary&amp;lt;TKey, TValue&amp;gt;와 같은 collection는 AsReadOnly method를 갖고 있는데 이를 통해 본래 collection을 참조하는 ReadOnlyCollection&amp;lt;T&amp;gt;를 생성하게 됩니다. ReadOnlyCollection&amp;lt;T&amp;gt;도 ICollection&amp;lt;T&amp;gt; interface를 구현하고 있기 때문에 Add나 Remove와 같은 method를 갖고 있지만 해당 method의 호출을 시도하면 NotImplementedException을 throw하게 됩니다. ReadOnlyCollection&amp;lt;T&amp;gt;는 참조이므로 본래 collection에서 item의 추가나 삭제와 같은 변경사항이 발생하면 당연히 ReadOnlyCollection&amp;lt;T&amp;gt;에도 그대로 변경사항이 적용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708586342751&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.OutputEncoding = System.Text.Encoding.UTF8;

void UseDictionary(IDictionary&amp;lt;string, string&amp;gt; dictionary)
{
    try
    {
        dictionary.Add(key: &quot;A7&quot;, value: &quot;홍길석&quot;); 
    }
    catch (NotSupportedException)
    {
        Console.WriteLine(&quot;collection이 읽기 전용임.(item을 추가할 수 없음).&quot;);
    }
    
    Console.WriteLine($&quot;전체 요소 수 : {dictionary.Count}&quot;);
}

Dictionary&amp;lt;string, string&amp;gt; employees = new Dictionary&amp;lt;string, string&amp;gt;();
employees.Add(&quot;A1&quot;, &quot;홍길순&quot;);
employees.Add(&quot;A2&quot;, &quot;홍길동&quot;);
employees.Add(&quot;A3&quot;, &quot;홍길영&quot;);

UseDictionary(employees);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 method에서 dicttionary type을 전달받아 새로운 item을 추가하도록 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;method에서는 IDictionary type의 인수가 정의되어 있습니다. 특정 type보다 interface를 사용하면 program에 더 많은 유연성을 부여할 수 있습니다. 예제에서도 IDictionary interface를 사용해 Dictionary&amp;lt;TKey, TValue&amp;gt;나 ReadOnlyDictionary&amp;lt;TKey, TValue&amp;gt;등 해당 interface를 구현한 모든 type을 잔달받을 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 method안에서 정상적으로 item을 추가하게 되었으므로 Count의 수는 늘어나게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn8UG0/btsE93qyyEW/SwMz5T65R1VsULHKlDZKM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn8UG0/btsE93qyyEW/SwMz5T65R1VsULHKlDZKM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn8UG0/btsE93qyyEW/SwMz5T65R1VsULHKlDZKM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn8UG0%2FbtsE93qyyEW%2FSwMz5T65R1VsULHKlDZKM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 AsReadOnly method를 통해 읽기전용 collection을 전달해 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708587060006&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UseDictionary(employees.AsReadOnly());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행결과는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mAEyA/btsE98ZEHPT/7UhKK3SdYfqsNhE75dl36K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mAEyA/btsE98ZEHPT/7UhKK3SdYfqsNhE75dl36K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mAEyA/btsE98ZEHPT/7UhKK3SdYfqsNhE75dl36K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmAEyA%2FbtsE98ZEHPT%2F7UhKK3SdYfqsNhE75dl36K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;153&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method에서는 item을 추가할 수 없기 때문에 결과에서의 Count값은 기존과 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Immutable collection은 말 그대로 불변의 collection을 생성합니다. 다시 말해 collection의 member는 바뀔 수 없고 따라서 추가나 삭제도 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Immutable collection은 System.Collections.Immutable namespace를 import 하게 되면 IEnumerable&amp;lt;T&amp;gt;를 구현하는 모든 collection은 여섯 개의 확장 method가 주어지는데 이를 통해 immutable list나 dictionary, hash, set 등으로 변환하는 것으로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WorkingWithCollections의 Program.cs에서 System.Collections.Immutable namespace를 import 하고 cities list를 immutable list로 변환한 뒤 새로운 item을 아래와 같이 추가하도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688612828537&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;string&amp;gt; cities = new();
cities.Add(&quot;Daegu&quot;);
cities.Add(&quot;Seoul&quot;);
cities.Add(&quot;Busan&quot;);

ImmutableList&amp;lt;string&amp;gt; immutableCities = cities.ToImmutableList();
ImmutableList&amp;lt;string&amp;gt; newList = immutableCities.Add(&quot;Jeju&quot;);
Output(&quot;Immutable list of cities:&quot;, immutableCities);
Output(&quot;New list of cities:&quot;, newList);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 다음과 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfFISq/btsEZSCAZ3l/emKiQq39VPJBDKUToea5qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfFISq/btsEZSCAZ3l/emKiQq39VPJBDKUToea5qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfFISq/btsEZSCAZ3l/emKiQq39VPJBDKUToea5qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfFISq%2FbtsEZSCAZ3l%2FemKiQq39VPJBDKUToea5qK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;191&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 cities의 immutable list는 바뀌지 않음에 주목해야 합니다. Add method를 통해 item을 추가하게 되면 자신을 바꾸는 것이 아닌 기존의 것에서 새로운 것이 추가된 상태의 list가 새롭게 반환됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;성능향상을 위해 많은 application에서는 공통적으로 접근되는 개체의 공유복사본을 cache에 복사하고 여러 thread에서의 안정적인 동작이 필요한 경우 이를 immutable로 만들거나 concurrent collection type을 사용하게 됩니다. 이와 관련해서는 아래 link에서 자세한 내용을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot;&gt;System.Collections.Concurrent Namespace | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688613328311&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;System.Collections.Concurrent Namespace&quot; data-og-description=&quot;Provides several thread-safe collection classes that should be used in place of the corresponding types in the System.Collections and System.Collections.Generic namespaces whenever multiple threads are accessing the collection concurrently. However, access&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bBU9HE/hyTeahupd0/ekCX75UVI8D3ZCS7H6yxwk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=net-7.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bBU9HE/hyTeahupd0/ekCX75UVI8D3ZCS7H6yxwk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.Collections.Concurrent Namespace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides several thread-safe collection classes that should be used in place of the corresponding types in the System.Collections and System.Collections.Generic namespaces whenever multiple threads are accessing the collection concurrently. However, access&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generic collection은 설계된 방식으로 인해 잠재먹인 성능 issue를 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로는 dictionary에서 key와 값에 사용되는 type이 어떤 것이냐에 따라 성능에 큰 영향을 준다는 것입니다. 거의 모든 type을 다 받아들일 수 있기 때문에 사실상 성능을 최적화하게에는 무리가 있었던 것입니다. 실세 여기에 사용되는 type은 string과 int가 가장 흔한데 만약 .NET team이 type을 이것으로만 한정하게 된다면 아마도 최적화가 가능했을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째로 collection은 동적입니다. 이는 collection에 새로운 item이 언제든지 추가될 수 있고, 또 언제든지 제거될 수 있다는 것을 의미합니다. Collection이 더이상 변경되지 않는다는것을 보장할 수 있다면 이 역시 최적화가 가능한 부분이기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 .NET 8에서는 frozen collection이라는 새로운 개념을 도입하게 되었습니다.&amp;nbsp;현재 frozen collection은 향후에 더 추가될 수 있지만 FrozenDictionary&amp;lt;TKey, TValue&amp;gt;와 FrozenSet&amp;lt;T&amp;gt;가 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708589391317&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Frozen;

Console.OutputEncoding = System.Text.Encoding.UTF8;

Dictionary&amp;lt;string, string&amp;gt; employees = new Dictionary&amp;lt;string, string&amp;gt;();
employees.Add(&quot;A1&quot;, &quot;홍길순&quot;);
employees.Add(&quot;A2&quot;, &quot;홍길동&quot;);
employees.Add(&quot;A3&quot;, &quot;홍길영&quot;);

FrozenDictionary&amp;lt;string, string&amp;gt; frozenEmployee = employees.ToFrozenDictionary();

foreach(var item in frozenEmployee)
{
    Console.WriteLine(item.Key + &quot;:&quot; + item.Value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 기존 employees dictionary를 frozen dictionary로 변환한 뒤 각 item을 표시하도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 내용에 따라 Add method의 동작은 type에 따라 달라진다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;T&amp;gt; : 새로운 item은 list의 끝에 추가합니다.&lt;/li&gt;
&lt;li&gt;Dictionary&amp;lt;TKey, TValue&amp;gt; : 새로운 item은 내부구조에 따라 결정된 위치에 추가합니다.&lt;/li&gt;
&lt;li&gt;ReadOnlyCollection&amp;lt;T&amp;gt; : item추가가 지원되지 않으며 추가를 시도하는 경우 예외를 발생시키게 됩니다.&lt;/li&gt;
&lt;li&gt;ImmutableList&amp;lt;T&amp;gt; : 새로 추가된 item을 포함한 새로운 list를 반환합니다. 본래 list에는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;ImmutableDictionary&amp;lt;TKey, TValue&amp;gt; : 새로 추가된 item을 포함한 새로운 list를 반환합니다. 본래 list에는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;FrozenDictionary&amp;lt;TKey, TValue&amp;gt; : 해당 method가 존재하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;frozen collection에 관한 더 많은 정보는 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot;&gt;System.Collections.Frozen Namespace | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708589969840&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;System.Collections.Frozen Namespace&quot; data-og-description=&quot;Explore all classes and interfaces of the System.Collections.Frozen namespace.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zbUan/hyVmQnKgrT/CAFpMDnkt9PTNmJwzEZ0vk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-8.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zbUan/hyVmQnKgrT/CAFpMDnkt9PTNmJwzEZ0vk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.Collections.Frozen Namespace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore all classes and interfaces of the System.Collections.Frozen namespace.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) Collection 표현식을 사용한 초기화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 11이전에는 collection, array, span등을 초기화 할때 int형인 경우 다음과 같은 방식을 사용했었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708645814464&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] myArray = { 1, 2, 3 };
List&amp;lt;int&amp;gt; MyList = new() { 1, 2, 3 };
Span&amp;lt;int&amp;gt; MyySpan = stackalloc int[] { 1, 2, 3 };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 C# 12부터는 collection, array, span등을 초기화 할 수 있는 새로운 문법을 도입하게 되어 대괄호를 사용한 일관성있는 초기화가 가능해쪘습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708645976885&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] myArray = [ 1, 2, 3 ];
List&amp;lt;int&amp;gt; MyList =  [ 1, 2, 3 ];
Span&amp;lt;int&amp;gt; MyySpan = [ 1, 2, 3 ];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) Collection에 대한 모범사례&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collection을 처리해야 하는 method를 만들어야 한다고 가정해 보면 예를 들어 최대한의 유용성을 활용하기 위해 입력 매개변수를 IEnumerable&amp;lt;T&amp;gt;로 선언하여 method를 generic으로 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688630836377&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ProcessCollection&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; collection)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 여기에 int나 string 또는 사용자 type 등 IEnumerable&amp;lt;T&amp;gt;를 구현하는 다른 모든 type을 포함하는 list, queue, stack과 같은 array를 전달할 수 있습니다. 그러나 해당 method로 모든 collection을 전달하고자 하는 이런 유연성은 꽤 많은 처리비용을 요구할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IEnumerable&amp;lt;T&amp;gt;로 하면 이에 대한 성능저하의 문제점으로 개체를 heap에 할당해야 하는 것으로 기인합니다. 이러한 memory할당을 피하려면 위의 method에서 아래와 같이 구체적인 type을 명시해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688631970800&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ProcessCollection&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; collection)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 IEnumerator&amp;lt;T&amp;gt;에서 참조 type을 반환하는 GetEnumerator method대신 구조체를 반환하는 List&amp;lt;T&amp;gt;의 GetEnumerator method를 사용하게 됩니다. 이것만으로 2~3배 정도의 성능상향을 기대할 수 있으며 그만큼 memory로 더 적게 사용합니다. 성능과 관련된 다른 모든 권장사항들이 그렇듯이 실제 code를 test 함으로써 성능에 관한 이점을 확인해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Span, Index 그리고 range&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Core 2.1에서 Microsoft의 목표 중 하나는 성능 및 resource의 사용을 개선하는 것이었으며 이에 대한 핵심 .NET 기능이 Span&amp;lt;T&amp;gt; type입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Span을 통한 memory 효율성 증가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array를 다룰 때 어떤 경우에는 기존의 것에서 일부만 따로 떼어낸 새로운 복사본이 필요할 때가 있습니다. 그런데 실제 이와 같은 복사본을 생성하는 것은 memory안에 동일한 같은 개체를 만들게 되므로 그리 효휼적이라고 할 수는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 기존 array에서 전체 혹은 부분적으로 동일한 array를 대신 사용해야 한다면 본래 array에 대한 거울과 같은 span을 사용할 수 있습니다. Memory사용율과 성능면에서 더 효율적인 span은 오로지 array에 대해서만 사용할 수 있으며 memory가 연속되어야 하므로 collection에서는 사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Index type을 통한 위치 식별&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 8.0에서는 array안에서 item의 위치와 2개의 index를 사용해 item의 범위를 식별하기 위한 2가지 기능을 도입했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list의 개체는 indexer에 int값을 전달함으로써 다음과 같이 접근할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688696667467&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int index = 3;
Person p = people[index];
char letter = name[index];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Index type은 아래와 같이 사용할 수 있으며 position을 식별하기 위한 공식적인 방법이고 또한 아래와 같이 끝에서부터 계산할 수 있는 fromEnd를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688696933593&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Index i1 = new(value: 3);
Index i2 = 3;

Index i3 = new(value: 5, fromEnd: true);
Index i4 = ^5;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index를 정의하기 위해서 예제와 같이 명시적으로 숫자를 사용하거나 new(value: 3)과 같이 사용할 수 있습니다. 이때 index는 시작부터 3번째를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝에서 부터 counting 되도록 하는 경우 fromEnd를 사용하거나 ^(caret) 문자를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Range type으로 범위 식별하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Range type은 범위의 시작과 끝을 나타내기 위해 Index값을 사용하며 생성자 혹은 정적 method를 사용해 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688697271408&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Range r1 = new(start: new Index(3), end: new Index(7));
Range r2 = new(start: 3, end: 7);
Range r3 = 3..7;
Range r4 = Range.StartAt(3);
Range r5 = 3..;
Range r6 = Range.EndAt(3); 
Range r7 = ..3;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 range type은 위 예제에서 첫 번째와 두 번째같이 생성자를 통해 생성할 수 있습니다. 세 번째는 C# 8부터 사용가능한 방식이며 네번째는 정적 method를 사용한 것으로 세번째 index부터 끝까지를 의미합니다. 다섯번째는 네번째와 의미가 같고 여섯번째는 끝에서 부터 세번째 index를 의미합니다. 일곱 번째는 여섯 번째와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장 method는 문자열값(내부적으로 char 배열을 사용), int array 그리고 span에 추가되어 범위를 더 쉽게 다룰 수 있습니다. 이들 확장 method는 매개변수로 범위를 수용하고 Span&amp;lt;T&amp;gt;를 반환함으로써 memory를 더 효휼적으로 사용할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Indexe, range,&amp;nbsp; 그리고 span사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Span을 반환하는 index와 range는 아래와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy08 solution에서 WorkingWithRanges이름의 Console app project를 생성하고 Program.cs에서 기존의 문을 모두 삭제한 뒤 string type의 substring method에서 범위를 지정하여 이름을 추출하고 비교하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688712901189&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string name = &quot;Gildong hong&quot;;

//성과 이름의 길이를 확인합니다.
int lengthOfFirst = name.IndexOf(' ');
int lengthOfLast = name.Length - lengthOfFirst - 1;

//성과 이름을 분리해 가져옵니다.
string firstName = name.Substring(startIndex: 0, length: lengthOfFirst);
string lastName = name.Substring(startIndex: name.Length - lengthOfLast, length: lengthOfLast);

Console.WriteLine($&quot;First name: {firstName}, Last name: {lastName}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 span을 사용해 구현하면 다음과 같이 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688712937221&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReadOnlySpan&amp;lt;char&amp;gt; nameAsSpan = name.AsSpan();
ReadOnlySpan&amp;lt;char&amp;gt; firstNameSpan = nameAsSpan[0..lengthOfFirst];
ReadOnlySpan&amp;lt;char&amp;gt; lastNameSpan = nameAsSpan[^lengthOfLast..^0];

Console.WriteLine(&quot;First name: {0}, Last name: {1}&quot;, arg0: firstNameSpan.ToString(), arg1: lastNameSpan.ToString());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하면 결과를 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf8JpE/btsE40sT9mC/rcDMhNdfi2uZIjXByD6wH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf8JpE/btsE40sT9mC/rcDMhNdfi2uZIjXByD6wH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf8JpE/btsE40sT9mC/rcDMhNdfi2uZIjXByD6wH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf8JpE%2FbtsE40sT9mC%2FrcDMhNdfi2uZIjXByD6wH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;82&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. Network Resource&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부와의 통신이 필요한 application이라면 network와 관련된 여러 가지 작업이 필요합니다. 아래 표는 그런 경우 사용가능한 type을 나열한 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 225px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 17px;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 17px;&quot;&gt;Type&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 40px;&quot;&gt;System.Net&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 40px;&quot;&gt;Dns, Uri, Cookie, WebClient, IPAddress&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 40px;&quot;&gt;DNS server, URIs, IP address관련및 기타&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 34px;&quot;&gt;System.Net&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 34px;&quot;&gt;FtpStatusCode, FtpWebRequest, FtpWebResponse&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 34px;&quot;&gt;FTP server 관련&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 40px;&quot;&gt;System.Net&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 40px;&quot;&gt;HttpStatusCode, HttpWebRequest, HttpWebResponse&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 40px;&quot;&gt;HTTP server와 관련된 것으로서 website와 service등을 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 40px;&quot;&gt;System.Net.Http&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 40px;&quot;&gt;HttpClient, HttpMethod, HttpRequestMessage, HttpResponseMessage&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 40px;&quot;&gt;HTTP server와 관련된 것으로서 website와 service등을 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 34px;&quot;&gt;System.Net.Mail&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 34px;&quot;&gt;Attachment, MailAddress, MailMessage, SmtpClient&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 34px;&quot;&gt;Mail을 발송하는 SMTP server관련&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 28.6821%; height: 20px;&quot;&gt;System.Net .NetworkInformation&lt;/td&gt;
&lt;td style=&quot;width: 37.9845%; height: 20px;&quot;&gt;IPStatus, NetworkChange, Ping, TcpStatistics&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;저수준 network protocol관련&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) URI, DNS, IP 주소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network resource를 사용하는 예를 작성하기 위해 csStudy08 solution에서 WorkingWithNetworkResources이름의 Console App을 생성하고 Program.cs에서 기존의 문을 모두 삭제한 뒤 아래의 namespace를 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688714368403&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Net;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 사용자에게 website주소의 입력을 요청하고 주소가 입력되면 Uri type을 사용해 해당 주소를 HTTP, FTP 등의 scheme, port 번호, host 등 각각의 요소로 분해하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688714626124&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.Write(&quot;Enter a valid web address (or press Enter): &quot;);

string? url = Console.ReadLine();
if (string.IsNullOrWhiteSpace(url))
{
    url = &quot;http://cliel.com/&quot;;
}

Uri uri = new(url);
Console.WriteLine($&quot;URL: {url}&quot;);
Console.WriteLine($&quot;Scheme: {uri.Scheme}&quot;);
Console.WriteLine($&quot;Port: {uri.Port}&quot;);
Console.WriteLine($&quot;Host: {uri.Host}&quot;);
Console.WriteLine($&quot;Path: {uri.AbsolutePath}&quot;);
Console.WriteLine($&quot;Query: {uri.Query}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 정확한 website주소를 입력하게 되면 아래와 같은 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XgJBM/btsE8AmpTZQ/RZ1ZsJtiMXEMrpg2K1V3P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XgJBM/btsE8AmpTZQ/RZ1ZsJtiMXEMrpg2K1V3P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XgJBM/btsE8AmpTZQ/RZ1ZsJtiMXEMrpg2K1V3P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXgJBM%2FbtsE8AmpTZQ%2FRZ1ZsJtiMXEMrpg2K1V3P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;170&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 입력된 website주소에 대한 ip를 확인할 수 있는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688714761755&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IPHostEntry entry = Dns.GetHostEntry(uri.Host);
Console.WriteLine($&quot;{entry.HostName} has the following IP addresses:&quot;);
foreach (IPAddress address in entry.AddressList)
{
    Console.WriteLine($&quot; {address} ({address.AddressFamily})&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 정확한 website주소를 입력하면 다음의 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIdR0I/btsEZbWNVLS/eUzqFKHnhVFlWJmKaKW5P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIdR0I/btsEZbWNVLS/eUzqFKHnhVFlWJmKaKW5P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIdR0I/btsEZbWNVLS/eUzqFKHnhVFlWJmKaKW5P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIdR0I%2FbtsEZbWNVLS%2FeUzqFKHnhVFlWJmKaKW5P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;205&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Server로 ping 보내기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 입력된 website주소의 web server 상태는 ping응답을 받음으로써 확인할 수 있습니다. Program.cs에서 이를 구현하기 위한 code를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688715147723&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;Host: {uri.Host}&quot;);

Ping ping = new();
Console.WriteLine(&quot;Pinging server. Please wait...&quot;);

PingReply reply = ping.Send(uri.Host);
Console.WriteLine($&quot;{uri.Host} was pinged and replied: {reply.Status}.&quot;);

if (reply.Status == IPStatus.Success)
{
    Console.WriteLine(&quot;Reply from {0} took {1:N0}ms&quot;, arg0: reply.Address, arg1: reply.RoundtripTime);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 실행하고 정확한 website주소를 입력해 보면 다음의 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6ea3s/btsE3pGuQ3O/jfRpXQPzxJGwty0hlOH121/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6ea3s/btsE3pGuQ3O/jfRpXQPzxJGwty0hlOH121/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6ea3s/btsE3pGuQ3O/jfRpXQPzxJGwty0hlOH121/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6ea3s%2FbtsE3pGuQ3O%2FjfRpXQPzxJGwty0hlOH121%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;173&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>array</category>
      <category>c#</category>
      <category>Collection</category>
      <category>index</category>
      <category>number</category>
      <category>pattern matching</category>
      <category>Regular Expression</category>
      <category>span</category>
      <category>TEXT</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/598</guid>
      <comments>https://cliel.tistory.com/entry/C-12%EC%99%80-NET-8-8-%EA%B3%B5%EC%9A%A9-NET-Type#entry598comment</comments>
      <pubDate>Fri, 23 Feb 2024 12:09:55 +0900</pubDate>
    </item>
    <item>
      <title>[C# 12와 .NET 8] 7. .NET Packaging과 배포</title>
      <link>https://cliel.tistory.com/entry/XFile</link>
      <description>&lt;div id=&quot;queryBox&quot; style=&quot;color: #333333; text-align: start;&quot; data-tiara-layer=&quot;query-box&quot;&gt;
&lt;div id=&quot;queryButtons&quot; style=&quot;background-color: #ffffff;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;C# 키워드가 .NET 유형과 어떻게 관련되어 있는지, 네임스페이스와 어셈블리 간에는 어떤 관계가 있는지에 대한 것을 알면 C#언어를 이해하는데 도움이 될 수 있습니다. 또한 .NET library에서 이전 .NET framework library를 어떻게 사용하고 이식할 수 있는지의 여부도 함께 알아볼 것이며 이를 통해 .NET을 좀더 폭넓게 활용할 수 있을 것입니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;1. .NET 8&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;.NET에서는 Base Class Library (BCL) API를 통해 수 많은 기능들을 제공하고 있습니다. .NET Standard를 통해서는 다른 전체 .NET platform간 이런 기능들을 재사용할 수 있도록 하고 있는데 때문에 &lt;/span&gt;&lt;span style=&quot;color: #000000; background-color: #fafbfc; letter-spacing: 0px;&quot;&gt;지금의 .NET과 이전의 것을 적절히 이해해둘 필요가 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;.NET Standard 2.0의 최소규격을 지원하는 .NET Core 2.0부터는 .NET Core의 첫번째 version에는 없던 많은 API를 제공하고 있으므로 여기서 부터가 중요한 전환점이 될 수 있습니다. .NET Framework 개발자가 최신의 개발과 관련하여 15년간 이용할 수 있었던 library와 application은 지금 .NET과 통합되었고 Windows는 물론 macOS와 다양한 Linux에서 동작할 수 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;.NET Standard 2.1은 3,000여개의 새로운 API가 추가되었는데 이들 중 몇몇은 이전 version과의 호환성을 깨는 runtime의 변경이 필요했습니다. 따라서 .NET Framework 4.8은 .&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;NET Standard 2.0만을 구현하며 .NET Core 3.0, Xamarin, Mono, 그리고 Unity는 .NET Standard 2.1을 구현하게 되었습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.NET 5에 들어서는 project에서 .NET 5만을 사용한다면 .NET Standard의 필요성을 제거할 수 있게 되었고 이는 .NET 6, .NET 7에서도 동일합니다. 하지만 여전히 .NET Framework project와 Xamarin mobile app을 생성해야 하는 경우도 있으므로 지금도 .NET Standard 2.0과 2.1 class library를 생성할 수 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;현재 .NET 6와 .NET 7은 자체 .NET MAUI를 통해 완벽하게 mobile과 desktop app을 지원하고 있는데 이로서 .NET Standard의 필요성은 점차 감소하고 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;아래는 지난 5년동안의 .NET의 진행상황을 요약하기 위해 주요 .NET Core와 최신 .NET을 동등한 .NET Framework와 비교한 것입니다.&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.NET Core 1.x : 2016년 3월이 현재 version이며 .NET Framework 4.6.1에 비해 훨씬 작은 API를 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;.NET Core 2.x : .NET Framework 4.7.1과 동일하게 .NET Standard 2.0을 구현함으로서 동등한 API에 도달하였습니다.&lt;/li&gt;
&lt;li&gt;.NET Core 3.x :&amp;nbsp; .NET Framework 4.8에서 구현하지 않은 .NET Standard 2.1을 구현함으로서 월씬 더 많은 API를 지원하게 되었습니다.&lt;/li&gt;
&lt;li&gt;.NET 5 : 상당히 향상된 성능과 함께 더 .NET Framework 4.8에 비해 더 많은 API를 지원합니다.&lt;/li&gt;
&lt;li&gt;.NET 6 : 지속적인 성능 향상과 API확장을 이루었으며 2022년 5월에 추가된 .NET MAUI를 통해 mobile app을 지원할 수 있게 되었습니다.&lt;/li&gt;
&lt;li&gt;.NET 7 : .NET MAUI로 mobile app지원을 위한 최종 통합 version입니다.&lt;/li&gt;
&lt;li&gt;.NET 8 : 지속적인 성능 향상이 이루어지고 있으며 모든 신규 project에 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) .NET Core 1.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2016년 6월에 release 되었으며 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;ASP.NET Core를 사용하는 Linux용&amp;nbsp;&lt;/span&gt;web과 cloud application 그리고 service를 포함한 최신의 cross-platform app개발을 위해 안정적인 API를 제공하는데 초점이 맞춰진 version입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) .NET Core 1.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2016년 11월에 release 되었으며 bug를 수정하고 지원되는 Linux의 배포한 수를 증가되었으며 .NET Standard 1.6를 지원하게 되었습니다. 또한 성능의 증가도 이루어졌는데 특히 web app과 service를 위한 ASP.NET Core에서 큰 성능향상이 이루어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) .NET Core 2.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2017년 8월에 release되었으며 .NET Standard 2.0을 구현하였고 .NET Framework library를 참조하고 더 많은 성능향상을 이루었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) .NET Core 2.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2018년 5월에 release 되었으며 확장가능한 tooling system(사용 목적에 따라 각각의 도구(기능)를 선택/조합하는 체계)에 중점을 두었습니다. Span&amp;lt;T&amp;gt;, 암호화 및 압축 그리고 예전 windows application을 이식하는데 필요한 20,000가지의 추가적인 API와 함께 Windows Compatibility Pack, Entity Framework Core값 변환, LINQ GroupBy 변환, data seeding, query type과 같은 새로운 type이 추가되었으며 아래 표의 기능을 포함해 전반적인 성능 향상이 이루어졌습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 77px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Spans&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Brotli compression&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Brotli algorithm을 통한 압축방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;EF Core Lazy loading&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;lazy(지연) loading 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core Data seeding&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;-&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) .NET Core 2.2&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2018년 12월에 release 되었으며 runtime에서의 진단향상, 선택적 계층 compile등에 중점을 두었으며 ASP.NET Core과 Entity Framework에 NetTopologySuite (NTS) library, query tag, 소유 entity collection 등의 type을 사용하는 공간 data지원등 새로운 기능이 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) .NET Core 3.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2019년 9월에 release 되었으며 Windows forms, WPF(Windows Presentation Foundation) 그리고 Entity Framework 6.3, side-by-side와 app-local 개발, fast JSON reader, serial port 접근, IoT(Internet of Things) solution을 위한 pinout 접근등을 사용하는 desktop application구축하는데 필요한 지원에 중점을 두었습니다. 또한 아래 기능을 포함해 기본 계층 compile이 추가되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Embedding .NET in-app&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Index and Range&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Text.Json&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;고성능 JSON 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) .NET Core 3.1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2019년 12월에 release 되었으며 bug수정과 개선에 초점이 맞춰져 Long Term Support (LTS)가 되었고 이로서 2022년 12월까지 지원이 이루어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) .NET 5.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2020년 10월에 release 되었으며 mobile을 제외한 다양한 .NET platform을 통합하는데 주력하였습니다. platform을 개선하고 아래 표의 기능을 포함해 전반적으로 성능향상이 이루어졌습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Half type&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #161616; text-align: start;&quot;&gt;반정밀도 부동 소수점 숫자&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;정규 표현식 성능 향상&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;정규 표현식 성능 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Text.Json 향상&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;고성능 JSON 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core generated SQL&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core Filtered Include&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Entity filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;EF Core의 Humanizer 통합&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) .NET 6.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년 10월에 release 되었으며 data관리를 위한 EF Core로 더 많은 기능과 함께 시간과 날짜에 관한 새로운 type이 추가되었습니다. 또한 아래 표의 기능을 포함해 전반적인 성능향상이 이루어졌습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 177px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;.NET SDK 상태 확인&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;.NET SDK update 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Apple Silicon 지원&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;배포가능한 console application 생성가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Link trim mode 기본 사용&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;app trimming을 통한 app size 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;List&amp;lt;T&amp;gt;에 대한 EnsureCapacity&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;collection용량 확보를 통한 성능 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;RandomAccess를 사용하는 저수준 file API&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;EF Core 구성 규칙&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;새로운 LINQ method&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;TryGetNonEnumeratedCount&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) .NET 7.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 10월에 release 되었으며 mobile platform을 통합하고 string syntax coloring과 IntelliSense 같은 새로운 기능이 추가되었습니다. tar 형식의 압축을 생성하거나 풀어낼 수 있게 되었고 EF Core를 통한 insert와 upate성능이 향상되었습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;[StringSyntax] attribute&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;regular expression syntax coloring 활성화&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;[GeneratedRegex] attribute&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;source generator를 통한 정규 표현식 성능 향상&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Tar archive support&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;tar 압축 file&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;ExecuteUpdate와 ExecuteDelete&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;더욱 효과적인 update및 삭제&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Order와 OrderDescending&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;item 자체 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) .NET 8.0&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 [&amp;quot;orderedList&amp;quot;,{&amp;quot;columnSpan&amp;quot;:&amp;quot;none&amp;quot;,&amp;quot;order&amp;quot;:1},&amp;quot;listItem&amp;quot;,{&amp;quot;closed&amp;quot;:false,&amp;quot;nested&amp;quot;:false}]&quot; data-ke-size=&quot;size16&quot;&gt;C# 12 및 F# 8 언어의 새로운 기능과 개선 사항을 포함하고 있으며 Dynamic Profile-Guided Optimization (PGO)이라는 새로운 코드 생성기를 도입하여 실제 사용에 기반한 코드 최적화를 이룰 수 있습니다. 실제 이를 통해 통해 애플리케이션 성능을 최대 20%까지 향상시킬 수 있습니다. 이 밖에 AI사용을 위한 기능이 강화되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) .NET 5 이후의 성능 향상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 지난 몇년간 .NET에 대한 상당한 성능향상을 이루어 왔습니다. 이와 관련된 상세한 내용은 아래 글을 통해 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot;&gt;Performance Improvements in .NET 5 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687070290757&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 5 - .NET Blog&quot; data-og-description=&quot;Explore many performance improvements, big and small, that have gone into the .NET 5 runtime and core libraries to make apps and services leaner and faster.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dclyKU/hyS2F1FAfc/ypbhCUOibp9KMm9KbpukLk/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dclyKU/hyS2F1FAfc/ypbhCUOibp9KMm9KbpukLk/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 5 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore many performance improvements, big and small, that have gone into the .NET 5 runtime and core libraries to make apps and services leaner and faster.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot;&gt;Performance Improvements in .NET 6 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687070308473&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 6 - .NET Blog&quot; data-og-description=&quot;Take a rip-roarin' tour through hundreds of PRs worth of performance improvements for .NET 6.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Vl1uy/hyS1pMVtQE/l7Nr2q6AuKFOX6V1zgx2t0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Vl1uy/hyS1pMVtQE/l7Nr2q6AuKFOX6V1zgx2t0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 6 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Take a rip-roarin' tour through hundreds of PRs worth of performance improvements for .NET 6.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot;&gt;Performance Improvements in .NET 7 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687070318805&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 7 - .NET Blog&quot; data-og-description=&quot;.NET 7 is fast. Really fast. This post deep-dives into hundreds of performance improvements that contributed to that reality.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btz7Pt/hyS1deEGnm/dx8GV5jXIZYvkx1Ojm88mk/img.png?width=315&amp;amp;height=288&amp;amp;face=0_0_315_288&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btz7Pt/hyS1deEGnm/dx8GV5jXIZYvkx1Ojm88mk/img.png?width=315&amp;amp;height=288&amp;amp;face=0_0_315_288');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 7 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 7 is fast. Really fast. This post deep-dives into hundreds of performance improvements that contributed to that reality.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot;&gt;Performance Improvements in .NET 8 - .NET Blog (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707631183304&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Performance Improvements in .NET 8 - .NET Blog&quot; data-og-description=&quot;.NET 7 was super fast, .NET 8 is faster. Take an in-depth tour through over 500 pull requests that make that a reality.&quot; data-og-host=&quot;devblogs.microsoft.com&quot; data-og-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot; data-og-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMKeXV/hyVf9VC1aL/w2nlsoFfueg2iYrQAxlGz1/img.png?width=1207&amp;amp;height=703&amp;amp;face=0_0_1207_703,https://scrap.kakaocdn.net/dn/3CLaY/hyVf7Dwh4m/RprK3k1yRXaxANesjiZfm0/img.png?width=1225&amp;amp;height=493&amp;amp;face=0_0_1225_493,https://scrap.kakaocdn.net/dn/ctRL9A/hyVfYfvqrM/xr4VQvcKn4jwFh5hS7aIJK/img.png?width=1396&amp;amp;height=415&amp;amp;face=0_0_1396_415&quot;&gt;&lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMKeXV/hyVf9VC1aL/w2nlsoFfueg2iYrQAxlGz1/img.png?width=1207&amp;amp;height=703&amp;amp;face=0_0_1207_703,https://scrap.kakaocdn.net/dn/3CLaY/hyVf7Dwh4m/RprK3k1yRXaxANesjiZfm0/img.png?width=1225&amp;amp;height=493&amp;amp;face=0_0_1225_493,https://scrap.kakaocdn.net/dn/ctRL9A/hyVfYfvqrM/xr4VQvcKn4jwFh5hS7aIJK/img.png?width=1396&amp;amp;height=415&amp;amp;face=0_0_1396_415');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Performance Improvements in .NET 8 - .NET Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 7 was super fast, .NET 8 is faster. Take an in-depth tour through over 500 pull requests that make that a reality.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devblogs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(13) Update를 위한 .NET SDK 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서 부터 Microsoft는 설치 SDK와 runtime의 version을 확인하기 위한 명령을 추가했고 이를 통해 update가 필요하다고 확인되는 경우 이를 알려줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령은 아래와 같으며&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;sdk&amp;nbsp;check&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 경우처럼 update가 필요한 항목을 확인할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;.NET&amp;nbsp;SDKs: &lt;br /&gt;Version&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status &lt;br /&gt;------------------------ &lt;br /&gt;8.0.101&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;&lt;br /&gt;.NET&amp;nbsp;Runtimes: &lt;br /&gt;Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Version&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status &lt;br /&gt;------------------------------------------------------------------------------------- &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5.0.17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;5.0&amp;nbsp;is&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.11&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.11&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.12&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.12&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.13&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.13&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.14&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Patch&amp;nbsp;6.0.26&amp;nbsp;is&amp;nbsp;available. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.26&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.26&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.0.26&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.0.15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.NET&amp;nbsp;7.0&amp;nbsp;is&amp;nbsp;going&amp;nbsp;out&amp;nbsp;of&amp;nbsp;support&amp;nbsp;soon. &lt;br /&gt;Microsoft.AspNetCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.NETCore.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;Microsoft.WindowsDesktop.App&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8.0.1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Up&amp;nbsp;to&amp;nbsp;date. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The&amp;nbsp;latest&amp;nbsp;versions&amp;nbsp;of&amp;nbsp;.NET&amp;nbsp;can&amp;nbsp;be&amp;nbsp;installed&amp;nbsp;from&amp;nbsp;&lt;a href=&quot;https://aka.ms/dotnet-core-download.&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aka.ms/dotnet-core-download.&lt;/a&gt;&amp;nbsp;For&amp;nbsp;more&amp;nbsp;information&amp;nbsp;about&amp;nbsp;.NET&amp;nbsp;lifecycles,&amp;nbsp;see&amp;nbsp;&lt;a href=&quot;https://aka.ms/dotnet-core-support.&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aka.ms/dotnet-core-support.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2 .NET Component&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 아래와 같은 요소로 이루어져 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Language compiler : C#이나 F#등으로 된 source code를 assembly로 저장된 IL code로 변환시킵니다. C# 6.0 이후부터는 compiler를 Roslyn이라는 이름으로 open-source화 하였습니다.&lt;/li&gt;
&lt;li&gt;Common Language Runtime(CoreCLR) : Assembly를 load 하여 해당 assembly에 저장된 IL code를 해당 computer의 CPU에 맞는 native code로 compile한뒤 thread와 memory 같은 관리 resource환경 안에서 code를 실행하게 됩니다.&lt;/li&gt;
&lt;li&gt;Base Class Libraries(BCL 또는 CoreFX) : Application을 구축할 때 공통적인 동작을 수행하기 위해 Nuget을 통하여 package 및 배포된 type의 사전 build 된 assembly를 포함할 수 있습니다. 이를 사용하여 LEGO 부품을 조립하듯 필요한 것을 빠르고 안정적으로 구축할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Assembly, NuGet package, namespace&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly는 type이 filesystem으로 저장된 것입니다. Code를 배포하기 위한 mechanism인데 예를 들어 System.Data.dll assembly는 data를 관리하기 위한 type을 포함하고 있습니다. 다른 assembly의 다른 type을 사용하기 위해서는 해당 assembly가 참조되어 있아야 하며 이때 assembly는 정적(pre-created) 또는 동적(generated at runtime)인 것이 될 수 있습니다. 또한 assembly는 class library인 dll이나 console app과 같은 exe의 단일 file로 compile 될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly는 NuGet package를 통해 내려받기 가능하도록 배포될 수 있으며 이때 다수의 assembly나 다른 resource를 포함할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft NuGet feed는 아래 주소에서 찾을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nuget.org/&quot;&gt;NuGet Gallery | Home&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687135794081&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;NuGet Gallery | Home&quot; data-og-description=&quot;The NuGet Gallery is the central package repository for NuGet, the package manager for .NET.&quot; data-og-host=&quot;www.nuget.org&quot; data-og-source-url=&quot;https://www.nuget.org/&quot; data-og-url=&quot;https://www.nuget.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OSDC2/hyS2DwshbC/ksSW2uVsYNx6oYKd2OU0u0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://www.nuget.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nuget.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OSDC2/hyS2DwshbC/ksSW2uVsYNx6oYKd2OU0u0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;NuGet Gallery | Home&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The NuGet Gallery is the central package repository for NuGet, the package manager for .NET.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nuget.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Namespace&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace는 type의 주소입니다. 단순한 이름대신 주소체계를 사용함으로써 type을 식별하기 위한 mechanism으로 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 예로 .NET에서 System.Web.Mvc namespace의 interface인 IActionFilter는 System.Web.Http.Filters namespace의 IActionFilter에 비해 namespace가 다르므로 다른 interface로 구별될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Assembly 의존성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly가 class library로 compile 되고 다른 assembly에서의 사용을 위해 이를 제공하게 된다면 이때 file은 .dll 확장자를 가지게 되며 독립적으로 실행될 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로, assembly가 application으로 compile 되면 이때는 exe확장자를 가지게 되며 독립적으로 실행가능한 상태가 됩니다. (.NET Core 3.0이전에 console app은 dll file로 compile 되었으며 이때는 dotnet run 명령을 통해 실행될 수 있었습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 assembly는 하나 또는 그 이상의 class library를 의존성으로 참조할 수 있습니다. 하지만 소위 순환참조는 불가능하기 때문에 예를 들어 B라는 assembly는 A라는 assembly에서 이미 B assembly를 참조하고 있다면 A assembly를 참조할 수 없습니다. 때문에 compiler는 순환참조를 유발할 수 있는 의존성 참조를 추가하려고 한다면 경고를 표시하게 됩니다. 대게는 오래전에 작성된 code에서 순환참조에 대한 오류가 나오기 쉬우며 만약 정말 순환참조가 필요한 상황이 온다면 이는 interface로 관련 문제를 해결하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Microsoft .NET Project SDK&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 console application은 Microsoft .NET project SDK에 대한 의존성 참조를 가지고 있습니다. 이 platform은 NuGet package에서 거의 대부분의 application에서 필요로 하는, 예를 들어 System.Int32나 System.tring type과 같은 수천 개의 type들을 포함하고 있으며&amp;nbsp;.NET을 사용할 때는 project file에서 필요로 하는 의존성 assembly와 NuGet package 그리고 platform 등을 참조하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Assembly에서 namespace와 type&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 일반적인 .NET type은 System.Runtime.dll assembly에 있으며 assembly와 namespace는 항상 1:1로 mapping 되지는 않습니다. 단일 assembly라도 다수의 namespace를 포함할 수 있으며 namespace는 다수의 assembly에서 정의될 수 있습니다. 아래 표는 type을 지원하기 위해 사용되는 일부 assembly와 namespace 간 연관성을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Assembly&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;사용 Namespace&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;제공 Type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Runtime.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System,&lt;br /&gt;System.Collections,&lt;br /&gt;System.Collections.Generic&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Int32, String, IEnumerable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Console.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Threading.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Threading&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Interlocked,&lt;br /&gt;Monitor,&lt;br /&gt;Mutex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Xml.XDocument.dll&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;System.Xml.Linq&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;XDocument,&lt;br /&gt;XElement,&lt;br /&gt;XNode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) NuGet package&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 하나가 아닌 몇 개의 package별로 나누어졌고 이를 NuGet이라는 package 관리 기술을 사용해 게시하였습니다. 이렇게 게시된 각각의 package들은 같은 이름의 단일 assembly로 표현됩니다. 예를 들어 System.Collections package는 System.Collections.dll assembly를 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Package는 다음과 같은 장점 및 특징을 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개 feed(&lt;span style=&quot;background-color: #ffffff; color: #161616; text-align: start;&quot;&gt;패키지를 인터넷의 모든 사용자와 공개적으로 공유하는 데 사용)&lt;/span&gt;에 쉽게 게시할 수 있습니다.&lt;/li&gt;
&lt;li&gt;재사용이 가능합니다.&lt;/li&gt;
&lt;li&gt;자체 일정에 따라 게시될 수 있습니다.&lt;/li&gt;
&lt;li&gt;다른 package와 독립적으로 test 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;같은 assembly를 다른 OS와 CPU에 따라 build 한 여러 version을 포함시킴으로써 다른 OS와 CPU를 지원할 수 있습니다.&lt;/li&gt;
&lt;li&gt;하나의 library에 대해서만 특정한 종속성을 가질 수 있습니다.&lt;/li&gt;
&lt;li&gt;참조되지 않은 package는 배포의 일부가 아니므로 app은 더 작아질 수 있습니다. 아래 표는 가장 중요한 package와 해당 package에서 중요한 type의 일부를 나타낸 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Package&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;중요 type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Runtime&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Object, String, Int32, Array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Collections&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;List&amp;lt;T&amp;gt;, Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Net.Http&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;HttpClient, HttpResponseMessage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.IO.FileSystem&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;File, Directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System.Reflection&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Assembly, TypeInfo, MethodInfo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Package는 API를 정의하며 framework는 이런 package를 group화 합니다. 어떠한 package도 없는 framework는 어떠한 API도 정의하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET package는 각각 일련의 framework를 지원합니다. 예를 들어 System.IO.FileSystem package version 4.3.0은 아래 framework를 지원합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.NET Standard, version 1.3 이후&lt;/li&gt;
&lt;li&gt;.NET Framework, version 4.6 이후&lt;/li&gt;
&lt;li&gt;Six Mono와 Xamarin platform (Xamarin.iOS 1.0)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 상세한 내용은 아래 주소를 참조하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot;&gt;NuGet Gallery | System.IO.FileSystem 4.3.0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687154065350&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;nugetgallery:package&quot; data-og-title=&quot;System.IO.FileSystem 4.3.0&quot; data-og-description=&quot;Provides types that allow reading and writing to files and types that provide basic file and directory support. Commonly Used Types: System.IO.FileStream System.IO.FileInfo System.IO.DirectoryInfo System.IO.FileSystemInfo System.IO.File System.IO.Directory&quot; data-og-host=&quot;www.nuget.org&quot; data-og-source-url=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot; data-og-url=&quot;https://nuget.org/packages/System.IO.FileSystem/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xfePT/hyS2xJ8EP8/285bmFhmuXskEGoCFVh2dK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nuget.org/packages/System.IO.FileSystem/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xfePT/hyS2xJ8EP8/285bmFhmuXskEGoCFVh2dK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;System.IO.FileSystem 4.3.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides types that allow reading and writing to files and types that provide basic file and directory support. Commonly Used Types: System.IO.FileStream System.IO.FileInfo System.IO.DirectoryInfo System.IO.FileSystemInfo System.IO.File System.IO.Directory&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nuget.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) Type 사용을 위한 namespace importing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 namespace가 assembly와 type에 어떻게 관련되는지를 알아보도록 하겠습니다. csStudy07 이름의 빈 solution을 생성하고 그 안에 'AssembliesAndNamespaces'이름의 project를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AssembliesAndNamespaces의 Program.cs에서 기존 구문을 모두 삭제하고 아래와 같이 XDocument type의 instance를 생성하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687160824080&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;XDocument doc = new();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 Project를 build 하면 다음과 같은 compiler error message가 발생하게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;The&amp;nbsp;type&amp;nbsp;or&amp;nbsp;namespace&amp;nbsp;name&amp;nbsp;'XDocument'&amp;nbsp;could&amp;nbsp;not&amp;nbsp;be&amp;nbsp;found&amp;nbsp;(are&amp;nbsp;you&amp;nbsp;missing&amp;nbsp;a&amp;nbsp;using&amp;nbsp;directive&amp;nbsp;or&amp;nbsp;an&amp;nbsp;assembly&amp;nbsp;reference?)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;XDocument type은 우리가 compiler에게 해당 type의 namespace가 무엇인지를 알려주지 않았기 때문에 인식될 수 없습니다. 설령 project가 type을 포함하고 있는 assembly를 참조하고 있다고 하더라도 type이름에 namespace에 대한 접두사를 붙이거나 해당 namespace를 import 해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XDocument class의 이름에 mouse를 대면 하단에 풍선도움말이 표시되는데 이때 'Show potential pixes'를 click한뒤 menu에 나오는 System.Xml.Linq를 선택하면 자동적으로 해당 Namespace가 file의 상단에 using문을 추가함으로써 import 될 것입니다. 일단 namespace가 import 되고 나면 namespace에 속하는 모든 type은 code에서 namespace전체를 붙여줄 필요 없이 이름만 입력하는 것으로 사용가능하게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Visual Studio를 사용하는 것이라면 위에서 처럼 type을 입력하는 것만으로 인식가능한 namespace를 자동으로 import 해줄 것입니다.(2022 version부터)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) C# keyword와 .NET type의 연결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 문자열을 다루는 keyword가 2가지가 있는데 하나는 string이고 다른 하나는 String입니다. 이 둘의 차이는 소문자 s와 대문자 S 뿐이며 사실상 용도와 동작에 있어서는 아무런 차이가 없습니다. int와 string과 같은 C# keyword는 class library assembly에 있는 .NET Type의 별칭입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 string keyword를 사용하는 경우에 compiler는 이를 System.String type으로 인식하게 되고 int를 사용하는 경우에는 System.Int32 type으로 인식합니다. 문자열을 다루기 위해 String이라고 하는 것보다는 string으로 사용하는 것이 더 익숙하고 편리한 면이 있으며 Int32보다는 int로 사용하는 것도 같은 이유기 때문에 좀 더 편리한 code의 작성을 위해 별칭으로 따로 만들어둔 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program.cs에서 문자열값을 가진 2개의 변수를 선언하는데 하나는 string으로 다른 하나는 String으로 선언합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687230304876&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s1 = &quot;Hello&quot;;
String s2 = &quot;World&quot;;
Console.WriteLine($&quot;{s1} {s2}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 두 변수는 정확히 동일하게 취급되어 동일한 처리를 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project의 csproj file에서 전역적으로 import 되는 System namespace를 사용하지 않도록 하는 요소를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687230571979&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
	&amp;lt;Using Remove=&quot;System&quot; /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같이 compiler 오류가 발생할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfFAdI/btsEEFi6O2C/po5qYELMTiydwDuyDE2NsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfFAdI/btsEEFi6O2C/po5qYELMTiydwDuyDE2NsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfFAdI/btsEEFi6O2C/po5qYELMTiydwDuyDE2NsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfFAdI%2FbtsEEFi6O2C%2Fpo5qYELMTiydwDuyDE2NsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;99&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 String은 System namespace의 String type을 사용한다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 String과 같은 type대신 C# keyword를 사용하면 위에서와 같이 해당 type의 namespace import를 신경 쓰지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● C# 별칭과 .NET type의 Mapping&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 표는 18가지 C# type keyword와 같이 연결된 .NET Type을 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Keyword&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;.NET type&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;Keyword&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;.NET type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.String&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;char&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Char&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;sbyte&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.SByte&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;byte&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;short&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Int16&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;ushort&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UInt16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;int&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Int32&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;uint&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UInt32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;long&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Int64&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;ulong&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UInt64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;nint&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.IntPtr&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;nuint&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.UIntPtr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;float&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Single&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;double&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Double&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;decimal&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Decimal&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;bool&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;object&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;System.Object&lt;/td&gt;
&lt;td style=&quot;width: 17.6744%;&quot;&gt;dynamic&lt;/td&gt;
&lt;td style=&quot;width: 32.3256%;&quot;&gt;System.Dynamic.DynamicObject&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 .NET Programming 언어 compiler도 같은 방식을 사용할 수 있습니다. 예를 들어 VB.NET의 경우에는 Integer라는 이름의 type을 가지고 있으며 이는 System.Int32의 별칭으로 연결됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● native-sized integers&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 9에서는 native-sized integers에 대한 별칭에 해당하는 nint와 nuint를 도입하였습니다. 여기서 저장되는 integer값의 크기는 platform에 따라 다릅니다. 따라서 32bit process에서는 32bit integer로 저장되며 sizeof()에서는 4byte를 반환하고 64bit process에서는 64bit integer로, sizeof()에서는 8byte를 반환합니다. 해당 별칭의 이름이 IntPtr/UIntPtr인데 이를 통해 해당 별칭은 integer값이 저장된 memory의 위치를 나타낸다는 것을 알 수 있으며 실제 저장되는 type은 process에 따라 System.Int32 또는 System.Int64가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687241682365&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine($&quot;int.MaxValue = {int.MaxValue:N0}&quot;);
Console.WriteLine($&quot;nint.MaxValue = {nint.MaxValue:N0}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvL0M8/btsEEEYQFjE/z2QavkqdIKWnmKzWJXMS3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvL0M8/btsEEEYQFjE/z2QavkqdIKWnmKzWJXMS3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvL0M8/btsEEEYQFjE/z2QavkqdIKWnmKzWJXMS3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvL0M8%2FbtsEEEYQFjE%2Fz2QavkqdIKWnmKzWJXMS3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;88&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type의 위치 표시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서 아래와 같이 string이나 String의 변수를 생성하고&lt;/p&gt;
&lt;pre id=&quot;code_1687242455189&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string s;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 type을 선택한 뒤 mouse오른쪽 button을 눌러 'Go to Definition'을 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddpHvK/btsEEDZUF7y/ktLM2IUvK6YUD0Yu9hrrAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddpHvK/btsEEDZUF7y/ktLM2IUvK6YUD0Yu9hrrAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddpHvK/btsEEDZUF7y/ktLM2IUvK6YUD0Yu9hrrAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddpHvK%2FbtsEEDZUF7y%2FktLM2IUvK6YUD0Yu9hrrAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1258&quot; height=&quot;218&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code editor를 보면 assembly file명은 'System.Runtime.dll'이지만 class는 System namespace에 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 지난 글에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lab.cliel.com/entry/C-12&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[.NET/C#] - [C# 12와 .NET 8] 2. C#&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Runtime.dll에 포함된 type은 0 임을 언급한 바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System.Runtime.dll assembly가 포함하고 있는 것은 type-forwarder입니다. 이들은 assembly안에 포함되어 있는 것처럼 표시하지만 다른 곳에 구현되어 있는 특별한 type입니다. 이 경우 string은 고도로 최적화된 code를 사용해 .NET runtime내부에 깊이 구현되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) .NET Standard를 사용한 legacy platform과의 code공유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Standard 이전에는 PCL(Portable Class Libraries)가 있었습니다. PCL에서는 code의 library를 만들고 명시적으로 library에서 지원할 Xamarin, Silverlight, Windows 등의 platform을 지정할 수 있었고, 그렇게 하면 library는 지정된 platform에 의해서 지원되는 API와의 상호작용에 사용될 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Microsoft는 이것이 지속가능하리라고 판단하지 않고 미래 모든 .NET platform에서 지원할 단일 API인 .NET Standard를 생성하게 됩니다. .NET Standard에 이어 .NET Standard 2.0에서는 중요한 최신 .NET platform의 통합을 시도했고 2019년 말에 발표된 .NET Standard 2.1에서는 .NET Core 3.0과 그해 Xamarin에서만이 새로운 기능을 지원했습니다. (앞으로 언급되는 .NET Standard는 .NET Standard 2.0을 의미합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Standard는 HTML5와 같이 둘다 platform이 지원할 수 있는 표준에 해당합니다. Google의 Chrome browser나 Microsoft의 Edge browser가 HTML5의 표준을 구현하듯 .NET Core, .NET Framework 그리고 Xamarin은 모두 .NET Standard를 구현합니다. 만약 다양한 legacy .NET에 걸쳐 작동하는 type의 library를 만들고자 한다면 .NET Standard를 통해 가장 쉽게 구현할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET Standard 2.1에서 많은 API가 추가되어 runtime의 변화가 필요했으며 .NET Framework는 가능한 한 변화 없이 유지되어야 할 Microsoft의 legacy platform이므로 .NET Framework 4.8에서는 .NET Standard 2.1을 구현하기 보다는 .NET Standard 2.0에서 남아있습니다. 만약 .NET Framework에서 지원되어야 할 필요가 있다면 비록 최신의 version이 아니고 모든 최근의 언어와 BCL의 새로운 기능을 지원하지 않지만 .NET Standard 2.0상의 class library를 만들 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 .NET Standard version을 목표로 할지에 대한 선택에서는 지원하고자 하는 platform과 가능한 기능사이에 균형을 맞추는 것이 중요합니다. 하위 version은 더 많은 platform을 지원하지만 더 작은 API를 갖게 됩니다. 상위 version은 적은 platform을 지원하지만 더 많은 API를 가질 수 있습니다. 일반적으로는 필요로 하는 모든 API를 지원하는 선에서 하위 version을 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) 다른 SDK를 사용하는 class library의 기본 framework&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class library를 만들기 위해 dotnet SDK tool을 사용할 때 어떤 framework가 기본적으로 사용될지 알고 있을 필요가 있습니다. 아래 표는 이를 간략히 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;SDK&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;새로운 class library에 사용될 기본 target framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;netstandard2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET 6&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;net6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET 7&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;net7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.907%;&quot;&gt;.NET 8&lt;/td&gt;
&lt;td style=&quot;width: 67.093%;&quot;&gt;net8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 class library가 기본적으로 특정 .NET의 version을 대상으로 한다 하더라도 이것이 기본 template을 사용해 class library project를 생성한 이후에는 바꿀 수 없다는 것을 의미하는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정에 의해 target framework를 library참조가 필요한 project에서 지원하는 값으로 바꿀 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;Class library target framework&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;project에서 사용할 수 있는 대상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;netstandard2.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET Framework 4.6.1이후, .NET Core 2.0이후, .NET 5.0이후, Mono 5.4이후, Xamarin.Android 8.0이후, Xamarin.iOS 10.14이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;netstandard2.1&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET Core 3.0이후, .NET 5.0이후, Mono 6.4이후, Xamarin.Android 10이후, Xamarin.iOS 12.16이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;net6.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET 6.0이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;net7.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET 7.0이후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.2093%;&quot;&gt;net8.0&lt;/td&gt;
&lt;td style=&quot;width: 67.7907%;&quot;&gt;.NET 8.0이후&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class library의 대상 framework를 확인하고 필요하다면 더 적절한 것으로 바꿀 수 있습니다. 기본값을 그대로 수용하기보다는 무엇이 적합한지 의식적으로 결정을 내리시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(10) .NET Standard 2.0 class library를 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Standard 2.0을 사용해 모든 .NET legacy platform과 Windows 및 macOS, Linux OS의 cross-platform에서 사용가능하며 다양한 .NET API에 access 할 수 있는 class library를 만들어볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 solution에 SharedLibrary이름의 Class Library유형의 project를 추가합니다. 이때 .NET Standard 2.0을 target으로 설정해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOcIMe/btsEINzG8hk/MxHf8Lppn9Uy8d7yJqtC70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOcIMe/btsEINzG8hk/MxHf8Lppn9Uy8d7yJqtC70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOcIMe/btsEINzG8hk/MxHf8Lppn9Uy8d7yJqtC70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOcIMe%2FbtsEINzG8hk%2FMxHf8Lppn9Uy8d7yJqtC70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;784&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;만약 .NET Standard 2.0 기능만 사용하는 type뿐만 아니라 .NET 7의 새로운 기능을 사용하는 type을 생성해야 한다면 이 두 개의 class library를 분리하여 생성해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개의 class library를 생성하는 대신 multi-targeting을 지원하는 방법도 존재합니다. 자세한 내용은 아래 link를 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot;&gt;Cross-platform targeting for .NET libraries | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687329848302&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Cross-platform targeting for .NET libraries&quot; data-og-description=&quot;Best practice recommendations for creating cross-platform .NET libraries.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/l6XqB/hyS4yJacmX/0tiHO2RdY6zLQYVfKSGuQ0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/b5iX3v/hyS4p6wJNh/tc7e6GyqVnOdhw2yWdgYj1/img.png?width=1581&amp;amp;height=853&amp;amp;face=0_0_1581_853&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/l6XqB/hyS4yJacmX/0tiHO2RdY6zLQYVfKSGuQ0/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/b5iX3v/hyS4p6wJNh/tc7e6GyqVnOdhw2yWdgYj1/img.png?width=1581&amp;amp;height=853&amp;amp;face=0_0_1581_853');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Cross-platform targeting for .NET libraries&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Best practice recommendations for creating cross-platform .NET libraries.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(11) .NET SDK 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 dotnet 명령줄 실행에서는 system에 설치된 가장 최신의 .NET SDK를 사용합니다. 그러나 때로는 이렇게 사용되는 SDK를 변경해야 하는 경우가 있습니다. 그리고 이것은 global.json file을 사용함으로써 가능한데 dotnet명령은 &lt;span style=&quot;background-color: #fafbfc; color: #000000; text-align: start;&quot;&gt;global.json file을 &lt;/span&gt;현재 folder와 상위 folder에거 검색합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 설명하는 절차를 굳이 따라 할 필요는 없지만 그래도 해보고자 한다면 .NET 6.0 SDK를 설치해야 하며 아래 link로부터 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot;&gt;Download .NET 6.0 (Linux, macOS, and Windows) (microsoft.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687349761147&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Download .NET 6.0 (Linux, macOS, and Windows)&quot; data-og-description=&quot;.NET 6.0 downloads for Linux, macOS, and Windows. .NET is a free, cross-platform, open-source developer platform for building many different types of applications.&quot; data-og-host=&quot;dotnet.microsoft.com&quot; data-og-source-url=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot; data-og-url=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ExuXQ/hyS4q5LWe2/uq3JJNlMiKuWZNQnIf3hS1/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/stNqx/hyS4xcLtPP/6kkwkXuInXP2B2AUCyc5Q1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238&quot;&gt;&lt;a href=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dotnet.microsoft.com/en-us/download/dotnet/6.0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ExuXQ/hyS4q5LWe2/uq3JJNlMiKuWZNQnIf3hS1/img.png?width=238&amp;amp;height=238&amp;amp;face=0_0_238_238,https://scrap.kakaocdn.net/dn/stNqx/hyS4xcLtPP/6kkwkXuInXP2B2AUCyc5Q1/img.png?width=459&amp;amp;height=238&amp;amp;face=0_0_459_238');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Download .NET 6.0 (Linux, macOS, and Windows)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET 6.0 downloads for Linux, macOS, and Windows. .NET is a free, cross-platform, open-source developer platform for building many different types of applications.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dotnet.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 project folder하위에 ControlSDK이름의 새로운 folder를 만들고 해당 folder안에서 terminal을 열어 아래 명령을 내려줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet --list-sdks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 .NET SDK 6.0을 설치했다면 아마도 아래와 같은 결과를 표시할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;6.0.418&amp;nbsp;[C:\Program&amp;nbsp;Files\dotnet\sdk] &lt;br /&gt;8.0.101&amp;nbsp;[C:\Program&amp;nbsp;Files\dotnet\sdk]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 아래의 명령을 내려 global.json이름의 file을 만들고 아래와 같이 설정하여 설치한 .NET Core 6.0 SDK의 사용을 강제하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new globaljson --sdk-version 6.0.410&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 file을 열어보면 아마도 다음과 같이 저장되어 있을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687350201338&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;sdk&quot;: {
    &quot;version&quot;: &quot;6.0.410&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 아래 명령을 통해 class library project의 생성을 시도합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new classlib&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 정상적으로 처리된다면 해당 project가 생성되어 있을 테지만 .NET SDK 6.0이 설치되지 않은 상태라면 다음의 오류가 표시될 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;The&amp;nbsp;command&amp;nbsp;could&amp;nbsp;not&amp;nbsp;be&amp;nbsp;loaded,&amp;nbsp;possibly&amp;nbsp;because: &lt;br /&gt;&amp;nbsp;&amp;nbsp;*&amp;nbsp;You&amp;nbsp;intended&amp;nbsp;to&amp;nbsp;execute&amp;nbsp;a&amp;nbsp;.NET&amp;nbsp;application: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;The&amp;nbsp;application&amp;nbsp;'new'&amp;nbsp;does&amp;nbsp;not&amp;nbsp;exist. &lt;br /&gt;&amp;nbsp;&amp;nbsp;*&amp;nbsp;You&amp;nbsp;intended&amp;nbsp;to&amp;nbsp;execute&amp;nbsp;a&amp;nbsp;.NET&amp;nbsp;SDK&amp;nbsp;command: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;A&amp;nbsp;compatible&amp;nbsp;.NET&amp;nbsp;SDK&amp;nbsp;was&amp;nbsp;not&amp;nbsp;found. &lt;br /&gt;&lt;br /&gt;Requested SDK version: 6.0.410&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(12) SDK와 framework의 혼합사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대개의 경우 신규 project를 생성할때 Microsoft의 장기적(3년)지원을 받기 위해 long-term version의 .NET을 사용할 것입니다. 이후에 새로운 .NET version이 나오고 거기에 맞춰 upgrade할 수도 있겠지만 그렇게 하지 않는다고 해서 사용중인 .NET runtime의 생명주기동안 C#언어의 개선된 기능이나 성능에 대한 이점을 포기할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;향후에도 C# compiler를 설치하고 사용하면서 기존 .NET에 대한 사용을 지속할 수 있습니다. 이걸 시기적으로 보면 다음과 같이 설명할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2023년 11월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;.NET SDK 8.0을 설치하고 이를 사용해 .NET 8로 target이 설정된 project를 build합니다. 이때 C# 12가 기본적으로 사용됩니다. 이후에는 매달 개발자 computer에서&amp;nbsp; .NET 8 SDK와 사용자 computer상에서 .NET 8 runtime update patch가 이루어집니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2024년 2월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;새로운 C# 언어와 .NET library기능을 확인해 보기 위해 선택적으로 .NET SDK 9 Preview를 설치할 수 있습니다. .NET 8로 target이 맞춰진 경우에는 새로운 library기능은 사용할 수 없습니다. Preview는 매년 2월 ~ 10월 사이에 release되며 공식 blog의 공지사항을 통해 Preview에서의 새로운 기능을 확인할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2024년 11월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;.NET SDK 9.0.x를 설치하고 이를 사용해 .NET 8로 target이 설정된 project를 build합니다. 이때 C# 13이 기본적으로 사용되며 언어의 새로운 기능역시 사용할 수 있습니다. 또한 SDK와 runtime에 대한 완전한 지원을 받을 수 있고 .NET 8 target project에서도 EF Core 9의 새로운 기능을 사용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025년 2월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;새로운 C# 언어와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;.NET library기능을 확인해 보기 위해 선택적으로 .NET SDK 10 Preview를 설치할 수 있습니다. 만약 .NET 8 project를 새로운 version으로 전환해야 한다면 .NET9와 .NET10의 새로운 library및 ASP.NET Core 기능을 적용할 수 있는지에 대한 계획을 세워야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025년 11월&lt;/td&gt;
&lt;td style=&quot;width: 87.2093%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; .NET SDK 10.0.x를 설치하고 이를 사용해 .NET 8로 target이 설정된 project를 build합니다. 이때 C# 14이 기본적으로 사용되며 언어의 새로운 기능역시 사용할 수 있습니다. 다만 .NET 8은 LTS이므로 .NET 8의 수명주기종료가 되는 2026년 11월까지 .NET 10으로의 전환이 필요합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DK4mc/btsES2wO4M2/I77YejXiKdhAvBhbXoPWH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DK4mc/btsES2wO4M2/I77YejXiKdhAvBhbXoPWH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DK4mc/btsES2wO4M2/I77YejXiKdhAvBhbXoPWH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDK4mc%2FbtsES2wO4M2%2FI77YejXiKdhAvBhbXoPWH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;254&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK를 설치할때는 모든 .NET project를 build하기 위해 기본적으로 가장 최신의 version이 사용됨을 기억하시기 바랍니다. 따라서 만약 현 시점에서&amp;nbsp; .NET 9 SDK preview가 설치되면 인위적으로 사용할 version을 강제하지 않는 이상 project build를 위해 해당 version이 사용될 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 배포를 위한 code 게시하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 소설과 같은 글을 쓴 경우라면 이것을 다른 사람이 읽도록 하기 위해서는 '출판'해야 할 것입니다. 대부분의 개발자는 자신의 project에서 사용하거나 또는 app을 실행하는 일반사용자를 위한 code를 작성합니다. 그렇게 하기 위해서는 class library를 packaging 하거나 실행 가능한 형태의 application으로 게시를 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET application을 배포하고 게시하기 위해서는 아래 3가지 방법을 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Framework-dependent deployment (FDD)&lt;/li&gt;
&lt;li&gt;Framework-dependent executable (FDE)&lt;/li&gt;
&lt;li&gt;Self-contained&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 application 및 여기에 해당하는 package종속성을 같이 배포하기로 했지만 .NET자체는 제외한다면 대상 computer에 설치된 .NET에 의존해야 합니다. 이것은 server에 web application을 배포하기에 적합한 방식입니다. 왜냐하면 .NET은 물론 다른 web application 이미 server에 존재할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Framework-dependent deployment (FDD)는 dotnet 명령줄 도구를 통해 실행될 수 있는 DLL을 배포하는 경우를 의미하며 Framework-dependent executables (FDE)는 직접적으로 실행될 수 있는 EXE를 배포하는 경우를 의미합니다. 이 둘은 모두 system에 적절한 version의 .NET runtime이 설치되어 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 다른 사람에게 USB memory를 통해 application을 전달하고자 할 수 있습니다. 이런 경우 전달받은 사용자의 computer에서 실행될 수 있도록 self-contained deployment를 수행할 수 있습니다. 물론 이렇게 하는 경우 배포 file의 size가 커질 수 있지만 관련된 모든 library를 포함하게 되므로 실행하는 데는 아무런 문제가 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 게시를 위한 console app 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 solution에서 DotNetEverywhere이름의 Console App project를 생성합니다. 그리고 Program.cs에서 기존 구문을 모두 삭제하고 console app이 어디서든 작동할 수 있음을 말하는 message와 현재 운영체제에 대한 정보를 출력하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687416918001&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;I can run everywhere!&quot;);
Console.WriteLine($&quot;OS Version is {Environment.OSVersion}.&quot;);

if (OperatingSystem.IsMacOS())
{
    Console.WriteLine(&quot;I am macOS.&quot;);
}
else if (OperatingSystem.IsWindowsVersionAtLeast(major: 10, build: 22000))
{
    Console.WriteLine(&quot;I am Windows 11.&quot;);
}
else if (OperatingSystem.IsWindowsVersionAtLeast(major: 10))
{
    Console.WriteLine(&quot;I am Windows 10.&quot;);
}
else
{
    Console.WriteLine(&quot;I am some other mysterious OS.&quot;);
}

Console.WriteLine(&quot;Press ENTER to stop me.&quot;);
Console.ReadLine();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.(실행되는 환경에 따라 약간씩 다를 수 있습니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebWOdG/btsEGTf3wn2/nK7SRRYHQ1uYyxNHnx4aY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebWOdG/btsEGTf3wn2/nK7SRRYHQ1uYyxNHnx4aY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebWOdG/btsEGTf3wn2/nK7SRRYHQ1uYyxNHnx4aY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebWOdG%2FbtsEGTf3wn2%2FnK7SRRYHQ1uYyxNHnx4aY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;107&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project file(csproj)을 열어 &amp;lt;PropertyGroup&amp;gt; 요소 안에 세 가지 운영체제를 대상으로 하기 위한 runtime 식별자를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687417120952&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;RuntimeIdentifiers&amp;gt;
	win10-x64;osx-x64;osx.11.0-arm64;linux-x64;linux-arm64
&amp;lt;/RuntimeIdentifiers&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가한 요소는 다음과 같은 의미를 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;win10-x64 : Windows 10 또는 Windows Server 2016 64-bit를 의미합니다. 만약 Microsoft Surface Pro X, Surface Pro 9(SQ 3) 또는 Windows Dev Kit 2023으로 배포하고자 한다면 win10-arm64로 설정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;osx-64 : macOS Sierra 10.12이상을 의미합니다. 또한 osx.10.15-x64 (Catalina), osx.13.0-x64 (Ventura on Intel), osx.13.0-arm64 (Ventura on Apple Silicon)등 특정 version을 명시할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;linux-x64 : Ubuntu, CentOS, Debian, Fedora와 같은 Linux의 대부분의 desktop 배포판을 의미합니다. linux-arm을 사용하는 경우에는 Raspbian 또는 Raspberry Pi OS 32-bit를 의미하며 linux-arm64라면 Raspberry Pi running Ubuntu 64-bit를 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;특정 runtime을 식별하기 위해 사용가능한 2가지 요소가 있습니다. &amp;lt;RuntimeIdentifier&amp;gt;는 특정한 한 가지만을 지정해야 하는 경우에 사용하며 &amp;lt;RuntimeIdentifiers&amp;gt;는 예제와 같이 여러 가지를 지정해야 하는 경우에 사용합니다. 그런데 만약 잘못된 값을 사용하게 된다면 compiler는 error를 발생시키게 되는데 경우에 따라 단 하나의 잘못된 문자로 error가 발생하는 경우에 무엇이 잘못되었는지 찾기가 어려워질 수 있으므로 주의해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) dotnet 명령&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET SDK를 설치하게 되면 dotnet이라는 이름의 command line interface(CLI)를 같이 설치하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● 새로운 project 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET CLI는 template을 사용해 새로운 project를 현재 folder에 생성하는 명령을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal을 열고 .NET 7이상 이라면 'dotnet new list'를, .NET 6라면 'dotnet new --list'혹은 'dotnet new -l'명령을 내려줍니다. 그러면 다음과 같이 현재 설치된 template을 표시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pITaj/btsEINBQZ8r/uOKaNqsTGeKu2MXLZkQRV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pITaj/btsEINBQZ8r/uOKaNqsTGeKu2MXLZkQRV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pITaj/btsEINBQZ8r/uOKaNqsTGeKu2MXLZkQRV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpITaj%2FbtsEINBQZ8r%2FuOKaNqsTGeKu2MXLZkQRV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;1426&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 dotnet 명령의 switche는 길고 짧은 version을 각각 사용할 수 있기 때문에 만약 --list라 하면 -l처럼 사용할 수 있습니다. 짧은 것은 typing 하기에 빠를 수 있지만 다른 사람이나 심지어 본인도 잘못 명령을 해설할 수 있으므로 긴 것을 사용하면 의미를 좀 더 명확히 전달할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET MAUI는 Linux를 지원하지 않습니다. 해당 작업은 open source community에서 계속 진행되겠지만 진정한 cross-platform UI를 사용해야 한다면 Avalonia를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://avaloniaui.net/&quot;&gt;Avalonia UI - Home&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707889180258&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Avalonia UI - Cross-Platform UI Framework for .NET&quot; data-og-description=&quot;Pixel-Perfect Cross-Platform .NET Applications with C# for Windows, macOS, Linux, iOS, Android and Browser.&quot; data-og-host=&quot;www.avaloniaui.net&quot; data-og-source-url=&quot;https://avaloniaui.net/&quot; data-og-url=&quot;http://www.avaloniaui.net/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bADu46/hyVjcxfKNw/0S11aDB1Z2oNmoa1NTNjW0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/EEiEp/hyVja7gBvJ/bb0R3T9VGDIMytiFRvsrg0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/cDXcBo/hyVjlgDec4/7VqKs6D36AqGtbJe82uwHK/img.png?width=1358&amp;amp;height=764&amp;amp;face=0_0_1358_764&quot;&gt;&lt;a href=&quot;https://avaloniaui.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://avaloniaui.net/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bADu46/hyVjcxfKNw/0S11aDB1Z2oNmoa1NTNjW0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/EEiEp/hyVja7gBvJ/bb0R3T9VGDIMytiFRvsrg0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/cDXcBo/hyVjlgDec4/7VqKs6D36AqGtbJe82uwHK/img.png?width=1358&amp;amp;height=764&amp;amp;face=0_0_1358_764');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Avalonia UI - Cross-Platform UI Framework for .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Pixel-Perfect Cross-Platform .NET Applications with C# for Windows, macOS, Linux, iOS, Android and Browser.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.avaloniaui.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) .NET및 실행환경에 대한 정보 파악하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 설치된 .NET SDK및 runtime과 함께 운영체제에 대한 정보를 확인해 보려면 'dotnet --info'명령을 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clOL2H/btsEORXMgPm/efE8L76BnYpck5wFvn2PB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clOL2H/btsEORXMgPm/efE8L76BnYpck5wFvn2PB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clOL2H/btsEORXMgPm/efE8L76BnYpck5wFvn2PB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclOL2H%2FbtsEORXMgPm%2FefE8L76BnYpck5wFvn2PB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;1426&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) dotnet CLI로 Project 관리하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET CLI는 아래명령을 통해 project를 관리하기 위해 현재 folder에 project에 대한 몇 가지 작업을 수행할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dotnet help : dotnet 명령에 대한 도움말을 표시합니다.&lt;/li&gt;
&lt;li&gt;dotnet new : 새로운 .NET project 또는 file을 생성합니다.&lt;/li&gt;
&lt;li&gt;dotnet tool : .NET 환경을 확장하기 위한 도구를 설치하거나 관리합니다.&lt;/li&gt;
&lt;li&gt;dotnet workload : .NET MAUI와 같은 선택적 workload를 관리합니다.&lt;/li&gt;
&lt;li&gt;donet restore : project에 대한 의존성을 download 합니다.&lt;/li&gt;
&lt;li&gt;dotnet build : .NET Project를 build(compile)합니다. .NET 8에서는 --tl라는 새로운 option을 추가했으며 이를 통해 build가 수행중인 작업에 대한 실시간 정보를 확인할 수 있습니다. 좀더 자세한 사항은 &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build#options&quot;&gt;dotnet build command - .NET CLI | Microsoft Learn&lt;/a&gt; 이곳을 참고하시기 바랍니다.&lt;/li&gt;
&lt;li&gt;dotnet build-server : build에 의해 시작된 server와의 상호작용을 수행합니다.&lt;/li&gt;
&lt;li&gt;dotnet msbuild : MS Build Engine명령을 실행합니다.&lt;/li&gt;
&lt;li&gt;dotnet clean : build로 생성되는 임시 folder를 삭제합니다.&lt;/li&gt;
&lt;li&gt;dotnet test : project에 대한 단위 test를 build 하고 실행합니다.&lt;/li&gt;
&lt;li&gt;dotnet run : project를 build하고 실행합니다.&lt;/li&gt;
&lt;li&gt;dotnet pack : project에 대한 NuGet package를 생성합니다.&lt;/li&gt;
&lt;li&gt;dotnet publish : 의존성 또는 self-contained application과 함께 project를 build하고 게시합니다. .NET 7및 그 이전 version에서는 배포시 Debug의 설정이 기본이었으나 .NET 8부터는 Release설정이 기본으로 게시합니다.&lt;/li&gt;
&lt;li&gt;dotnet add : project에 package 혹은 class library에 대한 참조를 추가합니다.&lt;/li&gt;
&lt;li&gt;dotnet remove : project에 대한 package 혹은 class library 참조를 삭제합니다.&lt;/li&gt;
&lt;li&gt;dotnet list : project에 참조된 package 또는 class library를 listing 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Self-contained app 게시하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 cross-platform console app인 DotNetEverywhere를 게시하려면 terminal에서 DotNetEverywhere project가 있는(csproj가 있는) folder로 이동한 뒤 다음과 같은 명령을 내려 project를 build 하고 self-contained release version으로 Windows 10에서의 console application을 게시하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;--self-contained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 build engine은 필요한 모든 package를 복구하고 project source code를 assembly DLL로 compile 하여 publish folder를 만들어 저장하게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Determining&amp;nbsp;projects&amp;nbsp;to&amp;nbsp;restore... &lt;br /&gt;All projects are up-to-date for restore. &lt;br /&gt;DotNetEverywhere -&amp;gt; C:\Users\w10tsp\DotNetEverywhere\bin\Release\net7.0\win10-x64\DotNetEverywhere.dll &lt;br /&gt;DotNetEverywhere -&amp;gt; C:\Users\w10tsp\DotNetEverywhere\bin\Release\net7.0\win10-x64\publish\&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 아래 명령을 추가로 사용해 macOS와 다양한 Linux에서의 release version으로 게시하도록 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet publish -c Release -r osx-x64 --self-contained&lt;br /&gt;dotnet publish -c Release -r osx.11.0-arm64 --self-contained&lt;br /&gt;dotnet publish -c Release -r linux-x64 --self-contained&lt;br /&gt;dotnet publish -c Release -r linux-arm64 --self-contained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;필요하다면 위와 같은 명령들은 PowerShell과 같은 script 언어를 사용해 자동화하고 cross-platform인 PowerSheel Core를 사용해 모든 OS상에서 script file을 실행할 수 있습니다. 이는 그저 위의 명령들을 저장한 .ps1 확장자 file을 만들고 file을 실행하기만 하면 됩니다. PowerShell에 관해서는 아래 link를 통해 더 자세한 정보를 확인하실 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot;&gt;cs11dotnet7/docs/powershell at main &amp;middot; markjprice/cs11dotnet7 &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687486368105&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&quot; data-og-description=&quot;Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&amp;quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot; data-og-url=&quot;https://github.com/markjprice/cs11dotnet7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lTc20/hyS5tHyhvD/fcGKJRbF2IssB30XMKEtl1/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/markjprice/cs11dotnet7/tree/main/docs/powershell&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lTc20/hyS5tHyhvD/fcGKJRbF2IssB30XMKEtl1/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 실행한 뒤 Windows 탐색기를 열어 'DotNetEverywhere\bin\ Release\net7.0' folder로 이동해 보면 다섯 가지 OS에 대한 output folder가 생성되어 있음을 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'win10-x64' folder안에서 publish folder를 선택해 보면 Microsoft.CSharp.dll과 같은 필요한 모든 assembly가 저장되어 있음을 볼 수 있으며 이중 DotNetEverywhere.exe file을 선택하면 대략 file의 크기가 151 KB정도 됨을 보실 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xdutt/btsEIRB6gSH/BI0EeEmdSZtDg2IXuB1xtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xdutt/btsEIRB6gSH/BI0EeEmdSZtDg2IXuB1xtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xdutt/btsEIRB6gSH/BI0EeEmdSZtDg2IXuB1xtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxdutt%2FbtsEIRB6gSH%2FBI0EeEmdSZtDg2IXuB1xtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1163&quot; height=&quot;701&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 OS가 Windows라면 해당 file을 double click 하여 program을 실행하고 그 결과를 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;I&amp;nbsp;can&amp;nbsp;run&amp;nbsp;everywhere! &lt;br /&gt;OS&amp;nbsp;Version&amp;nbsp;is&amp;nbsp;Microsoft&amp;nbsp;Windows&amp;nbsp;NT&amp;nbsp;10.0.19045.0. &lt;br /&gt;I&amp;nbsp;am&amp;nbsp;Windows&amp;nbsp;10. &lt;br /&gt;Press&amp;nbsp;ENTER&amp;nbsp;to&amp;nbsp;stop&amp;nbsp;me.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 publish folder의 전체 크기는 대략 70MB 정도 됩니다. 각 OS별로 만들어진 publish folder를 필요한 OS에 복사하고 실행하면 아마도 정상적으로 실행됨을 알 수 있을 것입니다. 이는 console app이 .NET application에 대한 self-contained 배포판이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 console app을 사용했지만 ASP.NET Core website나 web service 또는 Windows Forms나 WPF app과 같은 것들도 쉽게 만들 수 있습니다. 물론 이때도 Linux나 macOS가 아닌 Windows computer를 위한 Windows desktop app을 배포할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 단일 file app 게시하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 file로서 게시하고자 한다면 게시할 때 flag를 지정할 수 있습니다. .NET 5에서 단일 file app은 Windows와 macOS에서 사실상 기술적으로 단일 file 배포가 불가능했기 때문에 주로 Linux에 집중되었습니다. 그러나 .NET 6 이후에부터는 Windows에서도 적절한 단일 file app을 만들 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 app을 실행하고자 하는 computer에 적절한 version의 .NET이 설치되어 있다면 app을 게시할 때 아래와 같이 확장 flag를 사용해 self-contained가 필요하지 않고 단일 file로서 배포(가능하다면)하고자 함을 알려줄 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;--no-self-contained&amp;nbsp;/p:PublishSingleFile=true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 2개의 file이 생성되는데 하나는 exe, 다른 하나는 pdb입니다. exe는 실행가능한 file을 의미하며 pdb는 program debug database로서 debugging에 필요한 정보를 담고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS상에서 게시된 application애 대한 exe는 존재하지 않으므로 osx-x64와 같은 명령을 사용해야 하며 생성된 file에는 확장자가 존재하지 않을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 pdb file마저도 exe안으로 포함시키고자 한다면 assembly와 함께 배포되도록 해야 하며 그러기 위해서 csproj file에서 &amp;lt;PropertyGroup&amp;gt; 요소 안에 &amp;lt;DebugType&amp;gt; 요소를 추가하고 여기에 embedded를 아래와 같이 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687501875543&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;RuntimeIdentifiers&amp;gt;
	win10-x64;osx-x64;osx.11.0-arm64;linux-x64;linux-arm64
&amp;lt;/RuntimeIdentifiers&amp;gt;
&amp;lt;DebugType&amp;gt;embedded&amp;lt;/DebugType&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 대상 computer에 .NET이 설치되어 있지 않은 상태라면 Linux의 경우 단 2개의 file만을 필요로 하지만 Windows라면 coreclr.dll, clrjit.dll, clrcompression.dll 그리고 mscordaccore.dll과 같은 추가적인 file이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;terminal에서 아래 명령을 통해 Windows 10용 console app의 self-contained release version을 build 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;--self-contained&amp;nbsp;/p:PublishSingleFile=true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DotNetEverywhere\bin\Release\net7.0\win10-x64\publish foler에서 DotNetEverywhere.exe file을 선택합니다. 그러면 해당 file의 크기가 거의 65MB 정도로 커지게 되는 것을 알 수 있습니다. 또한 pdb file은 11KB 정도인데 이는 system에 따라 크기가 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(7) App trimming을 사용한 app size 줄이기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET app을 self-contained app으로 배포하는 데 있어서 한 가지 문제점은 .NET library에서 너무 많은 용량을 차지한다는 것이며 특히 문제는 모든 .NET Library를 brwoser에서 내려받아야 하는 Blazor WebAssembly component가 문제가 될 수 있고 이러한 여러 가지 이유로 size를 줄여줄 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이러한 문제는 배포 시 사용하지 않는 assembly를 packaging에서 제외하도록 함으로써 해결할 수 있습니다. .NET Core 3.0에서 도입된 app trimming system은 code에서 필요한 assembly를 식별하고 필요하지 않은 것들을 제거할 수 있습니다. 이를 copyused trim mode라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 5에서 trimming은 개별 type을 제거하고 심지어 assembly안에서 사용되지 않는 method등의 member를 제거할 수도 있습니다. template에 의해 생성된 기본 console app의 경우 trimming된 System.Console.dll assembly는 대략 60KB에서 30KB로 줄어드는데 .NET 5에서 당시 이 기능은 실험적인 기능이라 기본적으로 사용되지 않도록 disable되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서 Microsoft는 자신들의 library에 주석을 추가하여 어떤 식으로 안전하게 trimming 할 수 있는지를 나타내도록 하였으며 이에 따라 type과 member의 trimming이 기본적으로 사용되도록 적용되었고 이를 link trim mode라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 7에서 Microsoft는 link를 full로 copyused를 partial로 명칭을 변경하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 trimming이 사용되지 않는 assembly, type, member 등을 얼마나 잘 식별하느냐 하는 것입니다. 만약 code가 동적인 것, 예를 들어 reflection과 같은 것이라면&amp;nbsp; 정확하게 작동하지 않을 수 있고 이것 때문에 Microsoft는 수동적인 조정이 가능하도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Assembly 수준 trimming 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assembly 수준의 trimming을 사용하는 데는 2가지 방법이 있습니다. 첫 번째는 csproj(project file)에 아래와 같은 요소를 추가하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687574186787&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PublishTrimmed&amp;gt;true&amp;lt;/PublishTrimmed&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 게시할 때 아래와 같이 flag를 사용하는 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;publish&amp;nbsp;-c&amp;nbsp;Release&amp;nbsp;-r&amp;nbsp;win10-x64&amp;nbsp;--self-contained&amp;nbsp;-p:PublishTrimmed=True&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Type 및 member수준의 trimming 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에도 2가지 방법이 있는데 첫 번째는 위의 예와 마찬가지로 아래의 요소를 추가하는 것이며&lt;/p&gt;
&lt;pre id=&quot;code_1687574412999&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PublishTrimmed&amp;gt;true&amp;lt;/PublishTrimmed&amp;gt;
&amp;lt;TrimMode&amp;gt;Link&amp;lt;/TrimMode&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 방법으로 flag를 사용하는 것입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet publish ... -p:PublishTrimmed=True -p:TrimMode=Link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 6에서 link trim mode는 기본적으로 사용할 수 있으므로 assembly수준의 trimming을 의미하는 copyuse와 같은 다른 trim mode를 사용하고자 하는 경우일 때만 swich를 지정하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(8) build artifact가 생성되는 위치 조정하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 각 project는 자신만의 obj와 bin folder를 가지고 있어서 여기에 build 동안의 임시 file이 생성되며 특히 bin은 배포를 위한 file이 생성됩니다. .NET 8에서는 필요하다면 obj와 bin에 생성되는 file의 위치를 변경할 수도 있는 기능을 도입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project가 존재하는(*.csproj file이 존재하는) folder에서 아래와 같이 명령을 내려줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet&amp;nbsp;new&amp;nbsp;buildprops&amp;nbsp;--use-artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 특별한 경우가 아닌한 다음과 같이&amp;nbsp; message가 표시되며&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5Yl8X/btsES25fd3W/sT5pJ7ClN3fQT75g1X9oEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5Yl8X/btsES25fd3W/sT5pJ7ClN3fQT75g1X9oEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5Yl8X/btsES25fd3W/sT5pJ7ClN3fQT75g1X9oEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5Yl8X%2FbtsES25fd3W%2FsT5pJ7ClN3fQT75g1X9oEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1636&quot; height=&quot;210&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 folder에는 'Directory.Build.props'이름의 file이 표시됩니다. file은 다음과 같은 makeup으로 저장되어 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707965559278&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project&amp;gt;
  &amp;lt;!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build --&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;

    &amp;lt;ArtifactsPath&amp;gt;$(MSBuildThisFileDirectory)artifacts&amp;lt;/ArtifactsPath&amp;gt;

  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설정은 build process가 시작되면 그에 따른 folder가 artifacts 이름의 folder안에 생성됨을 의미합니다. 따라서 project를 build해 보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzddCD/btsEP77AJl1/paXyt2qlVGn4tqn5pwhUfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzddCD/btsEP77AJl1/paXyt2qlVGn4tqn5pwhUfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzddCD/btsEP77AJl1/paXyt2qlVGn4tqn5pwhUfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzddCD%2FbtsEP77AJl1%2FpaXyt2qlVGn4tqn5pwhUfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;634&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bin과 obj folder가 위와 같이 생성되어 있음을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(9) Native ahead-of-time compilation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 .NET이 설치되어 있지 않은 환경에서의 실행을 위해 Self-contained 형태로 assembly를 생성하고 그것을 native code로 compile하여 더 빠르게 실행하고 더 적은 memory를 사용할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, runtime에서 JIT(Just In Time) compiler를 사용하는 대신 배포시점에 IL code를 native code로 compile하는 것인데 위에서 설명한 이점을 얻을 수 있는 대신 Windows x64나 Linux Arm과 같이 특정한 runtime환경이 지정되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;native AOT는 게시시점에 발생하므로 현재 project가 AOT를 사용하도록 설정되어 있다고 하더라도 debugging의 목적으로 실행하는등의 동작시에는 여전히 native AOT가 아닌 runtime JIT compiler를 사용하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 native AOT와 호환되지 않는 일부 기능은 disable되거나 예외를 발생시킬 수 있으며 source 분석기가 활성화되어 잠재적인 code 비호환성에 대한 경고를 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Native AOT 의 한계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 다음과 같은 일부 한계가 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Assembly의 동적 loading이 없습니다.&lt;/li&gt;
&lt;li&gt;System.Reflection.Emit을 사용하는 것과 같은 runtime code 생성이 없습니다.&lt;/li&gt;
&lt;li&gt;Trimming이 필요하며 여기에는 자체적인 한계가 존재합니다.&lt;/li&gt;
&lt;li&gt;필요한 모든 library를 포함하는 Self-contained으로만 배포되므로 배포크기가 커질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 자체 assembly에서 위의 기능을 사용하지 않을 수 있지만 .NET 자체의 주요부분은 해당 기능을 사용합니다. 예를 들어 ASP.NET Core MVC(Controller를 사용하는 Web API service를 포함해)와&amp;nbsp; EF Core는 자신들의 기능을 구현하기 위해 runtime code 생성을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET Team은 native AOT와 .NET과의 더 나은 호환성을 위해 노력중이지만 Minimal API를 사용하는 경우에만 .NET 8에서 ASP.NET Core에 대한 기본적인 지원을 포함하고 있으며 EF Core는 지원하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT 게시 process는 지원하지 않는 기능을 사용하는 경우 이를 알려주기 위한 code 분석기를 포함하고 있지만 아직까지 모든 package가 이러한 기능이 잘 작동하도록 anotate되어 있지는 않습니다. Type또는 member가 AOT를 지원하지 않음을 나타내는데 사용된 가장 일반적인 annotation은 [RequiresDynamicCode] attribute입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOT가 나타내는 위험성에 관한 자세한 사항은 아래 link를 참고하시가 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot;&gt;Introduction to AOT warnings - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707983031201&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Introduction to AOT warnings - .NET&quot; data-og-description=&quot;Learn why warnings might be produced when publishing an application as Native AOT, how to address them, and how to make the application &amp;quot;AOT compatible.&amp;quot;&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/csUYKO/hyVm5wMfR8/QAKJgQZYjbMRjtu0kAbpv0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/fixing-warnings&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/csUYKO/hyVm5wMfR8/QAKJgQZYjbMRjtu0kAbpv0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Introduction to AOT warnings - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn why warnings might be produced when publishing an application as Native AOT, how to address them, and how to make the application &quot;AOT compatible.&quot;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Reflection 과 native AOT&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reflection는 runtime에서 type의 metadata를 검사하고 member의 동적호출과 code생성을 목적으로 자주사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 일부 reflection기능을 허용하긴 하지만 native AOT compilation process동안 수행되는 trimming은 type이&amp;nbsp; reflection을 통해서만 접근가능한 member를 가지고 있는지의 여부를 정적으로 판단할 수 없고 따라서 이들 member는 AOT에 의해 제거될 수 있고 때문에 runtime예외를 일으킬 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개발자는 자신의 type에 [DynamicallyAccessedMembers]으로 annotate함으로서 해당 member가 reflection을 통해서만 접근될 수 있고 때문에 trimming하지 않은 상태로 남아있어야 함을 표시할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;● Native AOT의 요구사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native AOT는 운영체제에 따라 다음과 같은 추가적인 요구사항이 뒤따릅니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6279%;&quot;&gt;Windows&lt;/td&gt;
&lt;td style=&quot;width: 83.3721%;&quot;&gt;전체 기본 component를 가진 Visual Studio 2022 Desktop development with C++ workload 설치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6279%;&quot;&gt;Linux&lt;/td&gt;
&lt;td style=&quot;width: 83.3721%;&quot;&gt;compiler toolchain과 .NET runtime이 의존하는 library를 위해 developer package를 설치해야 합니다. 예를 들어 Ubuntu 18.04 이상에서 다음과 같은 명령을 내려줍니다.&lt;br /&gt;&lt;br /&gt;sudo apt-get install clang zlib1g-dev&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Cross-platform native AOT 배포는 지원하지 않습니다. 즉, 배포하고자 하는 특정 운영제제상에서 배포되어야 합니다. 때문에 Linux에서의 native AOT project 배포본은 Windows상에서 실행될 수 없습니다. 물론 그 반대도 마찬가지 입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Project에 native AOT 적용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project에서 native AOT 배포를 적용하려면 project (csproj) file에 다음과 같이 &amp;lt;PublishAot&amp;gt;요소를 추가해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708045143269&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;PublishAot&amp;gt;true&amp;lt;/PublishAot&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Native AOT project build 하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio를 통해 native AOT 호환 project를 생성하려면 다음과 같이 'Enable native AOT publish'를 check합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBMVsk/btsEWx4Olb2/x2SkYhAfzJXkdVWfmb3YN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBMVsk/btsEWx4Olb2/x2SkYhAfzJXkdVWfmb3YN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBMVsk/btsEWx4Olb2/x2SkYhAfzJXkdVWfmb3YN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBMVsk%2FbtsEWx4Olb2%2Fx2SkYhAfzJXkdVWfmb3YN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1014&quot; height=&quot;675&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotnet CLI를 통해서는 다음과 같이 --aot option을 통해 project를 생성할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet new console --aot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법으로 생성된 project file에서는 PublishAot와 InvariantGlobalization요소가 추가됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708046478062&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;PropertyGroup&amp;gt;
  &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
  &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
  &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
  &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;PublishAot&amp;gt;true&amp;lt;/PublishAot&amp;gt;
  &amp;lt;InvariantGlobalization&amp;gt;true&amp;lt;/InvariantGlobalization&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;.NET 8의 Console App project에서는 명시적으로 invariant globalization를 true로 설정하는 것은 새로운 기능으로 이는 app이 특정한 문화권에 구애받지 않고 전세계 어디서든 배포되고 동일한 동작을 수행하도록 합니다. 만약 이 요소의 설정을 false로 하거나 요소자체를 누락한다면 현재 computer의 문화권을 기본으로 설정하게 됩니다. 이에 관한 좀더 자세한 사항은 아래 link를 참고하시기 바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md&quot;&gt;runtime/docs/design/features/globalization-invariant-mode.md at main &amp;middot; dotnet/runtime &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어 program.cs에서는 기존의 문을 모두 삭제하고 현재 문화권과 OS version을 표시하도록 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708047913049&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Globalization;

Console.WriteLine(&quot;현재 문화권 설정: {0}&quot;, CultureInfo.CurrentCulture. DisplayName);
Console.WriteLine(&quot;운영체제: {0}&quot;, Environment.OSVersion);

Console.Write(&quot;종료하려면 아무키나 누르십시오...&quot;);
Console.ReadKey(intercept: true);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 실행하고 결과를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjIXZO/btsEVeSsTAr/2c5ca77tuf5SUcPuxNb3ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjIXZO/btsEVeSsTAr/2c5ca77tuf5SUcPuxNb3ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjIXZO/btsEVeSsTAr/2c5ca77tuf5SUcPuxNb3ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjIXZO%2FbtsEVeSsTAr%2F2c5ca77tuf5SUcPuxNb3ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;628&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 native AOT는 배포시점에 적용되므로 build된 실행 file은 여전히 JIT compiler를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;● Native AOT project 배포하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발중에 정상적으로 작동하는 console app이라도 trimming하지 않고 JIT compile하게 되면 이를 native AOT를 사용해 배포하는 것은 실패할 수 있습니다. 일단 trimming이 이루어지고 JIT compile되어야 native AOT 배포를 사용할 수 있는 상태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포동안에 project가 AOT에 관한 어떠한 경고도 표시하지 않는다면 해당 app은 AOT 배포후에 잘 작동하리라고 기대할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;project를 배포하려면 다음과 같이 명령을 내려줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BMRno/btsEVMVCzu6/pKzxF5zVIwhtvkqUGkrNv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BMRno/btsEVMVCzu6/pKzxF5zVIwhtvkqUGkrNv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BMRno/btsEVMVCzu6/pKzxF5zVIwhtvkqUGkrNv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBMRno%2FbtsEVMVCzu6%2FpKzxF5zVIwhtvkqUGkrNv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;248&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포실행 후 'bin\Release\net8.0\win-x64\publish' folder를 보면 실행 file이 1.2M가 넘는것을 확인할 수 있습니다. 참고로 pdb file은 debuging에 사용되는 file입니다. 여기에 있는 해당 exe file을 실행하면 이전과 정확히 같은 동작을 수행한다는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 program.cs로 돌아와 이번에는 동적 code를 위한 namespaces를 아래와 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1708050026530&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection; // To AssemblyName.
using System.Reflection.Emit; // To AssemblyBuilder.

using System.Globalization;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 동적 assembly builder를 생성하도록 아래와 같이 code를 추가하고&lt;/p&gt;
&lt;pre id=&quot;code_1708050118384&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(&quot;assemblyName&quot;), AssemblyBuilderAccess.Run);

Console.WriteLine(&quot;현재 문화권 설정: {0}&quot;, CultureInfo.CurrentCulture. DisplayName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 배포를 시도하면 아래와 같은 경고 message를 보게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Np4LT/btsETpUAHNn/LDLikB9AyL6KMmJZnhY6JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Np4LT/btsETpUAHNn/LDLikB9AyL6KMmJZnhY6JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Np4LT/btsETpUAHNn/LDLikB9AyL6KMmJZnhY6JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNp4LT%2FbtsETpUAHNn%2FLDLikB9AyL6KMmJZnhY6JK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1115&quot; height=&quot;400&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략적인 내용은 .NET team 이 [RequiresDynamicCode]&amp;nbsp; attribute를 적용한 DefineDynamicAssembly method의 호출에 관한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;native AOT에 관한것은 아래 linki에서 좀더 자세하게 알아보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot;&gt;Native AOT deployment overview - .NET | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708051126534&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Native AOT deployment overview - .NET&quot; data-og-description=&quot;Learn what Native AOT deployments are and why you should consider using it as part of the publishing your app with .NET 7 and later.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kkKUx/hyVmTpHZ1B/SLwnhhg7rzjPihpHUdvbT0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net7%2Cwindows&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kkKUx/hyVmTpHZ1B/SLwnhhg7rzjPihpHUdvbT0/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Native AOT deployment overview - .NET&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn what Native AOT deployments are and why you should consider using it as part of the publishing your app with .NET 7 and later.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;4. .NET assembly decompile&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#을 학습하기 위한 가장 좋은 방법 중에 하나는 수준 높은 다른 누군가가 만들어 놓은 code를 보는 것입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;물론 학습목적이 아닌 자신의 library나 application에 사용할 목적으로 code를 복사하기 위한 decompile을 시도해 볼 수도 있을 것입니다. 어쨌건 decompile은 그들의 지적재산권을 침해하는 것으로 귀결될 수 있으므로 주의해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Visual Studio 2022의 ILSpy extension을 사용한 decompile&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET assembly의 decompile은 ILSpy와 같은 도구를 통해 가능합니다. Visual Studio에서 Extensions -&amp;gt; Manage Extensions menu를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 category에서 online을 선택하고 search에 'ILSpy'를 입력하고 ILSpy 2022 선택한 뒤 download button을 눌러줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8FTNU/btsEGSamrmc/oekaGzCkQGdP7lxWgSahI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8FTNU/btsEGSamrmc/oekaGzCkQGdP7lxWgSahI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8FTNU/btsEGSamrmc/oekaGzCkQGdP7lxWgSahI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8FTNU%2FbtsEGSamrmc%2FoekaGzCkQGdP7lxWgSahI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;941&quot; height=&quot;653&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Download가 완료되면 Visual Studio를 닫아 내려받은 ILSpy 2022의 설치를 완료합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GDZee/btsEIQiSWvI/20KkDFFSmudE3DaXW0qyE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GDZee/btsEIQiSWvI/20KkDFFSmudE3DaXW0qyE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GDZee/btsEIQiSWvI/20KkDFFSmudE3DaXW0qyE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGDZee%2FbtsEIQiSWvI%2F20KkDFFSmudE3DaXW0qyE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;332&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio를 재시작하고 csStudy07 project를 열어줍니다. 그리고 solution explorer에서 DotNetEverywhere project를 mouse오른쪽 click한뒤 'Open output in ILSpy'를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시가다리면 ILSpy가 열리게 되는데 decompile 할 언어설정이 C#으로 되어 있는지 확인합니다. 왼쪽 화면에는 'Assemblies navigation'화면이 보이는데 여기에서 DotNetEverywhere를 확장합니다. 이어서 '{ }'와 Program을 차례로 확장한 뒤 '&amp;lt;Main&amp;gt;$(string[]) : void'부분을 click 하여 compiler가 생성한 Program class와 보간문자열이 어떻게 작동하는지를 나타내는 &amp;lt;Main&amp;gt;$ method를 표시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edtFWe/btsEFUmc724/8rGzVj5qV5NuBaj0YENfnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edtFWe/btsEFUmc724/8rGzVj5qV5NuBaj0YENfnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edtFWe/btsEFUmc724/8rGzVj5qV5NuBaj0YENfnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedtFWe%2FbtsEFUmc724%2F8rGzVj5qV5NuBaj0YENfnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;807&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 ILSpy에서 File -&amp;gt; Open menu를 선택한 뒤 'DotNetEverywhere/bin/Release/net7.0/linux-x64' foler에서 System.Linq.dll file을 열어봅니다. 그리고 Assemblies tree에서 'System.Linq ~'와 System.Linq namespace, Enumerable class를 차례로 확장한 뒤 'Count&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt;) : int'를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WX38J/btsEF7r8MKJ/9PzbC9KTzX591osgy9nfi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WX38J/btsEF7r8MKJ/9PzbC9KTzX591osgy9nfi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WX38J/btsEF7r8MKJ/9PzbC9KTzX591osgy9nfi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWX38J%2FbtsEF7r8MKJ%2F9PzbC9KTzX591osgy9nfi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;807&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Count method를 보면 몇 가지 배워볼 만한 점을 발견할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;source 매개변수를 확인하고 null이라면 ArgumentNullException 예외를 발생시키도록 합니다.&lt;/li&gt;
&lt;li&gt;source는 좀 더 효휼적으로 읽어 들일 수 있는 자체의 Count속성으로 구현할 수 있는 Interface를 확인합니다.&lt;/li&gt;
&lt;li&gt;source의 모든 item을 열거하고 counter를 증가시키는 마지막 method는 구현 효율성이 가장 낮습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Count method의 source code를 확인하고&lt;/p&gt;
&lt;pre id=&quot;code_1687666908368&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static int Count&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source)
{
	if (source == null)
	{
		ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
	}
	if (source is ICollection&amp;lt;TSource&amp;gt; collection)
	{
		return collection.Count;
	}
	if (source is IIListProvider&amp;lt;TSource&amp;gt; iIListProvider)
	{
		return iIListProvider.GetCount(onlyIfCheap: false);
	}
	if (source is ICollection collection2)
	{
		return collection2.Count;
	}
	int num = 0;
	using IEnumerator&amp;lt;TSource&amp;gt; enumerator = source.GetEnumerator();
	while (enumerator.MoveNext())
	{
		num = checked(num + 1);
	}
	return num;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 Intermediate Language (IL)에서 생성한 것과 동일한 code를 비교해 보기 위해 toolbar에서 C#에 대한 dropdownlist를 ILSpy로 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JHQNc/btsEHGtYPBO/H5DzQLkWqk4gKv6Z1ztWN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JHQNc/btsEHGtYPBO/H5DzQLkWqk4gKv6Z1ztWN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JHQNc/btsEHGtYPBO/H5DzQLkWqk4gKv6Z1ztWN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJHQNc%2FbtsEHGtYPBO%2FH5DzQLkWqk4gKv6Z1ztWN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;807&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C# compiler가 source code를 IL code로 변환시킨다는 것만 알고 있으면 될 뿐 사실 IL code가 딱히 유용하지는 않습니다. 위의 source code만 가지고도 Count method가 인수에 대해 어떻게 null을 확인하는지 등을 알 수 있으므로 훨씬 유용하게 활용될 수 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link에서는 Visual Studio에서 ILSpy를 어떻게 사용할 수 있는지를 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot;&gt;cs11dotnet7/docs/code-editors/vscode.md at main &amp;middot; markjprice/cs11dotnet7 &amp;middot; GitHub&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687667860974&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&quot; data-og-description=&quot;Repository for the Packt Publishing book titled &amp;quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&amp;quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot; data-og-url=&quot;https://github.com/markjprice/cs11dotnet7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cVYXe0/hyS6CROL3i/mexhLzqpvAsIlS4JlZVWG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232&quot;&gt;&lt;a href=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/markjprice/cs11dotnet7/blob/main/docs/code-editors/vscode.md#decompiling-using-the-ilspy-extension-for-visual-studio-code&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cVYXe0/hyS6CROL3i/mexhLzqpvAsIlS4JlZVWG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=937_99_1058_232');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - markjprice/cs11dotnet7: Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Devel&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Repository for the Packt Publishing book titled &quot;C# 11 and .NET 7 - Modern Cross-Platform Development Fundamentals&quot; by Mark J. Price - GitHub - markjprice/cs11dotnet7: Repository for the ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Visual Studio 2022를 통한 source link 보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Decompile대신 Visual Studio에는 source link를 사용해 본래 source code를 볼 수 있는 기능을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csStudy07 solution에서 'SourceLinks'라는 Console app유형의 새로운 project를 생성하고 Program.cs에서 string변수를 선언하고 해당 변수가 가진 문자열의 수를 다른 변수에 할당하고 확인하는 문을 아래와 같이 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687674521169&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;string name = &quot;cliel&quot;;
int length = name.Count();

Console.WriteLine($&quot;{name} has {length} characters.&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Count method에서 mouse오른쪽 button을 click 하여 'Go To Implementation'을 선택합니다. 그러면 Count.cs이름의 source code file이 표시될 텐데 여기서 우리는 count와 관련된 다섯 개의 method가 Enumerable이라는 partial class에 구현되어 있음을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3842&quot; data-origin-height=&quot;2169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kY51T/btsEFQRHcY2/mwQZ97kst8RZmd7GrCK0JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kY51T/btsEFQRHcY2/mwQZ97kst8RZmd7GrCK0JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kY51T/btsEFQRHcY2/mwQZ97kst8RZmd7GrCK0JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkY51T%2FbtsEFQRHcY2%2FmwQZ97kst8RZmd7GrCK0JK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3842&quot; height=&quot;2169&quot; data-origin-width=&quot;3842&quot; data-origin-height=&quot;2169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 source links를 통해서도 위 예제와 같이 관리의 용이성을 위해 어떻게 partial class를 통해서 class를 나누는지와 같은 상황별 예시를 확인할 수 있기 때문에 decompile 하는 것만큼 많은 것을 배울 수 있습니다. ILSpy를 사용하는 경우에도 물론 Enumerable class에 대한 수천 개의 method를 모두 볼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아래 link를 통해서는 source links에 관한 더 많은 것과 얼마나 많은 NuGet package가 이를 지원할 수 있는지를 확인해 볼 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot;&gt;Source Link and .NET libraries | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687675249303&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Source Link and .NET libraries&quot; data-og-description=&quot;Best practice recommendations for using Source Link to improve debugging for .NET libraries.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXAycN/hyS6yWgTXl/IJKLUTfup5w5qxcFFc3LnK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/iUDN3/hyS6xiKg43/kRMPy1ohb3kr0B7xabcRCK/img.png?width=928&amp;amp;height=813&amp;amp;face=0_0_928_813&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXAycN/hyS6yWgTXl/IJKLUTfup5w5qxcFFc3LnK/img.png?width=636&amp;amp;height=300&amp;amp;face=0_0_636_300,https://scrap.kakaocdn.net/dn/iUDN3/hyS6xiKg43/kRMPy1ohb3kr0B7xabcRCK/img.png?width=928&amp;amp;height=813&amp;amp;face=0_0_928_813');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Source Link and .NET libraries&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Best practice recommendations for using Source Link to improve debugging for .NET libraries.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;만약 source link가 작동하지 않으면 아래 설정에서 source link의 설정여부를 확인해 보시기 바랍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nAzcP/btsEXtIbW9D/Cp8djO77kjQ8tp10c9x6cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nAzcP/btsEXtIbW9D/Cp8djO77kjQ8tp10c9x6cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nAzcP/btsEXtIbW9D/Cp8djO77kjQ8tp10c9x6cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnAzcP%2FbtsEXtIbW9D%2FCp8djO77kjQ8tp10c9x6cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;434&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 기술적으로 decompile을 막을 수 있는 방법은 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가는 compile 된 code에서 decompile을 막을 수는 없을까 하는 의문을 제기할 수도 있을 것입니다. 이 질문에 정확하게 답하자면 '그렇게 할 수 없다.'라고 할 수 있습니다. 대신 Dotfuscator와 같은 도구를 사용해 code를 읽기 어렵게 만들 수는 있지만 decompile자체는 막을 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 compile 된 application은 동작의 대상이 되는 platform, OS 그리고 hardware에 대한 명령을 포함하고 있습니다. 이들 명령은 기능적으로 본래 source code와 일치하지만 사람에게만 읽기 어렵게 만들수 있을 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source code를 보호하고자 한다면 여기에 debugger을 연결하고 단계적으로 code를 통과하는 것도 막을 수 있습니다. compile된 application이 pdb file을 가지고 있다면 debugger를 연결하고 구문을 한 줄씩 실행시킬 수도 있습니다. pdb file이 없어도 여전히 debugger를 연결하고 code가 어떻게 작동하는지를 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 decompile에 관한 이러한 내용은 C#, Visual Basic, F#과 같은 .NET언어 뿐만 아니라 C, C++, Delphi 및 assembly 언어(debugging에 연결하거나 disassembled 또는 decompile 될 수 있는)에도 해당하는 사실입니다. 아래 표는 해당 목적을 위해 사용하는 일부 도구를 나타내고 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.2403%; height: 20px;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%; height: 20px;&quot;&gt;도구&lt;/td&gt;
&lt;td style=&quot;width: 71.124%; height: 20px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.2403%; height: 17px;&quot;&gt;Debugger&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%; height: 17px;&quot;&gt;SoftICE&lt;/td&gt;
&lt;td style=&quot;width: 71.124%; height: 17px;&quot;&gt;운영체제(일반적으로 VM) 아래에서 실행됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.2403%; height: 17px;&quot;&gt;Debugger&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%; height: 17px;&quot;&gt;WinDbg&lt;/td&gt;
&lt;td style=&quot;width: 71.124%; height: 17px;&quot;&gt;다른 debugger보다 Windows data 구조에 관한 더 많은 것을 이해하는데 유용한 도구입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Disassembler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;IDA Pro&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;malware 분석등에 사용됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Decompiler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;HexRays&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;IDA Pro의 plugin이며 C app을 decompile합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Decompiler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;DeDe&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;Delphi app을 decompile합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;Decompiler&lt;/td&gt;
&lt;td style=&quot;width: 12.6356%;&quot;&gt;dotPeek&lt;/td&gt;
&lt;td style=&quot;width: 71.124%;&quot;&gt;JetBrains사에서 만든 것으로 .NET decompiler입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 누군가의 software를 Debugging, disassembling, decompiling 하는 행위는 license동의에 반하며 불법일 수 있습니다. 기술적인 방법으로 지적재산권을 보호하는 대신 법이 유일단 수단이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. .NET Framework 에서 .NET으로의 이식&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 .NET Framework 개발자라면 아마도 기존의 application을 .NET으로 전환하고자 할 수 있을 것입니다. 하지만 정말 .NET으로의 전환이 가장 좋은 방법인지는 심도 있게 고려해봐야 합니다. 단지 .NET으로의 이식만이 최상의 방법이 되지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 .NET Framework 4.8에서 동작하는 사용자가 그리 많지 않은 website project가 있습니다. 그러면 해당 website는 최소한의 hardware상에서 방문자 traffic은 충분히 제어되고 작 잘 동할 수 있을 텐데 이것을 .NET platform으로 전환하는데 잠재적으로 수개월을 소요하는 것은 시간낭비가 될 수 있습니다. 하지만 website가 현재 다수의 고비용 Windows Server가 필요하다면 더 비용이 적은 Linux server로 migration 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 전환 가능한 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 Windows, macOS, Linux에 대한 아래 type의 application을 지원하고 있으며 때문에 전환이 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Razor Pages와 MVC를 포함한 ASP.NET Core web site&lt;/li&gt;
&lt;li&gt;Web API, Minimal API, OData를 포함하는 ASP.NET Core web service(REST/HTTP)&lt;/li&gt;
&lt;li&gt;gRPC, GraphQL, SignalR을 포함하는 ASP.NET Core-hoste&lt;/li&gt;
&lt;li&gt;명령행 interface인 Console App포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 Windows에 대한 아래 Type의 application을 지원하고 있으며 운영에 적합이 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows Forms applications&lt;/li&gt;
&lt;li&gt;Windows Presentation Foundation (WPF) applications&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 cross-platform desktop과 mobile device에 대한 아래 type의 application을 지원하고 있으며 전환이 가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mobile iOS와 Android를 위한 Xamarin app&lt;/li&gt;
&lt;li&gt;Desktop Windows와 macOS 또는 mobile iOS와 Android를 위한 .NET MAUI&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET은 아래 legacy Microsoft project에 대한 지원을 하지 않으며 전환이 불가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ASP.NET Web Forms website. (ASP.NET Core Razor Page 또는 Blazor로의 재구현이 필요합니다.)&lt;/li&gt;
&lt;li&gt;Windows Communication Foundation (WCF ) service (단, 요구사항에 따라 사용가능한 open-source project인 CoreWCF가 존재합니다.). (ASP.NET Core gRPC service로의 재구현이 필요합니다.)&lt;/li&gt;
&lt;li&gt;Silverligth application. (Blazor 또는 .NET MAUI로의 재구현이 필요합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Silverlight와 ASP.NET Web Forms application은 .NET으로의 전환이 불가능하며 기존의 Windows Forms와 WPF application을 WIndows전용의 .NET으로 전환하면 이에 따른 새로운 API와 고성능에 대한 이점을 가져갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Legacy ASP.NET MVC web application과 ASP.NET Web API web service역시 .NET으로의 전환이 가능하며 Windows, Linux 또는 macOS에서 host가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 전환해야 하는 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전환이 가능하다면 그렇게 하는 것이 맞을까? 그렇게 한다면 어떤 이익을 얻을 수 있을까? 전환함으로써 얻을 수 있는 장점으로는 대략 다음과 같이 정리할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Website와 webservice에 대한 Linux, Docker 또는 Kubernetes로의 배포 : 해당 OS들은 website와 web service platform으로서 매우 가볍고 특히 Windows Server와 비교해서는 훨씬 비용 효과적이라고 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;IIS와 System.Web.dll에 대한 의존성 제거 : Windows Server로의 배포를 계속한다고 하더라도 ASP.NET Core는 Kestrel web server(또는 다른 것도 마찬가지)에서 가벼우면서도 고성능의 hosting이 가능합니다.&lt;/li&gt;
&lt;li&gt;명령줄 도구 : 개발자와 관리자가 자신들의 작업을 자동화하기 위해 사용하는 도구는 종종 console application으로 build 되는데 cross-platform에서 동작가능한 단일 도구는 그 자체로 매우 유용하게 사용될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) .NET Framework와 .NET의 차이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들 사이에는 대략 3가지 핵심적인 차이가 존재합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.NET&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.NET Framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;NuGet package로 배포되어 있으며 각 application은 필요한 .NET version의 자체 app local 복사를 통해 배포될 수 있습니다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;System전반에 배포되어 있으며 assembly들은 공유됩니다.(GAC-Global Assembly Cache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Component 계층으로 작게 나누어져 있으므로 최소한의 배포가 가능합니다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;단일적이며 획일적인 배포만이 가능합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;ASP.NET Web Forms와 같은 고전 기술과 AppDomains, .NET Remoting, binary serialization과 같은 비 cross-platform 기능들이 제거되었습니다.&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.NET에서의 ASP.NET Core MVC와 같은 비슷한 기능뿐만 아니라 ASP.NET Web Forms와 같은 고전적인 기술까지도 남아 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) .NET Portability Analyzer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 기존의 application에 대한 이식성의 보고서를 생성하는 유용한 도구를 지원하고 있습니다. 아래 link에서 해당 도구의 시연장면을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot;&gt;A Brief Look at the .NET Portability Analyzer | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687770527535&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;A Brief Look at the .NET Portability Analyzer&quot; data-og-description=&quot;.NET is going through some monumental changes! It is often difficult to even begin to think about how to port our particular code to the various flavors of .NET. In this video Taylor Southwick and Connie Yau spend some time discussing a great tool that wil&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Z05DC/hyS6yC2ys6/GcVxoFpWnHbmElbGKt4kVk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/shows/seth-juarez/brief-look-net-portability-analyzer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Z05DC/hyS6yC2ys6/GcVxoFpWnHbmElbGKt4kVk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;A Brief Look at the .NET Portability Analyzer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;.NET is going through some monumental changes! It is often difficult to even begin to think about how to port our particular code to the various flavors of .NET. In this video Taylor Southwick and Connie Yau spend some time discussing a great tool that wil&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) .NET Upgrade Assistant&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Legacy project를 .NET으로 upgrade 하기 위한 가장 최신의 도구로는 .NET Upgrade Assistant가 있습니다. 현재 다음과 같은 .NET Framework project type을 지원하고 있으며 향후에는 더 추가될 것으로 예상됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ASP.NET MVC&lt;/li&gt;
&lt;li&gt;Windows Forms&lt;/li&gt;
&lt;li&gt;WPF&lt;/li&gt;
&lt;li&gt;Console Application&lt;/li&gt;
&lt;li&gt;Class Library&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 dotnet 도구를 통해 .NET Upgrade Assistant를 설치하려면 아래의 명령을 내려줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;dotnet tool install -g upgrade-assistant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 link를 통해 .NET Upgrade Assistant에 대한 더 자세한 내용과 사용법을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot;&gt;Overview of the .NET Upgrade Assistant - .NET Core | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687771077397&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Overview of the .NET Upgrade Assistant - .NET Core&quot; data-og-description=&quot;Introducing the .NET Upgrade Assistant tool that helps upgrade .NET, .NET Core, or .NET Framework apps to the latest version of .NET.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dY4CmY/hyS8mucF5u/mZmveqrK566AAsVk4d1ltk/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456,https://scrap.kakaocdn.net/dn/sDIIl/hyS6AOoaQT/x2VDL28UPSKR6kuCTUWrlk/img.png?width=925&amp;amp;height=640&amp;amp;face=0_0_925_640,https://scrap.kakaocdn.net/dn/dEIL62/hyS6wFbuDd/dWVGqzblHyGXd0WU1XpYc1/img.png?width=995&amp;amp;height=442&amp;amp;face=0_0_995_442&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dY4CmY/hyS8mucF5u/mZmveqrK566AAsVk4d1ltk/img.png?width=456&amp;amp;height=456&amp;amp;face=0_0_456_456,https://scrap.kakaocdn.net/dn/sDIIl/hyS6AOoaQT/x2VDL28UPSKR6kuCTUWrlk/img.png?width=925&amp;amp;height=640&amp;amp;face=0_0_925_640,https://scrap.kakaocdn.net/dn/dEIL62/hyS6wFbuDd/dWVGqzblHyGXd0WU1XpYc1/img.png?width=995&amp;amp;height=442&amp;amp;face=0_0_995_442');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Overview of the .NET Upgrade Assistant - .NET Core&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Introducing the .NET Upgrade Assistant tool that helps upgrade .NET, .NET Core, or .NET Framework apps to the latest version of .NET.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(6) 비 .NET Standard library 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NuGet package는 .NET Standard 또는 .NET 7과 같은 version을 통해 compile 되지 않은 것이라 하더라도 대부분 .NET에서 사용될 수 있습니다. 만약 공식적으로 .NET Standard를 지원하지 않는 package라고 nuget.org web page에 표시된 package가 있어도 단정적으로 사용 못할 것이라 포기할 필요는 없습니다. 일단 시도해 보면서 되는지 안되는지의 여부를 판단해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같이 문서화된 것으로, Dialect Software LLC에서 제작된 행렬처리를 위한 사용자 지정 collection package는 .NET Core나 .NET 7이 존재하기 훨씬 이전인 2013년도가 마지막 update로 확인됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot;&gt;NuGet Gallery | DialectSoftware.Collections.Matrix 1.0.0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687833904902&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;nugetgallery:package&quot; data-og-title=&quot;DialectSoftware.Collections.Matrix 1.0.0&quot; data-og-description=&quot;Provides matrix data structure suitable for storing elements in an n-dimensional cartesian coordinate system of positive integers. example: Axis x = new Axis(&amp;quot;x&amp;quot;, 0, 100, 1); Axis y = new Axis(&amp;quot;y&amp;quot;, 0, 10, 1); Matrix&amp;lt;long&amp;gt; matrix = new Matrix&amp;lt;long&amp;gt;(new []{x&quot; data-og-host=&quot;www.nuget.org&quot; data-og-source-url=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot; data-og-url=&quot;https://nuget.org/packages/DialectSoftware.Collections.Matrix/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nuget.org/packages/DialectSoftware.Collections.Matrix/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DialectSoftware.Collections.Matrix 1.0.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides matrix data structure suitable for storing elements in an n-dimensional cartesian coordinate system of positive integers. example: Axis x = new Axis(&quot;x&quot;, 0, 100, 1); Axis y = new Axis(&quot;y&quot;, 0, 10, 1); Matrix&amp;lt;long&amp;gt; matrix = new Matrix&amp;lt;long&amp;gt;(new []{x&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nuget.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 해당 package는 .NET Framework로 build 된 것이며 이와 같은 package가 .NET Standard에서만 가능한 API만 사용하는 경우 .NET project에서도 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AssembliesAndNamespaces project에서 아래와 같이 Dialect Software의 package를 참조 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687835234407&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include=&quot;DialectSoftware.Collections.Matrix&quot; Version=&quot;1.0.0&quot; /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Program.cs에서 DialectSoftware.Collections과 DialectSoftware.Collections.Generics를 import 하고 아래와 같이 Axis와 Matrix&amp;lt;T&amp;gt;의 instance를 생성하고 값을 채워 넣은 다음 해당 값을 표시하는 문을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687835395061&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Axis x = new Axis(&quot;x&quot;, 0, 100, 1);
Axis y = new Axis(&quot;y&quot;, 0, 10, 1);

Matrix&amp;lt;long&amp;gt; matrix = new Matrix&amp;lt;long&amp;gt;(new[] { x, y });

int i = 0;
for (; i &amp;lt; matrix.Axes[0].Points.Length; i++)
{
    matrix.Axes[0].Points[i].Label = &quot;x&quot; + i.ToString();
}

i = 0;
for (; i &amp;lt; matrix.Axes[1].Points.Length; i++)
{
    matrix.Axes[1].Points[i].Label = &quot;y&quot; + i.ToString();
}

foreach (long[] c in matrix)
{
    matrix[c] = c[0] + c[1];
}

foreach (long[] c in matrix)
{
    Console.WriteLine(&quot;{0},{1} ({2},{3}) = {4}&quot;, matrix.Axes[0].Points[c[0]].Label, matrix.Axes[1].Points[c[1]].Label, c[0], c[1], matrix[c]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project를 실행하면 다음과 같은 결과를 표시할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oUPkw/btsEFjT6cg5/D6T8KsEst2hdIZ7Kx5QQe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oUPkw/btsEFjT6cg5/D6T8KsEst2hdIZ7Kx5QQe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oUPkw/btsEFjT6cg5/D6T8KsEst2hdIZ7Kx5QQe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoUPkw%2FbtsEFjT6cg5%2FD6T8KsEst2hdIZ7Kx5QQe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;512&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. Preview 기능 사용하기&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft는 주기적으로 runtime, 언어 compiler, API library등 .NET의 많은 부분에 전역적으로 영향을 미치는 새로운 기능을 제공합니다. 그런데 실질적으로 Microsoft가 기능에 필요한 대부분의 작업을 완료했다고 하더라도 .NET release에 대한 주기 때까지는 배포가 이루어지지 않을 것입니다. 문제는 이렇게 되면 실전에서 적절한 test를 하기에는 너무 늦다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 .NET 6부터 Microsoft는 GA(general availability) release에서는 preview 기능을 포함하게 되었습니다. 개발자는 이들 미리 보기 기능을 선택하고 Microsoft에게 feedback을 제공할 수 있습니다. 최신 GA release는 모두가 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Preview 기능에 관한 내용임에 주의해야 합니다. .NET 혹은 Visual Studio에 대한 preview version과는 다릅니다. Microsoft는 Visual Studio와 .NET에 대한 preview version을 개발자들로부터 feedback을 얻고자 개발 중에 release 하며 그런 뒤 최종 GA를 release 합니다. 이때 GA가 모두가 사용가능한 기능이 되는 것입니다. GA이전에는 새로운 기능을 경험해 볼 수 있는 방법은 preview version을 설치하는 것입니다. Preview 기능은 GA release와 함께 설치되며 선택적으로 사용가능해야 하므로 다르다고 할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Microsoft는 .NET SDK 6.0.200를 2022년 02월에 release 했는데 이것은 C# 11 compiler를 preview 기능으로서 포함시켰습니다. 이는 .NET 6 개발자가 선택적으로 언어의 version을 preview로 설정할 수 있었음을 의미하며 이것으로 raw string literal과 required keyword 같은 C# 11 기능을 사용해 볼 수 있었습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Preivew 기능은 production code에서는 지원하지 않으며 최종 release전에 대폭적인 변화가 있을 가능성이 있습니다. 따라서 오로지 test목적으로만 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) Preview 기능이 필요한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[RequiresPreviewFeatures] attribute는 사용하는 assembly, type 또는 member를 나타내는 데 사용되며 Code analyzer는 해당 assembly를 scan 하고 필요하다면 경고를 생성하게 됩니다. 만약 code에서 어떠한 preview 기능도 사용하지 않는다면 어떠한 경고 message도 보지 않게 될 테지만 그렇지 않다면 preview 기능을 사용한다는 경고를 해당 code를 사용하는 이에게 표시하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Preview 기능 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;csproj(project file)에서 아래와 같이 preview 기능과 preview 언어 기능을 사용하기 위한 요소를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687851759267&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
&amp;lt;EnablePreviewFeatures&amp;gt;true&amp;lt;/EnablePreviewFeatures&amp;gt;
&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>.NET/C#</category>
      <category>.NET</category>
      <category>AOT</category>
      <category>c#</category>
      <category>Library</category>
      <category>NuGet</category>
      <category>package</category>
      <category>Preview</category>
      <category>publish</category>
      <author>클리엘</author>
      <guid isPermaLink="true">https://cliel.tistory.com/597</guid>
      <comments>https://cliel.tistory.com/entry/XFile#entry597comment</comments>
      <pubDate>Tue, 20 Feb 2024 09:11:50 +0900</pubDate>
    </item>
  </channel>
</rss>