<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://amsomad.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://amsomad.github.io/" rel="alternate" type="text/html" /><updated>2025-05-22T12:39:31+00:00</updated><id>https://amsomad.github.io/feed.xml</id><title type="html">Amso Mad</title><subtitle>난 늦지 않았다.</subtitle><author><name>Yoon Sik Lim</name></author><entry><title type="html">AJCC 9th Edition (2021 ~ 2024) 분석 및 예시</title><link href="https://amsomad.github.io/%EB%A9%94%EB%94%94%EC%BB%AC%EC%9D%B4%EC%95%BC%EA%B8%B0/AJCC_9th/" rel="alternate" type="text/html" title="AJCC 9th Edition (2021 ~ 2024) 분석 및 예시" /><published>2025-05-22T00:00:00+00:00</published><updated>2025-05-22T00:00:00+00:00</updated><id>https://amsomad.github.io/%EB%A9%94%EB%94%94%EC%BB%AC%EC%9D%B4%EC%95%BC%EA%B8%B0/AJCC_9th</id><content type="html" xml:base="https://amsomad.github.io/%EB%A9%94%EB%94%94%EC%BB%AC%EC%9D%B4%EC%95%BC%EA%B8%B0/AJCC_9th/"><![CDATA[<p>해당 포스팅은 AJCC 9th Edition (2021 ~ 2024) 에 대한 분석 및 예시를 다룹니다.
모든 내용은 나만을 위한 것이며, AI 시대에 맞춰 나만의 지식을 정리하는 것입니다.</p>

<h2 id="-목차">📌 목차</h2>
<ul>
  <li><a href="#용어-백과사전-암-병기-쉽게-이해하기">🔎 용어 백과사전 (암 병기 쉽게 이해하기)</a></li>
  <li><a href="#1-유방암-breast-cancer">1. 유방암 (Breast Cancer)</a></li>
  <li><a href="#2-폐암-lung-cancer">2. 폐암 (Lung Cancer)</a></li>
  <li><a href="#3-위암-gastric-cancer">3. 위암 (Gastric Cancer)</a></li>
  <li><a href="#4-대장암-colorectal-cancer">4. 대장암 (Colorectal Cancer)</a></li>
  <li><a href="#5-간암-hepatocellular-carcinoma">5. 간암 (Hepatocellular Carcinoma)</a></li>
  <li><a href="#6-췌장암-pancreatic-cancer">6. 췌장암 (Pancreatic Cancer)</a></li>
  <li><a href="#7-식도암-esophageal-cancer">7. 식도암 (Esophageal Cancer)</a></li>
  <li><a href="#8-후두암-laryngeal-cancer">8. 후두암 (Laryngeal Cancer)</a></li>
  <li><a href="#9-자궁경부암-cervical-cancer">9. 자궁경부암 (Cervical Cancer)</a></li>
  <li><a href="#10-전립선암-prostate-cancer">10. 전립선암 (Prostate Cancer)</a></li>
  <li><a href="#11-피부-흑색종-melanoma">11. 피부 흑색종 (Melanoma)</a></li>
  <li><a href="#12-갑상선암-thyroid-cancer">12. 갑상선암 (Thyroid Cancer)</a></li>
</ul>

<h3 id="용어-백과사전-암-병기-쉽게-이해하기">🔎 용어 백과사전 (암 병기 쉽게 이해하기)</h3>

<h4 id="병기stage">병기(Stage)</h4>
<p>암이 몸에 얼마나 퍼졌는지를 숫자나 문자로 구분합니다.</p>
<ul>
  <li><strong>1기</strong>: 초기</li>
  <li><strong>4기</strong>: 말기 (전이 상태)</li>
</ul>

<h4 id="t-tumor">T (Tumor)</h4>
<p>암의 <strong>크기와 침윤 정도</strong></p>
<ul>
  <li>T1: 작음</li>
  <li>T4: 주변 장기까지 침범</li>
</ul>

<h4 id="n-node">N (Node)</h4>
<p><strong>림프절로 퍼졌는지</strong></p>
<ul>
  <li>N0: 퍼지지 않음</li>
  <li>N1~N3: 퍼진 개수에 따라 구분</li>
</ul>

<h4 id="m-metastasis">M (Metastasis)</h4>
<p><strong>몸의 다른 장기(간, 폐 등)로 퍼졌는지</strong></p>
<ul>
  <li>M0: 없음</li>
  <li>M1: 있음</li>
</ul>

<h4 id="grade-분화도">Grade (분화도)</h4>
<p>암세포가 얼마나 <strong>정상 세포와 다른지</strong></p>
<ul>
  <li>1~3단계, 숫자 높을수록 악성</li>
</ul>

<h4 id="psa-전립선암-지표">PSA (전립선암 지표)</h4>
<p>수치가 높을수록 암 가능성↑</p>

<h4 id="gleason-score-전립선암-악성도">Gleason Score (전립선암 악성도)</h4>
<p>암의 공격성 점수. 최대 10점</p>

<h4 id="breslow-thickness">Breslow Thickness</h4>
<p>피부 흑색종의 깊이 (mm 단위)</p>

<h4 id="궤양">궤양</h4>
<p>암 부위가 짓무른 상태 → 병기 상승</p>

<h4 id="병리-보고서">병리 보고서</h4>
<p>현미경으로 암을 분석한 공식 문서</p>

<h4 id="ajcc">AJCC</h4>
<p>암 병기 체계를 만든 국제기관 (미국암연합회)</p>

<h3 id="-1-유방암-breast-cancer-1-유방암-breast-cancer">🔍 1. 유방암 (Breast Cancer) {#1-유방암-breast-cancer}</h3>

<h4 id="-암종-설명">📘 암종 설명</h4>
<p>유방암은 전 세계 여성에서 가장 흔한 암 중 하나이며, 대부분 “침윤성 유관암(Invasive Ductal Carcinoma, IDC)” 또는 “침윤성 소엽암(Invasive Lobular Carcinoma, ILC)”의 형태로 발생합니다. 병리과에서는 조직학적 유형, 호르몬 수용체 상태(ER, PR, HER2), Ki-67 지표 등을 포함한 진단이 핵심입니다.</p>
<ul>
  <li>침윤성 유관암 (Invasive Ductal Carcinoma, IDC): 유관에서 발생하여 주변 조직으로 침범하는 형태.</li>
  <li>침윤성 소엽암 (Invasive Lobular Carcinoma, ILC): 소엽에서 발생하여 주변 조직으로 침범하는 형태.</li>
</ul>

<p>AJCC 9차에서는 종양의 크기(T), 림프절 전이(N), 원격 전이(M) 외에도 <strong>생물학적 지표 (ER/PR/HER2)</strong>를 기반으로 <strong>병기 그룹 조정</strong>이 중요하게 다뤄졌습니다. 병기 시스템은 치료 계획 수립과 예후 판단에 결정적 역할을 합니다.</p>

<hr />

<h4 id="-t-primary-tumor">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>Tis</strong>: 상피내암 (DCIS, LCIS)</li>
  <li><strong>T1</strong>: 종양 ≤ 20mm
    <ul>
      <li>T1mi: ≤ 1mm</li>
      <li>T1a: &gt; 1mm ≤ 5mm</li>
      <li>T1b: &gt; 5mm ≤ 10mm</li>
      <li>T1c: &gt; 10mm ≤ 20mm</li>
    </ul>
  </li>
  <li><strong>T2</strong>: &gt; 20mm ≤ 50mm</li>
  <li><strong>T3</strong>: &gt; 50mm</li>
  <li><strong>T4</strong>: 흉벽, 피부 침범
    <ul>
      <li>T4a: 흉벽 침범</li>
      <li>T4b: 피부 침범 (궤양 등)</li>
      <li>T4c: T4a + T4b</li>
      <li>T4d: 염증성 유방암</li>
    </ul>
  </li>
</ul>

<hr />

<h4 id="-n-regional-lymph-nodes">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 림프절 전이 없음</li>
  <li><strong>N1</strong>: 동측 액와 림프절 1~3개 전이</li>
  <li><strong>N1mi</strong>: 미세 전이 (0.2~2mm)</li>
  <li><strong>N2</strong>: 고정된 림프절 또는 내유방 림프절</li>
  <li><strong>N3</strong>: 쇄골상 림프절 또는 여러 림프절 동시 침범</li>
</ul>

<hr />

<h4 id="-m-distant-metastasis">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 원격 전이 없음</li>
  <li><strong>M1</strong>: 폐, 간, 뼈 등으로 전이된 경우</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>1. 유방암 (Breast Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tis</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IA</td>
      <td>T1</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IB</td>
      <td>T0-T1</td>
      <td>N1mi</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T0-T1</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T2</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T3</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T0-T3</td>
      <td>N2</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T4</td>
      <td>Any N</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>Any T</td>
      <td>N3</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IV</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-코드블록">📄 표준 서식 예시 (코드블록)</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">유방암</span> <span class="p">(</span><span class="nc">Breast</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2</span> <span class="p">(</span><span class="mi">30</span><span class="n">mm</span> <span class="err">크기</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N1</span> <span class="p">(</span><span class="err">액와</span> <span class="err">림프절</span> <span class="mi">2</span><span class="err">개</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIB</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">침윤성</span> <span class="err">유관암</span><span class="p">(</span><span class="nc">Invasive</span> <span class="n">ductal</span> <span class="n">carcinoma</span><span class="p">)</span><span class="err">으로</span> <span class="err">확인되며</span><span class="p">,</span> <span class="err">주변</span> <span class="err">연부조직</span> <span class="err">침습은</span> <span class="err">없음</span><span class="p">.</span> 
<span class="nc">HER2</span> <span class="err">음성</span><span class="p">,</span> <span class="nc">ER</span> <span class="err">양성</span><span class="p">,</span> <span class="nc">PR</span> <span class="err">양성</span> <span class="err">소견</span><span class="p">.</span> <span class="nc">Ki-67</span> <span class="n">index</span><span class="err">는</span> <span class="mi">25</span><span class="p">%</span> <span class="err">수준</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-2-폐암-lung-cancer-2-폐암-lung-cancer">🔍 2. 폐암 (Lung Cancer) {#2-폐암-lung-cancer}</h3>

<h4 id="-암종-설명-1">📘 암종 설명</h4>
<p>폐암은 비소세포폐암(NSCLC, 약 85%)과 소세포폐암(SCLC)으로 구분되며, AJCC 병기 시스템은 <strong>비소세포폐암</strong>을 기준으로 정의됩니다. 병리과에서는 조직 아형(선암, 편평상피암, 대세포암 등), EGFR, ALK, PD-L1 등의 바이오마커 정보가 병기와 함께 중요하게 다뤄집니다.</p>

<p>AJCC 9차에서는 <strong>T, N, M 세분화</strong>가 정밀해졌으며, <strong>종양 크기 기준이 1cm 단위로 더욱 세밀하게 조정</strong>되었습니다.</p>

<hr />

<h4 id="-t-primary-tumor-1">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>T1a~T1c</strong>: ≤ 3cm (세부적으로 1cm 단위로 구분)</li>
  <li><strong>T2a~T2b</strong>: &gt; 3cm ≤ 5cm, 기관지 근접 또는 폐렴동반</li>
  <li><strong>T3</strong>: &gt; 5cm ≤ 7cm 또는 흉막 침범, 동일 엽 다발성</li>
  <li><strong>T4</strong>: &gt; 7cm, 다른 엽 침범, 주요 구조 침범</li>
</ul>

<h4 id="-n-regional-lymph-nodes-1">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 림프절 전이 없음</li>
  <li><strong>N1</strong>: 기관지주위, 폐문 림프절</li>
  <li><strong>N2</strong>: 종격동 림프절 (동측)</li>
  <li><strong>N3</strong>: 대측 또는 쇄골상</li>
</ul>

<h4 id="-m-distant-metastasis-1">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 전이 없음</li>
  <li><strong>M1a</strong>: 반대편 폐 결절 또는 흉막/심막 삼출</li>
  <li><strong>M1b</strong>: 한 개의 원격 전이</li>
  <li><strong>M1c</strong>: 다발성 원격 전이</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-1">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>2. 폐암 (Lung Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>IA</td>
      <td>T1a~T1c</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IB</td>
      <td>T2a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T2b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T1a~T2b</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T1~T3</td>
      <td>N2</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T4</td>
      <td>N2</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>Any T</td>
      <td>N3</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IVA</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1a or M1b</td>
    </tr>
    <tr>
      <td>IVB</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1c</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">폐암</span> <span class="p">(</span><span class="nc">Lung</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2b</span> <span class="p">(</span><span class="mf">4.8</span><span class="n">cm</span> <span class="err">종괴</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N1</span> <span class="p">(</span><span class="err">폐문</span> <span class="err">림프절</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIB</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">비소세포폐암</span><span class="p">(</span><span class="nc">NSCLC</span><span class="p">,</span> <span class="err">선암</span><span class="p">)</span><span class="err">으로</span> <span class="err">확인되며</span><span class="p">,</span> <span class="nc">EGFR</span> <span class="err">양성</span><span class="p">,</span> <span class="nc">ALK</span> <span class="err">음성</span><span class="p">.</span> <span class="nc">PD-L1</span> <span class="err">발현율</span> <span class="mi">30</span><span class="p">%.</span>
</code></pre></div></div>

<h3 id="-3-위암-gastric-cancer-3-위암-gastric-cancer">🔍 3. 위암 (Gastric Cancer) {#3-위암-gastric-cancer}</h3>

<h4 id="-암종-설명-2">📘 암종 설명</h4>
<p>위암은 동아시아에서 특히 흔하며, 병리과에서는 <strong>분화도, Lauren 분류형(Intestinal vs Diffuse), 림프절 전이 수</strong>가 병기와 함께 중요합니다. AJCC 9차는 종양의 <strong>해부학적 위치, 림프절 수</strong>, 그리고 <strong>주변 장기 침습 여부</strong>에 따라 병기가 정해집니다.</p>

<hr />

<h4 id="-t-primary-tumor-2">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>Tis</strong>: 상피내암</li>
  <li><strong>T1</strong>: 점막/점막하 침범</li>
  <li><strong>T2</strong>: 근육층까지 침범</li>
  <li><strong>T3</strong>: 장막하 침범</li>
  <li><strong>T4a</strong>: 장막 침범</li>
  <li><strong>T4b</strong>: 인접 장기 침범 (췌장, 대장 등)</li>
</ul>

<h4 id="-n-regional-lymph-nodes-2">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 0개 전이</li>
  <li><strong>N1</strong>: 1~2개</li>
  <li><strong>N2</strong>: 3~6개</li>
  <li><strong>N3a</strong>: 7~15개</li>
  <li><strong>N3b</strong>: ≥16개</li>
</ul>

<h4 id="-m-distant-metastasis-2">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 복막, 간, 폐 등 전이</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-2">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>3. 위암 (Gastric Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
      <th> </th>
      <th> </th>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>IA</td>
      <td>T1</td>
      <td>N0</td>
      <td>M0</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>IB</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0 / T1</td>
      <td>N1</td>
      <td>M0</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T1</td>
      <td>N2</td>
      <td>M0 / T2</td>
      <td>N1</td>
      <td>M0 / T3</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T2</td>
      <td>N2</td>
      <td>M0 / T3</td>
      <td>N1</td>
      <td>M0 / T4a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T2</td>
      <td>N3</td>
      <td>M0 / T3</td>
      <td>N2</td>
      <td>M0 / T4a</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T3</td>
      <td>N3</td>
      <td>M0 / T4a</td>
      <td>N2</td>
      <td>M0 / T4b</td>
      <td>N0~1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>T4a</td>
      <td>N3</td>
      <td>M0 / T4b</td>
      <td>N2~3</td>
      <td>M0</td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td>IV</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1</td>
      <td> </td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-1">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">위암</span> <span class="p">(</span><span class="nc">Gastric</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T3</span> <span class="p">(</span><span class="err">장막하</span> <span class="err">침범</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N2</span> <span class="p">(</span><span class="mi">5</span><span class="err">개</span> <span class="err">림프절</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIIA</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">미만성형</span><span class="p">(</span><span class="nc">Diffuse</span> <span class="n">type</span><span class="p">)</span> <span class="err">위선암으로</span> <span class="err">확인됨</span><span class="p">.</span> <span class="err">종양은</span> <span class="err">전정부</span><span class="p">(</span><span class="n">antrum</span><span class="p">)</span><span class="err">에</span> <span class="err">위치하며</span><span class="p">,</span> <span class="err">수술적</span> <span class="err">절제는</span> <span class="n">subtotal</span> <span class="n">gastrectomy</span> <span class="err">시행</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-4-대장암-colorectal-cancer-4-대장암-colorectal-cancer">🔍 4. 대장암 (Colorectal Cancer) {#4-대장암-colorectal-cancer}</h3>

<h4 id="-암종-설명-3">📘 암종 설명</h4>
<p>대장암은 선암(Adenocarcinoma)이 대부분이며, 병리과에서는 <strong>종양 침습 깊이, 절제된 림프절 수, 림프관/신경 침습</strong> 등이 중요합니다. 병기 결정 시, 전이가 있는 림프절 수와 <strong>복막 전이 유무</strong>가 중요합니다.</p>

<p>AJCC 9차에서는 기존과 유사하지만, M 분류에 <strong>복막전이(PM: peritoneal metastasis)</strong> 구분이 강화되었고, N 분류 세분화가 강조됩니다.</p>

<hr />

<h4 id="-t-primary-tumor-3">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>Tis</strong>: 상피내암</li>
  <li><strong>T1</strong>: 점막하 침범</li>
  <li><strong>T2</strong>: 근육층 침범</li>
  <li><strong>T3</strong>: 장막외 침범</li>
  <li><strong>T4a</strong>: 장막 침범</li>
  <li><strong>T4b</strong>: 인접 구조물 침범</li>
</ul>

<h4 id="-n-regional-lymph-nodes-3">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 0개 전이</li>
  <li><strong>N1a</strong>: 1개 림프절</li>
  <li><strong>N1b</strong>: 2~3개</li>
  <li><strong>N1c</strong>: 림프절은 없으나 종양침범 부위에 결절 있음</li>
  <li><strong>N2a</strong>: 4~6개</li>
  <li><strong>N2b</strong>: ≥7개</li>
</ul>

<h4 id="-m-distant-metastasis-3">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 전이 없음</li>
  <li><strong>M1a</strong>: 한 기관 전이 (간, 폐 등)</li>
  <li><strong>M1b</strong>: 다기관 전이</li>
  <li><strong>M1c</strong>: 복막 전이 ± 다른 장기 동반</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-3">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>4. 대장암 (Colorectal Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tis</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>I</td>
      <td>T1–T2</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T3</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T4a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIC</td>
      <td>T4b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T1–T2</td>
      <td>N1/N1c</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T3–T4a</td>
      <td>N1/N1c</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>T4a–T4b</td>
      <td>N2</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IVA</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1a</td>
    </tr>
    <tr>
      <td>IVB</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1b</td>
    </tr>
    <tr>
      <td>IVC</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1c</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-2">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">대장암</span> <span class="p">(</span><span class="nc">Colorectal</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T3</span> <span class="p">(</span><span class="err">장막외</span> <span class="err">침범</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N2a</span> <span class="p">(</span><span class="mi">4</span><span class="err">개</span> <span class="err">림프절</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIIC</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">상행결장</span> <span class="err">우측에</span> <span class="mi">5</span><span class="n">cm</span> <span class="err">크기</span> <span class="err">선암성</span> <span class="err">병변</span><span class="p">.</span> <span class="err">림프관</span> <span class="err">침습</span><span class="p">(+),</span> <span class="err">신경</span> <span class="err">침습</span><span class="p">(+),</span> <span class="err">복막</span> <span class="err">침범</span> <span class="err">없음</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-5-간암-hepatocellular-carcinoma-5-간암-hepatocellular-carcinoma">🔍 5. 간암 (Hepatocellular Carcinoma) {#5-간암-hepatocellular-carcinoma}</h3>

<h4 id="-암종-설명-4">📘 암종 설명</h4>
<p>간세포암(HCC)은 B형 간염, C형 간염, 알코올성 간질환과 연관이 많습니다. 병리 진단에서는 <strong>종양 수, 크기, 혈관 침범 여부</strong>가 핵심이며, AJCC 9차에서는 특히 <strong>문맥혈관 침범과 담관 침범</strong> 유무가 병기 결정에 중요합니다.</p>

<hr />

<h4 id="-t-primary-tumor-4">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>T1a</strong>: ≤ 2cm, 혈관 침범 없음</li>
  <li><strong>T1b</strong>: &gt; 2cm, 단일, 혈관 침범 없음</li>
  <li><strong>T2</strong>: 단일 + 혈관 침범 or 다발성 ≤ 5cm</li>
  <li><strong>T3</strong>: 다발성 &gt; 5cm</li>
  <li><strong>T4</strong>: 인접 장기 또는 대혈관 침범</li>
</ul>

<h4 id="-n-regional-lymph-nodes-4">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 림프절 전이 없음</li>
  <li><strong>N1</strong>: 전이 있음</li>
</ul>

<h4 id="-m-distant-metastasis-4">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 원격 전이 있음</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-4">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>5. 간암 (Hepatocellular Carcinoma)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>I</td>
      <td>T1</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>II</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T3</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T4</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>Any T</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IV</td>
      <td>Any T</td>
      <td>Any N</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<h2><br /></h2>

<h4 id="-표준-서식-예시-3">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">간암</span> <span class="p">(</span><span class="nc">Hepatocellular</span> <span class="nc">Carcinoma</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2</span> <span class="p">(</span><span class="mf">2.8</span><span class="n">cm</span> <span class="p">+</span> <span class="err">혈관</span> <span class="err">침범</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N0</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">II</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">간</span> <span class="err">우엽에</span> <span class="mf">2.8</span><span class="n">cm</span> <span class="err">크기</span> <span class="err">간세포암</span><span class="p">(</span><span class="nc">HCC</span><span class="p">)</span> <span class="err">병변</span> <span class="err">존재</span><span class="p">.</span> <span class="err">위성</span> <span class="err">결절</span> <span class="err">없음</span><span class="p">.</span> <span class="err">혈관</span> <span class="err">침습</span><span class="p">(+),</span> <span class="err">림프절</span> <span class="err">전이</span> <span class="err">없음</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-6-췌장암-pancreatic-cancer-6-췌장암-pancreatic-cancer">🔍 6. 췌장암 (Pancreatic Cancer) {#6-췌장암-pancreatic-cancer}</h3>

<h4 id="-암종-설명-5">📘 암종 설명</h4>
<p>췌장암은 진단 당시 대부분 진행된 상태이며, 병리과에서 <strong>췌관선암(Adenocarcinoma of Pancreatic Duct)</strong>이 가장 흔합니다. AJCC 9차는 <strong>종양 크기, 혈관 침습, 림프절 수</strong>를 기반으로 병기 결정합니다.</p>

<hr />

<h4 id="-t-primary-tumor-5">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>T1a~T1c</strong>: ≤ 2cm (세분화)</li>
  <li><strong>T2</strong>: &gt; 2cm ≤ 4cm</li>
  <li><strong>T3</strong>: &gt; 4cm</li>
  <li><strong>T4</strong>: 주요 동맥 침범 (수술 불가)</li>
</ul>

<h4 id="-n-regional-lymph-nodes-5">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 림프절 전이 없음</li>
  <li><strong>N1</strong>: 1~3개 전이</li>
  <li><strong>N2</strong>: ≥ 4개 전이</li>
</ul>

<h4 id="-m-distant-metastasis-5">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 있음</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-5">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>6. 췌장암 (Pancreatic Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>IA</td>
      <td>T1</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IB</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T3</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T1–T3</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>III</td>
      <td>T4 or N2</td>
      <td>Any</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IV</td>
      <td>Any</td>
      <td>Any</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-4">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">췌장암</span> <span class="p">(</span><span class="nc">Pancreatic</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T3</span> <span class="p">(</span><span class="mf">4.5</span><span class="n">cm</span> <span class="err">종양</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N1</span> <span class="p">(</span><span class="err">림프절</span> <span class="mi">2</span><span class="err">개</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIB</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">췌장</span> <span class="err">두부에서</span> <span class="mf">4.5</span><span class="n">cm</span> <span class="err">크기의</span> <span class="err">췌관선암</span> <span class="err">확인됨</span><span class="p">.</span> <span class="err">상장간막정맥</span><span class="p">(</span><span class="nc">SMV</span><span class="p">)</span> <span class="err">침습</span> <span class="err">의심</span><span class="p">,</span> <span class="err">수술적</span> <span class="err">절제</span> <span class="err">경계선</span> <span class="err">병변</span><span class="p">(</span><span class="n">borderline</span> <span class="n">resectable</span><span class="p">).</span>
</code></pre></div></div>

<h3 id="-7-식도암-esophageal-cancer-7-식도암-esophageal-cancer">🔍 7. 식도암 (Esophageal Cancer) {#7-식도암-esophageal-cancer}</h3>

<h4 id="-암종-설명-6">📘 암종 설명</h4>
<p>식도암은 주로 <strong>편평상피암</strong>과 <strong>선암</strong>으로 나뉘며, 위치에 따라 경부, 흉부 상부/중부/하부로 분류됩니다. 병리과에서는 침윤 깊이, 림프절 전이 여부, 절제연 상태를 중점적으로 평가합니다.</p>

<p>AJCC 9차는 <strong>조직 아형(편평/선암)</strong>과 <strong>위치에 따른 병기 차이</strong>를 고려합니다.</p>

<hr />

<h4 id="-t-primary-tumor-6">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>Tis</strong>: 상피내암</li>
  <li><strong>T1a</strong>: 점막층</li>
  <li><strong>T1b</strong>: 점막하층</li>
  <li><strong>T2</strong>: 근육층</li>
  <li><strong>T3</strong>: 장막 전까지 침윤</li>
  <li><strong>T4a</strong>: 인접 구조 침범 (폐막 등) – 수술 가능</li>
  <li><strong>T4b</strong>: 수술 불가능 구조 침범 (기관, 대동맥 등)</li>
</ul>

<h4 id="-n-regional-lymph-nodes-6">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 전이 없음</li>
  <li><strong>N1</strong>: 1~2개</li>
  <li><strong>N2</strong>: 3~6개</li>
  <li><strong>N3</strong>: ≥7개</li>
</ul>

<h4 id="-m-distant-metastasis-6">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 원격 전이 있음</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-편평상피암-기준">🧩 병기 그룹 (Stage Grouping, 편평상피암 기준)</h4>

<p><strong>7. 식도암 (Esophageal Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tis</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IA</td>
      <td>T1a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IB</td>
      <td>T1b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T3</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T1–T2</td>
      <td>N2</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T3</td>
      <td>N1–N2</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>T4a</td>
      <td>Any N</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IV</td>
      <td>Any</td>
      <td>Any</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-5">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">식도암</span> <span class="p">(</span><span class="nc">Esophageal</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T3</span> <span class="p">(</span><span class="err">근육층</span> <span class="err">침범</span><span class="p">,</span> <span class="err">외막</span> <span class="err">근접</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N1</span> <span class="p">(</span><span class="mi">2</span><span class="err">개</span> <span class="err">림프절</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIIB</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">흉부</span> <span class="err">중간부위</span> <span class="err">식도에</span> <span class="mi">5</span><span class="n">cm</span> <span class="err">크기의</span> <span class="err">편평상피세포암</span> <span class="err">확인됨</span><span class="p">.</span> <span class="err">장막</span> <span class="err">직전까지</span> <span class="err">침윤</span><span class="p">,</span> <span class="err">기관</span> <span class="err">및</span> <span class="err">대동맥</span> <span class="err">침범</span> <span class="err">없음</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-8-후두암-laryngeal-cancer-8-후두암-laryngeal-cancer">🔍 8. 후두암 (Laryngeal Cancer) {#8-후두암-laryngeal-cancer}</h3>

<h4 id="-암종-설명-7">📘 암종 설명</h4>
<p>후두암은 해부학적으로 <strong>성문부</strong>, <strong>상부성문</strong>, <strong>하부성문</strong>으로 구분됩니다. 대부분은 편평상피암이며, 병리과에서는 <strong>성대 움직임</strong>, <strong>연골 침범 여부</strong>를 포함하여 기술합니다.</p>

<p>AJCC 9차는 해부학적 위치에 따라 T병기 정의가 다릅니다.</p>

<hr />

<h4 id="-t-glottic-cancer-기준">🔸 T (Glottic Cancer 기준)</h4>
<ul>
  <li><strong>T1</strong>: 한쪽 성대 국한, 정상 움직임</li>
  <li><strong>T2</strong>: 둘 중 하나 이상 침범, 성대운동 이상</li>
  <li><strong>T3</strong>: 성대 고정 or 연골 침범</li>
  <li><strong>T4a</strong>: 갑상선연골 침범 또는 경부 연부조직 침윤</li>
  <li><strong>T4b</strong>: 전척추근, 대혈관, 종격동 침윤</li>
</ul>

<h4 id="-n-regional-lymph-nodes-7">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 없음</li>
  <li><strong>N1</strong>: 단일, ≤3cm</li>
  <li><strong>N2</strong>: 다발, ≤6cm</li>
  <li><strong>N3</strong>: &gt;6cm</li>
</ul>

<h4 id="-m-distant-metastasis-7">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 있음</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-6">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>8. 후두암 (Laryngeal Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tis</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>I</td>
      <td>T1</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>II</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>III</td>
      <td>T3 or N1</td>
      <td>M0</td>
      <td> </td>
    </tr>
    <tr>
      <td>IVA</td>
      <td>T4a or N2</td>
      <td>M0</td>
      <td> </td>
    </tr>
    <tr>
      <td>IVB</td>
      <td>T4b or N3</td>
      <td>M0</td>
      <td> </td>
    </tr>
    <tr>
      <td>IVC</td>
      <td>Any</td>
      <td>Any</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-6">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">후두암</span> <span class="p">(</span><span class="nc">Laryngeal</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2</span> <span class="p">(</span><span class="err">상부성문</span> <span class="err">및</span> <span class="err">성문부</span> <span class="err">침범</span><span class="p">,</span> <span class="err">성대</span> <span class="err">운동</span> <span class="err">제한</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N0</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">II</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">편평상피암으로</span> <span class="err">상부성문을</span> <span class="err">포함한</span> <span class="err">병변</span><span class="p">.</span> <span class="err">좌측</span> <span class="err">성대</span> <span class="err">고정</span> <span class="err">없음</span><span class="p">.</span> <span class="err">갑상선</span> <span class="err">연골</span> <span class="err">침범</span> <span class="err">없음</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-9-자궁경부암-cervical-cancer-9-자궁경부암-cervical-cancer">🔍 9. 자궁경부암 (Cervical Cancer) {#9-자궁경부암-cervical-cancer}</h3>

<h4 id="-암종-설명-8">📘 암종 설명</h4>
<p>자궁경부암은 <strong>편평상피암</strong>, <strong>선암</strong>이 주로 발생하며, 병리과에서 진단은 생검이나 LEEP, 원추절제 등으로 이뤄집니다. AJCC는 FIGO 병기와 병용되며, <strong>림프절 전이 유무(N)</strong>가 병기에서 중요하게 반영됩니다.</p>

<hr />

<h4 id="-t-primary-tumor-7">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>Tis</strong>: 상피내암</li>
  <li><strong>T1a</strong>: 미세침윤</li>
  <li><strong>T1b</strong>: 육안적 병변 ≤ 4cm</li>
  <li><strong>T2</strong>: 자궁경부 외 확산 (질 상부, 인대 등)</li>
  <li><strong>T3</strong>: 골반벽, 질 하부 침범</li>
  <li><strong>T4</strong>: 방광, 직장 침범</li>
</ul>

<h4 id="-n-regional-lymph-nodes-8">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 없음</li>
  <li><strong>N1</strong>: 림프절 전이 있음</li>
</ul>

<h4 id="-m-distant-metastasis-8">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 있음</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-7">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>9. 자궁경부암 (Cervical Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tis</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IA</td>
      <td>T1a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IB</td>
      <td>T1b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T2a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T2b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIA</td>
      <td>T3a</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIB</td>
      <td>T3b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IIIC</td>
      <td>Any</td>
      <td>N1</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IVA</td>
      <td>T4</td>
      <td>Any</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IVB</td>
      <td>Any</td>
      <td>Any</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-7">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">자궁경부암</span> <span class="p">(</span><span class="nc">Cervical</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2b</span> <span class="p">(</span><span class="err">자궁인대</span> <span class="err">침범</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N0</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIB</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">편평상피세포암</span><span class="p">.</span> <span class="err">자궁체부는</span> <span class="err">침범</span> <span class="err">없음</span><span class="p">.</span> <span class="err">골반</span> <span class="err">림프절</span> <span class="err">전이</span> <span class="err">없음</span><span class="p">.</span> <span class="err">방광</span> <span class="err">직장</span> <span class="err">침습</span> <span class="err">없음</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-10-전립선암-prostate-cancer-10-전립선암-prostate-cancer">🔍 10. 전립선암 (Prostate Cancer) {#10-전립선암-prostate-cancer}</h3>

<h4 id="-암종-설명-9">📘 암종 설명</h4>
<p>전립선암은 고령 남성에서 흔하며 대부분 <strong>선암(Adenocarcinoma)</strong>입니다. 병리과에서는 <strong>Gleason 점수</strong>, <strong>PSA 수치</strong>, <strong>림프절 전이</strong>, <strong>주변 조직 침습</strong> 등을 고려합니다.</p>

<p>AJCC 9차에서는 기존 TNM에 더해, <strong>Grade Group</strong>과 <strong>PSA 수치</strong>를 포함한 예후 분류(Prognostic Stage Grouping)를 강조합니다.</p>

<hr />

<h4 id="-t-primary-tumor-8">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>T1</strong>: 영상/촉진에서 안 보이지만 생검으로 진단</li>
  <li><strong>T2</strong>: 전립선 내에 국한
    <ul>
      <li>T2a: 절반 미만</li>
      <li>T2b: 절반 초과</li>
      <li>T2c: 양엽 침범</li>
    </ul>
  </li>
  <li><strong>T3</strong>: 피막 밖 침범
    <ul>
      <li>T3a: 피막 밖</li>
      <li>T3b: 정낭 침범</li>
    </ul>
  </li>
  <li><strong>T4</strong>: 방광경부, 직장, 근육 침범</li>
</ul>

<h4 id="-n-regional-lymph-nodes-9">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 없음</li>
  <li><strong>N1</strong>: 골반 림프절 전이</li>
</ul>

<h4 id="-m-distant-metastasis-9">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1a</strong>: 림프절(원격)</li>
  <li><strong>M1b</strong>: 뼈</li>
  <li><strong>M1c</strong>: 다른 장기</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-8">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>10. 전립선암 (Prostate Cancer)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
      <th>PSA</th>
      <th>Gleason</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>I</td>
      <td>T1c</td>
      <td>N0</td>
      <td>M0</td>
      <td>&lt;10</td>
      <td>≤6</td>
    </tr>
    <tr>
      <td>IIA</td>
      <td>T1–T2</td>
      <td>N0</td>
      <td>M0</td>
      <td>10–20</td>
      <td>≤6</td>
    </tr>
    <tr>
      <td>IIB</td>
      <td>T2</td>
      <td>N0</td>
      <td>M0</td>
      <td>&gt;20</td>
      <td>≤6</td>
    </tr>
    <tr>
      <td>IIC</td>
      <td>T1–T2</td>
      <td>N0</td>
      <td>M0</td>
      <td>Any</td>
      <td>7</td>
    </tr>
    <tr>
      <td>III</td>
      <td>T3</td>
      <td>N0</td>
      <td>M0</td>
      <td>Any</td>
      <td>Any</td>
    </tr>
    <tr>
      <td>IVA</td>
      <td>Any</td>
      <td>N1</td>
      <td>M0</td>
      <td>Any</td>
      <td>Any</td>
    </tr>
    <tr>
      <td>IVB</td>
      <td>Any</td>
      <td>Any</td>
      <td>M1</td>
      <td>Any</td>
      <td>Any</td>
    </tr>
  </tbody>
</table>

<p><br /></p>

<h4 id="-표준-서식-예시-8">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">전립선암</span> <span class="p">(</span><span class="nc">Prostate</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2c</span> <span class="p">(</span><span class="err">양엽</span> <span class="err">침범</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N0</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIC</span>
<span class="nc">PSA</span><span class="p">:</span> <span class="mf">8.5</span> <span class="n">ng</span><span class="p">/</span><span class="n">mL</span>
<span class="nc">Gleason</span> <span class="nc">Score</span><span class="p">:</span> <span class="mi">3</span><span class="p">+</span><span class="mi">4</span><span class="p">=</span><span class="mi">7</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">전립선</span> <span class="err">선암으로</span> <span class="err">양엽에</span> <span class="err">병변</span> <span class="err">분포</span><span class="p">.</span> <span class="err">정낭</span><span class="p">,</span> <span class="err">직장</span> <span class="err">침범</span> <span class="err">없음</span><span class="p">.</span> <span class="err">골반</span> <span class="err">림프절</span> <span class="err">음성</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-11-피부-흑색종-melanoma-11-피부-흑색종-melanoma">🔍 11. 피부 흑색종 (Melanoma) {#11-피부-흑색종-melanoma}</h3>

<h4 id="-암종-설명-10">📘 암종 설명</h4>
<p>피부 흑색종은 악성도가 높은 피부암으로, 병리과에서는 <strong>Breslow 두께</strong>, <strong>궤양 유무</strong>, <strong>림프절 전이</strong>, <strong>미세전이 여부</strong>가 병기 결정에 결정적입니다.</p>

<p>AJCC 9차는 T 병기를 <strong>두께(mm)</strong>와 <strong>궤양</strong> 여부로 구분하며, N/M 병기와 함께 예후를 분류합니다.</p>

<hr />

<h4 id="-t-primary-tumor-9">🔸 T (Primary Tumor)</h4>
<ul>
  <li>T1: ≤1.0mm</li>
  <li>T2: 1.01–2.0mm</li>
  <li>T3: 2.01–4.0mm</li>
  <li>T4: &gt;4.0mm<br />
각 T는 <strong>a: 궤양 없음 / b: 궤양 있음</strong>으로 세분화</li>
</ul>

<h4 id="-n-regional-lymph-nodes-10">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li>N0: 없음</li>
  <li>N1: 1개 림프절</li>
  <li>N2: 2–3개</li>
  <li>N3: ≥4개 or 합병된 림프절</li>
</ul>

<h4 id="-m-distant-metastasis-10">🔸 M (Distant Metastasis)</h4>
<ul>
  <li>M0: 없음</li>
  <li>M1a: 피부/피하</li>
  <li>M1b: 폐</li>
  <li>M1c: 뇌 외 장기</li>
  <li>M1d: 뇌 포함</li>
</ul>

<hr />

<h4 id="-병기-그룹-stage-grouping-9">🧩 병기 그룹 (Stage Grouping)</h4>

<p><strong>11. 피부 흑색종 (Melanoma)</strong></p>

<p><br /></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tis</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>I</td>
      <td>T1a–b</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>II</td>
      <td>T2–T4</td>
      <td>N0</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>III</td>
      <td>Any</td>
      <td>N1–N3</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>IV</td>
      <td>Any</td>
      <td>Any</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<p><br /></p>
<h4 id="-표준-서식-예시-9">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">피부</span> <span class="err">흑색종</span> <span class="p">(</span><span class="nc">Melanoma</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T2a</span> <span class="p">(</span><span class="mf">1.5</span><span class="n">mm</span> <span class="err">두께</span><span class="p">,</span> <span class="err">궤양</span> <span class="err">없음</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N1</span> <span class="p">(</span><span class="mi">1</span><span class="err">개</span> <span class="err">림프절</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">IIIA</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">등</span> <span class="err">부위에서</span> <span class="err">절제된</span> <span class="err">흑색종</span><span class="p">.</span> <span class="nc">Breslow</span> <span class="err">두께</span> <span class="mf">1.5</span><span class="n">mm</span><span class="p">,</span> <span class="nc">Clark</span> <span class="nc">Level</span> <span class="nc">IV</span><span class="p">,</span> <span class="err">궤양</span> <span class="err">없음</span><span class="p">.</span> <span class="nc">SLNB</span><span class="err">에서</span> <span class="err">림프절</span> <span class="mi">1</span><span class="err">개</span> <span class="err">양성</span><span class="p">.</span>
</code></pre></div></div>

<h3 id="-12-갑상선암-thyroid-cancer-12-갑상선암-thyroid-cancer">🔍 12. 갑상선암 (Thyroid Cancer) {#12-갑상선암-thyroid-cancer}</h3>

<h4 id="-암종-설명-11">📘 암종 설명</h4>
<p>갑상선암은 대부분 <strong>유두암(Papillary)</strong>이나 <strong>여포암(Follicular)</strong> 형태로, 예후가 좋습니다. AJCC 9차에서는 <strong>나이 55세를 기준</strong>으로 병기 분류가 나뉘며, <strong>T/N/M에 따른 세분화</strong>도 존재합니다.</p>

<hr />

<h4 id="-t-primary-tumor-10">🔸 T (Primary Tumor)</h4>
<ul>
  <li><strong>T1</strong>: ≤2cm</li>
  <li><strong>T2</strong>: &gt;2cm ≤4cm</li>
  <li><strong>T3a</strong>: &gt;4cm, 갑상선 내 국한</li>
  <li><strong>T3b</strong>: 피막 침범</li>
  <li><strong>T4a</strong>: 기관, 식도 침범</li>
  <li><strong>T4b</strong>: 대혈관 침범</li>
</ul>

<h4 id="-n-regional-lymph-nodes-11">🔸 N (Regional Lymph Nodes)</h4>
<ul>
  <li><strong>N0</strong>: 없음</li>
  <li><strong>N1a</strong>: 중심부 림프절</li>
  <li><strong>N1b</strong>: 측경부, 종격동 림프절</li>
</ul>

<h4 id="-m-distant-metastasis-11">🔸 M (Distant Metastasis)</h4>
<ul>
  <li><strong>M0</strong>: 없음</li>
  <li><strong>M1</strong>: 있음</li>
</ul>

<hr />

<h4 id="-병기-그룹-55세-미만-환자">🧩 병기 그룹 (55세 미만 환자)</h4>

<p><strong>12. 갑상선암 (Thyroid Cancer)</strong></p>

<table>
  <thead>
    <tr>
      <th>병기</th>
      <th>T</th>
      <th>N</th>
      <th>M</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>I (55세 미만)</td>
      <td>-</td>
      <td>-</td>
      <td>M0</td>
    </tr>
    <tr>
      <td>II (55세 미만)</td>
      <td>-</td>
      <td>-</td>
      <td>M1</td>
    </tr>
  </tbody>
</table>

<h4 id="-병기-그룹-55세-이상-환자">🧩 병기 그룹 (55세 이상 환자)</h4>
<p>| 병기 | T | N | M |
| — | — | — | — |
| I (55세 이상) | T1–T2 | N0 | M0 |
| II (55세 이상) | T1–T2 | N1 | M0 / T3 | Any | M0 |
| III | T4a | Any | M0 |
| IV | T4b | Any | M0 / Any | Any | M1 |</p>

<hr />

<h4 id="-표준-서식-예시-10">📄 표준 서식 예시</h4>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">암종명</span><span class="p">:</span> <span class="err">갑상선암</span> <span class="p">(</span><span class="nc">Thyroid</span> <span class="nc">Cancer</span><span class="p">)</span>
<span class="nc">T</span><span class="p">:</span> <span class="nc">T1b</span> <span class="p">(</span><span class="mf">1.8</span><span class="n">cm</span><span class="p">,</span> <span class="err">좌엽</span> <span class="err">국한</span><span class="p">)</span>
<span class="nc">N</span><span class="p">:</span> <span class="nc">N1a</span> <span class="p">(</span><span class="err">중심</span> <span class="err">림프절</span> <span class="err">전이</span><span class="p">)</span>
<span class="nc">M</span><span class="p">:</span> <span class="nc">M0</span>
<span class="err">나이</span><span class="p">:</span> <span class="mi">56</span><span class="err">세</span>
<span class="nc">Stage</span> <span class="nc">Group</span><span class="p">:</span> <span class="nc">II</span>
<span class="err">소견</span><span class="p">:</span> <span class="err">유두암</span><span class="p">(</span><span class="nc">Papillary</span> <span class="n">thyroid</span> <span class="n">carcinoma</span><span class="p">),</span> <span class="mf">1.8</span><span class="n">cm</span><span class="p">.</span> <span class="err">갑상선</span> <span class="err">피막</span> <span class="err">침범</span> <span class="err">없음</span><span class="p">.</span> <span class="err">중심부</span> <span class="err">림프절</span> <span class="mi">2</span><span class="err">개</span> <span class="err">양성</span><span class="p">.</span> <span class="err">원격</span> <span class="err">전이</span> <span class="err">없음</span><span class="p">.</span>
</code></pre></div></div>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="메디컬이야기" /><category term="메디컬이야기" /><summary type="html"><![CDATA[엄청 오래 걸렷다]]></summary></entry><entry><title type="html">[NotionAPI] NotionAPI simple code</title><link href="https://amsomad.github.io/c%23/NotionAPI_Csharp/" rel="alternate" type="text/html" title="[NotionAPI] NotionAPI simple code" /><published>2024-07-24T00:00:00+00:00</published><updated>2024-02-24T00:00:00+00:00</updated><id>https://amsomad.github.io/c%23/NotionAPI_Csharp</id><content type="html" xml:base="https://amsomad.github.io/c%23/NotionAPI_Csharp/"><![CDATA[<!-- link -->

<h2 id="소개">소개</h2>

<p>Notion의 DB를 API로 불러오는데 사용되는 코드입니다. <br />
만약 해당DB에 특정 컬럼만 가져오고 싶다면 <code class="language-plaintext highlighter-rouge">columnOrder</code>를 사용하여 컬럼의 순서 및 이름을 지정할 수 있습니다. <br />
ex) <code class="language-plaintext highlighter-rouge">columnOrder = new List&lt;string&gt; { "Name", "Age", "Email" };</code> <br /></p>

<p>사용된 라이브러리는 <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code>,<code class="language-plaintext highlighter-rouge">RestSharp</code> 입니다. <br /></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Data</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Newtonsoft.Json.Linq</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">RestSharp</span><span class="p">;</span>

   <span class="k">public</span> <span class="k">class</span> <span class="nc">NotionAPI</span>
   <span class="p">{</span>
       <span class="k">private</span> <span class="kt">string</span> <span class="n">_apiKey</span><span class="p">;</span>
       <span class="k">private</span> <span class="kt">string</span> <span class="n">_dbKey</span><span class="p">;</span>

       <span class="k">public</span> <span class="nf">NotionAPI</span><span class="p">(</span><span class="kt">string</span> <span class="n">apiKey</span><span class="p">,</span> <span class="kt">string</span> <span class="n">dbKey</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="n">_apiKey</span> <span class="p">=</span> <span class="n">apiKey</span><span class="p">;</span>
           <span class="n">_dbKey</span> <span class="p">=</span> <span class="n">dbKey</span><span class="p">;</span>
       <span class="p">}</span>

       <span class="c1">/// &lt;summary&gt;</span>
       <span class="c1">/// Notion API에서 데이터를 가져와 DataTable로 반환합니다.</span>
       <span class="c1">/// &lt;/summary&gt;</span>
       <span class="c1">/// &lt;returns&gt;DataTable 형태의 노션 데이터&lt;/returns&gt;</span>
       <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">DataTable</span><span class="p">&gt;</span> <span class="nf">FetchDataAsDataTableAsync</span><span class="p">()</span>
       <span class="p">{</span>
           <span class="kt">string</span> <span class="n">jsonData</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">FetchNotionDataAsync</span><span class="p">();</span>
           <span class="k">return</span> <span class="nf">ParseDataToDataTable</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
       <span class="p">}</span>

       <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">DataTable</span><span class="p">&gt;</span> <span class="nf">FetchDataAsDataTableAsync</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">columnOrder</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">string</span> <span class="n">jsonData</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">FetchNotionDataAsync</span><span class="p">();</span>
           <span class="k">return</span> <span class="nf">ParseDataToDataTable</span><span class="p">(</span><span class="n">jsonData</span><span class="p">,</span> <span class="n">columnOrder</span><span class="p">);</span>
       <span class="p">}</span>


       <span class="c1">/// &lt;summary&gt;</span>
       <span class="c1">/// Notion API에서 데이터를 가져와 List&lt;Dictionary&lt;string, string&gt;&gt;로 반환합니다.</span>
       <span class="c1">/// &lt;/summary&gt;</span>
       <span class="c1">/// &lt;returns&gt;List&lt;Dictionary&lt;string, string&gt;&gt; 형태의 노션 데이터&lt;/returns&gt;</span>
       <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">FetchDataAsListAsync</span><span class="p">()</span>
       <span class="p">{</span>
           <span class="kt">string</span> <span class="n">jsonData</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">FetchNotionDataAsync</span><span class="p">();</span>
           <span class="k">return</span> <span class="nf">ParseDataToList</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
       <span class="p">}</span>

       <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;&gt;</span> <span class="nf">FetchDataAsListAsync</span><span class="p">(</span><span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">columnOrder</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">string</span> <span class="n">jsonData</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">FetchNotionDataAsync</span><span class="p">();</span>
           <span class="k">return</span> <span class="nf">ParseDataToList</span><span class="p">(</span><span class="n">jsonData</span><span class="p">,</span> <span class="n">columnOrder</span><span class="p">);</span>
       <span class="p">}</span>

       <span class="k">private</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">FetchNotionDataAsync</span><span class="p">()</span>
       <span class="p">{</span>
           <span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">RestClient</span><span class="p">(</span><span class="s">$"https://api.notion.com/v1/databases/</span><span class="p">{</span><span class="n">_dbKey</span><span class="p">}</span><span class="s">/query"</span><span class="p">);</span>
           <span class="kt">var</span> <span class="n">request</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">RestRequest</span><span class="p">();</span>
           <span class="n">request</span><span class="p">.</span><span class="n">Method</span> <span class="p">=</span> <span class="n">Method</span><span class="p">.</span><span class="n">Post</span><span class="p">;</span>
           <span class="n">request</span><span class="p">.</span><span class="nf">AddHeader</span><span class="p">(</span><span class="s">"Authorization"</span><span class="p">,</span> <span class="s">$"Bearer </span><span class="p">{</span><span class="n">_apiKey</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
           <span class="n">request</span><span class="p">.</span><span class="nf">AddHeader</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">);</span>
           <span class="n">request</span><span class="p">.</span><span class="nf">AddHeader</span><span class="p">(</span><span class="s">"Notion-Version"</span><span class="p">,</span> <span class="s">"2021-05-13"</span><span class="p">);</span>

           <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">ExecuteAsync</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>
           <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">;</span>
       <span class="p">}</span>

       <span class="k">private</span> <span class="n">DataTable</span> <span class="nf">ParseDataToDataTable</span><span class="p">(</span><span class="kt">string</span> <span class="n">jsonData</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">var</span> <span class="n">jObject</span> <span class="p">=</span> <span class="n">JObject</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
           <span class="kt">var</span> <span class="n">dataTable</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">DataTable</span><span class="p">();</span>

           <span class="kt">var</span> <span class="n">firstResult</span> <span class="p">=</span> <span class="n">jObject</span><span class="p">[</span><span class="s">"results"</span><span class="p">]?.</span><span class="nf">FirstOrDefault</span><span class="p">()</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">;</span>
           <span class="kt">var</span> <span class="n">properties</span> <span class="p">=</span> <span class="n">firstResult</span><span class="p">?[</span><span class="s">"properties"</span><span class="p">]</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">;</span>

           <span class="k">if</span> <span class="p">(</span><span class="n">properties</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="k">null</span><span class="p">;</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">property</span> <span class="k">in</span> <span class="n">properties</span><span class="p">)</span>
           <span class="p">{</span>
               <span class="n">dataTable</span><span class="p">.</span><span class="n">Columns</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">property</span><span class="p">.</span><span class="n">Key</span><span class="p">.</span><span class="nf">Replace</span><span class="p">(</span><span class="s">" "</span><span class="p">,</span> <span class="s">"_"</span><span class="p">));</span>
           <span class="p">}</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">result</span> <span class="k">in</span> <span class="n">jObject</span><span class="p">[</span><span class="s">"results"</span><span class="p">])</span>
           <span class="p">{</span>
               <span class="kt">var</span> <span class="n">row</span> <span class="p">=</span> <span class="n">dataTable</span><span class="p">.</span><span class="nf">NewRow</span><span class="p">();</span>
               <span class="kt">var</span> <span class="n">propertyValues</span> <span class="p">=</span> <span class="nf">ParseProperties</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s">"properties"</span><span class="p">]</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">);</span>

               <span class="k">foreach</span> <span class="p">(</span><span class="n">DataColumn</span> <span class="n">column</span> <span class="k">in</span> <span class="n">dataTable</span><span class="p">.</span><span class="n">Columns</span><span class="p">)</span>
               <span class="p">{</span>
                   <span class="n">row</span><span class="p">[</span><span class="n">column</span><span class="p">.</span><span class="n">ColumnName</span><span class="p">]</span> <span class="p">=</span> <span class="n">propertyValues</span><span class="p">.</span><span class="nf">ContainsKey</span><span class="p">(</span><span class="n">column</span><span class="p">.</span><span class="n">ColumnName</span><span class="p">.</span><span class="nf">Replace</span><span class="p">(</span><span class="s">"_"</span><span class="p">,</span> <span class="s">" "</span><span class="p">))</span> <span class="p">?</span> <span class="n">propertyValues</span><span class="p">[</span><span class="n">column</span><span class="p">.</span><span class="n">ColumnName</span><span class="p">.</span><span class="nf">Replace</span><span class="p">(</span><span class="s">"_"</span><span class="p">,</span> <span class="s">" "</span><span class="p">)]</span> <span class="p">:</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
               <span class="p">}</span>

               <span class="n">dataTable</span><span class="p">.</span><span class="n">Rows</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">row</span><span class="p">);</span>
           <span class="p">}</span>

           <span class="k">return</span> <span class="n">dataTable</span><span class="p">;</span>
       <span class="p">}</span>

       <span class="c1">/// &lt;summary&gt;</span>
       <span class="c1">/// 컬럼의 순서 및 이름을 지정하여 DataTable로 반환합니다.</span>
       <span class="c1">/// &lt;/summary&gt;</span>
       <span class="c1">/// &lt;param name="jsonData"&gt;&lt;/param&gt;</span>
       <span class="c1">/// &lt;param name="columnOrder"&gt;&lt;/param&gt;</span>
       <span class="c1">/// &lt;returns&gt;&lt;/returns&gt;</span>
       <span class="k">private</span> <span class="n">DataTable</span> <span class="nf">ParseDataToDataTable</span><span class="p">(</span><span class="kt">string</span> <span class="n">jsonData</span><span class="p">,</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">columnOrder</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">var</span> <span class="n">jObject</span> <span class="p">=</span> <span class="n">JObject</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
           <span class="kt">var</span> <span class="n">dataTable</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">DataTable</span><span class="p">();</span>

           <span class="kt">var</span> <span class="n">firstResult</span> <span class="p">=</span> <span class="n">jObject</span><span class="p">[</span><span class="s">"results"</span><span class="p">]?.</span><span class="nf">FirstOrDefault</span><span class="p">()</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">;</span>
           <span class="kt">var</span> <span class="n">properties</span> <span class="p">=</span> <span class="n">firstResult</span><span class="p">?[</span><span class="s">"properties"</span><span class="p">]</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">;</span>

           <span class="k">if</span> <span class="p">(</span><span class="n">properties</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="k">return</span> <span class="k">null</span><span class="p">;</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">column</span> <span class="k">in</span> <span class="n">columnOrder</span><span class="p">)</span>
           <span class="p">{</span>
               <span class="n">dataTable</span><span class="p">.</span><span class="n">Columns</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">column</span><span class="p">);</span>
           <span class="p">}</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">result</span> <span class="k">in</span> <span class="n">jObject</span><span class="p">[</span><span class="s">"results"</span><span class="p">])</span>
           <span class="p">{</span>
               <span class="kt">var</span> <span class="n">row</span> <span class="p">=</span> <span class="n">dataTable</span><span class="p">.</span><span class="nf">NewRow</span><span class="p">();</span>
               <span class="kt">var</span> <span class="n">propertyValues</span> <span class="p">=</span> <span class="nf">ParseProperties</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s">"properties"</span><span class="p">]</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">);</span>

               <span class="k">foreach</span> <span class="p">(</span><span class="n">DataColumn</span> <span class="n">column</span> <span class="k">in</span> <span class="n">dataTable</span><span class="p">.</span><span class="n">Columns</span><span class="p">)</span>
               <span class="p">{</span>
                   <span class="n">row</span><span class="p">[</span><span class="n">column</span><span class="p">.</span><span class="n">ColumnName</span><span class="p">]</span> <span class="p">=</span> <span class="n">propertyValues</span><span class="p">.</span><span class="nf">ContainsKey</span><span class="p">(</span><span class="n">column</span><span class="p">.</span><span class="n">ColumnName</span><span class="p">)</span> <span class="p">?</span> <span class="n">propertyValues</span><span class="p">[</span><span class="n">column</span><span class="p">.</span><span class="n">ColumnName</span><span class="p">]</span> <span class="p">:</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
               <span class="p">}</span>

               <span class="n">dataTable</span><span class="p">.</span><span class="n">Rows</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">row</span><span class="p">);</span>
           <span class="p">}</span>

           <span class="k">return</span> <span class="n">dataTable</span><span class="p">;</span>

       <span class="p">}</span>

       <span class="k">private</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">ParseDataToList</span><span class="p">(</span><span class="kt">string</span> <span class="n">jsonData</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">var</span> <span class="n">jObject</span> <span class="p">=</span> <span class="n">JObject</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
           <span class="kt">var</span> <span class="n">resultsList</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;();</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">result</span> <span class="k">in</span> <span class="n">jObject</span><span class="p">[</span><span class="s">"results"</span><span class="p">])</span>
           <span class="p">{</span>
               <span class="kt">var</span> <span class="n">propertyValues</span> <span class="p">=</span> <span class="nf">ParseProperties</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s">"properties"</span><span class="p">]</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">);</span>
               <span class="n">resultsList</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">propertyValues</span><span class="p">);</span>
           <span class="p">}</span>

           <span class="k">return</span> <span class="n">resultsList</span><span class="p">;</span>
       <span class="p">}</span>

       <span class="c1">/// &lt;summary&gt;</span>
       <span class="c1">/// 컬럼의 순서 및 이름을 지정하여 Dictionary로 반환합니다.</span>
       <span class="c1">/// &lt;/summary&gt;</span>
       <span class="c1">/// &lt;param name="jsonData"&gt;&lt;/param&gt;</span>
       <span class="c1">/// &lt;param name="columnOrder"&gt;&lt;/param&gt;</span>
       <span class="c1">/// &lt;returns&gt;&lt;/returns&gt;</span>
       <span class="k">private</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;</span> <span class="nf">ParseDataToList</span><span class="p">(</span><span class="kt">string</span> <span class="n">jsonData</span><span class="p">,</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">columnOrder</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">var</span> <span class="n">jObject</span> <span class="p">=</span> <span class="n">JObject</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">jsonData</span><span class="p">);</span>
           <span class="kt">var</span> <span class="n">resultsList</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;&gt;();</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">result</span> <span class="k">in</span> <span class="n">jObject</span><span class="p">[</span><span class="s">"results"</span><span class="p">])</span>
           <span class="p">{</span>
               <span class="kt">var</span> <span class="n">propertyValues</span> <span class="p">=</span> <span class="nf">ParseProperties</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s">"properties"</span><span class="p">]</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">);</span>
               <span class="kt">var</span> <span class="n">orderedPropertyValues</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;();</span>

               <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">column</span> <span class="k">in</span> <span class="n">columnOrder</span><span class="p">)</span>
               <span class="p">{</span>
                   <span class="n">orderedPropertyValues</span><span class="p">[</span><span class="n">column</span><span class="p">]</span> <span class="p">=</span> <span class="n">propertyValues</span><span class="p">.</span><span class="nf">ContainsKey</span><span class="p">(</span><span class="n">column</span><span class="p">)</span> <span class="p">?</span> <span class="n">propertyValues</span><span class="p">[</span><span class="n">column</span><span class="p">]</span> <span class="p">:</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span><span class="p">;</span>
               <span class="p">}</span>

               <span class="n">resultsList</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">orderedPropertyValues</span><span class="p">);</span>
           <span class="p">}</span>

           <span class="k">return</span> <span class="n">resultsList</span><span class="p">;</span>
       <span class="p">}</span>

       <span class="k">private</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;</span> <span class="nf">ParseProperties</span><span class="p">(</span><span class="n">JObject</span> <span class="n">properties</span><span class="p">)</span>
       <span class="p">{</span>
           <span class="kt">var</span> <span class="n">parsedProperties</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">string</span><span class="p">&gt;();</span>

           <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">property</span> <span class="k">in</span> <span class="n">properties</span><span class="p">)</span>
           <span class="p">{</span>
               <span class="kt">var</span> <span class="k">value</span> <span class="p">=</span> <span class="n">property</span><span class="p">.</span><span class="n">Value</span> <span class="k">as</span> <span class="n">JObject</span><span class="p">;</span>
               <span class="kt">var</span> <span class="n">propertyType</span> <span class="p">=</span> <span class="k">value</span><span class="p">?[</span><span class="s">"type"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">();</span>

               <span class="n">parsedProperties</span><span class="p">[</span><span class="n">property</span><span class="p">.</span><span class="n">Key</span><span class="p">]</span> <span class="p">=</span> <span class="n">propertyType</span> <span class="k">switch</span>
               <span class="p">{</span>
                   <span class="s">"title"</span> <span class="p">=&gt;</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"title"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"text"</span><span class="p">]?[</span><span class="s">"content"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()),</span>
                   <span class="s">"rich_text"</span> <span class="p">=&gt;</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">""</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"rich_text"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"text"</span><span class="p">]?[</span><span class="s">"content"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()),</span>
                   <span class="s">"number"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"number"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"select"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"select"</span><span class="p">]?[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"multi_select"</span> <span class="p">=&gt;</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"multi_select"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"name"</span><span class="p">].</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()),</span>
                   <span class="s">"date"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"date"</span><span class="p">]?[</span><span class="s">"start"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"checkbox"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"checkbox"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"url"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"url"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"email"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"email"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"phone_number"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"phone_number"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"people"</span> <span class="p">=&gt;</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"people"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()),</span>
                   <span class="s">"files"</span> <span class="p">=&gt;</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"files"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()),</span>
                   <span class="s">"relation"</span> <span class="p">=&gt;</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"relation"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"id"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()),</span>
                   <span class="s">"formula"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"formula"</span><span class="p">]?[</span><span class="s">"string"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"created_time"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"created_time"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"created_by"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"created_by"</span><span class="p">]?[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"last_edited_time"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"last_edited_time"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"last_edited_by"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"last_edited_by"</span><span class="p">]?[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"rollup"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"rollup"</span><span class="p">]?[</span><span class="s">"type"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">==</span> <span class="s">"array"</span>
                       <span class="p">?</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="k">value</span><span class="p">[</span><span class="s">"rollup"</span><span class="p">]?[</span><span class="s">"array"</span><span class="p">]?.</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">())</span> <span class="p">??</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;())</span>
                       <span class="p">:</span> <span class="k">value</span><span class="p">[</span><span class="s">"rollup"</span><span class="p">]?[</span><span class="s">"number"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="s">"status"</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">[</span><span class="s">"status"</span><span class="p">]?[</span><span class="s">"name"</span><span class="p">]?.</span><span class="nf">ToString</span><span class="p">()</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span>
                   <span class="n">_</span> <span class="p">=&gt;</span> <span class="s">""</span>
               <span class="p">};</span>
           <span class="p">}</span>

           <span class="k">return</span> <span class="n">parsedProperties</span><span class="p">;</span>
       <span class="p">}</span>
   <span class="p">}</span>
</code></pre></div></div>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="C#" /><category term="C#" /><category term="ASP.NET Core" /><category term="Windows" /><summary type="html"><![CDATA[Notion RestAPI simple code]]></summary></entry><entry><title type="html">[gRPC]python gRPC client</title><link href="https://amsomad.github.io/gRPC/python-grpc-client/" rel="alternate" type="text/html" title="[gRPC]python gRPC client" /><published>2024-04-30T00:00:00+00:00</published><updated>2024-04-30T00:00:00+00:00</updated><id>https://amsomad.github.io/gRPC/python-grpc-client</id><content type="html" xml:base="https://amsomad.github.io/gRPC/python-grpc-client/"><![CDATA[<hr />

<p>gRPC를 Python에서 사용하는 방법을 알아보겠습니다. <br /></p>

<h2 id="grpc-python-client">gRPC Python Client</h2>

<p>Proto File을 통해 생성된 Python 코드를 사용하여 gRPC 서버에 요청을 보내는 클라이언트를 작성해보겠습니다.</p>

<h3 id="1-grpc-python-package-설치">1. gRPC Python Package 설치</h3>

<p>gRPC Python Package를 설치합니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>grpcio
pip <span class="nb">install </span>grpcio-tools
</code></pre></div></div>

<blockquote>
  <p>grpcio는 gRPC Python 라이브러리입니다. <br />
grpcio-tools는 .proto 파일을 Python 코드로 변환하는데 사용됩니다. <br /></p>
</blockquote>

<h3 id="2-proto-파일-작성">2. .proto 파일 작성</h3>

<p>gRPC 서버와 클라이언트가 통신할 때 사용할 .proto 파일을 작성합니다.</p>

<p>현업에서 쓰일만한 예제를 작성해보겠습니다.
partsclass &lt;–&gt; parts &lt;–&gt; sales 구조로 이루어진 예제입니다.</p>

<p>** partclass.proto 파일 작성</p>
<div class="language-proto highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">syntax</span> <span class="o">=</span> <span class="s">"proto3"</span><span class="p">;</span>

<span class="k">option</span> <span class="na">csharp_namespace</span> <span class="o">=</span> <span class="s">"SalesDB.partclassProtos"</span><span class="p">;</span>

<span class="kn">package</span> <span class="nn">partclassSvc</span><span class="p">;</span>

<span class="k">import</span> <span class="s">"google/protobuf/timestamp.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/duration.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/wrappers.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/empty.proto"</span><span class="p">;</span>

<span class="kd">service</span> <span class="n">partclass</span> <span class="p">{</span>
   <span class="k">rpc</span> <span class="n">GetByKey</span> <span class="p">(</span><span class="n">GetByKeyRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partclassResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetByKeyJson</span> <span class="p">(</span><span class="n">GetByKeyRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partclassResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetAll</span> <span class="p">(</span><span class="n">google.protobuf.Empty</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partclassSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetAllJson</span> <span class="p">(</span><span class="n">google.protobuf.Empty</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partclassSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Insert</span> <span class="p">(</span><span class="n">InsertRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partclassResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">InsertJson</span> <span class="p">(</span><span class="n">InsertRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partclassResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Update</span> <span class="p">(</span><span class="n">UpdateRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">UpdateResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Delete</span> <span class="p">(</span><span class="n">DeleteRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">DeleteResponse</span><span class="p">);</span>
<span class="p">}</span>


<span class="kd">message</span> <span class="nc">GrpcDecimal</span> <span class="p">{</span>
    <span class="kt">int64</span> <span class="na">units</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">sfixed32</span> <span class="na">nanos</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">partclassResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">partclass</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">partclassSetResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">partclassSet</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">DataSetResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">dataSet</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">GetByKeyRequest</span> <span class="p">{</span>
   <span class="kt">sint32</span> <span class="na">partclasscode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">InsertRequest</span> <span class="p">{</span>
   <span class="kt">string</span> <span class="na">partclassname</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">UpdateRequest</span> <span class="p">{</span>
   <span class="kt">sint32</span> <span class="na">partclasscode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">partclassname</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">UpdateResponse</span> <span class="p">{</span>
   <span class="kt">int32</span> <span class="na">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">DeleteRequest</span> <span class="p">{</span>
   <span class="kt">sint32</span> <span class="na">partclasscode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">DeleteResponse</span> <span class="p">{</span>
   <span class="kt">int32</span> <span class="na">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>** parts.proto 파일 작성</p>
<div class="language-proto highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">syntax</span> <span class="o">=</span> <span class="s">"proto3"</span><span class="p">;</span>

<span class="k">option</span> <span class="na">csharp_namespace</span> <span class="o">=</span> <span class="s">"SalesDB.partsProtos"</span><span class="p">;</span>

<span class="kn">package</span> <span class="nn">partsSvc</span><span class="p">;</span>

<span class="k">import</span> <span class="s">"google/protobuf/timestamp.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/duration.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/wrappers.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/empty.proto"</span><span class="p">;</span>

<span class="kd">service</span> <span class="n">parts</span> <span class="p">{</span>
   <span class="k">rpc</span> <span class="n">GetForpartclass</span> <span class="p">(</span><span class="n">GetForpartclassRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetForpartclassJson</span> <span class="p">(</span><span class="n">GetForpartclassRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetByKey</span> <span class="p">(</span><span class="n">GetByKeyRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetByKeyJson</span> <span class="p">(</span><span class="n">GetByKeyRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetAll</span> <span class="p">(</span><span class="n">google.protobuf.Empty</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetAllJson</span> <span class="p">(</span><span class="n">google.protobuf.Empty</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Insert</span> <span class="p">(</span><span class="n">InsertRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">InsertJson</span> <span class="p">(</span><span class="n">InsertRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">partsResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Update</span> <span class="p">(</span><span class="n">UpdateRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">UpdateResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Delete</span> <span class="p">(</span><span class="n">DeleteRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">DeleteResponse</span><span class="p">);</span>
<span class="p">}</span>


<span class="kd">message</span> <span class="nc">GrpcDecimal</span> <span class="p">{</span>
    <span class="kt">int64</span> <span class="na">units</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">sfixed32</span> <span class="na">nanos</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">partsResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">parts</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">partsSetResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">partsSet</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">DataSetResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">dataSet</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">GetForpartclassRequest</span> <span class="p">{</span>
  <span class="kt">sint32</span>  <span class="na">partclasscode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">GetByKeyRequest</span> <span class="p">{</span>
   <span class="kt">string</span> <span class="na">partcode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">InsertRequest</span> <span class="p">{</span>
   <span class="kt">string</span> <span class="na">partcode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">sint32</span> <span class="na">partclasscode</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">partname</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
   <span class="n">google.protobuf.StringValue</span> <span class="na">spec</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
   <span class="n">GrpcDecimal</span> <span class="na">price</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
   <span class="k">oneof</span> <span class="n">oneofManufacturedate</span> <span class="p">{</span> <span class="n">google.protobuf.Timestamp</span> <span class="na">manufacturedate</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="p">}</span>
   <span class="n">google.protobuf.StringValue</span> <span class="na">remark</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">UpdateRequest</span> <span class="p">{</span>
   <span class="kt">string</span> <span class="na">partcode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">sint32</span> <span class="na">partclasscode</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">partname</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
   <span class="n">google.protobuf.StringValue</span> <span class="na">spec</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
   <span class="n">GrpcDecimal</span> <span class="na">price</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
   <span class="k">oneof</span> <span class="n">oneofManufacturedate</span> <span class="p">{</span> <span class="n">google.protobuf.Timestamp</span> <span class="na">manufacturedate</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="p">}</span>
   <span class="n">google.protobuf.StringValue</span> <span class="na">remark</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">UpdateResponse</span> <span class="p">{</span>
   <span class="kt">int32</span> <span class="na">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">DeleteRequest</span> <span class="p">{</span>
   <span class="kt">string</span> <span class="na">partcode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">DeleteResponse</span> <span class="p">{</span>
   <span class="kt">int32</span> <span class="na">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>** sales.proto 파일 작성</p>
<div class="language-proto highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">syntax</span> <span class="o">=</span> <span class="s">"proto3"</span><span class="p">;</span>

<span class="k">option</span> <span class="na">csharp_namespace</span> <span class="o">=</span> <span class="s">"SalesDB.salesProtos"</span><span class="p">;</span>

<span class="kn">package</span> <span class="nn">salesSvc</span><span class="p">;</span>

<span class="k">import</span> <span class="s">"google/protobuf/timestamp.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/duration.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/wrappers.proto"</span><span class="p">;</span>
<span class="k">import</span> <span class="s">"google/protobuf/empty.proto"</span><span class="p">;</span>

<span class="kd">service</span> <span class="n">sales</span> <span class="p">{</span>
   <span class="k">rpc</span> <span class="n">GetForparts</span> <span class="p">(</span><span class="n">GetForpartsRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetForpartsJson</span> <span class="p">(</span><span class="n">GetForpartsRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetByKey</span> <span class="p">(</span><span class="n">GetByKeyRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetByKeyJson</span> <span class="p">(</span><span class="n">GetByKeyRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetAll</span> <span class="p">(</span><span class="n">google.protobuf.Empty</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">GetAllJson</span> <span class="p">(</span><span class="n">google.protobuf.Empty</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesSetResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Insert</span> <span class="p">(</span><span class="n">InsertRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">InsertJson</span> <span class="p">(</span><span class="n">InsertRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">salesResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Update</span> <span class="p">(</span><span class="n">UpdateRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">UpdateResponse</span><span class="p">);</span>
   <span class="k">rpc</span> <span class="n">Delete</span> <span class="p">(</span><span class="n">DeleteRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">DeleteResponse</span><span class="p">);</span>
<span class="p">}</span>


<span class="kd">message</span> <span class="nc">GrpcDecimal</span> <span class="p">{</span>
    <span class="kt">int64</span> <span class="na">units</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">sfixed32</span> <span class="na">nanos</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">salesResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">sales</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">salesSetResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">salesSet</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">DataSetResponse</span> <span class="p">{</span>
   <span class="kt">bytes</span> <span class="na">dataSet</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">jsonString</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">GetForpartsRequest</span> <span class="p">{</span>
  <span class="kt">string</span>  <span class="na">partcode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">GetByKeyRequest</span> <span class="p">{</span>
   <span class="n">google.protobuf.Timestamp</span> <span class="na">saledate</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">InsertRequest</span> <span class="p">{</span>
   <span class="kt">string</span> <span class="na">partcode</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="n">GrpcDecimal</span> <span class="na">quantity</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
   <span class="n">google.protobuf.StringValue</span> <span class="na">remark</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
   <span class="k">oneof</span> <span class="n">oneofCreatedate</span> <span class="p">{</span> <span class="n">google.protobuf.Timestamp</span> <span class="na">createdate</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">message</span> <span class="nc">UpdateRequest</span> <span class="p">{</span>
   <span class="n">google.protobuf.Timestamp</span> <span class="na">saledate</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
   <span class="kt">string</span> <span class="na">partcode</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
   <span class="n">GrpcDecimal</span> <span class="na">quantity</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
   <span class="n">google.protobuf.StringValue</span> <span class="na">remark</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
   <span class="k">oneof</span> <span class="n">oneofCreatedate</span> <span class="p">{</span> <span class="n">google.protobuf.Timestamp</span> <span class="na">createdate</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">UpdateResponse</span> <span class="p">{</span>
   <span class="kt">int32</span> <span class="na">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">DeleteRequest</span> <span class="p">{</span>
   <span class="n">google.protobuf.Timestamp</span> <span class="na">saledate</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">DeleteResponse</span> <span class="p">{</span>
   <span class="kt">int32</span> <span class="na">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>3개의 proto 파일을 작성하였습니다. <br />
이제 .proto 파일을 Python 코드로 변환하겠습니다. <br />
** 주의: .proto 파일이 있는 디렉토리로 이동한 후 명령어를 실행해야 합니다. <br /></p>

<h3 id="3-proto-파일을-python-코드로-변환">3. .proto 파일을 Python 코드로 변환</h3>

<p>.proto 파일을 Python 코드로 변환합니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> grpc_tools.protoc <span class="nt">-I</span><span class="nb">.</span> <span class="nt">--python_out</span><span class="o">=</span><span class="nb">.</span> <span class="nt">--grpc_python_out</span><span class="o">=</span><span class="nb">.</span> partclass.proto parts.proto sales.proto
</code></pre></div></div>

<blockquote>
  <p>-I: .proto 파일이 있는 디렉토리를 지정합니다. <br />
–python_out: Python 코드를 저장할 디렉토리를 지정합니다. <br />
–grpc_python_out: gRPC Python 코드를 저장할 위치를 지정합니다. <br />
proto 파일이 여러 개일 경우, 각 파일을 모두 지정해주어야 합니다. <br />
혹은 디렉토리를 지정하여 모든 proto 파일을 변환할 수 있습니다. <br /></p>
</blockquote>

<p>** 주의: .proto 파일이 있는 디렉토리로 이동한 후 명령어를 실행해야 합니다. <br />
** 주의2: proto Compiler가 설치되어 있어야 합니다.</p>

<h3 id="초보자들을-위한-절차를-설명-">초보자들을 위한 절차를 설명 <br /></h3>

<h4 id="1-python-설치">1. python 설치</h4>

<p>python을 설치합니다.</p>
<blockquote>
  <p><strong><a href="python.org">python.org</a></strong>에서 python을 다운로드 받아 설치합니다.</p>
  <ul>
    <li>필수 설치 항목: Add Python to PATH 체크</li>
  </ul>
</blockquote>

<ul>
  <li>python 설치 확인
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">--version</span>
</code></pre></div>    </div>
  </li>
  <li>python 설치 위치 확인
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>where python
// 기본 위치: C:<span class="se">\U</span>sers<span class="se">\사</span>용자이름<span class="se">\A</span>ppData<span class="se">\L</span>ocal<span class="se">\P</span>rograms<span class="se">\P</span>ython<span class="se">\P</span>ython버전<span class="se">\p</span>ython.exe
</code></pre></div>    </div>
  </li>
</ul>

<p>//microsoft store에서 python 설치한 경우의 위치</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>where python
// 기본 위치: C:<span class="se">\U</span>sers<span class="se">\사</span>용자이름<span class="se">\A</span>ppData<span class="se">\L</span>ocal<span class="se">\M</span>icrosoft<span class="se">\W</span>indowsApps<span class="se">\p</span>ython.exe
</code></pre></div></div>

<h4 id="2-환경-생성">2. 환경 생성</h4>

<p>python 가상환경을 생성합니다.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> venv grpcmodule
</code></pre></div></div>

<p>가상환경을 활성화합니다.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grpcmodule<span class="se">\S</span>cripts<span class="se">\a</span>ctivate
</code></pre></div></div>

<h4 id="3-grpcio-grpcio-tools-설치">3. grpcio, grpcio-tools 설치</h4>

<p>활성화된 가상환경에서 필요한 패키지를 설치합니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>grpcio
</code></pre></div></div>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nx">grpcmodule</span><span class="p">)</span> <span class="p">{</span><span class="nx">사용자</span> <span class="nx">path</span><span class="p">}</span><span class="err">\</span><span class="nx">Scripts</span><span class="o">&gt;</span><span class="nx">pip</span> <span class="nx">install</span> <span class="nx">grpcio</span>
<span class="nx">Collecting</span> <span class="nx">grpcio</span>
  <span class="nx">Downloading</span> <span class="nx">grpcio</span><span class="o">-</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">win_amd64</span><span class="p">.</span><span class="nx">whl</span><span class="p">.</span><span class="nx">metadata</span> <span class="p">(</span><span class="mf">4.2</span> <span class="nx">kB</span><span class="p">)</span>
<span class="nx">Downloading</span> <span class="nx">grpcio</span><span class="o">-</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">win_amd64</span><span class="p">.</span><span class="nx">whl</span> <span class="p">(</span><span class="mf">3.8</span> <span class="nx">MB</span><span class="p">)</span>
   <span class="err">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span> <span class="mf">3.8</span><span class="o">/</span><span class="mf">3.8</span> <span class="nx">MB</span> <span class="mf">9.2</span> <span class="nx">MB</span><span class="o">/</span><span class="nx">s</span> <span class="nx">eta</span> <span class="mi">0</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">00</span>
<span class="nx">Installing</span> <span class="nx">collected</span> <span class="nx">packages</span><span class="p">:</span> <span class="nx">grpcio</span>
<span class="nx">Successfully</span> <span class="nx">installed</span> <span class="nx">grpcio</span><span class="o">-</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>grpcio-tools
</code></pre></div></div>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nx">grpcmodule</span><span class="p">)</span> <span class="p">{</span><span class="nx">사용자</span> <span class="nx">path</span><span class="p">}</span><span class="err">\</span><span class="nx">Scripts</span><span class="o">&gt;</span><span class="nx">pip</span> <span class="nx">install</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span>
<span class="nx">Collecting</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span>
  <span class="nx">Downloading</span> <span class="nx">grpcio_tools</span><span class="o">-</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">win_amd64</span><span class="p">.</span><span class="nx">whl</span><span class="p">.</span><span class="nx">metadata</span> <span class="p">(</span><span class="mf">6.4</span> <span class="nx">kB</span><span class="p">)</span>
<span class="nx">Collecting</span> <span class="nx">protobuf</span><span class="o">&lt;</span><span class="mf">5.0d</span><span class="nx">ev</span><span class="p">,</span><span class="o">&gt;=</span><span class="mf">4.21</span><span class="p">.</span><span class="mi">6</span> <span class="p">(</span><span class="k">from</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span><span class="p">)</span>
  <span class="nx">Downloading</span> <span class="nx">protobuf</span><span class="o">-</span><span class="mf">4.25</span><span class="p">.</span><span class="mi">3</span><span class="o">-</span><span class="nx">cp310</span><span class="o">-</span><span class="nx">abi3</span><span class="o">-</span><span class="nx">win_amd64</span><span class="p">.</span><span class="nx">whl</span><span class="p">.</span><span class="nx">metadata</span> <span class="p">(</span><span class="mi">541</span> <span class="nx">bytes</span><span class="p">)</span>
<span class="nx">Requirement</span> <span class="nx">already</span> <span class="nx">satisfied</span><span class="p">:</span> <span class="nx">grpcio</span><span class="o">&gt;=</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span> <span class="k">in</span> <span class="p">{</span><span class="nx">사용자</span> <span class="nx">path</span><span class="p">}</span><span class="err">\</span><span class="nx">lib</span><span class="err">\</span><span class="nx">site</span><span class="o">-</span><span class="nx">packages</span> <span class="p">(</span><span class="k">from</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span><span class="p">)</span> <span class="p">(</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span><span class="p">)</span>
<span class="nx">Collecting</span> <span class="nx">setuptools</span> <span class="p">(</span><span class="k">from</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span><span class="p">)</span>
  <span class="nx">Downloading</span> <span class="nx">setuptools</span><span class="o">-</span><span class="mf">69.5</span><span class="p">.</span><span class="mi">1</span><span class="o">-</span><span class="nx">py3</span><span class="o">-</span><span class="nx">none</span><span class="o">-</span><span class="nx">any</span><span class="p">.</span><span class="nx">whl</span><span class="p">.</span><span class="nx">metadata</span> <span class="p">(</span><span class="mf">6.2</span> <span class="nx">kB</span><span class="p">)</span>
<span class="nx">Downloading</span> <span class="nx">grpcio_tools</span><span class="o">-</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">cp312</span><span class="o">-</span><span class="nx">win_amd64</span><span class="p">.</span><span class="nx">whl</span> <span class="p">(</span><span class="mf">1.1</span> <span class="nx">MB</span><span class="p">)</span>
   <span class="err">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span> <span class="mf">1.1</span><span class="o">/</span><span class="mf">1.1</span> <span class="nx">MB</span> <span class="mf">6.1</span> <span class="nx">MB</span><span class="o">/</span><span class="nx">s</span> <span class="nx">eta</span> <span class="mi">0</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">00</span>
<span class="nx">Downloading</span> <span class="nx">protobuf</span><span class="o">-</span><span class="mf">4.25</span><span class="p">.</span><span class="mi">3</span><span class="o">-</span><span class="nx">cp310</span><span class="o">-</span><span class="nx">abi3</span><span class="o">-</span><span class="nx">win_amd64</span><span class="p">.</span><span class="nx">whl</span> <span class="p">(</span><span class="mi">413</span> <span class="nx">kB</span><span class="p">)</span>
   <span class="err">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span> <span class="mf">413.4</span><span class="o">/</span><span class="mf">413.4</span> <span class="nx">kB</span> <span class="mf">8.6</span> <span class="nx">MB</span><span class="o">/</span><span class="nx">s</span> <span class="nx">eta</span> <span class="mi">0</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">00</span>
<span class="nx">Downloading</span> <span class="nx">setuptools</span><span class="o">-</span><span class="mf">69.5</span><span class="p">.</span><span class="mi">1</span><span class="o">-</span><span class="nx">py3</span><span class="o">-</span><span class="nx">none</span><span class="o">-</span><span class="nx">any</span><span class="p">.</span><span class="nx">whl</span> <span class="p">(</span><span class="mi">894</span> <span class="nx">kB</span><span class="p">)</span>
   <span class="err">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span> <span class="mf">894.6</span><span class="o">/</span><span class="mf">894.6</span> <span class="nx">kB</span> <span class="mf">11.3</span> <span class="nx">MB</span><span class="o">/</span><span class="nx">s</span> <span class="nx">eta</span> <span class="mi">0</span><span class="p">:</span><span class="mi">00</span><span class="p">:</span><span class="mi">00</span>
<span class="nx">Installing</span> <span class="nx">collected</span> <span class="nx">packages</span><span class="p">:</span> <span class="nx">setuptools</span><span class="p">,</span> <span class="nx">protobuf</span><span class="p">,</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span>
<span class="nx">Successfully</span> <span class="nx">installed</span> <span class="nx">grpcio</span><span class="o">-</span><span class="nx">tools</span><span class="o">-</span><span class="mf">1.62</span><span class="p">.</span><span class="mi">2</span> <span class="nx">protobuf</span><span class="o">-</span><span class="mf">4.25</span><span class="p">.</span><span class="mi">3</span> <span class="nx">setuptools</span><span class="o">-</span><span class="mf">69.5</span><span class="p">.</span><span class="mi">1</span>
</code></pre></div></div>

<h4 id="4-proto-파일-컴파일">4. proto 파일 컴파일</h4>

<p>.proto 파일을 Python 코드로 변환합니다.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 먼저 .proto 파일이 있는 디렉토리로 이동합니다.
<span class="nb">cd</span> <span class="o">{</span>proto 파일이 있는 디렉토리<span class="o">}</span>

python <span class="nt">-m</span> grpc_tools.protoc <span class="nt">-I</span><span class="nb">.</span> <span class="nt">--python_out</span><span class="o">=</span><span class="nb">.</span> <span class="nt">--grpc_python_out</span><span class="o">=</span><span class="nb">.</span> partclass.proto parts.proto sales.proto
</code></pre></div></div>

<h4 id="5-compile된-파일-확인">5. Compile된 파일 확인</h4>

<p>.proto 파일을 Python 코드로 변환하면, 해당 디렉토리에 _pb2.py 파일과 _pb2_grpc.py 파일이 생성됩니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">dir</span>

// 결과
partclass_pb2.py
partclass_pb2_grpc.py
parts_pb2.py
parts_pb2_grpc.py
sales_pb2.py
sales_pb2_grpc.py
</code></pre></div></div>

<h3 id="4-grpc-client-작성">4. gRPC Client 작성</h3>

<p>gRPC 클라이언트를 작성합니다.
예제로는 partclass.proto 파일을 사용하여 작성하겠습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">grpc</span>
<span class="kn">import</span> <span class="nn">grpc</span>
<span class="kn">from</span> <span class="nn">google.protobuf.empty_pb2</span> <span class="kn">import</span> <span class="n">Empty</span>  <span class="c1"># 확인: 정상적으로 Empty 임포트
</span>
<span class="c1"># Generated classes import
</span><span class="kn">import</span> <span class="nn">partclass_pb2</span>
<span class="kn">import</span> <span class="nn">partclass_pb2_grpc</span>

<span class="c1"># Server address configuration
</span><span class="n">http_server_address</span> <span class="o">=</span> <span class="s">'loclahost:5100'</span>


<span class="k">def</span> <span class="nf">run</span><span class="p">():</span>
    <span class="n">channel</span> <span class="o">=</span> <span class="n">grpc</span><span class="p">.</span><span class="n">insecure_channel</span><span class="p">(</span><span class="n">http_server_address</span><span class="p">)</span>
    <span class="n">stub</span> <span class="o">=</span> <span class="n">partclass_pb2_grpc</span><span class="p">.</span><span class="n">partclassStub</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span>
    <span class="n">response</span> <span class="o">=</span> <span class="n">stub</span><span class="p">.</span><span class="n">GetAllJson</span><span class="p">(</span><span class="n">Empty</span><span class="p">())</span>  <span class="c1"># 수정: Empty() 형태로 객체 생성
</span>    <span class="k">print</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
    
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">run</span><span class="p">()</span>
</code></pre></div></div>

<p>결과를 확인해보겠습니다. <br />
DataBase에 저장된 데이터를 가져오는 부분이므로, 데이터베이스에 데이터가 없다면 빈 리스트가 출력됩니다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>grpcmodule<span class="o">)</span> python <span class="nt">-m</span> app
jsonString: <span class="s2">"[{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:3,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">class3</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:11,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"\\</span><span class="s2">uCE5C</span><span class="se">\\</span><span class="s2">uAD6C</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:12,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Apple</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:14,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Samsung</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:15,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">TV</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:17,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Table</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:18,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Table</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:19,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Table</span><span class="se">\"</span><span class="s2">},{</span><span class="se">\"</span><span class="s2">partclasscode</span><span class="se">\"</span><span class="s2">:1,</span><span class="se">\"</span><span class="s2">partclassname</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Table</span><span class="se">\"</span><span class="s2">}]"</span>
</code></pre></div></div>

<h2 id="마치며">마치며</h2>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code>
```</p>]]></content><author><name>Yoon Sik Lim</name></author><category term="gRPC" /><category term="python" /><category term="gRPC" /><category term="HTTP2" /><category term="google" /><category term="protoc" /><category term="protobuf" /><category term="proto" /><summary type="html"><![CDATA[python gRPC client example]]></summary></entry><entry><title type="html">[WinUI3] .NET 8 - WinUI Project Install Error &amp;amp; Fixed</title><link href="https://amsomad.github.io/c%23/winUI3_error/" rel="alternate" type="text/html" title="[WinUI3] .NET 8 - WinUI Project Install Error &amp;amp; Fixed" /><published>2024-02-18T00:00:00+00:00</published><updated>2024-02-18T00:00:00+00:00</updated><id>https://amsomad.github.io/c%23/winUI3_error</id><content type="html" xml:base="https://amsomad.github.io/c%23/winUI3_error/"><![CDATA[<!-- link -->

<h2 id="소개">소개</h2>
<p>해당 게시물에서는 WinUI 3 프로젝트를 만들 때 발생하는 오류와 해결방법을 정리하였습니다.
사용되는 SDK는 .NET 8.0 입니다.</p>

<h2 id="작업-환경">작업 환경</h2>
<ul>
  <li>Windows 10</li>
  <li>Visual Studio 2022</li>
  <li>.NET 8.0</li>
  <li>WinUI 3.0</li>
  <li>C#</li>
  <li>Tamplate Studio WinUI 3.0</li>
  <li>WinUI 3.0 Desktop App</li>
</ul>

<h2 id="작업-순서">작업 순서</h2>

<h3 id="1-winui-30-설치">1. WinUI 3.0 설치</h3>
<p>Visual Studio 2022에서 WinUI 3.0 설치를 진행합니다. <br /></p>

<h3 id="2-winui-30-프로젝트-생성">2. WinUI 3.0 프로젝트 생성</h3>
<p>Template Studio WinUI 3.0 을 사용하여 프로젝트를 생성합니다. <br /></p>

<h3 id="3-프로젝트-빌드">3. 프로젝트 빌드</h3>
<p>프로젝트를 빌드합니다. <br />
에러가 발생하면 아래의 Error List 를 참고하여 해결합니다. <br />
2024년 2월 기준으로 발생하는 오류를 정리하였습니다. <br />
프로젝트 생성시 .NET 7.0 으로 설정이 되어있습니다. <br />
해당 프로젝트를 .NET 8.0 으로 변경하려면 몇가지 설정을 변경해야합니다. <br /></p>

<h3 id="4-프로젝트-net-80-으로-변경">4. 프로젝트 .NET 8.0 으로 변경</h3>
<p>.csproj 파일을 열어서 아래와 같이 변경합니다. <br />
최근 RID가 변경되었습니다. <br />
win10-x64 -&gt; win-x64 <br />
win10-x86 -&gt; win-x86 <br />
win10-arm64 -&gt; win-arm64 <br />
또한 WPF 와 WinUI3 를 사용하기 위해서는 아래와 같이 설정해야합니다. <br /></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nt">&lt;RuntimeIdentifiers&gt;</span>win-x86;win-x64;win-arm64<span class="nt">&lt;/RuntimeIdentifiers&gt;</span>
    <span class="nt">&lt;UseWPF&gt;</span>true<span class="nt">&lt;/UseWPF&gt;</span>
    <span class="nt">&lt;UseWinUI3&gt;</span>true<span class="nt">&lt;/UseWinUI3&gt;</span>
    <span class="nt">&lt;WindowsAppSDKSelfContained&gt;</span>true<span class="nt">&lt;/WindowsAppSDKSelfContained&gt;</span>
</code></pre></div></div>

<h2 id="error-list">Error List</h2>
<!-- Error List -->

<h3 id="1-netsdk1083">1. NETSDK1083</h3>

<p>.NET 6.0 이상에서 발생하는 오류인데 <br />
프로젝트 파일에 RuntimeIdentifier 가 잘못되어 발생하는 오류입니다. <br />
.NET 6.0 이상에서는 RuntimeIdentifier 가 변경되었습니다. <br />
win10-x64 -&gt; win-x64 <br />
win10-x86 -&gt; win-x86 <br />
win10-arm64 -&gt; win-arm64 로 변경되었습니다.<br /></p>

<!-- 링크 -->
<p><a href="https://learn.microsoft.com/ko-kr/dotnet/core/rid-catalog#known-rids">RuntimeIdentifier</a> <br /></p>

<p>Error Message <br /></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NETSDK1083	The specified RuntimeIdentifier <span class="s1">'win10-x64'</span> is not recognized. See https://aka.ms/netsdk1083 <span class="k">for </span>more information.
</code></pre></div></div>

<p>해결방법</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. 프로젝트 파일 열기</span>
<span class="c"># 2. &lt;RuntimeIdentifier&gt;win10-x64&lt;/RuntimeIdentifier&gt; 삭제</span>
<span class="c"># 3. 프로젝트 저장</span>
<span class="c"># 4. 프로젝트 다시 빌드</span>
</code></pre></div></div>

<p>또는</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;UseRidGraph&gt;true&lt;/UseRidGraph&gt;
&lt;RuntimeIdentifiers&gt;win10-x86<span class="p">;</span>win10-x64<span class="p">;</span>win10-arm64&lt;/RuntimeIdentifiers&gt;
</code></pre></div></div>

<h1 id="dep0700-앱을-등록하지-못했습니다-0x80073cfd-windows에서-파일-시스템-유형-exfat의-appx-경로에-배포할-수-없습니다">DEP0700: 앱을 등록하지 못했습니다. [0x80073CFD] Windows에서 파일 시스템 유형 exFAT의 AppX 경로에 배포할 수 없습니다.</h1>

<p>Error Message <br /></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DEP0700: 앱을 등록하지 못했습니다. <span class="o">[</span>0x80073CFD] Windows에서 파일 시스템 유형 exFAT의 AppX 경로에 배포할 수 없습니다.
</code></pre></div></div>

<h2 id="마무리">마무리</h2>

<p>코드 전체는 <a href="https://github.com/AmSoMad/winformOnnxRuntime_ObjectDetection">Github</a> 에서 확인하실 수 있습니다. <br /></p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="C#" /><category term="C#" /><category term="ASP.NET Core" /><category term="Windows" /><category term="WinUI3" /><summary type="html"><![CDATA[WinUI 3 install build Error list]]></summary></entry><entry><title type="html">[windows11] 추가 옵션 표시를 기존으로</title><link href="https://amsomad.github.io/%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0/windows11_menu/" rel="alternate" type="text/html" title="[windows11] 추가 옵션 표시를 기존으로" /><published>2023-12-28T00:00:00+00:00</published><updated>2023-12-28T00:00:00+00:00</updated><id>https://amsomad.github.io/%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0/windows11_menu</id><content type="html" xml:base="https://amsomad.github.io/%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0/windows11_menu/"><![CDATA[<h1 id="-추가-옵션-표시">🐐 추가 옵션 표시</h1>
<p>과거 windows10으로 변경 하려한다.</p>

<p>윈도우 11 -&gt; 윈도우10 형태로변경</p>
<pre><code class="language-cmd">reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve
taskkill /f /im explorer.exe
explorer.exe
</code></pre>

<p>윈도우10 -&gt; 윈도우11로 원복하려할때</p>
<pre><code class="language-cmd">reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f
taskkill /f /im explorer.exe
explorer.exe
</code></pre>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="유틸리티" /><category term="windows11" /><category term="마우스" /><summary type="html"><![CDATA[개열받는다 작업할때마다]]></summary></entry><entry><title type="html">[Flask] Python Flask 웹 서버를 Windows 서비스로 등록</title><link href="https://amsomad.github.io/%EC%84%9C%EB%B2%84/python_windows_service/" rel="alternate" type="text/html" title="[Flask] Python Flask 웹 서버를 Windows 서비스로 등록" /><published>2023-11-14T00:00:00+00:00</published><updated>2023-11-14T00:00:00+00:00</updated><id>https://amsomad.github.io/%EC%84%9C%EB%B2%84/python_windows_service</id><content type="html" xml:base="https://amsomad.github.io/%EC%84%9C%EB%B2%84/python_windows_service/"><![CDATA[<h1 id="개요">개요</h1>

<p>Flask 웹 서버를 Windows 서비스로 등록하는 방법을 알아보겠습니다. <br />
Windows 서비스로 등록하면 Windows가 시작될 때 자동으로 실행되며, <br />
Windows 서비스 관리자에서 시작/중지/재시작 등의 작업을 수행할 수 있습니다. <br /></p>

<hr />

<h2 id="목차">목차</h2>

<ul>
  <li><a href="#개요">개요</a>
    <ul>
      <li><a href="#목차">목차</a></li>
      <li><a href="#개발환경">개발환경</a></li>
      <li><a href="#python-필수-패키지-설치">python 필수 패키지 설치</a></li>
      <li><a href="#runserverpy">runserver.py</a></li>
      <li><a href="#windows-서비스-등록">Windows 서비스 등록</a></li>
      <li><a href="#windows-서비스-관리">Windows 서비스 관리</a></li>
      <li><a href="#windows-서비스-로그-확인">Windows 서비스 로그 확인</a></li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="개발환경">개발환경</h2>

<ul>
  <li>Windows Server 2012 R2 Standard(64bit)</li>
  <li><strong><a href="https://www.python.org/downloads/release/python-3913/">Python 3.9.13</a></strong></li>
  <li>혹은 <strong><a href="https://www.python.org/downloads/windows/">https://www.python.org/downloads/windows/</a></strong></li>
</ul>

<h2 id="python-필수-패키지-설치">python 필수 패키지 설치</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install</span> <span class="nt">--upgrade</span> pip
pip <span class="nb">install </span>flask
pip <span class="nb">install </span>waitress
pip <span class="nb">install </span>pywin32
</code></pre></div></div>

<h2 id="runserverpy">runserver.py</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kn">from</span> <span class="nn">os</span> <span class="kn">import</span> <span class="n">environ</span>
<span class="kn">from</span> <span class="nn">waitress</span> <span class="kn">import</span> <span class="n">serve</span>
<span class="kn">from</span> <span class="nn">win32</span> <span class="kn">import</span> <span class="n">servicemanager</span>
<span class="kn">import</span> <span class="nn">win32serviceutil</span><span class="p">,</span><span class="n">win32service</span><span class="p">,</span> <span class="n">win32event</span><span class="p">,</span> <span class="n">win32evtlogutil</span>
<span class="kn">from</span> <span class="nn">FlaskService</span> <span class="kn">import</span> <span class="n">app</span>

<span class="k">class</span> <span class="nc">FlaskAppService</span><span class="p">(</span><span class="n">win32serviceutil</span><span class="p">.</span><span class="n">ServiceFramework</span><span class="p">):</span>
    <span class="n">_svc_name_</span> <span class="o">=</span> <span class="s">'FlaskAppService'</span>
    <span class="n">_svc_display_name_</span> <span class="o">=</span> <span class="s">'Flask App Service'</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
        <span class="n">win32serviceutil</span><span class="p">.</span><span class="n">ServiceFramework</span><span class="p">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">hWaitStop</span> <span class="o">=</span> <span class="n">win32event</span><span class="p">.</span><span class="n">CreateEvent</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">SvcStop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">ReportServiceStatus</span><span class="p">(</span><span class="n">win32service</span><span class="p">.</span><span class="n">SERVICE_STOP_PENDING</span><span class="p">)</span>
        <span class="n">win32event</span><span class="p">.</span><span class="n">SetEvent</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hWaitStop</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">SvcDoRun</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">servicemanager</span><span class="p">.</span><span class="n">LogMsg</span><span class="p">(</span><span class="n">servicemanager</span><span class="p">.</span><span class="n">EVENTLOG_INFORMATION_TYPE</span><span class="p">,</span>
                              <span class="n">servicemanager</span><span class="p">.</span><span class="n">PYS_SERVICE_STARTED</span><span class="p">,</span>
                              <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">_svc_name_</span><span class="p">,</span> <span class="s">''</span><span class="p">))</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">main</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">HOST</span> <span class="o">=</span> <span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SERVER_HOST'</span><span class="p">,</span> <span class="s">'0.0.0.0'</span><span class="p">)</span>
        <span class="n">PORT</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'SERVER_PORT'</span><span class="p">,</span> <span class="s">'5555'</span><span class="p">))</span>
        <span class="n">serve</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="n">HOST</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="n">PORT</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">win32serviceutil</span><span class="p">.</span><span class="n">HandleCommandLine</span><span class="p">(</span><span class="n">FlaskAppService</span><span class="p">)</span>

</code></pre></div></div>

<h2 id="windows-서비스-등록">Windows 서비스 등록</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 서비스 설치</span>
python runserver.py <span class="nb">install</span>
<span class="c"># Installing service FlaskAppService</span>
<span class="c"># Service installed</span>

<span class="c"># 서비스 시작</span>
python runserver.py start
<span class="c"># Starting service FlaskAppService</span>

<span class="c"># 서비스 상태 확인</span>
python runserver.py status
<span class="c"># Service is running</span>

<span class="c"># 서비스 중지 및 제거</span>
python runserver.py stop
<span class="c"># Stopping service FlaskAppService</span>
python runserver.py remove
<span class="c"># Removing service FlaskAppService</span>


<span class="c"># 서비스 종료하는 중에서 에러가 발생할 경우</span>
<span class="c"># ex : 다른 프로세서가 파일을 사용 중이기 때문에 프로세서가 엑세스 할 수 없습니다.</span>
<span class="c"># 서비스를 중지하고 다시 시작하면 해결됩니다.</span>

<span class="c"># 서비스 강제 종료</span>
taskkill /f /pid <span class="o">[</span>PID]

taskkill /f /fi <span class="s2">"SERVICES eq {서비스명}"</span>
<span class="c"># ex : taskkill /f /fi "SERVICES eq FlaskAppService"</span>
</code></pre></div></div>

<h2 id="windows-서비스-관리">Windows 서비스 관리</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 서비스 목록 확인</span>
sc query

<span class="c"># 서비스 상태 확인</span>
sc query FlaskAppService

<span class="c"># 서비스 시작</span>
sc start FlaskAppService

<span class="c"># 서비스 중지</span>
sc stop FlaskAppService

<span class="c"># 서비스 재시작</span>
sc restart FlaskAppService

<span class="c"># 서비스 삭제</span>
sc delete FlaskAppService
</code></pre></div></div>

<h2 id="windows-서비스-로그-확인">Windows 서비스 로그 확인</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 서비스 로그 확인</span>
eventvwr.msc
<span class="c"># 이벤트 뷰어 &gt; 응용 프로그램 및 서비스 로그 &gt; FlaskAppService</span>
</code></pre></div></div>

<p><br /></p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="서버" /><category term="서버" /><category term="Windows" /><category term="Flask" /><category term="Python" /><category term="Windows Service" /><summary type="html"><![CDATA[Flask 웹 서버를 Windows 서비스로 등록]]></summary></entry><entry><title type="html">[Ubuntu] 우분투 설치 후 Docker, Kubernetes, SQL Server 설치</title><link href="https://amsomad.github.io/%EC%84%9C%EB%B2%84/ubuntu_install_after_command/" rel="alternate" type="text/html" title="[Ubuntu] 우분투 설치 후 Docker, Kubernetes, SQL Server 설치" /><published>2023-10-31T00:00:00+00:00</published><updated>2023-10-31T00:00:00+00:00</updated><id>https://amsomad.github.io/%EC%84%9C%EB%B2%84/ubuntu_install_after_command</id><content type="html" xml:base="https://amsomad.github.io/%EC%84%9C%EB%B2%84/ubuntu_install_after_command/"><![CDATA[<h1 id="ubuntu-22043-서버-설정-가이드">Ubuntu 22.04.3 서버 설정 가이드</h1>

<p>Ubuntu 22.04.3 서버를 설치하고 Docker, Kubernetes, SQL Server를 설치하는 가이드입니다.<br /></p>

<p>사전에 Ubuntu 22.04.3 서버를 설치해야 합니다.</p>
<ul>
  <li>Rufus를 사용하여 USB 부팅디스크를 만들고, BIOS에서 부팅디스크를 선택하여 설치합니다.</li>
  <li><a href="https://ubuntu.com/download/server#download">https://ubuntu.com/download/server#download</a> 에서 Ubuntu 22.04.3 LTS를 다운로드 받습니다.</li>
</ul>

<hr />

<h2 id="목차">목차</h2>

<ul>
  <li><a href="#ubuntu-22043-서버-설정-가이드">Ubuntu 22.04.3 서버 설정 가이드</a>
    <ul>
      <li><a href="#목차">목차</a></li>
      <li><a href="#서버-초기-설정">서버 초기 설정</a></li>
      <li><a href="#docker-설치">Docker 설치</a></li>
      <li><a href="#kubernetes-설치">Kubernetes 설치</a></li>
      <li><a href="#sql-server-2019-컨테이너-설치">SQL Server 2019 컨테이너 설치</a></li>
      <li><a href="#ssh-설치-및-설정-원격-접속">SSH 설치 및 설정 (원격 접속)</a></li>
      <li><a href="#방화벽-설정-ufw-사용">방화벽 설정 (UFW 사용)</a></li>
      <li><a href="#기타-사항">기타 사항</a>
        <ul>
          <li><a href="#보안-강화">보안 강화</a>
            <ul>
              <li><a href="#fail2ban-설치-및-설정">Fail2Ban 설치 및 설정</a></li>
            </ul>
          </li>
          <li><a href="#로깅과-모니터링">로깅과 모니터링</a>
            <ul>
              <li><a href="#prometheus와-grafana-설치">Prometheus와 Grafana 설치</a></li>
            </ul>
          </li>
          <li><a href="#사용자-관리">사용자 관리</a>
            <ul>
              <li><a href="#계정-생성-및-권한-설정">계정 생성 및 권한 설정</a></li>
            </ul>
          </li>
          <li><a href="#백업과-복구">백업과 복구</a>
            <ul>
              <li><a href="#rsync를-사용한-데이터-백업">rsync를 사용한 데이터 백업</a></li>
            </ul>
          </li>
          <li><a href="#웹-서버-설정">웹 서버 설정</a>
            <ul>
              <li><a href="#nginx-설치-및-설정">Nginx 설치 및 설정</a></li>
            </ul>
          </li>
          <li><a href="#데이터베이스-연결">데이터베이스 연결</a>
            <ul>
              <li><a href="#postgresql-설치-및-설정">PostgreSQL 설치 및 설정</a></li>
            </ul>
          </li>
          <li><a href="#devops-파이프라인">DevOps 파이프라인</a>
            <ul>
              <li><a href="#jenkins-설치">Jenkins 설치</a></li>
            </ul>
          </li>
          <li><a href="#클러스터링과-로드-밸런싱">클러스터링과 로드 밸런싱</a>
            <ul>
              <li><a href="#haproxy-설치">HAProxy 설치</a></li>
            </ul>
          </li>
          <li><a href="#ssltls-설정">SSL/TLS 설정</a>
            <ul>
              <li><a href="#lets-encrypt-사용하여-ssl-인증서-발급">Let’s Encrypt 사용하여 SSL 인증서 발급</a></li>
            </ul>
          </li>
          <li><a href="#시스템-최적화">시스템 최적화</a>
            <ul>
              <li><a href="#swappiness-설정-변경">Swappiness 설정 변경</a></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="서버-초기-설정">서버 초기 설정</h2>

<ol>
  <li><strong>시스템 업데이트</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt update <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>apt upgrade <span class="nt">-y</span>
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="docker-설치">Docker 설치</h2>

<ol>
  <li><strong>Docker 저장소 설정 및 필요한 패키지 설치</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt <span class="nb">install </span>apt-transport-https ca-certificates curl software-properties-common
 curl <span class="nt">-fsSL</span> https://download.docker.com/linux/ubuntu/gpg | <span class="nb">sudo </span>apt-key add -
 <span class="nb">sudo </span>add-apt-repository <span class="s2">"deb [arch=amd64] https://download.docker.com/linux/ubuntu </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="s2"> stable"</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Docker 설치</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt update
 <span class="nb">sudo </span>apt <span class="nb">install </span>docker-ce
</code></pre></div>    </div>
  </li>
  <li><strong>Docker 서비스 상태 확인</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>systemctl status docker
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="kubernetes-설치">Kubernetes 설치</h2>

<ol>
  <li><strong>Kubernetes 저장소 설정</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt update
 <span class="nb">sudo </span>apt <span class="nb">install </span>apt-transport-https
 curl <span class="nt">-s</span> https://packages.cloud.google.com/apt/doc/apt-key.gpg | <span class="nb">sudo </span>apt-key add -
 <span class="nb">echo</span> <span class="s2">"deb https://apt.kubernetes.io/ kubernetes-xenial main"</span> | <span class="nb">sudo tee</span> <span class="nt">-a</span> /etc/apt/sources.list.d/kubernetes.list
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 패키지 설치</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt update
 <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> kubelet kubeadm kubectl
</code></pre></div>    </div>
  </li>
  <li><strong>Kubelet 서비스 활성화</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>systemctl <span class="nb">enable </span>kubelet
 <span class="nb">sudo </span>systemctl start kubelet
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 버전 확인</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kubectl version
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 초기화</strong>
 kubeadm init 명령어는 일반적으로 쿠버네티스 마스터 노드를 초기화하는 데 사용됩니다. <br />
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>kubeadm init
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 설정</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$HOME</span>/.kube
 <span class="nb">sudo cp</span> <span class="nt">-i</span> /etc/kubernetes/admin.conf <span class="nv">$HOME</span>/.kube/config
 <span class="nb">sudo chown</span> <span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span>:<span class="si">$(</span><span class="nb">id</span> <span class="nt">-g</span><span class="si">)</span> <span class="nv">$HOME</span>/.kube/config
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 상태 확인</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kubectl get nodes
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 네트워크 설치</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kubectl apply <span class="nt">-f</span> https://docs.projectcalico.org/manifests/calico.yaml
</code></pre></div>    </div>
  </li>
  <li><strong>Kubernetes 네트워크 상태 확인</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kubectl get pods <span class="nt">-n</span> kube-system
 or 
 kubectl get nodes <span class="nt">-o</span> wide
</code></pre></div>    </div>
  </li>
  <li><strong>특정 버전의 Kubernetes 설치</strong>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install -y kubelet=1.21.0-00 kubeadm=1.21.0-00 kubectl=1.21.0-00
</code></pre></div>    </div>
  </li>
  <li><strong>특정 Pod 네트워크 CIDR 설정</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>kubeadm init <span class="nt">--pod-network-cidr</span><span class="o">=</span>10.244.0.0/16
</code></pre></div>    </div>
  </li>
  <li><strong>Role-Based Access Control(RBAC) 설정</strong>
Role-Based Access Control (RBAC)은 쿠버네티스에서 역할을 기반으로 <br />
리소스에 대한 접근을 제어하는 인증 메커니즘입니다. <br />
RBAC 규칙을 구성하여 사용자나 시스템 컴포넌트가 수행할 수 있는 작업을 제한할 수 있습니다. <br /></li>
</ol>

<p>–apiserver-advertise-address 플래그는 API 서버가 광고할 IP 주소를 지정할 수 있게 합니다. <br />
그러나 이 명령어 자체는 RBAC 규칙을 설정하지 않습니다. RBAC 정책은 주로 YAML 매니페스트를 통해 정의됩니다. <br /></p>

<p>read-pods-role.yaml 파일을 생성하고 다음 내용을 추가합니다. <br /></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">rbac.authorization.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Role</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">default</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">read-pods</span>
<span class="na">rules</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">apiGroups</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">"</span><span class="pi">]</span>
  <span class="na">resources</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">pods"</span><span class="pi">]</span>
  <span class="na">verbs</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">get"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">list"</span><span class="pi">]</span>
</code></pre></div></div>

<p>read-pods-role-binding.yaml 파일을 생성하고 다음 내용을 추가합니다. <br /></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">rbac.authorization.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">RoleBinding</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">read-pods-role-binding</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">default</span>
<span class="na">subjects</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">kind</span><span class="pi">:</span> <span class="s">User</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">admin</span>
  <span class="na">apiGroup</span><span class="pi">:</span> <span class="s">rbac.authorization.k8s.io</span>
<span class="na">roleRef</span><span class="pi">:</span>
  <span class="na">kind</span><span class="pi">:</span> <span class="s">Role</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">read-pods</span>
  <span class="na">apiGroup</span><span class="pi">:</span> <span class="s">rbac.authorization.k8s.io</span>
</code></pre></div></div>

<p>kubectl apply 명령어를 사용하여 RBAC 정책을 적용합니다. <br /></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl apply <span class="nt">-f</span> read-pods-role.yaml
kubectl apply <span class="nt">-f</span> read-pods-role-binding.yaml
</code></pre></div></div>

<p>read-pods 라는 이름의 Role을 생성하고, <br />
admin 이라는 이름의 User에게 read-pods Role을 바인딩합니다. <br />
이제 admin User는 default 네임스페이스의 모든 Pod를 읽을 수 있습니다. <br /></p>

<ol>
  <li><strong>Kubernetes 재설정</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nb">sudo </span>kubeadm reset
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="sql-server-2019-컨테이너-설치">SQL Server 2019 컨테이너 설치</h2>

<ol>
  <li><strong>볼륨 생성 (옵션)</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> docker volume create sql_server_data
</code></pre></div>    </div>
  </li>
  <li><strong>SQL Server 컨테이너 실행</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>docker run <span class="nt">-e</span> <span class="s1">'ACCEPT_EULA=Y'</span> <span class="nt">-e</span> <span class="s1">'SA_PASSWORD=&lt;YourPassword&gt;'</span> 
 <span class="nt">-p</span> 1433:1433 <span class="nt">--name</span> sql_server_container 
 <span class="nt">-v</span> sql_server_data:/var/opt/mssql 
 <span class="nt">-d</span> mcr.microsoft.com/mssql/server:2019-latest
</code></pre></div>    </div>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># 볼륨 생성 확인</span>
 docker volume inspect sql_server_data
</code></pre></div>    </div>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">[</span>
   <span class="o">{</span>
     <span class="s2">"CreatedAt"</span>: <span class="s2">"2023-10-31T15:24:08+09:00"</span>,
     <span class="s2">"Driver"</span>: <span class="s2">"local"</span>,
     <span class="s2">"Labels"</span>: null,
     <span class="s2">"Mountpoint"</span>: <span class="s2">"/var/lib/docker/volumes/sql_server_data/_data"</span>,
     <span class="s2">"Name"</span>: <span class="s2">"sql_server_data"</span>,
     <span class="s2">"Options"</span>: null,
     <span class="s2">"Scope"</span>: <span class="s2">"local"</span>
   <span class="o">}</span>
 <span class="o">]</span>

 <span class="nb">sudo ls</span> /var/lib/docker/volumes/sql_server_data/_data
</code></pre></div>    </div>
    <blockquote>
      <p>data: 실제 데이터베이스 파일이 저장되는 위치입니다. <br />
log: SQL Server의 로그 파일이 저장됩니다. <br />
secrets: 비밀 정보나 인증 관련 파일이 저장될 수 있습니다. <br /></p>
    </blockquote>
  </li>
  <li><strong>SQL Server 컨테이너 상태 확인</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>docker ps <span class="nt">-a</span>
</code></pre></div>    </div>
  </li>
  <li><strong>SQL Server 컨테이너 접속</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c"># SQL Server 컨테이너 접속</span>
 <span class="nb">sudo </span>docker <span class="nb">exec</span> <span class="nt">-it</span> sql_server_container /opt/mssql-tools/bin/sqlcmd

 <span class="c"># 컨테이너 내부 쉘 접속</span>
 <span class="nb">sudo </span>docker <span class="nb">exec</span> <span class="nt">-it</span> sql_server_container /bin/bash

 <span class="c"># 데이터 파일 위치 확인</span>
 <span class="nb">cd</span> /var/opt/mssql/data
 <span class="nb">ls</span>

 <span class="c"># 에러 로그 확인</span>
 <span class="nb">cat </span>errorlog

 <span class="c"># 컨테이너에서 나가기</span>
 <span class="nb">exit</span>

 <span class="c"># 컨테이너 정지 및 삭제</span>
 <span class="nb">sudo </span>docker stop sql_server_container
 <span class="nb">sudo </span>docker <span class="nb">rm </span>sql_server_container

 <span class="c"># 볼륨 삭제</span>
 <span class="nb">sudo </span>docker volume <span class="nb">rm </span>sql_server_data

 <span class="c"># 컨테이너 목록 확인</span>
 <span class="nb">sudo </span>docker ps <span class="nt">-a</span>

 <span class="c"># 이미지 목록 확인</span>
 <span class="nb">sudo </span>docker images

 <span class="c"># 미사용 리소스 삭제</span>
 <span class="nb">sudo </span>docker system prune <span class="nt">-a</span>
</code></pre></div>    </div>

    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">-- SQL Server 버전 확인</span>
 <span class="k">SELECT</span> <span class="o">@@</span><span class="k">VERSION</span>
 <span class="k">GO</span>

 <span class="c1">-- 데이터베이스 목록 확인</span>
 <span class="k">SELECT</span> <span class="n">name</span> <span class="k">FROM</span> <span class="n">sys</span><span class="p">.</span><span class="n">databases</span>
 <span class="k">GO</span>

 <span class="c1">-- 마스터 데이터베이스 사용</span>
 <span class="n">USE</span> <span class="n">master</span>
 <span class="k">GO</span>

 <span class="c1">-- 새 데이터베이스 생성</span>
 <span class="k">CREATE</span> <span class="k">DATABASE</span> <span class="n">TestDB</span>
 <span class="k">GO</span>

 <span class="c1">-- 데이터베이스 목록 다시 확인</span>
 <span class="k">SELECT</span> <span class="n">name</span> <span class="k">FROM</span> <span class="n">sys</span><span class="p">.</span><span class="n">databases</span>
 <span class="k">GO</span>

 <span class="c1">-- 새 데이터베이스 사용</span>
 <span class="n">USE</span> <span class="n">TestDB</span>
 <span class="k">GO</span>

 <span class="c1">-- 테이블 생성</span>
 <span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">TestTable</span> <span class="p">(</span><span class="n">id</span> <span class="nb">INT</span><span class="p">,</span> <span class="n">name</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">))</span>
 <span class="k">GO</span>

 <span class="c1">-- 데이터 삽입</span>
 <span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">TestTable</span> <span class="k">VALUES</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'Test1'</span><span class="p">)</span>
 <span class="k">GO</span>

 <span class="c1">-- 데이터 확인</span>
 <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">TestTable</span>
 <span class="k">GO</span>

 <span class="c1">-- 테이블 삭제</span>
 <span class="k">DROP</span> <span class="k">TABLE</span> <span class="n">TestTable</span>
 <span class="k">GO</span>

 <span class="c1">-- 데이터베이스 삭제</span>
 <span class="k">DROP</span> <span class="k">DATABASE</span> <span class="n">TestDB</span>
 <span class="k">GO</span>

 <span class="c1">-- 컨테이너에서 나가기</span>
 <span class="n">QUIT</span>

</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="ssh-설치-및-설정-원격-접속">SSH 설치 및 설정 (원격 접속)</h2>

<ol>
  <li><strong>SSH 설치</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt update
 <span class="nb">sudo </span>apt <span class="nb">install </span>openssh-server
</code></pre></div>    </div>
  </li>
  <li><strong>SSH 서비스 상태 확인</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>systemctl status ssh
</code></pre></div>    </div>
  </li>
  <li><strong>SSH 서비스 활성화</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>systemctl <span class="nb">enable </span>ssh
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="방화벽-설정-ufw-사용">방화벽 설정 (UFW 사용)</h2>

<ol>
  <li><strong>UFW 설치</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt <span class="nb">install </span>ufw
</code></pre></div>    </div>
  </li>
  <li><strong>SSH 포트 (기본적으로 22) 열기</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>ufw allow 22/tcp
</code></pre></div>    </div>
  </li>
  <li><strong>UFW 활성화</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>ufw <span class="nb">enable</span>
</code></pre></div>    </div>
  </li>
  <li><strong>다른 필요한 포트 열기 (예: HTTP, HTTPS)</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>ufw allow 80,443/tcp
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="기타-사항">기타 사항</h2>

<h3 id="보안-강화">보안 강화</h3>

<h4 id="fail2ban-설치-및-설정">Fail2Ban 설치 및 설정</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>fail2ban
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>fail2ban
<span class="nb">sudo </span>systemctl start fail2ban
</code></pre></div></div>

<h3 id="로깅과-모니터링">로깅과 모니터링</h3>

<h4 id="prometheus와-grafana-설치">Prometheus와 Grafana 설치</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>prometheus
<span class="nb">sudo </span>apt <span class="nb">install </span>grafana
</code></pre></div></div>

<h3 id="사용자-관리">사용자 관리</h3>

<h4 id="계정-생성-및-권한-설정">계정 생성 및 권한 설정</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>adduser new_user
<span class="nb">sudo </span>usermod <span class="nt">-aG</span> <span class="nb">sudo </span>new_user
</code></pre></div></div>

<h3 id="백업과-복구">백업과 복구</h3>

<h4 id="rsync를-사용한-데이터-백업">rsync를 사용한 데이터 백업</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rsync <span class="nt">-av</span> <span class="nt">--progress</span> source_folder destination_folder
</code></pre></div></div>

<h3 id="웹-서버-설정">웹 서버 설정</h3>

<h4 id="nginx-설치-및-설정">Nginx 설치 및 설정</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>nginx
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>nginx
<span class="nb">sudo </span>systemctl start nginx
</code></pre></div></div>

<h3 id="데이터베이스-연결">데이터베이스 연결</h3>

<h4 id="postgresql-설치-및-설정">PostgreSQL 설치 및 설정</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>postgresql
</code></pre></div></div>

<h3 id="devops-파이프라인">DevOps 파이프라인</h3>

<h4 id="jenkins-설치">Jenkins 설치</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>jenkins
</code></pre></div></div>

<h3 id="클러스터링과-로드-밸런싱">클러스터링과 로드 밸런싱</h3>

<h4 id="haproxy-설치">HAProxy 설치</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>haproxy
</code></pre></div></div>

<h3 id="ssltls-설정">SSL/TLS 설정</h3>

<h4 id="lets-encrypt-사용하여-ssl-인증서-발급">Let’s Encrypt 사용하여 SSL 인증서 발급</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>certbot
<span class="nb">sudo </span>certbot <span class="nt">--nginx</span>
</code></pre></div></div>

<h3 id="시스템-최적화">시스템 최적화</h3>

<h4 id="swappiness-설정-변경">Swappiness 설정 변경</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"vm.swappiness=10"</span> | <span class="nb">sudo tee</span> <span class="nt">-a</span> /etc/sysctl.conf
</code></pre></div></div>

<p><br /></p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="서버" /><category term="서버" /><category term="Ubuntu" /><category term="Docker" /><category term="Kubernetes" /><category term="SQL Server" /><summary type="html"><![CDATA[우분투 설치 후 Docker, Kubernetes, SQL Server 설치]]></summary></entry><entry><title type="html">[AI] C# Onnx Object Detection with Winforms</title><link href="https://amsomad.github.io/c%23/Onnx_ObjectDetection_winforms/" rel="alternate" type="text/html" title="[AI] C# Onnx Object Detection with Winforms" /><published>2023-10-25T00:00:00+00:00</published><updated>2023-10-25T00:00:00+00:00</updated><id>https://amsomad.github.io/c%23/Onnx_ObjectDetection_winforms</id><content type="html" xml:base="https://amsomad.github.io/c%23/Onnx_ObjectDetection_winforms/"><![CDATA[<p><img src="https://amsomad.github.io/assets/images/post/OnnxObjectdetect.png" alt="OnnxObjectdetect" /></p>

<h2 id="소개">소개</h2>

<p>기존 솔루션에 기능을 추가하기위해, 또한 시대에 맞춰서 AI를 접목한 기능을 추가하기 위해, C#을 사용하여 <br />
Desktop의 screen을 캡처하고, Onnx 모델을 사용하여 Object Detection을 하는 방법을 찾고 있었습니다. <br />
해당 기능이 누군가에게는 도움이 될 수 있을 것 같아서, 기록해둡니다. <br />
물론 그게 나일지는 모르겠지만…. <br /></p>

<h2 id="사용된-기술">사용된 기술</h2>

<ul>
  <li>C# (DotNet 4.6.1)</li>
  <li>Winforms</li>
  <li>OnnxRuntime</li>
  <li>Onnx 모델</li>
  <li>YOLOv5</li>
</ul>

<h2 id="사전-준비">사전 준비</h2>

<h3 id="onnxruntime">OnnxRuntime</h3>

<p><a href="https://onnxruntime.ai/">OnnxRuntime</a>은 Microsoft에서 개발한 오픈소스 라이브러리로, <br />
Onnx 모델을 사용하여 AI를 적용할 수 있습니다. <br />
OnnxRuntime은 C#, C++, Python 등 다양한 언어를 지원합니다. <br /></p>

<p>만약 내가 어떠한 환경에서 어떠한 하드웨어를 사용할지 모르겠다면, <br />
<a href="https://onnxruntime.ai/">OnnxRuntime</a>의 하단에보면 최적화형태를 소개하고 있습니다. <br /></p>

<p><img src="https://amsomad.github.io/assets/images/post/Optimize_Inferencing.png" alt="Optimize_Inferencing" /><br /></p>

<h3 id="onnx-모델">Onnx 모델</h3>

<p>Onnx 모델은 AI를 적용하기 위한 모델로, 다양한 AI 모델을 Onnx 모델로 변환할 수 있습니다. <br />
기존 모델의 형태는 다양한데, Tensorflow, Pytorch, Caffe 등이 있습니다. <br />
(pt, pb, h5, pbtxt, caffe, caffemodel 등등) <br /></p>

<h3 id="사용된-모델">사용된 모델</h3>

<p>본 글에서는 <a href="https://github.com/ultralytics/yolov5">YOLOv5</a> 모델을 사용하였습니다. <br />
해당 Repository에서는 다양한 모델을 제공하고 있습니다.  <br />
Yolov5s 모델을 Export하여 Onnx 모델로 변환하였습니다. <br /></p>

<h3 id="export-onnx-model">Export Onnx Model</h3>

<p>Yolov5s 모델을 Export하여 Onnx 모델로 변환하는 방법은 아래와 같습니다. <br /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python models/export.py <span class="nt">--weights</span> yolov5s.pt <span class="nt">--img</span> 640 <span class="nt">--batch</span> 1
</code></pre></div></div>

<h3 id="netron-사용">NETRON 사용</h3>

<p><a href="https://netron.app/">NETRON</a>은 Onnx 모델을 시각화하여 확인할 수 있는 툴입니다. <br />
본 글에서는 NETRON을 사용하여 Onnx 모델을 확인하였습니다. <br /></p>

<h2 id="코드-설명">코드 설명</h2>

<p>작업의 흐름은 아래와 같다.<br /></p>
<blockquote>
  <ol>
    <li>화면 캡처</li>
    <li>캡처한 화면을 Onnx 모델에 입력</li>
    <li>Onnx 모델의 결과를 화면에 출력</li>
    <li>1번으로 돌아가기</li>
  </ol>
</blockquote>

<h3 id="1-onnx-모델-로드">1. Onnx 모델 로드</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 전역 또는 클래스 멤버 변수로 선언</span>
<span class="k">private</span> <span class="n">InferenceSession</span> <span class="n">session</span><span class="p">;</span>
<span class="k">private</span> <span class="kt">string</span> <span class="n">inputName</span><span class="p">;</span>
<span class="k">private</span> <span class="kt">string</span> <span class="n">outputName</span><span class="p">;</span>
<span class="k">private</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Tuple</span><span class="p">&lt;</span><span class="n">RectangleF</span><span class="p">,</span> <span class="kt">string</span><span class="p">,</span> <span class="kt">float</span><span class="p">&gt;&gt;</span> <span class="n">rectData</span><span class="p">;</span>

<span class="c1">// 생성자 또는 초기화 메서드에서 선언</span>
<span class="n">session</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">InferenceSession</span><span class="p">(</span><span class="s">"yolov5s.onnx"</span><span class="p">);</span>
<span class="n">inputName</span> <span class="p">=</span> <span class="n">session</span><span class="p">.</span><span class="n">InputMetadata</span><span class="p">.</span><span class="n">Keys</span><span class="p">.</span><span class="nf">FirstOrDefault</span><span class="p">();</span>
<span class="n">outputName</span> <span class="p">=</span> <span class="n">session</span><span class="p">.</span><span class="n">OutputMetadata</span><span class="p">.</span><span class="n">Keys</span><span class="p">.</span><span class="nf">FirstOrDefault</span><span class="p">();</span>
<span class="n">rectData</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Tuple</span><span class="p">&lt;</span><span class="n">RectangleF</span><span class="p">,</span> <span class="kt">string</span><span class="p">,</span> <span class="kt">float</span><span class="p">&gt;&gt;();</span>
</code></pre></div></div>

<h3 id="2-화면-캡처">2. 화면 캡처</h3>

<p>화면 캡처는 Winforms의 Panel 컨트롤을 사용하였습니다. <br />
해당 Panel에 PictureBox를 추가하고, 추론의 결과를 PictureBox에 출력합니다. <br /></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Point</span> <span class="n">screenPoint</span> <span class="p">=</span> <span class="n">capturePanel</span><span class="p">.</span><span class="nf">PointToScreen</span><span class="p">(</span><span class="n">Point</span><span class="p">.</span><span class="n">Empty</span><span class="p">);</span>

<span class="c1">// 스크린 캡처 로직</span>
<span class="n">Bitmap</span> <span class="n">bmp</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Bitmap</span><span class="p">(</span><span class="n">capturePanel</span><span class="p">.</span><span class="n">Width</span><span class="p">,</span> <span class="n">capturePanel</span><span class="p">.</span><span class="n">Height</span><span class="p">);</span>

<span class="k">using</span> <span class="p">(</span><span class="n">Graphics</span> <span class="n">g</span> <span class="p">=</span> <span class="n">Graphics</span><span class="p">.</span><span class="nf">FromImage</span><span class="p">(</span><span class="n">bmp</span> <span class="k">as</span> <span class="n">Image</span><span class="p">))</span>
<span class="p">{</span>
    <span class="n">g</span><span class="p">.</span><span class="nf">CopyFromScreen</span><span class="p">(</span><span class="n">screenPoint</span><span class="p">.</span><span class="n">X</span><span class="p">,</span> <span class="n">screenPoint</span><span class="p">.</span><span class="n">Y</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">capturePanel</span><span class="p">.</span><span class="n">Size</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">//캡처한 화면을 Onnx 모델에 입력</span>
<span class="nf">OnnxRuntime_ObjectDetect</span><span class="p">(</span><span class="n">bmp</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="3-onnx-모델의-결과를-화면에-출력">3. Onnx 모델의 결과를 화면에 출력</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">OnnxRuntime_ObjectDetect</span><span class="p">(</span><span class="n">Image</span> <span class="n">inputImage</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// 입력 이미지를 리사이즈하고 전처리</span>
    <span class="n">Bitmap</span> <span class="n">resizedBitmap</span> <span class="p">=</span> <span class="nf">ResizeAndPreprocessImage</span><span class="p">(</span><span class="n">inputImage</span><span class="p">,</span> <span class="m">640</span><span class="p">,</span> <span class="m">640</span><span class="p">);</span>

    <span class="c1">// 이미지를 텐서 형식으로 변환</span>
    <span class="kt">var</span> <span class="n">tensorInput</span> <span class="p">=</span> <span class="nf">PreprocessImage</span><span class="p">(</span><span class="n">resizedBitmap</span><span class="p">,</span> <span class="m">640</span><span class="p">,</span> <span class="m">640</span><span class="p">);</span>

    <span class="c1">// 기존의 경계 상자 데이터를 클리어</span>
    <span class="k">this</span><span class="p">.</span><span class="n">rectData</span><span class="p">.</span><span class="nf">Clear</span><span class="p">();</span>

    <span class="c1">// 객체 검출 추론 실행</span>
    <span class="nf">RunInference</span><span class="p">(</span><span class="n">tensorInput</span><span class="p">);</span>

    <span class="c1">// 경계 상자가 그려진 비트맵 생성</span>
    <span class="n">Bitmap</span> <span class="n">bboxBitmap</span> <span class="p">=</span> <span class="nf">GenerateBoundingBoxBitmap</span><span class="p">(</span><span class="n">inputImage</span><span class="p">);</span>

    <span class="c1">// PictureBox 이미지 업데이트</span>
    <span class="n">pictureBox</span><span class="p">.</span><span class="n">Image</span> <span class="p">=</span> <span class="n">bboxBitmap</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>함수의 흐름을보면 아래와 같다. <br /></p>
<blockquote>
  <ol>
    <li>입력 이미지를 리사이즈하고 전처리</li>
    <li>이미지를 텐서 형식으로 변환</li>
    <li>기존의 경계 상자 데이터를 클리어</li>
    <li>객체 검출 추론 실행</li>
    <li>경계 상자가 그려진 비트맵 생성</li>
  </ol>
</blockquote>

<h3 id="4-입력-이미지를-리사이즈하고-전처리">4. 입력 이미지를 리사이즈하고 전처리</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="n">Bitmap</span> <span class="nf">ResizeAndPreprocessImage</span><span class="p">(</span><span class="n">Image</span> <span class="n">inputImage</span><span class="p">,</span> <span class="kt">int</span> <span class="n">width</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// 입력 이미지를 리사이즈</span>
    <span class="n">Bitmap</span> <span class="n">resizedBitmap</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Bitmap</span><span class="p">(</span><span class="n">inputImage</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>

    <span class="c1">// 리사이즈한 이미지를 전처리</span>
    <span class="k">using</span> <span class="p">(</span><span class="n">Graphics</span> <span class="n">g</span> <span class="p">=</span> <span class="n">Graphics</span><span class="p">.</span><span class="nf">FromImage</span><span class="p">(</span><span class="n">resizedBitmap</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">g</span><span class="p">.</span><span class="n">InterpolationMode</span> <span class="p">=</span> <span class="n">InterpolationMode</span><span class="p">.</span><span class="n">HighQualityBicubic</span><span class="p">;</span>
        <span class="n">g</span><span class="p">.</span><span class="nf">DrawImage</span><span class="p">(</span><span class="n">resizedBitmap</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">resizedBitmap</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="5-이미지를-텐서-형식으로-변환">5. 이미지를 텐서 형식으로 변환</h3>

<p>Width와 Height는 모델에 맞게 변경해야 합니다. <br />
기존 Yolo 모델의 경우 416 x 416이었는데, Yolov5 모델의 경우 640 x 640으로 변경되었습니다. <br /></p>

<p><a href="https://github.com/ultralytics/yolov5#pretrained-checkpoints">yolov5 - Pretrained Checkpoints</a> <br /></p>

<p>해당 링크를 참고하시면, 다양한 모델을 확인할 수 있습니다. <br /></p>

<p><br /></p>

<p>사용한 모델인 Yolov5s의 경우, 640 x 640의 이미지를 입력으로 사용합니다. <br />
Export 하여 Onnx모델로 변환을 하면, 입력 이미지의 크기가 1 x 3 x 640 x 640로 변환됩니다. <br />
혹시나 어떠한 구조로 변환되는지 궁금하신 분들은, <a href="https://netron.app/">NETRON</a>을 사용하여 확인할 수 있습니다. <br /></p>

<p>또한 cocoClasses는 아래와 같습니다. <br /></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kt">string</span><span class="p">[]</span> <span class="n">cocoClasses</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">string</span><span class="p">[]</span>
<span class="p">{</span>
    <span class="s">"person"</span><span class="p">,</span> <span class="s">"bicycle"</span><span class="p">,</span> <span class="s">"car"</span><span class="p">,</span> <span class="s">"motorcycle"</span><span class="p">,</span> <span class="s">"airplane"</span><span class="p">,</span> <span class="s">"bus"</span><span class="p">,</span> <span class="s">"train"</span><span class="p">,</span> <span class="s">"truck"</span><span class="p">,</span> <span class="s">"boat"</span><span class="p">,</span> <span class="s">"traffic light"</span><span class="p">,</span>
    <span class="s">"fire hydrant"</span><span class="p">,</span> <span class="s">"stop sign"</span><span class="p">,</span> <span class="s">"parking meter"</span><span class="p">,</span> <span class="s">"bench"</span><span class="p">,</span> <span class="s">"bird"</span><span class="p">,</span> <span class="s">"cat"</span><span class="p">,</span> <span class="s">"dog"</span><span class="p">,</span> <span class="s">"horse"</span><span class="p">,</span> <span class="s">"sheep"</span><span class="p">,</span> <span class="s">"cow"</span><span class="p">,</span>
    <span class="s">"elephant"</span><span class="p">,</span> <span class="s">"bear"</span><span class="p">,</span> <span class="s">"zebra"</span><span class="p">,</span> <span class="s">"giraffe"</span><span class="p">,</span> <span class="s">"backpack"</span><span class="p">,</span> <span class="s">"umbrella"</span><span class="p">,</span> <span class="s">"handbag"</span><span class="p">,</span> <span class="s">"tie"</span><span class="p">,</span> <span class="s">"suitcase"</span><span class="p">,</span> <span class="s">"frisbee"</span><span class="p">,</span>
    <span class="s">"skis"</span><span class="p">,</span> <span class="s">"snowboard"</span><span class="p">,</span> <span class="s">"sports ball"</span><span class="p">,</span> <span class="s">"kite"</span><span class="p">,</span> <span class="s">"baseball bat"</span><span class="p">,</span> <span class="s">"baseball glove"</span><span class="p">,</span> <span class="s">"skateboard"</span><span class="p">,</span> <span class="s">"surfboard"</span><span class="p">,</span>
    <span class="s">"tennis racket"</span><span class="p">,</span> <span class="s">"bottle"</span><span class="p">,</span> <span class="s">"wine glass"</span><span class="p">,</span> <span class="s">"cup"</span><span class="p">,</span> <span class="s">"fork"</span><span class="p">,</span> <span class="s">"knife"</span><span class="p">,</span> <span class="s">"spoon"</span><span class="p">,</span> <span class="s">"bowl"</span><span class="p">,</span> <span class="s">"banana"</span><span class="p">,</span> <span class="s">"apple"</span><span class="p">,</span>
    <span class="s">"sandwich"</span><span class="p">,</span> <span class="s">"orange"</span><span class="p">,</span> <span class="s">"broccoli"</span><span class="p">,</span> <span class="s">"carrot"</span><span class="p">,</span> <span class="s">"hot dog"</span><span class="p">,</span> <span class="s">"pizza"</span><span class="p">,</span> <span class="s">"donut"</span><span class="p">,</span> <span class="s">"cake"</span><span class="p">,</span> <span class="s">"chair"</span><span class="p">,</span> <span class="s">"couch"</span><span class="p">,</span>
    <span class="s">"potted plant"</span><span class="p">,</span> <span class="s">"bed"</span><span class="p">,</span> <span class="s">"dining table"</span><span class="p">,</span> <span class="s">"toilet"</span><span class="p">,</span> <span class="s">"tv"</span><span class="p">,</span> <span class="s">"laptop"</span><span class="p">,</span> <span class="s">"mouse"</span><span class="p">,</span> <span class="s">"remote"</span><span class="p">,</span> <span class="s">"keyboard"</span><span class="p">,</span> <span class="s">"cell phone"</span><span class="p">,</span>
    <span class="s">"microwave"</span><span class="p">,</span> <span class="s">"oven"</span><span class="p">,</span> <span class="s">"toaster"</span><span class="p">,</span> <span class="s">"sink"</span><span class="p">,</span> <span class="s">"refrigerator"</span><span class="p">,</span> <span class="s">"book"</span><span class="p">,</span> <span class="s">"clock"</span><span class="p">,</span> <span class="s">"vase"</span><span class="p">,</span> <span class="s">"scissors"</span><span class="p">,</span> <span class="s">"teddy bear"</span><span class="p">,</span>
    <span class="s">"hair drier"</span><span class="p">,</span> <span class="s">"toothbrush"</span>
<span class="p">};</span>
</code></pre></div></div>
<p>별도 학습된 모델을 사용하지 않고, Yolov5s 모델을 사용하였기 때문에, cocoClasses를 사용하였습니다. <br />
만약 별도 학습된 모델을 사용하신다면, 해당 모델에 맞는 클래스를 사용하시면 됩니다. <br /></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="n">Tensor</span><span class="p">&lt;</span><span class="kt">float</span><span class="p">&gt;</span> <span class="nf">PreprocessImage</span><span class="p">(</span><span class="n">Bitmap</span> <span class="n">inputImage</span><span class="p">,</span> <span class="kt">int</span> <span class="n">width</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// 입력 이미지를 텐서 형식으로 변환</span>
    <span class="kt">var</span> <span class="n">tensorInput</span> <span class="p">=</span> <span class="k">new</span> <span class="n">DenseTensor</span><span class="p">&lt;</span><span class="kt">float</span><span class="p">&gt;(</span><span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span> <span class="p">});</span>

    <span class="c1">// 입력 이미지를 텐서 형식으로 변환</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">y</span> <span class="p">&lt;</span> <span class="n">height</span><span class="p">;</span> <span class="n">y</span><span class="p">++)</span>
    <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">x</span> <span class="p">&lt;</span> <span class="n">width</span><span class="p">;</span> <span class="n">x</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">pixel</span> <span class="p">=</span> <span class="n">inputImage</span><span class="p">.</span><span class="nf">GetPixel</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>

            <span class="c1">// 이미지의 픽셀 값을 0 ~ 1 사이의 값으로 정규화</span>
            <span class="kt">float</span> <span class="n">r</span> <span class="p">=</span> <span class="n">pixel</span><span class="p">.</span><span class="n">R</span> <span class="p">/</span> <span class="m">255.0f</span><span class="p">;</span>
            <span class="kt">float</span> <span class="n">g</span> <span class="p">=</span> <span class="n">pixel</span><span class="p">.</span><span class="n">G</span> <span class="p">/</span> <span class="m">255.0f</span><span class="p">;</span>
            <span class="kt">float</span> <span class="n">b</span> <span class="p">=</span> <span class="n">pixel</span><span class="p">.</span><span class="n">B</span> <span class="p">/</span> <span class="m">255.0f</span><span class="p">;</span>

            <span class="c1">// 이미지의 픽셀 값을 텐서에 입력</span>
            <span class="n">tensorInput</span><span class="p">[</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">]</span> <span class="p">=</span> <span class="n">r</span><span class="p">;</span>
            <span class="n">tensorInput</span><span class="p">[</span><span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">]</span> <span class="p">=</span> <span class="n">g</span><span class="p">;</span>
            <span class="n">tensorInput</span><span class="p">[</span><span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">]</span> <span class="p">=</span> <span class="n">b</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">tensorInput</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="6-객체-검출-추론-실행">6. 객체 검출 추론 실행</h3>

<p>rectData 변수는 추론 결과를 저장하기 위한 변수입니다. <br />
추론 결과의 경계 상자 데이터를 저장합니다. <br /></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">RunInference</span><span class="p">(</span><span class="n">Tensor</span><span class="p">&lt;</span><span class="kt">float</span><span class="p">&gt;</span> <span class="n">runs</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// 추론 실행</span>
    <span class="kt">var</span> <span class="n">inputs</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">NamedOnnxValue</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">NamedOnnxValue</span><span class="p">.</span><span class="n">CreateFromTensor</span><span class="p">&lt;</span><span class="kt">float</span><span class="p">&gt;(</span><span class="n">inputName</span><span class="p">,</span> <span class="n">runs</span><span class="p">)</span> <span class="p">};</span>
    
    <span class="c1">// 추론 결과</span>
    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">results</span> <span class="p">=</span> <span class="n">session</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="n">inputs</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">resultTensor</span> <span class="p">=</span> <span class="n">results</span><span class="p">.</span><span class="nf">First</span><span class="p">(</span><span class="n">r</span> <span class="p">=&gt;</span> <span class="n">r</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="k">this</span><span class="p">.</span><span class="n">outputName</span><span class="p">).</span><span class="n">AsTensor</span><span class="p">&lt;</span><span class="kt">float</span><span class="p">&gt;();</span>
        <span class="nf">ProcessResults</span><span class="p">(</span><span class="n">resultTensor</span><span class="p">.</span><span class="nf">ToArray</span><span class="p">());</span>
    <span class="p">}</span>

    <span class="c1">// 기존의 경계 상자 데이터를 클리어</span>
    <span class="k">this</span><span class="p">.</span><span class="n">rectData</span><span class="p">.</span><span class="nf">Clear</span><span class="p">();</span>

    <span class="c1">// 추론 결과를 경계 상자 데이터로 변환</span>
    <span class="nf">Postprocess</span><span class="p">(</span><span class="n">results</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// 출력 텐서를 처리하여 경계 상자와 신뢰 점수 추출</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">ProcessResults</span><span class="p">(</span><span class="kt">float</span><span class="p">[]</span> <span class="n">result</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">result</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span> <span class="n">i</span> <span class="p">+=</span> <span class="m">85</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">float</span> <span class="n">objConfidence</span> <span class="p">=</span> <span class="n">result</span><span class="p">[</span><span class="n">i</span> <span class="p">+</span> <span class="m">4</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">objConfidence</span> <span class="p">&gt;</span> <span class="m">0.5</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nf">CollectHighConfidenceData</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// 높은 신뢰도를 가진 경계 상자 데이터를 수집</span>
<span class="c1">// cocoClasses는 클래스 목록</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">CollectHighConfidenceData</span><span class="p">(</span><span class="kt">float</span><span class="p">[]</span> <span class="n">result</span><span class="p">,</span> <span class="kt">int</span> <span class="n">index</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">float</span> <span class="n">x</span> <span class="p">=</span> <span class="n">result</span><span class="p">[</span><span class="n">index</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">y</span> <span class="p">=</span> <span class="n">result</span><span class="p">[</span><span class="n">index</span> <span class="p">+</span> <span class="m">1</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">width</span> <span class="p">=</span> <span class="n">result</span><span class="p">[</span><span class="n">index</span> <span class="p">+</span> <span class="m">2</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">height</span> <span class="p">=</span> <span class="n">result</span><span class="p">[</span><span class="n">index</span> <span class="p">+</span> <span class="m">3</span><span class="p">];</span>
    <span class="kt">float</span> <span class="n">maxScore</span> <span class="p">=</span> <span class="n">result</span><span class="p">.</span><span class="nf">Skip</span><span class="p">(</span><span class="n">index</span> <span class="p">+</span> <span class="m">5</span><span class="p">).</span><span class="nf">Take</span><span class="p">(</span><span class="m">80</span><span class="p">).</span><span class="nf">Max</span><span class="p">();</span>
    <span class="kt">int</span> <span class="n">classIndex</span> <span class="p">=</span> <span class="n">result</span><span class="p">.</span><span class="nf">Skip</span><span class="p">(</span><span class="n">index</span> <span class="p">+</span> <span class="m">5</span><span class="p">).</span><span class="nf">Take</span><span class="p">(</span><span class="m">80</span><span class="p">).</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">IndexOf</span><span class="p">(</span><span class="n">maxScore</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">maxScore</span> <span class="p">&gt;</span> <span class="m">0.5</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">RectangleF</span> <span class="n">rect</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">RectangleF</span><span class="p">(</span><span class="n">x</span> <span class="p">-</span> <span class="n">width</span> <span class="p">/</span> <span class="m">2</span><span class="p">,</span> <span class="n">y</span> <span class="p">-</span> <span class="n">height</span> <span class="p">/</span> <span class="m">2</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">);</span>
        <span class="kt">string</span> <span class="n">className</span> <span class="p">=</span> <span class="n">cocoClasses</span><span class="p">[</span><span class="n">classIndex</span><span class="p">];</span>
        <span class="n">rectData</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Tuple</span><span class="p">&lt;</span><span class="n">RectangleF</span><span class="p">,</span> <span class="kt">string</span><span class="p">,</span> <span class="kt">float</span><span class="p">&gt;(</span><span class="n">rect</span><span class="p">,</span> <span class="n">className</span><span class="p">,</span> <span class="n">maxScore</span><span class="p">));</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="7-경계-상자가-그려진-비트맵-생성">7. 경계 상자가 그려진 비트맵 생성</h3>

<p>rectData변수에 저장된 경계 상자 데이터를 사용하여, 경계 상자를 그립니다. <br /></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="n">Bitmap</span> <span class="nf">GenerateBoundingBoxBitmap</span><span class="p">(</span><span class="n">Image</span> <span class="n">inputImage</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// 입력 이미지를 복사</span>
    <span class="n">Bitmap</span> <span class="n">bboxBitmap</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Bitmap</span><span class="p">(</span><span class="n">inputImage</span><span class="p">);</span>

    <span class="c1">// 경계 상자를 그릴 그래픽 객체 생성</span>
    <span class="k">using</span> <span class="p">(</span><span class="n">Graphics</span> <span class="n">g</span> <span class="p">=</span> <span class="n">Graphics</span><span class="p">.</span><span class="nf">FromImage</span><span class="p">(</span><span class="n">bboxBitmap</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="c1">// 경계 상자를 그릴 펜 생성</span>
        <span class="k">using</span> <span class="p">(</span><span class="n">Pen</span> <span class="n">pen</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Pen</span><span class="p">(</span><span class="n">Color</span><span class="p">.</span><span class="n">Red</span><span class="p">,</span> <span class="m">2</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="c1">// 경계 상자 그리기</span>
            <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">rect</span> <span class="k">in</span> <span class="n">rectData</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">g</span><span class="p">.</span><span class="nf">DrawRectangle</span><span class="p">(</span><span class="n">pen</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">X</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">Y</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">Width</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">Height</span><span class="p">);</span>
                <span class="n">g</span><span class="p">.</span><span class="nf">DrawString</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">rect</span><span class="p">.</span><span class="n">Item2</span><span class="p">}</span><span class="s"> (</span><span class="p">{</span><span class="n">rect</span><span class="p">.</span><span class="n">Item3</span><span class="p">:</span><span class="m">0.00</span><span class="p">}</span><span class="s">)"</span><span class="p">,</span> <span class="k">new</span> <span class="nf">Font</span><span class="p">(</span><span class="s">"Arial"</span><span class="p">,</span> <span class="m">12</span><span class="p">),</span> <span class="n">Brushes</span><span class="p">.</span><span class="n">Red</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">X</span><span class="p">,</span> <span class="n">rect</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="n">Y</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">bboxBitmap</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="8-picturebox-이미지-업데이트">8. PictureBox 이미지 업데이트</h3>

<p>GenerateBoundingBoxBitmap 함수에서 생성한 비트맵을 PictureBox에 출력합니다. <br /></p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pictureBox</span><span class="p">.</span><span class="n">Image</span> <span class="p">=</span> <span class="n">bboxBitmap</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="마무리">마무리</h2>
<p>Desktop의 화면을 캡쳐하여 객체 검출을 하는 방법을 작성하였습니다. <br />
본 글에서는 Yolov5s 모델을 사용하였지만, 다른 모델을 사용하셔도 무방합니다. <br />
만약 GPU 환경이라면, 추론 속도가 더 빠를 것입니다. <br /></p>

<p>코드 전체는 <a href="https://github.com/AmSoMad/winformOnnxRuntime_ObjectDetection">Github</a> 에서 확인하실 수 있습니다. <br /></p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="C#" /><category term="C#" /><category term="ASP.NET Core" /><category term="Web" /><category term="Camera" /><summary type="html"><![CDATA[C# Onnx Object Detection with Winforms]]></summary></entry><entry><title type="html">[Web] C# stream camera to web</title><link href="https://amsomad.github.io/c%23/camera_streaming/" rel="alternate" type="text/html" title="[Web] C# stream camera to web" /><published>2023-08-11T00:00:00+00:00</published><updated>2023-08-11T00:00:00+00:00</updated><id>https://amsomad.github.io/c%23/camera_streaming</id><content type="html" xml:base="https://amsomad.github.io/c%23/camera_streaming/"><![CDATA[<h2 id="소개">소개</h2>

<p>웹 카메라를 사용하여 영상 스트리밍을 위한 코드는 다양한 분야에서 활용될 수 있습니다. 본 글에서는 C#을 사용하여 카메라 영상을 웹으로 스트리밍하는 기본 코드를 살펴볼 것입니다.</p>

<h2 id="코드-설명">코드 설명</h2>

<h3 id="indexhtml">index.html</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="o">&lt;</span><span class="nx">canvas</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">videoCanvas</span><span class="dl">"</span> <span class="nx">width</span><span class="o">=</span><span class="dl">"</span><span class="s2">640</span><span class="dl">"</span> <span class="nx">height</span><span class="o">=</span><span class="dl">"</span><span class="s2">480</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/canvas</span><span class="err">&gt;
</span>    <span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span>
        <span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">videoCanvas</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">'</span><span class="s1">2d</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">image</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Image</span><span class="p">();</span>

        <span class="nx">image</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
            <span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
            <span class="c1">// 이미지가 로드되었을 때만 캔버스에 그리기</span>
            <span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="p">};</span>

        <span class="nx">image</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/api/stream</span><span class="dl">'</span><span class="p">;</span>

        <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
            <span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
        <span class="p">},</span> <span class="mi">30</span><span class="p">);</span>
    <span class="o">&lt;</span><span class="sr">/script</span><span class="err">&gt;
</span></code></pre></div></div>

<p>아래의 코드는 카메라에서 RTSP URL을 사용하여 비디오 스트림을 캡처하고 웹으로 전송합니다.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"stream"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">GetVideoStream</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Response</span><span class="p">.</span><span class="n">ContentType</span> <span class="p">=</span> <span class="s">"multipart/x-mixed-replace; boundary=frame"</span><span class="p">;</span>

    <span class="k">using</span> <span class="nn">var</span> <span class="n">capture</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">VideoCapture</span><span class="p">(</span><span class="m">0</span><span class="p">);</span>
    <span class="k">using</span> <span class="nn">var</span> <span class="n">memoryStream</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MemoryStream</span><span class="p">();</span>

    <span class="k">while</span> <span class="p">(</span><span class="k">true</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">using</span> <span class="nn">var</span> <span class="n">frame</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Mat</span><span class="p">();</span>
        <span class="n">capture</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="n">frame</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">frame</span><span class="p">.</span><span class="nf">Empty</span><span class="p">())</span> <span class="k">continue</span><span class="p">;</span>

        <span class="n">Cv2</span><span class="p">.</span><span class="nf">Flip</span><span class="p">(</span><span class="n">frame</span><span class="p">,</span> <span class="n">frame</span><span class="p">,</span> <span class="n">FlipMode</span><span class="p">.</span><span class="n">Y</span><span class="p">);</span>

        <span class="n">frame</span><span class="p">.</span><span class="nf">ToBitmap</span><span class="p">().</span><span class="nf">Save</span><span class="p">(</span><span class="n">memoryStream</span><span class="p">,</span> <span class="n">System</span><span class="p">.</span><span class="n">Drawing</span><span class="p">.</span><span class="n">Imaging</span><span class="p">.</span><span class="n">ImageFormat</span><span class="p">.</span><span class="n">Jpeg</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">buffer</span> <span class="p">=</span> <span class="n">memoryStream</span><span class="p">.</span><span class="nf">ToArray</span><span class="p">();</span>

        <span class="k">await</span> <span class="n">Response</span><span class="p">.</span><span class="n">Body</span><span class="p">.</span><span class="nf">WriteAsync</span><span class="p">(</span><span class="n">Encoding</span><span class="p">.</span><span class="n">ASCII</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="s">"\r\n--frame\r\n"</span><span class="p">));</span>
        <span class="k">await</span> <span class="n">Response</span><span class="p">.</span><span class="n">Body</span><span class="p">.</span><span class="nf">WriteAsync</span><span class="p">(</span><span class="n">Encoding</span><span class="p">.</span><span class="n">ASCII</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="s">"Content-Type: image/jpeg\r\n\r\n"</span><span class="p">));</span>
        <span class="k">await</span> <span class="n">Response</span><span class="p">.</span><span class="n">Body</span><span class="p">.</span><span class="nf">WriteAsync</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">buffer</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>

        <span class="n">memoryStream</span><span class="p">.</span><span class="nf">SetLength</span><span class="p">(</span><span class="m">0</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">Response</span><span class="p">.</span><span class="n">Body</span><span class="p">.</span><span class="nf">FlushAsync</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="핵심-부분">핵심 부분</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">VideoCapture</code>는 카메라나 비디오 파일에서 프레임을 캡처합니다.</li>
  <li><code class="language-plaintext highlighter-rouge">Mat</code> 객체는 OpenCV에서 이미지를 표현하며, 각 프레임을 처리합니다.</li>
  <li><code class="language-plaintext highlighter-rouge">Cv2.Flip</code>은 이미지를 좌우 반전합니다.</li>
  <li>마지막으로 메모리 스트림을 사용하여 JPEG 이미지로 저장하고, 이를 웹에 전송합니다.</li>
</ul>

<h2 id="마무리">마무리</h2>

<p>이 코드는 C#과 OpenCV 라이브러리를 사용하여 웹 카메라의 영상을 웹에 스트리밍하는 간단한 예시입니다. 프로젝트에 맞게 조정하거나 확장하여 사용할 수 있습니다.</p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code></p>]]></content><author><name>Yoon Sik Lim</name></author><category term="C#" /><category term="C#" /><category term="Blazor" /><category term="ASP.NET Core" /><category term="Web" /><category term="Camera" /><summary type="html"><![CDATA[웹으로 영상스트리밍 기본코드]]></summary></entry><entry><title type="html">[gRPC]gRPC-Auto Compiler with github action</title><link href="https://amsomad.github.io/grpc/grpc_auto_compiler/" rel="alternate" type="text/html" title="[gRPC]gRPC-Auto Compiler with github action" /><published>2023-08-09T00:00:00+00:00</published><updated>2023-08-09T00:00:00+00:00</updated><id>https://amsomad.github.io/grpc/grpc_auto_compiler</id><content type="html" xml:base="https://amsomad.github.io/grpc/grpc_auto_compiler/"><![CDATA[<hr />

<p>gRPC (gRPC Remote Procedure Calls)는 원격 프로시저 호출 (RPC) 시스템을 위한 <br />
 오픈 소스 프레임워크로, Google에서 개발하였습니다. <br />
 gRPC는 HTTP/2를 기반으로 하며, 통신 성능 최적화와 다양한 언어 지원이 강점입니다. <br /></p>

<h2 id="grpc-auto-compiler">gRPC-Auto Compiler</h2>

<p><a href="https://github.com/AmSoMad/gRPC-AutoCompiler">gRPC-AutoCompiler 저장소</a>는 Github Actions를 통해 .proto 파일을 감지하고 <br />
자동으로 컴파일하여 push하는 저장소입니다. 이 프로젝트는 다양한 언어에서 gRPC를 쉽게 사용할 수 있게 도와줍니다. <br /></p>

<h3 id="사용-방법">사용 방법</h3>

<ol>
  <li><strong>Github 저장소에 접근:</strong> 원하는 프로젝트에 .proto 파일을 push합니다. <br /></li>
  <li><strong>자동 컴파일:</strong> Github Actions가 .proto 파일을 감지하고 자동 컴파일을 시작합니다. <br /></li>
  <li><strong>결과 확인:</strong> 컴파일 된 파일은 지정된 출력 디렉토리에 자동으로 저장됩니다. <br /></li>
</ol>

<h3 id="지원되는-언어">지원되는 언어</h3>

<ul>
  <li>C#</li>
  <li>JavaScript</li>
  <li>TypeScript</li>
  <li>Java</li>
  <li>Dart</li>
  <li>Go</li>
  <li>gRPC-Web</li>
  <li>Python (추가 예정)</li>
</ul>

<h3 id="작동-원리">작동 원리</h3>

<p>gRPC-AutoCompiler는 Github Actions를 통해 자동 컴파일 프로세스를 관리합니다. Github 저장소에서 .proto 파일을 push하면 action이 해당 파일을 감지하고 지정된 언어로 컴파일합니다.</p>

<h3 id="예제-코드">예제 코드</h3>

<p>아래는 Github Action workflow의 예제입니다. 해당 코드는 .proto 파일을 감지하고, 필요한 컴파일러와 플러그인을 설치한 후, 지정된 언어로 .proto 파일을 컴파일합니다.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">gRPC-AutoCompiler</span>


</code></pre></div></div>

<h2 id="마치며">마치며</h2>

<p>gRPC-AutoCompiler는 여러 프로젝트와 언어에서 gRPC를 효율적으로 사용할 수 있도록 도와줍니다. 자동화된 컴파일 프로세스는 개발 시간을 줄이고 생산성을 향상시키는 데 기여할 것입니다.</p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">만들고나니 내것이 아니었다.</code>
```</p>]]></content><author><name>Yoon Sik Lim</name></author><category term="gRPC" /><category term="gRPC" /><category term="HTTP2" /><category term="google" /><category term="github" /><category term="action" /><category term="protoc" /><category term="protobuf" /><category term="proto" /><summary type="html"><![CDATA[gRPC-Auto Compiler with github action]]></summary></entry></feed>