<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>개발 log - 꺼내보는 수첩</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 08 May 2026 01:09:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>개발 log - 꺼내보는 수첩</title>
            <url>https://velog.velcdn.com/images/joongjii_1217/profile/180a3a4b-906a-4d98-939e-058abb655ea9/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 개발 log - 꺼내보는 수첩. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/joongjii_1217" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[노락]]></title>
            <link>https://velog.io/@joongjii_1217/%EB%85%B8%EB%9D%BD</link>
            <guid>https://velog.io/@joongjii_1217/%EB%85%B8%EB%9D%BD</guid>
            <pubDate>Fri, 08 May 2026 01:09:39 GMT</pubDate>
            <description><![CDATA[<p>[제목] ERP 결산의 적! DB 트리거로도 막지 못한 &#39;시점재고 마이너스(-)&#39;의 비밀과 해결책</p>
<p>안녕하세요! 오늘은 ERP를 도입한 많은 제조/유통 기업에서 월말 결산 때마다 담당자들의 뒷목을 잡게 만드는 단골 문제, 바로 &#39;시점재고 마이너스(-)&#39; 현상에 대해 파헤쳐 보려고 합니다.</p>
<p>&quot;우리 회사는 DB에 마이너스 재고 차단 트리거(Trigger)를 강력하게 걸어놨는데, 왜 과거 날짜로 재고 현황을 조회하면 마이너스가 뜨는 걸까요?&quot;</p>
<p>개발자와 현업 담당자 모두를 귀신 홀린 듯하게 만드는 이 현상! 시스템 오류일까요? 결론부터 말씀드리면 시스템은 죄가 없습니다. 현장의 &#39;전표 입력 시차&#39;와 &#39;예외 처리 악용&#39;이 만들어낸 완벽한 합작품입니다. 그 이유를 3가지 핵심 단서로 명쾌하게 정리해 드립니다.</p>
<p>🕵️‍♂️ 1. 트리거(Trigger)는 &#39;타임머신&#39;이 아닙니다
시스템에 걸어둔 마이너스 방지 트리거는 전표를 저장하는 &#39;지금 이 순간&#39;의 총재고만 검사하는 문지기입니다. 반면, 시점재고(과거 재고)는 현재 재고에서 과거의 입출고 내역을 거꾸로 더하고 빼는 &#39;역산(타임머신)&#39; 방식으로 계산됩니다.</p>
<p>여기서 맹점이 발생합니다.</p>
<p>상황: 5월 1일에 100개가 입고되었습니다. 작업자가 5월 2일에 출고를 잡으면서, 실수로(또는 고의로) 수불 날짜를 &#39;4월 25일&#39;로 쳤습니다.</p>
<p>트리거의 판단: &quot;지금 창고에 100개 있으니까 출고 통과!&quot;</p>
<p>시점재고의 계산: 4월 30일 기준으로 장부를 조회하면? 5월 1일 입고는 아직 일어나지 않은 미래의 일이고, 출고는 4월 25일에 발생했으니 장부는 마이너스(-) 가 찍힙니다.</p>
<p>즉, 실물이 아무리 넉넉해도 &#39;전표 상의 입고 날짜보다 출고 날짜가 더 과거로 잡히면&#39; 시점재고는 무조건 마이너스를 뱉어냅니다.</p>
<p>🕵️‍♂️ 2. 깐깐한 LOT 관리를 뚫는 마법의 단어, &#39;가짜 LOT&#39;
마이너스 재고가 터지는 두 번째 이유는 현장의 &#39;귀찮음&#39;에서 비롯됩니다.
보통 강력한 통제를 위해 &#39;LOT 관리 품목&#39;에만 깐깐한 마이너스 차단 로직을 걸어두는 경우가 많습니다. 출고할 때 정확한 LOT 번호를 지정해야만 전표가 넘어가도록 말이죠.</p>
<p>하지만 바쁜 현장 작업자들은 정확한 LOT 번호를 찾는 대신 꼼수를 발휘합니다.</p>
<p>99991231 같은 가짜(더미) LOT 번호를 입력하거나,</p>
<p>LOT 번호 칸을 아예 빈칸(Blank) 으로 둔 채 저장을 때려버립니다.</p>
<p>트리거는 이런 비정상적인 데이터(빈칸 등)를 만나면 &quot;이건 예외 품목인가 보네?&quot; 하고 검사를 건너뛰어 버립니다. 결국 진짜 재고는 그대로 묶여있고, 있지도 않은 가짜 LOT에서 재고가 무한대로 빠져나가며 장부 밑바닥을 파고 들어가게 됩니다.</p>
<p>🕵️‍♂️ 3. 월말 결산의 대환장 파티, &#39;실사 조정(J 전표)&#39;의 늪
이렇게 가짜 LOT로 출고를 잡다 보면 4월 30일 월말 마감 때 사단이 납니다. 자재팀이 수불부를 뽑아보니 재고가 안 맞는 거죠.</p>
<p>&quot;어? 99991231 LOT는 마이너스 35개고, 진짜 LOT에는 30개가 남아있네?!&quot;</p>
<p>부랴부랴 장부를 실물과 똑같이 맞추기 위해 점심시간에 &#39;재고 조정 전표&#39;를 칩니다. 마이너스가 난 가짜 LOT에 수량을 채워 넣고, 원래 빼야 했던 진짜 LOT에서 물건을 털어냅니다.</p>
<p>마감일 당일에 마이너스와 플러스를 오가며 억지로 숫자를 끼워 맞췄으니, 그 이전 날짜로 시점재고를 돌려보면 누적된 마이너스 적자가 화면에 그대로 노출될 수밖에 없습니다.</p>
<p>💡 그럼 어떻게 해결해야 할까? (솔루션)
이 문제는 DB 트리거만으로는 완벽히 막을 수 없습니다. 전산팀과 현업 부서가 함께 움직여야 합니다.</p>
<p>[전산팀] 화면 단의 철통 방어 로직 추가</p>
<p>출고/투입 전표 입력 화면(UI)에서 LOT 관리 대상 품목일 경우, 99991231 같은 더미 번호나 빈칸이 들어오면 아예 &#39;저장(Save)&#39; 버튼이 눌리지 않도록 스크립트를 추가해야 합니다.</p>
<p>[현업팀] 실시간 전표 입력 및 PDA 도입</p>
<p>입고보다 출고가 먼저 잡히는 &#39;수량 역전 현상&#39;을 막기 위해서는 실물 흐름과 전산 흐름이 1:1로 일치해야 합니다. 바코드/PDA를 도입하여 현장에서 즉시 수불을 잡는 것이 가장 완벽한 예방책입니다.</p>
<p>결산 때마다 시점재고 마이너스 리스트를 뽑아들고 범인을 찾으러 다니셨다면, 오늘 당장 DB 수불원장을 열어 &#39;입고 없는 출고 데이터&#39;와 &#39;가짜 LOT 데이터&#39;를 색출해 보세요! 원인을 알면 시스템의 구멍을 완벽하게 막을 수 있습니다.</p>
<p>Tags: #ERP #시점재고 #결산마감 #재고관리 #데이터베이스 #트리거 #스마트팩토리 #ERP개발 #전산실무</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[수불]]></title>
            <link>https://velog.io/@joongjii_1217/%EC%88%98%EB%B6%88</link>
            <guid>https://velog.io/@joongjii_1217/%EC%88%98%EB%B6%88</guid>
            <pubDate>Thu, 29 Jan 2026 08:11:36 GMT</pubDate>
            <description><![CDATA[<h1 id="erp-수불재고수불-기본-개념-정리-기초-입고-출고-재고-관점">ERP 수불(재고수불) 기본 개념 정리: 기초-입고-출고-재고 관점</h1>
<p>ERP에서 “수불”은 <strong>기간(월/일) 단위로 재고가 어떻게 변했는지</strong>를 보여주는 기록/집계 체계이다. 현업에서는 흔히 <strong>기초 → 입고 → 출고 → 재고(기말)</strong> 흐름으로 설명한다.</p>
<hr>
<h2 id="1-용어-정의수불표의-기본-칸">1) 용어 정의(수불표의 기본 칸)</h2>
<h3 id="1-기초期初">(1) 기초(期初)</h3>
<ul>
<li><strong>기간 시작 시점의 재고</strong>이다.  </li>
<li>예: 2026년 1월 수불이면 <strong>2026-01-01 00:00 기준 재고</strong>가 기초가 된다이다.  </li>
<li>보통 <strong>전기(이전 기간) 기말재고가 그대로 이월</strong>된다이다.</li>
</ul>
<h3 id="2-입고受">(2) 입고(受)</h3>
<ul>
<li>기간 중 재고를 <strong>증가</strong>시키는 모든 수불이다.  </li>
<li>예: <strong>매입입고, 생산입고(완제품), 외주입고, 반품입고, 이동입고(다른 창고에서 들어옴)</strong> 등이다.</li>
</ul>
<h3 id="3-출고拂">(3) 출고(拂)</h3>
<ul>
<li>기간 중 재고를 <strong>감소</strong>시키는 모든 수불이다.  </li>
<li>예: <strong>생산투입(원자재 출고), 판매출고, 출고반품(고객반품 처리 방식에 따라), 이동출고(다른 창고로 나감), 폐기출고</strong> 등이다.</li>
</ul>
<h3 id="4-재고기말">(4) 재고(기말)</h3>
<ul>
<li><strong>기간 종료 시점 재고</strong>이다.  </li>
<li>예: 2026년 1월이면 <strong>2026-01-31 23:59 기준 재고</strong>가 기말이다.</li>
</ul>
<hr>
<h2 id="2-핵심-공식수불의-정리">2) 핵심 공식(수불의 정리)</h2>
<p>수량 기준 핵심 공식은 다음과 같다.</p>
<blockquote>
<p><strong>기말재고 = 기초재고 + 입고수량 − 출고수량</strong></p>
</blockquote>
<p>금액(재고평가)까지 포함하면 보통 다음과 같이 정리한다.</p>
<blockquote>
<p><strong>기말금액 = 기초금액 + 입고금액 − 출고금액</strong></p>
</blockquote>
<p>여기서 <strong>출고금액</strong>은 재고평가 방식에 따라 달라진다.</p>
<ul>
<li>FIFO(선입선출)</li>
<li>이동평균</li>
<li>총평균</li>
<li>개별법</li>
</ul>
<hr>
<h2 id="3-예시로-감-잡기">3) 예시로 감 잡기</h2>
<p>어떤 품목 A가 1월에 다음과 같다고 치자이다.</p>
<ul>
<li>기초: 100EA  </li>
<li>입고: 매입 50EA, 반품입고 10EA → 총 입고 60EA  </li>
<li>출고: 생산투입 30EA, 판매출고 20EA → 총 출고 50EA  </li>
</ul>
<p>그러면:</p>
<blockquote>
<p><strong>기말 = 100 + 60 − 50 = 110EA</strong> 이다.</p>
</blockquote>
<p>ERP의 수불표는 이 계산을 <strong>품목/창고/로트(LOT)</strong> 단위로 확장해 펼친 것이고, 수불 데이터가 쌓이면 자동으로 산출된다이다.</p>
<hr>
<h2 id="4-기초가-어떻게-만들어지나실무-포인트">4) “기초”가 어떻게 만들어지나(실무 포인트)</h2>
<p>기초는 보통 아래 3가지 중 하나로 결정된다이다.</p>
<p>1) <strong>전월(전기) 기말을 이월</strong>  </p>
<ul>
<li>가장 일반적이다.</li>
</ul>
<p>2) <strong>실사(재고조정) 결과를 기초로 확정</strong>  </p>
<ul>
<li>월말 실사 후 다음 달 기초를 <strong>실사수량</strong>으로 고정하는 회사도 많다이다.</li>
</ul>
<p>3) <strong>신규 품목/신규 창고는 기초가 0에서 시작</strong>  </p>
<ul>
<li>첫 입고가 생기면 그때부터 재고가 생긴다이다.</li>
</ul>
<hr>
<h2 id="5-입고출고에-들어가는-대표-거래-유형분류-체계">5) 입고/출고에 들어가는 대표 거래 유형(분류 체계)</h2>
<p>현업에서는 입/출고를 더 쪼개서 <strong>수불유형(수불구분)</strong>으로 관리한다이다.</p>
<ul>
<li>입고: <strong>매입입고 / 생산입고 / 외주입고 / 반품입고 / 이동입고 / 재고조정(+)</strong></li>
<li>출고: <strong>생산출고(투입) / 판매출고 / 반품출고 / 이동출고 / 폐기출고 / 재고조정(-)</strong></li>
</ul>
<p>참고로 <code>iogbn</code> 같은 값(입고/출고/입고반품/출고반품)은 이러한 분류 체계의 일부로 쓰이는 경우가 많다이다.</p>
<hr>
<h2 id="6-재고가-안-맞을-때-흔한-원인">6) 재고가 “안 맞을 때” 흔한 원인</h2>
<p>재고 불일치는 대개 다음 케이스에서 발생한다이다.</p>
<ul>
<li>수불은 쌓였는데 <strong>기초 이월이 안 됨</strong>(마감 미수행/이월 누락)</li>
<li><strong>이동출고는 했는데 이동입고가 누락</strong>(창고 간 짝이 깨짐)</li>
<li>반품이 입고로 잡혀야 하는데 <strong>출고로 잡힘</strong>(수불유형 매핑 오류)</li>
<li><strong>실사조정이 중복 반영</strong>됨(조정 전표 중복)</li>
<li><strong>단위환산(BOM/포장단위) 오류</strong>로 수량이 틀어짐</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[패턴 매칭]]></title>
            <link>https://velog.io/@joongjii_1217/%ED%8C%A8%ED%84%B4-%EB%A7%A4%EC%B9%AD</link>
            <guid>https://velog.io/@joongjii_1217/%ED%8C%A8%ED%84%B4-%EB%A7%A4%EC%B9%AD</guid>
            <pubDate>Thu, 11 Dec 2025 08:00:23 GMT</pubDate>
            <description><![CDATA[<article>

  <ul>
    <li>선언 패턴 / <code>var</code> 패턴 / discard(<code>_</code>) 패턴</li>
    <li>위치(포지셔널) 패턴 – 튜플 / <code>Deconstruct</code> / record</li>
    <li>논리 패턴 – <code>and</code>, <code>or</code>, <code>not</code></li>
    <li>리스트 패턴(List Pattern) 응용</li>
    <li>재귀(Recursive) 패턴 – 패턴들을 섞어서 쓰기</li>
    <li><code>when</code> 가드와 패턴의 조합</li>
    <li><code>foreach</code> + 튜플/패턴 활용</li>
  </ul>

  <hr>

  <h2>1. 선언 패턴, <code>var</code> 패턴, discard(<code>_</code>) 패턴</h2>

  <h3>1-1. 선언 패턴 (Declaration Pattern)</h3>

  <p>
    패턴 매칭의 가장 기본 형태는 “타입 검사 + 캐스팅”을 한 줄로 합친 <strong>선언 패턴</strong>이다.
  </p>

  <pre><code class="language-csharp">if (obj is string s)
{
    // s는 이미 string으로 캐스팅된 상태
    Console.WriteLine(s.ToUpper());
}</code></pre>

  <ul>
    <li><code>obj is string s</code>의 의미
      <ul>
        <li><code>obj</code>가 <code>string</code> 타입이면 <code>true</code></li>
        <li>동시에 <code>s</code>라는 이름의 지역 변수에 캐스팅된 결과를 넣어줌</li>
      </ul>
    </li>
  </ul>

  <p>
    예전에는
  </p>

  <pre><code class="language-csharp">if (obj is string)
{
    string s = (string)obj;
    ...
}</code></pre>

  <p>이렇게 타입 체크와 캐스팅을 따로 했지만, 선언 패턴 덕분에 한 줄로 줄일 수 있다.</p>

  <h3>1-2. <code>var</code> 패턴</h3>

  <p><code>var</code> 패턴은 “어떤 값이든 받되 이름만 붙이고 싶다”는 느낌으로 사용할 수 있다.</p>

  <pre><code class="language-csharp">if (obj is var x)
{
    // 항상 true, obj를 x라는 이름으로 받는 패턴
    Console.WriteLine(x);
}</code></pre>

  <p>
    이렇게만 보면 굳이 쓸 이유가 별로 없는데,  
    <strong>다른 패턴과 섞을 때</strong> “중간 값에 이름 붙이기” 용도로 쓸 수 있다.
  </p>

  <pre><code class="language-csharp">if (n is &gt; 0 and &lt; 100 and var inRange)
{
    Console.WriteLine($"범위 안 숫자: {inRange}");
}</code></pre>

  <ul>
    <li><code>n is &gt; 0 and &lt; 100</code>으로 0 &lt; n &lt; 100 범위를 체크</li>
    <li><code>and var inRange</code>로 그 값을 <code>inRange</code>라는 이름으로 꺼내 사용</li>
  </ul>

  <p>
    즉, <strong>조건 검사 + 별칭(alias) 부여</strong> 용도로 쓰는 패턴이다.
  </p>

  <h3>1-3. discard(<code>_</code>) 패턴</h3>

  <p>
    <code>_</code>는 “이 값은 굳이 쓰지 않을 거라서 버리겠다”는 의미의 패턴이다.
  </p>

  <pre><code class="language-csharp">(int x, int y, int z) point = (1, 2, 3);

if (point is (_, _, 3))
{
    Console.WriteLine("세 번째 값이 3이다");
}</code></pre>

  <ul>
    <li><code>(_, _, 3)</code> → 첫 번째, 두 번째 값은 신경 안 쓰고, 세 번째가 3인지 여부만 확인</li>
  </ul>

  <p>가장 익숙한 예는 <code>switch</code>의 <code>default</code> 역할이다.</p>

  <pre><code class="language-csharp">return code switch
{
    200 =&gt; "OK",
    400 =&gt; "Bad Request",
    _   =&gt; "Unknown"
};</code></pre>

  <p>
    <code>_</code>는 “나머지 모든 경우”를 의미하는 패턴이며,  
    값을 버리는 패턴(discard)이라고 이해하면 된다.
  </p>

  <hr>

  <h2>2. 위치(포지셔널) 패턴 – 튜플 / Deconstruct / record</h2>

  <p>
    위치(포지셔널) 패턴은 <strong>튜플</strong>이나 <strong>Deconstruct</strong>를 제공하는 타입을  
    “좌표”처럼 다룰 때 사용하는 패턴이다.
  </p>

  <h3>2-1. 튜플과 위치 패턴</h3>

  <pre><code class="language-csharp">(int x, int y) p = (10, 0);

string Describe((int x, int y) point) =&gt; point switch
{
    (0, 0)      =&gt; "원점",
    (_, 0)      =&gt; "X축 위",
    (0, _)      =&gt; "Y축 위",
    var (a, b)  =&gt; $"일반 점 ({a}, {b})"
};</code></pre>

  <ul>
    <li><code>(0, 0)</code> : x == 0, y == 0 일 때</li>
    <li><code>(_, 0)</code> : y == 0이고 x는 어떤 값이든</li>
    <li><code>(0, _)</code> : x == 0이고 y는 어떤 값이든</li>
    <li><code>var (a, b)</code> : 나머지 모든 경우 → 튜플 요소를 <code>a</code>, <code>b</code>로 꺼냄</li>
  </ul>

  <h3>2-2. <code>Deconstruct</code>를 가진 클래스/struct에도 사용 가능</h3>

  <p>
    튜플뿐 아니라, <code>Deconstruct</code> 메서드를 가진 타입도 위치 패턴으로 매칭할 수 있다.
  </p>

  <pre><code class="language-csharp">class Range
{
    public int Start { get; }
    public int End   { get; }

    public Range(int start, int end) =&gt; (Start, End) = (start, end);

    public void Deconstruct(out int start, out int end)
        =&gt; (start, end) = (Start, End);
}

string Describe(Range r) =&gt; r switch
{
    (0, 0)    =&gt; "빈 구간",
    (0, &gt; 0)  =&gt; "0 이상 양수 구간",
    var (s, e) =&gt; $"[{s} ~ {e}] 구간"
};</code></pre>

  <p>
    여기서 <code>(0, &gt; 0)</code> 같은 부분은  
    “위치 패턴 + 관계 패턴(<code>&gt; 0</code>)”이 섞인 형태다.
  </p>

  <hr>

  <h2>3. 논리 패턴: <code>and</code>, <code>or</code>, <code>not</code></h2>

  <p>
    논리 패턴은 기존의 <code>&amp;&amp;</code>, <code>||</code>, <code>!</code>와 비슷한 역할을 하지만,  
    패턴 문법 안에서 자연스럽게 섞어서 쓸 수 있는 버전이다.
  </p>

  <pre><code class="language-csharp">if (n is &gt;= 0 and &lt;= 100)
{
    // 0~100 사이
}

if (n is &lt; 0 or &gt; 100)
{
    // 범위 밖
}

if (obj is not null)
{
    // null 아님
}</code></pre>

  <p>
    예를 들어 ERP 느낌으로 상태와 금액을 같이 볼 수도 있다.
  </p>

  <pre><code class="language-csharp">if (order is { Status: "CLOSE" or "CANCEL", Amount: &gt; 0 })
{
    Console.WriteLine("종료/취소 상태지만 금액은 0보다 큼");
}</code></pre>

  <ul>
    <li><code>Status: "CLOSE" or "CANCEL"</code> → 두 상태 중 하나면 매칭</li>
    <li><code>Amount: &gt; 0</code> → 금액이 0보다 큰 경우만</li>
  </ul>

  <p>이렇게 패턴만으로도 꽤 복잡한 조건을 표현할 수 있다.</p>

  <hr>

  <h2>4. 리스트 패턴(List Pattern) – 배열/리스트의 “모양”으로 매칭</h2>

  <p>
    C# 11부터 추가된 리스트 패턴은  
    <strong>배열/리스트의 형태</strong>로 매칭하는 기능이다.
  </p>

  <pre><code class="language-csharp">int[] arr = { 1, 2, 3, 4 };

string Describe(int[] xs) =&gt; xs switch
{
    []                =&gt; "빈 배열",
    [0]               =&gt; "0 하나만",
    [1, 2, 3]         =&gt; "1,2,3 딱 세 개",
    [1, ..]           =&gt; "1로 시작",
    [.., 4]           =&gt; "4로 끝",
    [_, _, _, _]      =&gt; "길이 4",
    [1, .. var rest]  =&gt; $"1로 시작 + 나머지 {rest.Length}개",
    _                 =&gt; "그 외"
};</code></pre>

  <ul>
    <li><code>[]</code> → 빈 배열</li>
    <li><code>[0]</code> → 요소가 하나이고 그 값이 0</li>
    <li><code>[1, 2, 3]</code> → 정확히 1,2,3 세 개일 때</li>
    <li><code>[1, ..]</code> → 첫 번째가 1이고 뒤에는 뭐든</li>
    <li><code>[.., 4]</code> → 마지막이 4</li>
    <li><code>[1, .. var rest]</code> → 1로 시작하고, 나머지를 <code>rest</code>로 받아서 사용</li>
  </ul>

  <p>ERP 예를 살짝 섞어 보면:</p>

  <pre><code class="language-csharp">// 특정 일자의 입출고 기록이 [입고, 출고, 출고] 순인지 체크
string CheckIoPattern(string[] iogbns) =&gt; iogbns switch
{
    ["I", "O", "O"] =&gt; "입고 후 2번 출고 패턴",
    ["I", .., "O"]  =&gt; "입고로 시작해서 출고로 끝나는 패턴",
    ["O", ..]       =&gt; "출고로 시작하는 특이 패턴",
    _               =&gt; "일반 패턴"
};</code></pre>

  <p>
    리스트 패턴을 한 번 익혀두면 시간 순서, 상태 변경 패턴 같은 것도  
    꽤 직관적으로 표현할 수 있다.
  </p>

  <hr>

  <h2>5. 재귀(Recursive) 패턴 – 패턴끼리 섞어서 쓰기</h2>

  <p>
    C# 패턴의 재미는 <strong>여러 패턴들을 섞어서</strong> 쓸 수 있다는 점이다.<br>
    속성 패턴, 확장 속성 패턴, 관계 패턴, 논리 패턴 등을 한 줄에 모아서<br>
    “조건 스펙”을 딱 표처럼 표현할 수 있다.
  </p>

  <pre><code class="language-csharp">class Order
{
    public string Status { get; set; }
    public decimal Amount { get; set; }
    public Customer Customer { get; set; }
}

class Customer
{
    public string Grade { get; set; }  // A, B, C 등급
}

string Describe(Order o) =&gt; o switch
{
    { Status: "CLOSE", Customer.Grade: "A", Amount: &gt; 1_000_000m }
        =&gt; "고액 프리미엄 종료 주문",

    { Status: "OPEN", Customer.Grade: "A" or "B" }
        =&gt; "우수 고객 진행 중 주문",

    { Status: "OPEN", Amount: &lt;= 0 }
        =&gt; "금액 없는 진행 주문",

    null
        =&gt; "null 주문",

    _
        =&gt; "일반 주문"
};</code></pre>

  <p>
    예를 들어 첫 번째 패턴:
  </p>

  <pre><code class="language-csharp">{ Status: "CLOSE", Customer.Grade: "A", Amount: &gt; 1_000_000m }</code></pre>

  <ul>
    <li><strong>속성 패턴</strong>: <code>Status</code>, <code>Amount</code></li>
    <li><strong>확장 속성 패턴</strong>: <code>Customer.Grade</code></li>
    <li><strong>관계 패턴</strong>: <code>Amount: &gt; 1_000_000m</code></li>
    <li>이 모든 걸 한 줄로 조합한 형태</li>
  </ul>

  <p>
    기존에 if/else로 길게 늘어졌던 조건문을  
    <code>switch</code> + 패턴 조합으로 정리하면  
    “조건 정의서”를 코드로 옮겨 놓은 것처럼 깔끔하게 보이는 효과가 있다.
  </p>

  <hr>

  <h2>6. 패턴 + <code>when</code> 가드</h2>

  <p>
    <code>when</code> 가드는 패턴 뒤에 “추가 필터”를 붙이고 싶을 때 사용한다.
  </p>

  <pre><code class="language-csharp">string Describe(int n) =&gt; n switch
{
    var x when x &lt; 0      =&gt; "음수",
    var x when x == 0     =&gt; "0",
    var x when x % 2 == 0 =&gt; "양수 짝수",
    _                     =&gt; "양수 홀수"
};</code></pre>

  <p>
    패턴으로 대략적인 범위를 잡고,  
    세부 조건은 <code>when</code>으로 필터링할 수도 있다.
  </p>

  <pre><code class="language-csharp">string Describe(Order o) =&gt; o switch
{
    { Status: "CLOSE" } when o.Amount == 0
        =&gt; "금액 0인 종료 주문",

    { Status: "CLOSE" }
        =&gt; "일반 종료 주문",

    _
        =&gt; "기타"
};</code></pre>

  <p>
    이런 식으로 “같은 패턴” 안에서 금액이 0인 경우와 아닌 경우를  
    <code>when</code> 가드로 나눠서 표현할 수 있다.
  </p>

  <hr>

  <h2>7. <code>foreach</code>와 튜플/패턴</h2>

  <p>
    <code>foreach</code>에서도 튜플 분해와 패턴을 같이 사용할 수 있다.
  </p>

  <h3>7-1. 튜플 분해</h3>

  <pre><code class="language-csharp">var users = new List&lt;(string Name, int Age)&gt;
{
    ("A", 10),
    ("B", 20),
    ("C", 30)
};

foreach (var (name, age) in users)
{
    Console.WriteLine($"{name} / {age}");
}</code></pre>

  <p>
    각 튜플 요소를 <code>name</code>, <code>age</code>로 바로 꺼내 쓸 수 있어  
    <code>user.Name</code>, <code>user.Age</code>보다 더 가볍게 데이터를 다룰 수 있다.
  </p>

  <h3>7-2. 튜플 + 패턴 매칭</h3>

  <pre><code class="language-csharp">foreach (var (name, age) in users)
{
    var grade = age switch
    {
        &lt; 13 =&gt; "어린이",
        &lt; 20 =&gt; "청소년",
        _    =&gt; "성인"
    };

    Console.WriteLine($"{name}는 {grade}");
}</code></pre>

  <p>
    튜플로 받은 데이터를 바로 <code>switch</code> 식 + 패턴으로 분류해서  
    코드를 간단하고 읽기 좋게 구성할 수 있다.
  </p>

  <hr>

  <h2>8. 전체 정리</h2>

  <p>C# 패턴 매칭을 머릿속에 이렇게 정리해 두면 편하다.</p>

  <h3>8-1. 어디서 쓰는가?</h3>

  <ul>
    <li><code>if (x is 패턴)</code></li>
    <li><code>switch (x) { case 패턴: ... }</code></li>
    <li><code>x switch { 패턴 =&gt; 결과, ... }</code> (switch 식)</li>
  </ul>

  <h3>8-2. 어떤 패턴들이 있는가?</h3>

  <ul>
    <li>선언 패턴: <code>is string s</code></li>
    <li><code>var</code> 패턴: <code>is var x</code></li>
    <li>discard 패턴: <code>_</code></li>
    <li>상수 패턴: <code>is 0</code>, <code>case "CLOSE":</code></li>
    <li>관계 패턴: <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code>, <code>&gt;=</code></li>
    <li>논리 패턴: <code>and</code>, <code>or</code>, <code>not</code></li>
    <li>속성 패턴: <code>{ Prop: ..., Other: ... }</code></li>
    <li>확장 속성 패턴: <code>{ A.B: ..., A.C: ... }</code></li>
    <li>위치(포지셔널) 패턴: <code>(x, y)</code>, <code>Deconstruct</code></li>
    <li>리스트 패턴: <code>[]</code>, <code>[1, ..]</code>, <code>[.., 4]</code>, <code>[1, .. var rest]</code></li>
    <li><code>when</code> 가드: 패턴 뒤에 추가 조건</li>
  </ul>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[확장 속성 패턴(Extended Property Pattern) ]]></title>
            <link>https://velog.io/@joongjii_1217/%ED%99%95%EC%9E%A5-%EC%86%8D%EC%84%B1-%ED%8C%A8%ED%84%B4Extended-Property-Pattern</link>
            <guid>https://velog.io/@joongjii_1217/%ED%99%95%EC%9E%A5-%EC%86%8D%EC%84%B1-%ED%8C%A8%ED%84%B4Extended-Property-Pattern</guid>
            <pubDate>Thu, 11 Dec 2025 07:51:15 GMT</pubDate>
            <description><![CDATA[<article>


  <hr>

  <h2>1. 먼저, “속성 패턴(Property Pattern)”이 뭔지부터</h2>

  <p>
    속성 패턴은 <strong>객체의 속성 값</strong>을 기준으로 매칭하는 패턴 매칭 문법이다.
  </p>

  <pre><code class="language-csharp">if (order is { Status: "Closed", Amount: &gt; 0 })
{
    Console.WriteLine("종료된 유효 주문");
}</code></pre>

  <p>위 코드는 이렇게 읽을 수 있다.</p>

  <ul>
    <li><code>order</code>라는 객체가 있고,</li>
    <li>그 안에 <code>Status</code> 속성이 있고 값이 <code>"Closed"</code>이고,</li>
    <li><code>Amount</code> 속성이 있고 값이 0보다 크면,</li>
    <li><strong>if 블록 안으로 들어간다</strong>.</li>
  </ul>

  <p>즉,</p>

  <pre><code class="language-csharp">order.Status == "Closed" &amp;&amp; order.Amount &gt; 0</code></pre>

  <p>
    를 패턴 매칭 문법으로 예쁘게 표현한 것이라고 보면 된다.<br>
    여기까지가 기본적인 <strong>속성 패턴</strong>이다.
  </p>

  <hr>

  <h2>2. 중첩된 객체일 때 기존 속성 패턴은 어떻게 생겼나?</h2>

  <p>
    이제 이런 모델을 가정해 보자.
  </p>

  <pre><code class="language-csharp">class Address
{
    public string City    { get; set; }
    public string Country { get; set; }
}

class Person
{
    public string Name    { get; set; }
    public Address Address { get; set; }
}</code></pre>

  <p>
    <code>Person</code> 안에 <code>Address</code>가 있고, 그 안에 다시 <code>City</code>가 있는 구조다.<br>
    이때 “서울에 사는 사람”을 체크하고 싶다고 하자.
  </p>

  <h3>2-1. C# 10 이전 스타일 (중첩 속성 패턴)</h3>

  <pre><code class="language-csharp">if (person is { Address: { City: "Seoul" } })
{
    Console.WriteLine("서울에 사는 사람");
}</code></pre>

  <p>읽어보면:</p>

  <ul>
    <li><code>person</code>은
      <ul>
        <li><code>Address</code>라는 속성을 가지고 있고,</li>
        <li>그 <code>Address</code> 속성 안에 <code>City</code>라는 속성이 있고,</li>
        <li>그 값이 <code>"Seoul"</code>이면 매칭된다.</li>
      </ul>
    </li>
  </ul>

  <p>
    동작은 이해되는데, <strong>괄호가 겹겹이 중첩</strong>되어 있어서 눈에 잘 안 들어오고,
    우리가 평소에 쓰는 <code>person.Address.City</code> 스타일과도 조금 거리가 있다.
  </p>

  <hr>

  <h2>3. 확장 속성 패턴(Extended Property Pattern) 등장</h2>

  <p>
    이런 불편함 때문에 C# 10부터 <strong>확장 속성 패턴</strong>이라는 문법이 추가되었다.<br>
    핵심 아이디어는 아주 단순하다.
  </p>

  <blockquote>
    “중첩 객체의 속성도 <code>Address.City</code>처럼 <strong>점(.)으로 이어서</strong> 쓸 수 있게 하자.”
  </blockquote>

  <h3>3-1. 같은 의미, 더 간단한 문법</h3>

  <pre><code class="language-csharp">// 옛날 방식
if (person is { Address: { City: "Seoul" } }) { ... }

// 확장 속성 패턴
if (person is { Address.City: "Seoul" }) { ... }</code></pre>

  <p>
    둘은 완전히 <strong>동일한 의미</strong>다.  
    다만 확장 속성 패턴은:
  </p>

  <ul>
    <li><code>Address</code> 속성 안으로 들어가서</li>
    <li>그 안에 있는 <code>City</code>를 바로 지정할 수 있도록</li>
  </ul>

  <p>“점(.)으로 경로를 확장해서 쓰는 속성 패턴” → Extended Property Pattern 이라고 부른다.</p>

  <hr>

  <h2>4. 확장 속성 패턴 예제 모음</h2>

  <h3>4-1. City + Country 동시에 체크하기</h3>

  <p>기존 방식:</p>

  <pre><code class="language-csharp">if (person is { Address: { City: "Seoul", Country: "KR" } })
{
    Console.WriteLine("한국 서울 거주자");
}</code></pre>

  <p>확장 속성 패턴으로 쓰면:</p>

  <pre><code class="language-csharp">if (person is { Address.City: "Seoul", Address.Country: "KR" })
{
    Console.WriteLine("한국 서울 거주자");
}</code></pre>

  <p>
    동작은 똑같지만, <code>Address.City</code>, <code>Address.Country</code>처럼  
    평소에 쓰던 프로퍼티 접근 방식과 거의 같아서 <strong>훨씬 읽기 편하다</strong>.
  </p>

  <h3>4-2. 속성 패턴 + switch 식</h3>

  <pre><code class="language-csharp">string Describe(Person p) =&gt; p switch
{
    { Address.City: "Seoul", Address.Country: "KR" }
        =&gt; "서울에 사는 한국인",

    { Address.Country: "KR" }
        =&gt; "한국에 사는 사람",

    { Address.Country: "US" }
        =&gt; "미국에 사는 사람",

    _   =&gt; "그 외"
};</code></pre>

  <p>
    원래라면 전부 <code>{ Address: { City: ..., Country: ... } }</code> 형태로 썼어야 한다.<br>
    확장 속성 패턴 덕분에, 우리가 평소에 생각하는
    <code>p.Address.City</code>, <code>p.Address.Country</code> 느낌을 그대로 패턴 안에서도 사용할 수 있게 된 것이다.
  </p>

  <hr>

  <h2>5. 확장 속성 패턴 정리</h2>

  <h3>5-1. 개념 요약</h3>

  <ul>
    <li><strong>기본 속성 패턴</strong>
      <ul>
        <li><code>obj is { Prop: value, OtherProp: &gt; 0 }</code></li>
        <li>→ 객체의 프로퍼티 값에 따라 매칭</li>
      </ul>
    </li>
    <li><strong>확장 속성 패턴</strong> (Extended Property Pattern)
      <ul>
        <li>중첩 객체에서도 <code>Address.City</code>처럼 점(.)으로 바로 접근</li>
        <li><code>obj is { Address.City: "Seoul" }</code></li>
        <li>→ <code>{ Address: { City: "Seoul" } }</code>와 동등한 표현</li>
      </ul>
    </li>
  </ul>

  <h3>5-2. 장점</h3>

  <ul>
    <li><strong>가독성 향상</strong>
      <ul>
        <li>중첩 중괄호를 줄여서 눈에 잘 들어온다.</li>
        <li>일반 C# 코드의 <code>obj.Address.City</code>와 사고방식이 거의 동일하다.</li>
      </ul>
    </li>
    <li><strong>복잡한 조건을 패턴 하나로 표현 가능</strong>
      <ul>
        <li>if-else로 늘어지던 조건을 switch + 패턴으로 깔끔하게 정리하기 좋다.</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>6. 언제 써보면 좋을까?</h2>

  <ul>
    <li>DTO / ViewModel 등이 <strong>중첩 구조</strong>를 가질 때
      <ul>
        <li>예: <code>Order.Customer.Grade</code>, <code>Order.Shipping.Address.City</code> 등</li>
      </ul>
    </li>
    <li>상태 분기를 switch 식으로 깔끔하게 정리하고 싶을 때</li>
    <li>“조건문 지옥”이 되어 있는 코드를 리팩터링하면서
      <ul>
        <li><code>if (p.Address != null &amp;&amp; p.Address.City == "Seoul") ...</code></li>
        <li>같은 코드를 패턴 매칭 기반으로 바꿔보고 싶을 때</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>7. 한 줄 요약</h2>

  <blockquote>
    확장 속성 패턴(Extended Property Pattern)은  
    <strong>“중첩 객체의 속성을 패턴 매칭에서 <code>Address.City</code>처럼 점(.)으로 편하게 표현하는 문법”</strong>이다.  
    기존 <code>{ Address: { City: ... } }</code> 를 더 읽기 쉬운 형태로 줄여준다고 생각하면 된다.
  </blockquote>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[stackalloc]]></title>
            <link>https://velog.io/@joongjii_1217/stackalloc</link>
            <guid>https://velog.io/@joongjii_1217/stackalloc</guid>
            <pubDate>Thu, 11 Dec 2025 07:44:24 GMT</pubDate>
            <description><![CDATA[<article>


  <pre><code class="language-csharp">int* p = stackalloc int[10];</code></pre>


  <pre><code class="language-csharp">Span&lt;int&gt; buffer = stackalloc int[10];</code></pre>

  <p>
    처음 보면 “이게 대체 뭐지?” 싶은데, 한 번 잡고 가면 <br>  
    <strong>스택/힙 구조, 고성능 코드</strong> 이해에 꽤 도움이 되는 키워드다.
  </p>

  <hr>

  <h2>1. <code>stackalloc</code> 한 줄 정의</h2>

  <blockquote>
    <strong><code>stackalloc</code>은 “힙(heap)이 아니라 <u>스택(stack)</u>에 작은 배열(버퍼)을 직접 만들어 쓰자”는 키워드</strong>다.
  </blockquote>

  <p>
    보통 우리는 이렇게 배열을 만든다.
  </p>

  <pre><code class="language-csharp">int[] arr = new int[10];</code></pre>

  <ul>
    <li>이 배열은 <strong>힙(Heap)</strong>에 만들어지고</li>
    <li>사용이 끝나면 <strong>가비지 컬렉터(GC)</strong>가 언젠가 치워 준다.</li>
  </ul>

  <p>
    반면 <code>stackalloc</code>을 쓰면:
  </p>

  <pre><code class="language-csharp">int* p = stackalloc int[10]; // 스택에 int 10개짜리 버퍼 생성</code></pre>

  <ul>
    <li><strong>스택(Stack)</strong>에 <code>int</code> 10개짜리 버퍼가 만들어지고</li>
    <li>해당 메서드/블록을 빠져나가면, 스택 프레임이 정리되면서 <strong>자동으로 사라진다</strong>.</li>
  </ul>

  <p>
    즉, 한 마디로 요약하면:
  </p>

  <blockquote>
    “잠깐 쓸 <strong>작은 배열</strong>을 힙 대신 스택에 올려서 <strong>빠르게 쓰고 한 번에 정리하자</strong>”
  </blockquote>

  <hr>

  <h2>2. 왜 굳이 써야 할까?</h2>

  <p>
    일반적인 업무 코드에서는 <code>stackalloc</code>을 자주 쓰지 않는다.  
    하지만 다음과 같은 상황에서는 꽤 유용하다.
  </p>

  <h3>2-1. 고성능 코드 (자주 호출되는 함수)</h3>

  <ul>
    <li>예: 초당 수십만 번 호출되는 파싱/인코딩 함수</li>
    <li>매번 <code>new byte[1024]</code>를 만들어서 쓰면:
      <ul>
        <li>힙에 계속 배열이 생기고</li>
        <li>GC가 자주 돌면서 성능에 부담이 된다.</li>
      </ul>
    </li>
    <li><code>stackalloc</code>으로 스택에 버퍼를 만들면:
      <ul>
        <li>할당/해제가 <strong>엄청 빠르다</strong> (스택 포인터만 위아래로 움직이는 수준)</li>
      </ul>
    </li>
  </ul>

  <h3>2-2. 네이티브/Interop 작업</h3>

  <ul>
    <li>C, C++ DLL(P/Invoke)을 호출할 때 임시 버퍼가 필요할 수 있다.</li>
    <li>이때 <code>byte*</code>, <code>int*</code> 같은 포인터 버퍼를 <code>stackalloc</code>으로 만들고 바로 네이티브 함수에 넘길 수 있다.</li>
  </ul>

  <h3>2-3. <code>Span&lt;T&gt;</code>와 함께 쓰는 고성능 문자열/버퍼 처리</h3>

  <ul>
    <li>예: <code>Span&lt;byte&gt; buffer = stackalloc byte[256];</code></li>
    <li>문자열 파싱, 인코딩/디코딩, 바이너리 처리 등에서 작은 임시 버퍼를 자주 쓸 때 성능에 도움이 된다.</li>
  </ul>

  <p>
    정리하면:
  </p>

  <blockquote>
    <strong>“작고, 일시적이고, 성능이 중요한 버퍼”</strong>가 필요한 경우 → <code>stackalloc</code> 후보
  </blockquote>

  <hr>

  <h2>3. 기본 문법 – 두 가지 스타일</h2>

  <h3>3-1. 포인터 기반 (unsafe 코드)</h3>

  <p>
    가장 기본 형태는 C 스타일 포인터와 함께 쓰는 것이다.
  </p>

  <pre><code class="language-csharp">unsafe
{
    int* p = stackalloc int[5];  // 스택에 int 5개짜리 버퍼 생성

    for (int i = 0; i &lt; 5; i++)
    {
        p[i] = i * 10;
    }

    for (int i = 0; i &lt; 5; i++)
    {
        Console.WriteLine(p[i]);
    }
}</code></pre>

  <ul>
    <li><code>unsafe</code> 문맥이 필요하다.</li>
    <li><code>int*</code>, <code>byte*</code> 같은 포인터 연산을 직접 쓸 수 있다.</li>
    <li>C 코드와 거의 비슷한 느낌이지만, 그만큼 실수하면 위험할 수 있다.</li>
  </ul>

  <h3>3-2. <code>Span&lt;T&gt;</code> 기반 (요즘 추천 스타일)</h3>

  <p>
    C# 7.2+에서는 <code>Span&lt;T&gt;</code>와 함께 쓰면 <strong>unsafe 없이</strong>도 <code>stackalloc</code>을 사용할 수 있다.
  </p>

  <pre><code class="language-csharp">using System;

class Program
{
    static void Main()
    {
        Span&lt;int&gt; buffer = stackalloc int[5]; // 스택에 int 5개

        for (int i = 0; i &lt; buffer.Length; i++)
        {
            buffer[i] = i * 10;
        }

        foreach (var x in buffer)
        {
            Console.WriteLine(x);
        }
    }
}</code></pre>

  <ul>
    <li><code>Span&lt;T&gt;</code>는 “연속된 메모리 블록”을 안전하게 다루기 위한 타입이다.</li>
    <li><code>stackalloc</code>으로 만든 버퍼를 <code>Span</code>으로 감싸면:
      <ul>
        <li>포인터를 직접 안 써도 되고</li>
        <li>범위 체크(인덱스 검사)도 해 줘서 더 안전하다.</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>4. 스택에 만든다는 건 어떤 의미인가?</h2>

  <p>
    “스택에 만든다”는 말은 단순히 위치만 다른 게 아니라,  
    <strong>수명, 속도, 크기 제한</strong>에서 차이가 난다는 뜻이다.
  </p>

  <h3>4-1. 수명(lifetime)이 짧다</h3>

  <ul>
    <li>스택에 올린 버퍼는 <strong>해당 메서드/블록 안에서만 유효</strong>하다.</li>
    <li>메서드에서 빠져나가는 순간 스택 프레임이 정리되면서 메모리가 같이 날아간다.</li>
    <li>그래서 <strong>바깥으로 주소/참조를 내보내면 절대 안 된다.</strong></li>
  </ul>

  <p>예를 들어, 이런 코드는 위험하다.</p>

  <pre><code class="language-csharp">unsafe int* MakeBuffer()
{
    int* p = stackalloc int[10];
    return p; // ❌ 함수가 끝나면 p가 가리키는 메모리는 이미 사라진 상태
}</code></pre>

  <h3>4-2. 할당/해제가 매우 빠르다</h3>

  <ul>
    <li>스택은 단순히 “위로 쌓았다가, 한 번에 내려오는” 구조다.</li>
    <li>버퍼를 만들 때 스택 포인터를 조금 올리고, 함수 끝날 때 한 번에 내려온다.</li>
    <li>힙 할당 + GC 부담에 비해 <strong>엄청 가볍다</strong>.</li>
  </ul>

  <h3>4-3. 크기를 너무 크게 잡으면 위험하다</h3>

  <ul>
    <li>스택은 용량이 제한적이다.</li>
    <li><code>stackalloc byte[256]</code> 정도는 괜찮지만,  
      <code>stackalloc byte[1024 * 1024]</code> 이런 건 <strong>스택 오버플로우 위험</strong>이 있다.</li>
    <li>일반적으로 <strong>수십~수백 개 원소, 몇 KB 수준 버퍼</strong>에 사용하는 것이 적당하다.</li>
  </ul>

  <hr>

  <h2>5. <code>new</code> 배열과 비교 – heap vs stack</h2>

  <p>같은 <code>int</code> 100개라도, 다음 둘은 완전히 다르게 동작한다.</p>

  <pre><code class="language-csharp">// 1) 힙에 생성
int[] arr = new int[100];

// 2) 스택에 생성
Span&lt;int&gt; buf = stackalloc int[100];</code></pre>

  <table border="1" cellpadding="6" cellspacing="0">
    <thead>
      <tr>
        <th>구분</th>
        <th><code>new int[100]</code> (Heap)</th>
        <th><code>stackalloc int[100]</code> (Stack)</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>메모리 위치</td>
        <td>힙(Heap)</td>
        <td>스택(Stack)</td>
      </tr>
      <tr>
        <td>수명</td>
        <td>변수를 더 이상 참조하지 않을 때, GC가 수거</td>
        <td>현재 메서드/블록 끝날 때 자동 소멸</td>
      </tr>
      <tr>
        <td>할당 비용</td>
        <td>상대적으로 비쌈 (힙 관리 + GC 부담)</td>
        <td>매우 빠름 (스택 포인터 이동 수준)</td>
      </tr>
      <tr>
        <td>크기 제한</td>
        <td>사실상 메모리 한도까지 가능</td>
        <td>크게 잡으면 스택 오버플로우 위험</td>
      </tr>
      <tr>
        <td>사용 난이도</td>
        <td>가장 일반적인 방식, 안전</td>
        <td>잘못 쓰면 위험 → 주의 필요</td>
      </tr>
    </tbody>
  </table>

  <hr>

  <h2>6. 사용할 때 주의할 점</h2>

  <h3>6-1. 버퍼 크기를 과하게 크게 잡지 말 것</h3>

  <ul>
    <li>수백 ~ 몇 KB 정도는 보통 괜찮지만,</li>
    <li>수 MB급 버퍼를 <code>stackalloc</code>으로 만들면 스택 오버플로우 가능성이 크다.</li>
  </ul>

  <h3>6-2. 수명(scope) 벗어난 참조 금지</h3>

  <ul>
    <li><code>stackalloc</code> 버퍼의 주소/참조를
      <ul>
        <li>필드에 저장하거나</li>
        <li>리턴 값으로 내보내거나</li>
        <li>비동기/스레드로 넘기는 것</li>
      </ul>
      전부 위험하다.</li>
    <li>현재 메서드/블록 안에서만 쓰고 끝내야 한다.</li>
  </ul>

  <h3>6-3. 진짜 필요한 곳에서만 사용</h3>

  <ul>
    <li>일반적인 비즈니스 로직(ERP 화면, CRUD, 간단 계산)에서는 그냥 <code>new</code> 배열을 쓰는 것이 더 낫다.</li>
    <li><code>stackalloc</code>은 <strong>“성능/저수준 작업용 특수 도구”</strong> 정도로 생각하는 게 좋다.</li>
  </ul>

  <hr>

  <h2>7. 언제 써볼 만한지 예시</h2>

  <ul>
    <li>초당 수십만 번 호출되는 문자열 파싱 함수에서 임시 버퍼가 필요할 때</li>
    <li>바이너리 데이터를 빠르게 처리하는 인코더/디코더 구현 시</li>
    <li>P/Invoke로 C 함수에 임시 버퍼 포인터를 넘겨야 할 때</li>
    <li><code>Span&lt;byte&gt;</code>로 문자열 조작/파싱을 최적화할 때</li>
  </ul>

  <p>
    그 외 대부분의 “일반적인” 코드에서는 <code>stackalloc</code> 없이도 충분하다.
  </p>

  <hr>

  <h2>8. 한 줄 요약</h2>

  <blockquote>
    <strong><code>stackalloc</code> = “잠깐 쓸 작은 배열을 힙이 아니라 <u>스택에 직접 만들어서</u>  
    엄청 빠르게 쓰고, 메서드 끝나면 흔적도 없이 같이 날려버리는 키워드”</strong>
  </blockquote>


</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[튜플 / out / dynamic]]></title>
            <link>https://velog.io/@joongjii_1217/%ED%8A%9C%ED%94%8C-out-dynamic</link>
            <guid>https://velog.io/@joongjii_1217/%ED%8A%9C%ED%94%8C-out-dynamic</guid>
            <pubDate>Thu, 11 Dec 2025 07:30:29 GMT</pubDate>
            <description><![CDATA[<article>

  <p>
    C# 메서드를 만들다 보면 이런 요구가 자주 나온다.
  </p>

  <blockquote>
    “계산해서 <strong>결과 두 개</strong>를 한 번에 돌려 주고 싶다.”
  </blockquote>

  <p>
    예를 들어, 예금 이자를 계산한다고 하면:
  </p>

  <ul>
    <li>① 이자 금액</li>
    <li>② 세금 뗀 후 실수령액</li>
  </ul>

  <p>
    이 두 값을 메서드 하나에서 같이 돌려줘야 한다고 하자.<br>
    C#에서는 이런 상황을 보통 세 가지 방식으로 처리할 수 있다.
  </p>

  <ul>
    <li><strong>튜플(tuple) 반환</strong></li>
    <li><strong><code>out</code> 파라미터</strong></li>
    <li><strong><code>dynamic</code> 반환</strong></li>
  </ul>

  <p>
    아래에서 각 방식을 <strong>비유 + 특징 + 언제 쓰면 좋은지</strong> 기준으로 정리해 본다.
  </p>

  <hr>

  <h2>1. 튜플 반환 – “한 박스에 묶어서 보내기”</h2>

  <h3>1-1. 개념 (비유)</h3>

  <p>
    튜플은 <strong>“작은 박스(패키지)에 값을 여러 개 넣어서 보내는 것”</strong>이라고 생각하면 쉽다.
  </p>

  <ul>
    <li>박스 안에
      <ul>
        <li>이자 금액</li>
        <li>실수령액</li>
      </ul>
      을 같이 넣어서 <strong>“한 번에”</strong> 전달한다.
    </li>
    <li>이 박스 안에는
      <ul>
        <li><code>InterestAmount</code>라는 칸(숫자 타입)</li>
        <li><code>NetAmount</code>라는 칸(숫자 타입)</li>
      </ul>
      이 있다고 <strong>컴파일 시점에 이미 정해져 있다</strong>.
    </li>
  </ul>

  <h3>1-2. 특징</h3>

  <ul>
    <li>메서드는 <strong>“반환값 하나”</strong>를 돌려준다.
      <ul>
        <li>그 “하나”가 안에서 여러 칸을 가진 <strong>작은 구조체</strong> 느낌이다.</li>
      </ul>
    </li>
    <li>각 칸의 타입이 명확하다.
      <ul>
        <li>이자 금액 칸은 숫자 타입, 실수령액 칸도 숫자 타입처럼, 타입이 고정되어 있다.</li>
        <li>문자열을 넣으려고 하면 <strong>컴파일 에러</strong>가 난다.</li>
      </ul>
    </li>
    <li>각 칸에 의미 있는 이름을 붙일 수 있다.
      <ul>
        <li><code>(decimal InterestAmount, decimal NetAmount)</code>처럼 필드 이름을 지정 가능.</li>
      </ul>
    </li>
  </ul>

  <h3>1-3. 장점</h3>

  <ul>
    <li><strong>타입 안전</strong>
      <ul>
        <li>무조건 정해진 타입/이름으로만 사용 가능 → 실수 대부분을 컴파일 단계에서 잡는다.</li>
      </ul>
    </li>
    <li><strong>가독성 좋음</strong>
      <ul>
        <li><code>result.InterestAmount</code>, <code>result.NetAmount</code>처럼 의미가 바로 보인다.</li>
      </ul>
    </li>
    <li><strong>다른 기능과 잘 어울림</strong>
      <ul>
        <li>LINQ, <code>async/await</code>, 메서드 체이닝 등과 같이 쓸 때 코드가 깔끔하다.</li>
      </ul>
    </li>
  </ul>

  <h3>1-4. 한 줄 정리</h3>

  <blockquote>
    “여러 값을 <strong>작은 튼튼한 박스</strong>에 함께 넣어서,  
    <strong>반환값 하나로</strong> 깔끔하게 돌려주는 방식”
  </blockquote>

  <hr>

  <h2>2. <code>out</code> 파라미터 – “빈 그릇을 가져오면 내가 채워 줄게”</h2>

  <h3>2-1. 개념 (비유)</h3>

  <p>
    <code>out</code>은 느낌이 다르다.  
    호출하는 쪽이 <strong>빈 그릇(변수)</strong>을 들고 온다고 생각하면 된다.
  </p>

  <ul>
    <li>그릇 A: 이자 금액을 담을 변수</li>
    <li>그릇 B: 실수령액을 담을 변수</li>
  </ul>

  <p>
    메서드는 이렇게 말하는 셈이다.
  </p>

  <blockquote>
    “네가 가져온 그릇에다가 값을 채워 줄게.  
    함수가 끝난 다음, 그 그릇 안을 보면 결과가 들어 있다.”
  </blockquote>

  <p>
    즉, 함수는 <strong>그릇(매개변수)을 수정해서 결과를 내보내는 구조</strong>이다.
  </p>

  <h3>2-2. 특징</h3>

  <ul>
    <li>함수의 <strong>return 값</strong>은 없거나, 성공/실패 정도만 반환할 수 있다.</li>
    <li>실제 결과는 <strong><code>out</code> 매개변수</strong>로 바깥에 전달된다.</li>
    <li>그릇(변수) 타입은 여전히 컴파일 시점에 고정이다.
      <ul>
        <li>이자 금액 그릇은 숫자 타입 변수여야 하고, 여기에 문자열은 넣을 수 없다.</li>
      </ul>
    </li>
  </ul>

  <h3>2-3. 장점</h3>

  <ul>
    <li>C# 초창기부터 존재하던, 오래된 전통적인 방식이다.</li>
    <li>.NET 라이브러리에서 자주 보이는 패턴이다.
      <ul>
        <li><code>int.TryParse(string, out int value)</code> 같은 형태.</li>
      </ul>
    </li>
    <li>“성공했을 때만 그릇에 값이 들어간다” 같은 패턴(TryXXX 계열)을 만들기 좋다.</li>
  </ul>

  <h3>2-4. 단점</h3>

  <ul>
    <li>호출 코드가 상대적으로 지저분해질 수 있다.
      <ul>
        <li>변수를 미리 선언하고, <code>out</code>으로 넘기고, 다시 읽는 과정이 필요하다.</li>
      </ul>
    </li>
    <li>
      값의 흐름이 한눈에 안 들어올 때가 있다.
      <ul>
        <li>겉으로 보면 “매개변수”인데 실제로는 “결과가 나가는 통로”라서 초보자에게 헷갈릴 수 있다.</li>
      </ul>
    </li>
    <li>람다/비동기 메서드와 같이 사용할 때 제약이 있다.</li>
  </ul>

  <h3>2-5. 한 줄 정리</h3>

  <blockquote>
    “호출하는 쪽이 <strong>빈 그릇(변수)</strong>을 주면,  
    함수가 그 그릇 안에 값을 채워 주는 방식.  
    결과가 <strong>반환값이 아니라 매개변수</strong>를 통해 나간다.”
  </blockquote>

  <hr>

  <h2>3. <code>dynamic</code> 반환 – “검사는 나중에, 일단 보내고 보자”</h2>

  <h3>3-1. 개념 (비유)</h3>

  <p>
    <code>dynamic</code>은 철학이 아예 다르다.
  </p>

  <ul>
    <li>역시 상자를 보내긴 하는데,
      <ul>
        <li>안에 정확히 어떤 칸이 있고, 이름이 뭔지에 대해 <strong>컴파일 시점에는 거의 검사하지 않는다</strong>.</li>
      </ul>
    </li>
    <li>호출하는 쪽은
      <ul>
        <li>“이 상자 안에는 <code>InterestAmount</code>라는 값이 있겠지?” 하고 <strong>믿고 꺼낸다</strong>.</li>
        <li>실제로 그 이름의 값이 없으면, 꺼내는 순간 실행 중에 예외가 터진다.</li>
      </ul>
    </li>
  </ul>

  <h3>3-2. 특징</h3>

  <ul>
    <li>함수가 “어떤 객체 하나”를 돌려준다.</li>
    <li>호출하는 쪽은 이를 <code>dynamic</code>으로 받아서
      <code>result.InterestAmount</code>처럼 멤버를 호출한다.</li>
    <li>해당 멤버가 실제로 존재하는지, 타입이 맞는지는 <strong>실행 시점에야</strong> 알 수 있다.</li>
    <li>컴파일러는 타입 체크를 거의 하지 않는다.
      <ul>
        <li>오타, 잘못된 멤버 이름, 잘못된 타입 사용이 전부 <strong>런타임으로 밀린다</strong>.</li>
      </ul>
    </li>
  </ul>

  <h3>3-3. 장점</h3>

  <ul>
    <li>코드가 짧고, 타입 선언 없이도 빠르게 작성할 수 있다.</li>
    <li>원래 타입이 느슨한 환경을 다룰 때 유용하다.
      <ul>
        <li>Excel/Word COM, JavaScript 엔진, JSON 응답 등</li>
      </ul>
    </li>
    <li>프로토타입이나 “일단 찍어 보고 구조 파악”할 때 쓸 수 있다.</li>
  </ul>

  <h3>3-4. 단점 (중요)</h3>

  <ul>
    <li><strong>타입 안전성이 없다시피 하다.</strong>
      <ul>
        <li><code>InterestAmount</code>를 <code>InterestAmout</code>처럼 오타 내도 컴파일은 통과한다.</li>
        <li>실제 실행 시점에 “그런 멤버 없음” 예외가 터진다.</li>
      </ul>
    </li>
    <li>프로젝트가 커질수록 디버깅이 어려워진다.</li>
    <li>리팩터링(이름 변경, 참조 찾기 등)에 대한 도구 지원이 약해질 수 있다.</li>
  </ul>

  <h3>3-5. 한 줄 정리</h3>

  <blockquote>
    “안에 뭐가 있는지 <strong>정확히 확인 안 하고</strong> 상자를 보내고,  
    받는 쪽이 <strong>믿고 꺼내다가</strong> 잘못 꺼내면  
    실행 중에 그때 가서 에러 나는 방식.”
  </blockquote>

  <hr>

  <h2>4. 세 가지를 감으로 비교해 보기</h2>

  <h3>4-1. 타입 안전성</h3>

  <ul>
    <li><strong>튜플</strong>
      <ul>
        <li>안에 몇 칸이 있고, 각 칸 타입이 뭔지가 <strong>컴파일 시점에 확정</strong>된다.</li>
        <li>타입이 안 맞으면 빌드 자체가 안 된다.</li>
      </ul>
    </li>
    <li><strong>out</strong>
      <ul>
        <li>그릇(변수) 타입이 정해져 있고, 그 타입에 맞는 값만 넣을 수 있다.</li>
        <li>이 역시 <strong>컴파일 타임에 타입 체크</strong>된다.</li>
      </ul>
    </li>
    <li><strong>dynamic</strong>
      <ul>
        <li>“검사는 나중에(런타임에) 하자” 모드다.</li>
        <li>컴파일러는 거의 막지 않기 때문에, 문제는 실행 시점에 터진다.</li>
      </ul>
    </li>
  </ul>

  <h3>4-2. 코드 구조 / 읽기 쉬운 정도</h3>

  <ul>
    <li><strong>튜플</strong>
      <ul>
        <li>“결과를 하나의 묶음으로 받는다”는 구조가 명확하다.</li>
        <li>의미 있는 필드 이름을 쓰면 비즈니스 로직도 읽기 쉽다.</li>
      </ul>
    </li>
    <li><strong>out</strong>
      <ul>
        <li>결과가 반환값이 아니라 <strong>매개변수 쪽으로 나가기 때문에</strong>, 처음 보면 흐름이 애매할 수 있다.</li>
        <li>그래도 오래된 C# 패턴이라, 어느 정도 익숙해지면 이해하기 어렵진 않다.</li>
      </ul>
    </li>
    <li><strong>dynamic</strong>
      <ul>
        <li>겉보기엔 코드가 짧고 간단하지만,</li>
        <li>실제로는 “<strong>어디서 에러 날지 눈에 안 보이는</strong>” 부담이 있다.</li>
      </ul>
    </li>
  </ul>

  <h3>4-3. 실제로 언제 쓰면 좋은가?</h3>

  <ul>
    <li><strong>튜플</strong>
      <ul>
        <li>새로 C# 코드를 짤 때,  
          “값 2~3개 함께 반환하고 싶다” → <strong>가장 먼저 고려할 대상</strong>.</li>
        <li>LINQ / async / 메서드 체인 등에 자연스럽게 연결된다.</li>
      </ul>
    </li>
    <li><strong>out</strong>
      <ul>
        <li>이미 .NET 라이브러리에서 <code>out</code> 패턴으로 제공하는 API를 쓸 때</li>
        <li>“성공 여부 + 결과 값” 패턴(<code>TryParse</code> 스타일)을 만들고 싶을 때</li>
        <li>새 코드에서 남발하기보다는, 특정 용도에만 사용하는 것이 좋다.</li>
      </ul>
    </li>
    <li><strong>dynamic</strong>
      <ul>
        <li>Excel/Word COM Interop</li>
        <li>스크립트 엔진, JSON 같은 동적 타입 세계</li>
        <li>정적 타입 정의가 오히려 부담스럽고, <strong>타입 안정성을 일부 포기해도 되는</strong> 상황에서만 신중하게 사용</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>5. 한 번에 정리</h2>

  <ul>
    <li><strong>튜플 반환</strong><br>
      → “두 개 이상 값을 <strong>하나의 박스</strong>에 깔끔하게 담아서 <strong>반환값 하나로</strong> 돌려줌”<br>
      → 요즘 C#에서 <strong>가장 추천되는 방식</strong>.</li>

<pre><code>&lt;li&gt;&lt;strong&gt;&lt;code&gt;out&lt;/code&gt; 파라미터&lt;/strong&gt;&lt;br&gt;
  → “호출하는 쪽이 &lt;strong&gt;빈 그릇&lt;/strong&gt;을 넘기면, 함수가 그 그릇에 결과를 채움”&lt;br&gt;
  → 오래된 방식이고, &lt;code&gt;TryParse&lt;/code&gt; 같은 특정 패턴에서 여전히 유용.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;&lt;code&gt;dynamic&lt;/code&gt; 반환&lt;/strong&gt;&lt;br&gt;
  → “타입/멤버 검사를 &lt;strong&gt;나중(런타임)으로 미뤄놓고&lt;/strong&gt;, 일단 돌려주고 쓰는 방식”&lt;br&gt;
  → 편하지만, &lt;strong&gt;타입 안전성이 떨어지고 버그가 런타임에 터지기 쉽다&lt;/strong&gt;는 점을 항상 기억해야 한다.&lt;/li&gt;</code></pre>  </ul>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[튜플]]></title>
            <link>https://velog.io/@joongjii_1217/%ED%8A%9C%ED%94%8C</link>
            <guid>https://velog.io/@joongjii_1217/%ED%8A%9C%ED%94%8C</guid>
            <pubDate>Thu, 11 Dec 2025 07:00:32 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 튜플이 뭐야? (한 줄 정의)</h2>

  <p>
    <strong>서로 다른 타입의 값 여러 개를 “작은 묶음”으로 만들어서, 한 변수 / 한 반환값으로 다루게 해주는 타입</strong>이다.
  </p>

  <p>예를 들어,</p>

  <ul>
    <li>이름: <code>string</code></li>
    <li>나이: <code>int</code></li>
    <li>키: <code>double</code></li>
  </ul>

  <p>
    이런 정보를 위해 <code>Person</code> 클래스를 굳이 만들지 않고, 그냥 한 번에 묶어서 쓰고 싶다면 튜플을 사용할 수 있다.
  </p>

  <pre><code class="language-csharp">(string Name, int Age, double Height) person = ("홍길동", 30, 175.5);</code></pre>

  <p>이렇게 하면 <code>person</code> 하나에 세 가지 값이 함께 들어간다.</p>

  <hr>

  <h2>2. C#에서 쓰는 두 종류의 튜플</h2>

  <p>C#에는 크게 두 세대의 튜플이 있다.</p>

  <ol>
    <li><strong>옛날 스타일 튜플</strong>: <code>Tuple&lt;T1, T2, ...&gt;</code><br>
      → 참조 타입(클래스), <code>Item1</code>, <code>Item2</code>로 접근</li>
    <li><strong>요즘 스타일 튜플</strong>: 값 튜플(ValueTuple)<br>
      → <code>(int, string)</code> 이런 식 문법, C# 7부터 본격 도입</li>
  </ol>

  <p>
    현재는 보통 <strong>2번 ValueTuple 문법</strong>을 쓰는 것이 일반적이다.
  </p>

  <hr>

  <h2>3. 기본 문법 – 값 튜플(ValueTuple)</h2>

  <h3>3-1. 만들기</h3>

  <pre><code class="language-csharp">var t = (10, "Hello");   // (int, string) 타입으로 추론됨</code></pre>

  <p>타입을 직접 명시해도 된다.</p>

  <pre><code class="language-csharp">(int, string) t = (10, "Hello");</code></pre>

  <h3>3-2. 꺼내서 쓰기 (Item1, Item2)</h3>

  <pre><code class="language-csharp">Console.WriteLine(t.Item1); // 10
Console.WriteLine(t.Item2); // Hello</code></pre>

  <p>
    이름 없이 사용할 경우 <code>Item1</code>, <code>Item2</code>와 같은 기본 이름으로 접근할 수 있다.
  </p>

  <hr>

  <h2>4. “이름 있는 튜플”이 훨씬 편하다</h2>

  <p>
    튜플 요소에는 <strong>직접 이름을 붙일 수 있다</strong>. 이 방식을 많이 사용한다.
  </p>

  <pre><code class="language-csharp">(string Name, int Age) person = ("홍길동", 30);

Console.WriteLine(person.Name); // "홍길동"
Console.WriteLine(person.Age);  // 30</code></pre>

  <p>
    이렇게 쓰면 <code>Item1</code>, <code>Item2</code> 대신 의미 있는 이름으로 접근할 수 있어 코드 가독성이 좋아진다.
  </p>

  <p><code>var</code>와 함께 쓰는 경우도 흔하다.</p>

  <pre><code class="language-csharp">var person = (Name: "홍길동", Age: 30);

Console.WriteLine(person.Name);
Console.WriteLine(person.Age);</code></pre>

  <hr>

  <h2>5. 가장 많이 쓰는 용도 1 – 메서드에서 여러 값 반환</h2>

  <p>
    C# 메서드는 원래 반환값을 하나만 돌려줄 수 있다.  
    그래서 예전에는 다음과 같은 방법을 많이 썼다.
  </p>

  <ul>
    <li><code>out</code> 매개변수</li>
    <li><code>ref</code> 매개변수</li>
    <li>별도의 DTO/클래스 생성 후 반환</li>
  </ul>

  <p>
    튜플을 쓰면 <strong>“반환값은 하나이지만, 그 안에 여러 개를 묶어서 보내는 것”</strong>이 가능해진다.
  </p>

  <h3>5-1. 예시: 나누기 결과 + 나머지를 동시에 반환</h3>

  <pre><code class="language-csharp">// 몫과 나머지를 한 번에 반환하는 메서드
static (int Quotient, int Remainder) Divide(int a, int b)
{
    int q = a / b;
    int r = a % b;
    return (q, r);  // 튜플로 반환
}</code></pre>

  <p>사용 예:</p>

  <pre><code class="language-csharp">var result = Divide(17, 5);

Console.WriteLine(result.Quotient);  // 3
Console.WriteLine(result.Remainder); // 2</code></pre>

  <h3>5-2. 더 깔끔하게: 분해(deconstruction) 문법</h3>

  <p>
    튜플을 그대로 받지 않고, <strong>변수 두 개로 바로 쪼개서 받을 수도 있다</strong>.
  </p>

  <pre><code class="language-csharp">var (q, r) = Divide(17, 5); // 튜플을 두 변수로 분해

Console.WriteLine(q); // 3
Console.WriteLine(r); // 2</code></pre>

  <p>
    실무에서 자주 쓰는 패턴이다.
  </p>

  <ul>
    <li>메서드는 <code>(T1, T2, ...)</code> 형태의 튜플을 반환하고</li>
    <li>호출하는 쪽에서는 <code>var (x, y) = ...;</code> 형태로 깔끔하게 받는다</li>
  </ul>

  <hr>

  <h2>6. 가장 많이 쓰는 용도 2 – LINQ 결과에서 임시로 묶기</h2>

  <p>
    LINQ를 사용할 때, “이 값과 저 값을 같이 들고 다니고 싶다”는 상황이 자주 나온다.
  </p>

  <pre><code class="language-csharp">var numbers = new[] { 3, 5, 7, 10 };

// 숫자와 그 숫자의 제곱을 한꺼번에 묶어서 리스트로 만들기
var list = numbers
    .Select(n => (Number: n, Square: n * n))
    .ToList();

foreach (var item in list)
{
    Console.WriteLine($"값: {item.Number}, 제곱: {item.Square}");
}</code></pre>

  <p>
    여기서 <code>(Number: n, Square: n * n)</code> 부분이 튜플이다.
  </p>

  <p>
    클래스를 만들기 애매한 간단한 상황에서,  
    <strong>“필드 2~3개짜리 임시 DTO 역할”</strong>로 자주 사용된다.
  </p>

  <hr>

  <h2>7. 가장 많이 쓰는 용도 3 – 작은 데이터 묶음 운반용</h2>

  <p>
    <q>이 정도면 굳이 클래스를 만들긴 애매한데…</q> 싶은 상황에서 튜플이 딱 맞다.
  </p>

  <pre><code class="language-csharp">(string Id, string Name, bool IsActive) userInfo = ("U001", "홍길동", true);

Console.WriteLine(userInfo.Id);
Console.WriteLine(userInfo.Name);
Console.WriteLine(userInfo.IsActive);</code></pre>

  <p>예를 들어:</p>

  <ul>
    <li>테스트 코드</li>
    <li>샘플 데이터</li>
    <li>함수 내부에서 잠깐 전달하는 중간 결과</li>
  </ul>

  <p>이럴 때 튜플은 아주 간편한 자료 구조가 된다.</p>

  <p>
    반대로, 아래처럼 <strong>의미 있는 도메인 개념</strong>일 경우:
  </p>

  <ul>
    <li><code>Order</code>, <code>Customer</code>, <code>Product</code>, <code>Invoice</code> 등</li>
  </ul>

  <p>
    이런 것들은 별도의 클래스/레코드 타입을 만드는 편이 훨씬 좋다.  
    튜플은 말 그대로 <strong>“가벼운 묶음”</strong> 정도로 생각하면 된다.
  </p>

  <hr>

  <h2>8. 옛날 스타일 Tuple&lt;T1, T2, ...&gt;와의 차이</h2>

  <p>예전에는 이런 방식의 튜플을 썼다.</p>

  <pre><code class="language-csharp">var t = Tuple.Create(10, "Hi");
Console.WriteLine(t.Item1); // 10
Console.WriteLine(t.Item2); // Hi</code></pre>

  <ul>
    <li><code>Tuple&lt;T1, T2&gt;</code> : <strong>참조 타입(클래스)</strong></li>
    <li>값 튜플 <code>(int, string)</code> : <strong>struct(값 타입)</strong></li>
  </ul>

  <p>
    값 튜플은 C# 7부터 본격 도입되었고, 문법이 훨씬 깔끔하며 성능도 더 좋다.<br>
    요즘에는 특별한 이유가 없다면 <code>Tuple&lt;...&gt;</code>보다는  
    <code>(int, string)</code> 같은 <strong>값 튜플 스타일</strong>을 쓰는 것이 일반적이다.
  </p>

  <hr>

  <h2>9. 튜플 vs 클래스를 언제 쓸까?</h2>

  <p>간단하게 다음 기준으로 생각하면 편하다.</p>

  <h3>튜플을 쓰기 좋은 경우</h3>

  <ul>
    <li>잠깐 쓰는 데이터 묶음</li>
    <li>메서드에서 반환값 여러 개를 한 번에 돌려주고 싶을 때</li>
    <li>테스트/샘플/로컬 데이터</li>
    <li>코드 한 함수 또는 한 파일 내부에서만 쓰는 임시 DTO 느낌</li>
  </ul>

  <h3>클래스를 만드는 게 좋은 경우</h3>

  <ul>
    <li>해당 데이터가 <strong>도메인 개념</strong>인 경우 (Order, User, Customer 등)</li>
    <li>여러 군데에서 재사용되는 타입일 때</li>
    <li>필드/기능이 앞으로 더 늘어날 가능성이 있을 때</li>
    <li>의미를 명확하게 표현하고 싶을 때</li>
  </ul>

  <hr>

  <h2>10. 한 번에 정리</h2>

  <ul>
    <li>튜플 = <strong>여러 값을 하나로 묶는 작은 패키지</strong></li>
    <li>C# 7 이후 스타일 예:
      <pre><code class="language-csharp">(int Id, string Name) user = (1, "홍길동");
Console.WriteLine(user.Id);
Console.WriteLine(user.Name);</code></pre>
    </li>
    <li>메서드에서 여러 값을 반환할 때:
      <pre><code class="language-csharp">(int q, int r) Divide(int a, int b) =&gt; (a / b, a % b);
var (q, r) = Divide(17, 5);</code></pre>
    </li>
    <li>LINQ/컬렉션에서 <strong>간단한 DTO 역할</strong>로 매우 자주 사용된다.</li>
  </ul>

  <p>
    튜플은 “<strong>클래스를 만들기엔 너무 무겁고, 값 하나로는 부족한 상황</strong>”에서 빛을 발한다.<br>
    가볍게 여러 값을 묶고 싶을 때, 먼저 떠올려볼 만한 도구라고 보면 된다.
  </p>
</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[람다]]></title>
            <link>https://velog.io/@joongjii_1217/%EB%9E%8C%EB%8B%A4</link>
            <guid>https://velog.io/@joongjii_1217/%EB%9E%8C%EB%8B%A4</guid>
            <pubDate>Tue, 09 Dec 2025 07:53:45 GMT</pubDate>
            <description><![CDATA[<article>


  <h2>1. 람다 메서드를 위한 전용 델리게이트 관점</h2>

  <h3>1-1. 람다는 결국 “메서드 조각”이다</h3>

  <p>
    람다는 한 줄로 말하면 이렇게 볼 수 있다.
  </p>

  <blockquote>
    람다식 = 어딘가에 넘겨줄 “작은 메서드 조각”을 코드 안에서 바로 적는 문법
  </blockquote>

  <p>
    그리고 이 “작은 메서드”를 담는 그릇이 바로 <strong>델리게이트 타입(delegate)</strong>이다.
  </p>

  <pre><code class="language-csharp">// 전용 델리게이트 타입 선언
delegate bool NumberTest(int n);

class Program
{
    static void Main()
    {
        NumberTest isEven = n =&gt; n % 2 == 0;   // 람다를 델리게이트에 담음
        NumberTest isOdd  = n =&gt; n % 2 != 0;

        Console.WriteLine(isEven(10)); // true
        Console.WriteLine(isOdd(10));  // false
    }
}</code></pre>

  <ul>
    <li><code>delegate bool NumberTest(int n);</code>  
      → “<strong>int 하나 받아서 bool을 반환하는 함수</strong>를 담는 전용 타입”</li>
    <li><code>n =&gt; n % 2 == 0</code>  
      → 이 부분이 바로 “<strong>익명 메서드 본문</strong>” 역할을 하는 람다식</li>
    <li><code>NumberTest isEven = ...</code>  
      → “이 델리게이트 변수는 이 람다 메서드를 가리킨다”는 뜻</li>
  </ul>

  <p>
    정리하면, 람다 자체는 <strong>익명 메서드</strong>이고,<br>
    그걸 담는 타입이 <strong>델리게이트</strong>라고 보면 된다.
  </p>

  <hr>

  <h3>1-2. 전용 델리게이트를 만들어 두면 좋은 이유</h3>

  <p>
    어떤 공통 함수를 만들고, “조건”만 바꿔가며 쓰고 싶을 때 전용 델리게이트가 유용하다.
  </p>

  <pre><code class="language-csharp">delegate bool NumberTest(int n);

static int Count(int[] numbers, NumberTest test)
{
    int count = 0;
    foreach (int n in numbers)
    {
        if (test(n))
            count++;
    }
    return count;
}</code></pre>

  <p>사용 예:</p>

  <pre><code class="language-csharp">int[] arr = { 1, 2, 3, 4, 5, 6 };

// 1) 일반 메서드 전달
static bool IsEven(int n) =&gt; n % 2 == 0;

int evenCount1 = Count(arr, IsEven);

// 2) 람다식으로 바로 조건 전달
int evenCount2 = Count(arr, n =&gt; n % 2 == 0);
int overThree  = Count(arr, n =&gt; n &gt; 3);</code></pre>

  <ul>
    <li><code>Count</code>는 “배열을 돌면서 조건에 맞는 것만 세는 공통 함수”</li>
    <li>어떤 조건으로 셀지는 <code>NumberTest</code> 델리게이트에 람다를 넣어 바꿔준다</li>
    <li>조건을 바꿔 끼우는 플러그인처럼 람다를 사용하는 느낌</li>
  </ul>

  <p>
    이 패턴이 확장된 게 <code>Predicate&lt;T&gt;</code>, <code>Comparison&lt;T&gt;</code> 같은 델리게이트들이다.
  </p>

  <hr>

  <h3>1-3. 직접 만든 델리게이트 vs Func / Action / Predicate</h3>

  <p>
    매번 <code>delegate ...</code>를 선언하기 귀찮으니까, .NET에서는 미리 여러 델리게이트 타입을 만들어두었다.
  </p>

  <ul>
    <li><code>Action</code>, <code>Action&lt;T&gt;</code>, <code>Action&lt;T1,T2,...&gt;</code> → 반환값 없음</li>
    <li><code>Func&lt;T1,...,TResult&gt;</code> → 마지막 타입이 반환형</li>
    <li><code>Predicate&lt;T&gt;</code> → 실질적으로 <code>Func&lt;T,bool&gt;</code>과 같은 역할</li>
  </ul>

  <p>예를 들어, 이런 전용 델리게이트를:</p>

  <pre><code class="language-csharp">delegate bool NumberTest(int n);</code></pre>

  <p>굳이 안 만들고 이렇게도 쓸 수 있다:</p>

  <pre><code class="language-csharp">Func&lt;int, bool&gt; test1 = n =&gt; n % 2 == 0;
Predicate&lt;int&gt; test2 = n =&gt; n &gt; 10;</code></pre>

  <p>
    요약하면:
  </p>

  <ul>
    <li>람다를 쓰려면 **어딘가에 담겨야 하는데**, 그 그릇이 델리게이트 타입이다.</li>
    <li>직접 선언한 전용 델리게이트: <code>delegate bool NumberTest(int n);</code></li>
    <li>미리 제공되는 전용 델리게이트: <code>Func</code>, <code>Action</code>, <code>Predicate&lt;T&gt;</code> 등</li>
    <li>람다 = 그 델리게이트에 들어가는 익명 메서드</li>
  </ul>

  <hr>

  <h2>2. 컬렉션과 람다 메서드 관점</h2>

  <p>
    두 번째 축은 <strong>컬렉션(List, 배열, LINQ)에서 람다를 어떻게 쓰는지</strong>이다.<br>
    실무에서는 이 부분에서 람다가 가장 많이 등장한다.
  </p>

  <h3>2-1. List&lt;T&gt; 메서드 + 람다</h3>

  <p><code>List&lt;T&gt;</code>에는 델리게이트를 받는 메서드가 아주 많다.</p>

  <ul>
    <li><code>Find(Predicate&lt;T&gt; match)</code></li>
    <li><code>FindAll(Predicate&lt;T&gt; match)</code></li>
    <li><code>Exists(Predicate&lt;T&gt; match)</code></li>
    <li><code>RemoveAll(Predicate&lt;T&gt; match)</code></li>
    <li><code>Sort(Comparison&lt;T&gt; comparison)</code></li>
    <li><code>ForEach(Action&lt;T&gt; action)</code></li>
  </ul>

  <p>모두 “델리게이트 인수”를 받기 때문에, 여기에 람다를 바로 넘겨줄 수 있다.</p>

  <h4>(1) Find / FindAll / Exists / RemoveAll 예제</h4>

  <pre><code class="language-csharp">var list = new List&lt;int&gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// 1) Find: 조건을 만족하는 첫 번째 원소
int firstEven = list.Find(n =&gt; n % 2 == 0);

// 2) FindAll: 조건을 만족하는 모든 원소
List&lt;int&gt; evenList = list.FindAll(n =&gt; n % 2 == 0);

// 3) Exists: 조건을 만족하는 게 하나라도 있는지
bool hasBig = list.Exists(n =&gt; n &gt; 100);

// 4) RemoveAll: 조건을 만족하는 원소 모두 삭제
int removedCount = list.RemoveAll(n =&gt; n % 2 == 0);</code></pre>

  <ul>
    <li><code>n =&gt; n % 2 == 0</code> → <code>Predicate&lt;int&gt;</code> 타입에 맞는 람다식</li>
    <li>“이 숫자가 조건에 맞는지?”를 검사하는 작은 함수를 바로 인수로 넘긴 셈</li>
  </ul>

  <h4>(2) Sort + Comparison&lt;T&gt; 예제</h4>

  <pre><code class="language-csharp">var people = new List&lt;(string Name, int Age)&gt;
{
    ("Kim", 30),
    ("Lee", 20),
    ("Park", 40),
};

// 나이 기준 오름차순 정렬
people.Sort((a, b) =&gt; a.Age.CompareTo(b.Age));

// 이름 기준 내림차순 정렬
people.Sort((a, b) =&gt; string.Compare(b.Name, a.Name, StringComparison.Ordinal));</code></pre>

  <p>
    여기서 <code>(a, b) =&gt; ...</code> 부분이 바로 <code>Comparison&lt;T&gt;</code> 델리게이트에 전달되는 람다 메서드이다.
  </p>

  <hr>

  <h3>2-2. LINQ와 람다 – 컬렉션 처리의 핵심 조합</h3>

  <p>
    LINQ 확장 메서드들도 결국<br>
    <strong>“제네릭 메서드 + 델리게이트 인수 + 람다 전달”</strong> 구조이다.
  </p>

  <p>대표적인 메서드들:</p>

  <ul>
    <li><code>Where(Func&lt;T, bool&gt;)</code></li>
    <li><code>Select(Func&lt;TSource, TResult&gt;)</code></li>
    <li><code>Any(Func&lt;T, bool&gt;)</code></li>
    <li><code>All(Func&lt;T, bool&gt;)</code></li>
    <li><code>OrderBy(Func&lt;T, TKey&gt;)</code></li>
    <li><code>GroupBy(Func&lt;T, TKey&gt;)</code></li>
  </ul>

  <h4>(1) Where + Select + ToList 간단 예제</h4>

  <pre><code class="language-csharp">var numbers = new List&lt;int&gt; { 1, 2, 3, 4, 5, 6 };

// 짝수만 고르고(Where), 제곱으로 바꾸고(Select), 리스트로 만들기(ToList)
var evenSquares = numbers
    .Where(n =&gt; n % 2 == 0)   // Func&lt;int, bool&gt;
    .Select(n =&gt; n * n)       // Func&lt;int, int&gt;
    .ToList();                // List&lt;int&gt;</code></pre>

  <ul>
    <li><code>Where</code> 안의 <code>n =&gt; n % 2 == 0</code> → <code>Func&lt;int,bool&gt;</code> 델리게이트에 들어가는 람다</li>
    <li><code>Select</code> 안의 <code>n =&gt; n * n</code> → <code>Func&lt;int,int&gt;</code> 델리게이트에 들어가는 람다</li>
  </ul>

  <h4>(2) 실무 스타일 예: 주문 목록 필터링</h4>

  <pre><code class="language-csharp">class Order
{
    public string CustomerCode { get; set; }
    public decimal Amount { get; set; }
    public bool IsCanceled { get; set; }
}

var orders = new List&lt;Order&gt;
{
    new Order { CustomerCode = "C001", Amount = 100_000m, IsCanceled = false },
    new Order { CustomerCode = "C001", Amount =  50_000m, IsCanceled = true  },
    new Order { CustomerCode = "C002", Amount = 200_000m, IsCanceled = false },
};

// 1) 취소되지 않은 주문만
var validOrders = orders
    .Where(o =&gt; !o.IsCanceled)
    .ToList();

// 2) 특정 거래처(C001) + 10만원 이상 주문만
var filtered = orders
    .Where(o =&gt; o.CustomerCode == "C001" &amp;&amp; o.Amount &gt;= 100_000m)
    .ToList();

// 3) 거래처별 매출 합계
var sumByCustomer = orders
    .Where(o =&gt; !o.IsCanceled)
    .GroupBy(o =&gt; o.CustomerCode)
    .Select(g =&gt; new
    {
        Customer = g.Key,
        Total = g.Sum(o =&gt; o.Amount)
    })
    .ToList();</code></pre>

  <p>
    여기 있는 모든 <code>=&gt;</code> 뒤의 코드들이 다 <strong>람다 메서드</strong>이고,<br>
    각각 <code>Func&lt;Order,bool&gt;</code>, <code>Func&lt;Order,string&gt;</code>, <code>Func&lt;Order,decimal&gt;</code> 등의 델리게이트 자리에 들어간다.
  </p>

  <hr>

  <h2>3. 두 관점을 한 번에 정리</h2>

  <h3>3-1. [람다 메서드를 위한 전용 델리게이트] 관점</h3>

  <ul>
    <li>람다식은 결국 <strong>델리게이트에 들어가는 익명 메서드</strong>다.</li>
    <li>델리게이트는 “이런 모양의 함수를 받을게”라고 선언하는 타입이다.
      <ul>
        <li>직접 만든 전용 델리게이트: <code>delegate bool NumberTest(int n);</code></li>
        <li>미리 제공된 델리게이트: <code>Func</code>, <code>Action</code>, <code>Predicate&lt;T&gt;</code>, <code>Comparison&lt;T&gt;</code> 등</li>
      </ul>
    </li>
    <li>
      공통 함수(<code>Count</code>, <code>Filter</code>, <code>Process</code> 등)에<br>
      “동작(조건/계산)을 나중에 결정하는 플러그인”으로 람다를 꽂는 느낌으로 쓰면 된다.
    </li>
  </ul>

  <h3>3-2. [컬렉션과 람다 메서드] 관점</h3>

  <ul>
    <li><code>List&lt;T&gt;</code>의 <code>Find</code>, <code>FindAll</code>, <code>Exists</code>, <code>Sort</code>, <code>RemoveAll</code>, <code>ForEach</code> 등은
      모두 델리게이트 인수를 받는다.</li>
    <li>여기에 람다를 넘겨서 “조건/정렬 기준/실행 동작”을 편하게 지정할 수 있다.</li>
    <li>LINQ의 <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> 등은
      <strong>제네릭 메서드 + Func 델리게이트 + 람다</strong>의 조합이다.</li>
    <li>실무에서 람다를 가장 많이 보는 곳은 결국
      <strong>“컬렉션 + 람다 = 데이터 필터링/변환/집계”</strong> 영역이다.</li>
  </ul>

  <p>
    이 두 관점을 머릿속에 같이 두면,
  </p>

  <blockquote>
    “람다 = 델리게이트에 들어가는 작은 메서드”<br>
    “컬렉션/ LINQ = 그 작은 메서드를 실전에서 쓰는 장소”
  </blockquote>

  <p>
    이렇게 연결돼서 훨씬 이해하기 쉬워진다.
  </p>
</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[await     -> try catch]]></title>
            <link>https://velog.io/@joongjii_1217/await-try-catch</link>
            <guid>https://velog.io/@joongjii_1217/await-try-catch</guid>
            <pubDate>Tue, 09 Dec 2025 07:45:27 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 한 줄 정리부터</h2>

  <blockquote>
    <strong>
      <code>await</code>로 기다리는 비동기 메서드에서 예외가 발생하면,<br>
      그 예외는 <code>await</code>가 있는 줄에서 터지는 것처럼 동작하고,<br>
      그 주변의 <code>try / catch</code>로 잡을 수 있다.
    </strong>
  </blockquote>

  <p>
    즉, “비동기라서 예외 처리가 완전 다르다”가 아니라,<br>
    <strong>“예외가 <code>await</code> 줄에서 터진다고 생각하면 된다”</strong> 정도로 이해하면 편하다.
  </p>

  <hr>

  <h2>2. 동기 코드 vs 비동기 코드 비교</h2>

  <h3>2-1. 동기 코드에서의 <code>try / catch</code></h3>

  <pre><code class="language-csharp">try
{
    DoSomething();  // 여기서 예외 나면 바로 catch로 감
}
catch (Exception ex)
{
    Console.WriteLine("에러 발생: " + ex.Message);
}</code></pre>

  <p>
    <code>DoSomething()</code> 안에서 예외가 발생하면,<br>
    바로 위의 <code>try / catch</code> 블록으로 예외가 전파된다.
  </p>

  <h3>2-2. 비동기 코드에서의 <code>await</code> + <code>try / catch</code></h3>

  <pre><code class="language-csharp">try
{
    await DoSomethingAsync();  // 이 줄에서 Task가 완료될 때 예외가 있으면 여기서 throw
}
catch (Exception ex)
{
    Console.WriteLine("에러 발생: " + ex.Message);
}</code></pre>

  <p>
    비동기 메서드 안에서 예외가 나면:
  </p>

  <ol>
    <li>예외가 바로 밖으로 튀어나오는 게 아니라, <strong>Task 안에 저장된다</strong>.</li>
    <li>나중에 그 Task를 <code>await</code>할 때, <strong>그 줄에서 예외를 다시 던진다(throw)</strong>.</li>
    <li>그래서 <code>await</code>를 둘러싼 <code>try / catch</code>에서 예외를 잡을 수 있다.</li>
  </ol>

  <p>
    결국 “예외는 <strong><code>await</code> 줄에서</strong> 터진다”고 보면 된다.
  </p>

  <hr>

  <h2>3. 기본 예제로 흐름 이해하기</h2>

  <pre><code class="language-csharp">static async Task Main()
{
    try
    {
        Console.WriteLine("요청 시작");
        string result = await DownloadDataAsync();  // 여기에서 예외가 다시 throw 됨
        Console.WriteLine("결과: " + result);
    }
    catch (Exception ex)
    {
        Console.WriteLine("예외 잡음: " + ex.Message);
    }

    Console.WriteLine("메인 계속 실행");
}

static async Task&lt;string&gt; DownloadDataAsync()
{
    // 예를 들어 HttpClient 호출 같은 비동기 작업이라고 가정
    await Task.Delay(1000); // 네트워크 대기라고 생각

    // 일부러 에러 발생
    throw new InvalidOperationException("다운로드 중 오류 발생!");
}</code></pre>

  <p>실행 흐름을 찬찬히 따라가면:</p>

  <ol>
    <li><code>DownloadDataAsync()</code>를 호출 → <code>Task&lt;string&gt;</code>를 반환</li>
    <li><code>await</code>가 이 Task가 끝날 때까지 기다린다.</li>
    <li><code>DownloadDataAsync</code> 내부에서 <code>throw</code> 발생.</li>
    <li>예외 정보가 Task 안에 저장된다.</li>
    <li>Task 완료 후 <code>await</code> 지점으로 돌아올 때, 그 예외를 다시 던진다.</li>
    <li>바깥의 <code>try / catch</code>에서 이 예외를 잡는다.</li>
  </ol>

  <p>
    그래서 “예외는 DownloadDataAsync 안에서 났지만,<br>
    실제로 잡는 위치는 <strong><code>await DownloadDataAsync()</code> 줄의 <code>catch</code></strong>라고 보면 된다.
  </p>

  <hr>

  <h2>4. 예외를 어디서 잡을지에 따른 패턴</h2>

  <h3>4-1. async 메서드 안에서 직접 처리</h3>

  <pre><code class="language-csharp">static async Task DownloadAndPrintAsync()
{
    try
    {
        string result = await DownloadDataAsync();
        Console.WriteLine("결과: " + result);
    }
    catch (Exception ex)
    {
        Console.WriteLine("DownloadAndPrintAsync 내부에서 처리: " + ex.Message);
    }
}</code></pre>

  <ul>
    <li>이 경우, 예외는 <code>DownloadAndPrintAsync</code> 안에서 끝까지 처리된다.</li>
    <li>호출하는 쪽에서는 예외가 올라오지 않는다.</li>
  </ul>

  <h3>4-2. 호출하는 쪽(상위)에서 처리</h3>

  <pre><code class="language-csharp">static async Task DownloadAndPrintAsync()
{
    // 여기서는 예외를 처리하지 않고 위로 올림
    string result = await DownloadDataAsync(); 
    Console.WriteLine("결과: " + result);
}

static async Task Main()
{
    try
    {
        await DownloadAndPrintAsync();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Main에서 예외 처리: " + ex.Message);
    }
}</code></pre>

  <ul>
    <li>여기서는 <code>DownloadAndPrintAsync</code> 안에서 예외 처리 없이 그대로 던진다.</li>
    <li>최상위 (<code>Main</code> 또는 UI 레이어)에서 한 번에 예외를 처리하는 구조.</li>
  </ul>

  <p>
    결국 <strong>“예외를 어디 레벨에서 책임지고 처리할 것인가?”</strong>를 설계하는 문제다.
  </p>

  <hr>

  <h2>5. <code>await</code>를 안 쓰면 어떻게 되나? (중요 포인트)</h2>

  <pre><code class="language-csharp">// ❌ 이렇게 하면 예외가 try/catch에서 안 잡힐 수 있음
try
{
    Task t = DoSomethingAsync();  // Task만 만든 상태, await 안 함
    // 다른 작업...
}
catch (Exception ex)
{
    Console.WriteLine("여기선 예외 못 잡음");
}</code></pre>

  <ul>
    <li><code>await</code>를 하지 않으면, 예외가 **Task 안에서만** 발생하고 호출 스택으로 올라오지 않는다.</li>
    <li>나중에 <code>t.Wait()</code>나 <code>t.Result</code>를 사용할 때 예외가 튀어나오는데,<br>
        이때는 <code>AggregateException</code> 같은 형태로 감싸져서 나와서 더 복잡해진다.</li>
  </ul>

  <p>
    그래서 <strong>“비동기 예외를 <code>try / catch</code>로 제대로 처리하고 싶으면 반드시 <code>await</code> 해야 한다”</strong>라고 생각하면 편하다.
  </p>

  <hr>

  <h2>6. 여러 비동기 작업을 동시에 기다릴 때 (Task.WhenAll)</h2>

  <p>
    여러 개 Task를 동시에 돌려놓고 한 번에 기다릴 때도 패턴은 똑같다.
  </p>

  <pre><code class="language-csharp">try
{
    Task t1 = DoWorkAsync(1);
    Task t2 = DoWorkAsync(2);
    Task t3 = DoWorkAsync(3);

    await Task.WhenAll(t1, t2, t3);  // 여기서 여러 Task 예외가 한 번에 표출
}
catch (Exception ex)
{
    Console.WriteLine("WhenAll에서 예외 잡음: " + ex.Message);
}</code></pre>

  <ul>
    <li><code>Task.WhenAll</code>에 들어간 Task 중 하나라도 예외가 나면,</li>
    <li><code>await Task.WhenAll(...)</code> 줄에서 예외가 터진다.</li>
    <li>역시 그 줄을 <code>try / catch</code>로 감싸면 예외를 잡을 수 있다.</li>
    <li>실제로는 여러 예외가 한 번에 발생할 수도 있어서 내부적으로 <code>AggregateException</code>에 담기기도 한다.<br>
        처음에는 “여러 개 Task → <code>WhenAll</code>에서 한 번에 터진다” 정도만 기억해 두면 충분하다.</li>
  </ul>

  <hr>

  <h2>7. 자주 쓰는 패턴 정리</h2>

  <h3>패턴 1) async 메서드 안에서 try / catch</h3>

  <pre><code class="language-csharp">static async Task DoSomethingSafeAsync()
{
    try
    {
        await DoSomethingDangerousAsync();
    }
    catch (Exception ex)
    {
        // 여기서 로그 찍고 끝내기
        Console.WriteLine("내부 처리: " + ex.Message);
    }
}</code></pre>

  <h3>패턴 2) async 메서드는 그냥 던지고, 호출자에서 처리</h3>

  <pre><code class="language-csharp">static async Task DoSomethingAsync()
{
    await DoSomethingDangerousAsync(); // 예외를 위로 던짐
}

static async Task Main()
{
    try
    {
        await DoSomethingAsync();
    }
    catch (Exception ex)
    {
        Console.WriteLine("최상위에서 처리: " + ex.Message);
    }
}</code></pre>

  <p>
    상황에 따라 “어디서 예외를 책임질지”를 선택해서 이 두 패턴을 섞어 쓰게 된다.
  </p>

  <hr>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[제네릭 메서드(Generic Method)]]></title>
            <link>https://velog.io/@joongjii_1217/%EC%A0%9C%EB%84%A4%EB%A6%AD-%EB%A9%94%EC%84%9C%EB%93%9CGeneric-Method</link>
            <guid>https://velog.io/@joongjii_1217/%EC%A0%9C%EB%84%A4%EB%A6%AD-%EB%A9%94%EC%84%9C%EB%93%9CGeneric-Method</guid>
            <pubDate>Tue, 09 Dec 2025 07:29:16 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 제네릭 메서드란 무엇인가?</h2>

  <p>
    한 줄로 정리하면:
  </p>

  <blockquote>
    <strong>“메서드 단위에서 타입을 외부에서 받는 메서드”</strong>
  </blockquote>

  <p>
    제네릭 클래스는 <code>class MyClass&lt;T&gt;</code>처럼 <strong>클래스 전체</strong>가 타입 매개변수 T를 공유하지만,<br>
    제네릭 메서드는 <strong>특정 메서드 하나만</strong> 타입 매개변수를 가진다.
  </p>

  <p>기본 형태는 이렇게 생겼다:</p>

  <pre><code class="language-csharp">반환형 메서드이름&lt;T&gt;(매개변수들...)
{
    // T를 타입처럼 사용
}</code></pre>

  <p>예를 들어:</p>

  <pre><code class="language-csharp">static void PrintTwice&lt;T&gt;(T value)
{
    Console.WriteLine(value);
    Console.WriteLine(value);
}</code></pre>

  <p>사용 예:</p>

  <pre><code class="language-csharp">PrintTwice&lt;int&gt;(10);        // T = int
PrintTwice&lt;string&gt;("Hi");   // T = string
PrintTwice(3.14);            // T를 컴파일러가 double로 추론</code></pre>

  <ul>
    <li><code>PrintTwice&lt;T&gt;</code> 가 제네릭 메서드</li>
    <li><code>&lt;T&gt;</code>가 <strong>형식 매개변수(Type Parameter)</strong></li>
    <li><code>PrintTwice&lt;int&gt;</code> 호출 시 <code>T</code>는 <code>int</code></li>
    <li>대부분은 <code>PrintTwice(3.14)</code>처럼 쓰면 <strong>컴파일러가 T를 자동으로 추론</strong>해 준다</li>
  </ul>

  <hr>

  <h2>2. 제네릭 “클래스”와 제네릭 “메서드” 차이</h2>

  <h3>2-1. 제네릭 클래스</h3>

  <pre><code class="language-csharp">class Box&lt;T&gt;
{
    public T Value;
}</code></pre>

  <pre><code class="language-csharp">var intBox = new Box&lt;int&gt;();
intBox.Value = 10;

var strBox = new Box&lt;string&gt;();
strBox.Value = "Hello";</code></pre>

  <ul>
    <li>클래스 수준에서 <code>T</code>를 받는다.</li>
    <li>클래스 안의 필드, 프로퍼티, 메서드 모두가 같은 <code>T</code>를 공유한다.</li>
  </ul>

  <h3>2-2. 제네릭 메서드</h3>

  <pre><code class="language-csharp">class Util
{
    public static void PrintTwice&lt;T&gt;(T value)
    {
        Console.WriteLine(value);
        Console.WriteLine(value);
    }
}</code></pre>

  <ul>
    <li><code>Util</code> 클래스 자체는 제네릭이 아니다.</li>
    <li><strong>메서드 <code>PrintTwice&lt;T&gt;</code>만</strong> 제네릭이다.</li>
    <li>각 호출마다 T를 다르게 줄 수 있다.</li>
  </ul>

  <h3>2-3. 클래스 제네릭 + 메서드 제네릭 같이 쓰는 경우</h3>

  <pre><code class="language-csharp">class Repository&lt;T&gt;
{
    public T GetById(int id)
    {
        // ...
        return default;
    }

    // 메서드만 따로 제네릭
    public U Convert&lt;U&gt;(T source)
    {
        // 여기서 U는 메서드 전용 타입 매개변수
        return default;
    }
}</code></pre>

  <ul>
    <li><code>T</code>는 클래스 전체에서 사용하는 타입</li>
    <li><code>U</code>는 <strong>Convert 메서드 전용 타입</strong></li>
    <li>둘은 서로 독립적이다 (이름만 다를 뿐 둘 다 타입 매개변수)</li>
  </ul>

  <hr>

  <h2>3. 제네릭 메서드 기본 문법 예제</h2>

  <h3>3-1. 가장 단순한 형태 – Echo</h3>

  <pre><code class="language-csharp">static T Echo&lt;T&gt;(T value)
{
    return value;
}</code></pre>

  <pre><code class="language-csharp">int a = Echo&lt;int&gt;(10);        // 10
string s = Echo("Hello");      // T를 string으로 자동 추론</code></pre>

  <ul>
    <li>어떤 타입이 오든 그대로 되돌려주는 메서드</li>
    <li>유틸리티/헬퍼 코드를 만들 때 자주 쓰는 패턴</li>
  </ul>

  <h3>3-2. 타입 매개변수 여러 개</h3>

  <pre><code class="language-csharp">static Dictionary&lt;TKey, TValue&gt; MakeDic&lt;TKey, TValue&gt;(TKey key, TValue value)
{
    var dic = new Dictionary&lt;TKey, TValue&gt;();
    dic[key] = value;
    return dic;
}</code></pre>

  <pre><code class="language-csharp">var dic1 = MakeDic&lt;int, string&gt;(1, "One");
var dic2 = MakeDic("ID", 100);   // 타입 추론 → TKey=string, TValue=int</code></pre>

  <hr>

  <h2>4. 제네릭 메서드 + 제약조건(where)</h2>

  <p>
    제네릭 클래스처럼, 제네릭 메서드도 <strong>형식 매개변수 제약조건</strong>을 걸 수 있다.
  </p>

  <h3>4-1. new() 제약 – 기본 생성자 필요</h3>

  <pre><code class="language-csharp">static T CreateAndLog&lt;T&gt;() where T : new()
{
    T obj = new T();
    Console.WriteLine(typeof(T).Name + " 생성됨");
    return obj;
}</code></pre>

  <pre><code class="language-csharp">class User
{
    public User() { }  // 기본 생성자
}

var user = CreateAndLog&lt;User&gt;();</code></pre>

  <ul>
    <li><code>where T : new()</code> 덕분에 <code>new T()</code> 사용 가능</li>
    <li>T는 반드시 public 기본 생성자를 가져야 한다</li>
  </ul>

  <h3>4-2. 인터페이스 제약</h3>

  <pre><code class="language-csharp">interface IHasName
{
    string Name { get; set; }
}

static void PrintName&lt;T&gt;(T obj) where T : IHasName
{
    Console.WriteLine(obj.Name);
}</code></pre>

  <pre><code class="language-csharp">class User : IHasName
{
    public string Name { get; set; }
}

PrintName(new User { Name = "홍길동" });</code></pre>

  <ul>
    <li>T가 어떤 구체 타입인지 몰라도, <code>IHasName</code>만 구현했다면 <code>Name</code> 속성을 안전하게 사용할 수 있다.</li>
  </ul>

  <hr>

  <h2>5. 제네릭 메서드, 실무에서 어디에 많이 쓰일까?</h2>

  <p>사실 이미 엄청 많이 쓰고 있다. 대표적으로:</p>

  <h3>5-1. 컬렉션/LINQ 확장 메서드</h3>

  <pre><code class="language-csharp">public static T[] ToArray&lt;T&gt;(this IEnumerable&lt;T&gt; source)
public static List&lt;T&gt; ToList&lt;T&gt;(this IEnumerable&lt;T&gt; source)
public static T FirstOrDefault&lt;T&gt;(this IEnumerable&lt;T&gt; source)
public static IEnumerable&lt;TResult&gt; Select&lt;TSource, TResult&gt;(
    this IEnumerable&lt;TSource&gt; source,
    Func&lt;TSource, TResult&gt; selector)</code></pre>

  <pre><code class="language-csharp">var list = new List&lt;int&gt; { 1, 2, 3 };
int first = list.FirstOrDefault();  // T = int</code></pre>

  <ul>
    <li>전부 제네릭 메서드이다.</li>
    <li>이 덕분에 <strong>하나의 메서드</strong>로 모든 타입의 리스트, 컬렉션을 다 다룰 수 있다.</li>
  </ul>

  <h3>5-2. Task / 비동기 라이브러리</h3>

  <pre><code class="language-csharp">var t = Task.FromResult(10);  // 사실은 Task.FromResult&lt;int&gt;(10)</code></pre>

  <p><code>Task&lt;TResult&gt;</code> 자체도 제네릭 타입이고, 그걸 생성하는 여러 API도 제네릭 메서드로 제공된다.</p>

  <hr>

  <h2>6. 제네릭 메서드를 쓰는 이유 (장점)</h2>

  <h3>1) 코드 재사용성</h3>
  <p>
    타입만 다른 동일한 로직을 여러 버전으로 만들 필요 없이,  
    <strong>한 번만 정의하고 모든 타입에 쓸 수 있다.</strong>
  </p>

  <h3>2) 타입 안정성</h3>
  <p>
    <code>object</code> + 캐스팅으로 처리하는 대신,  
    제네릭으로 타입을 받으면 <strong>컴파일 타임 타입 체크</strong>를 받을 수 있다.
  </p>

  <h3>3) 박싱/언박싱 감소</h3>
  <p>
    값 형식(<code>int</code>, <code>double</code> 등)을 <code>object</code>에 담았다 뺐다 하면 박싱/언박싱이 생기는데,  
    제네릭을 쓰면 이런 오버헤드가 줄어든다.
  </p>

  <hr>

  <h2>7. 대표적인 예: Swap 제네릭 메서드</h2>

  <pre><code class="language-csharp">static void Swap&lt;T&gt;(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}</code></pre>

  <pre><code class="language-csharp">int x = 10, y = 20;
Swap(ref x, ref y);             // T = int

string s1 = "A", s2 = "B";
Swap(ref s1, ref s2);           // T = string</code></pre>

  <ul>
    <li><code>int</code>용 Swap, <code>string</code>용 Swap을 따로 만들 필요가 없다.</li>
    <li><strong>하나의 Swap&lt;T&gt;</strong>로 모든 타입을 처리할 수 있다.</li>
  </ul>

  <hr>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[ dynamic vs Reflection, 그리고 var / object와의 관계]]></title>
            <link>https://velog.io/@joongjii_1217/dynamic-vs-Reflection-%EA%B7%B8%EB%A6%AC%EA%B3%A0-var-object%EC%99%80%EC%9D%98-%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@joongjii_1217/dynamic-vs-Reflection-%EA%B7%B8%EB%A6%AC%EA%B3%A0-var-object%EC%99%80%EC%9D%98-%EA%B4%80%EA%B3%84</guid>
            <pubDate>Tue, 09 Dec 2025 07:19:46 GMT</pubDate>
            <description><![CDATA[<article>



  <h2>1. dynamic vs reflection – 코드 스타일 / 사용성</h2>

  <h3>1) dynamic</h3>
  <ul>
    <li>마치 <strong>정적 타입인 것처럼 점(.) 찍고 바로 사용</strong>할 수 있다.</li>
    <li>문법이 깔끔하고 짧다.</li>
    <li>내부적으로는 <strong>런타임 바인딩</strong>(reflection + 캐시 등)을 사용해서 어떤 멤버를 호출할지 결정한다.</li>
  </ul>



  <h3>2) reflection</h3>
  <ul>
    <li><code>Type</code>, <code>MethodInfo</code>, <code>PropertyInfo</code> 같은 타입을 직접 다룬다.</li>
    <li>코드가 길어지고, 멤버 이름을 문자열로 다루기 때문에 <strong>오타</strong>에 취약하다.</li>
    <li>그 대신 <strong>더 저수준 제어가 가능</strong>하다. (멤버 나열, 특정 조건으로 필터링 등)</li>
  </ul>


  <hr>

  <h2>2. dynamic vs reflection – 기능 / 유연성 측면</h2>

  <h3>1) dynamic이 어울리는 경우</h3>
  <ul>
    <li><strong>“이 멤버가 있다는 걸 안다”</strong>는 전제 하에, 그냥 편하게 호출하고 싶을 때</li>
    <li>JSON / COM / 스크립트 객체처럼, <strong>“형태는 대충 아는데 타입 선언이 귀찮은”</strong> 경우</li>
  </ul>

  <p>예:</p>
  <pre><code class="language-csharp">dynamic json = JsonConvert.DeserializeObject(jsonText);

<p>Console.WriteLine(json.name);
Console.WriteLine(json.address.city);</code></pre></p>
  <p>
    DTO를 정식으로 만들기 전, 프로토타입/테스트 단계에서 빠르게 다뤄볼 때도 많이 사용한다.
  </p>

  <h3>2) reflection이 어울리는 경우</h3>
  <ul>
    <li>멤버 이름을 <strong>미리 모르는 상태에서</strong>, 런타임에 타입 구조를 탐색해야 할 때</li>
    <li>대표적인 사용 예:
      <ul>
        <li>DTO 자동 매핑 (프로퍼티 이름대로 값을 복사)</li>
        <li>객체의 모든 속성을 읽어서 로그 출력</li>
        <li>특정 <code>Attribute</code>가 붙은 멤버만 골라서 처리</li>
      </ul>
    </li>
    <li>dynamic보다 <strong>더 로우레벨</strong> 도구라서, “멤버가 무엇이 있는지”를 발견하는 작업에 적합하다.</li>
  </ul>

  <p>예:</p>
  <pre><code class="language-csharp">Type type = obj.GetType();
foreach (var prop in type.GetProperties())
{
    var value = prop.GetValue(obj);
    Console.WriteLine($"{prop.Name} = {value}");
}</code></pre>

  <hr>

  <h2>3. 성능 관점 </h2>

  <ul>
    <li><strong>정적 호출보다 둘 다 느리다</strong>는 점은 공통이다.</li>
    <li><code>dynamic</code>은 내부적으로 한 번 바인딩한 결과를 캐시하는 등 최적화가 들어가 있지만, 여전히 정적 호출보다는 비싸다.</li>
    <li><code>reflection</code>은 개발자가 잘못 쓰면 훨씬 비싸질 수 있다.
      <ul>
        <li>예: 루프 안에서 매번 <code>GetMethod</code>, <code>GetProperty</code> 호출</li>
      </ul>
    </li>
    <li>실무에서는 보통:
      <ul>
        <li>핵심 로직 / 성능이 중요한 반복문 안에서는 둘 다 피하고,</li>
        <li><strong>초기화, 설정, 매핑, 플러그인 로딩</strong> 같은 “바깥쪽”에서만 사용하는 경우가 많다.</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>4. 에러가 언제 터지느냐 (에러 시점)</h2>

  <h3>1) dynamic</h3>
  <ul>
    <li>멤버가 없거나 시그니처가 맞지 않으면 → <strong>런타임에 <code>RuntimeBinderException</code></strong> 발생</li>
    <li>컴파일은 일단 다 통과한다.</li>
  </ul>

  <pre><code class="language-csharp">dynamic d = "Hello";
d.NonExistMethod(); // 컴파일 OK, 실행 시 RuntimeBinderException</code></pre>

  <h3>2) reflection</h3>
  <ul>
    <li>멤버를 찾을 때 <code>null</code> 여부를 체크해서, 예외를 직접 제어할 수 있다.</li>
    <li>단, 잘못된 방식으로 <code>Invoke</code>하면 여전히 런타임 예외가 발생한다.</li>
  </ul>

  <pre><code class="language-csharp">var mi = obj.GetType().GetMethod("Run");
if (mi != null)
{
    mi.Invoke(obj, null); // 시그니처가 맞지 않으면 여기서 예외
}
else
{
    Console.WriteLine("Run 메서드가 없습니다.");
}</code></pre>

  <hr>

  <h2>5. 함께 정리: dynamic / object / var</h2>

  <p>
    dynamic과 reflection을 이해할 때 자주 같이 언급되는 키워드가 <code>var</code>, <code>object</code>라서,
    간단하게 비교해 두면 기억하기 좋다.
  </p>

  <ul>
    <li><strong><code>var</code></strong>
      <ul>
        <li>그냥 <strong>컴파일 타임 타입 추론</strong>이다.</li>
        <li>한 번 타입이 결정되면 일반 변수와 똑같이 타입이 고정된다.</li>
        <li>타입 안전하고, 제일 많이 쓰는 기본 문법.</li>
      </ul>
    </li>
    <li><strong><code>object</code></strong>
      <ul>
        <li>모든 타입의 최상위 타입.</li>
        <li>형변환 + 박싱/언박싱이 필요할 수 있다.</li>
        <li>제네릭이 없던 시절 컬렉션에서 많이 쓰였던 방식 (예: <code>ArrayList</code>).</li>
      </ul>
    </li>
    <li><strong><code>dynamic</code></strong>
      <ul>
        <li>타입 체크를 <strong>런타임</strong>으로 미룬다.</li>
        <li>문법은 편하지만, 타입 안정성과 성능을 조금 희생한다.</li>
        <li>JSON/COM/스크립트/실험용 코드 등에 적당하다.</li>
      </ul>
    </li>
  </ul>

  <hr>




</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[다이나믹 예약어]]></title>
            <link>https://velog.io/@joongjii_1217/%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9-%EC%98%88%EC%95%BD%EC%96%B4</link>
            <guid>https://velog.io/@joongjii_1217/%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9-%EC%98%88%EC%95%BD%EC%96%B4</guid>
            <pubDate>Tue, 09 Dec 2025 07:17:07 GMT</pubDate>
            <description><![CDATA[<article>

  <h2>1. <code>dynamic</code> 한 줄 정의</h2>

  <p>
    보통 <code>int</code>, <code>string</code> 같은 정적 타입들은:
  </p>
  <ul>
    <li><strong>컴파일 시점</strong>에 타입이 확정되고</li>
    <li>그 타입 기준으로 “이 멤버를 써도 되는지/안 되는지”를 컴파일러가 다 검사한다</li>
  </ul>

  <p>
    그런데 <code>dynamic</code>은 이렇게 행동한다:
  </p>

  <blockquote>
    “지금 컴파일할 땐 타입 검사하지 말고,<br>
    실행할 때 실제 타입을 보고 알아서 동작해봐.”
  </blockquote>

  <p>
    그래서:
  </p>

  <ul>
    <li>멤버 접근</li>
    <li>메서드 호출</li>
    <li>연산</li>
  </ul>

  <p>
    이런 것들이 전부 <strong>런타임에 결정</strong>된다.
  </p>

  <hr>

  <h2>2. 제일 중요한 특징: “컴파일러가 눈을 감는다”</h2>

  <pre><code class="language-csharp">dynamic x = 10;
x = "Hello";      // OK, 타입 바뀌어도 컴파일 에러 없음
x = DateTime.Now; // OK

Console.WriteLine(x.NonExistMethod()); // 컴파일은 통과하지만...</code></pre>

  <p>이 코드를 보면:</p>

  <ul>
    <li>컴파일할 때는 <strong>에러가 안 난다</strong><br>
      → 컴파일러가 “dynamic이면 일단 믿고 넘어갈게…” 모드라서
    </li>
    <li>실행 시점에 <code>x.NonExistMethod()</code>를 호출하려고 할 때,
      실제 <code>x</code> 타입에 그런 메서드가 없으면<br>
      <strong><code>RuntimeBinderException</code> 예외</strong>가 터진다
    </li>
  </ul>

  <p>
    반대로, 같은 걸 <code>object</code>로 쓰면:
  </p>

  <pre><code class="language-csharp">object x = 10;
x.NonExistMethod(); // ❌ 컴파일 에러 (object에는 그런 메서드 없음)</code></pre>

  <p>
    <code>object</code>일 때는 컴파일러가 타입을 알고 있으니까:
  </p>

  <blockquote>
    “야, <code>object</code>에는 <code>NonExistMethod</code> 같은 멤버 없어.”  
    → 컴파일 단계에서 바로 막아버린다.
  </blockquote>

  <p>
    정리하면:
  </p>

  <ul>
    <li><strong><code>object</code></strong> → 컴파일 시점에 타입이 <code>object</code>라서, <code>object</code> 범위 안에서만 사용 가능</li>
    <li><strong><code>dynamic</code></strong> → 컴파일러가 타입 체크를 포기하고, <strong>실행 시점에 실제 타입 기준으로</strong> 멤버를 찾고 호출을 시도</li>
  </ul>

  <hr>

  <h2>3. <code>dynamic</code> vs <code>var</code> – 제일 많이 헷갈리는 포인트</h2>

  <p>
    거의 100% 한 번씩 헷갈리는 부분이라, 확실히 정리해 두는 게 좋다.
  </p>

  <h3>3-1. <code>var</code></h3>

  <pre><code class="language-csharp">var s = "Hello";  // s는 컴파일 타임에 string으로 확정
s = 123;          // ❌ 컴파일 에러 (int 대입 불가)</code></pre>

  <ul>
    <li><code>var</code>는 그냥 <strong>“형식 추론”</strong>일 뿐이다.</li>
    <li>컴파일러가 <code>s</code>를 보고 “아, 이건 <code>string</code>이구나”라고 딱 정해버린다.</li>
    <li>이후에는 <code>string</code> 변수처럼 완전히 작동한다.</li>
  </ul>

  <h3>3-2. <code>dynamic</code></h3>

  <pre><code class="language-csharp">dynamic s = "Hello";  // s의 런타임 타입은 string, 컴파일러는 타입 검사 안 함
s = 123;              // OK, 이제 런타임 타입은 int로 바뀜</code></pre>

  <ul>
    <li><code>dynamic</code>은 타입이 <strong>런타임에 바뀔 수 있다</strong>.</li>
    <li>컴파일러가 타입 체크를 거의 안 한다.</li>
    <li>어떤 메서드를 호출하더라도 <strong>일단 컴파일은 통과</strong>하고,
      실행 중에 실제 타입에 맞는지 검사한다.</li>
  </ul>

  <h3>3-3. 표로 요약</h3>

  <table border="1" cellspacing="0" cellpadding="4">
    <thead>
      <tr>
        <th>키워드</th>
        <th>의미</th>
        <th>타입 결정 시점</th>
        <th>타입 체크</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><code>var</code></td>
        <td>형식 추론 (컴파일 시)</td>
        <td>컴파일 타임</td>
        <td>컴파일 타임에 엄격하게 검사</td>
      </tr>
      <tr>
        <td><code>dynamic</code></td>
        <td>런타임 바인딩 (동적 타입)</td>
        <td>런타임</td>
        <td>런타임에 검사</td>
      </tr>
    </tbody>
  </table>

  <hr>

  <h2>4. <code>dynamic</code>은 어디에 쓰는 게 좋을까?</h2>

  <p>
    대부분의 C# 코드는 <strong>정적 타입</strong>으로 깔끔하게 짜는 게 좋다.<br>
    그래도 <code>dynamic</code>이 유용한 상황이 분명 있다.
  </p>

  <h3>4-1. COM / Office 자동화 (Excel, Word 등)</h3>

  <p>기존 스타일:</p>

  <pre><code class="language-csharp">using Excel = Microsoft.Office.Interop.Excel;

Excel.Application app = new Excel.Application();
Excel.Workbook wb = app.Workbooks.Open("test.xlsx");
Excel.Worksheet ws = (Excel.Worksheet)wb.Sheets[1];</code></pre>

  <p>
    인터페이스/캐스팅이 많아서 코드가 좀 번거롭다.
  </p>

  <p><code>dynamic</code>을 쓰면:</p>

  <pre><code class="language-csharp">dynamic app = new Excel.Application();
dynamic wb = app.Workbooks.Open("test.xlsx");
dynamic ws = wb.Sheets[1];

ws.Cells[1, 1].Value = "Hello";</code></pre>

  <ul>
    <li>멤버를 전부 <code>dynamic</code>에 넣고 쓰면, 캐스팅/구체 타입을 덜 신경 써도 된다.</li>
    <li>대신, <strong>멤버 이름을 잘못 쓰면 런타임 에러</strong>가 난다.</li>
  </ul>

  <h3>4-2. 동적 언어(.NET 상의 Python, JS 등)와 연동</h3>

  <p>예를 들어 IronPython, ClearScript 같은 스크립트 엔진과 연동할 때:</p>

  <ul>
    <li>스크립트에서 넘어오는 객체의 타입을 C# 컴파일러가 미리 모른다.</li>
    <li><code>dynamic</code>으로 받아서 <strong>“그냥 메서드 있는 것처럼”</strong> 호출할 수 있다.</li>
  </ul>

  <pre><code class="language-csharp">dynamic pyObj = GetPythonObject(); // 예시
pyObj.Run();   // 런타임에 Run이 있는지 확인 후 호출</code></pre>

  <h3>4-3. JSON / <code>ExpandoObject</code>를 가볍게 다룰 때</h3>

  <p>예를 들어 Newtonsoft.Json을 사용할 때:</p>

  <pre><code class="language-csharp">dynamic obj = JsonConvert.DeserializeObject(json);
Console.WriteLine(obj.name);
Console.WriteLine(obj.address.city);</code></pre>

  <ul>
    <li>프로퍼티명을 문자열로 꺼내는 대신, 점(.)으로 바로 접근하는 코드를 작성할 수 있다.</li>
    <li>다만, 정석적으로는 DTO 클래스를 만들어서 <code>Person</code> 같은 타입으로 매핑하는 게 더 타입 안정성이 좋다.</li>
  </ul>

  <hr>

  <h2>5. 예제로 보는 <code>dynamic</code> 동작</h2>

  <h3>5-1. Duck Typing 비슷하게 사용하는 예</h3>

  <pre><code class="language-csharp">static void PrintLength(dynamic x)
{
    Console.WriteLine(x.Length);
}

PrintLength("Hello");            // string.Length → 5
PrintLength(new int[] {1,2,3});  // 배열.Length → 3
PrintLength(123);                // int에는 Length 없음 → 런타임 예외</code></pre>

  <p>
    컴파일러 입장:
  </p>

  <ul>
    <li>“<code>dynamic</code>이면 일단 <code>Length</code>가 있다고 믿자…” → 컴파일 OK</li>
    <li>실행 중에 실제 타입을 보고 <code>Length</code>를 찾는다.</li>
    <li>있으면 호출, 없으면 예외.</li>
  </ul>

  <hr>

  <h2>6. <code>dynamic</code>의 단점 / 주의할 점</h2>

  <h3>6-1. 타입 안전성이 떨어진다</h3>

  <pre><code class="language-csharp">dynamic x = "Hello";
int n = x;  // 컴파일 OK, 실행 시 예외 (string → int 변환 불가)</code></pre>

  <ul>
    <li><code>var</code>나 일반 정적 타입이라면 컴파일 단계에서 잡아줄 수 있는 오류도,</li>
    <li><code>dynamic</code>은 <strong>실행해 봐야만 알 수 있다</strong>.</li>
    <li>프로젝트가 커지고 팀원이 늘어나면, 이런 런타임 에러는 디버깅이 더 까다로워질 수 있다.</li>
  </ul>

  <h3>6-2. 성능 오버헤드</h3>

  <ul>
    <li>호출할 때마다 “어떤 멤버를 호출해야 할지”를 런타임에 찾아야 한다.</li>
    <li>즉, 내부적으로 항상 <strong>런타임 바인딩</strong> 작업이 수행된다.</li>
    <li>단순 정적 호출보다 느릴 수 있고, 특히 <strong>대량 루프 안에서 쓰면 부담</strong>이 될 수 있다.</li>
  </ul>

  <hr>

  <h2>7. 언제 쓰고, 언제 피해야 할까?</h2>

  <h3>써도 괜찮은/유리한 경우</h3>
  <ul>
    <li>COM / Office 자동화 코드 간단히 정리하고 싶을 때</li>
    <li>동적 언어(파이썬, JS 등)에서 넘어오는 객체를 편하게 다루고 싶을 때</li>
    <li>빠르게 프로토타입을 만들고, 나중에 정적 타입으로 정리할 생각일 때</li>
    <li>JSON을 잠깐 파싱해서 몇 개 필드만 임시로 보고 싶을 때</li>
  </ul>

  <h3>가능하면 피해야 하는 경우</h3>
  <ul>
    <li>핵심 도메인 로직, 중요한 계산 로직</li>
    <li>팀원이 많은 프로젝트의 공용 라이브러리/핵심 인프라 코드</li>
    <li>성능이 중요한 반복 루프 내부</li>
  </ul>

  <p>
    대부분의 일반적인 C# 개발에서는:
  </p>

  <ul>
    <li><strong>정적 타입 + 제네릭</strong>으로 설계하는 것이 기본 원칙이고,</li>
    <li><code>dynamic</code>은 진짜 필요한 구석(interop, 스크립팅, 테스트용)에서
     <br> <strong>“도구 상자에서 잠깐 꺼내 쓰는 공구”</strong> 정도로 쓰는 게 좋다.</li>
  </ul>

  <hr>


</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[제네릭 : 형식 매개변수 제약조건]]></title>
            <link>https://velog.io/@joongjii_1217/%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%98%95%EC%8B%9D-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98-%EC%A0%9C%EC%95%BD%EC%A1%B0%EA%B1%B4</link>
            <guid>https://velog.io/@joongjii_1217/%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%98%95%EC%8B%9D-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98-%EC%A0%9C%EC%95%BD%EC%A1%B0%EA%B1%B4</guid>
            <pubDate>Tue, 09 Dec 2025 06:56:09 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 형식 매개변수 제약조건이란?</h2>

  <p>
    기본적인 제네릭 클래스는 이렇게 생겼다:
  </p>

  <pre><code class="language-csharp">class MyClass&lt;T&gt;
{
    public T Value;
}</code></pre>

  <p>
    여기서 <code>T</code>에는 어떤 타입이든 들어갈 수 있다.
  </p>

  <pre><code class="language-csharp">var a = new MyClass&lt;int&gt;();
var b = new MyClass&lt;string&gt;();
var c = new MyClass&lt;DateTime&gt;();</code></pre>

  <p>
    그런데 가끔은 이런 식으로 제한하고 싶을 때가 있다:
  </p>

  <ul>
    <li>“T는 <strong>반드시 클래스 타입</strong>이어야 해”</li>
    <li>“T는 <strong>반드시 기본 생성자(new())</strong>가 있어야 해”</li>
    <li>“T는 <strong>내가 만든 BaseEntity를 상속한 타입</strong>만 허용하고 싶어”</li>
    <li>“T는 <strong>어떤 인터페이스를 구현한 타입</strong>만 받을래”</li>
  </ul>

  <p>
    이렇게 <strong>T에 조건을 거는 문법</strong>이 바로 제네릭 형식 매개변수에 대한 <strong>제약조건(Constraint)</strong>이다.
  </p>

  <p>
    문법 형태는 다음과 같다:
  </p>

  <pre><code class="language-csharp">class MyClass&lt;T&gt; where T : ...조건...</code></pre>

  <hr>

  <h2>2. 제약조건의 기본 종류</h2>

  <p>
    대표적으로 자주 쓰는 제약조건은 다음과 같다.
  </p>

  <ul>
    <li><code>where T : class</code> &rarr; T는 <strong>참조형</strong>(클래스)이어야 한다</li>
    <li><code>where T : struct</code> &rarr; T는 <strong>값 형식</strong>(struct)이어야 한다</li>
    <li><code>where T : new()</code> &rarr; T는 <strong>매개변수 없는 public 기본 생성자</strong>를 가져야 한다</li>
    <li><code>where T : SomeBaseClass</code> &rarr; T는 <strong>SomeBaseClass를 상속한 타입</strong>이어야 한다</li>
    <li><code>where T : ISomeInterface</code> &rarr; T는 <strong>ISomeInterface를 구현한 타입</strong>이어야 한다</li>
    <li><code>where T : unmanaged</code> &rarr; T는 <strong>비관리 값형</strong> (포인터로 다룰 수 있는 순수 값 타입)이어야 한다</li>
    <li><code>where T : notnull</code> &rarr; T는 <strong>null이 될 수 없는 타입</strong>이어야 한다 (nullable reference type과 연관)</li>
  </ul>

  <p>
    그리고 이 제약조건들은 <strong>콤마(,)</strong>로 이어서 여러 개를 동시에 걸 수도 있다.
  </p>

  <hr>

  <h2>3. <code>where T : class</code> – 참조형만 허용</h2>

  <pre><code class="language-csharp">class ReferenceOnlyBox&lt;T&gt; where T : class
{
    public T Value { get; set; }
}</code></pre>

  <pre><code class="language-csharp">var a = new ReferenceOnlyBox&lt;string&gt;();   // OK (string은 참조형)
var b = new ReferenceOnlyBox&lt;object&gt;();   // OK
// var c = new ReferenceOnlyBox&lt;int&gt;();   // ❌ 컴파일 에러 (int는 값 형식)</code></pre>

  <p>
    이런 경우에 쓴다:
  </p>

  <ul>
    <li>T가 항상 참조형이라고 가정하고 처리하고 싶을 때</li>
    <li>값 형식(int, double, struct 등)이 들어오는 걸 아예 막고 싶을 때</li>
  </ul>

  <hr>

  <h2>4. <code>where T : struct</code> – 값 형식만 허용</h2>

  <pre><code class="language-csharp">class ValueOnlyBox&lt;T&gt; where T : struct
{
    public T Value { get; set; }
}</code></pre>

  <pre><code class="language-csharp">var a = new ValueOnlyBox&lt;int&gt;();        // OK
var b = new ValueOnlyBox&lt;DateTime&gt;();   // OK

// var c = new ValueOnlyBox&lt;string&gt;();  // ❌ string은 참조형</code></pre>

  <p>
    이런 경우에 유용하다:
  </p>

  <ul>
    <li>T가 항상 값 형식이라서 <code>default(T)</code>가 null이 아니라고 가정할 수 있을 때</li>
    <li>nullable 처리, 포인터 연산, 성능 최적화 등을 값 형식 기준으로 설계할 때</li>
  </ul>

  <hr>

  <h2>5. <code>where T : new()</code> – 기본 생성자 필수</h2>

  <p>
    <strong>“제네릭 타입을 내가 직접 생성해야 하는 경우”</strong>에 자주 쓰는 제약조건이다.
  </p>

  <pre><code class="language-csharp">class Factory&lt;T&gt; where T : new()
{
    public T Create()
    {
        return new T(); // ★ new T()를 사용할 수 있게 됨
    }
}</code></pre>

  <pre><code class="language-csharp">class User
{
    public string Name { get; set; }

    public User()   // 매개변수 없는 기본 생성자
    {
    }
}

var factory = new Factory&lt;User&gt;();
User u = factory.Create(); // OK</code></pre>

  <p>
    만약 <code>User</code>가 매개변수 있는 생성자만 있고 기본 생성자가 없다면,
    <code>where T : new()</code> 제약을 만족하지 못해서 컴파일 에러가 난다.
  </p>

  <p>
    이런 곳에 잘 어울린다:
  </p>

  <ul>
    <li>리포지토리가 엔티티 인스턴스를 직접 만들어야 할 때</li>
    <li>공통 Factory / Builder / DTO 생성기 같은 걸 만들 때</li>
  </ul>

  <hr>

  <h2>6. <code>where T : SomeBaseClass</code> – 특정 베이스 클래스 상속</h2>

  <pre><code class="language-csharp">class EntityBase
{
    public int Id { get; set; }
}

class Repository&lt;T&gt; where T : EntityBase
{
    public void Save(T entity)
    {
        Console.WriteLine($"Id: {entity.Id} 저장");
    }
}</code></pre>

  <pre><code class="language-csharp">class User : EntityBase
{
    public string Name { get; set; }
}

class Product : EntityBase
{
    public string ItemName { get; set; }
}

var userRepo = new Repository&lt;User&gt;();      // OK
var productRepo = new Repository&lt;Product&gt;(); // OK

// class Something { }
// var repo = new Repository&lt;Something&gt;();  // ❌ EntityBase 상속 안 했으므로 에러</code></pre>

  <p>
    포인트는 이거다:
  </p>

  <blockquote>
    <strong>Repository&lt;T&gt; 내부에서는 T가 최소한 EntityBase의 멤버(Id 등)를 가지고 있다는 게 보장된다.</strong>
  </blockquote>

  <p>
    그래서 <code>entity.Id</code>, <code>entity.CreatedAt</code> 같은 공통 속성을  
    제네릭 타입에도 안전하게 사용할 수 있다.
  </p>

  <hr>

  <h2>7. <code>where T : 인터페이스</code> – 특정 인터페이스 구현</h2>

  <pre><code class="language-csharp">interface IHasName
{
    string Name { get; set; }
}

class NamePrinter&lt;T&gt; where T : IHasName
{
    public void Print(T obj)
    {
        Console.WriteLine(obj.Name);  // ★ 인터페이스의 Name 사용 가능
    }
}</code></pre>

  <pre><code class="language-csharp">class User : IHasName
{
    public string Name { get; set; }
}

class Company : IHasName
{
    public string Name { get; set; }
}

var p1 = new NamePrinter&lt;User&gt;();
p1.Print(new User { Name = "홍길동" });

var p2 = new NamePrinter&lt;Company&gt;();
p2.Print(new Company { Name = "ABC Corp" });</code></pre>

  <p>
    이 패턴의 장점:
  </p>

  <ul>
    <li>구체 타입(User, Company 등)에 의존하지 않고,</li>
    <li><strong>“이 인터페이스만 구현하면 다 받을 수 있다”</strong>는 식으로 설계가 가능하다.</li>
  </ul>

  <p>
    실무에서 가장 자주 쓰는 제약조건 패턴 중 하나다.
  </p>

  <hr>

  <h2>8. 여러 제약조건을 한 번에 걸기</h2>

  <p>
    제약조건은 콤마로 이어서 여러 개를 동시에 걸 수 있다.
  </p>

  <pre><code class="language-csharp">class MyService&lt;T&gt;
    where T : EntityBase, IHasName, new()
{
    public T CreateDefault()
    {
        var obj = new T();        // new() 제약 덕분에 생성 가능
        obj.Name = "기본 이름";   // IHasName 제약 덕분에 Name 사용 가능
        return obj;
    }
}</code></pre>

  <p>
    여기서 T는 다음 조건을 모두 만족해야 한다:
  </p>

  <ol>
    <li><code>EntityBase</code>를 상속해야 하고</li>
    <li><code>IHasName</code> 인터페이스를 구현해야 하고</li>
    <li><code>public</code> 기본 생성자를 가지고 있어야 한다</li>
  </ol>

  <p>
    <strong>제약조건 순서 규칙도 중요하다:</strong>
  </p>

  <ul>
    <li>클래스/struct/구체적인 베이스클래스 &rarr; 제일 앞</li>
    <li>인터페이스들 &rarr; 그 다음</li>
    <li><code>new()</code> &rarr; 항상 맨 마지막</li>
  </ul>

  <p>
    예를 들어:
  </p>

  <pre><code class="language-csharp">where T : EntityBase, IHasName, new()  // ✅ 올바른 순서
// where T : new(), EntityBase, IHasName  // ❌ 잘못된 순서, 컴파일 에러</code></pre>

  <hr>

  <h2>9. 제네릭 메서드에도 제약조건을 걸 수 있다</h2>

  <p>
    클래스 전체가 제네릭이 아니어도, <strong>메서드 하나만 제네릭</strong>이고 거기에 제약조건을 걸 수 있다.
  </p>

  <pre><code class="language-csharp">class Util
{
    public static T CreateAndLog&lt;T&gt;() where T : new()
    {
        T obj = new T();
        Console.WriteLine(typeof(T).Name + " 생성됨");
        return obj;
    }
}</code></pre>

  <pre><code class="language-csharp">class User
{
    public User() { }
}

var user = Util.CreateAndLog&lt;User&gt;();  // User는 기본 생성자가 있어야 함</code></pre>

  <p>
    이런 식으로 재사용 가능한 유틸리티 메서드를 깔끔하게 만들 수 있다.
  </p>

  <hr>

  <h2>10. 한 번에 정리</h2>

  <p>
    C# 제네릭 형식 매개변수 제약조건은 한 줄로 요약하면:
  </p>

  <blockquote>
    <strong>“제네릭 타입/메서드에서 T에 들어올 수 있는 타입을 제한해서,  
    안쪽 코드가 ‘T가 최소한 이 정도 기능은 가진 타입이다’라고 믿고 쓸 수 있게 해주는 장치”</strong>
  </blockquote>

  <p>자주 쓰는 패턴만 다시 모으면:</p>

  <pre><code class="language-csharp">// 1) 참조형만
where T : class

// 2) 값 형식만
where T : struct

// 3) 어떤 베이스 클래스 상속
where T : BaseClass

// 4) 어떤 인터페이스 구현
where T : ISomeInterface

// 5) 기본 생성자 필요
where T : new()

// 6) 여러 개 한꺼번에
where T : BaseClass, ISomeInterface, new()</code></pre>

  <p>
    실무에서는 보통 이런 상황에서 자주 보게 된다:
  </p>

  <ul>
    <li>공통 <code>Repository&lt;T&gt;</code> 만들 때 → <code>where T : EntityBase, new()</code></li>
    <li>이름/ID 등 공통 속성이 있는 타입만 처리하는 유틸 → <code>where T : IHasName</code></li>
    <li>값 형식 전용 유틸, 참조형 전용 유틸 만들 때 → <code>class</code>, <code>struct</code> 제약</li>
  </ul>

  <p>
    제약조건까지 이해하면 제네릭을 단순히 “타입 하나 받는 문법”이 아니라,  
    <strong>“타입에 대한 규칙을 걸 수 있는 강력한 설계 도구”</strong>로 볼 수 있게 된다.
  </p>
</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[await와 async]]></title>
            <link>https://velog.io/@joongjii_1217/await%EC%99%80-async</link>
            <guid>https://velog.io/@joongjii_1217/await%EC%99%80-async</guid>
            <pubDate>Wed, 03 Dec 2025 08:33:38 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 한 줄 정의</h2>

  <h3>async</h3>
  <blockquote>
    메서드나 람다 <strong>선언부</strong>에 붙이는 키워드.<br>
    “이 메서드는 비동기 스타일로 작성되었고, <code>Task</code> 또는 <code>Task&lt;T&gt;</code>를 반환할 거야” 라는 표시.
  </blockquote>

  <h3>await</h3>
  <blockquote>
    메서드/람다 <strong>몸통 안</strong>에서 쓰는 키워드(연산자).<br>
    “이 비동기 작업(<code>Task</code>)이 끝날 때까지 <em>비동기적으로</em> 기다렸다가,  
    끝나면 그 다음 줄부터 이어서 실행해 줘.” 라는 뜻.
  </blockquote>

  <hr>

  <h2>2. 어디에 쓰는 키워드인가?</h2>

  <h3>2-1. async는 ‘메서드 선언부’에 붙는다</h3>

  <pre><code class="language-csharp">async Task DoWorkAsync()
{
    // 여기 안에서 await 사용 가능
}

async Task&lt;int&gt; GetNumberAsync()
{
    await Task.Delay(1000);
    return 10;
}
</code></pre>

  <ul>
    <li><code>async</code>는 메서드 이름 앞에 붙는다.</li>
    <li>“이 메서드는 비동기 상태머신으로 변환된다”는 신호를 컴파일러에 주는 역할을 한다.</li>
    <li>반환 타입은 보통 <code>Task</code> / <code>Task&lt;T&gt;</code> / (특수한 경우) <code>void</code> 중 하나다.</li>
  </ul>

  <p>
    중요한 점 하나:  
    <strong><code>async</code>를 붙였다고 자동으로 새 스레드가 만들어지는 게 아니다.</strong><br>
    그냥 “비동기 로직을 담고 있고, 안에서 <code>await</code> 쓸 거야” 정도의 표지판에 가깝다.
  </p>

  <h3>2-2. await는 ‘메서드 몸통 안’에서만 쓴다</h3>

  <pre><code class="language-csharp">async Task TestAsync()
{
    await Task.Delay(1000); // OK
}

void Test()
{
    await Task.Delay(1000); // ❌ 컴파일 에러: async 메서드가 아니라서
}
</code></pre>

  <ul>
    <li><code>await</code>는 <strong>반드시 <code>async</code>가 붙은 메서드/람다 안</strong>에서만 사용할 수 있다.</li>
    <li><code>await someTask</code>는 “<code>someTask</code>가 끝날 때까지 비동기적으로 기다렸다가, 다시 여기로 돌아와서 아래 코드를 이어서 실행하자” 라는 의미다.</li>
    <li><code>Task&lt;T&gt;</code>를 await 하면, <code>T</code> 결과값을 바로 꺼내 쓸 수 있다.</li>
  </ul>

  <pre><code class="language-csharp">int result = await GetNumberAsync();  // GetNumberAsync가 Task&lt;int&gt;를 반환한다고 가정
</code></pre>

  <p>
    위 한 줄은 사실상:
  </p>

  <ol>
    <li><code>GetNumberAsync()</code>가 시작되고 <code>Task&lt;int&gt;</code> 하나가 만들어진다.</li>
    <li>그 Task가 완료될 때까지 비동기적으로 대기한다.</li>
    <li>완료되면 그 안의 <code>int</code> 값을 꺼내서 <code>result</code>에 넣는다.</li>
  </ol>

  <hr>

  <h2>3. 역할 관점에서 보는 async vs await</h2>

  <h3>3-1. async의 역할</h3>

  <ul>
    <li>이 메서드를 <strong>비동기 상태머신</strong>으로 변환하도록 컴파일러에게 지시한다.</li>
    <li>이 안에 <code>await</code>를 쓸 수 있게 해준다.</li>
    <li>반환 타입을 <code>Task</code> / <code>Task&lt;T&gt;</code> / <code>void</code> 중 하나로 맞추게 만든다.</li>
  </ul>

  <p>
    흔한 오해 중 하나가:
  </p>

  <blockquote>
    “async 붙이면 자동으로 다른 스레드에서 실행된다”
  </blockquote>

  <p>
    이건 아니다.  
    새 스레드를 만드는 건 <code>Task.Run</code>, <code>Thread</code>, <code>ThreadPool</code> 같은 애들이고,  
    <code>async</code>는 단지 비동기 흐름을 <strong>코드 구조</strong>로 표현하기 위한 키워드다.
  </p>

  <h3>3-2. await의 역할</h3>

  <ul>
    <li><code>Task</code> 또는 <code>Task&lt;T&gt;</code>가 <strong>완료될 때까지 비동기적으로 대기</strong>한다.</li>
    <li>대기하는 동안 <strong>스레드를 블록시키지 않는다</strong> (UI 프리징 방지의 핵심).</li>
    <li>작업이 끝나면 이어서 아래 줄 코드를 실행한다.</li>
    <li><code>Task&lt;T&gt;</code>를 await하면, 결과 <code>T</code>를 바로 받을 수 있다.</li>
  </ul>

  <pre><code class="language-csharp">async Task DemoAsync()
{
    Console.WriteLine("1");

    await Task.Delay(1000); // 1초 후, 다시 여기로 돌아와서

    Console.WriteLine("2"); // 이 줄부터 이어서 실행
}
</code></pre>

  <p>
    여기서 <code>await Task.Delay(1000)</code>를 <code>Thread.Sleep(1000)</code>으로 바꾸면  
    1초 동안 스레드가 진짜로 멈춰버린다.  
    <code>await</code>는 “기다린다”는 의미는 같지만, <strong>스레드를 점유하지 않는다는 점이 결정적으로 다르다</strong>.
  </p>

  <hr>

  <h2>4. async와 await의 관계</h2>

  <h3>4-1. async만 있고 await가 없을 때</h3>

  <pre><code class="language-csharp">async Task FooAsync()
{
    Console.WriteLine("Hello");
    // await 없음
}
</code></pre>

  <p>
    이런 코드는 사실상 동기 메서드와 거의 같다.  
    컴파일러가 내부적으로 Task를 감싸긴 하지만, 중간에 “끊기는 지점”이 없기 때문에  
    흐름은 그냥 위에서 아래로 쭉 실행된다.
  </p>

  <p>
    그래서 <strong>진짜 비동기의 느낌이 나는 지점은 결국 <code>await</code>를 만나는 순간부터</strong>라고 보면 된다.
  </p>

  <h3>4-2. await만 쓰고 async를 안 쓸 때</h3>

  <pre><code class="language-csharp">void Foo()
{
    await Task.Delay(1000); // ❌ 컴파일 에러
}
</code></pre>

  <p>
    이런 코드는 컴파일 자체가 안 된다.  
    <strong>await는 반드시 async 메서드 안에서만 사용 가능</strong>하기 때문이다.
  </p>

  <p>
    즉, 둘의 관계를 요약하면:
  </p>

  <ul>
    <li><code>await</code>는 <code>async</code> 없이 쓸 수 없다.</li>
    <li><code>async</code>는 <code>await</code> 없이 쓸 수는 있지만, 의미가 거의 없다.</li>
  </ul>

  <hr>

  <h2>5. 간단 예제로 전체 흐름 다시 보기</h2>

  <pre><code class="language-csharp">async Task&lt;int&gt; GetNumberAsync()
{
    await Task.Delay(1000); // 1초짜리 비동기 작업
    return 10;
}

async Task ShowAsync()
{
    Console.WriteLine("시작");

    int n = await GetNumberAsync();   // 여기서 비동기 대기 + 결과 받기

    Console.WriteLine("결과: " + n);
}
</code></pre>

  <ul>
    <li><code>GetNumberAsync</code>  
      → “나중에 <code>int</code>를 줄 비동기 메서드야” (반환 타입: <code>Task&lt;int&gt;</code>)</li>
    <li><code>await Task.Delay(1000)</code>  
      → 1초 뒤에 다시 아래 줄부터 이어서 실행</li>
    <li><code>int n = await GetNumberAsync();</code>  
      → <code>Task&lt;int&gt;</code>가 끝날 때까지 기다렸다가, 안의 <code>int</code> 값(<code>10</code>)을 꺼내서 <code>n</code>에 대입</li>
  </ul>

  <hr>

  <h2>6. 한 번에 요약</h2>

  <ul>
    <li><strong>async</strong>
      <ul>
        <li>메서드/람다 선언부에 붙는다.</li>
        <li>“이 메서드는 비동기 스타일로 작성됐다, 안에서 await 쓸 거다” 라는 표시.</li>
        <li>반환 타입은 <code>Task</code>, <code>Task&lt;T&gt;</code>, <code>void</code>(이벤트 핸들러) 중 하나.</li>
      </ul>
    </li>
    <li><strong>await</strong>
      <ul>
        <li>비동기 작업(<code>Task</code>, <code>Task&lt;T&gt;</code>) 앞에 붙는 연산자.</li>
        <li>해당 Task가 완료될 때까지 비동기적으로 기다린 뒤, 다음 줄부터 이어서 실행.</li>
        <li><code>Task&lt;T&gt;</code>라면 결과 <code>T</code>를 바로 꺼내서 변수에 넣어줌.</li>
      </ul>
    </li>
  </ul>

  <p>
    정말 짧게 말하면:
  </p>

  <blockquote>
    <strong>async</strong>는 “이 메서드는 비동기 모드로 작성할게” 라고 선언하는 스위치이고,<br>
    <strong>await</strong>는 “이 비동기 작업이 끝날 때까지 잠깐 맡겨 두고, 끝나면 여기서부터 다시 시작하자” 라고 말하는 실행 지점이다.
  </blockquote>


</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 추가]]></title>
            <link>https://velog.io/@joongjii_1217/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@joongjii_1217/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Wed, 03 Dec 2025 07:28:10 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. .NET BCL에 추가된 async 메서드</h2>

  <h3>1-1. BCL + async 메서드란?</h3>

  <p>
    먼저 BCL(Base Class Library)를 한 줄로 정리하면:
  </p>

  <blockquote>
    .NET이 기본으로 제공하는 표준 라이브러리 묶음 (예: <code>System.IO</code>, <code>System.Net.Http</code>, <code>System.Data</code>, <code>System.Threading.Tasks</code> 등)
  </blockquote>

  <p>
    .NET 4.5 이후부터 이 BCL 곳곳에 <strong>비동기 메서드</strong>들이 대량으로 추가됐다. 특징은:
  </p>

  <ul>
    <li>이름 끝에 <code>Async</code>가 붙고</li>
    <li>반환 타입이 <code>Task</code> 또는 <code>Task&lt;T&gt;</code>이고</li>
    <li>내부에서 OS의 비동기 IO를 사용해서 <strong>스레드를 블록하지 않는다</strong></li>
  </ul>

  <h3>1-2. 대표적인 BCL async 메서드 예시</h3>

  <ul>
    <li><strong>파일/스트림 IO</strong>
      <ul>
        <li><code>Stream.ReadAsync</code>, <code>Stream.WriteAsync</code></li>
        <li><code>FileStream.ReadAsync</code>, <code>FileStream.WriteAsync</code></li>
      </ul>
    </li>
    <li><strong>HTTP/네트워크</strong>
      <ul>
        <li><code>HttpClient.GetAsync</code>, <code>PostAsync</code>, <code>SendAsync</code></li>
        <li><code>NetworkStream.ReadAsync</code>, <code>WriteAsync</code></li>
      </ul>
    </li>
    <li><strong>DB / Entity Framework</strong>
      <ul>
        <li><code>DbCommand.ExecuteReaderAsync</code>, <code>ExecuteNonQueryAsync</code></li>
        <li><code>DbContext.SaveChangesAsync</code></li>
      </ul>
    </li>
    <li><strong>기타</strong>
      <ul>
        <li><code>Task.Delay</code></li>
        <li><code>Socket.*Async</code> 계열 등</li>
      </ul>
    </li>
  </ul>

  <p>
    이 메서드들은 “<strong>오래 걸리는 IO 작업을 스레드를 점유하지 않고 처리해준다</strong>”가 핵심이다.
  </p>

  <h3>1-3. 동기 vs 비동기 파일 읽기 간단 비교</h3>

  <pre><code class="language-csharp">// 동기 방식
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[1024];
int read = fs.Read(buffer, 0, buffer.Length);  // 여기서 스레드가 블록됨
</code></pre>

  <pre><code class="language-csharp">// 비동기 방식
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true);
byte[] buffer = new byte[1024];
int read = await fs.ReadAsync(buffer, 0, buffer.Length); // await, 스레드는 풀려 있음
</code></pre>

  <p>
    동기 <code>Read</code>는 작업이 끝날 때까지 스레드를 묶어 두고,  
    비동기 <code>ReadAsync</code>는 “이 IO 끝나면 알려줘”라고 OS에 맡겨놓고 스레드는 다른 일도 할 수 있다.
  </p>

  <hr>

  <h2>2. Task / Task&lt;TResult&gt; 타입 자세히 보기</h2>

  <h3>2-1. Task는 ‘작업의 상태’를 표현하는 객체</h3>

  <blockquote>
    <strong>Task = “미래에 끝날 작업 하나”를 표현하는 타입</strong>
  </blockquote>

  <p>Task는 내부적으로 상태(state)를 가진다. 예를 들면:</p>

  <ul>
    <li><code>Created</code> : 아직 시작 전</li>
    <li><code>Running</code> : 실행 중</li>
    <li><code>RanToCompletion</code> : 정상 완료</li>
    <li><code>Faulted</code> : 예외 발생 후 종료</li>
    <li><code>Canceled</code> : 취소됨</li>
  </ul>

  <pre><code class="language-csharp">Task t = Task.Run(() =>
{
    // 뭔가 작업
});

await t;

Console.WriteLine(t.Status); // RanToCompletion / Faulted / Canceled 등
</code></pre>

  <p>
    비동기 작업 안에서 예외가 나면 <code>Task</code>가 <code>Faulted</code> 상태가 되고,  
    그 Task를 <code>await</code>하는 시점에 예외가 다시 던져진다.
  </p>

  <pre><code class="language-csharp">Task t = Task.Run(() =>
{
    throw new InvalidOperationException("에러 발생");
});

try
{
    await t;  // 여기서 예외가 터짐
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
</code></pre>

  <h3>2-2. Task&lt;TResult&gt; = 결과를 담은 Task</h3>

  <pre><code class="language-csharp">Task&lt;int&gt; t = Task.Run(() =>
{
    // 오래 걸리는 계산
    return 42;
});

int result = await t;   // t.Result를 await로 안전하게 꺼내는 셈
</code></pre>

  <p>
    <code>Task&lt;T&gt;</code> 안에는 나중에 <code>T</code> 타입의 결과가 들어온다고 생각하면 된다.  
    <code>await</code>는 사실상:
  </p>

  <ol>
    <li>Task가 끝날 때까지 기다리고</li>
    <li>끝나면 <code>.Result</code> 값을 꺼내서</li>
    <li>예외가 있으면 다시 throw 해 주고</li>
    <li>그 값을 좌변 변수에 넣어주는 역할</li>
  </ol>

  <h3>2-3. Task와 Thread의 차이 (자주 헷갈리는 포인트)</h3>

  <ul>
    <li><strong>Thread</strong>는 OS 레벨의 실제 실행 흐름(스레드 하나)</li>
    <li><strong>Task</strong>는 “해야 할 일 하나(작업 단위)”를 표현하는 논리적인 개념</li>
  </ul>

  <p>
    Task는 스레드풀 위에서 돌 수도 있고, IO 대기 중에는 스레드를 점유하지 않고 기다릴 수도 있다.  
    즉, “비동기 = 항상 새로운 스레드를 만든다”는 개념이 아니다.
  </p>

  <hr>

  <h2>3. async 메서드의 반환 타입 규칙</h2>

  <h3>3-1. 기본 패턴 3가지</h3>

  <p><code>async</code>를 메서드에 붙일 때, 반환 타입은 보통 이 셋 중 하나다.</p>

  <ol>
    <li><code>async Task</code>  
      → 비동기 작업인데 리턴값이 필요 없는 경우</li>
    <li><code>async Task&lt;T&gt;</code>  
      → 비동기 작업인데 결과 값을 돌려줘야 하는 경우</li>
    <li><code>async void</code>  
      → 이벤트 핸들러용 특수 케이스 (WinForms, WPF의 <code>Button_Click</code> 등)</li>
  </ol>

  <pre><code class="language-csharp">// 반환값 없음
async Task DoWorkAsync()
{
    await Task.Delay(1000);
}

// int 결과 반환
async Task&lt;int&gt; GetNumberAsync()
{
    await Task.Delay(1000);
    return 42;
}

// UI 이벤트 핸들러 (WinForms, WPF 등)
async void Button_Click(object sender, EventArgs e)
{
    await Task.Delay(1000);
    MessageBox.Show("완료");
}
</code></pre>

  <h3>3-2. async void가 위험한 이유 (이벤트 핸들러 제외)</h3>

  <pre><code class="language-csharp">async void DoSomething()
{
    await Task.Delay(1000);
    throw new Exception("에러");
}
</code></pre>

  <ul>
    <li><code>async void</code> 메서드에서 발생한 예외는 호출자가 <code>try/catch</code>로 잡기 어렵다.</li>
    <li>호출자 입장에서 이 메서드가 끝났는지, 실패했는지, 취소됐는지를 알 방법이 없다.</li>
  </ul>

  <p>
    그래서 규칙은 간단하다.
  </p>

  <ul>
    <li><strong>일반 메서드</strong>: 웬만하면 <code>async void</code> 쓰지 말고 <strong><code>async Task</code></strong>로 만들기</li>
    <li><strong>이벤트 핸들러</strong>: 프레임워크가 <code>void</code> 시그니처를 요구하니 어쩔 수 없이 <code>async void</code></li>
  </ul>

  <h3>3-3. async 메서드는 내부적으로 상태 머신으로 변환된다</h3>

  <pre><code class="language-csharp">async Task&lt;int&gt; GetNumberAsync()
{
    await Task.Delay(1000);
    return 10;
}
</code></pre>

  <p>
    컴파일러는 위 코드를 컴파일할 때, 내부적으로 “상태 머신(state machine)” 코드로 변환한다.
  </p>

  <ul>
    <li><code>await</code>를 만날 때마다 “어디까지 실행했는지” 상태를 저장해 두고</li>
    <li>비동기 작업이 끝나면 그 상태를 다시 불러와서 이어서 실행한다</li>
  </ul>

  <p>
    개발자 입장에서는 동기 코드처럼 작성하지만,  
    실제로는 콜백 지옥을 컴파일러가 자동으로 펼쳐서 관리해 주는 셈이다.
  </p>

  <hr>

  <h2>4. async 메서드가 아닌 경우의 비동기 처리</h2>

  <h3>4-1. async 없이 Task 직접 반환하기</h3>

  <p>
    비동기 메서드를 만들 때 꼭 <code>async</code>를 써야만 하는 건 아니다.  
    <strong><code>Task</code> 또는 <code>Task&lt;T&gt;</code>만 반환해 주면 호출자는 <code>await</code>로 사용할 수 있다.</strong>
  </p>

  <pre><code class="language-csharp">Task&lt;int&gt; GetNumberAsync()
{
    // async 키워드 없음
    return Task.Run(() =>
    {
        // 무거운 작업이라고 가정
        Task.Delay(1000).Wait();
        return 7;
    });
}
</code></pre>

  <pre><code class="language-csharp">// 호출하는 쪽
int n = await GetNumberAsync();
</code></pre>

  <p>
    호출자는 이 메서드가 <code>async</code>인지 아닌지 알 필요가 없다.  
    <code>Task&lt;int&gt;</code>만 있으면 그냥 <code>await</code> 하면 끝이다.
  </p>

  <p>
    다만, 직접 Task를 조립하다 보면 예외/취소 처리 등에서 실수하기 쉽기 때문에  
    <strong>웬만하면 <code>async</code> + <code>await</code> 조합으로 작성하는 편이 더 안전하다</strong>.
  </p>

  <h3>4-2. 옛날 비동기 패턴(APM/EAP) 한 줄만 짚고 가기</h3>

  <p>async/await 이전에는 이런 패턴을 썼다.</p>

  <ul>
    <li><strong>APM (Asynchronous Programming Model)</strong>
      <ul>
        <li><code>BeginXXX</code> / <code>EndXXX</code> 콤보</li>
        <li>예: <code>BeginRead</code>, <code>EndRead</code></li>
      </ul>
    </li>
    <li><strong>EAP (Event-based Asynchronous Pattern)</strong>
      <ul>
        <li><code>XXXAsync</code> 메서드 + <code>XXXCompleted</code> 이벤트</li>
        <li>예: <code>WebClient.DownloadStringAsync</code> + <code>DownloadStringCompleted</code></li>
      </ul>
    </li>
  </ul>

  <p>
    요즘은 이 패턴들을 <code>TaskCompletionSource</code> 등으로 감싸서  
    <code>Task</code> 기반(TAP, Task-based Asynchronous Pattern)으로 바꾸고,  
    <code>async/await</code>로 사용하는 경우가 많다.
  </p>

  <h3>4-3. 동기 메서드를 비동기처럼 감싸는 Task.Run</h3>

  <pre><code class="language-csharp">Task&lt;string&gt; ReadFileAsyncWrapper(string path)
{
    return Task.Run(() =>
    {
        // 원래 동기 메서드
        return File.ReadAllText(path);
    });
}
</code></pre>

  <p>
    이렇게 하면 동기 메서드를 백그라운드 스레드에서 실행하게 만들 수 있다.  
    호출자는 다음처럼 쓸 수 있다.
  </p>

  <pre><code class="language-csharp">string text = await ReadFileAsyncWrapper("test.txt");
</code></pre>

  <p>
    다만 이 방식은 결국 스레드를 하나 잡아먹는 구조라서,  
    파일/네트워크 같은 IO는 가능하면 <strong>BCL에서 제공하는 진짜 비동기 메서드를 우선 사용하는 게 좋다</strong>.
  </p>

  <hr>

  <h2>5. 비동기 호출의 병렬 처리 (여러 작업 동시에)</h2>

  <h3>5-1. 순차 await vs 동시에 실행</h3>

  <p>나쁜(?) 패턴부터 보자.</p>

  <pre><code class="language-csharp">string html1 = await http.GetStringAsync(url1);
string html2 = await http.GetStringAsync(url2);
string html3 = await http.GetStringAsync(url3);
</code></pre>

  <ul>
    <li>url1 요청 → 완료</li>
    <li>그다음 url2 요청 → 완료</li>
    <li>그다음 url3 요청 → 완료</li>
  </ul>

  <p>
    각 요청이 1초씩 걸린다면 총 3초가 걸린다. IO는 대부분 “기다림”인데, 그 기다림을 순서대로 처리하는 셈이다.
  </p>

  <h3>5-2. 동시에 보내고 한 번에 기다리기 – Task.WhenAll</h3>

  <pre><code class="language-csharp">Task&lt;string&gt; t1 = http.GetStringAsync(url1);
Task&lt;string&gt; t2 = http.GetStringAsync(url2);
Task&lt;string&gt; t3 = http.GetStringAsync(url3);

// 세 요청을 동시에 출발시킨 뒤, 전부 끝날 때까지 기다리기
string[] results = await Task.WhenAll(t1, t2, t3);

string html1 = results[0];
string html2 = results[1];
string html3 = results[2];
</code></pre>

  <p>
    이 패턴에서는:
  </p>

  <ol>
    <li><code>GetStringAsync</code> 세 번을 빠르게 호출해서 <code>Task&lt;string&gt;</code> 세 개를 받는다.</li>
    <li>각 HTTP 요청이 동시에 진행된다.</li>
    <li><code>Task.WhenAll</code>은 “모든 Task가 끝날 때 완료되는 Task”를 하나 만들어 준다.</li>
    <li><code>await Task.WhenAll(...)</code> 하면, 세 요청이 모두 끝난 뒤에 결과 배열이 한 번에 들어온다.</li>
  </ol>

  <p>
    각 요청이 1초씩 걸려도, 전체적인 시간은 거의 1초 수준으로 줄어든다.
  </p>

  <h3>5-3. CPU 병렬 처리 간단 느낌</h3>

  <pre><code class="language-csharp">var tasks = new List&lt;Task&lt;int&gt;&gt;();

for (int i = 0; i &lt; 4; i++)
{
    int capture = i;
    tasks.Add(Task.Run(() => VeryHeavyCalc(capture)));
}

int[] results = await Task.WhenAll(tasks);
</code></pre>

  <p>
    <code>VeryHeavyCalc</code> 같은 CPU 중심 계산을 여러 조각으로 나누고,  
    <code>Task.Run</code>으로 여러 스레드에서 동시에 돌리는 예시다.
  </p>

  <p>
    실제로는 스레드풀이 여러 코어에 작업을 배분해서 병렬로 계산할 수 있고,  
    더 깊게 들어가면 <code>Parallel.For</code>, <code>Parallel.ForEach</code>, PLINQ(<code>AsParallel()</code>) 같은 것들도 배울 수 있다.
  </p>

  <p>
    입문 단계에서는 일단:
  </p>

  <blockquote>
    “여러 비동기 IO 작업을 함께 돌리고 싶으면  
    <strong>Task 여러 개 만들고 → <code>Task.WhenAll</code>로 한 번에 기다린다</strong>”
  </blockquote>

  <p>
    이 패턴만 확실하게 익혀둬도 비동기로 할 수 있는 일이 확 넓어진다.
  </p>

  <hr>

  <h2>정리</h2>

  <ul>
    <li><strong>BCL async 메서드</strong>  
      → .NET 기본 라이브러리에 붙어 있는 <code>XXXAsync</code> 메서드들.  
      IO 작업을 스레드 블록 없이 처리할 수 있게 해 준다.</li>

<pre><code>&lt;li&gt;&lt;strong&gt;Task / Task&amp;lt;T&amp;gt;&lt;/strong&gt;  
  → “미래에 끝날 작업”을 나타내는 타입.  
  &lt;code&gt;await&lt;/code&gt;로 자연스럽게 기다리면서 결과를 받을 수 있다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;async 메서드 반환 타입&lt;/strong&gt;  
  → 일반 메서드는 &lt;code&gt;async Task&lt;/code&gt;, &lt;code&gt;async Task&amp;lt;T&amp;gt;&lt;/code&gt;  
  → 이벤트 핸들러만 예외적으로 &lt;code&gt;async void&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;async 아닌 비동기&lt;/strong&gt;  
  → Task를 직접 만들어 반환하거나, 레거시 콜백 기반 비동기를 Task로 감싸서 사용.  
  동기 CPU 작업은 &lt;code&gt;Task.Run&lt;/code&gt;으로 백그라운드에서 돌릴 수도 있다.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;비동기 병렬 처리&lt;/strong&gt;  
  → 순차 &lt;code&gt;await&lt;/code&gt; 대신, Task 여러 개를 동시에 시작하고  
  &lt;code&gt;Task.WhenAll&lt;/code&gt;로 한 번에 기다리면 IO 작업을 동시에 진행할 수 있다.&lt;/li&gt;</code></pre>  </ul>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 - Task, async, await]]></title>
            <link>https://velog.io/@joongjii_1217/%EB%B9%84%EB%8F%99%EA%B8%B0-Task-async-await</link>
            <guid>https://velog.io/@joongjii_1217/%EB%B9%84%EB%8F%99%EA%B8%B0-Task-async-await</guid>
            <pubDate>Wed, 03 Dec 2025 06:59:11 GMT</pubDate>
            <description><![CDATA[<article>


  <hr>

  <h2>1. 동기 vs 비동기부터 직관적으로 이해하기</h2>

  <h3>1-1. 동기(synchronous) – 줄 서서 한 줄씩 처리</h3>

  <p>동기 코드는 “한 줄이 끝나야 다음 줄로 넘어가는 방식”이다.</p>

  <pre><code class="language-csharp">Console.WriteLine("1. 시작");

DoWork();   // 여기서 이 일이 끝날 때까지 멈춰 있음

Console.WriteLine("2. 끝");
</code></pre>

  <ul>
    <li><code>DoWork()</code> 안에서 5초가 걸리면,</li>
    <li>그 5초 동안 프로그램은 <strong>이 줄에서 멈춰 있다</strong>.</li>
    <li>그 다음 줄(<code>"2. 끝"</code> 출력)은 절대 먼저 실행되지 않는다.</li>
  </ul>

  <p>UI 프로그램(WinForms, WPF, 웹 등)에서는 이렇게 오래 걸리는 작업을 동기로 하면, 화면이 멈춘 것처럼 느껴진다.</p>

  <h3>1-2. 비동기(asynchronous) – “알바에게 시키고 나는 딴 일 계속”</h3>

  <p>편의점 사장 입장에서 생각해 보자.</p>

  <ul>
    <li>손님: “컵라면 뜨거운 물 좀 부어주세요.”</li>
    <li>동기 방식이라면?
      <ul>
        <li>사장이 컵라면에 물 붓고 <strong>라면 익을 때까지 3분 동안 계속 지켜본다</strong>.</li>
        <li>그동안 다른 손님 계산도 못하고, 가게 전체가 멈춘 느낌이 된다.</li>
      </ul>
    </li>
    <li>비동기 방식이라면?
      <ul>
        <li>사장: 알바한테 “저 컵라면 3분 뒤에 완성되면 손님께 드려.” 하고 맡긴다.</li>
        <li>그 사이 사장은 <strong>다른 손님 계산</strong>을 계속 처리한다.</li>
        <li>3분 후, 알바가 “컵라면 됐어요!” → 사장이 손님에게 건네준다.</li>
      </ul>
    </li>
  </ul>

  <p>
    여기서 비유를 맞춰 보면:
  </p>

  <ul>
    <li><strong>알바에게 맡긴 일 하나</strong> → <code>Task</code></li>
    <li><strong>“이거 끝나면 알려줘” 하고 기다리는 지점</strong> → <code>await</code></li>
    <li><strong>“컵라면 끓여줘”라고 정의된 메서드</strong> → <code>async</code> 메서드</li>
  </ul>

  <hr>

  <h2>2. 세 키워드 한 줄 정리</h2>

  <ul>
    <li><strong>Task</strong>: “미래에 끝날 작업 하나”를 나타내는 타입</li>
    <li><strong>async</strong>: “이 메서드는 비동기 스타일로 작성할 거야”라는 표시</li>
    <li><strong>await</strong>: “이 Task가 끝날 때까지 잠깐 맡겨두고, 끝나면 여기서부터 이어서 실행해줘”</li>
  </ul>

  <p>각각을 조금 더 자세히 보자.</p>

  <h3>2-1. Task – 미래에 끝날 작업(약속)</h3>

  <blockquote>
    <strong>Task = 아직 안 끝났을 수도 있지만 언젠가는 끝날 ‘작업’ 하나를 나타내는 객체</strong>
  </blockquote>

  <p>두 가지 형태가 있다.</p>

  <ul>
    <li><code>Task</code> – 리턴값 없는 작업</li>
    <li><code>Task&lt;T&gt;</code> – 나중에 <code>T</code> 타입 결과를 돌려주는 작업</li>
  </ul>

  <pre><code class="language-csharp">Task t = WorkAsync();          // 나중에 끝나는 작업 (리턴값 없음)
Task&lt;int&gt; t2 = CalcAsync();    // 나중에 int 결과를 하나 돌려줌
</code></pre>

  <p>
    감각적으로는 <strong>“배달 주문 번호”</strong>랑 비슷하다.<br>
    주문 넣으면 바로 음식이 나오는 게 아니라 <strong>주문 번호(Task)</strong>를 받고, 음식은 나중에 나온다.
  </p>

  <h3>2-2. async – “이 메서드는 비동기 메서드입니다” 표시</h3>

  <p>비동기 메서드를 만들 때는 이렇게 쓴다.</p>

  <pre><code class="language-csharp">async Task DoSomethingAsync() { ... }        // 리턴값 없음
async Task&lt;int&gt; GetNumberAsync() { ... }     // int 리턴
</code></pre>

  <p>
    <code>async</code>는 <strong>“이 안에서 <code>await</code>를 쓸 거야”</strong>라고 표시하는 키워드라고 보면 이해하기 쉽다.<br>
    실제 비동기 동작은 <code>await</code>가 담당한다.
  </p>

  <h3>2-3. await – “끝나면 다시 여기로 돌아와”</h3>

  <pre><code class="language-csharp">await Task.Delay(2000);
</code></pre>

  <p>
    이 한 줄의 의미는:
  </p>

  <blockquote>
    “2초 뒤에 끝나는 작업을 하나 시작해 놓고,<br>
    그 작업이 끝나면 <strong>현재 메서드의 다음 줄부터 다시 실행</strong>해 줘.”
  </blockquote>

  <p>
    중요한 점은, 이 기다리는 동안 <strong>스레드를 붙잡고 자는 게 아니라</strong>는 것.<br>
    “2초 뒤에 여기서 다시 이어서 실행해”라고 예약해 놓고, 스레드는 다른 일을 할 수 있게 돌려준다.
  </p>

  <hr>

  <h2>3. 가장 단순한 async/await 예제</h2>

  <p>일단 콘솔 기준으로 제일 짧은 예제를 보자.</p>

  <pre><code class="language-csharp">using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("1. 시작");

        await Task.Delay(2000);  // 2초 동안 비동기 지연

        Console.WriteLine("2. 2초 후에 실행");
    }
}
</code></pre>

  <h3>3-1. 실행 흐름 설명</h3>

  <ol>
    <li><code>"1. 시작"</code>이 바로 출력된다.</li>
    <li><code>await Task.Delay(2000);</code>를 만난다.
      <ul>
        <li>“2초 뒤에 다시 여기로 돌아와서 그 다음 줄부터 실행해라”라고 예약.</li>
        <li>그 사이 스레드는 놀고 있지 않고, 다른 작업에 쓸 수 있다.</li>
      </ul>
    </li>
    <li>2초가 지나면, 예약된 대로
      <ul>
        <li><code>Console.WriteLine("2. 2초 후에 실행");</code>이 실행된다.</li>
      </ul>
    </li>
  </ol>

  <p>
    겉으로 보기에는 <code>Thread.Sleep(2000)</code> 과 비슷하게 “2초 후에 다음 줄 실행”처럼 보이지만,  
    내부는 <strong>스레드를 멈추지 않고 비동기로 동작</strong>한다는 점이 다르다.
  </p>

  <hr>

  <h2>4. 동기 vs 비동기 코드 비교</h2>

  <h3>4-1. 동기 버전 – Thread.Sleep</h3>

  <pre><code class="language-csharp">using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("A");

        Thread.Sleep(2000);  // 2초 동안 스레드가 진짜로 멈춰 있음

        Console.WriteLine("B");
    }
}
</code></pre>

  <ul>
    <li>출력 순서: <code>A</code> → (2초 멈춤) → <code>B</code></li>
    <li>그 2초 동안 이 스레드는 아무 일도 못 한다.</li>
    <li>UI 스레드에서 이렇게 하면 화면이 “응답 없음”처럼 멈춘다.</li>
  </ul>

  <h3>4-2. 비동기 버전 – await Task.Delay</h3>

  <pre><code class="language-csharp">using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("A");

        await Task.Delay(2000);  // 2초 동안 비동기 지연

        Console.WriteLine("B");
    }
}
</code></pre>

  <p>출력 결과는 똑같이 <code>A</code> → 2초 후 → <code>B</code> 이지만, 내부 동작이 다르다.</p>

  <ul>
    <li><code>Thread.Sleep</code> : 스레드가 2초 동안 완전히 쉰다.</li>
    <li><code>await Task.Delay</code> :
      <ul>
        <li>“2초 뒤에 다시 이어서 실행해”라고 예약해놓고,</li>
        <li>그동안 스레드는 다른 일을 할 수 있게 풀어준다.</li>
      </ul>
    </li>
  </ul>

  <p>
    콘솔 앱에서는 체감이 안 될 수 있지만, UI 프로그램(WinForms, WPF, MAUI 등)에서는  
    이 차이가 <strong>“화면이 멈추느냐, 멈추지 않느냐”</strong>로 크게 드러난다.
  </p>

  <hr>

  <h2>5. 라면 끓이기 예제로 보는 Task + async + await</h2>

  <p>
    아까 편의점 비유를 실제 코드로 옮겨 보면 훨씬 이해가 쉽다.
  </p>

  <pre><code class="language-csharp">using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("1. 라면 주문 넣기");

        string result = await CookRamenAsync();  // 라면 끓이는 비동기 메서드 호출

        Console.WriteLine("3. 라면 받음: " + result);
    }

    // 라면 끓이는 비동기 메서드
    static async Task&lt;string&gt; CookRamenAsync()
    {
        Console.WriteLine("2. 라면 끓이는 중... (3초)");

        await Task.Delay(3000);  // 3초 동안 끓이는 중이라고 가정

        return "완성된 라면";
    }
}
</code></pre>

  <h3>5-1. 여기서 중요한 포인트</h3>

  <ul>
    <li><code>CookRamenAsync</code>의 선언:
      <pre><code class="language-csharp">static async Task&lt;string&gt; CookRamenAsync()</code></pre>
      <ul>
        <li><code>async</code> → 이 메서드는 비동기 스타일로 동작한다.</li>
        <li><code>Task&lt;string&gt;</code> → “나중에 string 결과를 줄 작업”을 나타낸다.</li>
      </ul>
    </li>

<pre><code>&lt;li&gt;&lt;code&gt;await CookRamenAsync()&lt;/code&gt;의 의미:
  &lt;ul&gt;
    &lt;li&gt;&lt;code&gt;CookRamenAsync()&lt;/code&gt;를 호출하면 &lt;code&gt;Task&amp;lt;string&amp;gt;&lt;/code&gt;이 하나 생긴다.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;await&lt;/code&gt;는 그 작업이 끝날 때까지 기다렸다가,&lt;/li&gt;
    &lt;li&gt;끝나면 그 결과(&lt;code&gt;string&lt;/code&gt;)를 꺼내서 &lt;code&gt;result&lt;/code&gt;에 넣어준다.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/li&gt;</code></pre>  </ul>

  <p>
    정리하면:
  </p>

  <ul>
    <li><code>async Task&lt;T&gt;</code> 메서드 → “나중에 T를 줄게”라고 약속하는 비동기 메서드</li>
    <li><code>await</code> → “그 약속(Task&lt;T&gt;)이 끝날 때까지 기다렸다가, T를 꺼내서 계속 진행해 줘”</li>
  </ul>

  <hr>

  <h2>6. 한 번에 정리해 보기</h2>

  <h3>6-1. 세 키워드 요약</h3>

  <ul>
    <li><strong>Task</strong>
      <ul>
        <li>“미래에 끝날 작업”을 나타내는 타입</li>
        <li><code>Task</code>, <code>Task&lt;T&gt;</code> 두 가지가 핵심</li>
      </ul>
    </li>
    <li><strong>async</strong>
      <ul>
        <li>“이 메서드는 비동기로 동작할 거야”라는 표시</li>
        <li>메서드 리턴 타입을 <code>Task</code> 또는 <code>Task&lt;T&gt;</code>로 만든다.</li>
      </ul>
    </li>
    <li><strong>await</strong>
      <ul>
        <li>“이 Task가 끝날 때까지 기다렸다가, 끝나면 다음 줄부터 다시 이어서 실행해 줘.”</li>
        <li>스레드를 묶어두지 않고, 다시 사용할 수 있게 반납하는 패턴</li>
      </ul>
    </li>
  </ul>

  <h3>6-2. 왜 이렇게 복잡하게 쓸까?</h3>

  <ul>
    <li>파일 IO, DB 쿼리, 웹 API 요청처럼 <strong>시간이 오래 걸리는 작업</strong> 때문에
      프로그램 전체(특히 UI)가 멈추지 않게 하려고</li>
    <li>원래 비동기 코드는 콜백 지옥처럼 복잡해지기 쉬운데,
      <code>async/await</code>를 쓰면 <strong>동기 코드처럼 위에서 아래로 읽히는 구조</strong>로 만들 수 있어서 유지보수가 훨씬 쉽다.</li>
  </ul>

  <hr>


</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[형변환]]></title>
            <link>https://velog.io/@joongjii_1217/%ED%98%95%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@joongjii_1217/%ED%98%95%EB%B3%80%ED%99%98</guid>
            <pubDate>Tue, 02 Dec 2025 07:06:59 GMT</pubDate>
            <description><![CDATA[<article>

  <h2>1. 형변환이란?</h2>

  <blockquote>
    <strong>형변환(캐스팅) = 어떤 타입의 값을 다른 타입처럼 취급하는 것</strong>
  </blockquote>

  <p>예를 들면:</p>

  <pre><code class="language-csharp">int i = 10;
double d = i;      // int → double</code></pre>

  <p>
    이렇게 <code>int</code> 값을 <code>double</code> 변수에 넣는 것도 형변환이다.<br>
    C#에서는 크게 이런 종류들이 자주 나온다.
  </p>

  <ul>
    <li>묵시적 형변환 (implicit)</li>
    <li>명시적 형변환 (explicit, <code>(타입)</code> 캐스팅)</li>
    <li>참조 타입 캐스팅 (상속 관계, <code>is</code>, <code>as</code>)</li>
    <li>박싱/언박싱 (값 타입 ↔ object)</li>
  </ul>

  <hr>

  <h2>2. 묵시적 형변환 (Implicit Conversion)</h2>

  <p>
    <strong>컴파일러가 “이건 안전하다”라고 보고, 자동으로 해주는 형변환</strong>이다.<br>
    주로 <strong>“작은 타입 → 큰 타입”</strong>으로 갈 때 가능하다.
  </p>

  <h3>2-1. 숫자끼리 묵시적 형변환</h3>

  <pre><code class="language-csharp">int i = 10;
double d = i;   // OK, int → double 자동 변환</code></pre>

  <ul>
    <li><code>int</code>는 4바이트, <code>double</code>은 8바이트</li>
    <li><code>int</code> 값은 모두 <code>double</code>로 표현 가능</li>
    <li>데이터 손실 위험이 거의 없기 때문에 C#이 자동으로 허용</li>
  </ul>

  <p>다른 예:</p>

  <pre><code class="language-csharp">byte b = 100;
int i = b;      // OK, byte → int
long l = i;     // OK, int → long
float f = l;    // OK, long → float (근사값이 될 수 있지만 허용)</code></pre>

  <p>
    한 줄로 정리하면:
  </p>

  <blockquote>
    <strong>“작은 그릇 → 큰 그릇”으로 가는 건 대부분 자동 형변환이 된다.</strong>
  </blockquote>

  <hr>

  <h2>3. 명시적 형변환 (Explicit Casting)</h2>

  <p>
    이번에는 반대로, <strong>개발자가 직접 <code>(타입)</code>을 써줘야 하는 경우</strong>다.<br>
    주로 “큰 타입 → 작은 타입” 또는 <strong>데이터 손실 가능성이 있는 경우</strong>에 필요하다.
  </p>

  <h3>3-1. 숫자끼리 명시적 형변환 예제</h3>

  <pre><code class="language-csharp">double d = 3.14;
int i = (int)d;   // i는 3 (소수점 잘림)</code></pre>

  <ul>
    <li>소수점 이하 정보가 날아갈 수 있기 때문에</li>
    <li>C#은 자동 변환을 허용하지 않는다.</li>
    <li>개발자가 <code>(int)</code>를 써서 “내가 책임진다”고 표시해야 한다.</li>
  </ul>

  <p>또 다른 예:</p>

  <pre><code class="language-csharp">int big = 300;
byte b = (byte)big;  // 값이 잘릴 수 있음 (0~255 범위를 넘어서면 값이 뒤틀림)</code></pre>

  <p>
    이런 식으로 <strong>데이터가 잘리거나 값이 달라질 수 있는 변환</strong>은  
    반드시 <code>(타입)</code>으로 명시적 캐스팅이 필요하다.
  </p>

  <hr>

  <h2>4. 참조 타입 형변환 (상속, 업캐스팅/다운캐스팅)</h2>

  <p>
    숫자 타입만 형변환이 있는 게 아니다.  
    클래스/인터페이스 같은 <strong>참조 타입</strong>도 형변환이 있다.
  </p>

  <h3>4-1. 업캐스팅(Upcasting): 자식 → 부모 (안전, 묵시적)</h3>

  <pre><code class="language-csharp">class Animal
{
    public void Eat() =&gt; Console.WriteLine("먹는다");
}

class Dog : Animal
{
    public void Bark() =&gt; Console.WriteLine("멍멍");
}

class Program
{
    static void Main()
    {
        Dog d = new Dog();
        Animal a = d;   // 업캐스팅: Dog → Animal (묵시적 형변환 가능)

        a.Eat();        // OK
        // a.Bark();    // 컴파일 에러 (Animal 타입에는 Bark가 없음)
    }
}</code></pre>

  <ul>
    <li><code>Dog</code>는 <code>Animal</code>을 상속하므로, <code>Dog</code>는 곧 <code>Animal</code>이다.</li>
    <li>자식 → 부모로 가는 건 항상 안전하다.</li>
    <li>그래서 C#이 자동으로 형변환을 허용한다(묵시적).</li>
  </ul>

  <h3>4-2. 다운캐스팅(Downcasting): 부모 → 자식 (명시적, 위험할 수 있음)</h3>

  <pre><code class="language-csharp">Animal a = new Dog();   // 실제 인스턴스는 Dog
Dog d = (Dog)a;         // 다운캐스팅 (OK)

d.Bark();               // 사용 가능</code></pre>

  <p>하지만 이런 경우도 있다:</p>

  <pre><code class="language-csharp">Animal a = new Animal();
Dog d = (Dog)a;   // 컴파일은 되지만, 실행 시 InvalidCastException 발생</code></pre>

  <ul>
    <li><code>a</code>의 실제 인스턴스는 <code>Animal</code>일 뿐, <code>Dog</code>가 아니다.</li>
    <li>이 경우 다운캐스팅하면 런타임에서 예외가 터진다.</li>
  </ul>

  <p>
    그래서 C#에서는 안전하게 형 변환을 하기 위해 <strong><code>is</code></strong>와 <strong><code>as</code></strong>를 제공한다.
  </p>

  <hr>

  <h2>5. <code>is</code>, <code>as</code> 연산자</h2>

  <h3>5-1. <code>is</code> : “이 타입이 맞냐?” 검사</h3>

  <pre><code class="language-csharp">Animal a = new Dog();

if (a is Dog)
{
    Console.WriteLine("a는 Dog 타입이다");
}</code></pre>

  <p>C# 패턴 매칭을 쓰면 이렇게도 가능하다.</p>

  <pre><code class="language-csharp">if (a is Dog d)
{
    d.Bark(); // 여기서 d는 Dog 타입으로 캐스팅된 상태
}</code></pre>

  <h3>5-2. <code>as</code> : 캐스팅 시도, 안 되면 null</h3>

  <pre><code class="language-csharp">Animal a = new Dog();

Dog d = a as Dog;   // 캐스팅 성공 → d는 Dog
if (d != null)
{
    d.Bark();
}

Animal a2 = new Animal();
Dog d2 = a2 as Dog; // 캐스팅 실패 → d2는 null</code></pre>

  <ul>
    <li><code>as</code>는 예외를 던지지 않는다.</li>
    <li>캐스팅이 안 되면 그냥 <code>null</code>을 돌려준다.</li>
    <li>그래서 보통 <code>if (d != null)</code> 체크와 같이 사용한다.</li>
  </ul>

  <hr>

  <h2>6. 문자열 ↔ 숫자 변환 (Parse / TryParse)</h2>

  <p>
    이건 엄밀히 말하면 “캐스팅”보다는 <strong>파싱(Parsing)</strong>이지만,  
    실무에서는 형변환과 같이 다루는 경우가 많다.
  </p>

  <pre><code class="language-csharp">string s = "123";

// 문자열 → 숫자
int i1 = int.Parse(s);             // 실패 시 예외 발생

int i2;
bool ok = int.TryParse(s, out i2); // 실패해도 예외 없이 false 반환

// 숫자 → 문자열
int n = 456;
string s2 = n.ToString();</code></pre>

  <ul>
    <li>문자열 → 숫자: <code>Parse</code>, <code>TryParse</code></li>
    <li>숫자 → 문자열: <code>ToString()</code></li>
  </ul>

  <hr>

  <h2>7. 박싱(Boxing) / 언박싱(Unboxing)</h2>

  <p>
    C#에서는 모든 타입이 궁극적으로 <code>object</code>를 상속한다.<br>
    그래서 <code>int</code> 같은 값 타입도 <code>object</code>로 다룰 수 있는데,  
    이때 등장하는 개념이 <strong>박싱/언박싱</strong>이다.
  </p>

  <h3>7-1. 박싱(Boxing) – 값 타입 → object</h3>

  <pre><code class="language-csharp">int i = 10;
object o = i;   // 박싱 발생</code></pre>

  <p>
    값 타입 <code>i</code>가 힙에 <code>object</code> 형태로 포장되는 느낌으로 이해하면 편하다.
  </p>

  <h3>7-2. 언박싱(Unboxing) – object → 값 타입</h3>

  <pre><code class="language-csharp">object o = 10;     // int 박싱
int i = (int)o;    // 언박싱 (명시적 캐스팅 필요)</code></pre>

  <p>주의할 점:</p>

  <pre><code class="language-csharp">object o = 10;   // 실제로는 int

double d = (double)o;  // 런타임 오류 (InvalidCastException)</code></pre>

  <ul>
    <li><code>o</code> 안에 실제 들어있는 건 <code>int</code>인데,</li>
    <li><code>double</code>로 바로 캐스팅하려고 하면 예외가 발생한다.</li>
    <li>언박싱은 “원래 값 타입”으로만 가능하다.</li>
  </ul>

  <hr>

  <h2>8. 한 번에 정리</h2>

  <ul>
    <li>
      <strong>묵시적 형변환 (implicit)</strong>
      <ul>
        <li>컴파일러가 자동으로 해주는 형변환</li>
        <li>주로 작은 숫자 타입 → 큰 숫자 타입, 자식 → 부모(업캐스팅)</li>
        <li>예: <code>int i = 10; double d = i;</code></li>
      </ul>
    </li>
    <li>
      <strong>명시적 형변환 (explicit casting)</strong>
      <ul>
        <li><code>(타입)</code>을 써서 개발자가 직접 캐스팅</li>
        <li>데이터 손실 가능성이 있을 때 필요</li>
        <li>예: <code>double d = 3.14; int i = (int)d;</code></li>
      </ul>
    </li>
    <li>
      <strong>참조 타입 캐스팅</strong>
      <ul>
        <li>상속 관계에서의 형변환</li>
        <li>업캐스팅: 자식 → 부모 (자동, 안전)</li>
        <li>다운캐스팅: 부모 → 자식 (명시적, 잘못하면 런타임 예외)</li>
        <li><code>is</code>, <code>as</code>로 안전하게 검사/캐스팅 가능</li>
      </ul>
    </li>
    <li>
      <strong>문자열 ↔ 숫자 변환</strong>
      <ul>
        <li>문자열 → 숫자: <code>int.Parse</code>, <code>int.TryParse</code></li>
        <li>숫자 → 문자열: <code>ToString()</code></li>
      </ul>
    </li>
    <li>
      <strong>박싱 / 언박싱</strong>
      <ul>
        <li>값 타입 → object로 포장: 박싱</li>
        <li>object → 값 타입으로 꺼내기: 언박싱 (<code>(int)o</code> 같은 형변환 필요)</li>
        <li>언박싱은 원래 타입으로만 가능</li>
      </ul>
    </li>
  </ul>

</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[partial class]]></title>
            <link>https://velog.io/@joongjii_1217/partial-class</link>
            <guid>https://velog.io/@joongjii_1217/partial-class</guid>
            <pubDate>Mon, 01 Dec 2025 10:39:48 GMT</pubDate>
            <description><![CDATA[<article>
<hr>

  <h2>1. <code>partial class</code> 한 줄 정의</h2>

  <blockquote>
    <strong>partial class = “하나의 클래스를 여러 파일로 나눠서 정의할 수 있게 해주는 문법”</strong>
  </blockquote>

  <p>
    보통은 “클래스 하나 = 파일 하나”처럼 생각하기 쉬운데, C#에서는 하나의 클래스를 여러 .cs 파일에 나눠서 적을 수 있다.<br>
    나눠져 있는 코드들은 컴파일할 때 <strong>컴파일러가 전부 합쳐서 하나의 클래스</strong>로 만들어 준다.
  </p>

  <h3>간단 예제</h3>

  <p>예를 들어, 아래처럼 두 파일이 있다고 해 보자.</p>

  <h4><code>Person.cs</code></h4>

  <pre><code class="language-csharp">public partial class Person
{
    public string Name { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"안녕, 나는 {Name}");
    }
}</code></pre>

  <h4><code>Person.Address.cs</code></h4>

  <pre><code class="language-csharp">public partial class Person
{
    public string Address { get; set; }

    public void PrintAddress()
    {
        Console.WriteLine($"주소: {Address}");
    }
}</code></pre>

  <p>
    이 두 파일은 컴파일될 때 이렇게 하나의 <code>Person</code> 클래스로 합쳐진 것처럼 동작한다.
  </p>

  <pre><code class="language-csharp">public class Person
{
    public string Name { get; set; }
    public void SayHello() { ... }

    public string Address { get; set; }
    public void PrintAddress() { ... }
}</code></pre>

  <p>
    개발자는 파일을 나눠서 관리하지만, 프로그램이 실행될 때는 <strong>그냥 한 클래스</strong>라고 생각하면 된다.
  </p>

  <hr>

  <h2>2. 왜 <code>partial class</code>를 쓰나?</h2>

  <p>“그냥 한 파일에 다 쓰면 되지, 왜 쪼개서 쓰지?” 라는 생각이 들 수 있다. 대표적인 이유는 세 가지 정도다.</p>

  <h3>2-1. 자동 생성 코드와 내가 짠 코드를 분리하려고</h3>

  <p>
    실무에서 <strong>가장 많이 쓰이는 이유</strong>는 바로 이거다.
  </p>

  <ul>
    <li>WinForms, WPF, ASP.NET WebForms 같은 UI 디자이너를 쓰면</li>
    <li>버튼, 텍스트박스 같은 컨트롤을 배치하는 순간 디자이너가 자동으로 코드를 만들어 준다.</li>
  </ul>

  <p>예를 들어 WinForms에서 폼 하나를 만들면 이런 구조를 많이 보게 된다.</p>

  <h4><code>Form1.cs</code> (내가 작성하는 코드)</h4>

  <pre><code class="language-csharp">public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent(); // 디자이너가 만든 설정 코드 호출
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("클릭!");
    }
}</code></pre>

  <h4><code>Form1.Designer.cs</code> (디자이너가 자동 생성하는 코드)</h4>

  <pre><code class="language-csharp">partial class Form1
{
    private System.Windows.Forms.Button button1;

    private void InitializeComponent()
    {
        this.button1 = new System.Windows.Forms.Button();
        // 위치, 크기, 텍스트, 이벤트 핸들러 연결 등 자동 생성 코드
    }
}</code></pre>

  <p>
    두 파일 모두 <code>partial class Form1</code>로 선언되어 있기 때문에, 둘이 합쳐져서 하나의 <code>Form1</code> 클래스가 된다.
  </p>

  <p>
    이렇게 나누면 좋은 점:
  </p>

  <ul>
    <li><strong>디자이너가 건드리는 코드</strong>와 <strong>내가 직접 쓰는 코드</strong>를 파일 수준에서 분리할 수 있다.</li>
    <li>디자이너가 <code>.Designer.cs</code> 파일을 자동으로 고쳐도, 내 코드가 섞이지 않는다.</li>
    <li>내가 실수로 디자이너 코드 부분을 지워서 폼이 깨지는 일을 줄일 수 있다.</li>
  </ul>

  <h3>2-2. 클래스가 너무 커서 논리적으로 쪼개고 싶을 때</h3>

  <p>
    어떤 클래스가 기능이 많아지면 줄 수가 엄청나게 길어질 수 있다. 예를 들어 <code>OrderService</code>에
  </p>

  <ul>
    <li>주문 조회</li>
    <li>주문 생성</li>
    <li>주문 취소</li>
    <li>정산 처리</li>
    <li>로그 기록</li>
  </ul>

  <p>등이 한꺼번에 들어 있으면 유지보수가 힘들다.</p>

  <p>이럴 때 <code>partial</code>로 파일을 역할별로 나눌 수 있다.</p>

  <h4><code>OrderService.Query.cs</code></h4>

  <pre><code class="language-csharp">public partial class OrderService
{
    public Order GetOrder(int id) { ... }

    public List&lt;Order&gt; GetOrdersByCustomer(string customerCode) { ... }
}</code></pre>

  <h4><code>OrderService.Command.cs</code></h4>

  <pre><code class="language-csharp">public partial class OrderService
{
    public void CreateOrder(Order order) { ... }

    public void CancelOrder(int id) { ... }
}</code></pre>

  <p>
    이런 식으로 나누면:
  </p>

  <ul>
    <li>“조회 관련 코드만 보고 싶다” → <code>.Query.cs</code>만 열면 됨</li>
    <li>“등록/수정 관련 코드만 보고 싶다” → <code>.Command.cs</code>만 보면 됨</li>
    <li>파일이 나뉘어 있어서 검색/관리/리뷰할 때 훨씬 편하다.</li>
  </ul>

  <h3>2-3. 여러 사람이 한 클래스를 동시에 작업할 때</h3>

  <p>
    팀 작업을 할 때, 한 클래스에 기능이 몰려 있으면 한 파일에서 충돌이 많이 날 수 있다.
  </p>

  <ul>
    <li>A 개발자: 조회 기능 작업</li>
    <li>B 개발자: 등록/수정 기능 작업</li>
  </ul>

  <p>
    이런 상황에서 파일을 나눠 두면, 서로 다른 파일에서 작업할 수 있어서 충돌을 줄일 수 있다.
  </p>

  <hr>

  <h2>3. <code>partial class</code>의 규칙과 주의사항</h2>

  <h3>3-1. 이름이 같아야 한다</h3>

  <pre><code class="language-csharp">public partial class Person { ... }
public partial class Person { ... }  // OK, 같은 클래스의 다른 부분

public partial class PersonEx { ... } // 이름이 다르면 완전히 다른 클래스</code></pre>

  <h3>3-2. 같은 namespace 안에 있어야 한다</h3>

  <pre><code class="language-csharp">// 파일 1
namespace MyApp.Models
{
    public partial class Person { ... }
}

// 파일 2
namespace MyApp.Models   // 네임스페이스가 같아야 합쳐진다
{
    public partial class Person { ... }
}</code></pre>

  <h3>3-3. 접근 제한자(public, internal 등)는 일관되게</h3>

  <p>
    보통 한 파일에만 <code>public partial class</code>를 적고, 나머지는 <code>partial class</code>만 적어도 컴파일은 잘 된다.<br>
    하지만 가독성 측면에서는 <strong>가능하면 같은 접근제한자로 맞춰주는 편이 좋다</strong>.
  </p>

  <h3>3-4. 실행될 때는 그냥 “한 클래스”일 뿐</h3>

  <p>
    <strong>중요한 포인트</strong>는, partial은 어디까지나 “코드 작성 편의를 위한 문법”이라는 점이다.
  </p>

  <ul>
    <li>런타임(실행 시점)에는 “부분1, 부분2” 같은 개념이 없다.</li>
    <li>그냥 <strong>하나의 <code>Person</code> 클래스</strong>만 존재한다.</li>
    <li>partial인지 아닌지 구분할 수 있는 정보도 별로 중요하지 않다.</li>
  </ul>

  <hr>

  <h2>4. 콘솔 프로젝트에서 직접 써 보는 간단 예제</h2>

  <p>실제로 partial class가 어떻게 동작하는지, 아주 간단한 예제로 확인해 보자.</p>

  <h3><code>Person.Basic.cs</code></h3>

  <pre><code class="language-csharp">public partial class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void PrintBasicInfo()
    {
        Console.WriteLine($"이름: {Name}, 나이: {Age}");
    }
}</code></pre>

  <h3><code>Person.Job.cs</code></h3>

  <pre><code class="language-csharp">public partial class Person
{
    public string JobTitle { get; set; }

    public void PrintJobInfo()
    {
        Console.WriteLine($"직업: {JobTitle}");
    }
}</code></pre>

  <h3><code>Program.cs</code></h3>

  <pre><code class="language-csharp">using System;

class Program
{
    static void Main()
    {
        Person p = new Person();
        p.Name = "홍길동";
        p.Age = 30;
        p.JobTitle = "개발자";

        p.PrintBasicInfo(); // 이름, 나이 출력
        p.PrintJobInfo();   // 직업 출력

        Console.ReadKey();
    }
}</code></pre>

  <p>
    비록 파일은 3개로 나눠져 있지만, 실제로는 그냥 <code>Person</code>이라는 클래스 한 개가 있는 것처럼 자연스럽게 동작한다.
  </p>

  <hr>

  <h2>5. 보너스: partial method 간단 맛보기</h2>

  <p>
    <code>partial class</code> 안에서는 <strong><code>partial method</code></strong>도 쓸 수 있다.
    이건 자동 생성 코드와 후처리 코드를 나눌 때 유용하다.
  </p>

  <h3>파일 1</h3>

  <pre><code class="language-csharp">public partial class Person
{
    partial void OnCreated();

    public Person()
    {
        OnCreated(); // 구현이 있으면 호출, 없으면 컴파일 시 제거됨
    }
}</code></pre>

  <h3>파일 2</h3>

  <pre><code class="language-csharp">public partial class Person
{
    partial void OnCreated()
    {
        Console.WriteLine("Person 객체가 생성되었습니다.");
    }
}</code></pre>

  <p>
    구현을 해 두면 생성자에서 자동으로 호출되고,<br>
    구현을 안 하면 컴파일러가 그냥 이 호출 자체를 지워 버린다.
  </p>

  <p>
    이건 조금 고급 기능에 가까우니까,  
    “partial class 안에 partial method라는 것도 있다” 정도만 알고 넘어가도 충분하다.
  </p>

  <hr>

  <h2>6. 정리</h2>

  <ul>
    <li><strong>partial class</strong>는 하나의 클래스를 여러 파일로 나누기 위한 문법이다.</li>
    <li>컴파일 시에 여러 파일의 partial 정의들을 합쳐서 하나의 클래스가 된다.</li>
    <li>주로:
      <ul>
        <li>자동 생성 코드와 직접 작성한 코드를 분리할 때</li>
        <li>클래스가 너무 커서 기능별로 파일을 나누고 싶을 때</li>
        <li>여러 개발자가 한 클래스를 나눠 작업할 때 사용된다.</li>
      </ul>
    </li>
    <li>이름, namespace가 동일해야 같은 클래스의 partial로 합쳐질 수 있다.</li>
    <li>실행 시점에는 그냥 일반 클래스와 완전히 똑같이 취급된다.</li>
  </ul>

  <p>
    앞으로 프로젝트에서 <code>Form1.Designer.cs</code>, <code>SomeWindow.g.cs</code> 같은 파일을 보게 되면,
    “아, 이게 바로 partial class로 나눠진 자동 생성 코드구나” 하고 이해하면 된다.
  </p>
</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[스레드(Thread)와 스레드풀(ThreadPool) ]]></title>
            <link>https://velog.io/@joongjii_1217/%EC%8A%A4%EB%A0%88%EB%93%9CThread%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C%ED%92%80ThreadPool</link>
            <guid>https://velog.io/@joongjii_1217/%EC%8A%A4%EB%A0%88%EB%93%9CThread%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C%ED%92%80ThreadPool</guid>
            <pubDate>Mon, 01 Dec 2025 10:36:19 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 스레드(Thread)가 뭐야?</h2>

  <h3>1-1. 회사/직원 비유로 이해하기</h3>

  <p>
    먼저 비유부터 해 보자.
  </p>

  <ul>
    <li><strong>프로세스(Process)</strong> = 회사</li>
    <li><strong>스레드(Thread)</strong> = 회사 안에서 일하는 직원들</li>
  </ul>

  <p>
    어떤 프로그램(EXE)을 실행하면, 운영체제 입장에서는 <strong>프로세스 하나</strong>가 생긴다.<br>
    그리고 그 안에서 실제로 일을 하는 단위가 <strong>스레드</strong>다.
  </p>

  <p>
    예를 들어 엑셀을 켜면:
  </p>

  <ul>
    <li>엑셀 프로세스 1개</li>
    <li>그 안에
      <ul>
        <li>화면 그리는 스레드</li>
        <li>마우스/키보드 입력 받는 스레드</li>
        <li>자동 저장하는 스레드</li>
      </ul>
      같이 여러 스레드가 동시에 일을 할 수 있다.
    </li>
  </ul>

  <p>
    C# 콘솔 프로그램도 마찬가지다.
  </p>

  <ul>
    <li>프로그램이 실행되면 <strong>Main 스레드</strong> 하나로 시작한다.</li>
    <li>우리가 필요하다면 <code>Thread</code>를 만들어 스레드를 더 늘릴 수 있다.</li>
  </ul>

  <hr>

  <h2>2. C#에서 Thread 직접 만들어 보기</h2>

  <p>가장 기초적인 예제부터 보자.</p>

  <pre><code class="language-csharp">using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("메인 스레드 시작");

        // 1) 새 스레드에서 실행할 작업 정의
        Thread t = new Thread(DoWork);

        // 2) 새 스레드 시작
        t.Start();

        // 3) 메인 스레드는 자기 할 일 계속
        for (int i = 0; i &lt; 5; i++)
        {
            Console.WriteLine($"[Main] i = {i}");
            Thread.Sleep(500); // 0.5초 쉬기
        }

        // 4) 새 스레드가 끝날 때까지 기다리고 싶으면 Join 사용
        t.Join();

        Console.WriteLine("메인 스레드 종료");
    }

    static void DoWork()
    {
        for (int i = 0; i &lt; 5; i++)
        {
            Console.WriteLine($"[Worker] i = {i}");
            Thread.Sleep(700); // 0.7초 쉬기
        }
    }
}
</code></pre>

  <h3>2-1. 이 코드에서 일어나는 일</h3>

  <ul>
    <li>프로그램 시작 → <strong>Main 스레드</strong> 시작</li>
    <li><code>new Thread(DoWork)</code> : 새 직원(스레드)을 한 명 더 채용</li>
    <li><code>t.Start()</code> : 그 직원에게 <code>DoWork</code> 일을 시킴</li>
    <li>Main 스레드는 <code>for</code> 루프를 돌면서 <code>[Main] ...</code> 출력</li>
    <li>새 스레드는 <code>DoWork</code> 안에서 <code>[Worker] ...</code> 출력</li>
  </ul>

  <p>
    콘솔에 <code>[Main] ...</code>, <code>[Worker] ...</code>가 섞여서 출력되면<br>
    두 개의 스레드가 동시에 일을 하고 있다는 뜻이다.
  </p>

  <ul>
    <li><code>Thread.Sleep(500)</code> : “이 스레드는 0.5초 동안 잠깐 쉰다”</li>
    <li><code>t.Join()</code> : “스레드 <code>t</code>가 끝날 때까지 여기서 기다린다”</li>
  </ul>

  <p>
    입문 단계에서는 <strong>“스레드를 직접 만들면 이렇게 여러 작업을 동시에 돌릴 수 있다”</strong> 정도만 이해하고 넘어가면 된다.
  </p>

  <hr>

  <h2>3. 스레드풀(ThreadPool)은 뭐야?</h2>

  <h3>3-1. 비유로 먼저 이해하기</h3>

  <p>
    방금 본 <code>new Thread(...)</code>는 이렇게 생각할 수 있다.
  </p>

  <blockquote>
    “작업 하나 처리하려고, 직원을 한 명 새로 채용한다.”
  </blockquote>

  <p>
    그런데 현실에서 이러면 비효율적이다.
  </p>

  <ul>
    <li>작업이 조금 생길 때마다 사람 채용 → 작업 끝나면 바로 해고</li>
    <li>채용/해고 비용이 너무 크다</li>
  </ul>

  <p>
    그래서 회사에서는 보통 이렇게 한다.
  </p>

  <blockquote>
    “기본 직원들을 몇 명 쭉 고용해 두고, 일이 생기면 그 직원들에게 나눠준다.”
  </blockquote>

  <p>
    이게 바로 <strong>스레드풀(Thread Pool)</strong> 개념이다.
  </p>

  <ul>
    <li>미리 만들어 놓은 스레드들의 “풀(pool)”이 있고</li>
    <li>우리는 “이 작업 좀 처리해줘”라고 <strong>일(작업)</strong>만 던져준다.</li>
    <li>풀 안에서 놀고 있는 스레드가 그 일을 가져다가 실행한다.</li>
  </ul>

  <p>
    .NET 내부에는 이미 <strong>ThreadPool</strong>이 준비되어 있고,  
    우리는 거기에 작업을 큐(queue)에 넣어 사용하면 된다.
  </p>

  <hr>

  <h2>4. ThreadPool 기본 사용 예제</h2>

  <pre><code class="language-csharp">using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("메인 스레드 시작");

        // 스레드풀에 작업을 큐에 넣기
        ThreadPool.QueueUserWorkItem(DoWork);

        Console.WriteLine("메인 스레드는 계속 진행 중...");
        Console.ReadKey(); // 프로그램이 바로 종료되지 않게 대기
    }

    // 스레드풀용 콜백 메서드는 보통 object? 하나를 인수로 받는 시그니처를 가진다.
    static void DoWork(object? state)
    {
        Console.WriteLine(
            $"스레드풀 작업 시작, Thread ID = {Thread.CurrentThread.ManagedThreadId}");

        Thread.Sleep(1000); // 1초 정도 일하는 척
        Console.WriteLine("스레드풀 작업 끝");
    }
}
</code></pre>

  <h3>4-1. 포인트 정리</h3>

  <ul>
    <li><code>ThreadPool.QueueUserWorkItem(DoWork);</code>
      <ul>
        <li>“스레드풀에 이 메서드 실행할 작업 하나 넣어줘”라는 의미</li>
        <li>새 스레드를 직접 만드는 것이 아니라, 풀에 있는 스레드를 재사용한다.</li>
      </ul>
    </li>
    <li><code>DoWork(object? state)</code>
      <ul>
        <li>ThreadPool에서 호출할 메서드는 인자로 <code>object?</code> 하나 받는 형태가 기본</li>
        <li>지금 예제에서는 <code>state</code>를 사용하지 않으니까 <code>null</code> 그대로 둬도 된다.</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>5. Thread 직접 생성 vs ThreadPool 비교</h2>

  <table border="1" cellspacing="0" cellpadding="6">
    <thead>
      <tr>
        <th>구분</th>
        <th><code>new Thread</code> 직접 생성</th>
        <th><code>ThreadPool</code> / <code>Task.Run</code></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>스레드 생성 비용</td>
        <td>스레드 만들 때마다 비용 큼</td>
        <td>미리 만든 스레드를 재사용 → 효율적</td>
      </tr>
      <tr>
        <td>수명 관리</td>
        <td><code>Start</code>, <code>Join</code>, 종료 시점 직접 관리</td>
        <td>작업만 던지면 끝나면 알아서 반환</td>
      </tr>
      <tr>
        <td>개수 제한 관리</td>
        <td>너무 많이 만들면 OS에 부담</td>
        <td>.NET이 내부적으로 최대 스레드 수를 조절</td>
      </tr>
      <tr>
        <td>사용 난이도</td>
        <td>입문자에게는 다소 복잡</td>
        <td><code>Task.Run</code>, <code>async/await</code>로 비교적 쉬움</td>
      </tr>
      <tr>
        <td>요즘 권장 방식</td>
        <td>특수한 경우 아니면 잘 안 씀</td>
        <td>대부분 스레드풀 + Task / async 조합 사용</td>
      </tr>
    </tbody>
  </table>

  <p>
    실무에서는 보통:
  </p>

  <blockquote>
    <strong>직접 <code>Thread</code>를 만드는 경우는 특수한 상황이고,</strong><br>
    <strong>대부분은 스레드풀 + <code>Task</code> / <code>async/await</code> 조합으로 처리한다.</strong>
  </blockquote>

  <hr>

  <h2>6. 사실 <code>Task.Run</code>도 스레드풀 위에서 돈다</h2>

  <p>
    요즘 C# 코드에서는 <code>ThreadPool.QueueUserWorkItem</code> 대신
    <strong><code>Task.Run</code></strong>을 훨씬 더 자주 쓴다.
  </p>

  <pre><code class="language-csharp">using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("메인 시작");

        // Task.Run 안의 코드는 스레드풀 스레드에서 실행된다.
        await Task.Run(() =>
        {
            Console.WriteLine(
                $"백그라운드 작업, Thread ID = {Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(1000);
            Console.WriteLine("백그라운드 작업 끝");
        });

        Console.WriteLine("메인 종료");
    }
}
</code></pre>

  <h3>6-1. 여기서 알아둘 점</h3>

  <ul>
    <li><code>Task.Run(() =&gt; { ... })</code>는 내부적으로 <strong>스레드풀에 작업을 던지는 것</strong>이라고 생각하면 된다.</li>
    <li>우리는 “이 코드를 백그라운드에서 돌려줘” 정도의 느낌만 가지고 사용해도 된다.</li>
    <li><code>await</code>를 쓰면, 그 작업이 끝난 다음에 다시 이어서 실행할 수 있다.</li>
  </ul>

  <p>
    그래서 요약하면:
  </p>

  <ul>
    <li>과거: <code>new Thread</code> 또는 <code>ThreadPool.QueueUserWorkItem</code> 사용</li>
    <li>현재: 대부분 <code>Task</code> + <code>async/await</code> 사용  
      (내부에서는 결국 스레드풀이 일을 한다)</li>
  </ul>

  <hr>

  <h2>7. 실전 느낌으로 보는 간단 예제</h2>

  <p>
    화면(UI)을 가진 프로그램(윈폼, WPF 등)에서는<br>
    <strong>무거운 작업을 메인(UI) 스레드에서 돌리면 화면이 멈춘다.</strong><br>
    그래서 보통 스레드풀/백그라운드에서 작업을 돌리고, 결과만 가져온다.
  </p>

  <p>
    콘솔에서 비슷한 느낌만 보이는 예제를 하나 보자.
  </p>

  <pre><code class="language-csharp">using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("프로그램 시작");

        // 무거운 작업을 스레드풀에서 실행
        Task&lt;long&gt; work = Task.Run(() =>
        {
            Console.WriteLine("무거운 작업 시작");
            Thread.Sleep(3000); // 3초 걸리는 작업이라고 가정
            Console.WriteLine("무거운 작업 끝");
            return 12345L; // 결과 예시
        });

        Console.WriteLine("메인은 다른 일 계속 할 수 있음...");

        // 여기서 결과를 기다림 (중간에 UI라면 다른 이벤트 처리 가능)
        long result = await work;

        Console.WriteLine($"작업 결과: {result}");
        Console.WriteLine("프로그램 종료");
    }
}
</code></pre>

  <h3>7-1. 실행 흐름</h3>

  <ol>
    <li><code>프로그램 시작</code> 출력</li>
    <li><code>무거운 작업 시작</code> 출력 (스레드풀 스레드)</li>
    <li><code>메인은 다른 일 계속 할 수 있음...</code> 출력 (메인 스레드)</li>
    <li>3초 후 <code>무거운 작업 끝</code> 출력</li>
    <li><code>await work</code> 이후 <code>작업 결과: 12345</code> 출력</li>
    <li><code>프로그램 종료</code> 출력</li>
  </ol>

  <p>
    중요한 건, <strong>무거운 작업 때문에 메인 흐름이 멈추지 않는다</strong>는 점이다.<br>
    이런 패턴이 UI 프로그램이나 서버 프로그램에서 매우 자주 사용된다.
  </p>

  <hr>

  <h2>8. 입문자가 꼭 기억하면 좋은 포인트</h2>

  <ul>
    <li>
      <strong>스레드(Thread)</strong>
      <ul>
        <li>프로그램 안에서 실제로 일을 하는 실행 흐름</li>
        <li>C# 프로그램은 기본적으로 Main 스레드 1개로 시작</li>
        <li><code>new Thread(...)</code> 로 직접 스레드를 만들 수 있지만, 요즘은 특별한 경우에만 사용</li>
      </ul>
    </li>
    <li>
      <strong>스레드풀(ThreadPool)</strong>
      <ul>
        <li>.NET이 미리 만들어 관리하는 “스레드 직원 풀”</li>
        <li><code>ThreadPool.QueueUserWorkItem</code> 또는 <code>Task.Run</code>으로 작업을 던지면, 풀 안의 스레드가 실행</li>
        <li>스레드를 직접 만드는 것보다 효율적이고 안전한 경우가 많다.</li>
      </ul>
    </li>
    <li>
      <strong>Task / async / await</strong>
      <ul>
        <li>스레드/스레드풀을 좀 더 쉽게 쓰도록 도와주는 고수준 도구</li>
        <li>내부적으로는 스레드풀이나 비동기 I/O를 활용</li>
        <li>입문자는 “비동기 코드는 Task + async/await로 작성한다”부터 익숙해지는 것이 좋다.</li>
      </ul>
    </li>
    <li>
      <strong>UI 프로그램에서는</strong>
      <ul>
        <li>무거운 작업을 메인(UI) 스레드에서 직접 돌리면 화면이 멈춘다.</li>
        <li>스레드풀/백그라운드에서 일을 돌리고, 결과만 UI 스레드로 가져오는 패턴을 사용한다.</li>
      </ul>
    </li>
  </ul>

  <hr>

  <h2>9. 이후 찾아보면 좋은 부분</h2>

  <p>스레드와 스레드풀 개념을 이해했다면, 다음 단계로 이런 것들을 공부해 보면 좋다.</p>

  <ul>
    <li><strong>동기화</strong> : <code>lock</code>, <code>Monitor</code>, <code>Interlocked</code> 등</li>
    <li><strong>스레드 안전(Thread-Safety)</strong> 개념</li>
    <li><strong>async/await</strong>가 스레드를 새로 만드는 게 아니라는 점 (I/O 비동기 개념)</li>
  </ul>


</article>
]]></description>
        </item>
        <item>
            <title><![CDATA[가비지컬렉터]]></title>
            <link>https://velog.io/@joongjii_1217/%EA%B0%80%EB%B9%84%EC%A7%80%EC%BB%AC%EB%A0%89%ED%84%B0</link>
            <guid>https://velog.io/@joongjii_1217/%EA%B0%80%EB%B9%84%EC%A7%80%EC%BB%AC%EB%A0%89%ED%84%B0</guid>
            <pubDate>Mon, 01 Dec 2025 10:30:53 GMT</pubDate>
            <description><![CDATA[<article>

  <hr>

  <h2>1. 가비지 컬렉터가 왜 필요할까?</h2>

  <p>
    C, C++ 같은 언어에서는 메모리를 직접 관리해야 한다.
  </p>

  <pre><code class="language-c">void* p = malloc(100);
// ...
free(p);
</code></pre>

  <ul>
    <li><code>free</code>를 깜빡하면 → 메모리 누수</li>
    <li>이미 해제한 메모리를 또 쓰면 → 크래시, 이상한 버그</li>
  </ul>

  <p>
    C#/.NET은 이런 귀찮고 위험한 일을 줄이기 위해 <strong>가비지 컬렉터(GC)</strong>를 도입했다.
  </p>

  <blockquote>
    <strong>GC = 더 이상 사용되지 않는 객체를 자동으로 찾아서 메모리를 회수해주는 시스템</strong>
  </blockquote>

  <p>
    개발자는 보통 <code>new</code>로 객체만 만들고,  
    “언제 free 할지”는 신경 안 써도 되게 만든 것.
  </p>

  <hr>

  <h2>2. 스택(stack) vs 힙(heap) 간단 복습</h2>

  <p>
    C# 메모리 구조를 아주 단순하게 나누면 이렇게 생각할 수 있다.
  </p>

  <h3>2-1. 스택(stack)</h3>

  <ul>
    <li>지역 변수, 매개변수 등이 주로 저장되는 영역</li>
    <li>메서드가 호출될 때 쌓이고, 리턴될 때 자동으로 제거된다 (LIFO 구조)</li>
  </ul>

  <pre><code class="language-csharp">void Foo()
{
    int x = 10;   // x는 스택에 위치
} // Foo가 끝나면 x도 자동으로 사라짐
</code></pre>

  <h3>2-2. 힙(heap)</h3>

  <ul>
    <li><code>new</code>로 생성되는 객체들이 저장되는 곳</li>
    <li><code>string</code>, <code>List&lt;T&gt;</code>, 사용자 정의 클래스 인스턴스 등</li>
  </ul>

  <pre><code class="language-csharp">void Foo()
{
    var list = new List&lt;int&gt;(); // List 객체는 힙에 생성
} // Foo가 끝나도 힙의 List는 자동으로 사라지지 않는다
</code></pre>

  <p>
    정리하면:
  </p>

  <ul>
    <li><strong>힙</strong> : 실제 객체가 있는 곳</li>
    <li><strong>스택</strong> : 그 객체를 가리키는 참조(주소)를 들고 있는 변수들이 있는 곳</li>
  </ul>

  <hr>

  <h2>3. 언제 객체가 “가비지(쓰레기)”가 될까?</h2>

  <p>
    GC가 객체를 지우려면 기준이 있어야 한다. 핵심은 딱 하나다:
  </p>

  <blockquote>
    <strong>“어디에서도 더 이상 참조하지 않는 객체” → 쓰레기</strong>
  </blockquote>

  <h3>3-1. 예시 1 – 메서드 안의 지역 변수</h3>

  <pre><code class="language-csharp">void Foo()
{
    var list = new List&lt;int&gt;();
    list.Add(1);
    list.Add(2);
} // 여기서 Foo 종료
</code></pre>

  <ul>
    <li><code>list</code>는 스택에 있는 지역 변수</li>
    <li>Foo가 끝나면 스택 프레임이 사라지고, <code>list</code>도 함께 사라진다</li>
    <li>이제 힙에 있는 <code>new List&lt;int&gt;()</code>를 가리키는 참조가 아무 것도 없다</li>
  </ul>

  <p>GC 입장에서는 이 객체는 이제 “어디에서도 도달 불가” → <strong>가비지</strong>가 된다.</p>

  <h3>3-2. 예시 2 – 여전히 참조가 남아 있는 경우</h3>

  <pre><code class="language-csharp">List&lt;int&gt; _cache;

void Make()
{
    _cache = new List&lt;int&gt;();
}

void Use()
{
    _cache.Add(1);
}
</code></pre>

  <ul>
    <li><code>_cache</code>는 필드(멤버 변수) → 프로그램이 살아 있는 동안 계속 유지될 수 있다</li>
    <li>따라서 <code>_cache</code>가 가리키는 List는 GC 대상이 아니다 (아직 “살아 있는 객체”)</li>
  </ul>

  <p>
    정리하면:
  </p>

  <blockquote>
    <strong>GC는 “참조가 끊어진 객체”만 지운다.</strong><br>
    참조가 남아 있으면, 아무리 우리가 “더 이상 안 쓰는데…”라고 생각해도 GC는 지우지 않는다.
  </blockquote>

  <hr>

  <h2>4. GC가 실제로 하는 일 (직관적 버전)</h2>

  <p>
    내부 구현은 엄청 복잡하지만, 개념적으로는 대략 이런 일을 한다고 보면 된다.
  </p>

  <ol>
    <li><strong>Root(루트) 찾기</strong><br>
      스택의 지역 변수들, static 필드들, CPU 레지스터 등  
      → 프로그램이 직접 들고 있는 참조들을 시작점으로 잡는다.
    </li>
    <li><strong>도달 가능한 객체 표시(mark)</strong><br>
      루트에서 참조를 따라가면서, 도달할 수 있는 모든 객체를 “살아 있음”으로 표시한다.
    </li>
    <li><strong>도달 불가능한 객체 찾기</strong><br>
      어떤 루트에서도 도달할 수 없는 객체들 = 더 이상 사용되지 않는 객체들 = 가비지.
    </li>
    <li><strong>가비지 제거 + 힙 정리(compact)</strong><br>
      가비지 객체들을 제거하고, 남은 객체들을 한쪽으로 몰아서 메모리를 연속적으로 만들기도 한다.
    </li>
  </ol>

  <p>
    이 과정에서 잠깐 <strong>GC가 도는 동안 해당 스레드가 멈추는 구간(stop-the-world)</strong>이 생길 수 있다.<br>
    성능 튜닝할 때는 이 <em>GC pause</em>도 고려해야 한다.
  </p>

  <hr>

  <h2>5. 세대별(Generation) GC – Gen 0 / Gen 1 / Gen 2</h2>

  <p>
    .NET GC는 성능을 높이기 위해 힙을 <strong>세대(Generation)</strong>로 나누어 관리한다.
  </p>

  <ul>
    <li><strong>Gen 0</strong> : 새로 생성된 객체들이 있는 영역 (아기)</li>
    <li><strong>Gen 1</strong> : 한 번 GC를 버틴 애들</li>
    <li><strong>Gen 2</strong> : 오래 살아남은 객체들 (노인)</li>
  </ul>

  <p>
    경험적으로 이런 특징이 있다.
  </p>

  <blockquote>
    “대부분의 객체는 금방 죽고,  
    생각보다 오래 사는 객체는 적다.”
  </blockquote>

  <p>
    따라서 GC는:
  </p>

  <ul>
    <li>Gen 0처럼 “금방 죽을 가능성이 큰 영역”을 위주로 자주 검사하고</li>
    <li>Gen 2처럼 오래 사는 영역은 덜 자주 검사한다</li>
  </ul>

  <p>
    지금 단계에서는:
  </p>

  <blockquote>
    <strong>“객체는 처음에 Gen 0에서 시작하고,  
    GC를 통과해 살아남으면 점점 위 세대로 승격된다”</strong>  
    이 정도만 알고 있어도 충분하다.
  </blockquote>

  <hr>

  <h2>6. GC는 언제 도는가?</h2>

  <p>
    우리가 <code>new</code>를 계속 호출해서 힙이 어느 정도 차면,  
    런타임이 “이제 한 번 청소할 때가 된 것 같은데?” 하고 GC를 돌린다.
  </p>

  <p>
    또한:
  </p>

  <ul>
    <li>메모리 상황을 보고 필요할 때</li>
    <li>CPU가 한가할 때 백그라운드로</li>
  </ul>

  <p>등 여러 요인을 고려해서 자동으로 실행된다.</p>

  <p>우리가 직접 호출할 수도 있다:</p>

  <pre><code class="language-csharp">GC.Collect();
</code></pre>

  <p>
    하지만 실무에서는 <strong>왠만하면 사용을 권장하지 않는다.</strong><br>
    런타임이 보통 우리보다 더 좋은 타이밍에 돌릴 수 있고,  
    우리가 억지로 자주 호출하면 오히려 성능이 떨어질 수 있기 때문이다.
  </p>

  <hr>

  <h2>7. GC가 “못 하는 일” – <code>IDisposable</code>, <code>using</code>과의 관계</h2>

  <p>
    GC는 어디까지나 <strong>“관리 힙 메모리”</strong>만 치운다.<br>
    그런데 객체 안에는 메모리 말고도 이런 것들이 들어있을 수 있다.
  </p>

  <ul>
    <li>파일 핸들</li>
    <li>데이터베이스 연결</li>
    <li>네트워크 소켓</li>
    <li>윈도우 핸들, GDI 리소스 등</li>
  </ul>

  <p>
    이런 것들은 OS 수준 자원이기 때문에, GC가 직접 정리해 줄 수 없다.<br>
    그래서 이런 타입들은 보통 <strong><code>IDisposable</code></strong>을 구현하고,  
    우리가 <code>Dispose()</code>를 호출해서 정리해야 한다.
  </p>

  <pre><code class="language-csharp">using (var fs = new FileStream("test.txt", FileMode.Open))
{
    // 파일 사용
} // 여기서 자동으로 fs.Dispose() 호출 → 파일 핸들 반납
</code></pre>

  <p>
    정리하면:
  </p>

  <ul>
    <li><strong>GC</strong> : 관리 힙 메모리 회수 담당</li>
    <li><strong><code>IDisposable</code> / <code>using</code></strong> : 파일 핸들, DB 연결 같은 <strong>비관리 자원</strong> 정리 담당</li>
  </ul>

  <hr>

  <h2>8. C#에서도 메모리 누수가 생길 수 있을까?</h2>

  <p>
    GC가 있는데도 왜 “메모리 누수” 얘기가 나올까?  
    이유는 아주 간단하다.
  </p>

  <blockquote>
    <strong>GC는 “참조가 있는 객체”는 절대로 지우지 않는다.</strong>
  </blockquote>

  <p>
    예를 들어:
  </p>

  <pre><code class="language-csharp">static List&lt;byte[]&gt; _cache = new List&lt;byte[]&gt;();

void Leak()
{
    var big = new byte[10 * 1024 * 1024]; // 10MB
    _cache.Add(big);                      // 전역 리스트에 계속 추가
}
</code></pre>

  <ul>
    <li><code>_cache</code>는 <code>static</code> 필드라 프로그램이 끝날 때까지 참조를 유지할 수 있다.</li>
    <li>여기에 10MB짜리 배열을 계속 넣으면, GC 입장에서는 “참조가 있으니 지우면 안 된다”고 판단한다.</li>
    <li>결과적으로 메모리는 계속 늘어나서 <strong>사실상 누수처럼 보이는 상황</strong>이 된다.</li>
  </ul>

  <p>
    즉, C#에서도:
  </p>

  <blockquote>
    <strong>우리가 참조를 계속 잡아두면, GC가 치울 수 없다.</strong><br>
    그래서 “참조를 언제 끊어줄지”도 중요한 설계 포인트다.
  </blockquote>

  <hr>



  <blockquote>
    <strong>GC는 “메모리 free를 자동으로 해주는 친구”지만,  
    우리가 참조를 어떻게 관리하느냐에 따라 효율과 안정성이 달라진다.</strong>
  </blockquote>

  <p>
    다음 단계로는:
  </p>

  <ul>
    <li>Finalizer(종료자) vs <code>IDisposable</code> 차이</li>
    <li>Large Object Heap(LOH)</li>
    <li>Server GC vs Workstation GC</li>
    <li>GC Pause(스톱 더 월드)와 성능 튜닝</li>
  </ul>

  <p>
    같은 내용을 공부하면, C#/.NET 메모리 관리에 대한 이해가 한층 더 깊어진다.
  </p>
</article>
]]></description>
        </item>
    </channel>
</rss>