<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>no-glass-otacku.log</title>
        <link>https://velog.io/</link>
        <description>이제 개발해야지...</description>
        <lastBuildDate>Tue, 12 May 2026 05:11:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>no-glass-otacku.log</title>
            <url>https://velog.velcdn.com/images/no-glass-otacku/profile/baf194e2-1f83-4314-a853-1d5b5a30163c/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. no-glass-otacku.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/no-glass-otacku" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Azure Data Factory (미완)]]></title>
            <link>https://velog.io/@no-glass-otacku/Azure-Data-Factory</link>
            <guid>https://velog.io/@no-glass-otacku/Azure-Data-Factory</guid>
            <pubDate>Tue, 12 May 2026 05:11:03 GMT</pubDate>
            <description><![CDATA[<pre><code>{
    &quot;runStatus&quot;: {
    ------중략-----
        &quot;metrics&quot;: {
            &quot;sinkTotalCount&quot;: {
    ------중략-----
                &quot;sources&quot;: {
                    &quot;employeeBatchData&quot;: {
                        &quot;rowsRead&quot;: 200, &lt;-값을 가져오는 곳
                        &quot;store&quot;: &quot;blob&quot;,

    ------중략-----

}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[시각화] PowerBI에 Stream Analytics에 연결하는 법]]></title>
            <link>https://velog.io/@no-glass-otacku/%EC%8B%9C%EA%B0%81%ED%99%94-PowerBI%EC%97%90-Stream-Analytics%EC%97%90-%EC%97%B0%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@no-glass-otacku/%EC%8B%9C%EA%B0%81%ED%99%94-PowerBI%EC%97%90-Stream-Analytics%EC%97%90-%EC%97%B0%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Sun, 10 May 2026 12:41:29 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Stream Analytics에 연결하는 법<ol>
<li>PowerBI 웹사이트에서 &#39;작업 영역&#39; &gt; &#39;+새 작업 영역&#39;</li>
<li>작업 영역 생성</li>
<li>URL 중 박스 쳐진 부분(작업 영역 ID) 저장해두기</li>
</ol>
</li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/5bcaf07f-22f6-4eba-bb55-fe6d2f3674f5/image.png" width="300"></th>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/e5211287-d456-4bf0-8eb6-27b6ea8674cb/image.png" width="280"></th>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/7db8d75f-e760-4a9f-bf33-e71ad81531df/image.png" width="800"></th>
</tr>
</thead>
</table>
<ol start="4">
<li>Stream Analytics에서 Power BI를 출력으로 추가할 때, &#39;그룹 작업 영역&#39;에 아까 저장해둔 작업 영역 ID를 입력.</li>
<li>&#39;권한 부여&#39;를 누르고, 로그인 화면이 뜨면 작업영역이랑 동일한 powerBI 계정으로 로그인</li>
<li>powerBI로 돌아와서 &#39;데이터 가져오기&#39; &gt; &#39;powerBI 의미 체계 모델&#39; &gt; &#39;연결&#39; &gt; 내가 사용하려는 데이터셋 선택하면 완료!</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/bd9b0573-b421-4105-a5bb-af2e1369dcd5/image.png" width="350"></th>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/951c8cc0-cb5c-4081-8648-c605893e72d0/image.png" width="250"></th>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/06db90f6-9313-456a-a119-34f0d8a2219e/image.png" width="280"></th>
</tr>
</thead>
</table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[2026 Hult prize national] 심사위원 피드백]]></title>
            <link>https://velog.io/@no-glass-otacku/2026-Hult-prize-national-%EC%8B%AC%EC%82%AC%EC%9C%84%EC%9B%90-%ED%94%BC%EB%93%9C%EB%B0%B1</link>
            <guid>https://velog.io/@no-glass-otacku/2026-Hult-prize-national-%EC%8B%AC%EC%82%AC%EC%9C%84%EC%9B%90-%ED%94%BC%EB%93%9C%EB%B0%B1</guid>
            <pubDate>Thu, 07 May 2026 02:11:04 GMT</pubDate>
            <description><![CDATA[<h3 id="404팀---aequalis-시각장애인을-위한-점역-통합-플랫폼-솔루션">&lt;404팀 - Aequalis 시각장애인을 위한 점역 통합 플랫폼 솔루션&gt;</h3>
<p><strong>피드백 정리</strong></p>
<hr>
<p><strong>✅ 잘 된 부분</strong></p>
<p>발표 앞부분의 논리 흐름이 좋았어. 문제 제기와 Why Now까지의 흐름이 설득력 있었고 첫인상이 좋았다는 평가를 받았어. 특히 규제가 존재해서 우리 서비스가 필요하다는 부분이 가장 좋게 평가됐어.</p>
<pre><code>**문제의 원인**
1. 점역이 비싸고 수작업이라 오래 걸린다
2. 접근성 표준은 있지만 그걸 준수할 기술이 없다
3. 결과적으로 7.6%의 자료만 접근성이 보장된다

**지금 해결해야 하는 이유**
1. **규제가 존재**해서 우리 서비스가 법적 필수재가 된다
2. 예산은 이미 배정되어 있다

**기존 솔루션의 문제**
1. 충분한 자동화가 없다
2. 서비스들이 분산되어 있다

**우리 솔루션**
1. 충분한 자동화
2. 분산된 서비스를 통합한 플랫폼</code></pre><p><em>다른 팀 적용: 발표 초반 문제 정의와 감정적 훅은 심사위원의 첫인상을 결정해. 앞부분에 가장 많은 공을 들여.</em></p>
<hr>
<p><strong>❌ 부족했던 부분</strong></p>
<p><strong>1. 솔루션 설명이 불명확했어</strong></p>
<p>심사위원이 &quot;뭘 하려는 건지&quot;, &quot;결과물이 뭔지&quot;를 계속 물었어. 흩어진 기술들을 모아서 빈 공간을 채운다는 핵심 개념이 발표에서 전달되지 않았어. 특히 Pedagogical Masking 같은 독자적 기술이 충분히 설명되지 않고 슬라이드에만 적혀서 넘어갔어.</p>
<p><em>다른 팀 적용: 솔루션의 핵심을 한 문장으로 말할 수 없다면 청중도 이해하지 못해. &quot;우리는 X를 Y방식으로 해결한다&quot;를 한 문장으로 압축해서 반드시 발표에 포함시켜.</em></p>
<p><strong>2. 교과서인지 학습지인지 명확하지 않았어</strong></p>
<p>타깃 제품이 무엇인지 흐지부지 넘어갔어. 심사위원 입장에서는 범위가 불명확하면 신뢰도가 떨어져.</p>
<p><em>다른 팀 적용: 타깃을 좁게 정의할수록 설득력이 높아져. &quot;우리는 모든 것을 한다&quot;보다 &quot;우리는 이것부터 한다&quot;가 더 강해.</em></p>
<p><strong>3. 비즈니스 모델 설명이 작동 방식 없이 숫자만 있었어</strong></p>
<p>돈 버는 구조는 설명됐지만 실제로 어떻게 운영되는지가 빠졌어. 심사위원은 비즈니스 모델에서 두 가지를 봐. 어떻게 작동하는지, 그리고 어떻게 돈을 버는지. 둘 다 한눈에 보여야 해.</p>
<p><em>다른 팀 적용: 비즈니스 모델 슬라이드는 화려하게 만들 필요 없어. &quot;A가 B를 하면 C가 발생하고 우리는 D를 가져간다&quot;는 흐름이 한눈에 보이면 충분해.</em></p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/1beedb34-d285-4fcb-9a89-174300e30a3c/image.png" alt="이미지1" width="200"> X</th>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/a3303e9a-00af-4e33-983b-fdbf361adae2/image.png" alt="이미지2" width="300"> ?</th>
</tr>
</thead>
</table>
<p><strong>4. Callback 답변이 너무 길었어</strong></p>
<p>질문의 의도를 파악하고 요점만 말하는 게 필요했어. 심사위원이 원한 건 &quot;있는 걸 모아서 빈 공간을 채운다&quot; 한 문장이었는데 너무 길게 답했어.</p>
<p><em>다른 팀 적용: Q&amp;A에서 답변은 두 문장 이내로 시작해. 핵심을 먼저 말하고, 필요하면 한 문장을 덧붙여. 길게 말할수록 핵심이 묻혀.</em></p>
<hr>
<p><strong>💡 핵심 인사이트 — 심사위원이 가장 강조한 것</strong></p>
<blockquote>
<p>&quot;결과물은 똑같아. How to가 다른 거야. 오퍼레이션이 다른 거야.&quot;</p>
</blockquote>
<p>기존 점자 교재와 Aequalis의 결과물은 같아. 차이는 만드는 방식이야. 
이걸 한 문장으로 압축해서 발표에 넣었어야 했어.</p>
<p>심사위원이 제안한 한 줄:</p>
<blockquote>
<p>&quot;흩어져 있는 점역 기술들을 모아서 큐레이션하고, 빈 공간은 우리만의 기술로 채운다. 결과물은 같지만 훨씬 빠르고 정확하게.&quot;</p>
</blockquote>
<p><em>다른 팀 적용: 발표에서 &quot;<strong>우리만의 한 줄</strong>&quot;이 없으면 심사위원은 발표가 끝난 후 아무것도 기억하지 못해. 발표 준비의 마지막 단계는 항상 &quot;이 발표를 한 문장으로 요약하면?&quot;에 답하는 거야.</em></p>
<hr>
<p><strong>🔍 추가 피드백 인사이트</strong></p>
<p><strong>플랫폼 비즈니스는 차별화가 핵심이야</strong></p>
<p>플랫폼 비즈니스는 비슷한 게 너무 많아. &quot;찾으면 나온다&quot;는 말이 나올 수 있어. 그래서 왜 우리가, 왜 지금, 왜 이 시장에서 이 생각을 하게 됐는지를 보여줘야 해. 
차별점은 기능이 아니라 문제 정의에서 나와. 문제 정의가 정확할수록 &quot;이 팀이 들어가면 해결된다&quot;는 확신을 줄 수 있어.</p>
<p><em>다른 팀 적용: 제일 좋은 비즈니스는 벤치마킹이라고 했어. 이미 검증된 모델을 가져오되, 우리가 이 문제를 발견한 맥락과 이유를 보여주는 게 차별점이야. &quot;왜 우리가 이걸 만들게 됐는가&quot;가 설득력의 핵심이야.</em></p>
<p><strong>4단계 구조를 지켜</strong></p>
<p>심사위원이 직접 제시한 발표 구조야.</p>
<ol>
<li>현상 — 이런 일이 일어나고 있다</li>
<li>문제 정의 — 이게 왜 문제인가</li>
<li>솔루션 — 우리는 이렇게 해결한다</li>
<li>임팩트 — 그 결과 이렇게 달라진다</li>
</ol>
<p><em>다른 팀 적용: 4분 발표든 10분 발표든 이 구조를 벗어나지 마. 솔루션을 먼저 말하고 싶은 충동을 참아. 문제가 먼저 공감돼야 솔루션이 설득돼.</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ARM 템플릿: Azure 인프라를 코드로 관리하기]]></title>
            <link>https://velog.io/@no-glass-otacku/ARM-%ED%85%9C%ED%94%8C%EB%A6%BF-Azure-%EC%9D%B8%ED%94%84%EB%9D%BC%EB%A5%BC-%EC%BD%94%EB%93%9C%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@no-glass-otacku/ARM-%ED%85%9C%ED%94%8C%EB%A6%BF-Azure-%EC%9D%B8%ED%94%84%EB%9D%BC%EB%A5%BC-%EC%BD%94%EB%93%9C%EB%A1%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 07 May 2026 01:54:32 GMT</pubDate>
            <description><![CDATA[<h2 id="1-azure-콘솔에서-클릭하다-지친-적-있으신가요">1. Azure 콘솔에서 클릭하다 지친 적 있으신가요?</h2>
<p>Azure 콘솔에서 리소스를 하나씩 클릭해서 만들어본 적 있으신가요?</p>
<p>개발 환경 하나 세팅하는 데 30분씩 클릭하고,
팀원한테 &quot;나랑 똑같이 만들어줘&quot; 하면 어딘가 하나씩 빠져있고,
운영 환경에 같은 구성을 다시 만들려니 뭘 먼저 만들었는지도 기억이 안 납니다.</p>
<p>ARM 템플릿은 이 문제를 해결하기 위해 존재합니다.</p>
<p>한 마디로 정의하면, <strong>&quot;Azure 인프라의 설계도를 JSON 파일로 저장해두는 것&quot;</strong> 입니다.
이 파일 하나면 누구든, 어디서든, 동일한 Azure 환경을 빠르게 재현할 수 있습니다.</p>
<hr>
<h2 id="2-arm-템플릿이-뭔가요">2. ARM 템플릿이 뭔가요?</h2>
<h3 id="선언형-vs-명령형">선언형 vs 명령형</h3>
<p>인프라를 만드는 방식에는 두 가지가 있습니다.</p>
<p><strong>명령형</strong> — &quot;어떻게 만들지&quot;를 순서대로 직접 지시하는 방식</p>
<pre><code class="language-bash">az group create --name myRG --location koreacentral
az storage account create --name myStorage ...
az functionapp create --name myFunctionApp ...</code></pre>
<p><strong>선언형</strong> — &quot;무엇을 만들지&quot;를 기술하면 Azure가 알아서 처리하는 방식</p>
<pre><code class="language-json">{
  &quot;resources&quot;: [
    { &quot;type&quot;: &quot;Microsoft.Storage/storageAccounts&quot;, ... },
    { &quot;type&quot;: &quot;Microsoft.Web/sites&quot;, ... }
  ]
}</code></pre>
<p>ARM 템플릿은 선언형입니다. 순서를 직접 신경 쓰지 않아도 되고, Azure가 의존 관계를 파악해서 알아서 순서대로 만들어줍니다.</p>
<h3 id="arm-템플릿의-기본-json-구조">ARM 템플릿의 기본 JSON 구조</h3>
<p>ARM 템플릿은 아래와 같은 구조로 이루어져 있습니다.</p>
<pre><code class="language-json">{
  &quot;$schema&quot;: &quot;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&quot;,
  &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
  &quot;parameters&quot;: {},
  &quot;variables&quot;: {},
  &quot;resources&quot;: [],
  &quot;outputs&quot;: {}
}</code></pre>
<p>각 섹션이 어떤 역할을 하는지는 아래 표를 참고하세요.</p>
<table>
<thead>
<tr>
<th>섹션</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>parameters</code></td>
<td>배포할 때 you가 입력하는 값 (환경마다 다른 값)</td>
</tr>
<tr>
<td><code>variables</code></td>
<td>템플릿 내부에서 재사용하는 값</td>
</tr>
<tr>
<td><code>resources</code></td>
<td>실제로 만들 Azure 리소스 목록</td>
</tr>
<tr>
<td><code>outputs</code></td>
<td>배포 후 결과로 반환할 값 (연결 문자열 등)</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-왜-써야-하나요">3. 왜 써야 하나요?</h2>
<h3 id="반복-배포-→-동일한-결과-보장">반복 배포 → 동일한 결과 보장</h3>
<p>같은 ARM 템플릿으로 배포하면 개발, 스테이징, 운영 환경이 항상 동일하게 만들어집니다.
사람이 클릭하다 실수로 설정이 달라지는 문제가 없어집니다.</p>
<p>또한 ARM 템플릿은 <strong>멱등성(Idempotency)</strong> 을 보장합니다.
같은 템플릿을 여러 번 실행해도 결과가 동일하고, 이미 있는 리소스는 건드리지 않고 없는 것만 만듭니다.</p>
<h3 id="인프라를-git으로-관리할-수-있음">인프라를 Git으로 관리할 수 있음</h3>
<p>ARM 템플릿은 JSON 파일이기 때문에 코드처럼 Git으로 버전 관리할 수 있습니다.</p>
<ul>
<li>누가 언제 어떤 인프라를 바꿨는지 추적 가능</li>
<li>PR 리뷰로 인프라 변경 사항을 팀이 함께 검토</li>
<li>잘못 배포했을 때 이전 버전으로 롤백 가능</li>
</ul>
<h3 id="팀-협업-시-환경-통일">팀 협업 시 환경 통일</h3>
<p>신규 팀원이 합류했을 때 &quot;이 파일로 배포하면 돼&quot;라고 하면 끝입니다.
구두로 세팅 방법을 설명하거나 문서를 따로 관리할 필요가 없습니다.</p>
<hr>
<h2 id="4-어떻게-만드나요">4. 어떻게 만드나요?</h2>
<p>ARM 템플릿을 만드는 방법은 크게 두 가지입니다.</p>
<h3 id="방법-1-기존-리소스-그룹에서-템플릿-내보내기">방법 1: 기존 리소스 그룹에서 템플릿 내보내기</h3>
<p>이미 Azure Portal에서 만들어둔 리소스가 있다면, 그걸 그대로 템플릿으로 추출할 수 있습니다.</p>
<pre><code>Azure Portal 접속
→ 리소스 그룹 선택
→ 왼쪽 메뉴에서 [자동화] &gt; [템플릿 내보내기] 클릭
→ [다운로드] 버튼 클릭</code></pre><p>⚠️ 내보낸 템플릿 사용 시 주의사항:</p>
<ul>
<li>일부 속성이 누락되거나 하드코딩된 값이 들어있을 수 있습니다</li>
<li>비밀번호, 연결 문자열 같은 민감한 정보는 보안상 의도적으로 빠져있습니다</li>
<li>바로 재배포하면 오류가 날 수 있으니 내용을 먼저 검토하는 것이 좋습니다</li>
</ul>
<h3 id="방법-2-vs-code에서-직접-작성">방법 2: VS Code에서 직접 작성</h3>
<p><strong>VS Code ARM 템플릿 확장 설치</strong></p>
<p>VS Code 확장 마켓플레이스에서 <code>Azure Resource Manager (ARM) Tools</code>를 설치합니다.</p>
<p><strong>템플릿 직접 작성 예시</strong></p>
<p>아래는 Storage Account 하나를 만드는 간단한 ARM 템플릿 예시입니다.</p>
<pre><code class="language-json">{
  &quot;$schema&quot;: &quot;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&quot;,
  &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
  &quot;parameters&quot;: {
    &quot;storageAccountName&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;metadata&quot;: {
        &quot;description&quot;: &quot;Storage Account 이름&quot;
      }
    },
    &quot;location&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;defaultValue&quot;: &quot;koreacentral&quot;,
      &quot;metadata&quot;: {
        &quot;description&quot;: &quot;리소스를 배포할 Azure 지역&quot;
      }
    }
  },
  &quot;variables&quot;: {
    &quot;storageSku&quot;: &quot;Standard_LRS&quot;
  },
  &quot;resources&quot;: [
    {
      &quot;type&quot;: &quot;Microsoft.Storage/storageAccounts&quot;,
      &quot;apiVersion&quot;: &quot;2021-09-01&quot;,
      &quot;name&quot;: &quot;[parameters(&#39;storageAccountName&#39;)]&quot;,
      &quot;location&quot;: &quot;[parameters(&#39;location&#39;)]&quot;,
      &quot;sku&quot;: {
        &quot;name&quot;: &quot;[variables(&#39;storageSku&#39;)]&quot;
      },
      &quot;kind&quot;: &quot;StorageV2&quot;
    }
  ],
  &quot;outputs&quot;: {
    &quot;storageAccountId&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;value&quot;: &quot;[resourceId(&#39;Microsoft.Storage/storageAccounts&#39;, parameters(&#39;storageAccountName&#39;))]&quot;
    }
  }
}</code></pre>
<p><strong>코드 포인트 설명:</strong></p>
<ul>
<li><code>parameters(&#39;storageAccountName&#39;)</code> : 배포할 때 입력받는 Storage Account 이름</li>
<li><code>variables(&#39;storageSku&#39;)</code> : 내부에서 재사용하는 SKU 값</li>
<li><code>outputs</code> : 배포 후 만들어진 Storage Account의 ID를 반환</li>
</ul>
<hr>
<h2 id="5-어떻게-배포하나요">5. 어떻게 배포하나요?</h2>
<h3 id="방법-1-azure-portal에서-배포">방법 1: Azure Portal에서 배포</h3>
<pre><code>Azure Portal 접속
→ 검색창에 &quot;사용자 지정 템플릿 배포&quot; 입력
→ [편집기에서 사용자 고유의 템플릿 빌드합니다.] 클릭
→ JSON 파일 붙여넣기 또는 파일 업로드
→ [저장] 클릭
→ 구독, 리소스 그룹, 파라미터 값 입력
→ [검토 + 만들기] 클릭 후 배포</code></pre><p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/23050ef2-b48e-46df-8f4e-f3935290ca4a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/f56fe99d-c811-44ae-87c0-519a8eeea78e/image.png" alt=""></p>
<h3 id="방법-2-azure-cli로-배포">방법 2: Azure CLI로 배포</h3>
<p>Azure CLI가 설치되어 있다면 터미널에서 바로 배포할 수 있습니다.</p>
<pre><code class="language-bash"># 1. Azure 로그인
az login

# 2. 리소스 그룹 생성 (없는 경우)
az group create --name myResourceGroup --location koreacentral

# 3. ARM 템플릿 배포
az deployment group create \
  --resource-group myResourceGroup \
  --template-file ./azuredeploy.json \
  --parameters storageAccountName=mystorageaccount123</code></pre>
<h3 id="배포-후-확인-방법">배포 후 확인 방법</h3>
<p>배포가 완료되면 아래 방법으로 결과를 확인할 수 있습니다.</p>
<p><strong>Azure Portal에서 확인:</strong></p>
<pre><code>리소스 그룹 → [배포] 메뉴 클릭
→ 배포 이력 및 각 리소스 생성 결과 확인 가능</code></pre><p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/9be8d561-17be-4ffc-b08a-32453d3f1d4a/image.png" alt=""></p>
<p><strong>CLI에서 확인:</strong></p>
<pre><code class="language-bash"># 배포 상태 확인
az deployment group show \
  --resource-group myResourceGroup \
  --name azuredeploy

# 실제 리소스 생성 확인
az storage account show --name mystorageaccount123</code></pre>
<hr>
<h2 id="6-주의할-점">6. 주의할 점</h2>
<h3 id="민감한-정보는-템플릿에-직접-넣지-마세요">민감한 정보는 템플릿에 직접 넣지 마세요</h3>
<p>비밀번호, 연결 문자열, API 키 같은 값을 JSON에 하드코딩하면 Git에 올라갔을 때 그대로 노출됩니다.
대신 <strong>Azure Key Vault</strong> 를 연동하거나 배포 시점에 파라미터로 주입하는 방식을 사용하세요.</p>
<pre><code class="language-json">// ❌ 절대 이렇게 하지 마세요
&quot;adminPassword&quot;: {
  &quot;value&quot;: &quot;mypassword123!&quot;
}

// ✅ 파라미터로 분리하고 Key Vault 참조
&quot;adminPassword&quot;: {
  &quot;reference&quot;: {
    &quot;keyVault&quot;: {
      &quot;id&quot;: &quot;/subscriptions/.../vaults/myKeyVault&quot;
    },
    &quot;secretName&quot;: &quot;adminPassword&quot;
  }
}</code></pre>
<h3 id="내보낸-템플릿은-100-완벽하지-않습니다">내보낸 템플릿은 100% 완벽하지 않습니다</h3>
<p>Portal에서 내보낸 템플릿은 바로 재배포하면 오류가 날 수 있습니다.
특히 아래 항목들은 반드시 검토하세요.</p>
<ul>
<li>하드코딩된 리소스 이름이나 ID 값</li>
<li>누락된 필수 속성</li>
<li>민감 정보가 비어있는 필드</li>
</ul>
<blockquote>
<p>참고할 공식 문서</p>
</blockquote>
<ul>
<li><a href="https://learn.microsoft.com/ko-kr/azure/azure-resource-manager/templates/overview">템플릿 개요</a></li>
<li><a href="https://learn.microsoft.com/ko-kr/azure/azure-resource-manager/templates/resource-declaration#set-resource-specific-properties">템플릿에서 리소스 선언</a></li>
</ul>
<h3 id="localsettingsjson은-별도로-관리해야-합니다">local.settings.json은 별도로 관리해야 합니다</h3>
<p>ARM 템플릿은 <strong>클라우드 인프라</strong>를 만들어주는 파일입니다.
로컬 개발 환경에서 사용하는 <code>local.settings.json</code>은 자동으로 채워지지 않습니다.</p>
<p>ARM 템플릿으로 Storage Account나 Function App을 만들고 나면, 그 연결 문자열을 직접 복사해서 <code>local.settings.json</code>에 붙여넣어야 합니다.</p>
<pre><code class="language-json">// local.settings.json - ARM 배포 후 직접 채워야 함
{
  &quot;IsEncrypted&quot;: false,
  &quot;Values&quot;: {
    &quot;AzureWebJobsStorage&quot;: &quot;여기에 배포된 Storage Account 연결 문자열 복사&quot;,
    &quot;FUNCTIONS_WORKER_RUNTIME&quot;: &quot;dotnet&quot;
  }
}</code></pre>
<hr>
<h2 id="7-마무리">7. 마무리</h2>
<p>ARM 템플릿의 핵심 개념은 <strong>&quot;인프라도 코드다 (Infrastructure as Code)&quot;</strong> 입니다.</p>
<p>서버, 스토리지, 네트워크 같은 인프라를 사람이 손으로 클릭하는 대신, 코드로 정의하고 버전 관리하고 자동화하는 것입니다.</p>
<p>처음에는 JSON 구조가 낯설게 느껴질 수 있지만, 한 번 익숙해지면 환경을 새로 세팅하거나 팀원과 동일한 환경을 공유하는 게 훨씬 쉬워집니다.</p>
<p><strong>다음 단계로 추천하는 것들:</strong></p>
<ul>
<li>Bicep — ARM 템플릿을 더 간결한 문법으로 작성할 수 있는 Azure 전용 언어</li>
<li>Azure DevOps 또는 GitHub Actions와 연동해서 배포 자동화 파이프라인 구성</li>
<li>Azure Key Vault 연동으로 민감 정보 안전하게 관리</li>
</ul>
<hr>
<p><em>이 글이 도움이 됐다면 좋아요와 댓글로 알려주세요! 잘못된 내용이나 추가했으면 하는 내용도 편하게 남겨주시면 반영하겠습니다.</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5일간 논문읽기]]></title>
            <link>https://velog.io/@no-glass-otacku/5%EC%9D%BC%EA%B0%84-%EB%85%BC%EB%AC%B8%EC%9D%BD%EA%B8%B0</link>
            <guid>https://velog.io/@no-glass-otacku/5%EC%9D%BC%EA%B0%84-%EB%85%BC%EB%AC%B8%EC%9D%BD%EA%B8%B0</guid>
            <pubDate>Thu, 30 Apr 2026 15:42:44 GMT</pubDate>
            <description><![CDATA[<p>논문 제목: <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC9173618/pdf/pone.0269311.pdf">Big data-based risk assessment of poultry farms during the 2020/2021 highly pathogenic avian influenza epidemic in Korea</a></p>
<p>읽은 날짜: 2026-05-01</p>
<p>[Day 1] 전체 구조</p>
<ul>
<li>문제:</li>
<li>데이터:</li>
<li>결론:</li>
</ul>
<p>[Day 2] 배경</p>
<ul>
<li>기존 방법의 문제:</li>
<li>이 논문이 새롭게 하는 것:</li>
</ul>
<p>[Day 3] 데이터/방법</p>
<ul>
<li>데이터 출처:</li>
<li>주요 변수:</li>
<li>전처리:</li>
</ul>
<p>[Day 4] 결과/논의</p>
<ul>
<li>주요 결과:</li>
<li>한계:</li>
<li>앞으로 필요한 연구:</li>
</ul>
<p>[Day 5] 내 생각</p>
<ul>
<li>한 문단 요약:</li>
<li>내 관심사와의 연결:</li>
<li>궁금증:</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MS Data School 4기] 배운 도구 정리]]></title>
            <link>https://velog.io/@no-glass-otacku/%EB%B0%B0%EC%9A%B4-%EB%8F%84%EA%B5%AC-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@no-glass-otacku/%EB%B0%B0%EC%9A%B4-%EB%8F%84%EA%B5%AC-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 30 Apr 2026 07:24:50 GMT</pubDate>
            <description><![CDATA[<h2 id="python-라이브러리">python 라이브러리</h2>
<h3 id="numpy-pandas">Numpy, Pandas</h3>
<p>Numpy: 고성능 수치 계산용. (Pandas의 뿌리!)
Pandas: 표(DataFrame) 형태로 데이터를 자유자재로 요리하는 도구.</p>
<h3 id="matplotlib-seaborn">Matplotlib, Seaborn</h3>
<p>Matplotlib: 파이썬 시각화의 기본. (커스터마이징 자유도가 높음)
Seaborn: Matplotlib을 기반으로 더 예쁘고 복잡한 통계 차트를 쉽게 그려줌.</p>
<h3 id="folium">Folium</h3>
<p>&#39;지리 데이터(Geo)&#39; 특화 도구</p>
<hr>
<h2 id="웹크롤링">웹크롤링</h2>
<h3 id="requests-beautifulsoup">requests, BeautifulSoup</h3>
<p>정적 크롤링 (가볍고 빠름)
requests: 웹 서버에 데이터를 달라고 요청하는 배달원.
BeautifulSoup: 받아온 HTML 뭉치에서 원하는 정보만 쏙쏙 뽑아주는 집게.</p>
<h3 id="selenium">selenium</h3>
<p>동적 크롤링 (느리지만 강력함)
Selenium: 브라우저를 직접 조종하는 아바타. (로그인, 클릭, 무한 스크롤 등 사람의 행동이 필요할 때 사용)</p>
<hr>
<h3 id="microsoft-power-automate">[Microsoft] Power Automate <img src="https://velog.velcdn.com/images/no-glass-otacku/post/3034921b-c317-4bfc-839e-3599f43d7caf/image.png" alt=""></h3>
<hr>
<h3 id="postgresql">PostgreSQL <img src="https://velog.velcdn.com/images/no-glass-otacku/post/35142dda-945a-458e-b6d3-fc72f53c3cc8/image.png" alt=""></h3>
<h3 id="dbeaver">DBeaver <img src="https://velog.velcdn.com/images/no-glass-otacku/post/f6776790-8b8e-4209-973b-3193c3205c61/image.png" alt=""></h3>
<hr>
<h3 id="azure-machine-learning">[Azure] Machine Learning <img src="https://velog.velcdn.com/images/no-glass-otacku/post/08343697-a8fd-4c46-9c2d-0c87453fb169/image.png" alt=""></h3>
<hr>
<p>[데이터 주고 받는 기술]</p>
<blockquote>
<ul>
<li>Webhook
특정 이벤트가 발생했을 때 자동으로 지정된 URL로 데이터를 전송하는 프로세스
ex) power automate에서 팀즈 웹후크를 이용해 함수 실행마다 팀즈 채널에 메세지 보내기
우리가 팀즈의 http post url에 전송하는 거임.</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>REST API
인터넷 주소(URL)를 통해 서버에 데이터를 요청하거나 수정하는 가장 대중적인 방식</li>
</ul>
</blockquote>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/d5bc383b-372c-4e7b-9765-1fb76dbe06d4/image.png" alt=""></p>
<h3 id="azure-function-app">[Azure] Function App <img src="https://velog.velcdn.com/images/no-glass-otacku/post/7e2cd4e0-7901-49dc-8228-85a219c18abd/image.png" alt=""></h3>
<p>서버리스 환경에서 API 호출 로직을 구현</p>
<ul>
<li>로그 확인법</li>
</ul>
<ol>
<li>remote: azure portal &gt; azure function app &gt; 모니터링 &gt; 로그 &gt; traces 테이블 실행</li>
<li>local: 커멘드창에서 azurite start &gt; 터미널에 &#39;func start&#39; 실행 &gt; 터미널에서 로그 출력
 ※ remote &lt;-&gt; local로 환경설정을 바꿨을 때, 주피터 노트북 사용 시에는 restart를 해야 환경설정의 변경사항이 적용이 됨. </li>
</ol>
<h3 id="azure-event-hubs">[Azure] Event Hubs <img src="https://velog.velcdn.com/images/no-glass-otacku/post/4fa2df03-63bf-4ae9-b0c8-0fb11f64881b/image.png" alt=""></h3>
<p>실시간으로 쏟아지는 방대한 <strong>데이터를 유실 없이 받아내고 순서대로 보관</strong>하는 &#39;대용량 데이터 입구&#39;.</p>
<p>뒷단(Stream Analytics 등)에서 데이터를 처리하기 전까지 안전하게 지켜주는 완충 작용(Buffer)을 함.
Event Hubs가 없다면? 센서에서 데이터가 빗발칠 때 뒷단이 조금이라도 느려지면 데이터가 그대로 사라져 버림.</p>
<pre><code>[IoT 센서/웹후크] → (데이터 발송) → [Event Hubs] → (데이터 보관) → [Stream Analytics] → (SQL로 분석) → [Power BI]</code></pre><h4 id="azure-schema-registry">Azure Schema Registry</h4>
<p>데이터가 어떤 모양이어야 하는지 적힌 &#39;표준 설계도&#39;를 보관하고 관리하는 저장소
별도의 리소스(보통은 Azure Event Hubs namespace 안에 포함됨)로 존재</p>
<ul>
<li>작동 방식:</li>
</ul>
<ol>
<li><p>보내는 쪽이 데이터를 보내기 전에 Schema Registry에서 &quot;나 이 설계도(ID)대로 보낼게&quot;라고 등록합니다.</p>
</li>
<li><p>데이터에는 설계도 자체가 아니라 &#39;설계도 번호(ID)&#39;만 살짝 붙여서 보냅니다. (데이터 크기가 줄어듭니다!)</p>
</li>
<li><p>받는 쪽은 데이터에 붙은 ID를 보고 Schema Registry에서 설계도를 찾아와 데이터를 정확하게 해석합니다.
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/5fa22da9-326d-4484-9896-947435c65f75/image.png" alt=""></p>
</li>
</ol>
<h4 id="azure-event-hubs-capture">Azure Event Hubs Capture</h4>
<p>Event Hubs로 들어오는 실시간 데이터를 설정된 시간이나 용량 단위로 묶어서 자동으로 Azure Storage(Blob/Data Lake)에 저장해주는 기능 = 코딩 없이 <strong>원본 데이터</strong>를 즉시 저장소에 백업하는 기능</p>
<p>왜 필요한가?
** Event Hubs는 자체적으로 &#39;데이터를 전송하는 능력&#39;이 거의 없어서** 다른 곳으로 보내려면 이 설정을 켜줘야함.</p>
<h3 id="azure-stream-analytics">[Azure] Stream Analytics <img src="https://velog.velcdn.com/images/no-glass-otacku/post/c67d7ff1-63dd-4117-9c18-9ec7bcb71bb8/image.png" alt=""></h3>
<p>하나 또는 여러 개의 input을 받아 실시간 스트림 데이터와 참고용 데이터를 쿼리로 결합하고(<strong>SQL 쿼리로 데이터를 가공</strong>), 가공된 데이터를 목적에 맞게 <strong>여러 곳(Output)으로 분기하여 전송</strong>.</p>
<p>✅ 핵심 요약</p>
<ul>
<li><p>다중 입출력: 입력 소스들을 JOIN(결합)하여 분석하고, 결과물을 실시간 대시보드나 DB 등 <strong>여러 저장소로 동시에 보낼 수 있음.</strong></p>
</li>
<li><p>시간의 기준: 데이터가 시스템에 도착한 시간이 아닌, 현장에서 실제로 발생한 시간을 기준으로 처리할 것.</p>
<p>  Tip: 쿼리 작성 시 TIMESTAMP BY 문구를 사용하여 발생 시간을 명시해주는 것이 정확한 분석의 핵심!</p>
</li>
</ul>
<h3 id="microsoft-powerbi">[Microsoft] PowerBI <img src="https://velog.velcdn.com/images/no-glass-otacku/post/13698f97-ceb4-4bfa-9322-7e35d4596255/image.png" alt=""></h3>
<p>Stream Analytics에 output으로 연결하면 별도의 새로고침 없이도 신규 데이터가 즉각 반영되는 실시간 시각화 보드를 구현할 수 있음.</p>
<ul>
<li><a href="https://velog.io/@no-glass-otacku/%EC%8B%9C%EA%B0%81%ED%99%94-PowerBI%EC%97%90-Stream-Analytics%EC%97%90-%EC%97%B0%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B2%95">Stream Analytics에 연결하는 법</a></li>
</ul>
<hr>
<h3 id="azure-cosmos-db">[Azure] Cosmos DB <img src="https://velog.velcdn.com/images/no-glass-otacku/post/8895f98e-d7a9-4a4d-b4f6-65f4e969e0b7/image.png" alt=""></h3>
<p>데이터를 임베딩 모델에 통과시켜 벡터로 바꾼 후 (벡터 검색이 필요한 경우에만)
cosmos DB에 전달하면 </p>
<ol>
<li><strong>자동으로 인덱싱</strong></li>
<li>나중에 질문이 들어왔을 때 우리가 정한 Distance Function을 써서 <strong>관련 데이터를 빠르게 찾아줌.</strong>
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/33a4b7ec-1096-4ed7-baaf-bc01db4d9f3e/image.png" alt=""></li>
</ol>
<ul>
<li>설정&gt; 기능
  <strong>Vector Search for NoSQL API</strong> :벡터 검색이 필요한 경우 &#39;켜기&#39;</li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/80705fcf-1d7a-416e-9178-f116447ac91b/image.png" width="600"></th>
</tr>
</thead>
</table>
<ul>
<li><p>비용관리 모델 택1
RU/s(요청 단위): 처리량을 미리 예측하고 예약. 지속적이고 예측 가능한 트래픽에 최적.
서버리스: 사용한 만큼만 지출. 간헐적이거나 예측 불가능한 트래픽에 최적.</p>
</li>
<li><p>내부 계층
cosmosDB 계정(리소스 그룹 내부)&gt; DB&gt; container</p>
</li>
</ul>
<h3 id="microsoft-foundry">[Microsoft] Foundry <img src="https://velog.velcdn.com/images/no-glass-otacku/post/c206a15b-8fc3-4a4d-9920-95bb84d2a7d9/image.png" alt=""></h3>
<p>&lt;아직 실습 중&gt;</p>
<h3 id="gradio">Gradio <img src="https://velog.velcdn.com/images/no-glass-otacku/post/7b1b77cd-2bd7-46fd-a5ec-570b8ebae468/image.png" alt=""></h3>
<p>파이썬으로 <strong>웹 UI</strong>를 만들 수 있음. 제공해주는 컴포넌트 사용법만 알면 됨.</p>
<h3 id="nl2sql">NL2SQL</h3>
<p><strong>자연어를 SQL로 바꿔줌</strong>.</p>
<h3 id="azure-azure-openai">[Azure] Azure OpenAI <img src="https://velog.velcdn.com/images/no-glass-otacku/post/be674db2-1ad2-47fe-88b6-70dc28258cbe/image.png" alt=""></h3>
<p>AI 모델을 제공. </p>
<h4 id="대체품">대체품</h4>
<ul>
<li><p>사용 한도 내에서 무료인 LLM API 제공 서비스</p>
<table>
<thead>
<tr>
<th><a href="https://groq.com/">Groq</a></th>
<th><a href="https://github.com/marketplace/models">Github</a></th>
<th><a href="https://aistudio.google.com/api-keys">Google AI Studio</a></th>
</tr>
</thead>
</table>
</li>
<li><p>충분한 GPU가 있다면 로컬 LLM 도구를 설치</p>
<table>
<thead>
<tr>
<th><a href="Ollama">Ollama</a></th>
<th><a href="https://lmstudio.ai/download">LM Studio</a></th>
</tr>
</thead>
</table>
</li>
</ul>
<hr>
<h2 id="azure-서비스를-대체할-오픈소스">Azure 서비스를 대체할 오픈소스</h2>
<h3 id="azure-event-hubs--apache-kafka">Azure Event Hubs &gt; Apache Kafka <img src="https://velog.velcdn.com/images/no-glass-otacku/post/3a6f1e75-e31e-4012-ba37-c649d106983f/image.png" alt=""></h3>
<p>대용량의 실시간 스트리밍 데이터를 안정적으로
수집하고 전달하는 분산 메시징 시스템</p>
<h3 id="azure-stream-analytics--apache-flink">Azure Stream Analytics &gt; Apache Flink <img src="https://velog.velcdn.com/images/no-glass-otacku/post/88f23574-b939-4bb5-a9c5-afa395cea7e3/image.png" alt=""></h3>
<p>실시간으로 스트리밍 데이터를 처리하고 분석하는
분석 스트림 처리 프레임워크</p>
<h3 id="azure-cosmos-db--elasticsearch">Azure Cosmos DB &gt; Elasticsearch<img src="https://velog.velcdn.com/images/no-glass-otacku/post/1b62c00b-ed73-4071-909d-b95d69d55fba/image.png" alt=""></h3>
<p>처리된 데이터를 저장, 검색하고, 머신러닝 모델을 통합하는 검색 엔진</p>
<hr>
<h3 id="azure-azure-data-factory">[Azure] Azure Data Factory <img src="https://velog.velcdn.com/images/no-glass-otacku/post/82bebc9f-c03f-4c31-99cf-3089c9ddae1d/image.png" alt=""></h3>
<hr>
<h3 id="document-intelligence-studio">Document Intelligence Studio <img src="https://velog.velcdn.com/images/no-glass-otacku/post/ce3fc7b3-e93f-4d3a-9e7d-3d75707202f2/image.png" alt=""></h3>
<p>단일 서비스 리소스로 독립 생성이 가능.
최근에는 Azure AI Foundry (Foundry Tools) 산하로 편입되는 방향으로 가고 있음.</p>
<h4 id="ocr--문서처리">OCR / 문서처리</h4>
<ul>
<li><p>OCR/Read
손글씨도 인식하는데 단어 순서를 반대로 추출하기로 함... 가장 기본적인 text 추출</p>
</li>
<li><p>Layout
테이블, figure(그래프), 체크박스도 추출해줌.</p>
</li>
<li><p>General documents
신청서 같은 서류에서 key-value pair와 그냥 문장 덩어리를 추출함.</p>
<h5 id="prebuilt-model">prebuilt model</h5>
</li>
<li><p>Invoices
Query fields로 field 추가하면 더 잘 인식함.</p>
<p>+a</p>
</li>
</ul>
<h3 id="azure-ai-language-studio">Azure AI Language Studio <img src="https://velog.velcdn.com/images/no-glass-otacku/post/392644d1-3cd2-4daa-b930-c1c8cf8818fe/image.png" alt=""></h3>
<p> <a href="https://language.cognitive.azure.com/home">https://language.cognitive.azure.com/home</a>
 지역별로 제공 여부가 다르고 East US의 경우 모든 서비스를 제공</p>
<ul>
<li><p>정보 추출: 비구조화된 텍스트에서 범주 그룹에 속하는 개체를 추출해줌.
  NER(Named Entity Recognition)</p>
</li>
<li><p>텍스트 분류: 텍스트의 언어를 감지하거나 감정 분류</p>
<ul>
<li>고객 감정 분석</li>
<li>자동으로 텍스트 분류: 사용자 지정 텍스트 분류, 언어 감지</li>
</ul>
</li>
<li><p>질문과 대화형 언어 이해</p>
<ul>
<li>고객 쿼리에 응답 QnA: 사용자 지정 질문 답변<ul>
<li>대화형 환경 구축: 대화 언어 이해</li>
</ul>
</li>
</ul>
</li>
<li><p>텍스트 요약</p>
</li>
<li><p>번역</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Azure Machine Learning] 데이터 전처리 가이드 (미완)]]></title>
            <link>https://velog.io/@no-glass-otacku/Azure-Machine-Learning-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@no-glass-otacku/Azure-Machine-Learning-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Fri, 24 Apr 2026 00:41:19 GMT</pubDate>
            <description><![CDATA[<p>5.3</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Azure Machine Learning] 실습 환경 세팅 가이드]]></title>
            <link>https://velog.io/@no-glass-otacku/Azure-Machine-Learning-%EC%8B%A4%EC%8A%B5-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85-%EA%B0%80%EC%9D%B4%EB%93%9C-2l997vvb</link>
            <guid>https://velog.io/@no-glass-otacku/Azure-Machine-Learning-%EC%8B%A4%EC%8A%B5-%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85-%EA%B0%80%EC%9D%B4%EB%93%9C-2l997vvb</guid>
            <pubDate>Thu, 23 Apr 2026 05:16:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>대상 독자:</strong> Azure ML을 처음 접하는 분, 머신러닝 실습 환경 구축이 처음인 분<br><strong>사전 조건:</strong> Azure 계정 또는 실습용 계정 보유</p>
</blockquote>
<hr>
<h2 id="📋-목차">📋 목차</h2>
<ol>
<li><a href="#1-azure-portal-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%A1%9C%EA%B7%B8%EC%9D%B8">Azure Portal 접속 및 로그인</a></li>
<li><a href="#2-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EA%B7%B8%EB%A3%B9-%EC%84%A0%ED%83%9D">리소스 그룹 선택</a></li>
<li><a href="#3-azure-machine-learning-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EC%83%9D%EC%84%B1">Azure Machine Learning 리소스 생성</a></li>
<li><a href="#4-ml-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-%EC%8B%9C%EC%9E%91">ML 스튜디오 시작</a></li>
<li><a href="#5-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%85%8B-%EB%93%B1%EB%A1%9D">데이터셋 등록</a></li>
<li><a href="#6-%EC%BB%B4%ED%93%A8%ED%8A%B8-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1">컴퓨트 인스턴스 생성</a></li>
</ol>
<hr>
<h2 id="1-azure-portal-접속-및-로그인">1. Azure Portal 접속 및 로그인</h2>
<h3 id="1-1-포털-접속">1-1. 포털 접속</h3>
<ol>
<li>구글에서 <strong>&quot;Azure Portal&quot;</strong> 을 검색하거나, 주소창에 <code>portal.azure.com</code>을 직접 입력합니다.</li>
<li><strong>로그인</strong> 버튼을 클릭하여 본인 계정으로 로그인합니다.<ul>
<li>수업/실습용으로 배포받은 계정이 있다면 해당 계정을 사용합니다.</li>
<li>계정이 없다면 <strong>&quot;무료 체험 시작하기&quot;</strong> 를 통해 체험 계정을 만들 수 있습니다.</li>
</ul>
</li>
</ol>
<blockquote>
<p>💡 <strong>첫 로그인 시 주의:</strong> 실습 계정으로 처음 로그인하는 경우, 모바일 기기에 <strong>Microsoft Authenticator</strong> 앱을 설치하고 2단계 인증(2-Factor Authentication)을 완료해야 합니다. Google Play 또는 App Store에서 &quot;Microsoft Authenticator&quot;를 검색하여 설치하세요.</p>
</blockquote>
<hr>
<h2 id="2-리소스-그룹-선택">2. 리소스 그룹 선택</h2>
<p>리소스 그룹은 Azure에서 관련된 리소스들을 하나로 묶어 관리하는 단위입니다. 
실습 후 삭제할 때 리소스 그룹만 삭제하면 됨.</p>
<h3 id="2-1-리소스-그룹으로-이동">2-1. 리소스 그룹으로 이동</h3>
<ol>
<li>Azure Portal 메인 화면에서 상단 <strong>&quot;Azure 서비스&quot;</strong> 영역의 <strong>&quot;리소스 그룹&quot;</strong> 아이콘을 클릭합니다.</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/1009b7f3-57d7-488c-8102-a5d136e938b2/image.png" width="600"></th>
</tr>
</thead>
</table>
<ol start="2">
<li>리소스 그룹 만들기
리소스 그룹 이름은 자유롭게 짓고, 지역은 한국 외 다른 지역을 해도 상관없음.</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/1166ff18-78d0-40ed-a9f0-816b18bf4ede/image.png" alt="이미지1" width="300"></th>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/b3c50c37-08cb-4732-b4a6-88ecac7af96d/image.png" alt="이미지2" width="500"></th>
</tr>
</thead>
</table>
<ol start="3">
<li>검색 결과에서 본인의 리소스 그룹을 클릭하여 선택합니다.</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/b29c2350-d5f2-4d4f-a00d-9a52775522bd/image.png" width="500"></th>
</tr>
</thead>
</table>
<hr>
<h2 id="3-azure-machine-learning-리소스-생성">3. Azure Machine Learning 리소스 생성</h2>
<p>이제 본격적으로 머신러닝 작업을 수행할 <strong>Azure Machine Learning 워크스페이스</strong>를 만듭니다.</p>
<h3 id="3-1-리소스-만들기-시작">3-1. 리소스 만들기 시작</h3>
<ol>
<li>선택한 리소스 그룹의 <strong>&quot;+ 만들기&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/34fae91e-7d93-496a-a04f-8feb2c15eab1/image.png" width="500"></th>
</tr>
</thead>
</table>
<ol start="2">
<li>Marketplace 검색창에 <strong>&quot;기계 학습&quot;</strong> 또는 <strong>&quot;machine learning&quot;</strong> 을 입력하고 검색합니다. 
검색 결과 중 <strong>&quot;Azure Machine Learning&quot;</strong> (Microsoft 제공)을 선택합니다.</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/dbb591d8-7522-4ebb-8ce5-ed41eee3bb4a/image.png" width="250"></th>
</tr>
</thead>
</table>
<hr>
<h3 id="3-2-워크스페이스-세부-정보-입력">3-2. 워크스페이스 세부 정보 입력</h3>
<ol>
<li><strong>기본(Basic)</strong> 탭에서 아래 항목을 입력합니다.<blockquote>
<p>💡 <strong>이름 규칙:</strong> 이름은 <code>A000-xxxxxxx</code> 또는 <code>B000-xxxxxxx</code> 형태로 지정하세요. 스토리지 계정, 키 자격 증명 모음 등 부속 리소스들은 워크스페이스 이름에 따라 <strong>자동으로 생성</strong>됩니다.</p>
</blockquote>
</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/90524d9d-f799-40e0-a5f2-606945ec17aa/image.png" width="700"></th>
</tr>
</thead>
<tbody><tr>
<td>2. 하단의 <strong>&quot;검토 + 만들기&quot;</strong> 버튼을 클릭합니다.</td>
</tr>
</tbody></table>
<hr>
<h3 id="3-3-유효성-검사-및-배포">3-3. 유효성 검사 및 배포</h3>
<ol>
<li><p>유효성 검사가 통과되면 <strong>&quot;✅ 유효성 검사 통과&quot;</strong> 메시지를 확인합니다.</p>
</li>
<li><p>화면 하단의 <strong>&quot;만들기&quot;</strong> 버튼을 클릭합니다.</p>
</li>
<li><p>배포가 시작되며, <strong>&quot;배포 진행 중...&quot;</strong> 화면이 표시됩니다. 완료까지 수 분 정도 소요됩니다.</p>
</li>
</ol>
<blockquote>
<p>📌 <strong>배포(Deployment)란?</strong> 개발 환경에서 설정한 리소스를 실제로 사용할 수 있는 환경(제품 환경)으로 생성해 주는 과정입니다.</p>
</blockquote>
<ol start="4">
<li>&quot;<strong>배포가 완료됨</strong>&quot; 메시지가 나타나면, 화면 하단의 <strong>&quot;리소스로 이동&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/no-glass-otacku/post/1ad3edfa-47fb-443e-acb9-3cfe7d3e7c11/image.png" width="350"></th>
</tr>
</thead>
</table>
<hr>
<h2 id="4-ml-스튜디오-시작">4. ML 스튜디오 시작</h2>
<p><strong>Azure Machine Learning Studio</strong>는 머신러닝 작업을 수행하기 위한 종합 웹 포털입니다.</p>
<ol>
<li>리소스 개요 화면 중앙 하단의 <strong>&quot;Studio 시작하기&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/11319e66-e867-4e37-ae90-ad21776df0a8/image.png" alt=""></p>
<blockquote>
<p>💡 <strong>ML 스튜디오에서 할 수 있는 것들</strong></p>
<ul>
<li><strong>Notebooks</strong>: Python 코드로 직접 머신러닝 구현</li>
<li><strong>Automated ML</strong>: 머신러닝을 자동으로 구현</li>
<li><strong>Designer</strong>: 드래그-앤-드롭 방식으로 머신러닝 파이프라인 구현 -&gt; 우리가 할 것</li>
</ul>
</blockquote>
<hr>
<h2 id="5-데이터셋-등록">5. 데이터셋 등록</h2>
<p>머신러닝 작업을 시작하기 전에, 분석할 <strong>데이터를 클라우드 환경에 업로드</strong>해야 합니다.</p>
<h3 id="5-1-데이터-메뉴-접근">5-1. 데이터 메뉴 접근</h3>
<ol>
<li>ML 스튜디오 좌측 네비게이션 메뉴에서 <strong>&quot;Data&quot;</strong> 를 클릭합니다.</li>
<li><strong>&quot;+ Create&quot;</strong> 버튼을 클릭하여 데이터 자산 생성을 시작합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/fae39385-22c6-4772-81e8-aa75c1bd23a4/image.png" alt=""></p>
<hr>
<h3 id="5-2-데이터-타입-설정-data-type">5-2. 데이터 타입 설정 (Data type)</h3>
<ol>
<li><strong>이름(Name)</strong> 을 입력합니다.<ul>
<li>예: <code>Automobile_price_data</code></li>
<li>⚠️ 개인 정보가 유추될 수 있는 이름은 사용하지 마세요.</li>
</ul>
</li>
<li><strong>Description</strong>에 데이터에 대한 간략한 설명을 입력합니다. (예: <code>자동차 가격 데이터</code>)</li>
<li><strong>Type</strong>은 <strong>&quot;Tabular&quot;</strong> 를 선택합니다. (데이터를 표 형식으로 표현)</li>
<li><strong>&quot;Next&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/96dd88f2-3445-4e97-879f-2148ce653c39/image.png" alt=""></p>
<hr>
<h3 id="5-3-데이터-소스-선택-data-source">5-3. 데이터 소스 선택 (Data source)</h3>
<ol>
<li>데이터를 가져올 소스를 선택하는 화면에서 <strong>&quot;From local files&quot;</strong> 를 선택합니다.</li>
<li><strong>&quot;Next&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/a088cb10-9d6e-4825-ad6b-e0c6372c4a59/image.png" alt=""></p>
<hr>
<h3 id="5-4-저장소-타입-선택-destination-storage-type">5-4. 저장소 타입 선택 (Destination storage type)</h3>
<ol>
<li><strong>Datastore type</strong>은 <strong>&quot;Azure Blob Storage&quot;</strong> 로 유지합니다.</li>
<li>목록에서 <strong>&quot;workspaceblobstore&quot;</strong> 가 선택된 상태를 확인합니다.<ul>
<li>Blob Storage는 대량의 비정형 데이터를 저장하는 데 특화된 저장소입니다. (Blob = Binary Large OBject)</li>
</ul>
</li>
<li><strong>&quot;Next&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/bec3ea4b-ef40-410e-b8d7-96ab7408659f/image.png" alt=""></p>
<hr>
<h3 id="5-5-파일-업로드-file-or-folder-selection">5-5. 파일 업로드 (File or folder selection)</h3>
<ol>
<li><strong>&quot;Upload files or folder&quot;</strong> 드롭다운을 클릭하고 <strong>&quot;Upload files&quot;</strong> 를 선택합니다.</li>
<li>로컬 PC에서 업로드할 데이터 파일(<code>imports-85.data.csv</code>)을 찾아 선택합니다.</li>
<li>파일 업로드가 완료되면 <strong>&quot;Next&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/e8b1de7f-0310-433a-a8ab-e684d2004032/image.png" alt=""></p>
<hr>
<h3 id="5-6-세팅-확인-settings">5-6. 세팅 확인 (Settings)</h3>
<p>업로드된 파일의 파싱 설정을 확인합니다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>값</th>
</tr>
</thead>
<tbody><tr>
<td>File format</td>
<td>Delimited</td>
</tr>
<tr>
<td>Delimiter</td>
<td>Comma</td>
</tr>
<tr>
<td>Encoding</td>
<td>UTF-8</td>
</tr>
<tr>
<td>Column headers</td>
<td>⚠️ <strong>No headers</strong> (이 데이터는 컬럼 이름(헤더 행)이 없어서 변경 필요! 데이터마다 적합하게 선택해주세요)</td>
</tr>
<tr>
<td>Skip rows</td>
<td>None</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/eba6b016-5b98-45ed-b1e8-d9a88e3bd18d/image.png" alt=""></p>
<p>화면 하단의 미리보기 표에서 데이터가 올바르게 파싱되는지 확인 후 <strong>&quot;Next&quot;</strong> 를 클릭합니다.</p>
<hr>
<h3 id="5-7-스키마-확인-schema">5-7. 스키마 확인 (Schema)</h3>
<p>컬럼별 데이터 타입을 확인합니다. 자동으로 감지된 타입을 검토하고 <strong>필요 시 수정</strong>합니다.</p>
<ul>
<li><code>String</code>: 문자 데이터</li>
<li><code>Integer</code>: 정수 데이터</li>
<li><code>Decimal dot</code>: 실수 데이터</li>
<li><code>Boolean</code>: 논리(참/거짓) 데이터</li>
</ul>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/d07e9ca2-5f1c-40d3-98ae-bebdc9c7eebd/image.png" alt=""></p>
<p><strong>&quot;Next&quot;</strong> 버튼을 클릭합니다.</p>
<hr>
<h3 id="5-8-최종-검토-및-생성-review">5-8. 최종 검토 및 생성 (Review)</h3>
<p>지금까지 입력한 모든 설정 정보를 최종 검토합니다.</p>
<ul>
<li>이상이 없으면 <strong>&quot;Create&quot;</strong> 버튼을 클릭합니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/98db44b7-1373-4f72-a3d5-0813791cd040/image.png" alt=""></p>
<hr>
<h2 id="6-컴퓨트-인스턴스-생성">6. 컴퓨트 인스턴스 생성</h2>
<p><strong>컴퓨트 대상(Compute Target)</strong> 은 머신러닝 모델을 학습시킬 때 실제로 연산을 수행하는 <strong>컴퓨팅 자원</strong>입니다. 실습에서는 가상 머신처럼 사용할 수 있는 <strong>컴퓨팅 인스턴스</strong>를 생성합니다.</p>
<h3 id="6-1-컴퓨트-메뉴-접근">6-1. 컴퓨트 메뉴 접근</h3>
<ol>
<li>ML 스튜디오 좌측 네비게이션 메뉴 하단 <strong>&quot;Manage&quot;</strong> 섹션에서 <strong>&quot;Compute&quot;</strong> 를 클릭합니다.</li>
<li>상단 탭에서 <strong>&quot;Compute instances&quot;</strong> 가 선택된 상태를 확인합니다.</li>
<li><strong>&quot;+ New&quot;</strong> 버튼을 클릭합니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/6b07dd25-b232-4e17-9665-f26a8a170af3/image.png" alt=""></p>
<hr>
<h3 id="6-2-컴퓨트-인스턴스-세부-설정">6-2. 컴퓨트 인스턴스 세부 설정</h3>
<ol>
<li><p><strong>Compute name</strong> 을 입력합니다.</p>
<ul>
<li>예: <code>A000Compute1</code></li>
<li>Azure 데이터센터 전체에서 고유한 이름이어야 합니다.</li>
<li>⚠️ 이름의 마지막을 <code>-</code> 또는 <code>-숫자</code> 형태로 끝내면 안 됩니다.</li>
</ul>
</li>
<li><p><strong>Virtual machine type</strong> 은 <strong>&quot;CPU&quot;</strong> 를 선택합니다.</p>
</li>
<li><p><strong>Virtual machine size</strong> 에서 사용할 VM 사양을 선택합니다.</p>
<ul>
<li>실습 권장 사양: 2 cores짜리 아무거나</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/06f69e8a-5a87-4042-aca6-08497f741d5b/image.png" alt=""></p>
<ol start="4">
<li><strong>&quot;Review + Create&quot;</strong> 버튼을 클릭합니다.</li>
<li>생성에 몇 분 정도 소요됩니다.</li>
</ol>
<hr>
<h3 id="6-3-생성-완료-확인">6-3. 생성 완료 확인</h3>
<p>Compute 목록 화면에서 Status가 <strong>&quot;Creating&quot;</strong> → <strong>&quot;Running&quot;</strong> 으로 변경되면 정상적으로 생성된 것입니다.</p>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/d71be06d-1655-472e-b6de-91148faa27c3/image.png" alt="">
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/e5cecffc-7027-4941-8247-e0dafd6d4056/image.png" alt=""></p>
<blockquote>
<p>💡 <strong>비용 절감 팁:</strong> 컴퓨팅 인스턴스는 실행 중일 때 비용이 발생합니다. 실습이 끝나면 <strong>Stop</strong> 버튼을 눌러 인스턴스를 중지하는 습관을 들이세요. 기본 설정으로 <strong>60분 비활성 시 자동 종료</strong>가 활성화되어 있습니다.</p>
</blockquote>
<hr>
<p>이제 <strong>머신러닝 디자이너(Designer)</strong> 를 열고 본격적인 실습을 시작할 준비가 완료된 것입니다! 🎉
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/cd3d88de-d91c-4722-86ce-5c9be58bbf73/image.png" alt=""></p>
<hr>
<blockquote>
<p>📝 <strong>다음 포스팅 예고:</strong> Azure ML 디자이너로 파이프라인 구축 가이드라인</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[MS Data School 4기 얼리버드 면접 및 합격 후기]]></title>
            <link>https://velog.io/@no-glass-otacku/MS-Data-School-4%EA%B8%B0-%EC%96%BC%EB%A6%AC%EB%B2%84%EB%93%9C-%EB%A9%B4%EC%A0%91-%EB%B0%8F-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@no-glass-otacku/MS-Data-School-4%EA%B8%B0-%EC%96%BC%EB%A6%AC%EB%B2%84%EB%93%9C-%EB%A9%B4%EC%A0%91-%EB%B0%8F-%ED%95%A9%EA%B2%A9-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 23 Apr 2026 04:50:23 GMT</pubDate>
            <description><![CDATA[<h3 id="제출-서류">제출 서류</h3>
<p>얼리버드로 신청했는데 덕분에 서류는 면제되었고 구글폼으로 이 두 가지만 제출하면 됐다.</p>
<blockquote>
<ol start="17">
<li>근무지/재직기간/업무내용 등 자유롭게 작성하여 주시기 바랍니다. </li>
<li>자기소개 및 지원동기를 자유롭게 작성하여 주시기 바랍니다.</li>
</ol>
</blockquote>
<p>이 내용을 바탕으로 질문 안함. 그냥 참고만 하시는 듯.</p>
<h3 id="기초역량-테스트">기초역량 테스트</h3>
<p>면접 대기 중에는 기초역량 테스트 링크를 주는데 파이썬 기본 문법 물어봄 (<strong>합격이랑 관계 없음</strong>)
ex) 수정이 안되는 자료형은? 튜플</p>
<h3 id="면접">면접</h3>
<p>면접 난이도는 별로 안높았음.
다른 면접 후기들에서 나와있는 것과 동일한 질문을 함.
Teams 온라인 면접이고 면접관님 2<del>3명에 질문은 한분만 하시고 동시에 면접을 본 인원은 2명(둘 다 합격). 다른 분들은 3</del>4명이서 본 팀도 있다고 했다.</p>
<blockquote>
<h4 id="면접-질문">면접 질문</h4>
</blockquote>
<ul>
<li><strong>지원동기</strong></li>
<li><strong>팀플 시 가장 힘든 부분과 해결방법</strong>: 제출한 내용보니까 하고 싶은게 좀 뚜렷한 편이신 것 같던데 만약 별로 하고 싶지 않은 주제를 해야하거나 팀플에서 갈등이 생기면 어떻게 할거에요? 
(팀플 하다가 그만두고 나간 케이스가 있어서 방지하기 위해 팀플에서 갈등이 있어도 해결하려는 의지가 있는 인성의 소유자인지 확인하는 질문) (농업, 해양 생태계, 야생동물 이런거 하고 싶다고 적었더니 하고 싶은게 뚜렷하다고 말씀하신듯)</li>
<li><strong>기본적인 IT 관련 질문 1개</strong>: 클라우드 컴퓨팅이란 무엇인가요? 클라우드란? 
(우리팀은 두명이라 같은 질문으로 답했는데 여러명인 팀은 질문을 다르게 준다고) 
(같이 면접 본 분은 대답 잘 못했는데 합격하심)</li>
<li><strong>개발 경험 있는지?</strong> (웹 프론트, 안드로이드 앱, 데이터 분석용 파이썬 등과 기본 SC 강의를 수강한 적 있다고 말함)</li>
<li><strong>부캠이 끝나고 어떤걸 할건지</strong> 미래계획? (4학년이 남아서 학교를 마저 다니고 부캠에서 배운걸 바탕으로 캡스톤을 잘 마무리하고 바로 취업할 수 있도록 하겠다고 답함. 다른 교육을 더 듣는다던지 그런 답변은 안해야 함.)</li>
</ul>
<h3 id="about-me">About me</h3>
<p>참고로 나는 반(半)전공자(전공자이긴한데 산업공학 계열이라 애매해서)로 3학년 수료 후 휴학하고 지원했고 20대 중반에 경력도 없었음.</p>
<h2 id="나중에-합격자들과-대화를-나눠보니-비전공자-분들도-있으시고-경력자도-있고-나처럼-휴학생들도-있었다">나중에 합격자들과 대화를 나눠보니 비전공자 분들도 있으시고 경력자도 있고 나처럼 휴학생들도 있었다.</h2>
<p>얼리버드 기준으로 면접 대기자 방에 100명이 넘었다는데 Data school이 30명 가량 되니까 AI, Data school 다 합쳐서 60명 정도 된다고 가정했을 때 떨어진 사람이 절반은 된다는게 믿기지 않았다. 
왜냐면 면접도 쉽고 코테도 안보니까... 그리고 나도 그렇게 면접을 잘봤다고 생각하지는 않는데(누구나 할법한 식상한 말만 했음..) 내가 합격했길래 지원하면 다 붙는줄 알았는데 그게 아닌게 놀라웠다.</p>
<p>더 궁금한 내용은 댓글 남겨주시면 추가해보겠습니다 끝</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Query plan 비교]]></title>
            <link>https://velog.io/@no-glass-otacku/Query-plan-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@no-glass-otacku/Query-plan-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Tue, 14 Apr 2026 02:16:39 GMT</pubDate>
            <description><![CDATA[<h3 id="📊-사례별-코드-차이-및-실행-결과-분석">📊 사례별 코드 차이 및 실행 결과 분석</h3>
<h3 id="1-1번-vs-2번-비교-select-다이어트의-중요성">1. [1번 vs 2번] 비교: &quot;SELECT 다이어트의 중요성&quot;</h3>
<div style="display: flex; gap: 10px;">
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/e21fde4f-a25a-42bf-abcc-9c05486a865a/image.png" width="60%">
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/914e81cc-6545-48ae-a833-af94fce2fd02/image.png" width="80%">
</div>

<ul>
<li><strong>차이점:</strong> 1번은 <code>SELECT *</code> (모든 컬럼)을 가져오고, 2번은 인덱스에 있는 <code>name</code> 컬럼만 가져옵니다.</li>
<li><strong>Plan 변화:</strong> <strong>Bitmap Heap Scan</strong> (1번) → <strong>Index Only Scan</strong> (2번)</li>
<li><strong>결과 해석:</strong> 1번은 인덱스(<code>name</code>)에 없는 <code>id</code>, <code>email</code>, <code>signup_date</code>를 찾으러 실제 데이터 테이블(Heap)로 가야만 했습니다. 반면, 2번은 <strong>Planner(플래너)</strong>가 &quot;인덱스만 봐도 답이 다 있네?&quot;라고 판단해 테이블 근처에도 가지 않았습니다.</li>
<li><strong>학습 한 줄 정리:</strong> <strong>불필요한 컬럼을 버리는 &#39;SELECT 다이어트&#39;만으로도 디스크 방문(Heap Scan)을 막을 수 있습니다.</strong></li>
</ul>
<hr>
<h3 id="2-2번-vs-3번-비교-인덱스-범위를-벗어난-필터">2. [2번 vs 3번] 비교: &quot;인덱스 범위를 벗어난 필터&quot;</h3>
<div style="display: flex; gap: 10px;">
<img src="이미지 링크" width="n%" height="n%">
<img src="이미지 링크" width="n%" height="n%">
</div>

<ul>
<li><strong>차이점:</strong> 3번은 <code>WHERE</code> 절에 인덱스에 없는 <code>signup_date</code> 조건을 추가하고, <code>SELECT</code> 결과에도 포함했습니다.</li>
<li><strong>Plan 변화:</strong> <strong>Index Only Scan</strong> (2번) → <strong>Bitmap Heap Scan</strong> (3번)</li>
<li>[cite_start]<strong>결과 해석:</strong> 3번은 인덱스에 없는 <code>signup_date</code>를 확인하고 출력해야 하므로, <strong>Executor(실행기)</strong>가 실제 데이터 파일 영역을 뒤져야 하는 <strong>비싼 비용(Cost)</strong>을 지불하게 된 것입니다[cite: 324, 331].</li>
<li><strong>학습 한 줄 정리:</strong> <strong>인덱스에 없는 컬럼을 조건이나 결과에 넣는 순간, &#39;인덱스 전용 스캔&#39;의 마법은 풀립니다.</strong></li>
</ul>
<hr>
<h3 id="3-3번-vs-4번-비교-단일-인덱스-vs-복합-인덱스">3. [3번 vs 4번] 비교: &quot;단일 인덱스 vs 복합 인덱스&quot;</h3>
<div style="display: flex; gap: 10px;">
<img src="이미지 링크" width="n%" height="n%">
<img src="이미지 링크" width="n%" height="n%">
</div>

<ul>
<li><strong>차이점:</strong> 3번은 <code>name</code>만 있는 인덱스고, 4번은 <code>(name, signup_date)</code>가 묶인 <strong>복합 인덱스</strong>입니다.</li>
<li><strong>Plan 변화:</strong> <strong>Bitmap Heap Scan</strong> (3번) → <strong>Index Only Scan</strong> (4번)</li>
<li><strong>결과 해석:</strong> 4번은 자주 같이 쓰이는 두 컬럼을 하나로 묶어버렸습니다. [cite_start]플래너는 이제 <code>signup_date</code> 정보까지 인덱스 안에서 모두 찾을 수 있게 되어 다시 가장 빠른 경로를 선택했습니다[cite: 322, 324].</li>
<li><strong>학습 한 줄 정리:</strong> <strong>자주 함께 쓰이는 필터 조건들은 &#39;복합 인덱스&#39;로 묶어야 플래너가 가장 &#39;싼 비용&#39;의 계획을 세웁니다.</strong></li>
</ul>
<hr>
<h3 id="4--1번-vs-5번-비교-인덱스-전용-스캔의-마법-covering-index">4.  [1번 vs 5번] 비교: &quot;인덱스 전용 스캔의 마법, Covering Index&quot;</h3>
<div style="display: flex; gap: 10px;">
<img src="이미지 링크" width="n%" height="n%">
<img src="이미지 링크" width="n%" height="n%">
</div>

<ul>
<li><p><strong>차이점 (Condition):</strong> * <strong>1번:</strong> <code>SELECT</code>로 테이블의 모든 컬럼(<code>id</code>, <code>name</code>, <code>email</code>, <code>signup_date</code>)을 요구하지만, 인덱스는 <code>name</code> 하나만 가지고 있습니다.</p>
<ul>
<li><strong>5번:</strong> <code>SELECT id, name, email</code>을 요구하며, 인덱스 생성 시 <code>INCLUDE (id, email)</code>를 사용하여 결과에 필요한 데이터를 인덱스 안에 미리 복사해 두었습니다.</li>
</ul>
</li>
<li><p><strong>Plan 변화 (Query Plan):</strong> <strong>Bitmap Heap Scan</strong> → <strong>Index Only Scan</strong></p>
</li>
<li><p><strong>결과 해석 (Interpretation):</strong> * <strong>1번</strong>은 인덱스에 없는 나머지 정보를 찾기 위해 <strong>Planner(플래너)</strong>가 실제 <strong>Data Files(Heap)</strong> 영역을 뒤져야 한다고 판단했습니다. 이 과정에서 디스크 읽기(<code>read</code>)가 발생하며 비용이 급증합니다.</p>
<ul>
<li><strong>5번</strong>은 여러 컬럼을 요구함에도 불구하고, 필요한 모든 데이터가 인덱스라는 &#39;보조 주머니&#39;안에 다 들어있습니다. <strong>Executor(실행기)</strong>는 무거운 테이블 파일(<code>base/</code> 디렉토리) 근처에도 가지 않고 인덱스만으로 모든 응답을 끝냈습니다.</li>
</ul>
</li>
</ul>
<ul>
<li><strong>학습 한 줄 정리:</strong> <strong>검색 조건은 아니지만 결과로 자주 쓰이는 데이터는 <code>INCLUDE</code>로 인덱스에 태워두면, &#39;테이블 방문&#39; 없는 초고속 조회가 가능합니다.</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] Pivot, 세로 데이터를 가로로!]]></title>
            <link>https://velog.io/@no-glass-otacku/SQL-Pivot-%EC%84%B8%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B0%80%EB%A1%9C%EB%A1%9C</link>
            <guid>https://velog.io/@no-glass-otacku/SQL-Pivot-%EC%84%B8%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B0%80%EB%A1%9C%EB%A1%9C</guid>
            <pubDate>Fri, 10 Apr 2026 05:30:05 GMT</pubDate>
            <description><![CDATA[<h2 id="📊-sql-세로-데이터를-가로로-pivot-완벽-가이드-case-when-vs-crosstab">📊 [SQL] 세로 데이터를 가로로! Pivot 완벽 가이드 (CASE WHEN vs CROSSTAB)</h2>
<p>데이터를 분석하다 보면, 아래로 길게 나열된 행(Row) 데이터를 엑셀 피벗 테이블처럼 옆으로(Column) 펼쳐야 할 때가 많습니다. SQL에서 이를 구현하는 두 가지 정석 방법을 정리합니다.</p>
<hr>
<h3 id="1-가장-클래식한-방법-case-when--sum">1. 가장 클래식한 방법: <code>CASE WHEN</code> + <code>SUM</code></h3>
<p>별도의 설치 없이 모든 SQL 엔진에서 사용할 수 있는 가장 범용적인 방식입니다.</p>
<h4 id="✅-작동-원리">✅ 작동 원리</h4>
<p>특정 조건일 때만 값을 남기고, 아니면 0을 주어 합산하는 방식입니다.</p>
<pre><code class="language-sql">SELECT 
    prod_month,
    SUM(CASE WHEN breeds_nm = &#39;Cornish&#39; THEN total_sum ELSE 0 END) AS &quot;Cornish_Total&quot;,
    SUM(CASE WHEN breeds_nm = &#39;Cochin&#39; THEN total_sum ELSE 0 END) AS &quot;Cochin_Total&quot;,
    SUM(total_sum) AS monthly_total
FROM breeds_prod
GROUP BY prod_month;</code></pre>
<h4 id="⚠️-주의할-점-삽질-포인트">⚠️ 주의할 점 (삽질 포인트)</h4>
<ul>
<li><strong>별칭 사용 주의</strong>: <code>SELECT</code> 절에서 만든 별칭(예: <code>prod_month</code>)은 엔진에 따라 <code>GROUP BY</code>에서 바로 쓸 수 있지만, <code>SUM</code>으로 만든 결과물 별칭은 <code>GROUP BY</code>에서 쓸 수 없습니다. (닭이 먼저냐 달걀이 먼저냐의 싸움 방지!)</li>
<li><strong>0 또는 NULL</strong>: 합계 시 <code>ELSE 0</code>을 써야 <code>NULL</code> 때문에 전체 합계가 <code>NULL</code>이 되는 대참사를 막을 수 있습니다.</li>
</ul>
<hr>
<h3 id="2-전문가의-도구-tablefunc와-crosstab">2. 전문가의 도구: <code>tablefunc</code>와 <code>crosstab</code></h3>
<p>PostgreSQL을 사용한다면 <code>tablefunc</code> 확장 기능을 사용하여 더 세련되게 피벗할 수 있습니다.</p>
<h4 id="🛠-준비-단계-import와-동일">🛠 준비 단계 (Import와 동일)</h4>
<pre><code class="language-sql">CREATE EXTENSION IF NOT EXISTS tablefunc;</code></pre>
<ul>
<li><strong>의미</strong>: DB에 &#39;피벗 전용 도구 상자&#39;를 들여놓는 것. 한 번만 실행하면 됩니다.</li>
</ul>
<h4 id="🍱-crosstab의-3단-도시락-규칙">🍱 crosstab의 &#39;3단 도시락&#39; 규칙</h4>
<p><code>crosstab</code> 함수는 반드시 <strong>딱 3개의 컬럼</strong>으로 구성된 쿼리를 입력받아야 합니다.</p>
<ol>
<li><strong>첫 번째 컬럼</strong>: 행의 기준 (예: 부화일자)</li>
<li><strong>두 번째 컬럼</strong>: 열(컬럼명)이 될 카테고리 (예: 성별)</li>
<li><strong>세 번째 컬럼</strong>: 실제 칸을 채울 값 (예: 마릿수)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/no-glass-otacku/post/da25ed3e-9446-4200-8b45-4dc983df81d9/image.png" alt=""></p>
<h4 id="✅-실행-코드-예시">✅ 실행 코드 예시</h4>
<pre><code class="language-sql">SELECT * FROM crosstab(
    --count는 bigint를 내뱉으므로 int로 형변환
    &#39;SELECT hatchday, gender, count(chick_no)::int 
     FROM chick_info 
     GROUP BY hatchday, gender 
     ORDER BY hatchday, gender&#39; -- 반드시 정렬(ORDER BY) 필요!
) AS 별칭(
    hatchday date,  -- 첫 번째 컬럼 (기준)
    male int,       -- 두 번째 컬럼 (값)
   female int       -- 세 번째 컬럼 (값)
); --열 이름의 타입이 아니라 내부 값의 타입을 적어야한단다</code></pre>
<h4 id="⚠️-주의할-점-삽질-포인트-1">⚠️ 주의할 점 (삽질 포인트)</h4>
<ul>
<li><strong>쿼리는 문자열이다</strong>: <code>crosstab</code>은 함수이므로 내부 쿼리를 통째로 <strong>작은따옴표(<code>&#39;</code>)</strong>로 감싸서 문자열 데이터로 넘겨야 합니다.</li>
<li><strong>설계도(<code>AS</code>) 필수</strong>: <code>crosstab</code>은 결과 모양을 스스로 알지 못하므로, <code>AS 별칭(컬럼명 타입, ...)</code>으로 결과표의 구조를 명시해줘야 합니다.</li>
<li><strong>따옴표 규칙</strong>: 설계도 안에서 컬럼 이름에 작은따옴표를 쓰면 에러가 납니다. (<code>&#39;male&#39;</code> ❌ → <code>male</code> ⭕)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 실행순서: SELECT 별칭을 GROUP BY에서 쓸 수 있는 이유?]]></title>
            <link>https://velog.io/@no-glass-otacku/SQL-%EC%8B%A4%ED%96%89%EC%88%9C%EC%84%9C-SELECT-%EB%B3%84%EC%B9%AD%EC%9D%84-GROUP-BY%EC%97%90%EC%84%9C-%EC%93%B8-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@no-glass-otacku/SQL-%EC%8B%A4%ED%96%89%EC%88%9C%EC%84%9C-SELECT-%EB%B3%84%EC%B9%AD%EC%9D%84-GROUP-BY%EC%97%90%EC%84%9C-%EC%93%B8-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Fri, 10 Apr 2026 02:07:11 GMT</pubDate>
            <description><![CDATA[<h3 id="1-의문의-시작">1. 의문의 시작</h3>
<p>SQL을 배울 때 우리는 분명 실행 순서를 다음과 같이 배웁니다.</p>
<ol>
<li><strong>FROM / JOIN</strong></li>
<li><strong>WHERE</strong></li>
<li><strong>GROUP BY</strong></li>
<li><strong>HAVING</strong></li>
<li><strong>SELECT</strong> (이때 별칭 <code>AS</code>가 생성됨)</li>
<li><strong>ORDER BY</strong></li>
</ol>
<p>그런데 아래 쿼리는 에러 없이 아주 잘 작동합니다.</p>
<pre><code class="language-sql">SELECT to_char(prod_date, &#39;YYYYMM&#39;) AS prod_month, -- 5번: 별칭 생성
       SUM(total_sum) AS monthly_total
FROM breeds_prod
GROUP BY prod_month; -- 3번: 그런데 어떻게 5번에서 만든 별칭을 미리 쓰지?</code></pre>
<p>논리적으로는 3번(<code>GROUP BY</code>) 단계에서 아직 존재하지도 않는 5번(<code>SELECT</code>)의 별명을 부르는 <strong>&#39;타임 패러독스&#39;</strong>가 발생합니다. 왜 그런 걸까요?
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/b148a349-a1f0-4717-84f8-71f85fd5e1de/image.png" alt=""></p>
<h3 id="2-범인은-똑똑한-sql-엔진">2. 범인은 똑똑한 &#39;SQL 엔진&#39;</h3>
<p>우리가 코드를 던지면 데이터베이스 엔진(PostgreSQL, MySQL 등)은 실행 전 코드를 전체적으로 훑어보는 <strong>파싱(Parsing)</strong> 단계를 거칩니다.</p>
<ul>
<li><strong>엔진의 판단:</strong> &quot;<code>GROUP BY</code>에 쓰인 <code>prod_month</code>가 뭐지? 아, <code>SELECT</code> 절을 보니 단순 날짜 변환식이네? 내가 미리 계산해서 그룹을 묶어줄게!&quot;</li>
<li><strong>실제 실행:</strong> 논리적 순서는 지키되, 엔진이 개발자의 편의를 위해 <code>SELECT</code> 절의 단순 별칭을 <strong>미리 참조</strong>할 수 있게 설계되어 있기 때문입니다.</li>
</ul>
<h3 id="3-무조건-다-되는-건-아니다-중요한-차이">3. &quot;무조건&quot; 다 되는 건 아니다! (중요한 차이)</h3>
<p>여기서 가장 중요한 포인트는 <strong>&#39;어떤&#39;</strong> 별칭이냐는 것입니다.</p>
<h4 id="✅-가능한-경우-단순-변환-별칭">✅ 가능한 경우: 단순 변환 별칭</h4>
<ul>
<li><code>to_char</code>, <code>SUBSTRING</code>, <code>REPLACE</code> 등 <strong>행 하나하나에 바로 적용되는 식</strong>의 별칭은 <code>GROUP BY</code>에서 쓸 수 있습니다. 엔진이 미리 계산하기 쉽기 때문이죠.</li>
</ul>
<h4 id="❌-불가능한-경우-집계-함수의-별칭">❌ 불가능한 경우: 집계 함수의 별칭</h4>
<ul>
<li><code>SUM</code>, <code>AVG</code>, <code>COUNT</code> 등으로 만든 별칭은 <strong>절대로 <code>GROUP BY</code>에서 쓸 수 없습니다.</strong></li>
<li><strong>이유:</strong> 집계 함수는 &#39;그룹화가 완료된 후&#39;에야 값이 결정됩니다. 그룹을 묶기 위해 그룹이 묶여야 나오는 값을 가져다 쓰는 것은 불가능하기 때문입니다. (순환 참조 오류)</li>
</ul>
<h3 id="4-정리하며">4. 정리하며</h3>
<ul>
<li><strong>SQL의 논리적 순서</strong>와 <strong>엔진의 실제 최적화 순서</strong>는 다를 수 있다.</li>
<li><strong>PostgreSQL</strong> 같은 현대적인 DB는 <code>SELECT</code>의 별칭을 <code>GROUP BY</code>에서 쓸 수 있게 지원한다. (단순 식에 한함)</li>
<li>이 원리를 이해하면 쿼리 가독성을 높이면서도 성능 최적화를 고려하는 데이터 엔지니어로 성장할 수 있다!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hybrid RAG (Vector+Graph RAG) 의문과 답 기록]]></title>
            <link>https://velog.io/@no-glass-otacku/Hybrid-RAG-VectorGraph-RAG-%EC%9D%98%EB%AC%B8%EA%B3%BC-%EB%8B%B5-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@no-glass-otacku/Hybrid-RAG-VectorGraph-RAG-%EC%9D%98%EB%AC%B8%EA%B3%BC-%EB%8B%B5-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Sun, 29 Mar 2026 11:42:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>구조가 깨졌지만 메타데이터로 page 확인이 가능하다면 그냥 문서에서 다시 해당 page를 가져와서 분할을 시도하는게 더 낫지 않아? 그렇게 안하는 이유가 뭐야?</p>
</blockquote>
<p>실무적인 데이터 엔지니어링 환경에서 <strong>&#39;원천 페이지 재호출&#39;</strong>을 하지 않고 <strong>&#39;강제 분할&#39;</strong>을 선택하는 데에는 몇 가지 현실적인 이유가 있습니다.</p>
<h3 id="1-같은-기계parser는-같은-불량을-냅니다">1. &quot;같은 기계(Parser)는 같은 불량을 냅니다&quot;</h3>
<p>가장 결정적인 이유입니다. <code>utils.py</code>에서 8,000자가 넘는 노드가 발생했다는 건, 그 페이지를 읽어낸 <strong>PDF 파서(Parser)가 이미 해당 페이지의 구조를 해석하는 데 실패했다</strong>는 뜻입니다.</p>
<h3 id="2-비용과-속도-공정의-병목-현상">2. 비용과 속도: &quot;공정의 병목 현상&quot;</h3>
<p>데이터 파이프라인은 보통 <strong>[원천 파일 → 전체 텍스트 추출 → 노드 분할 → 필터링]</strong>의 단방향 흐름으로 설계됩니다.</p>
<ul>
<li><strong>IO 부하:</strong> 필터링 단계에서 갑자기 &quot;이 페이지 다시 읽어와!&quot;라고 역행하면, 닫혀 있던 PDF 파일을 다시 열고, 해당 페이지를 찾고, 다시 파싱하는 무거운 작업이 추가됩니다.</li>
<li><strong>비효율:</strong> 만약 1,000페이지 중 100페이지에서 이런 문제가 생기면, 전체 처리 속도가 기하급수적으로 느려지는 <strong>병목(Bottleneck)</strong> 현상이 발생합니다.</li>
</ul>
<h3 id="3-데이터-심폐소생술의-관점">3. &quot;데이터 심폐소생술&quot;의 관점</h3>
<p>우리가 하려는 <strong>강제 분할(Force-chopping)</strong>은 &#39;완벽한 복구&#39;라기보다는 <strong>&#39;최소한의 검색 가능성 확보&#39;</strong>에 가깝습니다.</p>
<ul>
<li><strong>구조는 포기, 키워드는 보존:</strong> 비록 문단은 깨졌어도 8,000자 안의 단어들은 살아있습니다. 1,000자씩 잘라두면, 나중에 사용자가 그 안의 키워드로 질문했을 때 검색 결과에는 걸리게 됩니다.</li>
<li><strong>LLM의 추론 능력:</strong> 요즘 LLM은 앞뒤가 조금 잘린 문맥이라도 검색된 조각들을 합쳐서 주면, 그 안에서 의미를 찾아내 답변을 생성합니다.</li>
</ul>
<p>정말로 &quot;원천 페이지에서 다시 제대로 가져오고 싶다&quot;면, 단순 재시도가 아니라 <strong>&#39;다른 공정(다른 파서)&#39;</strong>을 써야 합니다.</p>
<blockquote>
<p><strong>[개선된 공정 제안]</strong></p>
<ol>
<li>기본 파서로 전체 파싱 진행.</li>
<li>8,000자 초과 노드 발생 시 <strong>&#39;비상 공정&#39;</strong> 가동.</li>
<li>해당 페이지를 텍스트가 아닌 <strong>이미지(OCR)</strong>로 읽거나, 훨씬 정교한(하지만 느린) <strong>AI 기반 파서</strong>로 해당 페이지만 다시 읽기.</li>
</ol>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[🐍 [Python] 왜 set과 dict는 똑같이 { }를 쓸까? (해시 테이블) + 헷갈리는 list method]]></title>
            <link>https://velog.io/@no-glass-otacku/Python-%EC%99%9C-set%EA%B3%BC-dict%EB%8A%94-%EB%98%91%EA%B0%99%EC%9D%B4-%EB%A5%BC-%EC%93%B8%EA%B9%8C-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-list-method</link>
            <guid>https://velog.io/@no-glass-otacku/Python-%EC%99%9C-set%EA%B3%BC-dict%EB%8A%94-%EB%98%91%EA%B0%99%EC%9D%B4-%EB%A5%BC-%EC%93%B8%EA%B9%8C-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-list-method</guid>
            <pubDate>Thu, 26 Mar 2026 05:46:26 GMT</pubDate>
            <description><![CDATA[<p>파이썬을 공부하다 보면 문득 드는 의문이 있습니다. <strong>&quot;집합(set)이랑 딕셔너리(dict)는 왜 둘 다 중괄호 <code>{}</code>를 쓰지?&quot;</strong> 단순히 우연일까요? 아니면 깊은 뜻이 숨겨져 있을까요? 오늘은 그 이유와 함께 리스트 메서드들의 네이밍 비화까지 정리해 보겠습니다.</p>
<hr>
<h3 id="1-중괄호-의-공통분모-해시-테이블hash-table">1. 중괄호 <code>{}</code>의 공통분모: 해시 테이블(Hash Table)</h3>
<p>두 자료형이 같은 괄호를 공유하는 가장 큰 이유는 내부적으로 <strong>데이터를 저장하는 메커니즘이 똑같기 때문</strong>입니다.</p>
<ol>
<li>해시 테이블 기반: 처음부터 끝까지 뒤지는 게 아니라, 주소를 계산해서 한 번에 찾아가는 방식을 사용 (Random Access)</li>
<li>중복 불허: 해시 테이블 특성상 set은 값의 중복을 허용하지 않고, dict는 키(key)의 중복을 허용하지 않습니다. 
데이터가 저장될 위치가 데이터 그 자체(값 또는 키)에 의해 이미 결정되어 있기 때문입니다. (Hash Function)*</li>
</ol>
<ul>
<li><strong>해시 함수(Hash Function):</strong> 어떤 데이터를 넣든 고유한 주소값(인덱스)으로 변환해주는 계산기입니다.</li>
</ul>
<h4 id="💡-여기서-잠깐-중복-불허와-해시의-상관관계">💡 여기서 잠깐! &quot;중복 불허&quot;와 &quot;해시&quot;의 상관관계</h4>
<p>해시 테이블은 데이터의 값 자체가 저장 위치를 결정합니다. </p>
<ol>
<li><code>hash(&quot;apple&quot;)</code>이 <code>5번 방</code>이라면, 사과는 무조건 5번에 들어갑니다.</li>
<li>또 다른 사과를 넣으려 해도 다시 <code>5번 방</code>을 가리킵니다.</li>
<li>가서 보니 이미 &quot;사과&quot;가 있네? <strong>그럼 새로 저장하지 않습니다.</strong>
이것이 바로 <code>set</code>과 <code>dict(key)</code>에서 중복이 불가능한 기술적 이유입니다.</li>
</ol>
<hr>
<h3 id="2-set-vs-dictionary-어떻게-구분할까">2. Set vs Dictionary: 어떻게 구분할까?</h3>
<p>파이썬은 중괄호 안의 <strong>콜론(<code>:</code>)</strong> 유무로 둘을 구분합니다.</p>
<table>
<thead>
<tr>
<th align="left">자료형</th>
<th align="left">표기법</th>
<th align="left">특징</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Dictionary</strong></td>
<td align="left"><code>{&#39;key&#39;: &#39;value&#39;}</code></td>
<td align="left"><strong>키:값</strong> 쌍으로 저장 (데이터 중심)</td>
</tr>
<tr>
<td align="left"><strong>Set</strong></td>
<td align="left"><code>{&#39;value1&#39;, &#39;value2&#39;}</code></td>
<td align="left"><strong>값</strong>만 단독 존재 (존재 여부 중심)</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>⚠️ 주의:</strong> 빈 괄호 <code>{}</code>는 파이썬에서 <strong>Dictionary</strong>로 인식됩니다. 빈 집합을 만들고 싶다면 반드시 <code>set()</code>을 사용하세요!</p>
</blockquote>
<hr>
<h3 id="3-왜-list의-pop은-인덱스고-remove는-값일까">3. 왜 List의 <code>pop()</code>은 인덱스고, <code>remove()</code>는 값일까?</h3>
<p>이 질문의 답은 <strong>영어 단어의 본래 의미</strong>와 <strong>자료구조의 역사</strong>에 있습니다.</p>
<h4 id="①-pop-튀어나오다-값을-나에게-쥐어줌">① <code>pop()</code>: 튀어&#39;나오다&#39; (값을 나에게 쥐어줌)</h4>
<ul>
<li><strong>유래:</strong> 데이터가 쌓여 있는 <strong>스택(Stack)</strong>에서 맨 위의 것을 꺼내는 동작입니다.</li>
<li><strong>특징:</strong> &quot;어디에 있는 것을 꺼낼까?&quot;가 중요합니다. 그래서 <strong>인덱스</strong>를 받으며, 삭제한 값을 우리에게 다시 <strong>반환(return)</strong>해줍니다.</li>
</ul>
<h4 id="②-remove-무엇을-제거하다">② <code>remove()</code>: &#39;무엇을&#39; 제거하다</h4>
<ul>
<li><strong>유래:</strong> 방에 있는 쓰레기를 치우듯, 특정 대상을 지목해서 없애는 동작입니다.</li>
<li><strong>특징:</strong> &quot;어디에 있든 상관없으니 &#39;이 값&#39;을 지워줘!&quot;라는 의미입니다. 그래서 <strong>값(Value)</strong>을 인덱스 대신 받으며, 별도의 값을 반환하지 않습니다.</li>
</ul>
<hr>
<p><strong>마치며</strong>
언어의 문법과 메서드 이름에는 개발자들의 의도가 담겨 있습니다. 단어의 뜻과 내부 구조를 연결해서 이해해야 덜 헷갈리고 기억에 남습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pydantic (지정한 타입으로 변환해주는 데이터 검증 라이브러리)]]></title>
            <link>https://velog.io/@no-glass-otacku/Pydantic-%EC%A7%80%EC%A0%95%ED%95%9C-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%B4%EC%A3%BC%EB%8A%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B2%80%EC%A6%9D-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@no-glass-otacku/Pydantic-%EC%A7%80%EC%A0%95%ED%95%9C-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%B4%EC%A3%BC%EB%8A%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B2%80%EC%A6%9D-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Wed, 25 Mar 2026 07:46:40 GMT</pubDate>
            <description><![CDATA[<h2 id="1-pydantic이-왜-필요한가요">1. Pydantic이 왜 필요한가요?</h2>
<p>파이썬은 동적 타이핑 언어라 자료형이 자유롭다는 장점이 있지만, 데이터가 복잡해지면 사고가 나기 쉽습니다. </p>
<ul>
<li><strong>기존 방식:</strong> 데이터가 들어올 때마다 <code>if isinstance(age, int)</code>나 <code>try-except</code>를 수십 개씩 써서 검증해야 했습니다.</li>
<li><strong>Pydantic 방식:</strong> &quot;데이터는 이런 모양이어야 해!&quot;라고 <strong>모델(Model)</strong>을 딱 한 번 정의해두면, Pydantic이 알아서 검증하고 변환까지 해줍니다.</li>
</ul>
<hr>
<h2 id="2-코드-비교-일반-클래스-vs-pydantic">2. 코드 비교: 일반 클래스 vs Pydantic</h2>
<h3 id="일반적인-방식">일반적인 방식</h3>
<pre><code class="language-python">class User:
    def __init__(self, id: int, name: str):
        self.id = id
        self.name = name

# 문자열 &quot;123&quot;을 넣으면? 그냥 문자열로 저장됨 (나중에 계산할 때 에러 날 수 있음)
user = User(id=&quot;123&quot;, name=&quot;가영&quot;) </code></pre>
<h3 id="pydantic-방식">Pydantic 방식</h3>
<pre><code class="language-python">from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str

# 문자열 &quot;123&quot;을 넣어도? 자동으로 정수 123으로 변환!
user = User(id=&quot;123&quot;, name=&quot;가영&quot;)
print(user.id) # 결과: 123 (int 타입)

# 만약 &quot;abc&quot;처럼 숫자로 못 바꾸는 걸 넣으면? 바로 에러(ValidationError) 발생!</code></pre>
<hr>
<h2 id="3-pydantic의-3가지-핵심-기능">3. Pydantic의 3가지 핵심 기능</h2>
<h3 id="①-데이터-검증-validation">① 데이터 검증 (Validation)</h3>
<p>정해진 타입이 아니거나 값이 범위를 벗어나면 즉시 에러를 발생시킵니다. 예를 들어 나이는 <code>int</code>여야 하고 <code>0</code>보다 커야 한다는 규칙을 아주 쉽게 정할 수 있습니다.</p>
<h3 id="②-데이터-변환-parsingcoercion">② 데이터 변환 (Parsing/Coercion)</h3>
<p>이게 정말 편리한 기능입니다! <code>&quot;100&quot;</code>이라는 문자열이 들어와도, 모델이 <code>int</code>를 원한다면 <strong>Pydantic이 알아서 숫자로 바꿔줍니다.</strong> (아까 우리가 <code>int(input())</code>으로 수동 변환했던 과정을 자동으로 해주는 거죠.)</p>
<h3 id="③-압도적인-성능">③ 압도적인 성능</h3>
<p>Pydantic V2부터는 핵심 로직이 <strong>Rust(러스트)</strong>라는 매우 빠른 언어로 작성되어, 파이썬에서 가장 빠른 데이터 검증 라이브러리로 평가받고 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[
Python 상기시키기]]></title>
            <link>https://velog.io/@no-glass-otacku/Python-%EC%83%81%EA%B8%B0%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@no-glass-otacku/Python-%EC%83%81%EA%B8%B0%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Wed, 25 Mar 2026 06:31:54 GMT</pubDate>
            <description><![CDATA[<h1 id="python-타입-시스템의-이해-dynamic-typing과-메모리-참조-모델">[Python] 타입 시스템의 이해: Dynamic Typing과 메모리 참조 모델</h1>
<p>타입 시스템을 비교 분석하고, 특히 파이썬(Python) 환경에서 변수가 메모리를 관리하는 내부 동작 원리를 살펴보는 글.</p>
<hr>
<h2 id="1-타입-시스템-전용-상자-vs-포스트잇">1. 타입 시스템: 전용 상자 vs 포스트잇</h2>
<p>변수의 자료형(Type)을 결정하는 방식에 따라 언어의 특성이 결정된다. 이를 비유를 통해 명확히 구분할 수 있다.</p>
<h3 id="11-정적-타이핑static-typing-전용-상자-모델">1.1 정적 타이핑(Static Typing): 전용 상자 모델</h3>
<ul>
<li><strong>비유:</strong> 변수는 특정 크기와 용도가 정해진 <strong>&#39;전용 상자&#39;</strong>와 같다.</li>
<li><strong>특징:</strong> 정수형(int) 상자를 만들면 오직 정수만 담을 수 있으며, 실행 전(Compile 타임)에 상자의 규격을 결정한다.</li>
<li><strong>언어:</strong> C, C++, Java 등.</li>
<li><strong>장점:</strong> 안정성이 높고 실행 속도가 빠르나, 유연성이 부족하다.</li>
</ul>
<h3 id="12-동적-타이핑dynamic-typing-포스트잇-모델">1.2 동적 타이핑(Dynamic Typing): 포스트잇 모델</h3>
<ul>
<li><strong>비유:</strong> 변수는 데이터에 붙이는 <strong>&#39;포스트잇(이름표)&#39;</strong>과 같다.</li>
<li><strong>특징:</strong> 데이터가 먼저 존재하고, 변수는 그 데이터가 무엇이든 상관없이 이름표만 붙인다. 자료형은 실행 시점(Runtime)에 결정된다.</li>
<li><strong>언어:</strong> Python, JavaScript 등.</li>
<li><strong>장점:</strong> 개발 속도가 빠르고 유연하지만, 실행 전까지 타입 에러를 발견하기 어렵다.</li>
</ul>
<p>-&gt; 아래는 파이썬(동적 타이핑)에 대한 내용</p>
<hr>
<h2 id="2-파이썬의-변수-할당과-재할당-메커니즘">2. 파이썬의 변수 할당과 재할당 메커니즘</h2>
<p>동적 타이핑 언어인 파이썬에서 <code>a = 5</code> 이후 <code>a = &quot;Hello&quot;</code>와 같이 자료형을 변경하며 재할당할 때의 내부 동작은 다음과 같다.</p>
<ol>
<li><strong>객체 생성:</strong> 메모리 공간에 <code>5</code>라는 정수 객체가 생성된다.</li>
<li><strong>참조(Binding):</strong> 변수 <code>a</code>라는 포스트잇이 <code>5</code> 객체에 부착된다.</li>
<li><strong>재할당:</strong> 새로운 <code>&quot;Hello&quot;</code> 문자열 객체가 생성되고, 포스트잇 <code>a</code>를 기존의 <code>5</code>에서 떼어내어 <code>&quot;Hello&quot;</code>에 다시 부착한다.</li>
<li><strong>가비지 컬렉션(GC):</strong> 어떠한 이름표도 붙어있지 않은(참조 카운트가 0인) 기존의 <code>5</code> 객체는 파이썬의 청소부인 가비지 컬렉터에 의해 메모리에서 삭제된다.</li>
</ol>
<hr>
<h2 id="3-별칭aliasing-현상과-mutable-객체의-주의점">3. 별칭(Aliasing) 현상과 Mutable 객체의 주의점</h2>
<p>하나의 객체에 여러 개의 이름표(변수)를 붙일 때 발생하는 현상을 <strong>Aliasing</strong>이라고 하며, 이는 데이터 무결성에 큰 영향을 미친다.</p>
<h3 id="31-수박watermelon-예시를-통한-고찰">3.1 수박(Watermelon) 예시를 통한 고찰</h3>
<p>두 명의 팀원이 하나의 수박을 공유하는 상황을 코드로 재현하면 다음과 같다.</p>
<pre><code class="language-python"># 1. 하나의 리스트 객체 생성 후 my_watermelon 이름표 부착
my_watermelon = [&quot;수박&quot;]

# 2. 새로운 이름표 our_food를 같은 객체에 부착 (Aliasing)
our_food = my_watermelon

# 3. id() 함수로 메모리 주소 확인 시 동일함 (id(my_watermelon) == id(our_food))
print(my_watermelon is our_food) # True</code></pre>
<h3 id="32-side-effect의-발생">3.2 Side Effect의 발생</h3>
<p>리스트와 같이 내부 수정이 가능한 <strong>Mutable(가변)</strong> 객체는 Aliasing 상태에서 의도치 않은 변경을 초래할 수 있다.</p>
<ul>
<li><strong>상황:</strong> 한 명이 <code>my_watermelon.append(&quot;꿀맛&quot;)</code>을 수행하면, 실제 데이터 자체가 수정된다.</li>
<li><strong>결과:</strong> 아무 작업도 하지 않은 다른 팀원의 <code>our_food</code>를 확인했을 때도 <code>[&quot;수박&quot;, &quot;꿀맛&quot;]</code>으로 변경되어 나타난다.</li>
</ul>
<p>이는 대규모 데이터 파이프라인 설계 시 원본 데이터를 보호하기 위해 반드시 <strong>깊은 복사(Deep Copy)</strong>와 같은 기법이 필요한 이유가 된다.</p>
<hr>
<h2 id="4-함수-인자-전달-메커니즘-키워드-인자keyword-argument">4. 함수 인자 전달 메커니즘: 키워드 인자(Keyword Argument)</h2>
<p>함수 호출 시 매개변수(Parameter)에 값을 직접 지정하여 전달하는 방식은 새로운 변수를 생성하는 것과는 구분되는 <strong>&#39;설정(Configuration)&#39;</strong>의 개념으로 이해해야 한다.</p>
<ul>
<li><p><strong>키워드 인자:</strong> 함수 정의 시 미리 지정된 매개변수 이름을 명시하여 인자(Argument)를 전달하는 방식이다. 인자의 순서와 상관없이 특정 매개변수에 값을 할당할 수 있다.</p>
</li>
<li><p><strong>주요 매개변수 분석 (<code>print</code> 함수 예시):</strong></p>
<ul>
<li><strong><code>end</code> (종료 문자):</strong> * <code>print</code> 함수 호출 종료 시 출력할 문자를 결정한다. <ul>
<li>기본값은 줄바꿈 문자(<code>\n</code>)이나, <code>end=&#39;&#39;</code>와 같이 명시함으로써 다음 출력문과의 연결 방식을 제어할 수 있다.</li>
</ul>
</li>
<li><strong><code>sep</code> (구분자, Separator):</strong> * 쉼표(<code>,</code>)로 나열된 여러 개의 출력 인자들 사이를 채울 문자를 결정한다.<ul>
<li>기본값은 공백(<code>&#39; &#39;</code>)이며, 이를 변경하여 간격을 없애거나 특정 기호(예: <code>-</code>, <code>/</code>)로 대체할 수 있다.<pre><code class="language-python"># sep 매개변수 활용 예시
print(&quot;010&quot;, &quot;1234&quot;, &quot;5678&quot;, sep=&quot;-&quot;) # 출력: 010-1234-5678</code></pre>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>작동 원리:</strong> 위 매개변수들은 새로운 메모리 공간에 영구적인 변수를 생성하는 행위가 아니다. 함수 내부에 정의된 <strong>설정값(Default Value)을 사용자 정의 값으로 일시적으로 치환</strong>하는 행위이며, 해당 함수의 실행이 종료됨과 동시에 해당 설정은 소멸한다.</p>
</li>
</ul>
<hr>
<h2 id="5-결론-및-요약-mutable-vs-immutable-구별법">5. 결론 및 요약: Mutable vs Immutable 구별법</h2>
<p>효율적인 디버깅을 위해 자료형의 가변성을 파악하는 것이 필수적이다.</p>
<ol>
<li><strong>가변성 테스트:</strong> <code>x[0] = 1</code>과 같이 인덱스를 통한 직접 수정을 시도했을 때 에러가 나면 <strong>Immutable</strong>(str, tuple 등), 성공하면 <strong>Mutable</strong>(list, dict 등)이다.</li>
<li><strong>ID 추적:</strong> 값을 변경했을 때 <code>id()</code> 값이 바뀌면 Immutable(새 집으로 이사), 그대로이면 Mutable(현 거주지에서 수리)이다.</li>
</ol>
<table>
<thead>
<tr>
<th align="left">분류</th>
<th align="left">특징</th>
<th align="left">주요 자료형</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Immutable (불변)</strong></td>
<td align="left">값 변경 시 기존 객체를 수정하지 않고 새로운 객체를 생성함</td>
<td align="left"><code>int</code>, <code>str</code>, <code>tuple</code>, <code>bool</code></td>
</tr>
<tr>
<td align="left"><strong>Mutable (가변)</strong></td>
<td align="left">기존 객체의 메모리 주소를 유지한 채 내부의 값을 변경 가능함</td>
<td align="left"><code>list</code>, <code>dict</code>, <code>set</code></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stable Diffusion (이미지 생성)]]></title>
            <link>https://velog.io/@no-glass-otacku/Stable-Diffusion-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@no-glass-otacku/Stable-Diffusion-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Wed, 25 Mar 2026 05:45:35 GMT</pubDate>
            <description><![CDATA[<h3 id="서버를-다시-켤-때">서버를 다시 켤 때</h3>
<pre><code># webui.sh 파일이 있는 메인 폴더로 이동
cd ~/stable-diffusion-webui-forge

#가상 환경 입장
. ./venv/bin/activate

#서버 실행(엔진 켜기)
./webui.sh --share --enable-insecure-extension-access --gradio-auth 아이디:비번</code></pre><p>&lt;주의&gt;
Gradio 주소는 매번 바뀝니다: --share 옵션을 쓰면 실행할 때마다 <a href="https://xxxx.gradio.live">https://xxxx.gradio.live</a> 주소가 새로 생성됨. 이전에 썼던 주소로 접속하면 안열림.</p>
<h3 id="storage-만들기">Storage 만들기</h3>
<p>Azure에서 &#39;리소스 만들기&#39; &gt; &#39;스토리지 계정&#39;&gt; &#39;만들기&#39;&gt; 기본 옵션으로 생성</p>
<p>생성한 스토리지 계정으로 들어가서 왼쪽에 있는 바에서 &#39;데이터 스토리지&#39;&gt; &#39;컨테이너&#39;&gt; &#39;컨테이너 추가&#39;&gt; 기본 옵션으로 생성</p>
<h3 id="blob-storage와-동기화하기">Blob Storage와 동기화하기</h3>
<p>&lt;주의&gt; 
이미지를 하나라도 생성하고 동기화 진행해야 함.</p>
<blockquote>
<p>파일을 동기화 시키기 위해서는 인증 관련 정보가 필요합니다.
액세스 키를 이용해 접근하는 방법, Entra ID 를 통해 진행하는 방법 등 여러가지 방법이 있지만, 보안이 강화되어 있는 SAS 토큰을 통해 파일을 동기화 시킵니다.
<img src="https://velog.velcdn.com/images/no-glass-otacku/post/f30b5948-40b9-44df-bd6f-72556b31df6f/image.png" alt=""> -&gt; 3개 다 선택해야 함.
아래로 스크롤하여 &#39;SAS 및 연결 문자열 생성&#39; 버튼 누르고 아래에 있는 SAS 토큰을 복사!</p>
<blockquote>
<p>SAS란? Shared Access Signature 
임시 출입증, 호텔 카드키처럼 일시적으로 권한을 줄때 사용</p>
</blockquote>
</blockquote>
<p>cmd로 돌아와서 아래 위치로 이동</p>
<pre><code>cd ~/stable-diffusion-webui-forge</code></pre><p>해당 위치에서 upload라는 이름의 sh파일 생성</p>
<pre><code>vi upload </code></pre><p>그리고 sh파일 내부에 다음 내용을 넣기</p>
<pre><code>#!/bin/bash
# Azure Storage 계정 이름
STORAGE_ACCOUNT_NAME=&quot;fimtrusstorage4&quot; --&gt; 아까 생성한 storage 이름
# Azure Blob 컨테이너 이름
CONTAINER_NAME=&quot;stable-diffusion-images&quot; --&gt; storage 내부에 생성한 컨테이너 이름

# 동기화할 로컬 디렉토리 (현재 위치)
LOCAL_PATH=&quot;/home/azureuser/stable-diffusion-webui-forge/outputs/&quot;

# SAS TOKEN 정보 입력
SAS_TOKEN=&quot;&lt;SAS_TOKEN&gt;&quot; --&gt; 발급한 SAS 토큰 넣기
# Azure Blob Storage URL

# Azure Blob Storage URL
BLOB_URL=&quot;https://${STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${CONTAINER_NAME}?${SAS_TOKEN}&quot;

# azcopy 명령어 실행 (동기화)
echo &quot;현재 위치의 모든 파일을 Azure Blob Storage &#39;${CONTAINER_NAME}&#39; 컨테이너와 동기화 시작...&quot;
azcopy sync &quot;$LOCAL_PATH&quot; &quot;$BLOB_URL&quot; --delete-destination=false --recursive --include-pattern=&quot;*.png&quot;
#azcopy sync &quot;$LOCAL_PATH&quot; &quot;$BLOB_URL&quot; --delete-destination=false --recursive --include-pattern=&quot;*.png;*.jpg;*.jpeg;*.webp;*.bmp;*.tiff;*.gif;*.mp4;*.mov;*.avi;*.mkv&quot;

if [ $? -eq 0 ]; then
  echo &quot;동기화 완료.&quot;
else
  echo &quot;동기화 실패.&quot;
fi</code></pre><p>마지막으로 sh uplaod를 입력하면 동기화 완료.</p>
<h3 id="storage에서-사진-확인">Storage에서 사진 확인</h3>
<h3 id="여러-모델-사용하기">여러 모델 사용하기</h3>
<p><a href="https://stable-diffusion-art.com/models/">https://stable-diffusion-art.com/models/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ ch14 chains]]></title>
            <link>https://velog.io/@no-glass-otacku/ch14-chains</link>
            <guid>https://velog.io/@no-glass-otacku/ch14-chains</guid>
            <pubDate>Sat, 21 Feb 2026 19:33:46 GMT</pubDate>
            <description><![CDATA[<h2 id="문서요약-체인">문서요약 체인</h2>
<h4 id="📋-문서-요약-방식별-선택-가이드">📋 문서 요약 방식별 선택 가이드</h4>
<table>
<thead>
<tr>
<th align="left">요약 방식</th>
<th align="left">선택 기준</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Stuff</strong></td>
<td align="left">문서가 짧고 한 번에 끝내고 싶을 때</td>
</tr>
<tr>
<td align="left"><strong>Map-Reduce</strong></td>
<td align="left">문서가 매우 길고 빠른 처리가 필요할 때</td>
</tr>
<tr>
<td align="left"><strong>Map-Refine</strong></td>
<td align="left">문서의 전체 맥락 연결이 중요할 때</td>
</tr>
<tr>
<td align="left"><strong>Chain of Density</strong></td>
<td align="left">가장 정보 밀도가 높은 완벽한 요약이 필요할 때</td>
</tr>
<tr>
<td align="left"><strong>Clustering</strong></td>
<td align="left">내용이 중복되는 방대한 자료를 정리할 때</td>
</tr>
</tbody></table>
<h4 id="1-stuff-몽땅-집어넣기">1. Stuff (몽땅 집어넣기)</h4>
<p>원리: 모든 문서를 하나의 프롬프트에 통째로 넣고 &quot;요약해줘&quot;라고 시키는 가장 단순한 방식입니다.</p>
<p>장점: 가장 빠르고 비용이 저렴하며, 문맥을 한 번에 파악하기 좋습니다.</p>
<p>단점: 문서가 너무 길면 AI가 읽을 수 있는 한계를 초과하여 에러가 발생합니다.</p>
<pre><code>stuff_chain = create_stuff_documents_chain(llm, prompt)</code></pre><h4 id="2-map-reduce-분할-요약-후-병합">2. Map-Reduce (분할 요약 후 병합)</h4>
<p>원리: 문서를 여러 조각으로 나눠서 각각 요약한 뒤(Map), 그 요약본들을 다시 모아서 최종 요약을 만듭니다(Reduce).</p>
<p>장점: 아주 방대한 양의 문서도 처리할 수 있고, 각 조각을 동시에 요약하므로 속도가 빠릅니다.</p>
<p>단점: 전체를 관통하는 미묘한 맥락이 요약 과정에서 사라질 수 있습니다.</p>
<pre><code>from langchain_core.runnables import chain


@chain
def map_reduce_chain(docs):
    map_llm = ChatOpenAI(
        temperature=0,
        #단순 반복 작업이 많으므로, 속도가 빠르고 가격이 *저렴한 가성비 모델*
        model_name=&quot;gpt-4o-mini&quot;,
    )

    # map prompt 다운로드
    map_prompt = hub.pull(&quot;teddynote/map-prompt&quot;)

    # map chain 생성
    map_chain = map_prompt | map_llm | StrOutputParser()

    # *병렬 처리 (batch)*
    doc_summaries = map_chain.batch(docs)

    # reduce prompt 다운로드
    reduce_prompt = hub.pull(&quot;teddynote/reduce-prompt&quot;)
    reduce_llm = ChatOpenAI(
        #문맥 파악 능력 위해 성능이 더 좋은 *고성능 모델*
        model_name=&quot;gpt-4o&quot;,
        temperature=0,
        callbacks=[StreamingCallback()],
        streaming=True,
    )

    reduce_chain = reduce_prompt | reduce_llm | StrOutputParser()

    return reduce_chain.invoke({&quot;doc_summaries&quot;: doc_summaries, &quot;language&quot;: &quot;Korean&quot;})
</code></pre><p>Map-Reduce에서 체인을 분리하는 이유는 각 단계의 특성에 맞는 AI 모델을 선택하여 비용을 절감하고, 병렬 처리를 통해 대량의 문서를 빠르게 요약하기 위함입니다.</p>
<h4 id="3-map-refine-점진적-보완-요약">3. Map-Refine (점진적 보완 요약)</h4>
<p>원리: 첫 번째 조각을 요약한 뒤, 그 요약본을 다음 조각과 함께 넘겨서 내용을 업데이트합니다. 이 과정을 마지막 조각까지 반복하며 요약을 완성해 나갑니다.</p>
<p>장점: 앞뒤 맥락이 잘 이어지며, 요약의 디테일이 살아있습니다.</p>
<p>단점: 순차적으로 작업해야 해서 속도가 느리고, 뒤로 갈수록 초기 내용이 희미해질 수 있습니다.</p>
<pre><code>ed!from langchain_core.runnables import chain


@chain
def map_refine_chain(docs):

    # map chain 생성
    map_summary = hub.pull(&quot;teddynote/map-summary-prompt&quot;)

    map_chain = (
        map_summary
        | ChatOpenAI(
            model_name=&quot;gpt-4o-mini&quot;,
            temperature=0,
        )
        | StrOutputParser()
    )

    input_doc = [{&quot;documents&quot;: doc.page_content, &quot;language&quot;: &quot;Korean&quot;} for doc in docs]

    # 첫 번째 프롬프트, ChatOpenAI, 문자열 출력 파서를 연결하여 체인을 생성합니다.
    doc_summaries = map_chain.batch(input_doc)

    refine_prompt = hub.pull(&quot;teddynote/refine-prompt&quot;)

    refine_llm = ChatOpenAI(
        model_name=&quot;gpt-4o-mini&quot;,
        temperature=0,
        callbacks=[StreamingCallback()],
        streaming=True,
    )

    refine_chain = refine_prompt | refine_llm | StrOutputParser()

    previous_summary = doc_summaries[0]

    for current_summary in doc_summaries[1:]:

        previous_summary = refine_chain.invoke(
            {
                &quot;previous_summary&quot;: previous_summary,
                &quot;current_summary&quot;: current_summary,
                &quot;language&quot;: &quot;Korean&quot;,
            }
        )
        print(&quot;\n\n-----------------\n\n&quot;)

    return previous_summary</code></pre><p>-&gt; 원래 Refine 방식은 <strong>이전 요약 + 다음 원문</strong>을 결합하는 것이 정석입니다. 
하지만 본 실습 코드에서는 토큰(입력량) 제한을 고려하여, <strong>각 페이지를 먼저 요약(batch)한 뒤 요약본끼리 Refine 하는 구조</strong>를 취하고 있습니다. 요약본 + 요약본</p>
<h4 id="4-chain-of-density-밀도-보완-반복-요약">4. Chain of Density (밀도 보완 반복 요약)</h4>
<p>&quot;Chain of Density&quot; (CoD) 프롬프트는 GPT-4를 사용한 요약 생성을 개선하기 위해 개발된 기법입니다.</p>
<p>원리: 요약을 한 번으로 끝내지 않고 여러 번 반복 실행합니다. 이때 핵심 정보(Entity)가 누락되지 않았는지 체크하며 요약문의 밀도를 점점 높여갑니다.</p>
<p>장점: 인간이 작성한 요약과 비슷한 밀도를 가진 고품질 요약본이 나옵니다. 원문의 앞부분에 치우치는 경향(lead bias)이 덜합니다.</p>
<p>단점: 여러 번 반복 실행하므로 비용과 시간이 많이 듭니다.</p>
<h4 id="5-clustering-map-refine-군집화-기반-정예-요약">5. Clustering-Map-Refine (군집화 기반 정예 요약)</h4>
<p>원리: 문서 조각(Chunk)들을 비슷한 내용끼리 그룹(클러스터)으로 묶습니다. 각 그룹에서 가장 대표적인 중심 문서들만 골라 Refine 방식으로 요약합니다.</p>
<p>장점: 중복된 내용은 과감히 생략하고 방대한 양의 핵심 주제를 효율적으로 요약할 수 있습니다.</p>
<p>단점: 그룹을 나누는 &#39;클러스터링&#39; 과정이 추가되어 기술적으로 더 복잡합니다.</p>
<h2 id="sql쿼리-만드는-체인-create_sql_query_chain">SQL쿼리 만드는 체인: create_sql_query_chain</h2>
<p>사용자의 질문(자연어)을 LLM이 분석하여, 데이터를 추출하기 위한 최적의 <strong>SQL 쿼리를 스스로 생성</strong>합니다. 이렇게 생성된 쿼리를 통해 데이터베이스를 실시간으로 조회하여 정확한 정보를 찾아냅니다.</p>
<pre><code>from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool

# SQLite 데이터베이스에 연결
db = SQLDatabase.from_uri(&quot;sqlite:///data/finance.db&quot;)
llm = ChatOpenAI(model=&quot;gpt-3.5-turbo&quot;, temperature=0)

# 생성한 쿼리를 실행하는 도구
execute_query = QuerySQLDataBaseTool(db=db)

# SQL 쿼리 생성
# 기본 프롬프트가 내장되어 있지만 매개변수로 Prompt를 추가해서 엉뚱한 쿼리 생성방지에 도움을 줄 수 있음 (아래에 서술)
write_query = create_sql_query_chain(llm, db)

# 생성한 쿼리를 실행하기 위한 체인을 생성합니다.
chain = write_query | execute_query</code></pre><blockquote>
<p> Prompt를 직접 작성해서 넣는 법
prompt = PromptTemplate.from_template(&quot;프롬프트 작성&quot;).partial(dialect=db.dialect) 
chain = create_sql_query_chain(llm, db, prompt)</p>
</blockquote>
<pre><code>chain.invoke({&quot;question&quot;: &quot;테디의 이메일을 조회하세요&quot;})</code></pre><p>위 방법으로는 답변이 <strong>단답형 형식으로 출력</strong>되므로 더 친절한 답변을 받기 위해서는 답변을 LLM으로 증강생성 하면 된다.</p>
<blockquote>
<p>-965.7 -&gt; &#39;테디의 transaction의 합계는 -965.7 입니다.&#39;</p>
</blockquote>
<pre><code>#LCEL 문법의 체인 사용
#친절한 답변 생성
from operator import itemgetter
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough

answer_prompt = PromptTemplate.from_template(
    &quot;&quot;&quot;Given the following user question, corresponding SQL query, and SQL result, answer the user question.

Question: {question}
SQL Query: {query}
SQL Result: {result}
Answer: &quot;&quot;&quot;
)

# 지시서(prompt)를 읽고, 모델(llm)이 생각해서, 문자열(Parser)로 답해라
answer = answer_prompt | llm | StrOutputParser()

# 생성한 쿼리를 실행하고 결과를 출력하기 위한 체인을 생성합니다.
chain = (
  RunnablePassthrough
  .assign(query=write_query)
  .assign(result=itemgetter(&quot;query&quot;) | execute_query)
  | answer
)</code></pre><p>RunnablePassthrough.assign(...): 기존에 있던 데이터는 그대로 두고, 옆에 새로운 정보를 <strong>&#39;추가&#39;</strong>해서 다음 단계로 넘김</p>
<pre><code>assign(query=write_query) 실행 결과: {&quot;question&quot;: &quot;...&quot;, &quot;query&quot;: &quot;SELECT...&quot;}

result=itemgetter(&quot;query&quot;) | execute_query
#itemgetter로 query를 가져와서 execute_query로 전달 후 result를 가져옴
실행결과: {&quot;question&quot;: &quot;...&quot;, &quot;query&quot;: &quot;...&quot;, &quot;result&quot;: &quot;[(&#39;...&#39;)]&quot;} 
-&gt; answer에 저장</code></pre><h3 id="sql-agent">SQL Agent</h3>
<p>Agent를 활용하여 Sql 쿼리를 생성하고 실행 결과를 답변으로 출력이 가능합니다.</p>
<p>&#39;A와 B를 비교해줘&#39; 같이 질문이 복잡해서 한 번의 쿼리로 안 끝날 때, 에이전트는 스스로 계획을 세워 여러 번 DB를 뒤져보고 최종 답을 냅니다.</p>
<p> <strong>[생각 → 행동 → 관찰]</strong>의 과정을 반복합니다.</p>
<pre><code>from langchain_openai import ChatOpenAI
from langchain_community.utilities import SQLDatabase
from langchain_community.agent_toolkits import create_sql_agent

llm = ChatOpenAI(model=&quot;gpt-3.5-turbo&quot;, temperature=0)
db = SQLDatabase.from_uri(&quot;sqlite:///data/finance.db&quot;)

# Agent 생성
agent_executor = create_sql_agent(llm, db=db, agent_type=&quot;openai-tools&quot;, verbose=True)
agent_executor.invoke(
    {&quot;input&quot;: &quot;테디와 셜리의 transaction 의 합계를 구하고 비교하세요&quot;}
)</code></pre><p>실행 과정</p>
<pre><code>Entering new SQL Agent Executor chain...
---1 어떤 테이블 있나 확인
Invoking: `sql_db_list_tables` with `{}`
accounts, customers, transactions

---2 &#39;transactions&#39; 태이블 구조 파악
Invoking: `sql_db_schema` with `{&#39;table_names&#39;: &#39;transactions&#39;}`

CREATE TABLE transactions (
    transaction_id INTEGER, 
    account_id INTEGER, 
    amount REAL, 
    transaction_date TEXT, 
    PRIMARY KEY (transaction_id), 
    FOREIGN KEY(account_id) REFERENCES accounts (account_id)
)

/* 예시 데이터 3줄
3 rows from transactions table:
transaction_id  account_id  amount  transaction_date
1   1   74.79   2024-07-13
2   1   -224.1  2024-05-13
3   1   -128.9  2024-01-25
*/

---3 정확한 쿼리 생성 및 조회
Invoking: `sql_db_query` with `{&#39;query&#39;: &#39;SELECT account_id, SUM(amount) AS total_amount FROM transactions WHERE account_id IN (1, 2) GROUP BY account_id&#39;}`

[(1, -965.7), (2, 743.13)] 
테디의 거래 합계는 -965.7이고, 셜리의 거래 합계는 743.13입니다.

Finished chain.</code></pre><h4 id="🛠️-실무-적용-시-반드시-고려할-점">🛠️ 실무 적용 시 반드시 고려할 점</h4>
<p>LLM 기반의 SQL 에이전트는 편리하지만, 실제 서비스에 적용하려면 안정성과 효율성을 꼭 따져봐야 합니다.</p>
<ol>
<li><p><strong>보안: &quot;읽기 전용&quot;은 필수</strong>
AI가 실수로 데이터를 지우거나 수정하지 않도록, DB 접속 시 <strong>읽기 전용(Read-Only) 계정</strong>을 연결해야 합니다. 또한, 위험한 명령어(DROP, DELETE 등)가 포함되었는지 검사하는 <strong>Query Validator</strong> 단계를 추가하는 것이 일반적입니다.</p>
</li>
<li><p><strong>비용과 속도: 하이브리드 전략</strong>
에이전트는 스스로 판단하는 과정에서 AI 모델을 다회 호출(Multi-turn)하므로 API 비용 상승과 응답 지연이 발생합니다. 단순한 질문은 <strong>Chain(고정)</strong>으로, 복잡한 분석은 <strong>Agent(자율 판단)</strong>로 처리하는 설계가 효율적입니다.</p>
</li>
<li><p><strong>컨텍스트 최적화 (Context Management)</strong>
DB의 모든 구조를 AI에게 넘기면 오히려 헷갈려 할 수 있습니다. 꼭 필요한 테이블 정보와 컬럼 설명만 골라 전달하여 AI가 엉뚱한 답변(환각)을 하지 않도록 관리해야 합니다.</p>
</li>
</ol>
<h4 id="🧰-함께-살펴보면-좋은-도구들">🧰 함께 살펴보면 좋은 도구들</h4>
<p>랭체인 외에도 현업에서 자주 언급되는 대안들입니다.</p>
<p><strong>LlamaIndex</strong>: 랭체인과 양대 산맥입니다. 특히 데이터를 찾고 연결하는 기능(RAG)에 특화되어 있어 SQL 관련 작업에서도 많이 쓰입니다.</p>
<p><strong>Vanna.ai</strong>: &#39;Text-to-SQL&#39;에만 집중한 특화 툴로, 회사의 과거 쿼리 데이터를 학습시켜 정확도를 높이기 좋습니다.</p>
<p><strong>LangGraph</strong>: 랭체인의 확장판입니다. 에이전트의 사고 과정을 더 세밀하게 제어하고 싶을 때(예: &quot;먼저 승인을 받고 쿼리를 실행해&quot;) 사용합니다.</p>
<h2 id="구조화된-출력-만드는-체인-with_structured_output">구조화된 출력 만드는 체인: with_structured_output</h2>
<p>AI 답변을 파싱(글자 쪼개기)하느라 고생할 필요 없이, 처음부터 API처럼 규격에 맞는 데이터를 받는 기술.</p>
<p>사람이 아닌 &#39;프로그램&#39;이 AI의 답변을 소비할 때 사용!</p>
<h4 id="예시-4지선다형-퀴즈를-생성">예시) 4지선다형 퀴즈를 생성</h4>
<ol>
<li>AI에게 줄 <strong>&#39;데이터의 틀&#39;</strong>를 만드는 작업</li>
</ol>
<p>Quiz 클래스는 퀴즈의 질문, 난이도, 그리고 네 개의 선택지를 정의</p>
<pre><code>class Quiz(BaseModel):
    question: str = Field(..., description=&quot;퀴즈의 질문&quot;)
    level: str = Field(...)
    options: List[str] = Field(...)</code></pre><pre><code>
llm = ChatOpenAI(model=&quot;gpt-4o&quot;, temperature=0.1)
prompt = ChatPromptTemplate.from_messages(
    [
        (
            &quot;system&quot;,
            &quot;You&#39;re a world-famous quizzer and generates quizzes in structured formats.&quot;,
        ),
        (
            &quot;human&quot;,
            &quot;TOPIC 에 제시된 내용과 관련한 4지선다형 퀴즈를 출제해 주세요. 만약, 실제 출제된 기출문제가 있다면 비슷한 문제를 만들어 출제하세요.&quot;
            &quot;단, 문제에 TOPIC 에 대한 내용이나 정보는 포함하지 마세요. \nTOPIC:\n{topic}&quot;,
        ),
        (&quot;human&quot;, &quot;Tip: Make sure to answer in the correct format&quot;),
    ]
)</code></pre><ol start="2">
<li>with_structured_output(Quiz) (AI에게 틀 전달)<pre><code># 구조화된 출력을 위한 모델 생성
llm_with_structured_output = llm.with_structured_output(Quiz)
</code></pre></li>
</ol>
<h1 id="퀴즈-생성-체인-생성">퀴즈 생성 체인 생성</h1>
<p>chain = prompt | llm_with_structured_output</p>
<pre><code></code></pre><h1 id="퀴즈-생성을-요청합니다">퀴즈 생성을 요청합니다.</h1>
<p>generated_quiz = chain.invoke({&quot;topic&quot;: &quot;ADSP(데이터 분석 준전문가) 자격 시험&quot;})</p>
<h1 id="생성된-퀴즈-출력">생성된 퀴즈 출력</h1>
<p>print(f&quot;{generated_quiz.question} (난이도: {generated_quiz.level})\n&quot;)
for i, opt in enumerate(generated_quiz.options):
    print(f&quot;{i+1}) {opt}&quot;)</p>
<pre><code></code></pre><p>#결과물
다음 중 데이터 분석의 과정에서 가장 먼저 수행해야 하는 단계는 무엇인가요? (난이도: 보통)</p>
<p>1) 데이터 수집
2) 데이터 전처리
3) 문제 정의
4) 모델 평가</p>
<p>```
3. 데이터의 흐름</p>
<p>① 입력: &quot;ADSP 시험에 대한 퀴즈를 내줘&quot;라고 요청합니다.</p>
<p>② 처리: AI가 내용을 생성한 뒤, Quiz 클래스 양식에 맞게 데이터를 칸칸이 집어넣습니다.</p>
<p>③ 출력: 결과물이 문장이 아니라 generated_quiz.question처럼 변수명으로 바로 접근할 수 있는 형태로 나옵니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CH08 Embedding]]></title>
            <link>https://velog.io/@no-glass-otacku/CH08-Embedding</link>
            <guid>https://velog.io/@no-glass-otacku/CH08-Embedding</guid>
            <pubDate>Sun, 04 Jan 2026 12:06:58 GMT</pubDate>
            <description><![CDATA[<h2 id="1-openai-임베딩-모델-실습">1. OpenAI 임베딩 모델 실습</h2>
<p>주요 도구: OpenAIEmbeddings</p>
<p>지원되는 모델 목록</p>
<table>
<thead>
<tr>
<th align="left">MODEL</th>
<th align="center">PAGES PER DOLLAR</th>
<th align="center">PERFORMANCE ON MTEB EVAL</th>
<th align="center">MAX INPUT</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>text-embedding-3-small</strong></td>
<td align="center">62,500</td>
<td align="center">62.3%</td>
<td align="center">8191</td>
</tr>
<tr>
<td align="left"><strong>text-embedding-3-large</strong></td>
<td align="center">9,615</td>
<td align="center">64.6%</td>
<td align="center">8191</td>
</tr>
<tr>
<td align="left"><strong>text-embedding-ada-002</strong></td>
<td align="center">12,500</td>
<td align="center">61.0%</td>
<td align="center">8191</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left">모델명</th>
<th align="left">특징</th>
<th align="left">추천 상황</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>text-embedding-3-small</strong></td>
<td align="left">가장 경제적이며 성능도 준수함</td>
<td align="left">가성비가 중요한 대규모 서비스</td>
</tr>
<tr>
<td align="left"><strong>text-embedding-3-large</strong></td>
<td align="left">최고 성능, 가격은 가장 비쌈</td>
<td align="left">정확도가 최우선인 전문 검색 서비스</td>
</tr>
<tr>
<td align="left"><strong>text-embedding-ada-002</strong></td>
<td align="left">과거의 표준 모델</td>
<td align="left">굳이 새로 쓸 필요 없는 구형 모델</td>
</tr>
</tbody></table>
<p>어떤 모델을 사용해서 embedding 할건지 정하고 (여기서는 text-embedding-3-small) 텍스트를 임베딩하면 벡터화(숫자)되어 리스트에 담긴 것을 확인 가능합니다.</p>
<h4 id="embed_query-검색용">embed_query (검색용)</h4>
<p>사용자의 질문 단 하나를 벡터로 바꿀 때</p>
<pre><code>embeddings = OpenAIEmbeddings(model=&quot;text-embedding-3-small&quot;)
text = &quot;임베딩 테스트를 하기 위한 샘플 문장입니다.&quot;
query_result = embeddings.➡️embed_query(text)</code></pre><blockquote>
<p>[-0.00776276458054781, 0.03680367395281792, 0.019545823335647583, -0.0196656696498394, 0.017203375697135925]</p>
</blockquote>
<h4 id="embed_documents-저장용">embed_documents (저장용)</h4>
<p>많은 양의 문서를 한꺼번에 벡터로 바꿀 때
-&gt; 항상 문자열의 리스트를 입력받고, 각 문서에 대한 임베딩 벡터를 포함하는 2차원 리스트를 반환함.</p>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">embed_documents</th>
<th align="left">embed_query</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>입력 형태</strong></td>
<td align="left"><strong>리스트</strong> (List of Strings)</td>
<td align="left"><strong>문자열</strong> (Single String)</td>
</tr>
<tr>
<td align="left"><strong>주요 용도</strong></td>
<td align="left">데이터베이스(Vector Store) 구축용</td>
<td align="left">사용자의 질문(검색어) 처리용</td>
</tr>
<tr>
<td align="left"><strong>특징</strong></td>
<td align="left">여러 문서를 한꺼번에 배치(Batch) 처리</td>
<td align="left">단일 문장의 의도 파악에 최적화</td>
</tr>
</tbody></table>
<h3 id="차원dimension-지정-기능이-필요한-이유">차원(Dimension) 지정 기능이 필요한 이유?</h3>
<blockquote>
<p>임베딩 차원 지정은 <strong>검색 속도와 저장 비용을 최적화</strong>하면서도, 핵심 의미 정보를 유지하여 <strong>시스템 효율과 정확도 사이의 최적의 밸런스</strong>를 찾기 위해 필요합니다.</p>
</blockquote>
<p>&#39;dimensions=1024&#39; 옵션을 추가하여 차원 지정가능</p>
<pre><code>embeddings_1024 = OpenAIEmbeddings(model=&quot;text-embedding-3-small&quot;, ➡️dimensions=1024)
</code></pre><ul>
<li><p>비용 및 저장소 최적화: 차원을 줄이면 벡터 DB 용량이 감소하여 인프라 유지 비용이 대폭 절감됩니다.</p>
</li>
<li><p>검색 속도(Latency) 향상: 저차원 벡터는 유사도 계산 연산량이 적어 대규모 데이터셋에서 훨씬 빠른 응답 속도를 보장합니다.</p>
</li>
<li><p>정확도 밸런스: &#39;매트료슈카 임베딩&#39; 기술 덕분에 차원을 줄여도 핵심 의미 정보는 대부분 유지되므로, 성능 손실을 최소화하며 효율을 극대화할 수 있습니다.</p>
</li>
<li><p>환경 적응성: 메모리가 제한된 모바일이나 엣지 디바이스 환경에 맞춰 모델 부하를 조절할 수 있습니다.</p>
</li>
</ul>
<h2 id="2-캐시를-이용한-임베딩-최적화">2. 캐시를 이용한 임베딩 최적화</h2>
<p>주요 도구: CacheBackedEmbeddings</p>
<p>이유: 임베딩은 비용(돈/시간)이 듭니다. 똑같은 문장을 매번 새로 계산하는 건 비효율적이죠.</p>
<h3 id="cachebackedembeddings">CacheBackedEmbeddings</h3>
<p>embeddings를 키-값 저장소에 캐싱하는 embedder 주변에 래퍼입니다.</p>
<ul>
<li>알맹이: 실제 계산을 담당하는 OpenAIEmbeddings.</li>
<li>껍데기(Wrapper): 계산된 결과를 저장하고 관리하는 CacheBackedEmbeddings.</li>
</ul>
<h4 id="🛠️-cachebackedembeddings-초기화-매개변수">🛠️ CacheBackedEmbeddings 초기화 매개변수</h4>
<p>캐시 임베더를 설정할 때는 <code>from_bytes_store</code> 메서드를 통해 다음 세 가지 핵심 부품을 연결합니다.</p>
<ol>
<li><strong>underlying_embeddings</strong>: 실제 임베딩 계산을 수행할 모델 엔진 (예: OpenAI).</li>
<li><strong>document_embedding_cache</strong>: 계산된 벡터를 바이트 단위로 보관할 저장소 (ByteStore).</li>
<li><strong>namespace</strong>: 저장소 내의 독립된 구역 이름. <ul>
<li><strong>주의</strong>: 서로 다른 모델이 동일한 텍스트에 대해 생성한 벡터가 섞이지 않도록, 모델명을 네임스페이스로 지정하는 것이 권장됩니다.</li>
</ul>
</li>
</ol>
<h4 id="localfilestore-에서-임베딩-사용-영구-보관">LocalFileStore 에서 임베딩 사용 (영구 보관)</h4>
<pre><code># 캐시를 지원하는 임베딩 생성
embedding = OpenAIEmbeddings()

cached_embedder = CacheBackedEmbeddings.from_bytes_store(
    underlying_embeddings=embedding,
    document_embedding_cache=➡️LocalFileStore(&quot;./cache/&quot;), # 로컬 파일 저장소 설정
    namespace=embedding.model  # 기본 임베딩과 저장소를 사용하여 캐시 지원 임베딩을 생성
)</code></pre><h4 id="inmemorybytestore-사용-비영구적">InmemoryByteStore 사용 (비영구적)</h4>
<pre><code>from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import InMemoryByteStore

store = ➡️InMemoryByteStore()  # 메모리 내 바이트 저장소 생성

# 캐시 지원 임베딩 생성
cached_embedder = CacheBackedEmbeddings.from_bytes_store(
    embedding, store, namespace=embedding.model
)
</code></pre><blockquote>
<p>비용 절감과 데이터 보존이 중요한 실무 환경에는 LocalFile을, 속도가 중요하고 일회성인 작업에는 InMemory를 선택</p>
</blockquote>
<h2 id="3-오픈소스-모델-활용-huggingface">3. 오픈소스 모델 활용: HuggingFace</h2>
<p>주요 도구: HuggingFaceEmbeddings</p>
<p>특징: OpenAI API(유료) 없이 내 로컬(코랩) 환경에서 무료 오픈소스 모델로 갈아타는 방법.</p>
<h4 id="쓸만한-한국어-임베딩-모델">쓸만한 한국어 임베딩 모델</h4>
<ul>
<li><p>intfloat/multilingual-e5-large (3~4위):
현재 오픈소스 모델 중 최상위권 성능입니다.
instruct 버전은 질문과 답변의 맥락을 더 잘 구분하도록 튜닝되어 있습니다.</p>
</li>
<li><p>BAAI/bge-m3 (6위):
한국어 임베딩의 교과서 같은 모델입니다.
성능이 안정적이고, 다국어 지원이 강력해서 실무에서 가장 많이 쓰입니다.</p>
</li>
<li><p>e5-base / e5-small (7~8위):
성능은 상위권 모델보다 조금 낮지만, 가볍고 속도가 매우 빠릅니다.
컴퓨터 자원이 부족하거나 빠른 응답이 필요할 때 선택합니다.</p>
</li>
</ul>
<h3 id="huggingface-endpoint-embedding">HuggingFace Endpoint Embedding</h3>
<p>모델을 내 컴퓨터가 아닌 별도의 <strong>전용 서버(Endpoint)</strong>에 올려놓고 API 형태로 호출하는 방식</p>
<pre><code>from langchain_huggingface.embeddings import HuggingFaceEndpointEmbeddings

model_name = &quot;intfloat/multilingual-e5-large-instruct&quot;

hf_embeddings = ➡️HuggingFaceEndpointEmbeddings(
    model=model_name,
    task=&quot;feature-extraction&quot;,
    huggingfacehub_api_token=os.environ[&quot;HF_TOKEN&quot;],
)

embedded_documents = hf_embeddings.embed_documents(texts)
embedded_query = hf_embeddings.embed_query(&quot;LangChain 에 대해서 알려주세요.&quot;)</code></pre><h3 id="huggingface-embeddings">HuggingFace Embeddings</h3>
<p>내 컴퓨터(Local)의 GPU를 사용하여 임베딩을 생성합니다. 비용이 0원이며 보안이 중요한 프로젝트에 필수적</p>
<pre><code>from langchain_huggingface.embeddings import HuggingFaceEmbeddings

model_name = &quot;intfloat/multilingual-e5-large-instruct&quot;

hf_embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs={&quot;device&quot;: &quot;cuda&quot;},  # cuda, cpu-&gt; 내 컴퓨터 가동
    encode_kwargs={&quot;normalize_embeddings&quot;: True}, 
    # 추출된 벡터들의 길이를 1로 맞추는 정규화 작업 -&gt; 유사도를 구할 때 복잡한 계산 없이 단순한 &#39;내적&#39;만으로도 정확한 비교가 가능
)
</code></pre><h3 id="임베딩-방식별-비교표">임베딩 방식별 비교표</h3>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">Dense (밀집)</th>
<th align="left">b. Sparse (희소)</th>
<th align="left">Multi-Vector (다중)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>대표 모델</strong></td>
<td align="left"><strong>OpenAI</strong></td>
<td align="left"></td>
<td align="left"><strong>c. ColBERT</strong></td>
</tr>
<tr>
<td align="left"><strong>비유</strong></td>
<td align="left">문장의 전체적인 분위기</td>
<td align="left">문장 속 핵심 단어</td>
<td align="left">문장 속 모든 단어의 관계</td>
</tr>
<tr>
<td align="left"><strong>수학적 특징</strong></td>
<td align="left">고정된 차원에 정보 압축</td>
<td align="left">단어 사전 기반 점수 부여</td>
<td align="left">문장을 여러 벡터로 분할 저장</td>
</tr>
<tr>
<td align="left"><strong>장점</strong></td>
<td align="left">문맥 및 유사어 파악 능력이 좋음</td>
<td align="left">고유명사를 정확하게 검색함</td>
<td align="left">검색 정밀도가 가장 높음</td>
</tr>
<tr>
<td align="left"><strong>단점</strong></td>
<td align="left">특정 단어 포함 여부 확인이 어려움</td>
<td align="left">문맥(의미) 파악이 불가능함</td>
<td align="left">저장 용량이 크고 속도가 느림</td>
</tr>
</tbody></table>
<p><strong>a. BGE-M3</strong>는 세 가지 방식을 모델 하나로 모두 구현하고 있음❕❕❕</p>
<h3 id="a-bge-m3-임베딩🌟">a. BGE-M3 임베딩🌟</h3>
<p>현재 한국어를 포함한 다국어 검색에서 가장 가성비 좋고 강력한 오픈소스 모델이기 때문에 실무 표준으로 소개됨</p>
<pre><code>from langchain_huggingface import HuggingFaceEmbeddings

model_name = ➡️&quot;BAAI/bge-m3&quot;
model_kwargs = {&quot;device&quot;: &quot;cuda&quot;}
encode_kwargs = {&quot;normalize_embeddings&quot;: True}

hf_embeddings = HuggingFaceEmbeddings(
    model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)
</code></pre><h4 id="랭체인langchain말고-모델-제조사baai가-직접-만든-전용-라이브러리flagembedding를-사용하는-방법">랭체인(langchain)말고 모델 제조사(BAAI)가 직접 만든 전용 라이브러리(FlagEmbedding)를 사용하는 방법</h4>
<p>랭체인의 HuggingFaceEmbeddings는 범용적이지만, BGE-M3 전용 기능(Sparse, Multi-vector 추출 등)을 세밀하게 제어하기에는 한계가 있습니다.</p>
<p>제조사가 제공하는 라이브러리를 쓰면 use_fp16(반정밀도 연산) 같은 속도 최적화 옵션을 더 직접적으로 사용할 수 있습니다.</p>
<pre><code># FlagEmbedding 설치
!pip install -qU FlagEmbedding</code></pre><h4 id="1번-스타일-일단-계산시키고-dense_vecs만-사용하기">1번 스타일: 일단 계산시키고 dense_vecs만 사용하기</h4>
<pre><code>from FlagEmbedding import BGEM3FlagModel

bge_embeddings = BGEM3FlagModel(
    &quot;BAAI/bge-m3&quot;, use_fp16=True
)  # use_fp16을 True로 설정하면 약간의 성능 저하와 함께 숫자를 표현하는 비트 수를 줄여 계산량을 줄어 빨라짐.

bge_embedded = bge_embeddings.encode(
    texts,
    batch_size=12,
    max_length=8192,  # 한번에 읽는 토큰수:작은 값을 설정하여 프로세스의 속도를 높일 수 있음.
)➡️[&quot;dense_vecs&quot;]
</code></pre><p>[&quot;dense_vecs&quot;]
-&gt;BGE-M3는 Dense, Sparse, ColBERT 결과를 동시에 생성하는 멀티태스킹 모델이므로, encode() 결과 중 필요한 데이터 형태(dense_vecs)만 명시적으로 추출하여 사용하려고 넣은 코드.</p>
<h4 id="2번-스타일-처음부터-dense-결과만-돌려주도록-세팅">2번 스타일: 처음부터 dense 결과만 돌려주도록 세팅</h4>
<pre><code>from FlagEmbedding import BGEM3FlagModel

bge_flagmodel = BGEM3FlagModel(
    &quot;BAAI/bge-m3&quot;, use_fp16=True
)  

bge_encoded = bge_flagmodel.encode(texts, ➡️return_dense=True)
</code></pre><h3 id="b-sparse-embedding-lexical-weight">b. Sparse Embedding (Lexical Weight)</h3>
<p>기존 밀집(Dense) 임베딩은 문맥은 잘 잡지만 &quot;고유 명사(예: 제품명, 이름)&quot; 검색에 약합니다. 이를 보완하기 위해 키워드 일치 방식(Lexical)의 가중치를 더해 검색 정확도를 높임</p>
<pre><code>bge_flagmodel = BGEM3FlagModel(
    &quot;BAAI/bge-m3&quot;, use_fp16=True
)  # use_fp16을 True로 설정하면 약간의 성능 저하와 함께 계산 속도가 빨라집니다.
bge_encoded = bge_flagmodel.encode(texts, ➡️return_sparse=True)</code></pre><p>여기서는 BGE-M3 모델 사용함</p>
<pre><code>lexical_scores1 = bge_flagmodel.compute_lexical_matching_score(
    bge_encoded[&quot;lexical_weights&quot;][0], bge_encoded[&quot;lexical_weights&quot;][0]
)
# 0 &lt;-&gt; 0 자기 자신과의 비교
print(lexical_scores1)</code></pre><p>0.30167388916015625 
= 0번 문서와 0번 문서가 얼마나 <strong>&#39;같은 단어&#39;</strong>를 공유하는지 계산한 값입니다. 당연히 똑같은 단어들로 구성되어 있으니 모델이 판단한 최대 일치 점수가 나옵니다.</p>
<pre><code>lexical_scores2 = bge_flagmodel.compute_lexical_matching_score(
    bge_encoded[&quot;lexical_weights&quot;][0], bge_encoded[&quot;lexical_weights&quot;][1]
)
# 0 &lt;-&gt; 1 다른 문서와의 비교
print(lexical_scores2)</code></pre><p>0 =두 문서 사이에 겹치는 단어가 하나도 없다. Dense 방식은 단어가 달라도 &#39;분위기&#39;가 비슷하면 0.8 같은 점수를 주지만, Sparse 방식은 글자 자체가 일치하지 않으면 가차 없이 0점.</p>
<h3 id="c-colbert">c. ColBERT</h3>
<p>문장 전체를 하나의 점(Vector)으로 찍는 대신, 단어 하나하나의 벡터를 모두 비교합니다. 계산량은 훨씬 많지만, 문맥적 유사도를 찾는 정밀도가 압도적으로 높아 고성능 검색 엔진 구현 시 사용</p>
<h4 id="작동-방식">작동 방식</h4>
<ol>
<li>문서의 각 토큰에 대해 별도의 벡터를 생성합니다. 즉, 하나의 문서는 여러 개의 벡터로 표현됩니다.</li>
<li>쿼리도 마찬가지로 각 토큰에 대해 별도의 벡터를 생성합니다.</li>
<li>검색 시, 쿼리의 각 토큰 벡터와 문서의 모든 토큰 벡터 사이의 유사도를 계산합니다.</li>
<li>이 유사도들을 종합하여 최종 검색 점수를 계산합니다.</li>
</ol>
<pre><code>bge_flagmodel = BGEM3FlagModel(
    &quot;BAAI/bge-m3&quot;, use_fp16=True
)  # use_fp16을 True로 설정하면 약간의 성능 저하와 함께 계산 속도가 빨라집니다.
bge_encoded = bge_flagmodel.encode(texts, ➡️return_colbert_vecs=True)
</code></pre><pre><code>colbert_scores1 = bge_flagmodel.colbert_score(
    bge_encoded[&quot;colbert_vecs&quot;][0], bge_encoded[&quot;colbert_vecs&quot;][0]
)
colbert_scores2 = bge_flagmodel.colbert_score(
    bge_encoded[&quot;colbert_vecs&quot;][0], bge_encoded[&quot;colbert_vecs&quot;][1]
)
# 0 &lt;-&gt; 0
print(colbert_scores1)
# 0 &lt;-&gt; 1
print(colbert_scores2)</code></pre><p>앞서 본 Sparse 방식이 &quot;0점&quot;을 줬던 문장들에 대해, ColBERT는 어떻게 반응하는지 비교하면 , ColBERT는 글자는 다르지만, 단어들의 세부적인 맥락을 보니 의미적 연관성이 있다고 판단해 tensor(0.3748)라고 점수를 부여해줌.</p>
<blockquote>
<p>&quot;ColBERT가 좋은 이유&quot; Sparse 방식은 &#39;아이폰&#39;과 &#39;스마트폰&#39;을 완전히 다른 것으로 보지만, ColBERT는 두 단어를 구성하는 토큰(Token) 벡터들이 서로 유사한 영역에 있음을 감지한다.</p>
</blockquote>
<h2 id="4-upstageembeddings">4. UpstageEmbeddings</h2>
<p>국내 AI 기업인 <strong>Upstage(업스테이지)</strong>의 Solar 모델 실습</p>
<p>업스테이지는 검색의 정확도를 높이기 위해 비대칭 임베딩(Asymmetric Embedding) 전략을 사용하는데 
질문과 답변은 문장 구조가 다르기 때문에 각각의 특성에 맞춰 학습시켜, <strong>질문용 모델과 답변용 모델이 분리</strong> 되어 있다!</p>
<pre><code>from langchain_upstage import UpstageEmbeddings

# 쿼리 전용 임베딩 모델
query_embeddings = UpstageEmbeddings(model=&quot;solar-embedding-1-large-query&quot;)

# 문장 전용 임베딩 모델
passage_embeddings = UpstageEmbeddings(model=&quot;solar-embedding-1-large-passage&quot;)
</code></pre><pre><code># 쿼리 임베딩
embedded_query = query_embeddings.embed_query(&quot;LangChain 에 대해서 상세히 알려주세요.&quot;)
# 임베딩 차원 출력-&gt; 4096
len(embedded_query)
# 문서 임베딩
embedded_documents = passage_embeddings.embed_documents(texts)
</code></pre><p>앞서 배운 BGE-M3나 E5 모델은 1024차원이었지만 업스테이지 모델은 4096차원으로 정보를 4배나 더 세밀하게 쪼개서 저장합니다. 그만큼 문장의 미묘한 뉘앙스를 잡아내는 능력이 탁월하지만, 저장 공간(Vector DB)을 더 많이 차지한다는 트레이드오프가 있습니다.</p>
<p>유사도 결과에서도 한국어와 영어의 교차 검색에서 언어가 달라도 의미가 통하면 높은 점수를 주는 등 더 정교한 의미 검색이 가능함!</p>
<h2 id="5-ollamaembeddings">5. OllamaEmbeddings</h2>
<p>Ollama는 로컬 환경에서 대규모 언어 모델(LLM)을 쉽게 실행할 수 있게 해주는 오픈소스 프로젝트로 개발자들이 AI 모델을 자신의 컴퓨터에서 직접 실험하고 사용할 수 있도록 지원합니다.</p>
<pre><code>from langchain_community.embeddings import OllamaEmbeddings

ollama_embeddings = ➡️OllamaEmbeddings(
    model=&quot;nomic-embed-text&quot;, #768차원짜리 가벼운 모델. 로컬에서 돌려야해서 가장 대중적으로 쓰임.
)</code></pre><p>쿼리와 문서를 인베딩하는 방식은 위 다른 실습과 동일하고 특이한 점은 유사도에서 점수가 높게 나왔는데 그 이유는 다른 모델과는 달리 정규화를 거치지 않은 순수 벡터 값을 그대로 내적했기 때문에, 벡터의 크기만큼 점수가 뻥튀기된 것입니다. 점수의 절댓값은 의미가 없고 상대적인 순위만 확인하면 됩니다.</p>
<pre><code>[Query] LangChain 에 대해서 알려주세요.
====================================
[0] 유사도: 399.644 | LangChain은 초거대 언어모델로 애플리케이션을 구축하는 과정을 단순화합니다.

[1] 유사도: 356.518 | 랭체인 한국어 튜토리얼은 LangChain의 공식 문서, cookbook 및 다양한 실용 예제를 바탕으로 하여 사용자가 LangChain을 더 쉽고 효과적으로 활용할 수 있도록 구성되어 있습니다. 

[2] 유사도: 322.359 | LangChain simplifies the process of building applications with large language models

[3] 유사도: 321.078 | 안녕, 만나서 반가워.

[4] 유사도: 224.858 | Retrieval-Augmented Generation (RAG) is an effective technique for improving AI responses.
</code></pre><h2 id="6-gpt4all-임베딩">6. GPT4ALL 임베딩</h2>
<p>GPT4All은 무료로 사용할 수 있는 로컬 실행 기반의 개인정보 보호를 고려한 챗봇으로
GPU 없이 CPU만으로도 돌아가는 저사양 환경을 위한 가성비 모델입니다.
업스테이지(4096), BGE-M3(1024)에 비해 훨씬 작은 384차원을 사용합니다. (실습에서 확인가능)</p>
<ul>
<li>장점: 숫자가 적으니 계산 속도가 빛의 속도만큼 빠르고, 메모리(RAM)를 거의 차지하지 않습니다.</li>
<li>단점: 정보의 &#39;해상도&#39;는 낮습니다. 복잡하고 긴 전문 지식 검색보다는 간단한 일상 대화나 가벼운 문장 검색에 적합합니다.</li>
</ul>
<table>
<thead>
<tr>
<th align="left">모델명</th>
<th align="left">제공사</th>
<th align="left">차원</th>
<th align="left">주요 특징</th>
<th align="left">추천 용도</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>OpenAI</strong></td>
<td align="left">OpenAI</td>
<td align="left">1536</td>
<td align="left">유료, 가장 표준적인 성능</td>
<td align="left">범용 RAG 서비스 구축</td>
</tr>
<tr>
<td align="left"><strong>BGE-M3</strong></td>
<td align="left">BAAI</td>
<td align="left">1024</td>
<td align="left">오픈소스, 한국어 성능 최강</td>
<td align="left">국내 검색 서비스, 하이브리드 검색</td>
</tr>
<tr>
<td align="left"><strong>Solar</strong></td>
<td align="left">Upstage</td>
<td align="left">4096</td>
<td align="left">유료, 초고해상도, 비대칭 임베딩</td>
<td align="left">정밀도가 중요한 전문 문서 검색</td>
</tr>
<tr>
<td align="left"><strong>GPT4All</strong></td>
<td align="left">Nomic</td>
<td align="left">384</td>
<td align="left">무료, CPU 최적화(저사양 최적)</td>
<td align="left">개인 프로젝트 및 로컬 환경</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[코틀린은 왜 자바와 같은 컴파일 언어인데 선언 순서가 중요할까?]]></title>
            <link>https://velog.io/@no-glass-otacku/%EC%BD%94%ED%8B%80%EB%A6%B0%EC%9D%80-%EC%99%9C-%EC%9E%90%EB%B0%94%EC%99%80-%EA%B0%99%EC%9D%80-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EC%96%B8%EC%96%B4%EC%9D%B8%EB%8D%B0-%EC%84%A0%EC%96%B8-%EC%88%9C%EC%84%9C%EA%B0%80-%EC%A4%91%EC%9A%94%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@no-glass-otacku/%EC%BD%94%ED%8B%80%EB%A6%B0%EC%9D%80-%EC%99%9C-%EC%9E%90%EB%B0%94%EC%99%80-%EA%B0%99%EC%9D%80-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EC%96%B8%EC%96%B4%EC%9D%B8%EB%8D%B0-%EC%84%A0%EC%96%B8-%EC%88%9C%EC%84%9C%EA%B0%80-%EC%A4%91%EC%9A%94%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Sun, 19 Oct 2025 15:11:10 GMT</pubDate>
            <description><![CDATA[<p>요즘 코틀린을 배우는 중인데 과제를 하던 중 교수님이 적어두신 </p>
<pre><code>//implement here...</code></pre><p>에 class를 선언하니까 참조를 못한다고 떴습니다. 물론 class가 사용처보다 아래에 작성되긴 했지만 저는 교수님이 적으라고 한 곳에 적은 죄 밖에 없었기에 억울했습니다...
게다가 코틀린은 자바와 같은 컴파일 언어인데 선언 순서에 영향을 받을 거라고 생각치도 못했습니다.</p>
<p>저 같은 오개념을 가진 사람도 있을 것이라 생각해 이번 블로그 주제를 선정하게 되었습니다. </p>
<h3 id="기술적-오해">기술적 오해</h3>
<p>코틀린은 자바와 마찬가지로 JVM(Java Virtual Machine)을 타겟으로 하는 컴파일 언어입니다. 그러나 코틀린은 사용하고자 하는 요소가 그 사용처보다 코드 상위(먼저)에 정의되어야만 컴파일이 성공하는 경우가 일반적입니다.</p>
<p>이는 흔히 알려진 &#39;컴파일 언어는 선언 순서에 자유롭다&#39;는 인식과는 다릅니다. 그렇다면 선언 순서의 자유도에 영향을 미치는 근본적인 요인은 무엇일까요?</p>
<h3 id="선언-순서의-결정-요인-컴파일러-처리-방식의-차이">선언 순서의 결정 요인: 컴파일러 처리 방식의 차이</h3>
<p>선언 순서의 자유도를 결정하는 핵심 요인은 언어의 종류(컴파일 vs 인터프리트)가 아닌, <strong>컴파일러가 소스 코드를 처리하는 방식, 즉 &#39;통과(Pass)&#39; 횟수</strong>에 있습니다.</p>
<ul>
<li><p>$\text{다중 통과(Multi-Pass)}$ 방식: 코드를 여러 번 스캔하여, <strong>첫 번째 통과에서 모든 심볼(클래스, 함수 등)의 존재와 위치를 파악</strong>(전방 참조 허용)하고, 이후 통과에서 실제 검증 및 코드를 생성합니다. -&gt; 즉, <strong>선언 순서가 비교적 자유롭습니다.</strong> </p>
</li>
<li><p>$\text{단일 통과(Single-Pass)}$ 방식: 코드를 위에서 아래로 한 번에 처리하며, 요소를 사용할 시점에는 반드시 그 정의가 이미 컴파일러에게 알려져 있어야 합니다. -&gt; 즉, ** 선언이 사용처보다 앞에 되어있어야만 합니다.**</p>
</li>
</ul>
<h3 id="코틀린kotlin-단일-통과-경향과-언어-설계의-합리성">코틀린(Kotlin): 단일 통과 경향과 언어 설계의 합리성</h3>
<p>코틀린 컴파일러는 단일 통과에 가까운 효율적인 처리를 지향하며, 이는 언어 설계 철학과 긴밀하게 연결되어 있습니다.
다른 근본 언어를 배우고 코틀린을 배우기 시작하면 코틀린이 얼마나 컴팩트함과 직관적임을 유지하고자 했는지 참... 느껴집니다 (개인적인 의견)</p>
<p>코틀린은 다중 스캔의 복잡성을 줄여 컴파일러 구조를 단순화하고 처리 속도를 높이고자 하였고 (근데 앱 개발할때는 애뮬레이터 때문에 느려터짐) 스크립트와 유사한 순차적 실행 흐름 갖도록  선언도 이 흐름에 맞춰 사용 전 정의를 규칙으로 확립함으로써 코드의 <strong>지역성(Locality)</strong>과 가독성을 극대화합니다.</p>
<p>코틀린은 JVM 기반 컴파일 언어의 이점을 유지하면서도, 현대적인 언어의 요구사항(간결성, 명확성)을 위해 명시적인 선언 순서를 채택한 것입니다.</p>
<h3 id="결론">결론</h3>
<p>결론적으로, <strong>선언 순서의 자유도</strong>는 언어의 종류가 아닌 <strong>컴파일러의 내부 설계 방식</strong>에 의해 결정됩니다. 자바는 다중 통과로 유연성을 확보했고, 코틀린은 단일 통과 경향과 가독성 철학으로 명확성을 선택한 것입니다.</p>
<p>실무에서 코틀린을 사용할 때는 이 원리를 이해하고, 클래스나 함수를 사용하기 전에 정의하는 Top-Down 방식의 코딩 습관을 유지합시다!</p>
]]></description>
        </item>
    </channel>
</rss>