<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>.NET Archives - 어제와 내일의 나 그 사이의 이야기</title>
	<atom:link href="https://lycos7560.com/tag/net/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>생각의 흐름을 타고 다니며 만드는 블로그</description>
	<lastBuildDate>Tue, 25 Nov 2025 15:17:43 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://lycos7560.com/wp-content/uploads/2022/11/cropped-cropped-cropped-log-1-150x150-1-80x80.png</url>
	<title>.NET Archives - 어제와 내일의 나 그 사이의 이야기</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>C# Switch 식(Expression)</title>
		<link>https://lycos7560.com/c/c-switch-%ec%8b%9dexpression/40362/</link>
					<comments>https://lycos7560.com/c/c-switch-%ec%8b%9dexpression/40362/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Tue, 11 Nov 2025 15:02:40 +0000</pubDate>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[!=]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.NET 5]]></category>
		<category><![CDATA[.NET 6]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[>=]]></category>
		<category><![CDATA[<=]]></category>
		<category><![CDATA[==]]></category>
		<category><![CDATA[2019]]></category>
		<category><![CDATA[2020]]></category>
		<category><![CDATA[access]]></category>
		<category><![CDATA[action]]></category>
		<category><![CDATA[Admin]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Arrow Operator]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[Boolean]]></category>
		<category><![CDATA[Branch]]></category>
		<category><![CDATA[Branching]]></category>
		<category><![CDATA[C#7]]></category>
		<category><![CDATA[C#8]]></category>
		<category><![CDATA[C#9]]></category>
		<category><![CDATA[Case]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[Clean Code]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Comparison]]></category>
		<category><![CDATA[Compile]]></category>
		<category><![CDATA[Concise]]></category>
		<category><![CDATA[Condition]]></category>
		<category><![CDATA[Conditional]]></category>
		<category><![CDATA[Control]]></category>
		<category><![CDATA[Control Flow]]></category>
		<category><![CDATA[CSharp]]></category>
		<category><![CDATA[Declarative]]></category>
		<category><![CDATA[Declarative Style]]></category>
		<category><![CDATA[Default]]></category>
		<category><![CDATA[delegate]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[Developer Guide]]></category>
		<category><![CDATA[Docs]]></category>
		<category><![CDATA[Documentation]]></category>
		<category><![CDATA[dotnet]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[Error Handling]]></category>
		<category><![CDATA[Example]]></category>
		<category><![CDATA[Exception]]></category>
		<category><![CDATA[Execution]]></category>
		<category><![CDATA[Expression]]></category>
		<category><![CDATA[Expression Body]]></category>
		<category><![CDATA[Expression Tree]]></category>
		<category><![CDATA[False]]></category>
		<category><![CDATA[Feature]]></category>
		<category><![CDATA[Flow]]></category>
		<category><![CDATA[Framework]]></category>
		<category><![CDATA[Function Pointer]]></category>
		<category><![CDATA[Functional]]></category>
		<category><![CDATA[Functional Programming]]></category>
		<category><![CDATA[Grammar]]></category>
		<category><![CDATA[Guide]]></category>
		<category><![CDATA[History]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Imperative]]></category>
		<category><![CDATA[Intuitive]]></category>
		<category><![CDATA[Lambda]]></category>
		<category><![CDATA[Language Evolution]]></category>
		<category><![CDATA[Language Feature]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Library]]></category>
		<category><![CDATA[Logical Pattern]]></category>
		<category><![CDATA[Maintainable]]></category>
		<category><![CDATA[Mapping]]></category>
		<category><![CDATA[method]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Migration]]></category>
		<category><![CDATA[Modern C#]]></category>
		<category><![CDATA[Modern Programming]]></category>
		<category><![CDATA[New Feature]]></category>
		<category><![CDATA[November]]></category>
		<category><![CDATA[Null]]></category>
		<category><![CDATA[Null Check]]></category>
		<category><![CDATA[Nullable]]></category>
		<category><![CDATA[Object]]></category>
		<category><![CDATA[Object-Oriented]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Operator]]></category>
		<category><![CDATA[Pattern Matching]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Property]]></category>
		<category><![CDATA[Property Pattern]]></category>
		<category><![CDATA[Range]]></category>
		<category><![CDATA[Readable]]></category>
		<category><![CDATA[Refactor]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[Relational Pattern]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[Return Value]]></category>
		<category><![CDATA[Role]]></category>
		<category><![CDATA[Running]]></category>
		<category><![CDATA[Runtime]]></category>
		<category><![CDATA[Sample Code]]></category>
		<category><![CDATA[SDK]]></category>
		<category><![CDATA[September]]></category>
		<category><![CDATA[Simplify]]></category>
		<category><![CDATA[Snippet]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[State]]></category>
		<category><![CDATA[Statement]]></category>
		<category><![CDATA[Stopped]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Switch Action]]></category>
		<category><![CDATA[Switch Branch]]></category>
		<category><![CDATA[Switch Case]]></category>
		<category><![CDATA[Switch Clean]]></category>
		<category><![CDATA[Switch Default]]></category>
		<category><![CDATA[Switch Delegate]]></category>
		<category><![CDATA[Switch Docs]]></category>
		<category><![CDATA[Switch Evolution]]></category>
		<category><![CDATA[Switch Example]]></category>
		<category><![CDATA[Switch Expression]]></category>
		<category><![CDATA[Switch Expression Action]]></category>
		<category><![CDATA[Switch Expression C#]]></category>
		<category><![CDATA[Switch Expression Clean]]></category>
		<category><![CDATA[Switch Expression Default]]></category>
		<category><![CDATA[Switch Expression Delegate]]></category>
		<category><![CDATA[Switch Expression Docs]]></category>
		<category><![CDATA[Switch Expression Education]]></category>
		<category><![CDATA[Switch Expression Evolution]]></category>
		<category><![CDATA[Switch Expression Example]]></category>
		<category><![CDATA[Switch Expression Guide]]></category>
		<category><![CDATA[Switch Expression History]]></category>
		<category><![CDATA[Switch Expression Lambda]]></category>
		<category><![CDATA[Switch Expression Learning]]></category>
		<category><![CDATA[Switch Expression Logical]]></category>
		<category><![CDATA[Switch Expression Migration]]></category>
		<category><![CDATA[Switch Expression Modern]]></category>
		<category><![CDATA[Switch Expression Null]]></category>
		<category><![CDATA[Switch Expression Pattern]]></category>
		<category><![CDATA[Switch Expression Property]]></category>
		<category><![CDATA[Switch Expression Relational]]></category>
		<category><![CDATA[Switch Expression Sample]]></category>
		<category><![CDATA[Switch Expression Simplify]]></category>
		<category><![CDATA[Switch Expression Snippet]]></category>
		<category><![CDATA[Switch Expression Study]]></category>
		<category><![CDATA[Switch Expression Tuple]]></category>
		<category><![CDATA[Switch Expression Tutorial]]></category>
		<category><![CDATA[Switch Expression Upgrade]]></category>
		<category><![CDATA[Switch Expression Version]]></category>
		<category><![CDATA[Switch Expression Wildcard]]></category>
		<category><![CDATA[Switch Feature]]></category>
		<category><![CDATA[Switch Flow]]></category>
		<category><![CDATA[Switch Function]]></category>
		<category><![CDATA[Switch Guide]]></category>
		<category><![CDATA[Switch Keyword]]></category>
		<category><![CDATA[Switch Lambda]]></category>
		<category><![CDATA[Switch Logical]]></category>
		<category><![CDATA[Switch Mapping]]></category>
		<category><![CDATA[Switch Modern]]></category>
		<category><![CDATA[Switch Null]]></category>
		<category><![CDATA[Switch Operator]]></category>
		<category><![CDATA[Switch Pattern]]></category>
		<category><![CDATA[Switch Property]]></category>
		<category><![CDATA[Switch Relational]]></category>
		<category><![CDATA[Switch Simplify]]></category>
		<category><![CDATA[Switch Statement]]></category>
		<category><![CDATA[Switch Syntax]]></category>
		<category><![CDATA[Switch Tuple]]></category>
		<category><![CDATA[Switch Tutorial]]></category>
		<category><![CDATA[Switch Wildcard]]></category>
		<category><![CDATA[Syntax]]></category>
		<category><![CDATA[System]]></category>
		<category><![CDATA[True]]></category>
		<category><![CDATA[tuple]]></category>
		<category><![CDATA[Tuple Pattern]]></category>
		<category><![CDATA[TUTORIAL]]></category>
		<category><![CDATA[Upgrade]]></category>
		<category><![CDATA[User]]></category>
		<category><![CDATA[Value]]></category>
		<category><![CDATA[Variable]]></category>
		<category><![CDATA[version]]></category>
		<category><![CDATA[Visual studio]]></category>
		<category><![CDATA[VS Code]]></category>
		<category><![CDATA[Wildcard]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40362</guid>

					<description><![CDATA[<p>C# Switch 식(Expression) C# 8.0(2019년 9월)부터 도입된 Switch 식(Switch Expression)은 기존의 switch-case 문을 획기적으로 줄여주는 강력한 기능 람다 표현식(=>)을 쓰는 것처럼 간결하고 직관적인 분기 처리가 가능 기존 방식 (Statement) 새로운 방식 (Expression) 강력한 활용 패턴 C# 9.0(2020년 11월)부터 Switch 식이 단순 매핑이 아니라 패턴 매칭(Pattern Matching)과 결합됨 범위 비교 (Relational Pattern) C# 9.0부터는 when 키워드 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/c-switch-%ec%8b%9dexpression/40362/">C# Switch 식(Expression)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-1bf84b0a      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#c-switch-식expression" class="uagb-toc-link__trigger">C# Switch 식(Expression)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#기존-방식-statement" class="uagb-toc-link__trigger">기존 방식 (Statement)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#새로운-방식-expression" class="uagb-toc-link__trigger">새로운 방식 (Expression)</a></li></ul></li><li class="uagb-toc__list"><a href="#강력한-활용-패턴" class="uagb-toc-link__trigger">강력한 활용 패턴</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#범위-비교-relational-pattern" class="uagb-toc-link__trigger">범위 비교 (Relational Pattern)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#여러-변수-동시에-비교-tuple-pattern" class="uagb-toc-link__trigger">여러 변수 동시에 비교 (Tuple Pattern)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#객체-속성-비교-property-pattern" class="uagb-toc-link__trigger">객체 속성 비교 (Property Pattern)</a></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#리턴값이-없는-메서드action-실행" class="uagb-toc-link__trigger">리턴값이 없는 메서드(Action) 실행</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">C# Switch 식(Expression)</h2>



<p>C# 8.0(2019년 9월)부터 도입된 <strong>Switch 식(Switch Expression)</strong>은 기존의 <code><strong>switch-case</strong></code> 문을 획기적으로 줄여주는 강력한 기능</p>



<p>람다 표현식(<code>=></code>)을 쓰는 것처럼 간결하고 직관적인 분기 처리가 가능</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">기존 방식 (Statement)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public string GetGrade(int score)
{
    string grade;
    switch (score)
    {
        case 90:
            grade = "A";
            break;
        case 80:
            grade = "B";
            break;
        default:
            grade = "C";
            break;
    }
    return grade;
}</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">새로운 방식 (Expression)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public string GetGrade(int score) => score switch
{
    90 => "A",
    80 => "B",
    _  => "C"
};


또는 다른 범위 방식으로 (결과는 다름) 
score switch
{
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    _     => "D"
};
</pre>



<ul class="wp-block-list">
<li><strong>위치 이동:</strong> <code>switch</code> 키워드가 변수 <strong>뒤</strong>로 이동 (<code>score switch</code>)</li>



<li><strong>화살표 사용:</strong> <code>case ... :</code> 대신 람다 화살표 <code>=></code>를 사용</li>



<li><strong>Default 처리:</strong> <code>default:</code> 대신 와일드카드 <code>_</code>를 사용</li>



<li><strong>식(Expression):</strong> 그 자체가 &#8216;값&#8217;을 반환하므로 메서드의 리턴값으로 바로 사용할 수 있음</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">강력한 활용 패턴</h2>



<p>C# 9.0(2020년 11월)부터 Switch 식이 단순 매핑이 아니라 <strong>패턴 매칭(Pattern Matching)</strong>과 결합됨</p>



<ul class="wp-block-list">
<li><strong>Switch Expression 자체는 C# 8에서 처음 도입</strong></li>



<li><strong>Pattern Matching 자체는 C# 7부터 지원</strong></li>



<li><strong>Relational Pattern, Logical Pattern 등이 C# 9에서 추가</strong></li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">범위 비교 (Relational Pattern)</h3>



<p>C# 9.0부터는 <code>when</code> 키워드 없이도 관계 연산자를 직접 사용</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">int temperature = 28;

string weather = temperature switch
{
    >= 30 => "폭염",
    >= 20 => "쾌적함",
    >= 10 => "쌀쌀함",
    _     => "추움"
};</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">여러 변수 동시에 비교 (Tuple Pattern)</h3>



<p>두 개 이상의 상태를 조합해서 분기해야 할 때, <code>if-else</code> 중첩 없이 튜플 패턴으로 깔끔하게 해결</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">string state = "Running";
bool hasError = true;

// (상태, 에러여부)를 한 번에 검사
string message = (state, hasError) switch
{
    ("Running", false) => "정상 작동 중",
    ("Running", true)  => "작동 중이나 오류 발생",
    ("Stopped", _)     => "시스템 중지됨", // _는 true/false 상관없음
    _                  => "알 수 없는 상태"
};</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">객체 속성 비교 (Property Pattern)</h3>



<p>객체 내부의 특정 프로퍼티 값에 따라 분기할 수도 있음 </p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var user = new User { Role = "Admin", IsDeleted = false };

string accessLevel = user switch
{
    { Role: "Admin", IsDeleted: false } => "관리자 권한",
    { Role: "User", IsDeleted: false }  => "일반 사용자",
    { IsDeleted: true }                 => "삭제된 계정",
    _                                   => "접근 불가" // null 포함
};

// 객체가 null인 경우도 자동으로 _</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">리턴값이 없는 메서드(Action) 실행</h2>



<p><code>Action</code> 델리게이트를 반환받아 즉시 실행</p>



<p>switch expression은 &#8220;값&#8221;을 반환하므로 델리게이트(함수 포인터)를 반환하는 것도 가능</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">int command = 1;

// 1. 각 조건에 맞는 Action(행동)을 선택하고
// 2. 마지막의 ()로 즉시 실행합니다.
(command switch
{
    1 => (Action)(() => SaveData()),
    2 => () => LoadData(),
    _ => () => Console.WriteLine("대기")
})(); 
// 마지막 ()는 "반환된 델리게이트 실행"</pre>



<p></p>
<p>The post <a href="https://lycos7560.com/c/c-switch-%ec%8b%9dexpression/40362/">C# Switch 식(Expression)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/c-switch-%ec%8b%9dexpression/40362/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ASP.NET Core Identity를 활용한 구글 로그인(OAuth) 전체 흐름 분석</title>
		<link>https://lycos7560.com/c/asp-net/asp-net-core-identity%eb%a5%bc-%ed%99%9c%ec%9a%a9%ed%95%9c-%ea%b5%ac%ea%b8%80-%eb%a1%9c%ea%b7%b8%ec%9d%b8oauth-%ec%a0%84%ec%b2%b4-%ed%9d%90%eb%a6%84-%eb%b6%84%ec%84%9d/40245/</link>
					<comments>https://lycos7560.com/c/asp-net/asp-net-core-identity%eb%a5%bc-%ed%99%9c%ec%9a%a9%ed%95%9c-%ea%b5%ac%ea%b8%80-%eb%a1%9c%ea%b7%b8%ec%9d%b8oauth-%ec%a0%84%ec%b2%b4-%ed%9d%90%eb%a6%84-%eb%b6%84%ec%84%9d/40245/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Fri, 08 Aug 2025 07:45:23 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[2FA]]></category>
		<category><![CDATA[Account]]></category>
		<category><![CDATA[AccountManagement]]></category>
		<category><![CDATA[action]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[appsettings]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Backend]]></category>
		<category><![CDATA[Blazor]]></category>
		<category><![CDATA[Callback]]></category>
		<category><![CDATA[CD]]></category>
		<category><![CDATA[Challenge]]></category>
		<category><![CDATA[CI]]></category>
		<category><![CDATA[CICD]]></category>
		<category><![CDATA[Claim]]></category>
		<category><![CDATA[ClaimsPrincipal]]></category>
		<category><![CDATA[ClientSide]]></category>
		<category><![CDATA[CloudComputing]]></category>
		<category><![CDATA[CloudNative]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[Cookie]]></category>
		<category><![CDATA[Core]]></category>
		<category><![CDATA[Cryptography]]></category>
		<category><![CDATA[CSharp]]></category>
		<category><![CDATA[DataBase]]></category>
		<category><![CDATA[DataTransferObject]]></category>
		<category><![CDATA[DbContext]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[DTO]]></category>
		<category><![CDATA[EmailConfirmation]]></category>
		<category><![CDATA[EntityFramework]]></category>
		<category><![CDATA[ErrorHandling]]></category>
		<category><![CDATA[Exception]]></category>
		<category><![CDATA[ExternalLogin]]></category>
		<category><![CDATA[ExternalProvider]]></category>
		<category><![CDATA[Frontend]]></category>
		<category><![CDATA[FullStack]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[GitLab]]></category>
		<category><![CDATA[GoogleAuth]]></category>
		<category><![CDATA[GoogleCloud]]></category>
		<category><![CDATA[GoogleLogin]]></category>
		<category><![CDATA[HTTP302]]></category>
		<category><![CDATA[HttpGet]]></category>
		<category><![CDATA[HttpPost]]></category>
		<category><![CDATA[HttpRequest]]></category>
		<category><![CDATA[HttpResponse]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Identity]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[JSONWebToken]]></category>
		<category><![CDATA[JWT]]></category>
		<category><![CDATA[Kestrel]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Log]]></category>
		<category><![CDATA[Logging]]></category>
		<category><![CDATA[Login]]></category>
		<category><![CDATA[Logout]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[MicrosoftIdentity]]></category>
		<category><![CDATA[Middleware]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[OAuth2.0]]></category>
		<category><![CDATA[OpenID]]></category>
		<category><![CDATA[OpenIDConnect]]></category>
		<category><![CDATA[ORM]]></category>
		<category><![CDATA[password]]></category>
		<category><![CDATA[Passwordless]]></category>
		<category><![CDATA[path]]></category>
		<category><![CDATA[pipeline]]></category>
		<category><![CDATA[Production]]></category>
		<category><![CDATA[Profile]]></category>
		<category><![CDATA[QueryString]]></category>
		<category><![CDATA[Redirect]]></category>
		<category><![CDATA[RedirectResponse]]></category>
		<category><![CDATA[Register]]></category>
		<category><![CDATA[ResetPassword]]></category>
		<category><![CDATA[RESTfulAPI]]></category>
		<category><![CDATA[Route]]></category>
		<category><![CDATA[Routing]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SecurityToken]]></category>
		<category><![CDATA[ServerSide]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[SignInManager]]></category>
		<category><![CDATA[SignUp]]></category>
		<category><![CDATA[SinglePageApplication]]></category>
		<category><![CDATA[SocialLogin]]></category>
		<category><![CDATA[SPA]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[stage]]></category>
		<category><![CDATA[Startup]]></category>
		<category><![CDATA[StateManagement]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Token]]></category>
		<category><![CDATA[TryCatch]]></category>
		<category><![CDATA[TwoFactor]]></category>
		<category><![CDATA[URI]]></category>
		<category><![CDATA[url]]></category>
		<category><![CDATA[UserDB]]></category>
		<category><![CDATA[UserManagement]]></category>
		<category><![CDATA[UserManager]]></category>
		<category><![CDATA[UserProfile]]></category>
		<category><![CDATA[UserStore]]></category>
		<category><![CDATA[VisualStudio]]></category>
		<category><![CDATA[VSCode]]></category>
		<category><![CDATA[WebAPI]]></category>
		<category><![CDATA[WebApp]]></category>
		<category><![CDATA[WebApplication]]></category>
		<category><![CDATA[WebDevelopment]]></category>
		<category><![CDATA[WebSecurity]]></category>
		<category><![CDATA[WebServer]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40245</guid>

					<description><![CDATA[<p>🔥 ASP.NET Core Identity를 활용한 구글 로그인(OAuth) 전체 흐름 분석 1️⃣ 사용자가 &#8220;Google로 로그인&#8221; 버튼을 클릭 Navigation.NavigateTo(..., forceLoad: true) Blazor의 내부 라우팅이 아닌, 브라우저가 직접 해당 URL(api/auth/Challenge/Google...)로 GET 요청보낸다. 2️⃣ 백엔드의 인증 시작 (AuthController &#8211; Challenge) 3️⃣ 외부 공급자 인증 (Google) 사용자는 Google 로그인 페이지로 이동 4️⃣ 백엔드의 콜백 처리 및 로그인/회원가입 (AuthController &#8211; Callback) [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/asp-net/asp-net-core-identity%eb%a5%bc-%ed%99%9c%ec%9a%a9%ed%95%9c-%ea%b5%ac%ea%b8%80-%eb%a1%9c%ea%b7%b8%ec%9d%b8oauth-%ec%a0%84%ec%b2%b4-%ed%9d%90%eb%a6%84-%eb%b6%84%ec%84%9d/40245/">ASP.NET Core Identity를 활용한 구글 로그인(OAuth) 전체 흐름 분석</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ASP.NET Core Identity를 활용한 구글 로그인(OAuth) 전체 흐름 분석</h2>


				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-cbc30617      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#aspnet-core-identity를-활용한-구글-로그인oauth-전체-흐름-분석" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ASP.NET Core Identity를 활용한 구글 로그인(OAuth) 전체 흐름 분석</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-사용자가-google로-로그인-버튼을-클릭" class="uagb-toc-link__trigger">1&#x20e3; 사용자가 &quot;Google로 로그인&quot; 버튼을 클릭</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-백엔드의-인증-시작-authcontroller-challenge" class="uagb-toc-link__trigger">2&#x20e3; 백엔드의 인증 시작 (AuthController &#8211; Challenge)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-외부-공급자-인증-google" class="uagb-toc-link__trigger">3&#x20e3; 외부 공급자 인증 (Google)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-백엔드의-콜백-처리-및-로그인회원가입-authcontroller-callback" class="uagb-toc-link__trigger">4&#x20e3; 백엔드의 콜백 처리 및 로그인/회원가입 (AuthController &#8211; Callback)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#5-완료" class="uagb-toc-link__trigger">5&#x20e3; 완료</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#6-authcontrollercs" class="uagb-toc-link__trigger">6&#x20e3; AuthController.cs</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1159" height="1920" src="https://lycos7560.com/wp-content/uploads/2025/08/Google-Auth-Mermaid.jpg" alt="" class="wp-image-40248" srcset="https://lycos7560.com/wp-content/uploads/2025/08/Google-Auth-Mermaid.jpg 1159w, https://lycos7560.com/wp-content/uploads/2025/08/Google-Auth-Mermaid-181x300.jpg 181w, https://lycos7560.com/wp-content/uploads/2025/08/Google-Auth-Mermaid-768x1272.jpg 768w, https://lycos7560.com/wp-content/uploads/2025/08/Google-Auth-Mermaid-927x1536.jpg 927w" sizes="(max-width: 1159px) 100vw, 1159px" /></figure>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1&#x20e3; 사용자가 &#8220;Google로 로그인&#8221; 버튼을 클릭</h3>



<p><code>Navigation.NavigateTo(..., forceLoad: true)</code></p>



<p>Blazor의 내부 라우팅이 아닌, 브라우저가 직접 해당 URL(<code>api/auth/Challenge/Google...</code>)로 <strong>GET 요청</strong>보낸다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
private void LoginWithGoogle()
{
    var returnUrl = "/"; // 로그인 성공 후 돌아올 주소
    var googleLoginUrl = $"/api/auth/Challenge/Google?returnUrl={Uri.EscapeDataString(returnUrl)}";

    // 이 주소로 브라우저가 페이지를 새로고침하며 이동
    Navigation.NavigateTo(googleLoginUrl, forceLoad: true);
}</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2&#x20e3; 백엔드의 인증 시작 (AuthController &#8211; Challenge)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// AuthController.cs
[HttpGet("Challenge/{provider}")]
public IActionResult Challenge(string provider, string? returnUrl = null)
{
    // ...
    var redirectUrl = Url.Action("Callback", "Auth", ...); // 1. Google이 인증 후 돌아올 우리 API 주소(Callback)를 지정
    var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); // 2. 인증 요청에 필요한 속성 구성

    return Challenge(properties, provider); // 3. Google 로그인 페이지로 리디렉션하는 응답 생성
}</pre>



<ul class="wp-block-list">
<li><code>AuthController.cs</code>의 <code>Challenge</code> 메서드가 실행됩니다. <br><code>provider</code> 매개변수에는 &#8220;Google&#8221;이 전달</li>



<li><code>_signInManager</code>는 ASP.NET Core Identity의 핵심 기능으로, 외부 로그인 과정을 도와줌</li>



<li><code>return Challenge(properties, provider);</code>는 브라우저에게 <strong>HTTP 302 Redirect</strong> 응답을 보냄<br>이 응답 헤더에는 사용자가 이동해야 할 <strong>Google의 로그인 페이지 주소</strong>가 포함</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3&#x20e3; 외부 공급자 인증 (Google)</h3>



<p><strong>사용자는 Google 로그인 페이지로 이동</strong></p>



<ul class="wp-block-list">
<li>사용자는 자신의 Google 계정으로 로그인하고, 우리 애플리케이션이 요청하는 정보(이메일, 프로필 등)에 대한 접근 권한을 허용</li>



<li>인증이 성공적으로 완료되면, Google은 2단계에서 백엔드가 지정했던 <strong>콜백 URL</strong>(<code>api/Auth/Callback</code>)로 사용자를 다시 리디렉션<br>이때 인증 코드 또는 토큰과 함께 사용자를 보냄</li>
</ul>



<ol start="1" class="wp-block-list"></ol>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">4&#x20e3; 백엔드의 콜백 처리 및 로그인/회원가입 (AuthController &#8211; Callback)</h3>



<p><strong>Google이 사용자를 우리 백엔드의 <code>Callback</code> 엔드포인트로 돌려보냄</strong></p>



<p><code>AuthController.cs</code>의 <code>Callback</code> (라우팅 경로: <code>externalLogin</code>) 메서드가 실행</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// AuthController.cs
[HttpGet("externalLogin")]
public async Task&lt;IActionResult> Callback(...)
{
    // 1. Google이 보내준 정보로 외부 로그인 정보를 가져옴
    var info = await _signInManager.GetExternalLoginInfoAsync();
    if (info == null) { /* 에러 처리 */ }

    // 2. 이 외부 정보(공급자, 키)로 우리 DB에 연결된 사용자가 있는지 확인하고 로그인 시도
    var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, ...);

    if (result.Succeeded)
    {
        // 3a. 성공! 이미 가입하고 연동된 사용자. 로그인 처리 후 returnUrl로 리디렉션
        return LocalRedirect(returnUrl);
    }
    else
    {
        // 3b. 실패. 신규 사용자이거나, 이메일은 같지만 연동되지 않은 사용자.
        return await HandleUserCreationOrLinking(info, returnUrl);
    }
}</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>HandleUserCreationOrLinking</code> 메서드는 다음 두 가지 시나리오를 처리</p>



<ul class="wp-block-list">
<li><strong>시나리오 A: 이메일은 존재하지만 소셜 연동이 안 된 경우 (<code>LinkExistingUser</code>)</strong>
<ul class="wp-block-list">
<li>Google에서 받은 이메일로 우리 DB에서 사용자를 찾습니다. (<code>_userManager.FindByEmailAsync</code>)</li>



<li>기존 사용자가 있다면, 해당 계정에 이 Google 로그인 정보를 추가로 연결합니다. (<code>_userManager.AddLoginAsync</code>)</li>



<li>연결 후, 사용자를 로그인시키고 <code>returnUrl</code>로 리디렉션합니다.</li>
</ul>
</li>



<li><strong>시나리오 B: 완전히 새로운 사용자인 경우 (<code>CreateNewUserAsync</code>)</strong>
<ul class="wp-block-list">
<li>Google에서 받은 이메일과 이름으로 새로운 <code>ApplicationUser</code> 객체를 만듭니다. 이때 <code>EmailConfirmed</code>는 <code>true</code>로 설정합니다. (소셜 로그인은 이미 이메일이 인증된 것으로 간주)</li>



<li><code>_userManager.CreateAsync(user)</code>로 DB에 새 사용자를 저장합니다.</li>



<li>새로 생성된 사용자 계정에 Google 로그인 정보를 연결합니다. (<code>_userManager.AddLoginAsync</code>)</li>



<li>새 사용자를 로그인시키고 <code>returnUrl</code>로 리디렉션합니다.</li>
</ul>
</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">5&#x20e3; 완료</h3>



<p>로그인 또는 회원가입 및 연동이 모두 성공적으로 끝나면, 백엔드는 최종적으로 <code>LocalRedirect(returnUrl)</code>을 통해 사용자를 맨 처음 <code>Login.razor</code>에서 지정했던 주소(<code>/</code>)로 리디렉션합니다. </p>



<p>이제 사용자는 로그인된 상태로 메인 페이지를 보게 됩니다.</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">6&#x20e3; AuthController.cs</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.IdentityModel.Tokens;
using Neco.BaseTemplate.Shared.Data;
using Neco.BaseTemplete.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;

namespace Neco.BaseTemplete.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : ControllerBase
    {
        private readonly UserManager&lt;ApplicationUser> _userManager;
        private readonly SignInManager&lt;ApplicationUser> _signInManager;
        private readonly IUserStore&lt;ApplicationUser> _userStore;
        private readonly IEmailSender&lt;ApplicationUser> _emailSender;
        private readonly ILogger&lt;AuthController> _logger;

        public AuthController(
            UserManager&lt;ApplicationUser> userManager,
            SignInManager&lt;ApplicationUser> signInManager,
            IUserStore&lt;ApplicationUser> userStore,
            IEmailSender&lt;ApplicationUser> emailSender,
            ILogger&lt;AuthController> logger)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _userStore = userStore;
            _emailSender = emailSender;
            _logger = logger;
        }

        #region Basic Authentication

        /// &lt;summary>
        /// Handles user login.
        /// &lt;/summary>
        /// &lt;param name="model">Login request DTO (email, password, RememberMe option)&lt;/param>
        /// &lt;returns>The login result&lt;/returns>
        [HttpPost("login")]
        public async Task&lt;IActionResult> Login([FromBody] LoginUserRequestDTO model)
        {
            try
            {
                var result = await _signInManager.PasswordSignInAsync(
                    model.Email,
                    model.Password,
                    model.RememberMe,
                    lockoutOnFailure: true);

                if (result.Succeeded)
                {
                    _logger.LogInformation("User logged in: {Email}", model.Email);
                    return Ok(new { Success = true });
                }

                if (result.RequiresTwoFactor)
                {
                    return Ok(new { RequiresTwoFactor = true });
                }

                if (result.IsLockedOut)
                {
                    _logger.LogWarning("User account locked out: {Email}", model.Email);
                    return BadRequest("Account locked out");
                }

                return BadRequest("Invalid login attempt");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Login error for {Email}", model.Email);
                return StatusCode(500, "An error occurred");
            }
        }

        /// &lt;summary>
        /// Handles new user registration.
        /// &lt;/summary>
        /// &lt;param name="model">Registration request DTO&lt;/param>
        /// &lt;returns>The registration result&lt;/returns>
        [HttpPost("register")]
        public async Task&lt;IActionResult> Register([FromBody] CreateUserRequestDTO model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(new RegisterUserResponseDTO
                {
                    Status = RegisterUserResponseDTO.ResponseStatus.Fail_RequestInvalid,
                    Errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage)
                });
            }

            var user = CreateUser();
            await _userStore.SetUserNameAsync(user, model.Email, CancellationToken.None);
            var emailStore = GetEmailStore();
            await emailStore.SetEmailAsync(user, model.Email, CancellationToken.None);

            var result = await _userManager.CreateAsync(user, model.Password);

            if (!result.Succeeded)
            {
                _logger.LogInformation("User registration failed: {Email}", model.Email);

                // Handle duplicate email error
                if (result.Errors.Any(e => e.Code == "DuplicateUserName"))
                {
                    return BadRequest(new RegisterUserResponseDTO
                    {
                        Status = RegisterUserResponseDTO.ResponseStatus.Fail_DuplicateEmail,
                    });
                }

                // Handle general Identity errors
                return BadRequest(new RegisterUserResponseDTO
                {
                    Status = RegisterUserResponseDTO.ResponseStatus.Fail_General,
                    Errors = result.Errors.Select(e => e.Description)
                });
            }

            _logger.LogInformation("User created a new account with a password.");

            // Send email confirmation link
            try
            {
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));

                // NOTE: For security, it's recommended to configure a fixed base URL
                // from your application's settings rather than relying on the request host.
                var baseUrl = model.BaseUrl ?? $"{Request.Scheme}://{Request.Host.Value}";
                var callbackUrl = $"{baseUrl}/Account/ConfirmEmail?userId={userId}&amp;code={code}";

                await _emailSender.SendConfirmationLinkAsync(user, model.Email, HtmlEncoder.Default.Encode(callbackUrl));
            }
            catch (Exception)
            {
                // Account created successfully but email sending failed.
                return Ok(new RegisterUserResponseDTO
                {
                    Status = RegisterUserResponseDTO.ResponseStatus.Success_EmailWarning,
                    Message = "The account was created, but the verification email sending failed."
                });
            }

            return Ok(new RegisterUserResponseDTO
            {
                Status = RegisterUserResponseDTO.ResponseStatus.Success_NeedConfirmation
            });
        }

        /// &lt;summary>
        /// Resends the email confirmation link.
        /// &lt;/summary>
        /// &lt;param name="model">Email confirmation request DTO&lt;/param>
        /// &lt;returns>The result of the process&lt;/returns>
        [HttpPost("emailConfirm")]
        public async Task&lt;IActionResult> ResendEmailConfirm([FromBody] RequestEmailConfirmDTO model)
        {
            if (!ModelState.IsValid)
            {
                // Always return a success response for security (to avoid user enumeration).
                return Ok(new ResponseEmailConfirmDTO
                {
                    Status = ResponseEmailConfirmDTO.ResponseStatus.Success,
                    Message = "The confirmation email has been sent successfully."
                });
            }

            try
            {
                var user = await _userManager.FindByEmailAsync(model.Email);

                // Even if the user doesn't exist or is already confirmed, return a success response for security.
                if (user == null || user.EmailConfirmed)
                {
                    return Ok(new ResponseEmailConfirmDTO
                    {
                        Status = ResponseEmailConfirmDTO.ResponseStatus.Success,
                        Message = "The confirmation email has been sent successfully."
                    });
                }

                // Generate and send the email confirmation link
                var userId = await _userManager.GetUserIdAsync(user);
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));

                // NOTE: It is recommended to use a pre-configured base URL.
                var baseUrl = model.BaseUrl ?? $"{Request.Scheme}://{Request.Host.Value}";
                var callbackUrl = $"{baseUrl}/Account/ConfirmEmail?userId={userId}&amp;code={code}";

                await _emailSender.SendConfirmationLinkAsync(user, model.Email, HtmlEncoder.Default.Encode(callbackUrl));

                return Ok(new ResponseEmailConfirmDTO
                {
                    Status = ResponseEmailConfirmDTO.ResponseStatus.Success,
                    Message = "The confirmation email has been sent successfully."
                });
            }
            catch (Exception)
            {
                // Mail server or other exception occurred.
                return BadRequest(new ResponseEmailConfirmDTO
                {
                    Status = ResponseEmailConfirmDTO.ResponseStatus.Fail,
                    Message = $"An error occurred while processing your request. (Server Error)"
                });
            }
        }

        /// &lt;summary>
        /// Handles password reset requests.
        /// &lt;/summary>
        /// &lt;param name="model">Password reset request DTO&lt;/param>
        /// &lt;returns>The result of the process&lt;/returns>
        [HttpPost("resetPassword")]
        public async Task&lt;IActionResult> ResetPassword([FromBody] ResetPasswordRequestDTO model)
        {
            var user = await _userManager.FindByEmailAsync(model.Email);
            if (user == null)
            {
                return BadRequest("User not found");
            }

            var resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
            var callbackUrl = $"{model.BaseUrl}/Account/ResetPassword?email={model.Email}&amp;token={resetToken}";

            await _emailSender.SendPasswordResetLinkAsync(user, model.Email, callbackUrl);

            return Ok("Password reset link sent");
        }

        /// &lt;summary>
        /// Handles user logout.
        /// &lt;/summary>
        /// &lt;returns>The logout result&lt;/returns>
        [HttpPost("logout")]
        public async Task&lt;IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            return Ok("Logged out successfully");
        }

        /// &lt;summary>
        /// Handles the generation of a JWT token.
        /// &lt;/summary>
        /// &lt;param name="model">Login request DTO (email, password)&lt;/param>
        /// &lt;returns>Token information&lt;/returns>
        [HttpPost("token")]
        public async Task&lt;IActionResult> GenerateToken([FromBody] LoginUserRequestDTO model)
        {
            var user = await _userManager.FindByEmailAsync(model.Email);
            if (user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
            {
                return Unauthorized("Invalid credentials");
            }

            var claims = new[]
            {
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(ClaimTypes.Email, user.Email)
            };

            // WARNING: Do not hard-code the key in production.
            // Move this to a secure configuration file (e.g., appsettings.json)
            // and use a strong, randomly generated key.
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("A_Very_Strong_And_Secret_Key_For_JWT_Auth"));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: "yourdomain.com",
                audience: "yourdomain.com",
                claims: claims,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: creds);

            return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
        }

        #endregion

        #region External Authentication (OAuth)

        /// &lt;summary>
        /// Initiates a challenge request to an external authentication provider (e.g., Google).
        /// &lt;/summary>
        /// &lt;param name="provider">The name of the authentication provider (e.g., "Google")&lt;/param>
        /// &lt;param name="returnUrl">The URL to redirect to after successful authentication&lt;/param>
        /// &lt;returns>A challenge result to the external provider&lt;/returns>
        [HttpGet("Challenge/{provider}")]
        public IActionResult Challenge(string provider, string? returnUrl = null)
        {
            _logger.LogInformation("Starting {Provider} login", provider);

            if (string.IsNullOrWhiteSpace(provider))
            {
                return BadRequest(new { error = "Provider not specified." });
            }

            // Normalize and validate returnUrl
            returnUrl = NormalizeReturnUrl(returnUrl);

            // Set the callback URL
            var redirectUrl = Url.Action("Callback", "Auth", new { returnUrl }, Request.Scheme);

            // Configure authentication properties
            var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);

            return Challenge(properties, provider);
        }

        /// &lt;summary>
        /// Handles the callback from an external authentication provider.
        /// &lt;/summary>
        /// &lt;param name="returnUrl">The URL to redirect to after successful authentication&lt;/param>
        /// &lt;param name="remoteError">Error message from the external provider&lt;/param>
        /// &lt;returns>An appropriate response based on the authentication result&lt;/returns>
        [HttpGet("externalLogin")]
        public async Task&lt;IActionResult> Callback(string? returnUrl = null, string? remoteError = null)
        {
            try
            {
                returnUrl = NormalizeReturnUrl(returnUrl);

                if (!string.IsNullOrEmpty(remoteError))
                {
                    _logger.LogError("External provider error: {RemoteError}", remoteError);
                    return Redirect($"/login?error={Uri.EscapeDataString(remoteError)}");
                }

                var info = await _signInManager.GetExternalLoginInfoAsync();
                if (info == null)
                {
                    _logger.LogError("Could not retrieve external login info");
                    return Redirect("/login?error=external_login_failed");
                }

                // Attempt to sign in an existing user
                var result = await _signInManager.ExternalLoginSignInAsync(
                    info.LoginProvider,
                    info.ProviderKey,
                    isPersistent: false,
                    bypassTwoFactor: true);

                if (result.Succeeded)
                {
                    var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
                    _logger.LogInformation("User {Email} successfully logged in", user?.Email);
                    return LocalRedirect(returnUrl);
                }

                if (result.IsLockedOut)
                {
                    _logger.LogWarning("Attempt to log in with a locked-out account");
                    return Redirect("/login?error=locked_out");
                }

                // New user or linking an existing user
                return await HandleUserCreationOrLinking(info, returnUrl);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while processing external login");
                return Redirect("/login?error=server_error");
            }
        }

        #endregion

        #region Helper Methods

        /// &lt;summary>
        /// Normalizes and validates the returnUrl.
        /// &lt;/summary>
        private string NormalizeReturnUrl(string? returnUrl)
        {
            if (string.IsNullOrWhiteSpace(returnUrl))
                return "/";

            if (Uri.TryCreate(returnUrl, UriKind.Absolute, out var uri))
                returnUrl = uri.PathAndQuery;

            return Url.IsLocalUrl(returnUrl) ? returnUrl : "/";
        }

        /// &lt;summary>
        /// Handles new user creation or linking an existing user to an external login.
        /// &lt;/summary>
        private async Task&lt;IActionResult> HandleUserCreationOrLinking(ExternalLoginInfo info, string returnUrl)
        {
            var email = info.Principal.FindFirstValue(ClaimTypes.Email);
            if (string.IsNullOrEmpty(email))
            {
                _logger.LogError("Missing email information from external provider {Provider}", info.LoginProvider);
                return Redirect("/login?error=email_required");
            }

            var existingUser = await _userManager.FindByEmailAsync(email);
            return existingUser != null
                ? await LinkExistingUser(existingUser, info, returnUrl)
                : await CreateNewUserAsync(info, returnUrl);
        }

        /// &lt;summary>
        /// Links external login information to an existing user.
        /// &lt;/summary>
        private async Task&lt;IActionResult> LinkExistingUser(ApplicationUser user, ExternalLoginInfo info, string returnUrl)
        {
            var addLoginResult = await _userManager.AddLoginAsync(user, info);
            if (addLoginResult.Succeeded)
            {
                _logger.LogInformation("Successfully linked {Provider} login to existing user {Email}", info.LoginProvider, user.Email);
                await _signInManager.SignInAsync(user, isPersistent: false, info.LoginProvider);
                return LocalRedirect(returnUrl);
            }

            _logger.LogError("Failed to link external login to existing user: {Errors}",
                string.Join(", ", addLoginResult.Errors.Select(e => e.Description)));
            return Redirect("/login?error=link_failed");
        }

        /// &lt;summary>
        /// Creates a new user based on external login information.
        /// &lt;/summary>
        private async Task&lt;IActionResult> CreateNewUserAsync(ExternalLoginInfo info, string returnUrl)
        {
            var email = info.Principal.FindFirstValue(ClaimTypes.Email);
            if (string.IsNullOrEmpty(email))
            {
                _logger.LogError("Missing email information from external provider");
                return BadRequest(new { error = "Email information was not provided." });
            }

            var userName = email.Split('@')[0];
            var user = new ApplicationUser
            {
                UserName = email,
                Email = email,
                NickName = userName,
                EmailConfirmed = true // Email is already confirmed via external provider
            };

            var createResult = await _userManager.CreateAsync(user);
            if (!createResult.Succeeded)
            {
                _logger.LogError("User creation failed: {Errors}",
                    string.Join(", ", createResult.Errors.Select(e => e.Description)));

                // Attempt to create an alternative user name on a naming conflict
                if (createResult.Errors.Any(e => e.Code == "DuplicateUserName"))
                {
                    user.UserName = $"{userName}_{Guid.NewGuid().ToString("N").Substring(0, 4)}";
                    createResult = await _userManager.CreateAsync(user);

                    if (!createResult.Succeeded)
                    {
                        return BadRequest(new
                        {
                            error = "Account creation failed",
                            details = createResult.Errors.Select(e => e.Description)
                        });
                    }
                }
                else
                {
                    return BadRequest(new
                    {
                        error = "Account creation failed",
                        details = createResult.Errors.Select(e => e.Description)
                    });
                }
            }

            // Link external login information
            var addLoginResult = await _userManager.AddLoginAsync(user, info);
            if (!addLoginResult.Succeeded)
            {
                _logger.LogError("Failed to add login information: {Errors}",
                    string.Join(", ", addLoginResult.Errors.Select(e => e.Description)));

                // Rollback: Delete the created user
                await _userManager.DeleteAsync(user);

                return BadRequest(new
                {
                    error = "Failed to link external login information",
                    details = addLoginResult.Errors.Select(e => e.Description)
                });
            }

            _logger.LogInformation("New user created and logged in successfully: {Email}", email);
            await _signInManager.SignInAsync(user, isPersistent: false, info.LoginProvider);

            return LocalRedirect(returnUrl);
        }

        private ApplicationUser CreateUser()
        {
            try
            {
                return Activator.CreateInstance&lt;ApplicationUser>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
                    $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor.");
            }
        }

        private IUserEmailStore&lt;ApplicationUser> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore&lt;ApplicationUser>)_userStore;
        }

        #endregion

        #region Profile Management

        /// &lt;summary>
        /// Handles user profile updates.
        /// &lt;/summary>
        /// &lt;param name="model">Update request DTO&lt;/param>
        /// &lt;returns>The result of the process&lt;/returns>
        [HttpPut("updateProfile")]
        public async Task&lt;IActionResult> UpdateProfile([FromBody] UpdateUserProfileDTO model)
        {
            var user = await _userManager.FindByIdAsync(model.Email);
            if (user == null)
            {
                return NotFound("User not found");
            }

            user.NickName = model.Nickname;
            user.PhoneNumber = model.PhoneNumber;

            var result = await _userManager.UpdateAsync(user);
            if (!result.Succeeded)
            {
                return BadRequest(result.Errors.Select(e => e.Description));
            }

            return Ok("Profile updated successfully");
        }

        /// &lt;summary>
        /// Handles user deletion.
        /// &lt;/summary>
        /// &lt;param name="userId">The ID of the user to delete&lt;/param>
        /// &lt;returns>The result of the process&lt;/returns>
        [HttpDelete("deleteUser/{userId}")]
        public async Task&lt;IActionResult> DeleteUser(string userId)
        {
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                return NotFound("User not found");
            }

            var result = await _userManager.DeleteAsync(user);
            if (!result.Succeeded)
            {
                return BadRequest(result.Errors.Select(e => e.Description));
            }

            return Ok("User deleted successfully");
        }

        #endregion
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private void LoginWithGoogle()
{
    try
    {
        var returnUrl = "/";
        var googleLoginUrl = $"/api/auth/Challenge/Google?returnUrl={Uri.EscapeDataString(returnUrl)}";

        // Redirect in the current window instead of a popup
        // This is a placeholder for a client-side navigation method, e.g., in Blazor
        // Navigation.NavigateTo(googleLoginUrl, forceLoad: true);
    }
    catch (Exception ex)
    {
        // Error handling
        Console.WriteLine($"Google login error: {ex.Message}");
    }
}
</pre>



<p></p>
<p>The post <a href="https://lycos7560.com/c/asp-net/asp-net-core-identity%eb%a5%bc-%ed%99%9c%ec%9a%a9%ed%95%9c-%ea%b5%ac%ea%b8%80-%eb%a1%9c%ea%b7%b8%ec%9d%b8oauth-%ec%a0%84%ec%b2%b4-%ed%9d%90%eb%a6%84-%eb%b6%84%ec%84%9d/40245/">ASP.NET Core Identity를 활용한 구글 로그인(OAuth) 전체 흐름 분석</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/asp-net/asp-net-core-identity%eb%a5%bc-%ed%99%9c%ec%9a%a9%ed%95%9c-%ea%b5%ac%ea%b8%80-%eb%a1%9c%ea%b7%b8%ec%9d%b8oauth-%ec%a0%84%ec%b2%b4-%ed%9d%90%eb%a6%84-%eb%b6%84%ec%84%9d/40245/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ubuntu export 명령어 (.NET)</title>
		<link>https://lycos7560.com/ubuntu/ubuntu-export-%eb%aa%85%eb%a0%b9%ec%96%b4-net/40241/</link>
					<comments>https://lycos7560.com/ubuntu/ubuntu-export-%eb%aa%85%eb%a0%b9%ec%96%b4-net/40241/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Fri, 08 Aug 2025 06:21:42 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[app-config]]></category>
		<category><![CDATA[appsettings]]></category>
		<category><![CDATA[appsettings.development.json]]></category>
		<category><![CDATA[appsettings.json]]></category>
		<category><![CDATA[appsettings.production.json]]></category>
		<category><![CDATA[appsettings.staging.json]]></category>
		<category><![CDATA[asp-net-core]]></category>
		<category><![CDATA[aspnetcore]]></category>
		<category><![CDATA[aspnetcore-environment]]></category>
		<category><![CDATA[aspnetcore-urls]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[bash_profile]]></category>
		<category><![CDATA[bashrc]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[child-process]]></category>
		<category><![CDATA[CLI]]></category>
		<category><![CDATA[command-line]]></category>
		<category><![CDATA[config]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[connection-string-env]]></category>
		<category><![CDATA[connection-strings]]></category>
		<category><![CDATA[CSharp]]></category>
		<category><![CDATA[database-config]]></category>
		<category><![CDATA[database-connection]]></category>
		<category><![CDATA[db-connection]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[dotnet]]></category>
		<category><![CDATA[dotnet-config]]></category>
		<category><![CDATA[dotnet-core]]></category>
		<category><![CDATA[dotnet-environment]]></category>
		<category><![CDATA[dotnet-environment-variable]]></category>
		<category><![CDATA[dotnet-publish]]></category>
		<category><![CDATA[dotnet-run]]></category>
		<category><![CDATA[dotnet-settings]]></category>
		<category><![CDATA[env]]></category>
		<category><![CDATA[Environment]]></category>
		<category><![CDATA[environment-config]]></category>
		<category><![CDATA[environment-example]]></category>
		<category><![CDATA[environment-variable]]></category>
		<category><![CDATA[environment-variable-setup]]></category>
		<category><![CDATA[export]]></category>
		<category><![CDATA[export-command]]></category>
		<category><![CDATA[export-example]]></category>
		<category><![CDATA[export-variable]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[linux-dotnet]]></category>
		<category><![CDATA[linux-export]]></category>
		<category><![CDATA[localhost]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[path]]></category>
		<category><![CDATA[port-config]]></category>
		<category><![CDATA[printenv]]></category>
		<category><![CDATA[Process]]></category>
		<category><![CDATA[prod]]></category>
		<category><![CDATA[Production]]></category>
		<category><![CDATA[production-server]]></category>
		<category><![CDATA[publish]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[secrets]]></category>
		<category><![CDATA[secure-environment-variable]]></category>
		<category><![CDATA[server-config]]></category>
		<category><![CDATA[server-environment]]></category>
		<category><![CDATA[Service]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[shell-variable]]></category>
		<category><![CDATA[stage]]></category>
		<category><![CDATA[Staging]]></category>
		<category><![CDATA[staging-server]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[system-environment]]></category>
		<category><![CDATA[systemd]]></category>
		<category><![CDATA[systemd-service]]></category>
		<category><![CDATA[Terminal]]></category>
		<category><![CDATA[test-environment]]></category>
		<category><![CDATA[ubuntu-dotnet]]></category>
		<category><![CDATA[ubuntu-dotnet-deploy]]></category>
		<category><![CDATA[url-binding]]></category>
		<category><![CDATA[개발]]></category>
		<category><![CDATA[개발서버]]></category>
		<category><![CDATA[개발환경]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[닷넷]]></category>
		<category><![CDATA[닷넷코어]]></category>
		<category><![CDATA[리눅스]]></category>
		<category><![CDATA[민감정보]]></category>
		<category><![CDATA[배포]]></category>
		<category><![CDATA[배포서버]]></category>
		<category><![CDATA[배포환경]]></category>
		<category><![CDATA[서버환경]]></category>
		<category><![CDATA[설정파일]]></category>
		<category><![CDATA[쉘]]></category>
		<category><![CDATA[스테이징]]></category>
		<category><![CDATA[스테이징환경]]></category>
		<category><![CDATA[우분투]]></category>
		<category><![CDATA[운영환경]]></category>
		<category><![CDATA[터미널]]></category>
		<category><![CDATA[테스트서버]]></category>
		<category><![CDATA[테스트환경]]></category>
		<category><![CDATA[포트설정]]></category>
		<category><![CDATA[프로덕션]]></category>
		<category><![CDATA[프로덕션환경]]></category>
		<category><![CDATA[프로세스]]></category>
		<category><![CDATA[환경변수]]></category>
		<category><![CDATA[환경변수보안]]></category>
		<category><![CDATA[환경설정]]></category>
		<category><![CDATA[환경설정파일]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40241</guid>

					<description><![CDATA[<p>📄 Ubuntu export 명령어 1️⃣ 개요 export 명령어는 현재 셸 환경에 환경 변수를 등록하거나 수정하는 데 사용 등록된 변수는 현재 셸과 그 자식 프로세스에서 사용할 수 있음 2️⃣ 기본 문법 3️⃣ 특징 4️⃣ 확인 방법 💡 .NET에서의 사용 예시 1️⃣ ASP.NET Core에서 환경 지정 프로젝트 유형 권장 환경 변수 이유 ASP.NET Core 웹앱 ASPNETCORE_ENVIRONMENT 웹 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/ubuntu/ubuntu-export-%eb%aa%85%eb%a0%b9%ec%96%b4-net/40241/">Ubuntu export 명령어 (.NET)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-b32ec816      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#ubuntu-export-명령어" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c4.png" alt="📄" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Ubuntu export 명령어</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-개요" class="uagb-toc-link__trigger">1&#x20e3; 개요</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-기본-문법" class="uagb-toc-link__trigger">2&#x20e3; 기본 문법</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-특징" class="uagb-toc-link__trigger">3&#x20e3; 특징</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-확인-방법" class="uagb-toc-link__trigger">4&#x20e3; 확인 방법</a></li></ul></li><li class="uagb-toc__list"><a href="#net에서의-사용-예시" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET에서의 사용 예시</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-aspnet-core에서-환경-지정" class="uagb-toc-link__trigger">1&#x20e3; ASP.NET Core에서 환경 지정</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-net에서-특정-포트로-실행" class="uagb-toc-link__trigger">2&#x20e3; .NET에서 특정 포트로 실행</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-영구-설정" class="uagb-toc-link__trigger">3&#x20e3; 영구 설정</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c4.png" alt="📄" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Ubuntu export 명령어</h2>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1&#x20e3; 개요</h3>



<p><code>export</code> 명령어는 <strong>현재 셸 환경에 환경 변수를 등록하거나 수정</strong>하는 데 사용</p>



<p>등록된 변수는 <strong>현재 셸과 그 자식 프로세스</strong>에서 사용할 수 있음</p>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2&#x20e3; 기본 문법</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">export 변수명=값

또는

export 변수명</pre>



<ul class="wp-block-list">
<li><code>변수명=값</code> : 새 환경 변수를 만들거나 기존 변수 값을 변경</li>



<li><code>변수명</code> : 이미 선언된 변수를 환경 변수로 승격</li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3&#x20e3; 특징</h3>



<ul class="wp-block-list">
<li>환경 변수는 <strong>프로세스 간 전달</strong> 가능 (부모 → 자식).</li>



<li><code>export</code>로 설정한 변수는 <strong>현재 세션</strong>에서만 유효.</li>



<li>영구적으로 유지하려면 <code>~/.bashrc</code> 또는 <code>~/.bash_profile</code>에 추가해야 함.</li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">4&#x20e3; 확인 방법</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">echo $변수명       # 변수 값 출력
printenv 변수명    # 환경 변수 확인
env                # 전체 환경 변수 목록
</pre>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"/>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET에서의 사용 예시</h2>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1&#x20e3; ASP.NET Core에서 환경 지정</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">export ASPNETCORE_ENVIRONMENT=Development
export ASPNETCORE_ENVIRONMENT=Staging
export ASPNETCORE_ENVIRONMENT=Production

export DOTNET_ENVIRONMENT=Development
export DOTNET_ENVIRONMENT=Staging // 실제 운영 환경(Production)과 거의 동일하지만, 사용자에게는 서비스되지 않는 사전 검증용 환경
export DOTNET_ENVIRONMENT=Production

dotnet run</pre>



<ul class="wp-block-list">
<li>예를 들어 <code>Development</code>로 설정하면, 개발 모드 환경 설정(<code>appsettings.Development.json</code>)을 자동으로 사용</li>
</ul>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>프로젝트 유형</th><th>권장 환경 변수</th><th>이유</th></tr></thead><tbody><tr><td>ASP.NET Core 웹앱</td><td>ASPNETCORE_ENVIRONMENT</td><td>웹 호스팅에 최적화된 공식 표준이며 <code>DOTNET_ENVIRONMENT</code>보다 <strong>우선순위가 높</strong></td></tr><tr><td>Worker Service</td><td>DOTNET_ENVIRONMENT</td><td>웹과 관련 없는 일반적인 .NET 호스팅 환경을 위한 표준 변수</td></tr><tr><td>콘솔 앱</td><td>DOTNET_ENVIRONMENT</td><td>범용적 사용</td></tr><tr><td>Blazor</td><td>ASPNETCORE_ENVIRONMENT</td><td>ASP.NET Core 호스팅 모델을 기반으로 하므로 웹 앱과 동일한 기준</td></tr></tbody></table></figure>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2&#x20e3; .NET에서 특정 포트로 실행</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># 5005번 포트로 애플리케이션 실행
export ASPNETCORE_URLS="http://localhost:5005"

dotnet run</pre>



<ul class="wp-block-list">
<li><code>ASPNETCORE_URLS</code> 환경 변수를 이용해 실행 시 포트를 변경할 수 있음</li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3&#x20e3; 영구 설정</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># 1. .bashrc 파일에 환경 변수 설정 명령어를 추가
echo 'export DOTNET_ENVIRONMENT=Production' >> ~/.bashrc

# 2. 수정된 .bashrc 설정을 현재 터미널 세션에 바로 적용
source ~/.bashrc

# 3. 설정 확인
echo $DOTNET_ENVIRONMENT</pre>



<p></p>
<p>The post <a href="https://lycos7560.com/ubuntu/ubuntu-export-%eb%aa%85%eb%a0%b9%ec%96%b4-net/40241/">Ubuntu export 명령어 (.NET)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/ubuntu/ubuntu-export-%eb%aa%85%eb%a0%b9%ec%96%b4-net/40241/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ASP.NET Core의 미들웨어(Middleware)</title>
		<link>https://lycos7560.com/c/asp-net-core%ec%9d%98-%eb%af%b8%eb%93%a4%ec%9b%a8%ec%96%b4middleware/40130/</link>
					<comments>https://lycos7560.com/c/asp-net-core%ec%9d%98-%eb%af%b8%eb%93%a4%ec%9b%a8%ec%96%b4middleware/40130/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Thu, 24 Jul 2025 23:32:09 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[action]]></category>
		<category><![CDATA[API Gateway]]></category>
		<category><![CDATA[API 게이트웨이]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[ASP.NET Core MVC]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[Asynchronous]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[Await]]></category>
		<category><![CDATA[Caching]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Compression]]></category>
		<category><![CDATA[Conditional Middleware]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[Configure]]></category>
		<category><![CDATA[Container]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[Conventional Middleware]]></category>
		<category><![CDATA[CORS]]></category>
		<category><![CDATA[Cross-Origin Resource Sharing]]></category>
		<category><![CDATA[Custom Middleware]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[DELETE]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[Developer]]></category>
		<category><![CDATA[Development Environment]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[DI]]></category>
		<category><![CDATA[Error Handling]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[Event-driven]]></category>
		<category><![CDATA[Exception Handling]]></category>
		<category><![CDATA[Extension Method]]></category>
		<category><![CDATA[Factory-based Middleware]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[GET]]></category>
		<category><![CDATA[HEAD]]></category>
		<category><![CDATA[Headers]]></category>
		<category><![CDATA[HTTP Context]]></category>
		<category><![CDATA[HTTP Method]]></category>
		<category><![CDATA[HTTP Request]]></category>
		<category><![CDATA[HTTP Response]]></category>
		<category><![CDATA[HTTP 메서드]]></category>
		<category><![CDATA[HTTP 요청]]></category>
		<category><![CDATA[HTTP 응답]]></category>
		<category><![CDATA[HTTP 컨텍스트]]></category>
		<category><![CDATA[HTTP/1.1]]></category>
		<category><![CDATA[HTTP/2]]></category>
		<category><![CDATA[HttpContext]]></category>
		<category><![CDATA[HTTPS Redirection]]></category>
		<category><![CDATA[HTTPS 리디렉션]]></category>
		<category><![CDATA[IApplicationBuilder]]></category>
		<category><![CDATA[IMiddleware]]></category>
		<category><![CDATA[Integration Testing]]></category>
		<category><![CDATA[Invoke]]></category>
		<category><![CDATA[Kestrel]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Logging]]></category>
		<category><![CDATA[Logging Provider]]></category>
		<category><![CDATA[Maintainability]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[Middleware]]></category>
		<category><![CDATA[Middleware Chain]]></category>
		<category><![CDATA[Minimal APIs]]></category>
		<category><![CDATA[Modularization]]></category>
		<category><![CDATA[next]]></category>
		<category><![CDATA[Non-Terminal Middleware]]></category>
		<category><![CDATA[Ops]]></category>
		<category><![CDATA[options]]></category>
		<category><![CDATA[Order Critical]]></category>
		<category><![CDATA[PATCH]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[pipeline]]></category>
		<category><![CDATA[POST]]></category>
		<category><![CDATA[Production Environment]]></category>
		<category><![CDATA[Program.cs]]></category>
		<category><![CDATA[Protocol]]></category>
		<category><![CDATA[PUT]]></category>
		<category><![CDATA[Request Body]]></category>
		<category><![CDATA[Request Flow]]></category>
		<category><![CDATA[Request Pipeline]]></category>
		<category><![CDATA[RequestDelegate]]></category>
		<category><![CDATA[Response Body]]></category>
		<category><![CDATA[Response Flow]]></category>
		<category><![CDATA[Reusability]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[Routing]]></category>
		<category><![CDATA[Run]]></category>
		<category><![CDATA[Scalability]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Separation of Concerns]]></category>
		<category><![CDATA[Service Container]]></category>
		<category><![CDATA[Short-circuit]]></category>
		<category><![CDATA[Single Responsibility Principle]]></category>
		<category><![CDATA[SoC]]></category>
		<category><![CDATA[SSL]]></category>
		<category><![CDATA[Startup]]></category>
		<category><![CDATA[Static Files]]></category>
		<category><![CDATA[Status Code]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Terminal Middleware]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[TLS]]></category>
		<category><![CDATA[Unit Testing]]></category>
		<category><![CDATA[Use]]></category>
		<category><![CDATA[UseWhen]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[Web Application Framework]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Web Service]]></category>
		<category><![CDATA[WebApplication]]></category>
		<category><![CDATA[WebApplicationBuilder]]></category>
		<category><![CDATA[개발 환경]]></category>
		<category><![CDATA[개발자]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[관심사의 분리]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[단락]]></category>
		<category><![CDATA[단위 테스트]]></category>
		<category><![CDATA[단일 책임 원칙]]></category>
		<category><![CDATA[디버깅]]></category>
		<category><![CDATA[라우팅]]></category>
		<category><![CDATA[로깅]]></category>
		<category><![CDATA[로깅 프로바이더]]></category>
		<category><![CDATA[리버스 프록시]]></category>
		<category><![CDATA[마이크로서비스]]></category>
		<category><![CDATA[모듈화]]></category>
		<category><![CDATA[미들웨어]]></category>
		<category><![CDATA[미들웨어 체인]]></category>
		<category><![CDATA[배포]]></category>
		<category><![CDATA[보안]]></category>
		<category><![CDATA[비동기]]></category>
		<category><![CDATA[비종료 미들웨어]]></category>
		<category><![CDATA[상태 코드]]></category>
		<category><![CDATA[서비스 메쉬]]></category>
		<category><![CDATA[서비스 컨테이너]]></category>
		<category><![CDATA[설정]]></category>
		<category><![CDATA[성능]]></category>
		<category><![CDATA[순서 중요]]></category>
		<category><![CDATA[압축]]></category>
		<category><![CDATA[액션]]></category>
		<category><![CDATA[예외 처리]]></category>
		<category><![CDATA[오류 처리]]></category>
		<category><![CDATA[요청 본문]]></category>
		<category><![CDATA[요청 파이프라인]]></category>
		<category><![CDATA[요청 흐름]]></category>
		<category><![CDATA[웹 개발]]></category>
		<category><![CDATA[웹 서버]]></category>
		<category><![CDATA[웹 서비스]]></category>
		<category><![CDATA[웹 애플리케이션]]></category>
		<category><![CDATA[웹 애플리케이션 프레임워크]]></category>
		<category><![CDATA[유지보수성]]></category>
		<category><![CDATA[응답 본문]]></category>
		<category><![CDATA[응답 흐름]]></category>
		<category><![CDATA[의존성 주입]]></category>
		<category><![CDATA[이벤트]]></category>
		<category><![CDATA[이벤트 드리븐]]></category>
		<category><![CDATA[인가]]></category>
		<category><![CDATA[인증]]></category>
		<category><![CDATA[재사용성]]></category>
		<category><![CDATA[정적 파일]]></category>
		<category><![CDATA[조건부 미들웨어]]></category>
		<category><![CDATA[종료 미들웨어]]></category>
		<category><![CDATA[최소 API]]></category>
		<category><![CDATA[캐싱]]></category>
		<category><![CDATA[커스텀 미들웨어]]></category>
		<category><![CDATA[컨벤셔널 미들웨어]]></category>
		<category><![CDATA[컨테이너]]></category>
		<category><![CDATA[컨트롤러]]></category>
		<category><![CDATA[쿠버네티스]]></category>
		<category><![CDATA[클라우드]]></category>
		<category><![CDATA[테스트]]></category>
		<category><![CDATA[통합 테스트]]></category>
		<category><![CDATA[파이프라인]]></category>
		<category><![CDATA[팩토리 기반 미들웨어]]></category>
		<category><![CDATA[프로덕션 환경]]></category>
		<category><![CDATA[프로토콜]]></category>
		<category><![CDATA[필터]]></category>
		<category><![CDATA[헤더]]></category>
		<category><![CDATA[확장 메서드]]></category>
		<category><![CDATA[확장성]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40130</guid>

					<description><![CDATA[<p>❓ 미들웨어(Middleware) https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0 ASP.NET Core의 미들웨어는 모든 HTTP 요청과 응답 파이프라인을 형성하는 일련의 구성 요소입니다. 각 미들웨어 구성 요소는 다음을 수행할 수 있습니다: 이러한 파이프라인을 통해 애플리케이션의 로직을 모듈화하고, 인증, 로깅, 오류 처리, 라우팅 등과 같은 기능을 깔끔하고 유지 관리하기 쉬운 방식으로 추가할 수 있습니다. ⛓️ 미들웨어 체인 (요청 파이프라인) ASP.NET Core 요청 파이프라인은 차례로 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/asp-net-core%ec%9d%98-%eb%af%b8%eb%93%a4%ec%9b%a8%ec%96%b4middleware/40130/">ASP.NET Core의 미들웨어(Middleware)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-db9d164c      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#미들웨어middleware" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 미들웨어(Middleware)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#미들웨어-체인-요청-파이프라인" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26d3.png" alt="⛓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 미들웨어 체인 (요청 파이프라인)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#요청-파이프라인-단락short-circuiting" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2702.png" alt="✂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 요청 파이프라인 단락(Short-circuiting)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#단락과-nextinvoke-이후-코드-실행" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 단락과 next.Invoke() 이후 코드 실행</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#경고-응답-전송-후-작업-주의" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 경고: 응답 전송 후 작업 주의</a></li></ul><li class="uagb-toc__list"><a href="#appuse-vs-apprun" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1fae0.png" alt="🫠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> app.Use vs. app.Run</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#aspnet-core의-커스텀-미들웨어" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ASP.NET Core의 커스텀 미들웨어</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#커스텀-미들웨어-클래스의-구조" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f527.png" alt="🔧" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 커스텀 미들웨어 클래스의 구조</a></li></ul><li class="uagb-toc__list"><a href="#커스텀-컨벤셔널-미들웨어-custom-conventional-middleware" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 커스텀 컨벤셔널 미들웨어 (Custom Conventional Middleware)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#미들웨어-파이프라인의-이상적인-순서" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f500.png" alt="🔀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 미들웨어 파이프라인의 이상적인 순서</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#usewhen" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> UseWhen()</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#usewhen-작동-방식" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> UseWhen() 작동 방식</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#usewhen-사용-시점" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f98b.png" alt="🦋" class="wp-smiley" style="height: 1em; max-height: 1em;" /> UseWhen() 사용 시점</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 미들웨어(Middleware)</h2>



<p><a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0</a></p>



<p>ASP.NET Core의 <strong>미들웨어</strong>는 모든 HTTP 요청과 응답 <strong>파이프라인을 형성하는 일련의 구성 요소</strong>입니다.</p>



<p>각 미들웨어 구성 요소는 다음을 수행할 수 있습니다:</p>



<ul class="wp-block-list">
<li>들어오는 <strong>요청을 검사</strong>합니다.</li>



<li>요청 또는 응답을 <strong>수정</strong>합니다 (필요한 경우).</li>



<li>파이프라인의 다음 미들웨어를 <strong>호출</strong>하거나, 프로세스를 <strong>단락(short-circuit)</strong>시키고 자체적으로 응답을 생성합니다.</li>
</ul>



<p>이러한 파이프라인을 통해 <strong>애플리케이션의 로직을 모듈화하고, 인증, 로깅, 오류 처리, 라우팅 등과 </strong>같은 기능을 깔끔하고<strong> 유지 관리하기 쉬운 방식으로 추가</strong>할 수 있습니다.</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26d3.png" alt="⛓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 미들웨어 체인 (요청 파이프라인)</h3>



<p>ASP.NET Core 요청 파이프라인은 차례로 호출되는 일련의 요청 대리자로 구성됩니다.</p>



<p>요청 파이프라인을 일련의 연결된 파이프라고 상상해 보세요. </p>



<p>각 미들웨어 조각은 이 파이프라인의 밸브와 같아서, <strong>정보의 흐름을 제어하고 다양한 단계에서 특정 작업을 적용</strong>할 수 있습니다. </p>



<p>미들웨어를 등록하는 <strong>순서는 매우 중요</strong>하며, 등록된 순서대로 실행됩니다.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="607" height="389" src="https://lycos7560.com/wp-content/uploads/2025/07/image-12.png" alt="" class="wp-image-40131" srcset="https://lycos7560.com/wp-content/uploads/2025/07/image-12.png 607w, https://lycos7560.com/wp-content/uploads/2025/07/image-12-300x192.png 300w" sizes="(max-width: 607px) 100vw, 607px" /><figcaption class="wp-element-caption"><a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0</a></figcaption></figure>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p>각 델리게이트는 <strong>다음 델리게이트를 호출하기 전과 후에 작업</strong>을 수행할 수 있습니다. </p>



<p>예외 처리 델리게이트는 파이프라인의 후반 단계에서 발생하는 예외를 Catch할 수 있도록 파이프라인의 <strong>초기에 호출</strong>되어야 합니다.</p>



<p>가장 간단한 ASP.NET Core 앱은 모든 요청을 처리하는 <strong>단일 요청 델리게이트</strong>를 설정합니다. </p>



<p>이 경우에는 실제 요청 파이프라인이 포함되지 않습니다. </p>



<p>대신, 모든 HTTP 요청에 대한 응답으로 단일 익명 함수가 호출됩니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run(); // 이 app.Run은 위의 app.Run이 파이프라인을 종료하므로 실행되지 않습니다.</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong><code>Use</code> 메서드를 사용하면 여러 요청 델리게이트를 체인으로 연결</strong>할 수 있습니다. </p>



<p>이때 <code>next</code> 매개변수는 파이프라인의 다음 델리게이트를 나타냅니다.</p>



<p><code>next</code> 매개변수를 호출하지 않음으로써 <strong>파이프라인을 단락(short-circuit)</strong>시킬 수 있습니다. </p>



<p>다음 예시에서 보여주듯이, 일반적으로 <code>next</code> 델리게이트를 호출하기 전과 후에 모두 작업을 수행할 수 있습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var builder = WebApplication.CreateBuilder(args); // 웹 애플리케이션 빌더를 생성합니다.
var app = builder.Build(); // 빌더를 사용하여 웹 애플리케이션 인스턴스를 생성합니다.

// 첫 번째 미들웨어: app.Use()를 사용하여 파이프라인에 추가합니다.
app.Use(async (context, next) =>
{
    // 응답에 쓸 수 있는 작업을 수행합니다. (예: 헤더 추가, 응답의 시작 부분 작성)
    // 이 부분의 코드는 다음 미들웨어가 실행되기 전에 실행됩니다.

    await next.Invoke(); // 다음 미들웨어(이 경우 app.Run)를 호출하여 제어를 넘깁니다.

    // 응답에 쓰지 않는 로깅 또는 다른 작업을 수행합니다.
    // 이 부분의 코드는 다음 미들웨어가 실행된 후(응답이 생성된 후) 실행됩니다.
});

// 두 번째 미들웨어: app.Run()을 사용하여 파이프라인에 추가합니다.
// app.Run()은 파이프라인을 종료하는 미들웨어입니다.
app.Run(async context =>
{
    // 이 미들웨어는 "Hello from 2nd delegate."를 응답에 작성하고 파이프라인을 종료합니다.
    await context.Response.WriteAsync("Hello from 2nd delegate.");
});

// 이 app.Run()은 위의 app.Run()이 이미 파이프라인을 종료했기 때문에 실행되지 않습니다.
app.Run();</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2702.png" alt="✂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 요청 파이프라인 단락(Short-circuiting)</h3>



<p>ASP.NET Core의 미들웨어 파이프라인에서, 특정 델리게이트(미들웨어)가 요청을 <strong>다음 델리게이트로 전달하지 않을 때</strong> 이를 <strong>요청 파이프라인을 단락(short-circuiting)시킨다</strong>고 합니다. </p>



<p>파이프라인 단락은 불필요한 작업을 피할 수 있기 때문에 종종 유용합니다.</p>



<p>예를 들어, <strong>정적 파일 미들웨어(Static File Middleware)</strong>는 요청된 파일이 정적 파일(이미지, CSS, JavaScript 등)일 경우 해당 요청을 처리하고</p>



<p> 파이프라인의 나머지 부분을 단락시킴으로써 <strong>종료 미들웨어(terminal middleware)</strong> 역할을 할 수 있습니다. </p>



<p>이렇게 되면 정적 파일을 제공한 후에는 <strong>인증이나 라우팅과 같은 다른 미들웨어들이 실행될 필요가 없으므로 효율성이 높아집니다.</strong></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 단락과 <code>next.Invoke()</code> 이후 코드 실행</h4>



<p>파이프라인의 추가적인 처리를 종료시키는 미들웨어보다 <strong>앞서 파이프라인에 추가된 미들웨어</strong>는 여전히 <code>next.Invoke()</code> 구문 이후의 코드를 처리합니다. </p>



<p>즉, 비록 파이프라인이 단락되어 최종 응답이 더 이상 아래로 전달되지 않더라도, <code>next.Invoke()</code>를 호출했던 이전 미들웨어들은 응답이 완료된 후 자신의 후처리 로직을 실행할 수 있습니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 경고: 응답 전송 후 작업 주의</h4>



<p><strong>응답이 클라이언트에게 이미 전송되었거나 전송되기 시작한 후에는 <code>next.Invoke()</code>를 호출하거나 응답에 쓰려고 시도하지 마세요.</strong> </p>



<p><code>HttpResponse</code>가 시작된 후에는 응답에 변경을 가하려고 하면 <strong>예외가 발생</strong>합니다. </p>



<p>예를 들어, 응답이 시작된 후 헤더나 상태 코드를 설정하려고 하면 예외가 발생합니다.</p>



<p><code>next.Invoke()</code> 호출 후 응답 본문에 쓰는 것은 다음과 같은 문제를 야기할 수 있습니다:</p>



<ul class="wp-block-list">
<li><strong>프로토콜 위반:</strong> 명시된 <code>Content-Length</code>보다 더 많은 내용을 작성하는 것과 같은 프로토콜 위반을 초래할 수 있습니다.</li>



<li><strong>본문 형식 손상:</strong> CSS 파일에 HTML 푸터(footer)를 작성하는 것처럼 본문 형식을 손상시킬 수 있습니다.</li>
</ul>



<p><code>HasStarted</code> 속성은 헤더가 전송되었는지 또는 본문이 작성되었는지 여부를 나타내는 유용한 힌트가 될 수 있습니다.</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1fae0.png" alt="🫠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>app.Use</code> vs. <code>app.Run</code></h3>



<p>이 두 메서드는 파이프라인에 미들웨어를 추가하는 데 기본적이지만, 핵심적인 차이점이 있습니다:</p>



<p><code><strong>app.Use(async (context, next) => { ... })</strong></code></p>



<ul class="wp-block-list">
<li><strong>요청 수정 불가:</strong> <br>마지막 단계이므로 요청을 다음으로 전달하기 전에 수정할 수 없습니다.</li>



<li><strong>비종료(Non-Terminal) 미들웨어:</strong> <br>이 유형의 미들웨어는 일반적으로 어떤 작업을 수행한 다음, <code>next</code> 델리게이트를 호출하여 파이프라인의 다음 미들웨어로 제어를 전달합니다.</li>



<li><strong>요청/응답 수정 가능:</strong> <br>요청을 다음으로 전달하기 전에 요청이나 응답을 변경할 수 있습니다.</li>



<li><strong>예시:</strong> <br>인증, 로깅, 커스텀 헤더 추가 등.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>a<strong>pp.Run(async (context) => { ... })</strong></code></p>



<ul class="wp-block-list">
<li><strong>종료(Terminal) 미들웨어:</strong> <br>이 미들웨어는 <code>next</code>를 호출하지 않습니다. <br>파이프라인을 종료하고 <strong>자체적으로 응답을 생성</strong>합니다.</li>



<li><strong>최종 응답에 주로 사용:</strong> <br>더 이상 처리가 필요 없는 요청(예: 간단한 메시지 반환)을 처리하는 데 일반적으로 사용됩니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 여러 app.Run 호출의 결과

app.Run(async (HttpContext context) => {
    await context.Response.WriteAsync("Hello");
});

app.Run(async (HttpContext context) => {
    await context.Response.WriteAsync("Hello again");
});

app.Run(); // 이 app.Run은 위의 app.Run들이 이미 파이프라인을 종료했기 때문에 절대 실행되지 않습니다.</pre>



<p>이 코드에서는 <strong>오직 첫 번째 <code>app.Run</code> 미들웨어만 실행됩니다.</strong> </p>



<p>&#8220;Hello&#8221;를 응답에 작성하여 파이프라인을 종료하고, 그 뒤의 <code>app.Run</code> (이것은 &#8220;Hello again&#8221;을 작성할 것임)은 실행될 기회를 얻지 못합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// app.Use와 app.Run으로 미들웨어 체인 연결하기

// 미들웨어 1
app.Use(async (context, next) => {
    await context.Response.WriteAsync("Hello "); // 1. "Hello " 작성
    await next(context); // 2. 다음 미들웨어 호출
});

// 미들웨어 2
app.Use(async (context, next) => {
    await context.Response.WriteAsync("Hello again "); // 3. "Hello again " 작성
    await next(context); // 4. 다음 미들웨어 호출 (이 경우 app.Run)
});

// 미들웨어 3 (종료 미들웨어)
app.Run(async (HttpContext context) => {
    await context.Response.WriteAsync("Hello again"); // 5. "Hello again" 작성 후 파이프라인 종료
});</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>이 코드는 미들웨어를 올바르게 연결하는 방법을 보여줍니다.</p>



<ol start="1" class="wp-block-list">
<li>첫 번째 <code>app.Use</code>는 응답에 &#8220;Hello &#8220;를 작성하고 <code>next</code>를 호출하여 다음 미들웨어로 제어를 전달합니다.</li>



<li>두 번째 <code>app.Use</code>는 &#8220;Hello again &#8220;을 작성하고 역시 <code>next</code>를 호출합니다.</li>



<li>마지막 <code>app.Run</code> (종료 미들웨어)는 &#8220;Hello again&#8221;을 작성하고 파이프라인을 종료합니다.</li>
</ol>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ASP.NET Core의 커스텀 미들웨어</h3>



<p>ASP.NET Core는 다양한 내장 미들웨어 구성 요소를 제공하지만, 때로는 애플리케이션 고유의 특정 요구 사항을 해결하기 위해 자신만의 <strong>커스텀 미들웨어</strong>를 만들어야 할 수도 있습니다. </p>



<p>커스텀 미들웨어를 통해 다음을 수행할 수 있습니다:</p>



<ul class="wp-block-list">
<li><strong>로직 캡슐화:</strong> <br>관련 작업(예: 로깅, 보안 검사, 사용자 정의 헤더)을 재사용 가능한 구성 요소로 묶습니다.</li>



<li><strong>동작 사용자 정의:</strong> <br>애플리케이션의 요구 사항에 정확히 맞게 요청/응답 파이프라인을 조정합니다.</li>



<li><strong>코드 구성 개선:</strong> <br>미들웨어 코드를 깔끔하고 유지 관리하기 쉽게 만듭니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f527.png" alt="🔧" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 커스텀 미들웨어 클래스의 구조</h4>



<p><strong><code>IMiddleware</code> 구현:</strong> 이 인터페이스는 단 하나의 메서드 <code>InvokeAsync(HttpContext context, RequestDelegate next)</code>를 요구합니다. </p>



<p>이 메서드는 미들웨어 로직의 핵심입니다</p>



<p><code>InvokeAsync</code> 또는 <code>Invoke</code> 메서드</p>



<ul class="wp-block-list">
<li><strong><code>context</code>:</strong> <br><code>HttpContext</code>는 요청 및 응답 객체에 대한 접근을 제공합니다.</li>



<li><strong><code>next</code>:</strong> <br><code>RequestDelegate</code>는 파이프라인의 다음 미들웨어를 호출할 수 있도록 합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// MyCustomMiddleware.cs
namespace MiddlewareExample.CustomMiddleware
{
    public class MyCustomMiddleware : IMiddleware // IMiddleware 인터페이스 구현
    {
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            await context.Response.WriteAsync("My Custom Middleware - Starts\n"); // 1. 응답 시작 부분에 출력
            await next(context); // 2. 다음 미들웨어 호출
            await context.Response.WriteAsync("My Custom Middleware - Ends\n"); // 3. 다음 미들웨어 완료 후 응답 끝 부분에 출력
        }
    }

    // 쉽게 등록하기 위한 확장 메서드
    public static class CustomMiddlewareExtension
    {
        public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder app)
        {
            return app.UseMiddleware&lt;MyCustomMiddleware>();
        }
    }
}</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// Program.cs (또는 Startup.cs)
using MiddlewareExample.CustomMiddleware;

// ... (다른 설정 코드) ...

builder.Services.AddTransient&lt;MyCustomMiddleware>(); // 트랜지언트 서비스로 등록

app.Use(async (HttpContext context, RequestDelegate next) => {
    await context.Response.WriteAsync("From Middleware 1\n"); // 첫 번째 미들웨어: 시작 부분
    await next(context);
});

app.UseMyCustomMiddleware(); // 확장 메서드를 사용하여 커스텀 미들웨어 추가

app.Run(async (HttpContext context) => {
    await context.Response.WriteAsync("From Middleware 3\n"); // 세 번째 미들웨어: 파이프라인 종료
});</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong>등록:</strong> <br>ASP.NET Core가 필요할 때 <code>MyCustomMiddleware</code> 인스턴스를 생성할 수 있도록 이를 트랜지언트 서비스로 등록합니다.</li>



<li><strong>파이프라인 통합:</strong> <br><code>app.UseMyCustomMiddleware()</code> 확장 메서드는 커스텀 미들웨어를 파이프라인에 추가합니다.</li>



<li><strong>실행 순서:</strong> <br>미들웨어 구성 요소는 파이프라인에 추가된 순서대로 실행됩니다. 이 경우 순서는 미들웨어 1, <code>MyCustomMiddleware</code>, 그리고 미들웨어 3이 됩니다.</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 커스텀 컨벤셔널 미들웨어 (Custom Conventional Middleware)</h3>



<p>ASP.NET Core 미들웨어에는 두 가지 유형이 있습니다: <strong>컨벤셔널(Conventional)</strong>과 <strong>팩토리 기반(Factory-based)</strong> </p>



<p>예시에서 보여진 컨벤셔널 미들웨어는 HTTP 요청 및 응답 처리를 위한 커스텀 로직을 캡슐화하는 간단하면서도 강력한 방법입니다.</p>



<p>주요 특징</p>



<ul class="wp-block-list">
<li><strong>클래스 기반:</strong> <br>컨벤셔널 미들웨어는 클래스로 구현됩니다.</li>



<li><strong>생성자 주입:</strong> <br>의존성(있는 경우)을 생성자를 통해 받습니다.</li>



<li><strong><code>Invoke</code> 메서드:</strong> <br>이 메서드는 각 요청을 처리하는 로직을 포함하는 미들웨어의 핵심입니다.</li>



<li><strong><code>RequestDelegate</code>:</strong> <br><code>Invoke</code> 메서드는 <code>RequestDelegate</code> 매개변수(<code>_next</code>로 명명)를 받습니다. 이 델리게이트는 파이프라인의 다음 미들웨어를 나타냅니다.</li>



<li><strong>유연성:</strong> <br><code>Invoke</code> 메서드 내에서 요청 및 응답 객체를 완벽하게 제어할 수 있습니다.</li>
</ul>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="689" height="343" src="https://lycos7560.com/wp-content/uploads/2025/07/image-13.png" alt="" class="wp-image-40132" srcset="https://lycos7560.com/wp-content/uploads/2025/07/image-13.png 689w, https://lycos7560.com/wp-content/uploads/2025/07/image-13-300x149.png 300w" sizes="(max-width: 689px) 100vw, 689px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// NameConcatenationMiddleware.cs
public class NameConcatenationMiddleware
{
    private readonly RequestDelegate _next;

    public NameConcatenationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Query.ContainsKey("firstname") &amp;&amp;
            context.Request.Query.ContainsKey("lastname"))
        {
            string fullName = $"{context.Request.Query["firstname"]} {context.Request.Query["lastname"]}";
            await context.Response.WriteAsync(fullName);
            return; // 명시적으로 반환하여 다음 미들웨어 호출 방지
        }
        
        await _next(context);
    }
}

// MiddlewareExtensions.cs
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseNameConcatenation(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware&lt;NameConcatenationMiddleware>();
    }
}</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f500.png" alt="🔀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 미들웨어 파이프라인의 이상적인 순서</h3>



<p>미들웨어의 순서는 애플리케이션의 동작과 효율성, 보안에 큰 영향을 미칩니다. </p>



<p>다음은 일반적으로 권장되는 이상적인 순서입니다</p>



<figure class="wp-block-image size-full"><img decoding="async" width="858" height="485" src="https://lycos7560.com/wp-content/uploads/2025/07/image-14.png" alt="" class="wp-image-40133" srcset="https://lycos7560.com/wp-content/uploads/2025/07/image-14.png 858w, https://lycos7560.com/wp-content/uploads/2025/07/image-14-300x170.png 300w, https://lycos7560.com/wp-content/uploads/2025/07/image-14-768x434.png 768w" sizes="(max-width: 858px) 100vw, 858px" /><figcaption class="wp-element-caption"><a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0</a></figcaption></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>예외/오류 처리(Exception/Error Handling):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 파이프라인의 어느 곳에서든 발생하는 예외를 Catch하고 처리합니다.</li>



<li><strong>예시:</strong> <code>UseExceptionHandler</code> (프로덕션용), <code>UseDeveloperExceptionPage</code> (개발 환경용).</li>



<li><strong>이유:</strong> 예외를 초기에 Catch하여 파이프라인 아래로 전파되어 더 큰 문제를 일으키는 것을 방지합니다.</li>
</ul>



<p><strong>HTTPS 리디렉션(HTTPS Redirection):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 보안을 위해 HTTP 요청을 HTTPS로 리디렉션합니다.</li>



<li><strong>예시:</strong> <code>UseHttpsRedirection</code>.</li>



<li><strong>이유:</strong> 보안을 최우선으로 하여 모든 통신이 암호화되도록 합니다.</li>
</ul>



<p><strong>정적 파일(Static Files):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 이미지, CSS, JavaScript 파일과 같은 정적 파일을 클라이언트에게 직접 제공합니다.</li>



<li><strong>예시:</strong> <code>UseStaticFiles</code>.</li>



<li><strong>이유:</strong> 정적 파일 요청은 빠르게 처리되어야 하며, 불필요하게 파이프라인의 다른 무거운 구성 요소를 거치지 않도록 일찍 처리합니다.</li>
</ul>



<p><strong>라우팅(Routing):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> URL을 기반으로 들어오는 요청을 특정 엔드포인트에 매칭합니다.</li>



<li><strong>예시:</strong> <code>UseRouting</code>, <code>UseEndpoints</code>.</li>



<li><strong>이유:</strong> 라우팅은 애플리케이션의 핵심 로직이 요청을 어떻게 처리할지 결정하는 기반이 됩니다.</li>
</ul>



<p><strong>CORS (Cross-Origin Resource Sharing):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 다른 도메인으로부터의 안전한 교차 출처(cross-origin) 요청을 가능하게 합니다.</li>



<li><strong>예시:</strong> <code>UseCors</code>.</li>



<li><strong>이유:</strong> 인증/인가 전에 위치하여, 사전 요청(preflight request)이 불필요하게 인증/인가 미들웨어를 거치지 않도록 합니다.</li>
</ul>



<p><strong>인증(Authentication):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 사용자 신원을 확인하고 사용자 주체(principal)를 설정합니다.</li>



<li><strong>예시:</strong> <code>UseAuthentication</code>.</li>



<li><strong>이유:</strong> 사용자가 누구인지 확인한 후에 리소스에 대한 접근 권한을 부여할 수 있습니다.</li>
</ul>



<p><strong>인가(Authorization):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 사용자가 특정 리소스에 접근하거나 특정 작업을 수행할 수 있는지 여부를 결정합니다.</li>



<li><strong>예시:</strong> <code>UseAuthorization</code>.</li>



<li><strong>이유:</strong> 인증된 사용자에게만 권한 부여 여부를 검사합니다.</li>
</ul>



<p><strong>커스텀 미들웨어(Custom Middleware):</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 로깅, 기능 플래그 등 애플리케이션별 미들웨어 구성 요소를 처리합니다.</li>



<li><strong>이유:</strong> 애플리케이션별 로직을 적절한 단계에서 파이프라인 내에 배치합니다.</li>
</ul>



<p><strong>MVC/Razor Pages/Minimal APIs:</strong></p>



<ul class="wp-block-list">
<li><strong>목적:</strong> 실제 애플리케이션의 최종 엔드포인트 처리 로직을 실행합니다.</li>



<li><strong>예시:</strong> <code>MapControllers()</code>, <code>MapRazorPages()</code>, <code>MapGet()</code> 등.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="848" height="589" src="https://lycos7560.com/wp-content/uploads/2025/07/image-15.png" alt="" class="wp-image-40134" srcset="https://lycos7560.com/wp-content/uploads/2025/07/image-15.png 848w, https://lycos7560.com/wp-content/uploads/2025/07/image-15-300x208.png 300w, https://lycos7560.com/wp-content/uploads/2025/07/image-15-768x533.png 768w" sizes="(max-width: 848px) 100vw, 848px" /><figcaption class="wp-element-caption"><a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0</a></figcaption></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>Program.cs</code> 파일에 미들웨어 구성 요소가 추가되는 순서는 요청 시 미들웨어 구성 요소가 호출되는 순서를 정의하며, 응답 시에는 역순으로 호출됩니다. </p>



<p>이러한 <strong>순서는 보안, 성능 및 기능에 매우 중요합니다.</strong></p>



<p><code>Program.cs</code>의 다음 강조 표시된 코드는 보안 관련 미들웨어 구성 요소를 일반적으로 권장되는 순서로 추가하는 예시입니다:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="19-43" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext&lt;ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity&lt;IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores&lt;ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();

app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();

app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> UseWhen()</h3>



<p><code>UseWhen()</code>은 ASP.NET Core의 <code>IApplicationBuilder</code> 인터페이스에 있는 강력한 확장 메서드입니다. </p>



<p>이는 <strong>조건(predicate)에 따라 미들웨어를 요청 파이프라인에 조건부로 추가</strong>할 수 있도록 합니다. </p>



<p>즉, 특정 조건이 충족될 때만 특정 미들웨어 구성 요소가 실행되는 동적인 파이프라인을 만들 수 있습니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.UseWhen(
    context => /* 여기에 조건 */, // HttpContext를 받아 true/false 반환
    app => /* 이 분기에서 실행될 미들웨어 구성 */ // 조건이 true일 때 실행될 미들웨어 파이프라인
);</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong><code>context</code>:</strong> <br>현재 요청을 나타내는 <code>HttpContext</code> 객체입니다.</li>



<li><strong>Predicate (조건):</strong> <br><code>HttpContext</code>를 받아들이고 미들웨어 분기가 실행되어야 할 경우 <code>true</code>를, 그렇지 않을 경우 <code>false</code>를 반환하는 함수입니다.</li>



<li><strong>Middleware Configuration (미들웨어 구성):</strong> <br>조건이 <code>true</code>일 때 실행되어야 할 미들웨어 구성 요소를 구성하는 액션입니다. <br>여기서 <code>app.Use()</code>, <code>app.Run()</code>, 또는 다른 미들웨어 등록 메서드를 사용합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>UseWhen()</code> 작동 방식</h4>



<ul class="wp-block-list">
<li><strong>조건 평가:</strong> <br>요청이 들어오면 <code>UseWhen()</code> 메서드는 먼저 <code>HttpContext</code>에 대해 조건 함수를 평가합니다.</li>



<li><strong>분기(조건이 true일 경우):</strong> <br>조건이 <code>true</code>를 반환하면, 구성 액션에 지정된 미들웨어 분기가 실행됩니다. <br>요청은 이 분기를 통해 흐르며, 수정되거나 응답을 생성할 수 있습니다.</li>



<li><strong>메인 파이프라인 재진입:</strong> <br>분기가 실행된 후(또는 조건이 <code>false</code>여서 건너뛰어진 경우), 요청 흐름은 메인 파이프라인으로 다시 진입하여 <code>UseWhen()</code> 호출 뒤에 등록된 다음 미들웨어 구성 요소로 계속 진행됩니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.UseWhen(
    context => context.Request.Query.ContainsKey("username"), // 조건: 쿼리 문자열에 "username"이 있는지 확인
    app => {
        app.Use(async (context, next) =>
        {
            await context.Response.WriteAsync("Hello from Middleware branch\n"); // 분기 미들웨어: "Hello from Middleware branch" 작성
            await next(); // 다음 미들웨어 호출
        });
    });

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from middleware at main chain"); // 메인 파이프라인 미들웨어
});</pre>



<ul class="wp-block-list">
<li><strong>조건:</strong> <br><code>context.Request.Query.ContainsKey("username")</code> 조건은 쿼리 문자열에 &#8220;username&#8221;이라는 매개변수가 포함되어 있는지 확인합니다.</li>



<li><strong>분기 미들웨어:</strong> <br>&#8220;username&#8221; 매개변수가 존재하면 분기 미들웨어가 실행됩니다. <br>이 미들웨어는 응답에 &#8220;Hello from Middleware branch&#8221;를 작성하고 <code>next</code>를 호출하여 나머지 파이프라인이 계속되도록 합니다.</li>



<li><strong>메인 파이프라인:</strong> <br>마지막 <code>app.Run</code> 미들웨어는 메인 파이프라인의 일부입니다. <br>이는 응답에 &#8220;Hello from middleware at main chain&#8221;을 작성합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f98b.png" alt="🦋" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>UseWhen()</code> 사용 시점</h4>



<ul class="wp-block-list">
<li><strong>조건부 기능:</strong> <br>요청에 따라 특정 기능을 활성화하거나 비활성화합니다 (예: 특정 사용자에게만 로깅, 쿼리 매개변수에 따른 캐싱 규칙 적용).</li>



<li><strong>동적 파이프라인:</strong> <br>다양한 요청에 맞춰 조정되는 파이프라인을 만듭니다 (예: 특정 경로에 대해 다른 인증 미들웨어).</li>



<li><strong>A/B 테스트:</strong> <br>실험을 위해 사용자 하위 집합을 대체 미들웨어 분기를 통해 라우팅합니다.</li>



<li><strong>디버깅 및 진단:</strong> <br>개발 환경에서만 진단 미들웨어를 적용합니다.</li>
</ul>



<p></p>
<p>The post <a href="https://lycos7560.com/c/asp-net-core%ec%9d%98-%eb%af%b8%eb%93%a4%ec%9b%a8%ec%96%b4middleware/40130/">ASP.NET Core의 미들웨어(Middleware)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/asp-net-core%ec%9d%98-%eb%af%b8%eb%93%a4%ec%9b%a8%ec%96%b4middleware/40130/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>.NET의 파이프 작업 (Pipe Operations in .NET)</title>
		<link>https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/</link>
					<comments>https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Tue, 15 Jul 2025 05:08:17 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.NETCore]]></category>
		<category><![CDATA[.NETFramework]]></category>
		<category><![CDATA[AnonymousPipe]]></category>
		<category><![CDATA[Asynchronous]]></category>
		<category><![CDATA[BackgroundService]]></category>
		<category><![CDATA[CancellationToken]]></category>
		<category><![CDATA[ChildProcess]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[CrossProcess]]></category>
		<category><![CDATA[DataExchange]]></category>
		<category><![CDATA[DataTransfer]]></category>
		<category><![CDATA[DependencyInjection]]></category>
		<category><![CDATA[Duplex]]></category>
		<category><![CDATA[ErrorHandling]]></category>
		<category><![CDATA[Flush]]></category>
		<category><![CDATA[GenericHost]]></category>
		<category><![CDATA[HandleInheritability]]></category>
		<category><![CDATA[HostBuilder]]></category>
		<category><![CDATA[IHostedService]]></category>
		<category><![CDATA[Impersonation]]></category>
		<category><![CDATA[InterprocessCommunication]]></category>
		<category><![CDATA[IOException]]></category>
		<category><![CDATA[IPC]]></category>
		<category><![CDATA[IPCMechanism]]></category>
		<category><![CDATA[IPC메커니즘]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Lifecycle]]></category>
		<category><![CDATA[LocalCommunication]]></category>
		<category><![CDATA[MemoryBuffer]]></category>
		<category><![CDATA[MessageBased]]></category>
		<category><![CDATA[NamedPipe]]></category>
		<category><![CDATA[NetworkCommunication]]></category>
		<category><![CDATA[OneWay]]></category>
		<category><![CDATA[Operations]]></category>
		<category><![CDATA[OS]]></category>
		<category><![CDATA[ParentProcess]]></category>
		<category><![CDATA[pipe]]></category>
		<category><![CDATA[Process]]></category>
		<category><![CDATA[Read]]></category>
		<category><![CDATA[Reconnect]]></category>
		<category><![CDATA[Resilience]]></category>
		<category><![CDATA[SecurityContext]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[Stream]]></category>
		<category><![CDATA[StreamReader]]></category>
		<category><![CDATA[StreamWriter]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Sync]]></category>
		<category><![CDATA[Threading]]></category>
		<category><![CDATA[Timeout]]></category>
		<category><![CDATA[UDS]]></category>
		<category><![CDATA[UnixDomainSockets]]></category>
		<category><![CDATA[VirtualFile]]></category>
		<category><![CDATA[WindowsPipes]]></category>
		<category><![CDATA[Write]]></category>
		<category><![CDATA[가상파일]]></category>
		<category><![CDATA[가장]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[네트워크통신]]></category>
		<category><![CDATA[단방향]]></category>
		<category><![CDATA[데이터교환]]></category>
		<category><![CDATA[데이터전송]]></category>
		<category><![CDATA[동기]]></category>
		<category><![CDATA[로컬통신]]></category>
		<category><![CDATA[메모리버퍼]]></category>
		<category><![CDATA[메시지기반]]></category>
		<category><![CDATA[명명된파이프]]></category>
		<category><![CDATA[백그라운드서비스]]></category>
		<category><![CDATA[보안컨텍스트]]></category>
		<category><![CDATA[복원력]]></category>
		<category><![CDATA[부모프로세스]]></category>
		<category><![CDATA[비동기]]></category>
		<category><![CDATA[생명주기]]></category>
		<category><![CDATA[서버]]></category>
		<category><![CDATA[스레딩]]></category>
		<category><![CDATA[스트림]]></category>
		<category><![CDATA[스트림라이터]]></category>
		<category><![CDATA[스트림리더]]></category>
		<category><![CDATA[쓰기]]></category>
		<category><![CDATA[양방향]]></category>
		<category><![CDATA[오류처리]]></category>
		<category><![CDATA[운영체제]]></category>
		<category><![CDATA[의존성주입]]></category>
		<category><![CDATA[익명파이프]]></category>
		<category><![CDATA[일반호스트]]></category>
		<category><![CDATA[읽기]]></category>
		<category><![CDATA[자식프로세스]]></category>
		<category><![CDATA[재연결]]></category>
		<category><![CDATA[취소토큰]]></category>
		<category><![CDATA[커널]]></category>
		<category><![CDATA[클라이언트]]></category>
		<category><![CDATA[타임아웃]]></category>
		<category><![CDATA[통신]]></category>
		<category><![CDATA[파이프오류]]></category>
		<category><![CDATA[프로세스]]></category>
		<category><![CDATA[프로세스간]]></category>
		<category><![CDATA[프로세스간통신]]></category>
		<category><![CDATA[플러시]]></category>
		<category><![CDATA[핸들상속]]></category>
		<category><![CDATA[호스트빌더]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40115</guid>

					<description><![CDATA[<p>네임드 파이프(Named Pipe)는 프로세스 간 통신(IPC) 방법 중 하나로, 파일 시스템에 이름이 지정된 파이프를 생성하여 프로세스들이 해당 파이프를 통해 데이터를 주고받을 수 있도록 합니다. 일반적인 파이프(익명 파이프)는 부모-자식 관계와 같이 명확히 연관된 프로세스 간에만 사용 가능하지만, 네임드 파이프는 서로 관련 없는 프로세스들도 이름을 통해 통신할 수 있다는 특징을 가집니다. </p>
<p>The post <a href="https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/">.NET의 파이프 작업 (Pipe Operations in .NET)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-65798460      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#net의-파이프-작업" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET의 파이프 작업</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#익명-파이프-anonymous-pipes" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 익명 파이프 (Anonymous Pipes)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#익명-파이프-anonymous-pipes-사용-방법" class="uagb-toc-link__trigger">익명 파이프 (Anonymous Pipes) 사용 방법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-1-서버-프로세스-부모-프로세스" class="uagb-toc-link__trigger">예제 1: 서버 프로세스 (부모 프로세스)</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-2-클라이언트-프로세스-자식-프로세스" class="uagb-toc-link__trigger">예제 2: 클라이언트 프로세스 (자식 프로세스)</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#명명된-파이프-named-pipes" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 명명된 파이프 (Named Pipes)</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#명명된-파이프named-pipes-사용-방법" class="uagb-toc-link__trigger">명명된 파이프(Named Pipes) 사용 방법</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-1-namedpipeserverstream-클래스를-사용하여-명명된-파이프-생성하기" class="uagb-toc-link__trigger">예제 1: NamedPipeServerStream 클래스를 사용하여 명명된 파이프 생성하기</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예제-2-namedpipeclientstream-클래스를-사용하는-클라이언트-프로세스" class="uagb-toc-link__trigger">예제 2: NamedPipeClientStream 클래스를 사용하는 클라이언트 프로세스</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#강력한-프로그래밍-named-pipe-클라이언트서버-설정" class="uagb-toc-link__trigger">강력한 프로그래밍: Named Pipe 클라이언트/서버 설정</a></li></ul></li></ul><li class="uagb-toc__list"><a href="#추가-정리" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" />추가 정리</a></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET의 파이프 작업</h2>



<p><a href="https://learn.microsoft.com/en-us/dotnet/standard/io/pipe-operations" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/standard/io/pipe-operations</a></p>



<p>파이프는 <strong>프로세스 간 통신(Interprocess Communication, IPC)을 위한 수단을 제공</strong>합니다. </p>



<p>파이프에는 두 가지 유형이 있습니다:</p>



<ul class="wp-block-list">
<li>익명 파이프 (Anonymous Pipes)</li>



<li>명명된 파이프 (Named Pipes)</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 익명 파이프 (Anonymous Pipes)</h3>



<p>익명 파이프는 로컬 컴퓨터에서 프로세스 간 통신을 제공합니다. </p>



<p>익명 파이프는 명명된 파이프보다 오버헤드가 적지만 제공하는 서비스가 제한적입니다. </p>



<p>익명 파이프는 <strong>단방향</strong>이며 네트워크를 통해 사용할 수 없습니다. 또한 <strong>단일 서버 인스턴스만 지원</strong>합니다. </p>



<p>익명 파이프는 스레드 간 통신, 또는 파이프 핸들을 자식 프로세스가 생성될 때 쉽게 전달할 수 있는 부모-자식 프로세스 간 통신에 유용합니다.</p>



<p>.NET에서는 <strong><code>AnonymousPipeServerStream</code> </strong>및 <strong><code>AnonymousPipeClientStream</code> </strong>클래스를 사용하여 익명 파이프를 구현합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">익명 파이프 (Anonymous Pipes) 사용 방법</h4>



<p><a href="https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-anonymous-pipes-for-local-interprocess-communication" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-anonymous-pipes-for-local-interprocess-communication</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 1: 서버 프로세스 (부모 프로세스)</h5>



<p>다음 예제는 익명 파이프를 사용하여 부모 프로세스에서 자식 프로세스로 문자열을 보내는 방법을 보여줍니다.</p>



<p>이 예제에서는 부모 프로세스에 <code>PipeDirection.Out</code> 값을 가진 <code>AnonymousPipeServerStream</code> 객체를 생성합니다. </p>



<p>그런 다음 부모 프로세스는 클라이언트 핸들을 사용하여 <code>AnonymousPipeClientStream</code> 객체를 생성하는 자식 프로세스를 시작합니다. </p>



<p>자식 프로세스는 <code>PipeDirection.In</code> 값을 가집니다.</p>



<p>이후 부모 프로세스는 사용자가 입력한 문자열을 자식 프로세스로 보냅니다. </p>



<p>해당 문자열은 자식 프로세스의 콘솔에 표시됩니다. 다음은 서버 프로세스(부모 프로세스) 코드입니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics; // Process 클래스를 사용하기 위해 추가

class PipeServer
{
    static void Main()
    {
        Process pipeClient = new Process(); // 자식 프로세스를 제어할 Process 객체 생성

        pipeClient.StartInfo.FileName = "pipeClient.exe"; // 실행할 자식 프로세스의 파일 이름 설정

        // AnonymousPipeServerStream 객체 생성
        // PipeDirection.Out: 서버 -> 클라이언트로 데이터를 보냄 (단방향)
        // HandleInheritability.Inheritable: 자식 프로세스가 이 파이프 핸들을 상속받을 수 있도록 설정
        using (AnonymousPipeServerStream pipeServer =
            new AnonymousPipeServerStream(PipeDirection.Out,
            HandleInheritability.Inheritable))
        {
            Console.WriteLine($"[SERVER] 현재 전송 모드: {pipeServer.TransmissionMode}.");

            // 자식 프로세스에게 서버 파이프의 클라이언트 핸들을 문자열로 전달합니다.
            pipeClient.StartInfo.Arguments =
                pipeServer.GetClientHandleAsString();
            pipeClient.StartInfo.UseShellExecute = false; // 셸 실행 대신 직접 프로세스 시작
            pipeClient.Start(); // 자식 프로세스 시작

            // 자식 프로세스가 핸들을 상속받은 후, 부모 프로세스는 자신의 클라이언트 핸들 복사본을 해제합니다.
            // 이는 불필요한 리소스 유지를 방지합니다.
            pipeServer.DisposeLocalCopyOfClientHandle();

            try
            {
                // 사용자 입력을 읽고 그 내용을 클라이언트 프로세스로 보냅니다.
                using (StreamWriter sw = new StreamWriter(pipeServer))
                {
                    sw.AutoFlush = true; // 매 쓰기 작업 후 스트림 자동 플러시 설정

                    // '동기화 메시지'를 클라이언트로 보내고 클라이언트가 이를 받을 때까지 기다립니다.
                    sw.WriteLine("SYNC");
                    pipeServer.WaitForPipeDrain(); // 파이프에 기록된 모든 데이터가 전송될 때까지 대기

                    // 콘솔 입력을 클라이언트 프로세스로 보냅니다.
                    Console.Write("[SERVER] 텍스트를 입력하세요: ");
                    sw.WriteLine(Console.ReadLine());
                }
            }
            // 파이프가 끊어지거나 연결이 해제될 때 발생하는 IOException을 잡습니다.
            catch (IOException e)
            {
                Console.WriteLine($"[SERVER] 오류: {e.Message}");
            }
        } // using 블록을 벗어나면 pipeServer는 자동으로 Dispose됩니다.

        // 자식 프로세스가 종료될 때까지 기다립니다.
        pipeClient.WaitForExit();
        // 자식 프로세스 객체를 닫습니다.
        pipeClient.Close();
        Console.WriteLine("[SERVER] 클라이언트 종료. 서버도 종료합니다.");
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 2: 클라이언트 프로세스 (자식 프로세스)</h5>



<p>다음 예제는 클라이언트 프로세스를 보여줍니다. </p>



<p>서버 프로세스는 클라이언트 프로세스를 시작하고 해당 프로세스에 클라이언트 핸들을 전달합니다. </p>



<p>클라이언트 코드로부터 생성된 실행 파일은 <code>pipeClient.exe</code>라는 이름으로 서버 실행 파일과 동일한 디렉토리에 복사되어야 서버 프로세스를 실행하기 전에 올바르게 동작합니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.IO;
using System.IO.Pipes;

class PipeClient
{
    static void Main(string[] args)
    {
        // 프로그램 인수가 있는지 확인합니다.
        // 서버가 클라이언트 핸들을 인수로 전달하기 때문에 필요합니다.
        if (args.Length > 0)
        {
            // AnonymousPipeClientStream 객체를 생성합니다.
            // PipeDirection.In: 클라이언트는 서버로부터 데이터를 받음 (단방향)
            // args[0]: 서버가 전달한 파이프 핸들 문자열
            using (PipeStream pipeClient =
                new AnonymousPipeClientStream(PipeDirection.In, args[0]))
            {
                Console.WriteLine($"[CLIENT] 현재 전송 모드: {pipeClient.TransmissionMode}.");

                // 파이프 스트림에서 텍스트를 읽기 위한 StreamReader 생성
                using (StreamReader sr = new StreamReader(pipeClient))
                {
                    string temp;

                    // 서버로부터 '동기화 메시지'가 올 때까지 기다립니다.
                    do
                    {
                        Console.WriteLine("[CLIENT] 동기화 대기 중...");
                        temp = sr.ReadLine();
                    }
                    while (!temp.StartsWith("SYNC")); // 'SYNC'로 시작하는 메시지를 받을 때까지 반복

                    // 서버 데이터를 읽고 콘솔에 출력합니다.
                    while ((temp = sr.ReadLine()) != null) // 스트림의 끝(null)이 아닐 때까지 읽기
                    {
                        Console.WriteLine("[CLIENT] 에코: " + temp);
                    }
                }
            } // using 블록을 벗어나면 pipeClient는 자동으로 Dispose됩니다.
        }
        Console.Write("[CLIENT] 계속하려면 Enter 키를 누르세요...");
        Console.ReadLine(); // 사용자가 Enter를 누를 때까지 대기
    }
}</pre>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2728.png" alt="✨" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 명명된 파이프 (Named Pipes)</h3>



<p>명명된 파이프는 파이프 서버와 하나 이상의 파이프 클라이언트 간 프로세스 간 통신을 제공합니다. </p>



<p>명명된 파이프는 <strong>단방향 또는 양방향</strong>이 될 수 있습니다. </p>



<p>이는 <strong>메시지 기반 통신을 지원</strong>하며, <strong>여러 클라이언트가 동일한 파이프 이름을 사용하여 서버 프로세스에 동시에 연결</strong>할 수 있도록 합니다. </p>



<p>명명된 파이프는 또한 가장(impersonation)을 지원하는데, 이를 통해 연결 프로세스가 원격 서버에서 자신의 권한을 사용할 수 있습니다.</p>



<p>.NET에서는 <strong><code>NamedPipeServerStream</code> </strong>및 <strong><code>NamedPipeClientStream</code> </strong>클래스를 사용하여 명명된 파이프를 구현합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2753.png" alt="❓" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 가장(Impersonation)이란?</p>



<p>컴퓨터 시스템에서 어떤 리소스(예: 파일, 네트워크 공유, 데이터베이스)에 접근하려면 해당 작업을 수행하는 <strong>사용자 계정의 권한</strong>이 필요</p>



<p>일반적으로 서버 애플리케이션은 특정 계정(예: 시스템 계정, 서비스 계정)의 권한으로 실행됩니다.</p>



<p>만약 서버가 <strong>클라이언트가 요청한 작업을 클라이언트 자신의 권한으로 수행</strong>해야 할 때가 있습니다. 예를 들어:</p>



<ul class="wp-block-list">
<li><strong>클라이언트가 접근 권한을 가진 특정 파일 읽기/쓰기:</strong> <br>서버는 클라이언트가 보낸 파일 경로에 대해 읽기/쓰기 요청을 받았습니다. <br>하지만 서버는 해당 파일에 직접 접근할 권한이 없고, <strong>오직 클라이언트 계정만 그 파일에 접근할 권한</strong>을 가지고 있을 수 있습니다.</li>



<li><strong>클라이언트의 네트워크 자격 증명 사용:</strong> <br>클라이언트가 원격 네트워크 공유에 있는 리소스에 접근하고 싶을 때, <br>서버가 클라이언트를 &#8216;가장&#8217;하여 클라이언트의 네트워크 자격 증명(사용자 이름과 비밀번호)을 사용하여 해당 공유에 접근할 수 있습니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">명명된 파이프(Named Pipes) 사용 방법</h4>



<p><a href="https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 1: <code>NamedPipeServerStream</code> 클래스를 사용하여 명명된 파이프 생성하기</h5>



<p>다음 예제는 <code>NamedPipeServerStream</code> 클래스를 사용하여 <strong>명명된 파이프를 생성하는 방법</strong>을 보여줍니다. </p>



<p>이 예제에서 서버 프로세스는 <strong>4개의 스레드를 생성</strong>합니다. 각 스레드는 <strong>하나의 클라이언트 연결을 수락</strong>할 수 있습니다. </p>



<p>연결된 클라이언트 프로세스는 서버에 파일 이름을 제공합니다. </p>



<p>만약 클라이언트가 충분한 권한을 가지고 있다면, 서버 프로세스는 해당 파일을 열고 그 내용을 다시 클라이언트에게 보냅니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;

public class PipeServer
{
    // 동시에 처리할 서버 스레드의 개수 (클라이언트 연결을 수락할 인스턴스 수)
    private static int numThreads = 4;

    public static void Main()
    {
        int i;
        // 서버 스레드를 담을 배열 선언
        Thread?[] servers = new Thread[numThreads];

        Console.WriteLine("\n*** 명명된 파이프 서버 스트림과 가장(impersonation) 예제 ***\n");
        Console.WriteLine("클라이언트 연결 대기 중...\n");

        // numThreads 개수만큼 서버 스레드를 생성하고 시작합니다.
        for (i = 0; i &lt; numThreads; i++)
        {
            servers[i] = new Thread(ServerThread); // ServerThread 메서드를 실행할 새 스레드 생성
            servers[i]?.Start(); // 스레드 시작
        }

        // 서버 스레드가 시작될 시간을 잠시 기다립니다.
        Thread.Sleep(250);

        // 모든 서버 스레드가 작업을 완료할 때까지 대기하는 루프
        // 'i'는 아직 완료되지 않은 스레드의 개수를 추적합니다.
        while (i > 0)
        {
            for (int j = 0; j &lt; numThreads; j++)
            {
                if (servers[j] != null) // 스레드가 아직 활성 상태인 경우
                {
                    // 스레드가 250ms 내에 완료되는지 확인합니다.
                    // 완료되면 해당 스레드를 null로 설정하고 카운트를 줄입니다.
                    if (servers[j]!.Join(250))
                    {
                        Console.WriteLine($"서버 스레드[{servers[j]!.ManagedThreadId}] 작업 완료.");
                        servers[j] = null; // 완료된 스레드 참조 제거
                        i--;               // 완료되지 않은 스레드 개수 감소
                    }
                }
            }
        }
        Console.WriteLine("\n서버 스레드 모두 소진, 종료합니다.");
    }

    // 각 클라이언트 연결을 처리할 서버 스레드의 진입점 메서드
    private static void ServerThread(object? data)
    {
        // NamedPipeServerStream 인스턴스 생성
        // "testpipe": 파이프 이름 (클라이언트가 이 이름으로 연결 시도)
        // PipeDirection.InOut: 양방향 통신
        // numThreads: 이 파이프 이름으로 동시에 허용할 서버 인스턴스(클라이언트 연결)의 최대 수
        NamedPipeServerStream pipeServer =
            new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);

        int threadId = Thread.CurrentThread.ManagedThreadId; // 현재 스레드의 ID

        // 클라이언트가 연결될 때까지 대기합니다.
        // 이 메서드는 블로킹(blocking) 호출입니다.
        pipeServer.WaitForConnection();

        Console.WriteLine($"클라이언트가 스레드[{threadId}]에 연결되었습니다.");
        try
        {
            // 클라이언트로부터 요청을 읽습니다.
            // 클라이언트가 파이프에 데이터를 쓰면, 클라이언트의 보안 토큰을 사용할 수 있게 됩니다.
            StreamString ss = new StreamString(pipeServer);

            // 연결된 클라이언트에게 서버임을 증명하는 문자열을 보냅니다.
            // 클라이언트는 이 문자열을 예상하고 있습니다.
            ss.WriteString("나는 유일한 참된 서버입니다!");
            // 클라이언트로부터 파일 이름을 읽습니다.
            string filename = ss.ReadString();

            // 클라이언트를 가장(impersonate)하여 파일 내용을 읽어 스트림으로 보냅니다.
            // ReadFileToStream 클래스는 파일 읽기 로직을 캡슐화합니다.
            ReadFileToStream fileReader = new ReadFileToStream(ss, filename);

            // 현재 가장하고 있는 사용자의 이름을 출력합니다.
            Console.WriteLine($"파일 읽기: {filename} (스레드[{threadId}]) 사용자: {pipeServer.GetImpersonationUserName()}로.");
            // 클라이언트를 가장한 보안 컨텍스트 내에서 fileReader.Start 메서드를 실행합니다.
            // 이 시점부터 Start() 메서드 내의 파일 접근은 클라이언트의 권한으로 이루어집니다.
            pipeServer.RunAsClient(fileReader.Start);
        }
        // 파이프가 끊어지거나 연결이 해제될 때 발생하는 IOException을 잡습니다.
        catch (IOException e)
        {
            Console.WriteLine($"오류: {e.Message}");
        }
        finally
        {
            // 파이프 서버 스트림을 닫고 리소스를 해제합니다.
            pipeServer.Close();
            Console.WriteLine($"스레드[{threadId}] 파이프 서버 닫힘.");
        }
    }
}

// 스트림에서 문자열을 읽고 쓰기 위한 데이터 프로토콜을 정의하는 클래스
public class StreamString
{
    private Stream ioStream; // 기본 스트림 (NamedPipeServerStream 또는 NamedPipeClientStream)
    private UnicodeEncoding streamEncoding; // 문자열 인코딩 방식

    public StreamString(Stream ioStream)
    {
        this.ioStream = ioStream;
        streamEncoding = new UnicodeEncoding(); // UTF-16 인코딩 사용
    }

    // 스트림에서 문자열을 읽습니다.
    public string ReadString()
    {
        // 먼저 문자열의 길이를 2바이트로 읽습니다. (Big-endian 방식으로)
        int len = 0;
        len = ioStream.ReadByte() * 256; // 첫 번째 바이트 (상위 8비트)
        len += ioStream.ReadByte();      // 두 번째 바이트 (하위 8비트)

        // 읽을 길이만큼의 바이트 배열을 생성합니다.
        byte[] inBuffer = new byte[len];
        // 스트림에서 해당 길이만큼의 바이트를 읽어 버퍼에 채웁니다.
        ioStream.Read(inBuffer, 0, len);

        // 바이트 배열을 문자열로 디코딩하여 반환합니다.
        return streamEncoding.GetString(inBuffer);
    }

    // 스트림에 문자열을 씁니다.
    public int WriteString(string outString)
    {
        // 문자열을 바이트 배열로 인코딩합니다.
        byte[] outBuffer = streamEncoding.GetBytes(outString);
        int len = outBuffer.Length;

        // 문자열 길이가 UInt16.MaxValue (65535)를 초과하면 잘라냅니다.
        // (프로토콜이 2바이트 길이 필드를 사용하므로 최대 65535 바이트까지 표현 가능)
        if (len > UInt16.MaxValue)
        {
            len = (int)UInt16.MaxValue;
        }

        // 문자열의 길이를 2바이트로 스트림에 씁니다. (Big-endian 방식으로)
        ioStream.WriteByte((byte)(len / 256)); // 상위 바이트
        ioStream.WriteByte((byte)(len &amp; 255)); // 하위 바이트
        // 문자열 바이트 배열을 스트림에 씁니다.
        ioStream.Write(outBuffer, 0, len);
        // 버퍼에 남아있는 데이터를 즉시 스트림으로 밀어 넣습니다.
        ioStream.Flush();

        // 전송된 총 바이트 수 (문자열 바이트 + 길이 2바이트)를 반환합니다.
        return outBuffer.Length + 2;
    }
}

// 가장된 사용자(클라이언트)의 컨텍스트에서 실행될 메서드를 포함하는 클래스
public class ReadFileToStream
{
    private string fn; // 읽을 파일 이름
    private StreamString ss; // 파일 내용을 쓸 스트림 (StreamString 헬퍼 사용)

    public ReadFileToStream(StreamString str, string filename)
    {
        fn = filename;
        ss = str;
    }

    // 이 메서드는 pipeServer.RunAsClient()에 의해 호출되며,
    // 호출되는 동안 현재 스레드의 보안 컨텍스트가 클라이언트로 변경됩니다.
    public void Start()
    {
        // 클라이언트의 권한으로 파일을 읽습니다.
        string contents = File.ReadAllText(fn);
        // 읽은 파일 내용을 스트림을 통해 클라이언트에게 다시 보냅니다.
        ss.WriteString(contents);
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">예제 2: <code>NamedPipeClientStream</code> 클래스를 사용하는 클라이언트 프로세스</h5>



<p>다음 예제는 <code>NamedPipeClientStream</code> 클래스를 사용하는 클라이언트 프로세스를 보여줍니다. </p>



<p>이 클라이언트는 서버 프로세스에 연결하여 서버로 파일 이름을 전송합니다. </p>



<p>이 예제에서는 <strong>가장(impersonation)</strong> 기능을 사용하므로, 클라이언트 애플리케이션을 실행하는 <strong>사용자 계정에 해당 파일에 접근할 수 있는 권한이 있어야</strong> 합니다. </p>



<p>그러면 서버는 파일 내용을 다시 클라이언트에게 전송하고, 클라이언트는 그 내용을 콘솔에 표시합니다.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using System;
using System.Diagnostics; // 프로세스 관련 기능을 위해 추가
using System.IO;          // 파일 및 스트림 관련 기능을 위해 추가
using System.IO.Pipes;    // Named Pipe 관련 기능을 위해 추가
using System.Security.Principal; // TokenImpersonationLevel을 위해 추가
using System.Text;        // 인코딩을 위해 추가
using System.Threading;   // 스레드 및 Sleep을 위해 추가

public class PipeClient
{
    // 생성할 클라이언트 프로세스의 개수
    private static int numClients = 4;

    public static void Main(string[] args)
    {
        // 프로그램이 'spawnclient' 인수를 가지고 실행되었는지 확인합니다.
        // 이 인수는 메인 프로세스가 다른 클라이언트 프로세스를 시작할 때 사용됩니다.
        if (args.Length > 0)
        {
            if (args[0] == "spawnclient")
            {
                // 'spawnclient' 인수가 있는 경우, 명명된 파이프 클라이언트를 생성하고 서버에 연결합니다.
                var pipeClient =
                    new NamedPipeClientStream(
                        ".",                 // 파이프가 로컬 컴퓨터에 있음을 나타냅니다. (서버 주소)
                        "testpipe",          // 연결할 파이프 이름 (서버의 파이프 이름과 일치해야 합니다)
                        PipeDirection.InOut, // 양방향 통신
                        PipeOptions.None,    // 추가 파이프 옵션 없음
                        TokenImpersonationLevel.Impersonation); // 클라이언트의 보안 토큰을 서버로 전송하여 가장을 허용합니다.

                Console.WriteLine("서버에 연결 중...\n");
                // 서버에 연결될 때까지 대기합니다.
                pipeClient.Connect();

                var ss = new StreamString(pipeClient);
                // 서버의 서명 문자열("I am the one true server!")을 읽어 유효성을 검사합니다.
                if (ss.ReadString() == "I am the one true server!")
                {
                    // 첫 번째 쓰기 작업과 함께 클라이언트의 보안 토큰이 서버로 전송됩니다.
                    // 서버가 파일 내용을 반환할 파일 이름을 서버로 보냅니다.
                    ss.WriteString("c:\\textfile.txt"); // 서버가 읽을 파일 경로를 지정합니다.

                    // 서버로부터 파일 내용을 읽어 콘솔에 출력합니다.
                    Console.Write(ss.ReadString());
                }
                else
                {
                    Console.WriteLine("서버를 확인할 수 없습니다.");
                }
                // 파이프 클라이언트 스트림을 닫고 리소스를 해제합니다.
                pipeClient.Close();
                // 클라이언트 프로세스가 결과를 표시할 시간을 주기 위해 4초 동안 대기합니다.
                Thread.Sleep(4000);
            }
        }
        else // 'spawnclient' 인수가 없는 경우, 여러 클라이언트 프로세스를 시작합니다.
        {
            Console.WriteLine("\n*** 가장(impersonation) 예제를 포함한 명명된 파이프 클라이언트 스트림 ***\n");
            // 클라이언트 프로세스를 시작하는 헬퍼 함수를 호출합니다.
            StartClients();
        }
    }

    // 파이프 클라이언트 프로세스를 생성하는 헬퍼 함수
    private static void StartClients()
    {
        // 현재 실행 중인 프로세스의 전체 명령줄을 가져옵니다. (예: "C:\path\to\YourApp.exe")
        string currentProcessName = Environment.CommandLine;

        // Visual Studio에서 실행될 때 추가되는 따옴표나 공백을 제거합니다.
        currentProcessName = currentProcessName.Trim('"', ' ');

        // 확장자를 .exe로 변경하여 실행 파일 경로를 확보합니다.
        currentProcessName = Path.ChangeExtension(currentProcessName, ".exe");
        Process?[] plist = new Process?[numClients]; // 생성될 클라이언트 프로세스들을 저장할 배열

        Console.WriteLine("클라이언트 프로세스들을 생성 중...\n");

        // 현재 디렉토리 경로가 명령줄에 포함되어 있다면 제거합니다.
        // 이는 Process.Start가 올바른 실행 파일을 찾도록 돕기 위함입니다.
        if (currentProcessName.Contains(Environment.CurrentDirectory))
        {
            currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
        }

        // Visual Studio 실행 시 발생할 수 있는 추가적인 문자(백슬래시, 따옴표)를 제거합니다.
        currentProcessName = currentProcessName.Replace("\\", String.Empty);
        currentProcessName = currentProcessName.Replace("\"", String.Empty);

        int i;
        for (i = 0; i &lt; numClients; i++)
        {
            // '이' 프로그램(PipeClient) 자체를 새 프로세스로 시작하되,
            // 'spawnclient' 인수를 전달하여 명명된 파이프 클라이언트 모드로 실행되도록 합니다.
            plist[i] = Process.Start(currentProcessName, "spawnclient");
        }

        // 모든 클라이언트 프로세스가 종료될 때까지 대기하는 루프
        // 'i'는 아직 종료되지 않은 프로세스의 개수를 추적합니다.
        while (i > 0)
        {
            for (int j = 0; j &lt; numClients; j++)
            {
                if (plist[j] != null) // 프로세스가 아직 활성 상태인 경우
                {
                    // 프로세스가 종료되었는지 확인합니다.
                    if (plist[j]!.HasExited)
                    {
                        Console.WriteLine($"클라이언트 프로세스[{plist[j]?.Id}]가 종료되었습니다.");
                        plist[j] = null; // 종료된 프로세스 참조 제거
                        i--;             // 종료되지 않은 프로세스 개수 감소
                    }
                    else
                    {
                        // 아직 종료되지 않았다면 잠시 대기합니다.
                        Thread.Sleep(250);
                    }
                }
            }
        }
        Console.WriteLine("\n클라이언트 프로세스들이 모두 완료되어 종료합니다.");
    }
}

// 스트림에서 문자열을 읽고 쓰기 위한 데이터 프로토콜을 정의하는 클래스
// (이 클래스는 서버 예제와 동일합니다)
public class StreamString
{
    private Stream ioStream;           // 기본 스트림 (NamedPipeClientStream)
    private UnicodeEncoding streamEncoding; // 문자열 인코딩 방식

    public StreamString(Stream ioStream)
    {
        this.ioStream = ioStream;
        streamEncoding = new UnicodeEncoding(); // UTF-16 인코딩 사용
    }

    // 스트림에서 문자열을 읽습니다.
    public string ReadString()
    {
        // 먼저 문자열의 길이를 2바이트로 읽습니다. (Big-endian 방식으로)
        int len;
        len = ioStream.ReadByte() * 256; // 첫 번째 바이트 (상위 8비트)
        len += ioStream.ReadByte();      // 두 번째 바이트 (하위 8비트)

        // 읽을 길이만큼의 바이트 배열을 생성합니다.
        var inBuffer = new byte[len];
        // 스트림에서 해당 길이만큼의 바이트를 읽어 버퍼에 채웁니다.
        ioStream.Read(inBuffer, 0, len);

        // 바이트 배열을 문자열로 디코딩하여 반환합니다.
        return streamEncoding.GetString(inBuffer);
    }

    // 스트림에 문자열을 씁니다.
    public int WriteString(string outString)
    {
        // 문자열을 바이트 배열로 인코딩합니다.
        byte[] outBuffer = streamEncoding.GetBytes(outString);
        int len = outBuffer.Length;

        // 문자열 길이가 UInt16.MaxValue (65535)를 초과하면 잘라냅니다.
        // (프로토콜이 2바이트 길이 필드를 사용하므로 최대 65535 바이트까지 표현 가능)
        if (len > UInt16.MaxValue)
        {
            len = (int)UInt16.MaxValue;
        }

        // 문자열의 길이를 2바이트로 스트림에 씁니다. (Big-endian 방식으로)
        ioStream.WriteByte((byte)(len / 256)); // 상위 바이트
        ioStream.WriteByte((byte)(len &amp; 255)); // 하위 바이트
        // 문자열 바이트 배열을 스트림에 씁니다.
        ioStream.Write(outBuffer, 0, len);
        // 버퍼에 남아있는 데이터를 즉시 스트림으로 밀어 넣습니다.
        ioStream.Flush();

        // 전송된 총 바이트 수 (문자열 바이트 + 길이 2바이트)를 반환합니다.
        return outBuffer.Length + 2;
    }
}</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading">강력한 프로그래밍: Named Pipe 클라이언트/서버 설정</h5>



<p>이 예제에서 클라이언트와 서버 프로세스는 <strong>동일한 컴퓨터에서 실행되도록</strong> 설계되었습니다. </p>



<p>그래서 <strong><code>NamedPipeClientStream</code> </strong>객체에 제공된 서버 이름이 <strong>&#8220;.&#8221;</strong> 입니다.</p>



<p>만약 클라이언트와 서버 프로세스가 <strong>다른 컴퓨터에 있다면</strong>, <strong>&#8220;.&#8221;</strong> 은 서버 프로세스를 실행하는 컴퓨터의 <strong>네트워크 이름</strong>으로 대체되어야 합니다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" />추가 정리</h3>



<p><strong>명명된 파이프에 대한 동작 개념</strong></p>



<p>명명된 파이프는 <strong>파일 시스템의 &#8216;이름&#8217;이라는 개념을 빌려와</strong> 프로세스들이 서로를 찾아 연결할 수 있게 해주지만, 실제 데이터 교환은 <strong>메모리를 통해 이루어지는 IPC(프로세스 간 통신) 메커니즘</strong>입니다.</p>



<p>이러한 특성 덕분에 명명된 파이프는 로컬 컴퓨터 내의 여러 프로세스 간 통신에 매우 효율적이며, Windows에서는 네트워크를 통해 다른 컴퓨터의 파이프에도 연결할 수 있어 더욱 유연하게 사용할 수 있습니다.</p>



<p></p>
<p>The post <a href="https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/">.NET의 파이프 작업 (Pipe Operations in .NET)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/net%ec%9d%98-%ed%8c%8c%ec%9d%b4%ed%94%84-%ec%9e%91%ec%97%85-pipe-operations-in-net/40115/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Microsoft.Extensions.Hosting, 호스팅 모델(Hosting Model)</title>
		<link>https://lycos7560.com/c/microsoft-extensions-hosting-%ed%98%b8%ec%8a%a4%ed%8c%85-%eb%aa%a8%eb%8d%b8hosting-model/40109/</link>
					<comments>https://lycos7560.com/c/microsoft-extensions-hosting-%ed%98%b8%ec%8a%a4%ed%8c%85-%eb%aa%a8%eb%8d%b8hosting-model/40109/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Fri, 11 Jul 2025 14:31:34 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AddHostedService]]></category>
		<category><![CDATA[AddScoped]]></category>
		<category><![CDATA[AddSingleton]]></category>
		<category><![CDATA[AddTransient]]></category>
		<category><![CDATA[appsettings.json]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[Await]]></category>
		<category><![CDATA[Background Service]]></category>
		<category><![CDATA[BackgroundService]]></category>
		<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[CancellationToken]]></category>
		<category><![CDATA[Clean Code]]></category>
		<category><![CDATA[Cloud Native]]></category>
		<category><![CDATA[Code Quality]]></category>
		<category><![CDATA[Command Line Arguments]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[ConfigureAppConfiguration]]></category>
		<category><![CDATA[ConfigureLogging]]></category>
		<category><![CDATA[ConfigureServices]]></category>
		<category><![CDATA[Console Application]]></category>
		<category><![CDATA[Console Logger]]></category>
		<category><![CDATA[CreateDefaultBuilder]]></category>
		<category><![CDATA[Cross Platform]]></category>
		<category><![CDATA[Debug Logger]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[DI]]></category>
		<category><![CDATA[Enterprise Application]]></category>
		<category><![CDATA[Environment Variables]]></category>
		<category><![CDATA[Error Handling]]></category>
		<category><![CDATA[Event Log]]></category>
		<category><![CDATA[Exception Handling]]></category>
		<category><![CDATA[Extensions.Hosting]]></category>
		<category><![CDATA[Framework]]></category>
		<category><![CDATA[Generic Host]]></category>
		<category><![CDATA[Graceful Shutdown]]></category>
		<category><![CDATA[Health Checks]]></category>
		<category><![CDATA[Host]]></category>
		<category><![CDATA[Hosting Model]]></category>
		<category><![CDATA[IHost]]></category>
		<category><![CDATA[IHostBuilder]]></category>
		<category><![CDATA[IHostedService]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Integration Testing]]></category>
		<category><![CDATA[Interlocked]]></category>
		<category><![CDATA[IOptions]]></category>
		<category><![CDATA[Lifecycle Management]]></category>
		<category><![CDATA[Log Provider]]></category>
		<category><![CDATA[Logging]]></category>
		<category><![CDATA[LogLevel]]></category>
		<category><![CDATA[Maintainability]]></category>
		<category><![CDATA[Memory Management]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Mocking]]></category>
		<category><![CDATA[Options Pattern]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[resource management]]></category>
		<category><![CDATA[RunAsync]]></category>
		<category><![CDATA[Scalability]]></category>
		<category><![CDATA[Service Lifetime]]></category>
		<category><![CDATA[Service Registration]]></category>
		<category><![CDATA[ServiceProvider]]></category>
		<category><![CDATA[Software Architecture]]></category>
		<category><![CDATA[SOLID Principles]]></category>
		<category><![CDATA[StartAsync]]></category>
		<category><![CDATA[StopAsync]]></category>
		<category><![CDATA[Structured Logging]]></category>
		<category><![CDATA[Task]]></category>
		<category><![CDATA[Testability]]></category>
		<category><![CDATA[Thread Safety]]></category>
		<category><![CDATA[Timer]]></category>
		<category><![CDATA[Unit Testing]]></category>
		<category><![CDATA[Windows Service]]></category>
		<category><![CDATA[Worker Service]]></category>
		<category><![CDATA[WPF Application]]></category>
		<category><![CDATA[구성 관리]]></category>
		<category><![CDATA[구조화된 로깅]]></category>
		<category><![CDATA[단위 테스트]]></category>
		<category><![CDATA[디자인 패턴]]></category>
		<category><![CDATA[로그 프로바이더]]></category>
		<category><![CDATA[로깅]]></category>
		<category><![CDATA[리소스 관리]]></category>
		<category><![CDATA[마이크로서비스]]></category>
		<category><![CDATA[메모리 관리]]></category>
		<category><![CDATA[명령줄 인수]]></category>
		<category><![CDATA[모범 사례]]></category>
		<category><![CDATA[모킹]]></category>
		<category><![CDATA[백그라운드 서비스]]></category>
		<category><![CDATA[생명 주기 관리]]></category>
		<category><![CDATA[서비스 등록]]></category>
		<category><![CDATA[서비스 생명주기]]></category>
		<category><![CDATA[성능]]></category>
		<category><![CDATA[소프트웨어 아키텍처]]></category>
		<category><![CDATA[스레드 안전성]]></category>
		<category><![CDATA[엔터프라이즈 애플리케이션]]></category>
		<category><![CDATA[예외 처리]]></category>
		<category><![CDATA[오류 처리]]></category>
		<category><![CDATA[옵션 패턴]]></category>
		<category><![CDATA[워커 서비스]]></category>
		<category><![CDATA[윈도우 서비스]]></category>
		<category><![CDATA[유지보수성]]></category>
		<category><![CDATA[의존성 주입]]></category>
		<category><![CDATA[인프라]]></category>
		<category><![CDATA[정상적인 종료]]></category>
		<category><![CDATA[제네릭 호스트]]></category>
		<category><![CDATA[코드 품질]]></category>
		<category><![CDATA[콘솔 애플리케이션]]></category>
		<category><![CDATA[크로스 플랫폼]]></category>
		<category><![CDATA[클린 코드]]></category>
		<category><![CDATA[테스트 용이성]]></category>
		<category><![CDATA[통합 테스트]]></category>
		<category><![CDATA[프레임워크]]></category>
		<category><![CDATA[헬스 체크]]></category>
		<category><![CDATA[호스팅 모델]]></category>
		<category><![CDATA[확장성]]></category>
		<category><![CDATA[환경 변수]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40109</guid>

					<description><![CDATA[<p>개요 Microsoft.Extensions.Hosting 네임스페이스는 .NET 애플리케이션의 호스팅 모델(Hosting Model)을 제공하는 핵심 구성 요소입니다. Host 클래스는 이 모델의 시작점 역할을 하며, 애플리케이션의 생명 주기(lifecycle), 구성(configuration), 의존성 주입(dependency injection, DI), 로깅(logging) 등 다양한 인프라 서비스를 중앙 집중식으로 관리합니다. 이 호스팅 모델은 ASP.NET Core 애플리케이션에서 시작되었지만, 현재는 콘솔 애플리케이션, 백그라운드 서비스, Windows 서비스, WPF 애플리케이션 등 다양한 종류의 .NET [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/microsoft-extensions-hosting-%ed%98%b8%ec%8a%a4%ed%8c%85-%eb%aa%a8%eb%8d%b8hosting-model/40109/">Microsoft.Extensions.Hosting, 호스팅 모델(Hosting Model)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-c19625a0      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#개요" class="uagb-toc-link__trigger">개요</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-host-클래스의-목적과-역할" class="uagb-toc-link__trigger">1. Host 클래스의 목적과 역할</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-host-클래스-핵심-인터페이스-및-메서드" class="uagb-toc-link__trigger">2. Host 클래스 핵심 인터페이스 및 메서드</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-ihostbuilder-인터페이스" class="uagb-toc-link__trigger">(1) IHostBuilder 인터페이스</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-ihost-인터페이스" class="uagb-toc-link__trigger">(2) IHost 인터페이스</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-ihostedservice-인터페이스" class="uagb-toc-link__trigger">(3) IHostedService 인터페이스</a></li></ul><li class="uagb-toc__list"><a href="#3-hostcreatedefaultbuilder-메서드의-이점" class="uagb-toc-link__trigger">3. Host.CreateDefaultBuilder() 메서드의 이점</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#예시-코드-콘솔-애플리케이션에서-host-사용하기" class="uagb-toc-link__trigger">예시 코드: 콘솔 애플리케이션에서 Host 사용하기</a></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">개요</h2>



<p>Microsoft.Extensions.Hosting 네임스페이스는 .NET 애플리케이션의 호스팅 모델(Hosting Model)을 제공하는 핵심 구성 요소입니다. </p>



<p>Host 클래스는 이 모델의 시작점 역할을 하며, 애플리케이션의 생명 주기(lifecycle), 구성(configuration), 의존성 주입(dependency injection, DI), 로깅(logging) 등 다양한 인프라 서비스를 중앙 집중식으로 관리합니다.</p>



<p>이 호스팅 모델은 ASP.NET Core 애플리케이션에서 시작되었지만, 현재는 콘솔 애플리케이션, 백그라운드 서비스, Windows 서비스, WPF 애플리케이션 등 다양한 종류의 .NET 애플리케이션에서 표준적인 방식으로 사용되고 있습니다.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1. <code>Host</code> 클래스의 목적과 역할</h3>



<p><code>Host</code> 클래스는 특정 애플리케이션 유형에 구애받지 않고, 공통적인 인프라 기능을 쉽게 통합하고 관리할 수 있도록 설계되었습니다.</p>



<ul class="wp-block-list">
<li><strong>생명 주기 관리</strong>: 애플리케이션의 시작(<code>StartAsync</code>) 및 종료(<code>StopAsync</code>)를 관리하고, 이 과정에서 필요한 서비스들이 올바른 순서로 초기화되고 정리될 수 있도록 합니다.</li>



<li><strong>의존성 주입 (DI)</strong>: 애플리케이션 전반에 걸쳐 강력한 DI 컨테이너를 제공하여, 서비스 간의 느슨한 결합을 촉진하고 테스트 용이성을 향상시킵니다.</li>



<li><strong>구성 (Configuration)</strong>: <code>appsettings.json</code>, 환경 변수, 명령줄 인수 등 다양한 소스에서 설정을 로드하고 관리하는 일관된 방법을 제공합니다.</li>



<li><strong>로깅 (Logging)</strong>: 다양한 로깅 프로바이더(콘솔, 파일, 데이터베이스 등)를 쉽게 통합하고, 로깅 레벨을 유연하게 제어할 수 있도록 합니다.</li>



<li><strong>백그라운드 서비스 (IHostedService)</strong>: 장시간 실행되는 백그라운드 작업을 호스트의 생명 주기에 통합하여 관리할 수 있도록 합니다. 이는 API를 제공하지 않고 단순히 작업을 수행하는 서비스에 특히 유용합니다.</li>
</ul>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2. <code>Host</code> 클래스 핵심 인터페이스 및 메서드</h3>



<p><code>Host</code> 클래스 자체보다는, 이와 관련된 <code>IHost</code> 및 <code>IHostBuilder</code> 인터페이스를 이해하는 것이 중요합니다.</p>



<h4 class="wp-block-heading">(1) <code>IHostBuilder</code> 인터페이스</h4>



<p><code>IHostBuilder</code>는 호스트를 <strong>구성(Configure)</strong>하는 데 사용되는 인터페이스입니다. </p>



<p><code>Host.CreateDefaultBuilder()</code> 메서드가 반환하는 객체가 바로 <strong><code>IHostBuilder</code> </strong>타입입니다. </p>



<p>이 빌더를 통해 다음을 정의할 수 있습니다:</p>



<ul class="wp-block-list">
<li><strong><code>ConfigureAppConfiguration</code></strong>: <br>설정 소스(파일, 환경 변수 등)를 추가하거나 커스터마이징합니다.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.ConfigureAppConfiguration((context, config) =>
{
    config.AddJsonFile("custom.json", optional: true)
          .AddEnvironmentVariables()
          .AddCommandLine(args);
})</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong><code>ConfigureLogging</code></strong>: <br>로깅 프로바이더(콘솔, 디버그, 파일 등)를 추가하고 로깅 필터링 규칙을 정의합니다.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.ConfigureLogging((context, logging) =>
{
    logging.AddConsole()
           .AddDebug()
           .SetMinimumLevel(LogLevel.Information);
})</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong><code>ConfigureServices</code></strong>: <br>의존성 주입 컨테이너에 애플리케이션의 서비스들을 등록합니다. <br><code>AddSingleton</code>, <code>AddScoped</code>, <code>AddTransient</code> 등의 메서드를 사용하여 서비스의 생명 주기를 지정할 수 있습니다.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.ConfigureServices((context, services) =>
{
    services.AddSingleton&lt;ISingletonService, SingletonService>();
    services.AddScoped&lt;IScopedService, ScopedService>();
    services.AddTransient&lt;ITransientService, TransientService>();
    services.AddHostedService&lt;MyBackgroundService>();
})</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><strong><code>UseConsoleLifetime</code></strong>: <br>콘솔 애플리케이션의 경우, <code>Ctrl+C</code>와 같은 콘솔 시그널을 감지하여 호스트를 정상적으로 종료하도록 설정합니다. <br>(이는 <code>Host.CreateDefaultBuilder()</code>에 의해 기본적으로 포함됩니다.)</li>



<li><strong><code>Build()</code></strong>: <br><code>IHostBuilder</code>에 정의된 모든 구성을 기반으로 최종 <code>IHost</code> 인스턴스를 생성합니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(2) <code>IHost</code> 인터페이스</h4>



<p><code>IHost</code>는 <strong>빌드된 호스트 인스턴스</strong>를 나타내며, 실제로 애플리케이션을 실행하고 관리하는 데 사용됩니다. </p>



<p>주요 메서드는 다음과 같습니다</p>



<ul class="wp-block-list">
<li><strong><code>Services</code></strong>: <br>등록된 모든 서비스 인스턴스에 접근할 수 있는 <code>IServiceProvider</code>를 제공합니다. <br>이를 통해 런타임에 필요한 서비스를 resolve(가져오기)할 수 있습니다.</li>



<li><strong><code>StartAsync(CancellationToken cancellationToken)</code></strong>: <br>호스트를 비동기적으로 시작합니다. 이 메서드가 호출되면 등록된 모든 <code>IHostedService</code> 인스턴스의 <code>StartAsync</code> 메서드가 호출됩니다.</li>



<li><strong><code>StopAsync(CancellationToken cancellationToken)</code></strong>: <br>호스트를 비동기적으로 종료합니다. 등록된 모든 <code>IHostedService</code> 인스턴스의 <code>StopAsync</code> 메서드가 호출되어 리소스를 정리할 기회를 줍니다.</li>



<li><strong><code>RunAsync(CancellationToken cancellationToken = default)</code></strong>: <br><code>StartAsync</code>를 호출하고, 호스트가 종료될 때까지 대기한 후 <code>StopAsync</code>를 호출합니다. <br>대부분의 콘솔 애플리케이션에서 메인 실행 루프로 사용됩니다.</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">(3) <code>IHostedService</code> 인터페이스</h4>



<p><code>IHostedService</code>는 호스트의 생명 주기에 통합되어 백그라운드 작업을 수행하는 서비스들을 정의하는 인터페이스입니다. </p>



<p><code>StartAsync</code>와 <code>StopAsync</code> 두 가지 메서드를 구현해야 합니다.</p>



<ul class="wp-block-list">
<li><strong><code>StartAsync(CancellationToken cancellationToken)</code></strong>: <br>호스트가 시작될 때 호출됩니다. 장시간 실행되는 작업을 시작하는 데 사용됩니다.</li>



<li><strong><code>StopAsync(CancellationToken cancellationToken)</code></strong>: <br>호스트가 종료될 때 호출됩니다. <code>StartAsync</code>에서 시작된 작업을 정상적으로 중지하고 리소스를 정리하는 데 사용됩니다.</li>
</ul>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3. <code>Host.CreateDefaultBuilder()</code> 메서드의 이점</h3>



<p>이 메서드는 많은 &#8220;관습(conventions)&#8221;을 제공하여 애플리케이션 초기 설정을 간소화하는 장점이 있습니다.</p>



<ul class="wp-block-list">
<li><strong>일관성</strong>: <br>모든 .NET 애플리케이션 유형(웹, 콘솔, 백그라운드 서비스)에서 동일한 방식으로 인프라를 설정할 수 있게 하여 코드의 일관성을 유지합니다.</li>



<li><strong>생산성</strong>: <br>설정, 로깅, DI 등 반복적인 초기화 코드를 작성할 필요 없이 바로 비즈니스 로직에 집중할 수 있도록 합니다.</li>



<li><strong>확장성</strong>: <br>기본 설정을 사용하면서도 <code>ConfigureAppConfiguration</code>, <code>ConfigureServices</code> 등의 메서드를 통해 언제든지 필요에 따라 커스터마이징하고 확장할 수 있습니다.</li>



<li><strong>테스트 용이성</strong>: <br>DI 컨테이너를 중심으로 설계되어, 단위 테스트 및 통합 테스트에서 의존성을 쉽게 Mocking하거나 대체할 수 있습니다.<br>참고: Mocking &#8211; 테스트 코드 작성 시 가짜 객체(Mock)를 만들어 실제 객체 대신 사용하는 기법</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h4 class="wp-block-heading">예시 코드: 콘솔 애플리케이션에서 <code>Host</code> 사용하기</h4>



<p>콘솔 애플리케이션에서 <code>Host</code> 클래스를 사용하여 백그라운드 서비스(카운터)를 실행하고, 설정 및 로깅을 사용하는 예제</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>Microsoft.Extensions.Hosting</code> NuGet 패키지를 설치</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">dotnet add package Microsoft.Extensions.Hosting</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>appsettings.json</code> 파일을 추가하고 내용을 작성합니다.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AppConfig": {
    "IntervalSeconds": 2
  }
}</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>Services/MyBackgroundService.cs</code> (커스텀 <code>IHostedService</code> 구현):</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration; // IConfiguration 주입을 위해 추가
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyConsoleApp.Services
{
    // IHostedService를 구현하여 호스트의 생명 주기에 통합될 백그라운드 서비스를 정의합니다.
    public class MyBackgroundService : IHostedService, IDisposable
    {
        private readonly ILogger&lt;MyBackgroundService> _logger;
        private readonly IConfiguration _configuration;
        private Timer? _timer;
        private int _executionCount = 0;
        private int _intervalSeconds;

        public MyBackgroundService(ILogger&lt;MyBackgroundService> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;

            // appsettings.json에서 설정 값 읽기
            _intervalSeconds = _configuration.GetValue&lt;int>("AppConfig:IntervalSeconds", 5); // 기본값 5초
        }

        // 호스트가 시작될 때 호출됩니다.
        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("MyBackgroundService가 시작되었습니다.");

            // 타이머를 설정하여 지정된 간격마다 DoWork 메서드를 호출합니다.
            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(_intervalSeconds));

            return Task.CompletedTask; // 비동기 작업 시작을 알리고 즉시 반환
        }

        // 타이머에 의해 주기적으로 호출되는 실제 작업 메서드
        private void DoWork(object? state)
        {
            var count = Interlocked.Increment(ref _executionCount);
            _logger.LogInformation(
                "MyBackgroundService가 {count}번째 작업을 수행 중입니다. 현재 시간: {time}",
                count, DateTimeOffset.Now);
        }

        // 호스트가 종료될 때 호출됩니다.
        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("MyBackgroundService가 중지되고 있습니다.");

            // 타이머를 중지하고 리소스를 해제합니다.
            // Change(Timeout.Infinite, 0)은 타이머를 비활성화하는 표준 방법입니다.
            _timer?.Change(Timeout.Infinite, 0);

            _logger.LogInformation("MyBackgroundService가 중지되었습니다.");
            return Task.CompletedTask; // 작업 완료를 알립니다.
        }

        // 리소스 해제를 위한 Dispose 메서드 (IHostedService는 IDisposable을 구현하는 경우가 많음)
        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li><code>Program.cs</code> 구현:</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MyConsoleApp.Services; // MyBackgroundService를 사용하기 위해 추가
using System;
using System.Threading.Tasks;

namespace MyConsoleApp
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            Console.WriteLine("콘솔 애플리케이션 호스트가 시작됩니다. 종료하려면 Ctrl+C를 누르세요.");

            // 1. IHostBuilder 인스턴스 생성: Host.CreateDefaultBuilder()는 기본적인 구성(config, logging, DI)을 설정합니다.
            var builder = Host.CreateDefaultBuilder(args)
                // 2. 서비스 컨테이너 구성: 의존성 주입을 위한 서비스들을 등록합니다.
                .ConfigureServices((hostContext, services) =>
                {
                    // MyBackgroundService를 IHostedService로 등록합니다.
                    // 호스트가 시작될 때 StartAsync가 호출되고, 종료될 때 StopAsync가 호출됩니다.
                    services.AddHostedService&lt;MyBackgroundService>();

                    // 여기에 다른 서비스들도 등록할 수 있습니다. 예:
                    // services.AddSingleton&lt;IMyService, MyService>();
                    // services.AddScoped&lt;IOtherService, OtherService>();
                })
                // 3. 로깅 구성 (선택 사항: CreateDefaultBuilder가 기본 설정 제공하지만, 커스터마이징 가능)
                .ConfigureLogging(logging =>
                {
                    logging.ClearProviders(); // 기본 로깅 프로바이더를 지우고 새로 추가하거나
                    logging.AddConsole();    // 콘솔 로거를 추가합니다.
                    // logging.AddDebug();    // 디버그 로거 추가
                });
                // 4. 기타 구성 (선택 사항: 파일 경로, 환경 등)
                // .UseContentRoot(Directory.GetCurrentDirectory())
                // .UseEnvironment("Development")
                // .ConfigureAppConfiguration((hostingContext, config) => {
                //    config.AddJsonFile("custom.json", optional: true);
                // });

            // 5. IHost 인스턴스 빌드: 구성된 빌더를 바탕으로 실제 호스트를 생성합니다.
            var host = builder.Build();

            // 6. 호스트 실행: 등록된 모든 IHostedService를 시작하고 애플리케이션 종료를 대기합니다.
            // Ctrl+C 시그널을 감지하여 정상적으로 종료됩니다.
            await host.RunAsync();

            Console.WriteLine("콘솔 애플리케이션 호스트가 종료되었습니다.");
        }
    }
}</pre>



<p></p>



<p></p>



<p></p>



<p></p>
<p>The post <a href="https://lycos7560.com/c/microsoft-extensions-hosting-%ed%98%b8%ec%8a%a4%ed%8c%85-%eb%aa%a8%eb%8d%b8hosting-model/40109/">Microsoft.Extensions.Hosting, 호스팅 모델(Hosting Model)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/microsoft-extensions-hosting-%ed%98%b8%ec%8a%a4%ed%8c%85-%eb%aa%a8%eb%8d%b8hosting-model/40109/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Swagger/OpenAPI for .NET</title>
		<link>https://lycos7560.com/c/asp-net/swagger-openapi-for-net/40167/</link>
					<comments>https://lycos7560.com/c/asp-net/swagger-openapi-for-net/40167/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Wed, 18 Jun 2025 07:06:11 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.NET6]]></category>
		<category><![CDATA[.NET7]]></category>
		<category><![CDATA[.NET8]]></category>
		<category><![CDATA[Accept헤더]]></category>
		<category><![CDATA[API가이드라인]]></category>
		<category><![CDATA[API계약]]></category>
		<category><![CDATA[API명세]]></category>
		<category><![CDATA[API문서화]]></category>
		<category><![CDATA[API버전관리]]></category>
		<category><![CDATA[API설계]]></category>
		<category><![CDATA[API테스트]]></category>
		<category><![CDATA[Application Insights]]></category>
		<category><![CDATA[appsettings]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[Bearer토큰]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[Content-Type]]></category>
		<category><![CDATA[CORS]]></category>
		<category><![CDATA[DTO패턴]]></category>
		<category><![CDATA[FluentValidation]]></category>
		<category><![CDATA[FromBody]]></category>
		<category><![CDATA[FromQuery]]></category>
		<category><![CDATA[HTTP메서드]]></category>
		<category><![CDATA[HTTP상태코드]]></category>
		<category><![CDATA[Identity]]></category>
		<category><![CDATA[IdentityServer]]></category>
		<category><![CDATA[Interactive Documentation]]></category>
		<category><![CDATA[JSON Schema]]></category>
		<category><![CDATA[JSON직렬화]]></category>
		<category><![CDATA[JWT인증]]></category>
		<category><![CDATA[NuGet패키지]]></category>
		<category><![CDATA[OAuth2]]></category>
		<category><![CDATA[OpenAPI]]></category>
		<category><![CDATA[OpenAPI Specification]]></category>
		<category><![CDATA[ProducesResponseType]]></category>
		<category><![CDATA[Program.cs]]></category>
		<category><![CDATA[REST API]]></category>
		<category><![CDATA[RESTful설계]]></category>
		<category><![CDATA[Swagger]]></category>
		<category><![CDATA[SwaggerGen]]></category>
		<category><![CDATA[SwaggerUI]]></category>
		<category><![CDATA[Swashbuckle]]></category>
		<category><![CDATA[XML주석]]></category>
		<category><![CDATA[개발도구]]></category>
		<category><![CDATA[개발생산성]]></category>
		<category><![CDATA[개발환경]]></category>
		<category><![CDATA[검색]]></category>
		<category><![CDATA[결과필터]]></category>
		<category><![CDATA[구성관리]]></category>
		<category><![CDATA[권한부여]]></category>
		<category><![CDATA[글로벌필터]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[네이밍컨벤션]]></category>
		<category><![CDATA[단위테스트]]></category>
		<category><![CDATA[데이터어노테이션]]></category>
		<category><![CDATA[디버깅]]></category>
		<category><![CDATA[라우팅]]></category>
		<category><![CDATA[로깅]]></category>
		<category><![CDATA[리플렉션]]></category>
		<category><![CDATA[린팅]]></category>
		<category><![CDATA[메타데이터추출]]></category>
		<category><![CDATA[모니터링]]></category>
		<category><![CDATA[모델바인딩]]></category>
		<category><![CDATA[문서화자동화]]></category>
		<category><![CDATA[미들웨어]]></category>
		<category><![CDATA[미들웨어파이프라인]]></category>
		<category><![CDATA[백엔드개발]]></category>
		<category><![CDATA[보안설정]]></category>
		<category><![CDATA[사용량추적]]></category>
		<category><![CDATA[상태코드]]></category>
		<category><![CDATA[서비스등록]]></category>
		<category><![CDATA[성능분석]]></category>
		<category><![CDATA[성능최적화]]></category>
		<category><![CDATA[순환참조]]></category>
		<category><![CDATA[스키마생성]]></category>
		<category><![CDATA[스키마필터]]></category>
		<category><![CDATA[스테이징환경]]></category>
		<category><![CDATA[액션메서드]]></category>
		<category><![CDATA[액션필터]]></category>
		<category><![CDATA[에러핸들링]]></category>
		<category><![CDATA[역할기반접근제어]]></category>
		<category><![CDATA[예외처리]]></category>
		<category><![CDATA[오류처리]]></category>
		<category><![CDATA[요청응답]]></category>
		<category><![CDATA[웹API]]></category>
		<category><![CDATA[유효성검사]]></category>
		<category><![CDATA[응답타입]]></category>
		<category><![CDATA[의존성주입]]></category>
		<category><![CDATA[인증통합]]></category>
		<category><![CDATA[인증필터]]></category>
		<category><![CDATA[자동문서화]]></category>
		<category><![CDATA[자동화배포]]></category>
		<category><![CDATA[정렬]]></category>
		<category><![CDATA[정적분석]]></category>
		<category><![CDATA[제네릭타입]]></category>
		<category><![CDATA[지속적통합]]></category>
		<category><![CDATA[컨트롤러]]></category>
		<category><![CDATA[코드리뷰]]></category>
		<category><![CDATA[코드퍼스트]]></category>
		<category><![CDATA[코드품질]]></category>
		<category><![CDATA[클레임기반인증]]></category>
		<category><![CDATA[타입안전성]]></category>
		<category><![CDATA[텔레메트리]]></category>
		<category><![CDATA[통합테스트]]></category>
		<category><![CDATA[트러블슈팅]]></category>
		<category><![CDATA[팀협업]]></category>
		<category><![CDATA[파라미터바인딩]]></category>
		<category><![CDATA[페이징]]></category>
		<category><![CDATA[프로덕션배포]]></category>
		<category><![CDATA[프론트엔드백엔드]]></category>
		<category><![CDATA[필터링]]></category>
		<category><![CDATA[환경변수]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40167</guid>

					<description><![CDATA[<p>📖 Swagger(스웨거)란 무엇인가? Swagger는 현재 OpenAPI Specification (OAS)라는 이름으로 표준화된 RESTful API 명세 및 문서화 도구입니다. 2015년 SmartBear Software에서 OpenAPI Initiative에 기증한 후, 현재는 Linux Foundation 산하에서 관리되고 있습니다. 핵심 개념 🎯 Swagger가 필요한 이유 1. 개발 생산성 향상 2. 팀 협업 효율성 3. 운영 및 테스트 📦 Swashbuckle.AspNetCore 아키텍처 Swashbuckle.AspNetCore는 .NET에서 Swagger/OpenAPI를 구현하는 가장 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/asp-net/swagger-openapi-for-net/40167/">Swagger/OpenAPI for .NET</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-aee946f9      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#swagger스웨거란-무엇인가" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d6.png" alt="📖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swagger(스웨거)란 무엇인가?</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#핵심-개념" class="uagb-toc-link__trigger">핵심 개념</a></li></ul></li><li class="uagb-toc__list"><a href="#swagger가-필요한-이유" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3af.png" alt="🎯" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swagger가 필요한 이유</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-개발-생산성-향상" class="uagb-toc-link__trigger">1. 개발 생산성 향상</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-팀-협업-효율성" class="uagb-toc-link__trigger">2. 팀 협업 효율성</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-운영-및-테스트" class="uagb-toc-link__trigger">3. 운영 및 테스트</a></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#swashbuckleaspnetcore-아키텍처" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4e6.png" alt="📦" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swashbuckle.AspNetCore 아키텍처</a><li class="uagb-toc__list"><a href="#net-6-프로젝트에서-swagger-구성" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6e0.png" alt="🛠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET 6+ 프로젝트에서 Swagger 구성</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-패키지-설치" class="uagb-toc-link__trigger">1. 패키지 설치</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-기본-구성-programcs" class="uagb-toc-link__trigger">2. 기본 구성 (Program.cs)</a></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#swaggergen의-동작-원리" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50d.png" alt="🔍" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SwaggerGen의 동작 원리</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#생성되는-openapi-문서-예시" class="uagb-toc-link__trigger">생성되는 OpenAPI 문서 예시</a></li></ul></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#swagger-ui-고급-활용" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c4.png" alt="📄" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swagger UI 고급 활용</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#주요-기능" class="uagb-toc-link__trigger">주요 기능</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#커스터마이징-옵션" class="uagb-toc-link__trigger">커스터마이징 옵션</a></li></ul></li></ul></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#실무-필수-확장-기능" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 실무 필수 확장 기능</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-xml-주석-통합" class="uagb-toc-link__trigger">1. XML 주석 통합</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-jwt-bearer-인증-통합" class="uagb-toc-link__trigger">2. JWT Bearer 인증 통합</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-api-버전-관리" class="uagb-toc-link__trigger">3. API 버전 관리</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#4-사용자-정의-스키마-필터" class="uagb-toc-link__trigger">4. 사용자 정의 스키마 필터</a></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#성능-최적화-및-모범-사례" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ca.png" alt="📊" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 성능 최적화 및 모범 사례</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-프로덕션-환경-고려사항" class="uagb-toc-link__trigger">1. 프로덕션 환경 고려사항</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#2-대용량-api-문서-최적화" class="uagb-toc-link__trigger">2. 대용량 API 문서 최적화</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#3-보안-강화" class="uagb-toc-link__trigger">3. 보안 강화</a></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#트러블슈팅-가이드" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f527.png" alt="🔧" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 트러블슈팅 가이드</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#일반적인-문제와-해결책" class="uagb-toc-link__trigger">일반적인 문제와 해결책</a></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#모니터링-및-분석" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c8.png" alt="📈" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 모니터링 및 분석</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#application-insights-통합" class="uagb-toc-link__trigger">Application Insights 통합</a></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul></li><li class="uagb-toc__list"><a href="#요약-및-베스트-프랙티스" class="uagb-toc-link__trigger"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 요약 및 베스트 프랙티스</a><ul class="uagb-toc__list"><li class="uagb-toc__list"><a href="#핵심-도구-정리" class="uagb-toc-link__trigger">핵심 도구 정리</a><li class="uagb-toc__list"><li class="uagb-toc__list"><a href="#개발팀을-위한-권장사항" class="uagb-toc-link__trigger">개발팀을 위한 권장사항</a></ul></ul></ul></ul></ul></ul></ul></ul></ul></ul></ol>					</div>
									</div>
				</div>
			


<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d6.png" alt="📖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swagger(스웨거)란 무엇인가?</h2>



<p><strong>Swagger</strong>는 현재 OpenAPI Specification (OAS)라는 이름으로 표준화된 <strong>RESTful API 명세 및 문서화 도구</strong>입니다. </p>



<p>2015년 SmartBear Software에서 OpenAPI Initiative에 기증한 후, 현재는 Linux Foundation 산하에서 관리되고 있습니다.</p>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">핵심 개념</h3>



<ul class="wp-block-list">
<li><strong>API 명세의 표준화</strong>: REST API의 엔드포인트, 파라미터, 응답 구조를 JSON/YAML 형식으로 정의</li>



<li><strong>Code-First 접근</strong>: 소스코드로부터 API 문서를 자동 생성하여 문서와 구현 간 불일치 방지</li>



<li><strong>Interactive Documentation</strong>: 브라우저에서 직접 API를 테스트할 수 있는 UI 제공</li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3af.png" alt="🎯" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swagger가 필요한 이유</h2>



<h3 class="wp-block-heading">1. 개발 생산성 향상</h3>



<ul class="wp-block-list">
<li><strong>자동 문서화</strong>: 수동으로 API 문서를 작성하고 유지보수하는 비용 절약</li>



<li><strong>실시간 동기화</strong>: 코드 변경 시 문서가 자동으로 업데이트</li>



<li><strong>타입 안전성</strong>: .NET의 강타입 시스템을 활용한 정확한 스키마 생성</li>
</ul>



<h3 class="wp-block-heading">2. 팀 협업 효율성</h3>



<ul class="wp-block-list">
<li><strong>프론트엔드-백엔드 협업</strong>: 명확한 API 계약 정의를 통한 병렬 개발 지원</li>



<li><strong>API 계약 테스트</strong>: 구현 전 API 설계 검증 가능</li>



<li><strong>버전 관리</strong>: API 변경사항 추적 및 하위 호환성 관리</li>
</ul>



<h3 class="wp-block-heading">3. 운영 및 테스트</h3>



<ul class="wp-block-list">
<li><strong>통합 테스트</strong>: CI/CD 파이프라인에서 자동화된 API 테스트</li>



<li><strong>모니터링</strong>: API 사용 패턴 분석 및 성능 모니터링 기반 제공</li>
</ul>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4e6.png" alt="📦" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swashbuckle.AspNetCore 아키텍처</h2>



<p>Swashbuckle.AspNetCore는 .NET에서 Swagger/OpenAPI를 구현하는 가장 널리 사용되는 라이브러리입니다.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>패키지</th><th>역할</th><th>주요 기능</th></tr></thead><tbody><tr><td><code>Swashbuckle.AspNetCore.Swagger</code></td><td>OpenAPI 문서 생성 엔진</td><td>JSON/YAML 형식의 OpenAPI 명세 생성</td></tr><tr><td><code>Swashbuckle.AspNetCore.SwaggerGen</code></td><td>코드 분석 및 메타데이터 추출</td><td>리플렉션을 통한 컨트롤러/액션 분석</td></tr><tr><td><code>Swashbuckle.AspNetCore.SwaggerUI</code></td><td>웹 UI 렌더링</td><td>인터랙티브한 API 문서 및 테스트 인터페이스</td></tr></tbody></table></figure>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6e0.png" alt="🛠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .NET 6+ 프로젝트에서 Swagger 구성</h2>



<h3 class="wp-block-heading">1. 패키지 설치</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Package Manager Console
Install-Package Swashbuckle.AspNetCore

# .NET CLI
dotnet add package Swashbuckle.AspNetCore
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2. 기본 구성 (Program.cs)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">var builder = WebApplication.CreateBuilder(args);

// 컨트롤러 서비스 등록
builder.Services.AddControllers();

// Swagger 서비스 등록
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Version = "v1",
        Title = "My API",
        Description = "ASP.NET Core Web API for demonstration",
        Contact = new OpenApiContact
        {
            Name = "Developer Name",
            Email = "developer@example.com"
        }
    });
});

var app = builder.Build();

// 개발 환경에서만 Swagger 활성화
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        options.RoutePrefix = string.Empty; // 루트 경로에서 Swagger UI 제공
    });
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f50d.png" alt="🔍" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SwaggerGen의 동작 원리</h2>



<p>SwaggerGen은 다음과 같은 과정을 통해 OpenAPI 문서를 생성합니다:</p>



<ol class="wp-block-list">
<li><strong>어셈블리 스캔</strong>: 등록된 컨트롤러와 액션 메서드 탐색</li>



<li><strong>메타데이터 추출</strong>: 라우트, HTTP 메서드, 파라미터, 반환 타입 분석</li>



<li><strong>스키마 생성</strong>: .NET 타입을 JSON Schema로 변환</li>



<li><strong>문서 조합</strong>: OpenAPI 3.0 사양에 맞는 JSON 문서 생성</li>
</ol>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">생성되는 OpenAPI 문서 예시</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{
  "openapi": "3.0.1",
  "info": {
    "title": "My API",
    "version": "v1"
  },
  "paths": {
    "/api/products": {
      "get": {
        "tags": ["Products"],
        "summary": "제품 목록 조회",
        "responses": {
          "200": {
            "description": "성공",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Product"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Product": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int32" },
          "name": { "type": "string", "nullable": true }
        }
      }
    }
  }
}
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1378" height="1299" src="https://lycos7560.com/wp-content/uploads/2025/07/image-19.png" alt="" class="wp-image-40168" srcset="https://lycos7560.com/wp-content/uploads/2025/07/image-19.png 1378w, https://lycos7560.com/wp-content/uploads/2025/07/image-19-300x283.png 300w, https://lycos7560.com/wp-content/uploads/2025/07/image-19-768x724.png 768w" sizes="(max-width: 1378px) 100vw, 1378px" /></figure>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c4.png" alt="📄" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Swagger UI 고급 활용</h2>



<p>Swagger UI는 생성된 OpenAPI 문서를 기반으로 다음 기능을 제공합니다:</p>



<h3 class="wp-block-heading">주요 기능</h3>



<ul class="wp-block-list">
<li><strong>HTTP 메서드별 분류</strong>: GET, POST, PUT, DELETE 등 시각적 구분</li>



<li><strong>스키마 검증</strong>: 요청/응답 데이터 구조 실시간 검증</li>



<li><strong>Try it Out</strong>: 브라우저에서 직접 API 호출 및 결과 확인</li>



<li><strong>모델 정의</strong>: 복잡한 객체 구조의 시각적 표현</li>



<li><strong>인증 통합</strong>: 다양한 인증 방식 지원 (Bearer, API Key 등)</li>
</ul>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">커스터마이징 옵션</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    options.DocumentTitle = "My API Documentation";
    options.DefaultModelsExpandDepth(2);
    options.DefaultModelRendering(ModelRendering.Model);
    options.DisplayRequestDuration();
    options.EnableDeepLinking();
    options.EnableFilter();
    options.ShowExtensions();
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 실무 필수 확장 기능</h2>



<h3 class="wp-block-heading">1. XML 주석 통합</h3>



<p>프로젝트 파일 설정:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;PropertyGroup>
  &lt;GenerateDocumentationFile>true&lt;/GenerateDocumentationFile>
  &lt;NoWarn>$(NoWarn);1591&lt;/NoWarn>
&lt;/PropertyGroup>
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Program.cs 구성:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">builder.Services.AddSwaggerGen(options =>
{
    var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
    
    // 상속된 XML 주석 포함
    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "MyModels.xml"));
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p>컨트롤러 예시:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">/// &lt;summary>
/// 제품 관리 API
/// &lt;/summary>
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    /// &lt;summary>
    /// 제품 목록을 조회합니다.
    /// &lt;/summary>
    /// &lt;param name="pageSize">페이지당 항목 수 (기본값: 10)&lt;/param>
    /// &lt;returns>제품 목록&lt;/returns>
    /// &lt;response code="200">성공적으로 조회됨&lt;/response>
    /// &lt;response code="400">잘못된 요청 파라미터&lt;/response>
    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable&lt;Product>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task&lt;ActionResult&lt;IEnumerable&lt;Product>>> GetProducts(
        [FromQuery] int pageSize = 10)
    {
        // 구현 로직
    }
}
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2. JWT Bearer 인증 통합</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">builder.Services.AddSwaggerGen(options =>
{
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\""
    });

    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty&lt;string>()
        }
    });
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3. API 버전 관리</h3>



<p>패키지 설치:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p>구성:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = ApiVersionReader.Combine(
        new UrlSegmentApiVersionReader(),
        new HeaderApiVersionReader("X-Version"),
        new MediaTypeApiVersionReader("ver"));
});

builder.Services.AddVersionedApiExplorer(setup =>
{
    setup.GroupNameFormat = "'v'VVV";
    setup.SubstituteApiVersionInUrl = true;
});

builder.Services.AddSwaggerGen();
builder.Services.ConfigureOptions&lt;ConfigureSwaggerOptions>();
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">4. 사용자 정의 스키마 필터</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            schema.Enum.Clear();
            Enum.GetNames(context.Type)
                .ToList()
                .ForEach(name => schema.Enum.Add(new OpenApiString(name)));
        }
    }
}

// 등록
builder.Services.AddSwaggerGen(options =>
{
    options.SchemaFilter&lt;EnumSchemaFilter>();
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ca.png" alt="📊" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 성능 최적화 및 모범 사례</h2>



<h3 class="wp-block-heading">1. 프로덕션 환경 고려사항</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 조건부 Swagger 활성화
if (app.Environment.IsDevelopment() || app.Environment.IsStaging())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// 또는 구성 기반 활성화
if (builder.Configuration.GetValue&lt;bool>("EnableSwagger"))
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
</pre>



<h3 class="wp-block-heading">2. 대용량 API 문서 최적화</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">builder.Services.AddSwaggerGen(options =>
{
    // 불필요한 스키마 제외
    options.SchemaFilter&lt;ExcludeInternalTypesFilter>();
    
    // 문서 압축
    options.EnableAnnotations();
    
    // 메모리 사용량 최적화
    options.UseAllOfToExtendReferenceSchemas();
    options.UseOneOfForPolymorphism();
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3. 보안 강화</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.UseSwagger(options =>
{
    // JSON 문서 접근 제한
    options.PreSerializeFilters.Add((swagger, httpReq) =>
    {
        if (!httpReq.Headers.ContainsKey("X-API-Key"))
        {
            throw new UnauthorizedAccessException();
        }
    });
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f527.png" alt="🔧" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 트러블슈팅 가이드</h2>



<h3 class="wp-block-heading">일반적인 문제와 해결책</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>문제</th><th>원인</th><th>해결방법</th></tr></thead><tbody><tr><td>XML 주석이 표시되지 않음</td><td>XML 파일 경로 오류</td><td>빌드 출력 디렉토리 확인 및 경로 수정</td></tr><tr><td>복잡한 제네릭 타입 오류</td><td>스키마 생성 실패</td><td>사용자 정의 SchemaFilter 구현</td></tr><tr><td>순환 참조 오류</td><td>모델 간 순환 의존성</td><td>JsonIgnore 또는 DTO 패턴 적용</td></tr><tr><td>인증 테스트 실패</td><td>CORS 또는 인증 설정 문제</td><td>CORS 정책 및 인증 미들웨어 순서 확인</td></tr></tbody></table></figure>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c8.png" alt="📈" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 모니터링 및 분석</h2>



<h3 class="wp-block-heading">Application Insights 통합</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">builder.Services.AddApplicationInsightsTelemetry();

// Swagger 사용량 추적
app.UseSwagger(options =>
{
    options.PreSerializeFilters.Add((swagger, httpReq) =>
    {
        var telemetryClient = httpReq.HttpContext.RequestServices
            .GetRequiredService&lt;TelemetryClient>();
        telemetryClient.TrackEvent("SwaggerAccessed");
    });
});
</pre>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 요약 및 베스트 프랙티스</h2>



<h3 class="wp-block-heading">핵심 도구 정리</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>도구/개념</th><th>설명</th><th>권장 사용 시나리오</th></tr></thead><tbody><tr><td><strong>OpenAPI/Swagger</strong></td><td>REST API 명세 표준</td><td>모든 REST API 프로젝트</td></tr><tr><td><strong>Swashbuckle.AspNetCore</strong></td><td>.NET용 Swagger 구현체</td><td>ASP.NET Core 웹 API</td></tr><tr><td><strong>Swagger UI</strong></td><td>인터랙티브 API 문서</td><td>개발 및 테스트 환경</td></tr><tr><td><strong>SwaggerGen</strong></td><td>코드 기반 문서 생성기</td><td>자동화된 문서 관리</td></tr></tbody></table></figure>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">개발팀을 위한 권장사항</h3>



<ol class="wp-block-list">
<li><strong>개발 초기</strong>부터 Swagger 도입하여 API 설계 단계에서 문서화</li>



<li><strong>XML 주석</strong>을 활용한 상세한 API 설명 작성</li>



<li><strong>DTO 패턴</strong> 적용으로 명확한 API 계약 정의</li>



<li><strong>버전 관리</strong> 전략 수립으로 하위 호환성 보장</li>



<li><strong>보안 설정</strong> 통합으로 실제 운영 환경과 일치하는 테스트 환경 구축</li>



<li><strong>CI/CD 파이프라인</strong>에 OpenAPI 스펙 검증 단계 포함</li>
</ol>



<p>이러한 접근을 통해 Swagger/OpenAPI는 단순한 문서화 도구를 넘어서 API 개발 생명주기 전반을 지원하는 핵심 인프라가 될 수 있습니다.</p>
<p>The post <a href="https://lycos7560.com/c/asp-net/swagger-openapi-for-net/40167/">Swagger/OpenAPI for .NET</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/asp-net/swagger-openapi-for-net/40167/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>.NET / C# Compiler, IL, DLL, EXE, JIT</title>
		<link>https://lycos7560.com/c/net-c-compiler-il-dll-exe-jit/40044/</link>
					<comments>https://lycos7560.com/c/net-c-compiler-il-dll-exe-jit/40044/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Wed, 16 Apr 2025 15:54:00 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[dotnet]]></category>
		<category><![CDATA[EXE]]></category>
		<category><![CDATA[IL]]></category>
		<category><![CDATA[JIT]]></category>
		<category><![CDATA[Roslyn]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[공부]]></category>
		<category><![CDATA[기초]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=40044</guid>

					<description><![CDATA[<p>1. 개요 C# 애플리케이션이 소스 코드에서 실행 가능한 프로그램으로 변환되는 과정은 여러 단계로 이루어져 있습니다. C# 컴파일러, 중간 언어(IL), 어셈블리(DLL, EXE), JIT 컴파일러 간의 관계와 전체 실행 흐름을 설명합니다. 2. 전체 실행 흐름 3. C# 컴파일러 (로슬린 &#8211; Roslyn) 역할 특징 컴파일러 파이프라인 구성요소 C# 코드 예시 4. 중간 언어 (IL, Intermediate Language) 특징 주요 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/net-c-compiler-il-dll-exe-jit/40044/">.NET / C# Compiler, IL, DLL, EXE, JIT</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[				<div class="wp-block-uagb-table-of-contents uagb-toc__align-left uagb-toc__columns-1  uagb-block-b89f8a18      "
					data-scroll= "1"
					data-offset= "30"
					style=""
				>
				<div class="uagb-toc__wrap">
						<div class="uagb-toc__title">
							목차						</div>
																						<div class="uagb-toc__list-wrap ">
						<ol class="uagb-toc__list"><li class="uagb-toc__list"><a href="#1-개요" class="uagb-toc-link__trigger">1. 개요</a><li class="uagb-toc__list"><a href="#2-전체-실행-흐름" class="uagb-toc-link__trigger">2. 전체 실행 흐름</a><li class="uagb-toc__list"><a href="#3-c-컴파일러-로슬린-roslyn" class="uagb-toc-link__trigger">3. C# 컴파일러 (로슬린 &#8211; Roslyn)</a><li class="uagb-toc__list"><a href="#4-중간-언어-il-intermediate-language" class="uagb-toc-link__trigger">4. 중간 언어 (IL, Intermediate Language)</a><li class="uagb-toc__list"><a href="#5-dll과-exe-파일-어셈블리" class="uagb-toc-link__trigger">5. DLL과 EXE 파일 (어셈블리)</a><li class="uagb-toc__list"><a href="#6-clr공통-언어-런타임" class="uagb-toc-link__trigger">6. CLR(공통 언어 런타임)</a><li class="uagb-toc__list"><a href="#7-jit-컴파일러-just-in-time" class="uagb-toc-link__trigger">7. JIT 컴파일러 (Just-In-Time)</a><li class="uagb-toc__list"><a href="#8-상세-실행-흐름" class="uagb-toc-link__trigger">8. 상세 실행 흐름</a><li class="uagb-toc__list"><a href="#9-코드-변환-예시" class="uagb-toc-link__trigger">9. 코드 변환 예시</a><li class="uagb-toc__list"><a href="#10-추가-정보" class="uagb-toc-link__trigger">10. 추가 정보</a><li class="uagb-toc__list"><a href="#11-결론" class="uagb-toc-link__trigger">11. 결론</a></ol>					</div>
									</div>
				</div>
			


<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">1. 개요</h2>



<p>C# 애플리케이션이 소스 코드에서 실행 가능한 프로그램으로 변환되는 과정은 여러 단계로 이루어져 있습니다. </p>



<p>C# 컴파일러, 중간 언어(IL), 어셈블리(DLL, EXE), JIT 컴파일러 간의 관계와 전체 실행 흐름을 설명합니다.</p>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">2. 전체 실행 흐름</h2>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">C# 소스 코드 → C# 컴파일러(Roslyn) → IL 코드 포함 DLL/EXE → CLR 로딩 → JIT 컴파일러 → 네이티브 머신 코드 → CPU 실행</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">3. C# 컴파일러 (로슬린 &#8211; Roslyn)</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">역할</h3>



<ul class="wp-block-list">
<li>C# 소스 코드(.cs 파일)를 <strong>중간 언어(IL, Intermediate Language)로 변환</strong></li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">특징</h3>



<ul class="wp-block-list">
<li>Microsoft의 오픈 소스 컴파일러 플랫폼</li>



<li><code>csc.exe</code>로 실행되거나 <code>dotnet build</code> 명령어 실행 시 자동 호출</li>



<li>어휘 분석, 구문 분석, 의미 분석, IL 생성의 단계적 작업 수행</li>



<li>주요 최적화는 JIT 컴파일 단계로 미루어짐</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">컴파일러 파이프라인 구성요소</h3>



<ul class="wp-block-list">
<li><strong>Syntax Tree API</strong>: 소스 코드의 구문적 구조 표현</li>



<li><strong>Semantic Model API</strong>: 코드의 의미론적 정보 제공</li>



<li><strong>Compilation API</strong>: 컴파일 과정 제어</li>



<li><strong>Workspace API</strong>: 솔루션, 프로젝트, 문서 등 작업 환경 관리</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">C# 코드 예시</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public class Program 
{
    public static void Main() 
    {
        Console.WriteLine("Hello, World!");
    }
}</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">4. 중간 언어 (IL, Intermediate Language)</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">특징</h3>



<ul class="wp-block-list">
<li>CPU 아키텍처에 독립적인 저수준 언어</li>



<li>.dll 또는 .exe 파일 내에 포함됨</li>



<li>인간이 읽을 수 있는 형태(ILASM)로도 표현 가능</li>



<li>CLR(공통 언어 런타임)이 이해하는 언어</li>



<li>스택 기반 명령어 집합으로 구성</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">주요 특성</h3>



<ul class="wp-block-list">
<li><strong>스택 기반 명령어 집합</strong>: 주로 스택을 사용하여 연산 수행</li>



<li><strong>객체 지향 지원</strong>: 클래스, 상속, 가상 메서드 등 지원</li>



<li><strong>타입 안전성</strong>: 강력한 타입 시스템 유지</li>



<li><strong>메타데이터 통합</strong>: 코드와 메타데이터가 밀접하게 연결</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">IL 코드 예시</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 위 C# 코드의 IL 표현 (간략화)
.method public hidebysig static void Main() cil managed {
  .entrypoint
  ldstr "Hello, World!"
  call void [System.Console]System.Console::WriteLine(string)
  ret
}
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">5. DLL과 EXE 파일 (어셈블리)</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">공통점</h3>



<ul class="wp-block-list">
<li>모두 PE(Portable Executable) 형식의 파일</li>



<li>메타데이터(형식 정보)와 IL 코드 포함</li>



<li>.NET 어셈블리로서 버전 관리, 보안 정보 등 포함</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">차이점</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>특성</th><th>DLL (동적 연결 라이브러리)</th><th>EXE (실행 파일)</th></tr></thead><tbody><tr><td>용도</td><td>재사용 가능한 라이브러리</td><td>실행 가능한 애플리케이션</td></tr><tr><td>진입점</td><td>없음</td><td><code>Main</code> 메서드 (IL에서 <code>.entrypoint</code> 표시)</td></tr><tr><td>내용</td><td>IL 코드와 메타데이터</td><td>IL 코드, 메타데이터 + 추가 실행 정보</td></tr><tr><td>실행</td><td>직접 실행 불가</td><td>직접 실행 가능</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">어셈블리 구성요소</h3>



<ul class="wp-block-list">
<li><strong>매니페스트</strong>: 어셈블리 ID, 버전, 문화권, 강력한 이름 등의 정보</li>



<li><strong>타입 메타데이터</strong>: 클래스, 메서드, 프로퍼티 등의 정보</li>



<li><strong>IL 코드</strong>: 실행 가능한 중간 언어 코드</li>



<li><strong>리소스</strong>: 이미지, 문자열 등 포함 가능</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">6. CLR(공통 언어 런타임)</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">역할</h3>



<ul class="wp-block-list">
<li>.NET 애플리케이션의 실행 환경 제공</li>



<li>메모리 관리, 보안, 예외 처리 등 다양한 서비스 제공</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">주요 구성요소</h3>



<ul class="wp-block-list">
<li><strong>클래스 로더</strong>: 필요한 타입을 메모리에 로드</li>



<li><strong>JIT 컴파일러</strong>: IL을 네이티브 코드로 변환</li>



<li><strong>가비지 컬렉터</strong>: 자동 메모리 관리</li>



<li><strong>보안 시스템</strong>: 코드 접근 보안(CAS) 등 제공</li>



<li><strong>스레드 관리</strong>: 다중 스레드 실행 지원</li>



<li><strong>예외 처리기</strong>: 예외 발생 및 처리 관리</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">7. JIT 컴파일러 (Just-In-Time)</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">역할</h3>



<ul class="wp-block-list">
<li>IL 코드를 네이티브 머신 코드로 변환</li>



<li>실행 시점에 최적화 수행</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">작동 과정</h3>



<ol class="wp-block-list">
<li>애플리케이션 실행 시 CLR 로드</li>



<li>메서드 첫 호출 시 해당 IL 코드 JIT 컴파일</li>



<li>컴파일된 네이티브 코드 캐싱 (재사용)</li>



<li>실제 CPU에서 실행</li>
</ol>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">최적화 기법</h3>



<ul class="wp-block-list">
<li><strong>인라인 확장</strong>: 작은 메서드를 호출 위치에 직접 삽입</li>



<li><strong>가상 메서드 최적화</strong>: 런타임 시 실제 타입 기반 최적화</li>



<li><strong>루프 최적화</strong>: 루프 언롤링, 벡터화 등</li>



<li><strong>불필요한 경계 검사 제거</strong>: 배열 접근 최적화</li>



<li><strong>상수 폴딩</strong>: 컴파일 시점에 상수 표현식 계산</li>



<li><strong>티어드 컴파일</strong>: 처음에는 빠르게 컴파일된 덜 최적화된 코드 실행, 후에 더 최적화된 버전으로 대체</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">8. 상세 실행 흐름</h2>



<ol class="wp-block-list">
<li><strong>C# 소스 코드 작성</strong>
<ul class="wp-block-list">
<li>개발자가 <code>.cs</code> 파일에 C# 코드 작성</li>
</ul>
</li>



<li><strong>C# 컴파일러(Roslyn) 처리</strong>
<ul class="wp-block-list">
<li><strong>어휘 분석(Lexical Analysis)</strong>: 소스 코드를 토큰으로 분리</li>



<li><strong>구문 분석(Syntax Analysis)</strong>: 토큰을 구문 트리로 구성</li>



<li><strong>의미 분석(Semantic Analysis)</strong>: 타입 체킹, 타입 추론 등</li>



<li><strong>IL 코드 생성</strong>: 컴파일된 중간 언어(IL) 생성</li>



<li><strong>메타데이터 생성</strong>: 타입 정보, 어셈블리 참조 등 생성</li>
</ul>
</li>



<li><strong>어셈블리 생성</strong>
<ul class="wp-block-list">
<li>컴파일된 IL 코드와 메타데이터가 PE(Portable Executable) 형식의 파일(<code>.dll</code> 또는 <code>.exe</code>)로 패키징</li>



<li>어셈블리 매니페스트 포함: 버전, 문화권, 강력한 이름 등의 정보</li>
</ul>
</li>



<li><strong>애플리케이션 실행</strong>
<ul class="wp-block-list">
<li><code>.exe</code> 파일 실행시 CLR(공통 언어 런타임) 호스트(예: <code>CoreCLR</code>)가 로드됨</li>



<li>CLR은 메타데이터를 읽고 필요한 어셈블리 로드</li>
</ul>
</li>



<li><strong>JIT 컴파일</strong>
<ul class="wp-block-list">
<li>메서드가 처음 호출될 때 JIT 컴파일러가 해당 메서드의 IL 코드를 네이티브 코드로 변환</li>



<li>티어드 컴파일 적용 시 점진적 최적화</li>
</ul>
</li>



<li><strong>실행</strong>
<ul class="wp-block-list">
<li>생성된 네이티브 코드가 CPU에서 직접 실행</li>



<li>가비지 컬렉션, 예외 처리 등 CLR 서비스가 실행 중 관리</li>
</ul>
</li>
</ol>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">9. 코드 변환 예시</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">C# 코드</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public static int AddNumbers(int a, int b) {
    return a + b;
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">변환된 IL 코드</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.method public static int32 AddNumbers(int32 a, int32 b) cil managed {
    .maxstack 2
    ldarg.0     // a를 스택에 로드
    ldarg.1     // b를 스택에 로드
    add         // 스택의 두 값을 더함
    ret         // 결과 반환
}
</pre>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">x86-64 네이티브 코드 (JIT 컴파일 후)</h3>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">mov eax, ecx    ; 첫 번째 인수(a)를 eax로 이동
add eax, edx    ; 두 번째 인수(b)를 더함
ret             ; 결과(eax에 저장됨) 반환
</pre>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">10. 추가 정보</h2>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">AOT(Ahead-of-Time) 컴파일</h3>



<ul class="wp-block-list">
<li>JIT 대신 미리 네이티브 코드로 컴파일하는 방식</li>



<li>초기 시작 시간 단축 및 메모리 사용량 감소</li>



<li>모바일/임베디드 환경에 적합</li>



<li>일부 동적 기능 제한 (리플렉션 등)</li>



<li>.NET 6 이상에서 Native AOT 지원</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">티어드 컴파일(Tiered Compilation)</h3>



<ul class="wp-block-list">
<li>처음에는 빠른 컴파일 우선으로 덜 최적화된 코드 생성</li>



<li>자주 사용되는 메서드는 백그라운드에서 더 많은 최적화 적용</li>



<li>시작 시간과 장기 실행 성능 사이의 균형 제공</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">11. 결론</h2>



<p>.NET의 컴파일 및 실행 모델은 플랫폼 독립성과 성능 사이의 균형을 효과적으로 맞추고 있습니다. </p>



<p>C# 소스 코드가 Roslyn 컴파일러를 통해 IL로 변환되고, 이후 JIT 컴파일러에 의해 필요할 때 네이티브 코드로 변환되는 과정은 다양한 플랫폼에서의 실행을 가능하게 하면서도 최적화된 성능을 제공합니다.</p>



<p>최근 버전의 .NET에서는 AOT 컴파일, 티어드 컴파일 등 더 다양한 최적화 기법을 도입하여 성능을 더욱 향상시키고 있으며, 이는 클라우드 네이티브 애플리케이션과 마이크로서비스 아키텍처에서 특히 중요한 역할을 합니다.</p>



<p></p>
<p>The post <a href="https://lycos7560.com/c/net-c-compiler-il-dll-exe-jit/40044/">.NET / C# Compiler, IL, DLL, EXE, JIT</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/net-c-compiler-il-dll-exe-jit/40044/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Dependency Inversion Principle(DIP, 의존성 역전 원리)</title>
		<link>https://lycos7560.com/c/dependency-inversion-principledip-%ec%9d%98%ec%a1%b4%ec%84%b1-%ec%97%ad%ec%a0%84-%ec%9b%90%eb%a6%ac/38731/</link>
					<comments>https://lycos7560.com/c/dependency-inversion-principledip-%ec%9d%98%ec%a1%b4%ec%84%b1-%ec%97%ad%ec%a0%84-%ec%9b%90%eb%a6%ac/38731/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Sun, 03 Nov 2024 07:48:04 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Blazor]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AddModelError]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Consuming]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[Create]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[Data access logic]]></category>
		<category><![CDATA[DataBase]]></category>
		<category><![CDATA[DbContext]]></category>
		<category><![CDATA[DbSet]]></category>
		<category><![CDATA[DELETE]]></category>
		<category><![CDATA[Dependency]]></category>
		<category><![CDATA[Dependency Inversion]]></category>
		<category><![CDATA[Deploying]]></category>
		<category><![CDATA[DI]]></category>
		<category><![CDATA[DIP]]></category>
		<category><![CDATA[Domain]]></category>
		<category><![CDATA[Domain Model]]></category>
		<category><![CDATA[File]]></category>
		<category><![CDATA[FileDescription]]></category>
		<category><![CDATA[Filestream]]></category>
		<category><![CDATA[GET]]></category>
		<category><![CDATA[IFormFile]]></category>
		<category><![CDATA[Image]]></category>
		<category><![CDATA[Image Controller]]></category>
		<category><![CDATA[IsValid]]></category>
		<category><![CDATA[Migration]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[ModelState]]></category>
		<category><![CDATA[pepe]]></category>
		<category><![CDATA[POST]]></category>
		<category><![CDATA[program]]></category>
		<category><![CDATA[Program.cs]]></category>
		<category><![CDATA[PUT]]></category>
		<category><![CDATA[Repository]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[REST Web API]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Upload]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[web API]]></category>
		<category><![CDATA[wpf]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[배포]]></category>
		<category><![CDATA[의존성]]></category>
		<category><![CDATA[의존성 역전]]></category>
		<category><![CDATA[의존성 역전 원리]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=38731</guid>

					<description><![CDATA[<p>Dependency Inversion Principle(DIP, 의존성 역전 원리) https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion The direction of dependency within the application should be in the direction of abstraction, not implementation details.애플리케이션 내의 종속성 방향은 구현 세부 사항이 아닌 추상화 방향이어야 합니다. Most applications are written such that compile-time dependency flows in the direction of runtime execution, producing a direct dependency graph.대부분의 애플리케이션은 [&#8230;]</p>
<p>The post <a href="https://lycos7560.com/c/dependency-inversion-principledip-%ec%9d%98%ec%a1%b4%ec%84%b1-%ec%97%ad%ec%a0%84-%ec%9b%90%eb%a6%ac/38731/">Dependency Inversion Principle(DIP, 의존성 역전 원리)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="dependency-inversion">Dependency Inversion Principle(DIP, 의존성 역전 원리)</h2>



<p><a href="https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion</a></p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p>The direction of dependency within the application should be in the direction of abstraction, not implementation details.<br>애플리케이션 내의 종속성 방향은 구현 세부 사항이 아닌 추상화 방향이어야 합니다. <br>Most applications are written such that compile-time dependency flows in the direction of runtime execution, producing a direct dependency graph.<br>대부분의 애플리케이션은 컴파일 타임 종속성이 런타임 실행 방향으로 흐르도록 작성되어 직접적인 종속성 그래프를 생성합니다.</p>



<p>That is, if class A calls a method of class B and class B calls a method of class C, then at compile time class A will depend on class B, and class B will depend on class C.<br>즉, class A가 class B의 메서드를 호출하고 class B가 class C의 메서드를 호출하는 경우 컴파일 타임에 class A는 class B에 종속되고 class B는 class C에 종속됩니다.</p>



<p>코드 구조에서 세부 구현 내용에 의존하지 말고, 더 추상적이고 일반적인 개념에 의존해야 한다는 뜻<br>이렇게 하면 코드가 더 유연해지고, 변경하기 쉽고, 재사용하기도 쉬워진다.<br>예를 들어, 어떤 기능을 <strong>직접 호출하는 것보다 그 기능을 일반적으로 설명하는 인터페이스를 사용하는 것이 더 좋다는 의미</strong>)</p>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="669" height="556" src="https://lycos7560.com/wp-content/uploads/2024/11/image-73.png" alt="" class="wp-image-38735" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-73.png 669w, https://lycos7560.com/wp-content/uploads/2024/11/image-73-300x249.png 300w" sizes="(max-width: 669px) 100vw, 669px" /><figcaption class="wp-element-caption">Direct dependency graph.</figcaption></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Applying the dependency inversion principle allows A to call methods on an abstraction that B implements, making it possible for A to call B at run time, but for B to depend on an interface controlled by A at compile time (thus,&nbsp;<em>inverting</em>&nbsp;the typical compile-time dependency).<br>종속성 반전 원칙을 적용하면 A가 B가 구현하는 추상화에 대한 메서드를 호출할 수 있으므로 A가 런타임에 B를 호출할 수 있지만 B는 컴파일 타임에 A에 의해 제어되는 인터페이스에 의존할 수 있습니다<br>(따라서 일반적인 컴파일 타임 종속성을&nbsp;<em>반전시킵니다</em>).&nbsp;</p>



<p>At run time, the flow of program execution remains unchanged, but the introduction of interfaces means that different implementations of these interfaces can easily be plugged in.<br>런타임에 프로그램 실행의 흐름은 변경되지 않지만 인터페이스의 도입은 이러한 인터페이스의 다른 구현을 쉽게 연결할 수 있음을 의미합니다.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="838" height="455" src="https://lycos7560.com/wp-content/uploads/2024/11/image-74.png" alt="" class="wp-image-38736" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-74.png 838w, https://lycos7560.com/wp-content/uploads/2024/11/image-74-300x163.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-74-768x417.png 768w" sizes="(max-width: 838px) 100vw, 838px" /><figcaption class="wp-element-caption">Inverted dependency graph.</figcaption></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Dependency inversion</strong>&nbsp;is a key part of building loosely coupled applications, since implementation details can be written to depend on and implement higher-level abstractions, rather than the other way around.<br><strong>종속성 반전</strong>은 느슨하게 결합된 애플리케이션을 구축하는 데 있어 중요한 부분인데, 그 이유는 구현 세부 정보를 작성하여 더 높은 수준의 추상화에 의존하고 구현하도록 할 수 있기 때문입니다.</p>



<p>The resulting applications are more testable, modular, and maintainable as a result.<br>그 결과 응용 프로그램은 더 쉽게 테스트할 수 있고, 모듈화되며, 유지 관리가 더 용이합니다.&nbsp;</p>



<p>The practice of&nbsp;<em>dependency injection</em>&nbsp;is made possible by following the dependency inversion principle.<br><em>종속성 주입</em>의 연습은 종속성 반전 원칙을 따름으로써 가능합니다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" style="margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20)"/>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p>애플리케이션의 의존성을 구체적인 구현이 아니라 추상화된 개념에 맞추는 것</p>



<p>대부분의 애플리케이션은 A 클래스가 B 클래스를, B 클래스가 C 클래스를 호출하는 형태로 작성됩니다. </p>



<p>이는 A가 B에, B가 C에 의존하게 만든다는 뜻입니다. 종속성 반전 원칙을 적용하면 A는 B의 구체적인 구현이 아니라 B가 구현하는 추상 개념에 의존하게 됩니다. </p>



<p>이렇게 하면 코드를 더 쉽게 테스트할 수 있고, 변경 및 유지보수가 용이해집니다. </p>



<p>간단히 말해, 코드를 유연하고 재사용 가능하게 만들어줍니다.</p>



<div style="height:102px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>종속성 반전 적용 전</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 엔진 클래스
public class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine starts.");
    }
}

// 자동차 클래스
public class Car
{
    private Engine _engine = new Engine();

    public void Start()
    {
        _engine.Start();
        Console.WriteLine("Car starts.");
    }
}

// 메인 프로그램
public class Program
{
    public static void Main(string[] args)
    {
        Car car = new Car();
        car.Start();
    }
}
</pre>



<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>종속성 반전 적용 후</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 인터페이스 정의
public interface IEngine
{
    void Start();
}

// 엔진 클래스
public class Engine : IEngine
{
    public void Start()
    {
        Console.WriteLine("Engine starts.");
    }
}

// 전기 엔진 클래스
public class ElectricEngine : IEngine
{
    public void Start()
    {
        Console.WriteLine("Electric engine starts.");
    }
}

// 자동차 클래스
public class Car
{
    private IEngine _engine;

    public Car(IEngine engine)
    {
        _engine = engine;
    }

    public void Start()
    {
        _engine.Start();
        Console.WriteLine("Car starts.");
    }
}

// 메인 프로그램
public class Program
{
    public static void Main(string[] args)
    {
        IEngine engine = new Engine();
        Car car = new Car(engine);
        car.Start();
        
        IEngine electricEngine = new ElectricEngine();
        Car electricCar = new Car(electricEngine);
        electricCar.Start();
    }
}
</pre>



<p></p>
<p>The post <a href="https://lycos7560.com/c/dependency-inversion-principledip-%ec%9d%98%ec%a1%b4%ec%84%b1-%ec%97%ad%ec%a0%84-%ec%9b%90%eb%a6%ac/38731/">Dependency Inversion Principle(DIP, 의존성 역전 원리)</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/dependency-inversion-principledip-%ec%9d%98%ec%a1%b4%ec%84%b1-%ec%97%ad%ec%a0%84-%ec%9b%90%eb%a6%ac/38731/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Deploying ASP.NET Web API To Azure</title>
		<link>https://lycos7560.com/c/deploying-asp-net-web-api-to-azure/38646/</link>
					<comments>https://lycos7560.com/c/deploying-asp-net-web-api-to-azure/38646/#respond</comments>
		
		<dc:creator><![CDATA[lycos7560]]></dc:creator>
		<pubDate>Fri, 01 Nov 2024 08:08:45 +0000</pubDate>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[.Net Core]]></category>
		<category><![CDATA[AddModelError]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[ASP.NET Core]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Consuming]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[Create]]></category>
		<category><![CDATA[Data]]></category>
		<category><![CDATA[Data access logic]]></category>
		<category><![CDATA[DataBase]]></category>
		<category><![CDATA[DbContext]]></category>
		<category><![CDATA[DbSet]]></category>
		<category><![CDATA[DELETE]]></category>
		<category><![CDATA[Deploying]]></category>
		<category><![CDATA[Domain]]></category>
		<category><![CDATA[Domain Model]]></category>
		<category><![CDATA[File]]></category>
		<category><![CDATA[FileDescription]]></category>
		<category><![CDATA[Filestream]]></category>
		<category><![CDATA[GET]]></category>
		<category><![CDATA[IFormFile]]></category>
		<category><![CDATA[Image]]></category>
		<category><![CDATA[Image Controller]]></category>
		<category><![CDATA[IsValid]]></category>
		<category><![CDATA[Migration]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[ModelState]]></category>
		<category><![CDATA[pepe]]></category>
		<category><![CDATA[POST]]></category>
		<category><![CDATA[program]]></category>
		<category><![CDATA[Program.cs]]></category>
		<category><![CDATA[PUT]]></category>
		<category><![CDATA[Repository]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[REST Web API]]></category>
		<category><![CDATA[study]]></category>
		<category><![CDATA[Upload]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[web API]]></category>
		<category><![CDATA[wpf]]></category>
		<category><![CDATA[기초]]></category>
		<category><![CDATA[배포]]></category>
		<guid isPermaLink="false">https://lycos7560.com/?p=38646</guid>

					<description><![CDATA[<p>Deploying ASP.NET Web API To Azure https://azure.microsoft.com/ko-kr</p>
<p>The post <a href="https://lycos7560.com/c/deploying-asp-net-web-api-to-azure/38646/">Deploying ASP.NET Web API To Azure</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Deploying ASP.NET Web API To Azure</h2>



<p><a href="https://azure.microsoft.com/ko-kr" target="_blank" rel="noreferrer noopener">https://azure.microsoft.com/ko-kr</a></p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1462" height="565" src="https://lycos7560.com/wp-content/uploads/2024/11/image.png" alt="" class="wp-image-38647" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image.png 1462w, https://lycos7560.com/wp-content/uploads/2024/11/image-300x116.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-768x297.png 768w" sizes="(max-width: 1462px) 100vw, 1462px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1242" height="801" src="https://lycos7560.com/wp-content/uploads/2024/11/image-1.png" alt="" class="wp-image-38650" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-1.png 1242w, https://lycos7560.com/wp-content/uploads/2024/11/image-1-300x193.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-1-768x495.png 768w" sizes="(max-width: 1242px) 100vw, 1242px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1019" height="525" src="https://lycos7560.com/wp-content/uploads/2024/11/image-2.png" alt="" class="wp-image-38651" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-2.png 1019w, https://lycos7560.com/wp-content/uploads/2024/11/image-2-300x155.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-2-768x396.png 768w" sizes="(max-width: 1019px) 100vw, 1019px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="745" height="541" src="https://lycos7560.com/wp-content/uploads/2024/11/image-3.png" alt="" class="wp-image-38652" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-3.png 745w, https://lycos7560.com/wp-content/uploads/2024/11/image-3-300x218.png 300w" sizes="(max-width: 745px) 100vw, 745px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1242" height="801" src="https://lycos7560.com/wp-content/uploads/2024/11/image-4.png" alt="" class="wp-image-38653" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-4.png 1242w, https://lycos7560.com/wp-content/uploads/2024/11/image-4-300x193.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-4-768x495.png 768w" sizes="(max-width: 1242px) 100vw, 1242px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1242" height="801" src="https://lycos7560.com/wp-content/uploads/2024/11/image-10.png" alt="" class="wp-image-38659" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-10.png 1242w, https://lycos7560.com/wp-content/uploads/2024/11/image-10-300x193.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-10-768x495.png 768w" sizes="(max-width: 1242px) 100vw, 1242px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1456" height="399" src="https://lycos7560.com/wp-content/uploads/2024/11/image-11.png" alt="" class="wp-image-38661" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-11.png 1456w, https://lycos7560.com/wp-content/uploads/2024/11/image-11-300x82.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-11-768x210.png 768w" sizes="(max-width: 1456px) 100vw, 1456px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="936" height="769" src="https://lycos7560.com/wp-content/uploads/2024/11/image-12.png" alt="" class="wp-image-38662" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-12.png 936w, https://lycos7560.com/wp-content/uploads/2024/11/image-12-300x246.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-12-768x631.png 768w" sizes="(max-width: 936px) 100vw, 936px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1176" height="457" src="https://lycos7560.com/wp-content/uploads/2024/11/image-13.png" alt="" class="wp-image-38663" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-13.png 1176w, https://lycos7560.com/wp-content/uploads/2024/11/image-13-300x117.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-13-768x298.png 768w" sizes="(max-width: 1176px) 100vw, 1176px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1510" height="801" src="https://lycos7560.com/wp-content/uploads/2024/11/image-14.png" alt="" class="wp-image-38664" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-14.png 1510w, https://lycos7560.com/wp-content/uploads/2024/11/image-14-300x159.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-14-768x407.png 768w" sizes="(max-width: 1510px) 100vw, 1510px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image"><img decoding="async" width="1146" height="521" src="https://lycos7560.com/wp-content/uploads/2024/11/image-15.png" alt="" class="wp-image-38665" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-15.png 1146w, https://lycos7560.com/wp-content/uploads/2024/11/image-15-300x136.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-15-768x349.png 768w" sizes="(max-width: 1146px) 100vw, 1146px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="845" height="518" src="https://lycos7560.com/wp-content/uploads/2024/11/image-16.png" alt="" class="wp-image-38666" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-16.png 845w, https://lycos7560.com/wp-content/uploads/2024/11/image-16-300x184.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-16-768x471.png 768w" sizes="(max-width: 845px) 100vw, 845px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="662" height="978" src="https://lycos7560.com/wp-content/uploads/2024/11/image-17.png" alt="" class="wp-image-38668" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-17.png 662w, https://lycos7560.com/wp-content/uploads/2024/11/image-17-203x300.png 203w" sizes="(max-width: 662px) 100vw, 662px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="661" height="820" src="https://lycos7560.com/wp-content/uploads/2024/11/image-18.png" alt="" class="wp-image-38670" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-18.png 661w, https://lycos7560.com/wp-content/uploads/2024/11/image-18-242x300.png 242w" sizes="(max-width: 661px) 100vw, 661px" /></figure>
</div>
</div>



<figure class="wp-block-image size-full"><img decoding="async" width="1726" height="984" src="https://lycos7560.com/wp-content/uploads/2024/11/image-20.png" alt="" class="wp-image-38672" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-20.png 1726w, https://lycos7560.com/wp-content/uploads/2024/11/image-20-300x171.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-20-768x438.png 768w, https://lycos7560.com/wp-content/uploads/2024/11/image-20-1536x876.png 1536w" sizes="(max-width: 1726px) 100vw, 1726px" /></figure>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="895" height="295" src="https://lycos7560.com/wp-content/uploads/2024/11/image-21.png" alt="" class="wp-image-38673" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-21.png 895w, https://lycos7560.com/wp-content/uploads/2024/11/image-21-300x99.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-21-768x253.png 768w" sizes="(max-width: 895px) 100vw, 895px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="688" height="328" src="https://lycos7560.com/wp-content/uploads/2024/11/image-25.png" alt="" class="wp-image-38677" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-25.png 688w, https://lycos7560.com/wp-content/uploads/2024/11/image-25-300x143.png 300w" sizes="(max-width: 688px) 100vw, 688px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="645" height="987" src="https://lycos7560.com/wp-content/uploads/2024/11/image-27.png" alt="" class="wp-image-38679" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-27.png 645w, https://lycos7560.com/wp-content/uploads/2024/11/image-27-196x300.png 196w" sizes="(max-width: 645px) 100vw, 645px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="696" height="1020" src="https://lycos7560.com/wp-content/uploads/2024/11/image-26.png" alt="" class="wp-image-38678" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-26.png 696w, https://lycos7560.com/wp-content/uploads/2024/11/image-26-205x300.png 205w" sizes="(max-width: 696px) 100vw, 696px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="824" height="415" src="https://lycos7560.com/wp-content/uploads/2024/11/image-28.png" alt="" class="wp-image-38680" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-28.png 824w, https://lycos7560.com/wp-content/uploads/2024/11/image-28-300x151.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-28-768x387.png 768w" sizes="(max-width: 824px) 100vw, 824px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="826" height="330" src="https://lycos7560.com/wp-content/uploads/2024/11/image-29.png" alt="" class="wp-image-38681" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-29.png 826w, https://lycos7560.com/wp-content/uploads/2024/11/image-29-300x120.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-29-768x307.png 768w" sizes="(max-width: 826px) 100vw, 826px" /></figure>
</div>
</div>



<figure class="wp-block-image size-full"><img decoding="async" width="1404" height="1005" src="https://lycos7560.com/wp-content/uploads/2024/11/image-30.png" alt="" class="wp-image-38682" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-30.png 1404w, https://lycos7560.com/wp-content/uploads/2024/11/image-30-300x215.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-30-768x550.png 768w" sizes="(max-width: 1404px) 100vw, 1404px" /></figure>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="714" height="604" src="https://lycos7560.com/wp-content/uploads/2024/11/image-31.png" alt="" class="wp-image-38683" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-31.png 714w, https://lycos7560.com/wp-content/uploads/2024/11/image-31-300x254.png 300w" sizes="(max-width: 714px) 100vw, 714px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="618" height="372" src="https://lycos7560.com/wp-content/uploads/2024/11/image-32.png" alt="" class="wp-image-38684" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-32.png 618w, https://lycos7560.com/wp-content/uploads/2024/11/image-32-300x181.png 300w" sizes="(max-width: 618px) 100vw, 618px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-full"><img decoding="async" width="473" height="496" src="https://lycos7560.com/wp-content/uploads/2024/11/image-33.png" alt="" class="wp-image-38685" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-33.png 473w, https://lycos7560.com/wp-content/uploads/2024/11/image-33-286x300.png 286w" sizes="(max-width: 473px) 100vw, 473px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="762" height="475" src="https://lycos7560.com/wp-content/uploads/2024/11/image-34.png" alt="" class="wp-image-38686" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-34.png 762w, https://lycos7560.com/wp-content/uploads/2024/11/image-34-300x187.png 300w" sizes="(max-width: 762px) 100vw, 762px" /><figcaption class="wp-element-caption">Azure 계정확인</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="596" height="362" src="https://lycos7560.com/wp-content/uploads/2024/11/image-35.png" alt="" class="wp-image-38687" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-35.png 596w, https://lycos7560.com/wp-content/uploads/2024/11/image-35-300x182.png 300w" sizes="(max-width: 596px) 100vw, 596px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-full"><img decoding="async" width="781" height="549" src="https://lycos7560.com/wp-content/uploads/2024/11/image-36.png" alt="" class="wp-image-38688" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-36.png 781w, https://lycos7560.com/wp-content/uploads/2024/11/image-36-300x211.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-36-768x540.png 768w" sizes="(max-width: 781px) 100vw, 781px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="787" height="541" src="https://lycos7560.com/wp-content/uploads/2024/11/image-38.png" alt="" class="wp-image-38690" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-38.png 787w, https://lycos7560.com/wp-content/uploads/2024/11/image-38-300x206.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-38-768x528.png 768w" sizes="(max-width: 787px) 100vw, 787px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="779" height="545" src="https://lycos7560.com/wp-content/uploads/2024/11/image-39.png" alt="" class="wp-image-38691" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-39.png 779w, https://lycos7560.com/wp-content/uploads/2024/11/image-39-300x210.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-39-768x537.png 768w" sizes="(max-width: 779px) 100vw, 779px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-full"><img decoding="async" width="764" height="523" src="https://lycos7560.com/wp-content/uploads/2024/11/image-40.png" alt="" class="wp-image-38692" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-40.png 764w, https://lycos7560.com/wp-content/uploads/2024/11/image-40-300x205.png 300w" sizes="(max-width: 764px) 100vw, 764px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50.01%">
<figure class="wp-block-image size-full"><img decoding="async" width="785" height="547" src="https://lycos7560.com/wp-content/uploads/2024/11/image-41.png" alt="" class="wp-image-38693" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-41.png 785w, https://lycos7560.com/wp-content/uploads/2024/11/image-41-300x209.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-41-768x535.png 768w" sizes="(max-width: 785px) 100vw, 785px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:49.99%">
<figure class="wp-block-image size-full"><img decoding="async" width="1133" height="772" src="https://lycos7560.com/wp-content/uploads/2024/11/image-42.png" alt="" class="wp-image-38694" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-42.png 1133w, https://lycos7560.com/wp-content/uploads/2024/11/image-42-300x204.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-42-768x523.png 768w" sizes="(max-width: 1133px) 100vw, 1133px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1074" height="288" src="https://lycos7560.com/wp-content/uploads/2024/11/image-43.png" alt="" class="wp-image-38695" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-43.png 1074w, https://lycos7560.com/wp-content/uploads/2024/11/image-43-300x80.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-43-768x206.png 768w" sizes="(max-width: 1074px) 100vw, 1074px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="786" height="549" src="https://lycos7560.com/wp-content/uploads/2024/11/image-44.png" alt="" class="wp-image-38697" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-44.png 786w, https://lycos7560.com/wp-content/uploads/2024/11/image-44-300x210.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-44-768x536.png 768w" sizes="(max-width: 786px) 100vw, 786px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="777" height="540" src="https://lycos7560.com/wp-content/uploads/2024/11/image-45.png" alt="" class="wp-image-38698" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-45.png 777w, https://lycos7560.com/wp-content/uploads/2024/11/image-45-300x208.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-45-768x534.png 768w" sizes="(max-width: 777px) 100vw, 777px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="775" height="543" src="https://lycos7560.com/wp-content/uploads/2024/11/image-46.png" alt="" class="wp-image-38699" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-46.png 775w, https://lycos7560.com/wp-content/uploads/2024/11/image-46-300x210.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-46-768x538.png 768w" sizes="(max-width: 775px) 100vw, 775px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="788" height="564" src="https://lycos7560.com/wp-content/uploads/2024/11/image-47.png" alt="" class="wp-image-38700" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-47.png 788w, https://lycos7560.com/wp-content/uploads/2024/11/image-47-300x215.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-47-768x550.png 768w" sizes="(max-width: 788px) 100vw, 788px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1059" height="338" src="https://lycos7560.com/wp-content/uploads/2024/11/image-48.png" alt="" class="wp-image-38701" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-48.png 1059w, https://lycos7560.com/wp-content/uploads/2024/11/image-48-300x96.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-48-768x245.png 768w" sizes="(max-width: 1059px) 100vw, 1059px" /><figcaption class="wp-element-caption">위와 같은 작업 반복</figcaption></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="858" height="260" src="https://lycos7560.com/wp-content/uploads/2024/11/image-49.png" alt="" class="wp-image-38702" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-49.png 858w, https://lycos7560.com/wp-content/uploads/2024/11/image-49-300x91.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-49-768x233.png 768w" sizes="(max-width: 858px) 100vw, 858px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1117" height="772" src="https://lycos7560.com/wp-content/uploads/2024/11/image-54.png" alt="" class="wp-image-38708" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-54.png 1117w, https://lycos7560.com/wp-content/uploads/2024/11/image-54-300x207.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-54-768x531.png 768w" sizes="(max-width: 1117px) 100vw, 1117px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1151" height="377" src="https://lycos7560.com/wp-content/uploads/2024/11/image-53.png" alt="" class="wp-image-38711" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-53.png 1151w, https://lycos7560.com/wp-content/uploads/2024/11/image-53-300x98.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-53-768x252.png 768w" sizes="(max-width: 1151px) 100vw, 1151px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="686" height="544" src="https://lycos7560.com/wp-content/uploads/2024/11/image-55.png" alt="" class="wp-image-38712" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-55.png 686w, https://lycos7560.com/wp-content/uploads/2024/11/image-55-300x238.png 300w" sizes="(max-width: 686px) 100vw, 686px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1170" height="123" src="https://lycos7560.com/wp-content/uploads/2024/11/image-50.png" alt="" class="wp-image-38703" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-50.png 1170w, https://lycos7560.com/wp-content/uploads/2024/11/image-50-300x32.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-50-768x81.png 768w" sizes="(max-width: 1170px) 100vw, 1170px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1046" height="319" src="https://lycos7560.com/wp-content/uploads/2024/11/image-56.png" alt="" class="wp-image-38713" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-56.png 1046w, https://lycos7560.com/wp-content/uploads/2024/11/image-56-300x91.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-56-768x234.png 768w" sizes="(max-width: 1046px) 100vw, 1046px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="687" height="537" src="https://lycos7560.com/wp-content/uploads/2024/11/image-57.png" alt="" class="wp-image-38714" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-57.png 687w, https://lycos7560.com/wp-content/uploads/2024/11/image-57-300x234.png 300w" sizes="(max-width: 687px) 100vw, 687px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-full"><img decoding="async" width="319" height="342" src="https://lycos7560.com/wp-content/uploads/2024/11/image-65.png" alt="" class="wp-image-38722" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-65.png 319w, https://lycos7560.com/wp-content/uploads/2024/11/image-65-280x300.png 280w" sizes="(max-width: 319px) 100vw, 319px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure class="wp-block-image size-full"><img decoding="async" width="1091" height="731" src="https://lycos7560.com/wp-content/uploads/2024/11/image-58.png" alt="" class="wp-image-38715" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-58.png 1091w, https://lycos7560.com/wp-content/uploads/2024/11/image-58-300x201.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-58-768x515.png 768w" sizes="(max-width: 1091px) 100vw, 1091px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="672" height="599" src="https://lycos7560.com/wp-content/uploads/2024/11/image-59.png" alt="" class="wp-image-38716" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-59.png 672w, https://lycos7560.com/wp-content/uploads/2024/11/image-59-300x267.png 300w" sizes="(max-width: 672px) 100vw, 672px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1066" height="750" src="https://lycos7560.com/wp-content/uploads/2024/11/image-60.png" alt="" class="wp-image-38717" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-60.png 1066w, https://lycos7560.com/wp-content/uploads/2024/11/image-60-300x211.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-60-768x540.png 768w" sizes="(max-width: 1066px) 100vw, 1066px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="304" height="432" src="https://lycos7560.com/wp-content/uploads/2024/11/image-61.png" alt="" class="wp-image-38718" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-61.png 304w, https://lycos7560.com/wp-content/uploads/2024/11/image-61-211x300.png 211w" sizes="(max-width: 304px) 100vw, 304px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="528" height="628" src="https://lycos7560.com/wp-content/uploads/2024/11/image-62.png" alt="" class="wp-image-38719" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-62.png 528w, https://lycos7560.com/wp-content/uploads/2024/11/image-62-252x300.png 252w" sizes="(max-width: 528px) 100vw, 528px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1034" height="568" src="https://lycos7560.com/wp-content/uploads/2024/11/image-63.png" alt="" class="wp-image-38720" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-63.png 1034w, https://lycos7560.com/wp-content/uploads/2024/11/image-63-300x165.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-63-768x422.png 768w" sizes="(max-width: 1034px) 100vw, 1034px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1005" height="434" src="https://lycos7560.com/wp-content/uploads/2024/11/image-64.png" alt="" class="wp-image-38721" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-64.png 1005w, https://lycos7560.com/wp-content/uploads/2024/11/image-64-300x130.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-64-768x332.png 768w" sizes="(max-width: 1005px) 100vw, 1005px" /></figure>
</div>
</div>



<figure class="wp-block-image size-full"><img decoding="async" width="1593" height="225" src="https://lycos7560.com/wp-content/uploads/2024/11/image-66.png" alt="" class="wp-image-38723" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-66.png 1593w, https://lycos7560.com/wp-content/uploads/2024/11/image-66-300x42.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-66-768x108.png 768w, https://lycos7560.com/wp-content/uploads/2024/11/image-66-1536x217.png 1536w" sizes="(max-width: 1593px) 100vw, 1593px" /></figure>



<figure class="wp-block-image size-full"><img decoding="async" width="1359" height="598" src="https://lycos7560.com/wp-content/uploads/2024/11/image-67.png" alt="" class="wp-image-38724" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-67.png 1359w, https://lycos7560.com/wp-content/uploads/2024/11/image-67-300x132.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-67-768x338.png 768w" sizes="(max-width: 1359px) 100vw, 1359px" /></figure>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="940" height="534" src="https://lycos7560.com/wp-content/uploads/2024/11/image-68.png" alt="" class="wp-image-38725" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-68.png 940w, https://lycos7560.com/wp-content/uploads/2024/11/image-68-300x170.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-68-768x436.png 768w" sizes="(max-width: 940px) 100vw, 940px" /><figcaption class="wp-element-caption">계정 생성</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="1435" height="379" src="https://lycos7560.com/wp-content/uploads/2024/11/image-69.png" alt="" class="wp-image-38726" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-69.png 1435w, https://lycos7560.com/wp-content/uploads/2024/11/image-69-300x79.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-69-768x203.png 768w" sizes="(max-width: 1435px) 100vw, 1435px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-full"><img decoding="async" width="927" height="637" data-id="38727" src="https://lycos7560.com/wp-content/uploads/2024/11/image-70.png" alt="" class="wp-image-38727" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-70.png 927w, https://lycos7560.com/wp-content/uploads/2024/11/image-70-300x206.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-70-768x528.png 768w" sizes="(max-width: 927px) 100vw, 927px" /></figure>
</figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full"><img decoding="async" width="939" height="447" src="https://lycos7560.com/wp-content/uploads/2024/11/image-71.png" alt="" class="wp-image-38728" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-71.png 939w, https://lycos7560.com/wp-content/uploads/2024/11/image-71-300x143.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-71-768x366.png 768w" sizes="(max-width: 939px) 100vw, 939px" /></figure>
</div>
</div>



<figure class="wp-block-image size-full"><img decoding="async" width="942" height="762" src="https://lycos7560.com/wp-content/uploads/2024/11/image-72.png" alt="" class="wp-image-38729" srcset="https://lycos7560.com/wp-content/uploads/2024/11/image-72.png 942w, https://lycos7560.com/wp-content/uploads/2024/11/image-72-300x243.png 300w, https://lycos7560.com/wp-content/uploads/2024/11/image-72-768x621.png 768w" sizes="(max-width: 942px) 100vw, 942px" /></figure>
<p>The post <a href="https://lycos7560.com/c/deploying-asp-net-web-api-to-azure/38646/">Deploying ASP.NET Web API To Azure</a> appeared first on <a href="https://lycos7560.com">어제와 내일의 나 그 사이의 이야기</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://lycos7560.com/c/deploying-asp-net-web-api-to-azure/38646/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
