devkuma – devkuma
https://www.devkuma.com/
https://www.devkuma.com/logo/180x180.jpg
devkuma
https://www.devkuma.com/
Recent content on devkuma
Hugo -- gohugo.io
ko-kr
[email protected] (kc kim)
[email protected] (kc kim)
The devkuma
-
NotebookLM
https://www.devkuma.com/docs/ai/notebook-lm/
Mon, 23 Feb 2026 08:48:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/notebook-lm/
<h2 id="notebooklm-이란">NotebookLM 이란?</h2>
<p><strong>NotebookLM</strong>은 Google 이 개발한 <strong>AI 기반 연구·노트 정리 도구</strong>이다.
사용자가 업로드한 자료(문서, PDF, 웹 링크 등)를 기반으로 AI가 내용을 이해하고, 요약·정리·질문 응답을 수행하는 <strong>개인화된 AI 연구 비서</strong>에 가깝다.</p>
<p>기존 챗봇이 일반 지식을 기반으로 답하는 것과 달리, <strong>사용자가 제공한 자료 안에서만 근거를 찾아 답변하는 것이 핵심 특징</strong>이다.</p>
<p><a href="https://notebooklm.google/" target="_blank" rel="noopener">Google NotebookLM<i class="fas fa-external-link-alt"></i></a></p>
<h2 id="주요-특징">주요 특징</h2>
<h3 id="자료-기반-답변-grounded-ai">자료 기반 답변 (Grounded AI)</h3>
<ul>
<li>업로드한 문서 안에서만 답변 생성</li>
<li>답변에 **출처(인용)**가 함께 제공됨</li>
<li>논문, 계약서, 강의자료 분석에 강점</li>
</ul>
<h3 id="자동-요약">자동 요약</h3>
<ul>
<li>긴 PDF를 핵심만 정리</li>
<li>핵심 개념, 주장, 결론 구조화</li>
<li>블로그 초안, 보고서 초안 작성에 활용 가능</li>
</ul>
<h3 id="질문--심화-분석">질문 & 심화 분석</h3>
<p>예:</p>
<ul>
<li>“이 문서의 핵심 주장 3가지는?”</li>
<li>“저자가 전제하는 가정은 무엇인가?”</li>
<li>“비판적으로 분석해줘”</li>
</ul>
<p>→ 단순 요약을 넘어 <strong>비판적 분석, 비교 분석도 가능</strong></p>
<h3 id="오디오-개요-audio-overview">오디오 개요 (Audio Overview)</h3>
<p>문서 내용을 두 명의 AI가 대화하듯 설명해주는 기능
→ 팟캐스트처럼 들을 수 있음</p>
<h2 id="지원-형식">지원 형식</h2>
<ul>
<li>Google Docs</li>
<li>PDF</li>
<li>웹사이트 링크</li>
<li>텍스트 파일</li>
<li>Google Slides</li>
</ul>
<h2 id="chatgpt와의-차이점">ChatGPT와의 차이점</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>NotebookLM</th>
<th>ChatGPT</th>
</tr>
</thead>
<tbody>
<tr>
<td>답변 기준</td>
<td>업로드한 자료 중심</td>
<td>일반 학습 데이터 기반</td>
</tr>
<tr>
<td>출처 표시</td>
<td>있음</td>
<td>기본적으로 없음</td>
</tr>
<tr>
<td>연구 분석</td>
<td>매우 강함</td>
<td>가능하지만 범용적</td>
</tr>
<tr>
<td>창의적 글쓰기</td>
<td>보통</td>
<td>매우 강함</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="이런-사람에게-추천">이런 사람에게 추천</h2>
<ul>
<li>논문·리서치 작업이 많은 사람</li>
<li>블로그/책 집필을 준비하는 사람</li>
<li>계약서·정책 문서 분석이 필요한 직장인</li>
<li>시험 대비 요약 정리가 필요한 수험생</li>
</ul>
<hr>
<h2 id="정리">정리</h2>
<p>NotebookLM은 단순한 AI 챗봇이 아니라 <strong>“내 자료를 읽고 이해하는 AI 연구 파트너”</strong> 에 가깝다.</p>
<p>특히 책을 쓰거나(예: Kotest 책 집필 같은 작업), 자료 기반 콘텐츠를 만들 때 매우 강력한 도구가 될 수 있다.</p>
AI
-
Google Gemini API를 사용한 MCP 함수 호출
https://www.devkuma.com/docs/ai/gemini/api-mcp/
Fri, 30 Jan 2026 16:28:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/gemini/api-mcp/
<h2 id="함수-호출-작동-방식">함수 호출 작동 방식</h2>
<p>함수 호출 작동 방식에 대해서는 <a href="https://ai.google.dev/gemini-api/docs/function-calling?hl=ko&_gl=1*15vau2u*_up*MQ..*_ga*MTEzMDM3OTczNi4xNzY5NzQ2MDI4*_ga_P1DBVKWT6V*czE3Njk3NDYwMjgkbzEkZzAkdDE3Njk3NDYwMjgkajYwJGwwJGg3MTQyMTk5NzU.&example=meeting#how-it-works" target="_blank" rel="noopener">Gemini API 공식 문서<i class="fas fa-external-link-alt"></i></a>에 자세히 설명이 되어 있다.</p>
<p><img src="https://www.devkuma.com/docs/ai/gemini-function-calling-overview.png" alt="함수 호출 개요"></p>
<p>간단히 설명하면 아래와 같다.</p>
<ul>
<li><strong>함수 호출</strong>은 애플리케이션, LLM, 외부 함수 간의 구조화된 협업 방식이다.</li>
<li>애플리케이션은 먼저 함수 선언을 정의해 함수의 이름, 매개변수, 목적을 모델에 설명을 준비한다.</li>
<li>사용자 프롬프트와 함수 선언을 함께 <strong>LLM에 전달</strong>하면, 모델은 함수 호출이 필요한지 판단해 <strong>구조화된 JSON 응답</strong>을 반환하거나 일반 텍스트로 응답한다.</li>
<li><strong>함수 실행은 모델이 아닌 애플리케이션의 책임</strong>이며, 모델은 함수 이름과 인수만 제공한다.</li>
<li>실행 결과를 다시 모델에 보내면, 모델이 이를 반영해 <strong>사용자 친화적인 최종 답변</strong>을 생성한다.</li>
</ul>
<h2 id="mcp-서버-준비">MCP 서버 준비</h2>
<p>MCP 서버는 다음 문서에서 설명한 간단히 서버를 활용할 것이다.<br>
<a href="https://www.devkuma.com/docs/spring-ai/mcp-server-auth/" target="_blank" rel="noopener">Spring, Kotlin를 활용하여 만든 WebMVC MCP Server에 인증 추가하기<i class="fas fa-external-link-alt"></i></a></p>
<h2 id="클라이언트-개발">클라이언트 개발</h2>
<p>클라이언트는 다음 문서에 만든 어플리케이션을 활용할 것이다.<br>
<a href="https://www.devkuma.com/docs/ai/gemini/api/" target="_blank" rel="noopener">Gemini API를 활용한 어플리케이션 만들기<i class="fas fa-external-link-alt"></i></a></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.sample1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.google.genai.Client</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.google.genai.types.*</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.McpClient</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.spec.McpSchema.CallToolRequest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">java.net.http.HttpRequest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">java.util.stream.Collectors</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 설정
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">mcpServerUrl</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"http://localhost:8080"</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">mcpApiKey</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"api01.mycustomapikey"</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">geminiApiKey</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"GEMINI_API_KEY"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"========================================"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Gemini API + MCP Function Calling Demo"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"========================================</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 1. MCP 클라이언트 초기화
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">request</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HttpRequest</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBuilder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">header</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Content-Type"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"application/json"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">header</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"X-API-key"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">mcpApiKey</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">transport</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HttpClientStreamableHttpTransport</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">mcpServerUrl</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">requestBuilder</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">request</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">mcpClient</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">McpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sync</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">transport</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">mcpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">initialize</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">mcpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">ping</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// MCP 서버에서 사용 가능한 도구 목록을 가져옵니다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">toolsList</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mcpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">listTools</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Available Tools = </span><span style="color:#4e9a06">$toolsList</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 2. Gemini 클라이언트 초기화 및 함수 설정
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionDeclarations</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">toolsList</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">tools</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">stream</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">t</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">FunctionDeclaration</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">t</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#8f5902;font-style:italic">// MCP tool name
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">description</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">t</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">description</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#8f5902;font-style:italic">// MCP tool description
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">parametersJsonSchema</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">t</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">inputSchema</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#8f5902;font-style:italic">// Object 타입으로 받음
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">})</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">collect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Collectors</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">toList</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">tool</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">Tool</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Tool</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">functionDeclarations</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionDeclarations</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">config</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">GenerateContentConfig</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">tools</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tool</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">client</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">apiKey</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">geminiApiKey</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 4. 사용자 질문 처리
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">userMessage</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"서울의 날씨를 알려줘"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">========================================"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"User: </span><span style="color:#4e9a06">$userMessage</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"========================================</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 5. 첫 번째 Gemini API 호출
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">models</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">generateContent</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"gemini-3-flash-preview"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">userMessage</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">config</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">=== First Response ==="</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Candidates: </span><span style="color:#4e9a06">${response.candidates()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 6. Function Call 확인 및 처리
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">candidatesOpt</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">candidates</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">candidatesOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isPresent</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">candidates</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">candidatesOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">candidates</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isNotEmpty</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">candidate</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">candidates</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">contentOpt</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">candidate</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contentOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isPresent</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">content</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">contentOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">partsOpt</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parts</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">partsOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isPresent</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">parts</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">partsOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Parts: </span><span style="color:#4e9a06">$parts</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Function Call이 있는지 확인
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionCalls</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">parts</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">p</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">Part</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">p</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">functionCall</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#ce5c00;font-weight:bold">!=</span> <span style="color:#204a87;font-weight:bold">null</span> <span style="color:#ce5c00;font-weight:bold">&&</span> <span style="color:#000">p</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">functionCall</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">isPresent</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionCalls</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isNotEmpty</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">=== Function Calls Detected ==="</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 대화 히스토리 구성 (사용자 메시지 + 모델의 function call)
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">contents</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mutableListOf</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Content</span><span style="color:#000;font-weight:bold">>()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 사용자 메시지 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">contents</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">Content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">role</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"user"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">parts</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Part</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">text</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">userMessage</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 모델의 응답 (function call) 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">contents</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 Function Call 처리
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionResponseParts</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mutableListOf</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Part</span><span style="color:#000;font-weight:bold">>()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">part</span> <span style="color:#204a87;font-weight:bold">in</span> <span style="color:#000">functionCalls</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionCallOpt</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">part</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">functionCall</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionCallOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isPresent</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionCall</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">functionCallOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionNameOpt</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">functionCall</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionArgsOpt</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">functionCall</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">args</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionNameOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isPresent</span> <span style="color:#ce5c00;font-weight:bold">&&</span> <span style="color:#000">functionArgsOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isPresent</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionName</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">functionNameOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionArgs</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">functionArgsOpt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Function Call: </span><span style="color:#4e9a06">$functionName</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Arguments: </span><span style="color:#4e9a06">$functionArgs</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 3단계: MCP 서버의 도구 호출
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">mcpResult</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mcpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">callTool</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">CallToolRequest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionName</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">functionArgs</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"MCP Result: </span><span style="color:#4e9a06">$mcpResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// MCP 결과에서 content 추출
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">mcpContent</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mcpResult</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">resultText</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mcpContent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isNotEmpty</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// MCP Content를 문자열로 변환
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#8f5902;font-style:italic">// TextContent의 경우 text 필드를 가짐
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">content</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mcpContent</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">when</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">content</span> <span style="color:#204a87;font-weight:bold">is</span> <span style="color:#000">io</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">modelcontextprotocol</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">spec</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">McpSchema</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">TextContent</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">text</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">toString</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"No result"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Extracted Result: </span><span style="color:#4e9a06">$resultText</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Function Response Part 생성
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">functionResponsePart</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Part</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">functionResponse</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">FunctionResponse</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionName</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">response</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">mapOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"result"</span> <span style="color:#000">to</span> <span style="color:#000">resultText</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">functionResponseParts</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionResponsePart</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 함수 응답을 user role로 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">contents</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">Content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">role</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"user"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">parts</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">functionResponseParts</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 4단계: 함수 호출 결과를 포함하여 Gemini API 재호출
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">=== Calling Gemini Again with Function Results ==="</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">models</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">generateContent</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"gemini-3-flash-preview"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">contents</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">config</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 7. 최종 응답 출력
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">=== Final Response ==="</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Assistant: </span><span style="color:#4e9a06">${response.text()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">\n</span><span style="color:#4e9a06">No function calls detected. Direct response: </span><span style="color:#4e9a06">${response.text()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 8. 정리:MCP 클라이언트 종료
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">mcpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">closeGracefully</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<pre tabindex="0"><code>========================================
Gemini API + MCP Function Calling Demo
========================================
Available Tools = ListToolsResult[tools=[Tool[name=get_weather, title=null, description=Return the weather of a given city., inputSchema=JsonSchema[type=object, properties={city={type=string, description=The city for which to get the weather}}, required=[city], additionalProperties=false, defs=null, definitions=null], outputSchema=null, annotations=null, meta=null]], nextCursor=null, meta=null]
========================================
User: 서울의 날씨를 알려줘
========================================
=== First Response ===
Candidates: Optional[[Candidate{content=Optional[Content{parts=Optional[[Part{mediaResolution=Optional.empty, codeExecutionResult=Optional.empty, executableCode=Optional.empty, fileData=Optional.empty, functionCall=Optional[FunctionCall{id=Optional.empty, args=Optional[{city=Seoul}], name=Optional[get_weather], partialArgs=Optional.empty, willContinue=Optional.empty}], functionResponse=Optional.empty, inlineData=Optional.empty, text=Optional.empty, thought=Optional.empty, thoughtSignature=Optional[[B@a7f0ab6], videoMetadata=Optional.empty}]], role=Optional[model]}], citationMetadata=Optional.empty, finishMessage=Optional.empty, tokenCount=Optional.empty, finishReason=Optional[STOP], avgLogprobs=Optional.empty, groundingMetadata=Optional.empty, index=Optional[0], logprobsResult=Optional.empty, safetyRatings=Optional.empty, urlContextMetadata=Optional.empty}]]
Parts: [Part{mediaResolution=Optional.empty, codeExecutionResult=Optional.empty, executableCode=Optional.empty, fileData=Optional.empty, functionCall=Optional[FunctionCall{id=Optional.empty, args=Optional[{city=Seoul}], name=Optional[get_weather], partialArgs=Optional.empty, willContinue=Optional.empty}], functionResponse=Optional.empty, inlineData=Optional.empty, text=Optional.empty, thought=Optional.empty, thoughtSignature=Optional[[B@a7f0ab6], videoMetadata=Optional.empty}]
=== Function Calls Detected ===
Function Call: get_weather
Arguments: {city=Seoul}
MCP Result: CallToolResult[content=[TextContent[annotations=null, text="The weather in Seoul is good.", meta=null]], isError=false, structuredContent=null, meta=null]
Extracted Result: "The weather in Seoul is good."
=== Calling Gemini Again with Function Results ===
=== Final Response ===
Assistant: 서울의 날씨는 좋습니다.
</code></pre><h2 id="참고-문서">참고 문서</h2>
<ul>
<li><a href="https://ai.google.dev/gemini-api/docs/function-calling?hl=ko&example=meeting" target="_blank" rel="noopener">Gemini API | Gemini API를 사용한 함수 호출<i class="fas fa-external-link-alt"></i></a></li>
</ul>
AI
kotlin
Gemini
-
Gemini API를 활용한 어플리케이션 만들기
https://www.devkuma.com/docs/ai/gemini/api/
Wed, 28 Jan 2026 18:08:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/gemini/api/
<h2 id="gemini-key-발급">Gemini Key 발급</h2>
<p>Google Gemini API를 사용하기 위해서는 API Key를 발급받아야 한다.</p>
<h3 id="1-gemini-developer-api-사이트에-접속">1. Gemini Developer API 사이트에 접속</h3>
<ul>
<li><a href="https://ai.google.dev/" target="_blank" rel="noopener">https://ai.google.dev/<i class="fas fa-external-link-alt"></i></a> 에 접속해서 <strong>Explore models in Google AI Studio</strong> 버튼을 클릭한다.</li>
<li>처음 접속하면 이용 약관이 표시되는데, 내용을 확인하고 “Continue” 버튼을 클릭한다.</li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/gemini-api-1.png" alt="Gemini Developer API"></p>
<h3 id="2-get-api-key-발급">2. Get API Key 발급</h3>
<ul>
<li><strong>Get API Key</strong> 메뉴를 선택하면, Key를 발급 받을 수 있다.</li>
<li>이미 발급을 받았다면 목록에 표시가 되었을 것이고, 발급 받은 적이 없다면 <strong>API 키 만들기</strong> 버튼을 발급 받을 수 있다.</li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/gemini-api-2.png" alt="Gemini API Key"></p>
<h3 id="무료-등급">무료 등급</h3>
<p>무료 등급은 모델별로 사용량에 제한이 있다.
gemini-3-flash 기준으로 일일 최대 요청 수(RPD)가 최대 20번 밖에 되지 않는다.</p>
<p><img src="https://www.devkuma.com/docs/ai/gemini-api-3.png" alt="Gemini API Key Free"></p>
<h2 id="라이브러리를-활용한-클라이언트-개발">라이브러리를 활용한 클라이언트 개발</h2>
<p>여기서는 Kotlin 언어로 Google GenAI SDK를 활용해서 API를 호출하는 방법을 알아보겠다.</p>
<h3 id="프로젝트-생성">프로젝트 생성</h3>
<p>IDE 도구를 이용해서 프로젝트를 생성한다.</p>
<pre tabindex="0"><code>.
├── build.gradle.kts
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
├── main
│ ├── kotlin
│ │ └── Main.kt
│ └── resources
└── test
├── kotlin
└── resources
</code></pre><h3 id="라이브러리-추가">라이브러리 추가</h3>
<p><strong>/build.gradle.kts</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">dependencies</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"com.google.genai:google-genai:1.36.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><ul>
<li>버전은 GitHub 저장소(<a href="https://github.com/googleapis/java-genai" target="_blank" rel="noopener">googleapis/java-genai<i class="fas fa-external-link-alt"></i></a>) 확인하여 최신 버전을 넣으면 된다.</li>
</ul>
<h3 id="클라이언트-개발">클라이언트 개발</h3>
<p><strong>/src/main/kotlin/Main.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.google.genai.Client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">client</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">apiKey</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"GEMINI_API_KEY"</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">models</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">generateContent</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"gemini-3-flash-preview"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"인공지능에 대해 한 문장으로 설명하세요."</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">null</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">text</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<pre tabindex="0"><code>인공지능은 인간의 학습, 추론, 지각 능력을 컴퓨터 시스템으로 구현하여 기계가 지능적인 작업을 수행할 수 있게 하는 기술입니다.
</code></pre><ul>
<li><strong>GEMINI_API_KEY</strong> 는 발급 받은 키를 넣으면 된다.</li>
</ul>
<h2 id="rest">REST</h2>
<p>Gemini API는 REST API로도 호출 할 수도 있다.</p>
<pre tabindex="0"><code>curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H 'Content-Type: application/json' \
-X POST \
-d '{
"contents": [
{
"parts": [
{
"text": "인공지능에 대해 한 문장으로 설명하세요."
}
]
}
]
}'
</code></pre><p>output:</p>
<pre tabindex="0"><code>{
"candidates": [
{
"content": {
"parts": [
{
"text": "인공지능은 인간의 학습, 추론, 지각 능력을 컴퓨터 시스템으로 구현하여 지적인 작업을 수행할 수 있도록 만든 기술입니다.",
"thoughtSignature": "Er8OCrwOAXLI2nzqxL3K8LCAB020BPaY+sv89...."
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0
}
],
"usageMetadata": {
"promptTokenCount": 13,
"candidatesTokenCount": 32,
"totalTokenCount": 415,
"promptTokensDetails": [
{
"modality": "TEXT",
"tokenCount": 13
}
],
"thoughtsTokenCount": 370
},
"modelVersion": "gemini-3-flash-preview",
"responseId": "5rh6afO6NYb22roPsKr06Ao"
}
</code></pre><h2 id="참고-문서">참고 문서</h2>
<ul>
<li><a href="https://ai.google.dev/gemini-api/docs" target="_blank" rel="noopener">Gemini API | Google AI for Developers<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<p>위에 예제 코드는 <a href="https://github.com/devkuma/kotlin-tutorial/tree/main/gemini-api-tutorial" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>에서 확인해 볼 수 있다.</p>
AI
kotlin
Gemini
-
Kotlin Exposed
https://www.devkuma.com/docs/kotlin/exposed/
Wed, 24 Dec 2025 10:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/exposed/
<h2 id="개요">개요</h2>
<p>Exposed는 JetBrains가 개발한 Kotlin용 경량 SQL 라이브러리로이다.</p>
<ul>
<li><a href="https://www.jetbrains.com/ko-kr/exposed/" target="_blank" rel="noopener">https://www.jetbrains.com/ko-kr/exposed/<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h2 id="특징">특징</h2>
<ul>
<li>타입 안정적인 DSL(Domain Specific Language)을 제공한다.</li>
<li>ORM(Object-Relational Mapping) 스타일로 쿼리를 작성할 수 있다.</li>
<li>JDBC 및 R2DBC 드라이버를 지원한다.</li>
<li>트랜잭션 관리, 마이그레이션 등 다양한 기능을 제공한다.</li>
<li>공식적으로 H2, MySQL, MariaDB, Oracle, PostgreSQL, SQL Server, SQLite 데이터베이스를 지원한다.</li>
<li>kotlin 타입 활용 가능하여 컴파일 시점에 타입 오류를 알 수 있다.</li>
<li>DSL, DAO 를 모두 제공해서 용도에 맞게 적합한 것을 선택할 수 있다.</li>
<li>코루틴 호환성: 비동기 프로그래밍을 지원해서 블로킹 작업을 효율적으로 처리할 수 있다.
<ul>
<li>Exposed는 내부적으로 JDBC를 사용해서 DB와 통신하고, JDBC는 전통적인 blocking API 이므로 DB 작업을 수행할 때 해당 작업이 완료될 때까지 스레드가 블로킹된다. 따라서 Exposed 자체는 non-blocking API를 제공하지 않지만 코루틴과 함께 사용할 경우 코루틴의 Dispatcher를 활용해서 블로킹 작업을 적절한 스레드 풀에서 실행하도록 관리할 수 있고, 이를 통해 메인 스레드를 차단하지 않고도 DB 작업을 할 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="데이터베이스-접근-방식">데이터베이스 접근 방식</h2>
<p>Exposed는 두 가지 레벨의 데이터베이스 access를 제공한다.</p>
<ul>
<li>SQL을 매핑 한 DSL(Domain Specific Language) 방식
<ul>
<li>SQL 과 유사한 형태로 쿼리를 작성할 수 있어서 익숙한 SQL 개념을 사용하면서도 kotlin 이 제공하는 타입 안정성의 이점을 얻을 수 있다.</li>
</ul>
</li>
<li>경량화한 DAO(Data Access Object) 방식
<ul>
<li>전통적인 ORM 프레임워크인 Hibernate와 유사하게 객체지향적 접근 방식을 제공한다.</li>
<li>객체 중심으로 DB 엔티티를 다루고, 테이블을 클래스로 표현하므로 DB 레코드를 직접 객체로 매핑할 수 있다.</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-kolin" data-lang="kolin">// DSL 방식
val users = UsersTable.select { UsersTable.age greater 20 }
// DAO 방식
val user = User.findById(1)
</code></pre><p>프로젝트의 요구사항에 가장 적합한 방식을 선택할 수 있다.</p>
-
Spring에 적용하는 gRPC 라이브러리 비교
https://www.devkuma.com/docs/spring-grpc/3rd-party-vs-spring-project/
Sat, 15 Nov 2025 21:37:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-grpc/3rd-party-vs-spring-project/
<h2 id="개요">개요</h2>
<p>Spring 환경에 gRPC를 적용하는 방법은 크게 2가지 라이브러리가 있다.</p>
<ul>
<li>net.devh:grpc-server-spring-boot-starter</li>
<li>org.springframewor.grpc:spring-grpc-spring-boot-starter</li>
</ul>
<p>두 라이브러리는 <strong>Spring 환경에 gRPC를 붙인다는 점에서는 비슷하지만</strong>, <strong>출신·철학·설계·지원 방식·미래성</strong>이 크게 다르다.</p>
<p>본인의 프로젝트 어떤 라이브러리를 적용해야 할지 비교 내용을 확인하고 선택하길 바란다.</p>
<hr>
<h2 id="라이브러리-비교">라이브러리 비교</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th><strong>net.devh:grpc-server-spring-boot-starter</strong></th>
<th><strong>org.springframework.grpc:spring-grpc-spring-boot-starter</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>출처</td>
<td>커뮤니티/오픈소스 (3rd-party)</td>
<td>Spring 공식 프로젝트 (Spring Team이 개발)</td>
</tr>
<tr>
<td>안정성</td>
<td>오랫동안 널리 사용됨</td>
<td>아직 초기(1.0 전후), 빠르게 발전 중</td>
</tr>
<tr>
<td>최신성</td>
<td>Spring Boot 3, Native 지원 부족</td>
<td>Spring Boot 3 / AOT / Native 완전 지원</td>
</tr>
<tr>
<td>아키텍처</td>
<td>gRPC 서버/클라이언트 Wrapping 중심</td>
<td>Spring 관점의 신규 gRPC 추상화 제공</td>
</tr>
<tr>
<td>의존성 구조</td>
<td>netty 기반의 직접 gRPC 통합</td>
<td>Spring Framework 6 기반 추상화 + AutoConfig</td>
</tr>
<tr>
<td>클라이언트 방식</td>
<td>직접 Stub 생성 + annotation 기반 주입</td>
<td>채널 팩토리 기반, Spring Bean 방식</td>
</tr>
<tr>
<td>확장성</td>
<td>Interceptor 등 기존 방식 고착</td>
<td>Spring 생태계에 맞춘 고급 확장 기능 가능</td>
</tr>
<tr>
<td>장기적 미래</td>
<td>유지보수는 하지만 성장 둔화</td>
<td>Spring 공식 표준으로 자리잡을 가능성 높음</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="출신과-배경">출신과 배경</h2>
<h3 id="netdevh-전통적인-방식">net.devh (전통적인 방식)</h3>
<ul>
<li>커뮤니티에서 오래전부터 만들어진 <strong>3rd-party 외부 라이브러리</strong></li>
<li>Spring Boot 1.x ~ 2.x 시절부터 gRPC를 쉽게 붙이기 위해 시작됨</li>
<li>기업에서 꽤 많이 사용했고, 안정성이 높음</li>
<li>하지만 Spring 팀이 만든 게 아님</li>
<li>최근에는 “유지보수는 하지만 새로운 기능은 적음”</li>
</ul>
<h3 id="spring-grpc-신규-공식">Spring gRPC (신규, 공식)</h3>
<ul>
<li><strong>Spring 프로젝트 공식 gRPC 지원</strong></li>
<li>Spring Framework 6 / Boot 3 에 맞춰 새롭게 설계됨</li>
<li>Spring 팀이 직접 관리하는 미래 지향 프로젝트</li>
<li>AOT, Native Image, Observability 등 Spring 생태계와 완전 호환</li>
</ul>
<h2 id="설계-철학-및-아키텍처-차이">설계 철학 및 아키텍처 차이</h2>
<h3 id="netdevh">net.devh</h3>
<ul>
<li>gRPC의 원래 Netty 기반 서버를 Spring 방식으로 감싼 것</li>
<li><code>@GrpcService</code>, <code>@GrpcClient</code> 등을 제공</li>
<li>Stub이나 Channel을 직접 관리하는 형태</li>
<li>확장성은 있지만 Spring스럽지 않은 부분이 많음</li>
<li>“Spring 애플리케이션 안에 gRPC를 집어넣는다"는 느낌</li>
</ul>
<h3 id="spring-grpc">Spring gRPC</h3>
<ul>
<li>gRPC 기능을 Spring Framework의 개념 안으로 재해석</li>
<li>채널 추상화, 서버 추상화, Bean 구성까지 Spring 방식으로 설계</li>
<li><strong>gRPC = Spring의 1급 지원 기능</strong>으로 편입</li>
<li>net.devh처럼 그냥 Wrapping한 것이 아님</li>
<li>Spring AOT(Native), GraalVM, Observability(Micrometer)와 함께 동작하도록 설계</li>
</ul>
<p>→ 즉, 단순 통합이 아니라 “Spring스러운 gRPC 구현체"라고 보면 됨.</p>
<h2 id="사용-방식-비교">사용 방식 비교</h2>
<h3 id="netdevh-1">net.devh</h3>
<p>서비스 등록</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@GrpcService</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">public</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">HelloServiceImpl</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">extends</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">HelloServiceGrpc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">HelloServiceImplBase</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">{</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">...</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">}</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div><p>클라이언트 주입</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@GrpcClient</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"myService"</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">private</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">HelloServiceGrpc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">HelloServiceBlockingStub</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">stub</span><span style="color:#000;font-weight:bold">;</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div><ul>
<li>Stub은 자동 주입되지만 내부 구현이 다소 난해</li>
<li>클라이언트 채널 관리가 덜 투명함</li>
</ul>
<h3 id="spring-grpc-1">Spring gRPC</h3>
<p>서비스 등록
→ <code>BindableService</code> 빈을 등록하면 자동 인식</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Service</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">HelloServiceImpl</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloServiceGrpcKt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceCoroutineImplBase</span><span style="color:#000;font-weight:bold">()</span>
</span></span></code></pre></div><p>클라이언트 생성
→ 채널 팩토리로 gRPC 채널을 만들고 Stub 직접 주입</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Bean</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">factory</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">GrpcChannelFactory</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">HelloServiceGrpcKt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceCoroutineStub</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">factory</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">createChannel</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"local"</span><span style="color:#000;font-weight:bold">))</span>
</span></span></code></pre></div><ul>
<li>Spring Bean 시스템에 완전히 통합된 API</li>
<li>Spring Cloud LoadBalancer, Metrics, AOT 등과 자연스럽게 동작</li>
</ul>
<h2 id="성숙도-및-실제-운영에서의-평가">성숙도 및 실제 운영에서의 평가</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>net.devh</th>
<th>spring-grpc</th>
</tr>
</thead>
<tbody>
<tr>
<td>성숙도</td>
<td>매우 높음</td>
<td>아직 초기</td>
</tr>
<tr>
<td>대기업 실제 사용</td>
<td>많음</td>
<td>점점 증가 중</td>
</tr>
<tr>
<td>Spring Boot 3 / AOT</td>
<td>일부 제한적</td>
<td>완전 지원</td>
</tr>
<tr>
<td>문서</td>
<td>GitHub 위키 중심</td>
<td>Spring 공식 문서 시스템</td>
</tr>
<tr>
<td>확장성</td>
<td>기존 gRPC 방식 그대로</td>
<td>Spring-native 확장 가능</td>
</tr>
</tbody>
</table>
<h2 id="어떤-걸-선택해야-하나">어떤 걸 선택해야 하나?</h2>
<h3 id="즉시-안정성이-중요하면--netdevh-추천">즉시 안정성이 중요하면 → <strong>net.devh 추천</strong></h3>
<ul>
<li>기존 기업 레거시/대규모 프로젝트 호환성 최고</li>
<li>문서 많고 예제 많음</li>
<li>이미 검증된 방식</li>
</ul>
<h3 id="장기적으로-최신-spring-기술을-쓴다면--spring-grpc-추천">장기적으로 최신 Spring 기술을 쓴다면 → <strong>Spring gRPC 추천</strong></h3>
<ul>
<li>Spring Boot 3.x / Spring 6.x 와 최적화됨</li>
<li>공식이기 때문에 Spring 에코시스템과 긴밀하게 진화</li>
<li>AOT + GraalVM Native Image 최적</li>
</ul>
<h3 id="절대로-섞어쓰면-안-됨">절대로 섞어쓰면 안 됨</h3>
<p>두 라이브러리는 내부적으로 서버 · Stub 관리 방식이 완전히 다르므로 공존이 불가능함.</p>
<h2 id="결론">결론</h2>
<h3 id="netdevh-2">net.devh</h3>
<ul>
<li>3rd party</li>
<li>오래 사용됨, 안정적</li>
<li>Spring Boot 1.x~2.x 시대의 솔루션</li>
<li>앞으로 큰 신규 기능은 기대 어려움</li>
</ul>
<h3 id="spring-grpc-2">Spring gRPC</h3>
<ul>
<li>Spring 공식 프로젝트</li>
<li>Boot 3 / Spring 6 / Native / Observability 등 현대적 기능 지원</li>
<li>향후 Spring 진영의 표준 gRPC 솔루션이 될 가능성이 큼</li>
</ul>
Spring
Kotlin
gRPC
-
gRPC란 무엇인가?
https://www.devkuma.com/docs/grpc/
Sat, 15 Nov 2025 17:05:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/grpc/
<h2 id="grpc란">gRPC란?</h2>
<p>gRPC는 <strong>Google에서 개발한 고성능, 범용 오픈소스 RPC(Remote Procedure Call) 프레임워크</strong>로, 서로 다른 서비스 간의 통신을 빠르고 효율적으로 수행하기 위해 만들어진 기술이다.</p>
<p>gRPC는 “네트워크 너머에 있는 함수를, 마치 내 코드 안의 함수처럼 호출할 수 있게 해주는 기술"이다. 즉, 서비스 간에 **원격 함수를 호출하는 방식(RPC)**을 표준화하고, <strong>빠르고 타입 안전한 통신</strong>을 가능하게 한다.</p>
<p>전통적인 REST 기반 통신이 주로 HTTP/1.1과 JSON을 사용하여 데이터를 주고받는 것과 달리, gRPC는 HTTP/2 프로토콜과 **Protocol Buffers(ProtoBuf)**라는 이진 직렬화 포맷을 기반으로 한다. 이러한 기술적 특성 덕분에 gRPC는 낮은 지연 시간, 높은 처리량, 경량의 메시지 크기로 빠르고 효율적인 통신을 할 수 있다.</p>
<p>또한 gRPC는 서비스 간 API를 <code>.proto</code> 파일로 명세함으로써, 서버와 클라이언트가 동일한 인터페이스 정의를 공유하도록 하며, 여러 프로그래밍 언어에 대한 자동 Stub 생성을 지원한다. 이러한 구조는 다중 언어 환경에서의 개발을 용이하게 하고, 서비스 경계를 명확히 하여 유지보수성을 크게 향상시킨다.</p>
<p><img src="https://www.devkuma.com/docs/spring-grpc/images/gRPC-Flow.png" alt="gRPC Flow">
출처: <a href="https://appmaster.io/ko/blog/grpcneun-mueosibnigga" target="_blank" rel="noopener">https://appmaster.io/ko/blog/grpcneun-mueosibnigga<i class="fas fa-external-link-alt"></i></a></p>
<h2 id="grpc의-핵심-요소">gRPC의 핵심 요소</h2>
<h3 id="1-protocol-buffers프로토콜-버퍼-기반">1. <strong>Protocol Buffers(프로토콜 버퍼)</strong> 기반</h3>
<p>gRPC는 JSON 대신 **프로토콜 버퍼(ProtoBuf)**라는 바이너리 포맷을 사용한다.</p>
<ul>
<li>JSON보다 <strong>작고 빠름</strong></li>
<li>명확한 <strong>스키마(protobuf 파일)</strong> 기반</li>
<li>각 언어로 자동으로 <strong>Stub 코드(클라이언트/서버 코드)</strong> 생성</li>
</ul>
<p>예: <code>hello.proto</code></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-protobuf" data-lang="protobuf"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">service</span> <span style="color:#000">HelloService</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">rpc</span> <span style="color:#000">SayHello</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">returns</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">HelloResponse</span><span style="color:#000;font-weight:bold">);</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">message</span> <span style="color:#000">HelloRequest</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">string</span> <span style="color:#000">name</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">message</span> <span style="color:#000">HelloResponse</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">string</span> <span style="color:#204a87;font-weight:bold">message</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span></code></pre></div><h3 id="2-http2-기반-통신">2. <strong>HTTP/2 기반 통신</strong></h3>
<p>gRPC는 내부적으로 <strong>HTTP/2</strong> 프로토콜을 사용합니다.</p>
<p>그래서 다음이 가능합니다:</p>
<ul>
<li><strong>Multiplexing</strong> (하나의 연결로 여러 요청)</li>
<li><strong>Server Push</strong></li>
<li><strong>헤더 압축</strong></li>
<li><strong>스트리밍(양방향 포함)</strong></li>
</ul>
<p>즉, REST보다 <strong>더 빠르고 효율적인</strong> 구조입니다.</p>
<h2 id="grpc-통신-방식-4가지">gRPC 통신 방식 (4가지)</h2>
<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td>Unary</td>
<td>가장 기본적인 1요청 → 1응답</td>
</tr>
<tr>
<td>Server Streaming</td>
<td>서버가 여러 메시지를 스트림으로 전달</td>
</tr>
<tr>
<td>Client Streaming</td>
<td>클라이언트가 여러 메시지를 보내서 서버가 한 번 응답</td>
</tr>
<tr>
<td>Bidirectional Streaming</td>
<td>클라이언트와 서버가 동시에 여러 메시지를 주고받음</td>
</tr>
</tbody>
</table>
<blockquote>
<p>예: 채팅, 실시간 로그 처리, 스트리밍 데이터 처리에 적합</p></blockquote>
<h2 id="grpc의-장단점">gRPC의 장단점</h2>
<h3 id="장점">장점</h3>
<ul>
<li>빠르고 가벼운 통신
<ul>
<li>프로토콜 버퍼 + HTTP/2 조합으로 매우 고성능.</li>
</ul>
</li>
<li>강력한 타입 안정성
<ul>
<li>proto 스키마 기반으로 컴파일 시 오류 확인 가능.</li>
</ul>
</li>
<li>멀티언어 지원
<ul>
<li>Go, Java, Kotlin, Python, Rust, C#, Node, Ruby 등 대부분의 언어에서 사용 가능.</li>
</ul>
</li>
<li>스트리밍 지원
<ul>
<li>REST에서는 어렵거나 번거로운 <strong>양방향 스트리밍</strong>을 기본으로 지원.</li>
</ul>
</li>
<li>마이크로서비스에 최적화
<ul>
<li>내부 통신은 REST보다 gRPC 채택률이 높음.</li>
</ul>
</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>브라우저 직접 호출 어려움 (WebAssembly나 gRPC-Web 필요)</li>
<li>개발자가 proto 작성, 컴파일 과정 이해 필요</li>
<li>디버깅이 REST보다 불편</li>
</ul>
<h2 id="언제-grpc를-써야-하나">언제 gRPC를 써야 하나?</h2>
<h3 id="적합한-경우">적합한 경우</h3>
<ul>
<li>마이크로서비스 내부 통신</li>
<li>고성능이 중요한 트래픽</li>
<li>실시간 스트리밍 서비스</li>
<li>다국어 서비스(서버는 Java, 클라이언트는 Go 등)</li>
<li>IoT, 게임 서버, 실시간 이벤트 처리</li>
</ul>
<h3 id="rest가-더-나은-경우">REST가 더 나은 경우</h3>
<ul>
<li>브라우저 직접 호출 API</li>
<li>단순한 public API</li>
<li>사람이 읽기 편한 포맷이 필요할 때(JSON)</li>
</ul>
<p>아래는 <strong>gRPC vs REST</strong> 차이를 한눈에 볼 수 있도록 정리한 비교 표입니다.
실무에서 판단 기준으로 쓰기 좋은 항목들만 골랐습니다.</p>
<h2 id="grpc-vs-rest-비교표">gRPC vs REST 비교표</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th><strong>gRPC</strong></th>
<th><strong>REST</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>통신 프로토콜</strong></td>
<td>HTTP/2</td>
<td>HTTP/1.1 (HTTP/2도 가능하나 대부분 1.1)</td>
</tr>
<tr>
<td><strong>데이터 포맷</strong></td>
<td>Protocol Buffers(바이너리)</td>
<td>JSON, XML 등 텍스트 기반</td>
</tr>
<tr>
<td><strong>속도</strong></td>
<td>매우 빠름 (JSON보다 작고 parsing 효율적)</td>
<td>상대적으로 느림</td>
</tr>
<tr>
<td><strong>스트리밍 지원</strong></td>
<td>완전 지원 (양방향 스트리밍)</td>
<td>제한적 (Server-Sent Events / WebSocket 별도 필요)</td>
</tr>
<tr>
<td><strong>인터페이스 정의</strong></td>
<td><code>.proto</code> 파일에서 타입·스키마 명확하게 정의</td>
<td>별도의 스키마 강제 없음(오픈 API 권장)</td>
</tr>
<tr>
<td><strong>타입 안정성</strong></td>
<td>컴파일 타임 검증 가능</td>
<td>런타임에 검증되는 경우가 많음</td>
</tr>
<tr>
<td><strong>코드 생성</strong></td>
<td>서버/클라이언트 Stub 자동 생성</td>
<td>Swagger/OpenAPI로 문서화는 가능하지만 Stub은 필수 아님</td>
</tr>
<tr>
<td><strong>사용 편의성</strong></td>
<td>브라우저 직접 호출 어려움(gRPC-Web 별도 필요)</td>
<td>브라우저/JavaScript에서 바로 사용 가능</td>
</tr>
<tr>
<td><strong>인간 가독성</strong></td>
<td>낮음 (바이너리)</td>
<td>높음(JSON 읽기 쉬움)</td>
</tr>
<tr>
<td><strong>디버깅</strong></td>
<td>어려움 (바이너리 포맷)</td>
<td>쉬움 (REST는 Curl·Postman으로 바로 요청 가능)</td>
</tr>
<tr>
<td><strong>보안</strong></td>
<td>TLS+HTTP/2 조합 매우 강력</td>
<td>표준적 TLS 보안</td>
</tr>
<tr>
<td><strong>적합한 서비스</strong></td>
<td>마이크로서비스 내부 통신, 고성능, 실시간 스트리밍</td>
<td>Public API, 외부 서비스 연동, 브라우저 중심 서비스</td>
</tr>
<tr>
<td><strong>언어 지원</strong></td>
<td>거의 모든 언어 공식 지원</td>
<td>언어 무관 (HTTP 지원하면 됨)</td>
</tr>
<tr>
<td><strong>장점 요약</strong></td>
<td>빠름, 효율적, 타입 안전, 스트리밍 강력</td>
<td>단순함, 디버깅 쉬움, 호환성 높음</td>
</tr>
<tr>
<td><strong>단점 요약</strong></td>
<td>복잡, 브라우저 접근성 낮음</td>
<td>성능 열세, 타입 안전성 부족</td>
</tr>
</tbody>
</table>
<h3 id="결론-요약">결론 요약</h3>
<ul>
<li><strong>gRPC</strong>: 내부 통신/고성능/스트리밍에 최적</li>
<li><strong>REST</strong>: 브라우저·대중 API·디버깅 편의성에 최적</li>
</ul>
Spring
Kotlin
gRPC
-
Spring에 net.devh 라이브러리를 활용한 gRPC 구현
https://www.devkuma.com/docs/spring-grpc/net-devh/
Sat, 15 Nov 2025 17:05:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-grpc/net-devh/
<h2 id="개요">개요</h2>
<p>Spring에 Kotlin와 커뮤니티/오픈소스(3rd-party) 라이브러리인 <code>net.devh</code>를 활용한 간단한 gRPC 구현을 해보겠다.</p>
<h3 id="목표">목표</h3>
<ul>
<li>Spring Boot 프로젝트에 커뮤니티/오픈소스(3rd-party) 라이브러리인 <code>net.devh</code>를 활용하여 gRPC 서버를 연동</li>
<li><code>.proto</code> 파일 작성 및 Stub 자동 생성</li>
<li>gRPC 서비스 구현</li>
<li>gRPC 클라이언트 작성 및 호출</li>
</ul>
<h2 id="프로젝트-생성">프로젝트 생성</h2>
<p>프로젝트 생성은 <a href="https://start.spring.io/#!type=gradle-project-kotlin&language=kotlin&platformVersion=3.5.7&packaging=jar&configurationFileFormat=properties&jvmVersion=21&groupId=com.devkuma&artifactId=spring-grpc&name=spring-grpc&description=Demo%20project%20for%20Spring%20gRPC&packageName=com.devkuma.grpc&dependencies=" target="_blank" rel="noopener">Spirng ininitializr<i class="fas fa-external-link-alt"></i></a>으로 생성할 수 있다.</p>
<p><img src="https://www.devkuma.com/docs/spring-grpc/images/Spring-gRPC_net-devh-initializr.png" alt="Spirng ininitializr"></p>
<p>화면 아래 부근에 “GENERATE” 버튼을 누르면, 설정한 프로젝트 파일을 다운로드를 받을 수 있다.</p>
<p>또는, 다음과 같이 <code>curl</code> 명령어를 사용하여 Spring Boot 초기 프로젝트를 생성할 수도 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>curl https://start.spring.io/starter.tgz <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">bootVersion</span><span style="color:#ce5c00;font-weight:bold">=</span>3.5.7 <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">baseDir</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-grpc-1 <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">groupId</span><span style="color:#ce5c00;font-weight:bold">=</span>com.devkuma <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">artifactId</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-grpc <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">packageName</span><span style="color:#ce5c00;font-weight:bold">=</span>com.devkuma.grpc <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">applicationName</span><span style="color:#ce5c00;font-weight:bold">=</span>GrpcApplication <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">packaging</span><span style="color:#ce5c00;font-weight:bold">=</span>jar <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">language</span><span style="color:#ce5c00;font-weight:bold">=</span>kotlin <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">javaVersion</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">21</span> <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">description</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">"Demo project for Spring gRPC"</span> <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">=</span>gradle-project-kotlin <span style="color:#000;font-weight:bold">|</span> tar -xzvf -
</span></span></code></pre></div><h3 id="빌드-스크립트-설정">빌드 스크립트 설정</h3>
<p>빌드 스크립트에 gRPC 관련 라이브러리를 추가하고, protobuf 설정을 작성한다.</p>
<p><strong>build.gradle</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">plugins</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">kotlin</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"jvm"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"1.9.25"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">kotlin</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"plugin.spring"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"1.9.25"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.boot"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"3.5.7"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.spring.dependency-management"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"1.1.7"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// gRPC
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"com.google.protobuf"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"0.9.5"</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">group</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"com.devkuma"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">version</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"0.0.1-SNAPSHOT"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">description</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"Demo project for Spring gRPC"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">java</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">toolchain</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">languageVersion</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">JavaLanguageVersion</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">of</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">21</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">repositories</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">mavenCentral</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">dependencies</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.boot:spring-boot-starter"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.jetbrains.kotlin:kotlin-reflect"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">testImplementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.boot:spring-boot-starter-test"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">testImplementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.jetbrains.kotlin:kotlin-test-junit5"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">testRuntimeOnly</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.junit.platform:junit-platform-launcher"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// gRPC + protobuf
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-kotlin-stub:1.4.3"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-protobuf:1.75.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-stub:1.75.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-netty-shaded:1.75.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"com.google.protobuf:protobuf-kotlin:3.22.3"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Spring gRPC Starter
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"net.devh:grpc-server-spring-boot-starter:3.1.0.RELEASE"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">kotlin</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">compilerOptions</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">freeCompilerArgs</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addAll</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"-Xjsr305=strict"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">protobuf</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">protoc</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">artifact</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"com.google.protobuf:protoc:3.24.4"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">plugins</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">register</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"grpc"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">artifact</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"io.grpc:protoc-gen-grpc-java:1.75.0"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">generateProtoTasks</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">all</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">task</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">task</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">plugins</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">create</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"grpc"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">withType</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Test</span><span style="color:#000;font-weight:bold">></span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">useJUnitPlatform</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="protobuf-파일-작성">Protobuf 파일 작성</h3>
<p><code>src/main/proto</code> 디렉토리에 gRPC 서비스와 메시지를 정의한 <code>.proto</code> 파일을 작성한다.</p>
<p>**src/main/proto/hello.proto</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-proto" data-lang="proto"><span style="display:flex;"><span><span style="color:#000">syntax</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"proto3"</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">option</span> <span style="color:#000">java_multiple_files</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">option</span> <span style="color:#000">java_package</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"com.devkuma.grpc"</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">option</span> <span style="color:#000">java_outer_classname</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"HelloProto"</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">service</span> <span style="color:#000">HelloService</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">rpc</span> <span style="color:#000">SayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">returns</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">HelloResponse</span><span style="color:#000;font-weight:bold">);</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">message</span> <span style="color:#000">HelloRequest</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">string</span> <span style="color:#000">name</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">message</span> <span style="color:#000">HelloResponse</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">string</span> <span style="color:#204a87;font-weight:bold">message</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span></code></pre></div><h2 id="grpc-서버-구현">gRPC 서버 구현</h2>
<h3 id="service-객체-생성">Service 객체 생성</h3>
<p><strong>src/main/kotlin/com/devkuma/grpc/service/HelloServiceImpl.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc.service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloRequest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloResponse</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloServiceGrpc</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">net.devh.boot.grpc.server.service.GrpcService</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.grpc.stub.StreamObserver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@GrpcService</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">HelloServiceImpl</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloServiceGrpc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceImplBase</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">override</span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">request</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">responseObserver</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">StreamObserver</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">HelloResponse</span><span style="color:#000;font-weight:bold">></span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">reply</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"Hello, </span><span style="color:#4e9a06">${request.name}</span><span style="color:#4e9a06">!"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HelloResponse</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBuilder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">setMessage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">reply</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">responseObserver</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">onNext</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">response</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">responseObserver</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">onCompleted</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="서버-설정">서버 설정</h3>
<p>기본 gRPC 포트는 <code>9090</code>이다.<br>
원하면 <code>application.yml</code>로 변경 가능하다.</p>
<p><strong>src/main/kotli/resources/application.yml</strong></p>
<pre tabindex="0"><code>grpc:
server:
port: 9090
</code></pre><h2 id="grpc-클라이언트-구현">gRPC 클라이언트 구현</h2>
<p>같은 프로젝트에서 테스트를 하기 위해, 클라이언트 설정 Bean을 추가한다.</p>
<h3 id="클라이언-설정">클라이언 설정</h3>
<p><strong>src/main/kotlin/com/devkuma/grpc/client/GrpcClientConfig.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloServiceGrpc</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.grpc.ManagedChannel</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.grpc.ManagedChannelBuilder</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Bean</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Configuration</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">GrpcClientConfig</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Bean</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">helloChannel</span><span style="color:#000;font-weight:bold">():</span> <span style="color:#000">ManagedChannel</span> <span style="color:#000;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">ManagedChannelBuilder</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">forAddress</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"localhost"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">9090</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">usePlaintext</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Bean</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">channel</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">ManagedChannel</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">HelloServiceGrpc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceBlockingStub</span> <span style="color:#000;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">HelloServiceGrpc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBlockingStub</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">channel</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h4 id="grpc-stub을-호출하는-service-생성">gRPC Stub을 호출하는 Service 생성</h4>
<p>단순히 stub을 호출하는 Service를 생성한다.</p>
<p><strong>src/main/kotlin/com/devkuma/grpc/client/HelloGrpcClientService.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloRequest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloServiceGrpc</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.stereotype.Service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Service</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">HelloGrpcClientService</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">private</span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloServiceGrpc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceBlockingStub</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">String</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">String</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">request</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBuilder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">setName</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">request</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">message</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h2 id="테스트">테스트</h2>
<p>서버와 클라이언트를 하나의 프로젝트에 넣었기에, 테스트 파일 1개로 테스트가 가능하다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.client.HelloGrpcClientService</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.junit.jupiter.api.Test</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.beans.factory.annotation.Autowired</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.boot.test.context.SpringBootTest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">kotlin.text.contains</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@SpringBootTest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">GrpcIntegrationTest</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Autowired</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">lateinit</span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloGrpcClientService</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Test</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">testSayHello</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">result</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">assert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contains</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma"</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>Hello, devkuma!
</span></span></code></pre></div><h2 id="참고">참고</h2>
<p>위에 예제 코드는 <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-grpc-1" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>에서 확인해 볼 수 있다.</p>
Spring
Kotlin
gRPC
-
Spring 공식 프로젝트 라이브러리를 활용한 gRPC 구현
https://www.devkuma.com/docs/spring-grpc/spring-framwork/
Sat, 15 Nov 2025 17:05:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-grpc/spring-framwork/
<h2 id="개요">개요</h2>
<p>Spring 공식 프로젝트(Spring Team이 개발) gRPC 라이브러리를 활용한 gRPC 구현을 해보겠다.</p>
<h3 id="목표">목표</h3>
<ul>
<li>Spring Boot 프로젝트에 Spring 공식 프로젝트 gRPC 라이브러리를 활용한 gRPC 서버를 연동</li>
<li><code>.proto</code> 파일 작성 및 Stub 자동 생성</li>
<li>gRPC 서비스 구현</li>
<li>gRPC 클라이언트 작성 및 호출</li>
</ul>
<h2 id="프로젝트-생성">프로젝트 생성</h2>
<p>프로젝트 생성은 <a href="https://start.spring.io/#!type=gradle-project-kotlin&language=kotlin&platformVersion=3.5.7&packaging=jar&configurationFileFormat=properties&jvmVersion=21&groupId=com.devkuma&artifactId=spring-grpc&name=spring-grpc&description=Demo%20project%20for%20Spring%20gRPC&packageName=com.devkuma.grpc&dependencies=spring-grpc" target="_blank" rel="noopener">Spirng ininitializr<i class="fas fa-external-link-alt"></i></a>으로 생성할 수 있다.</p>
<p><img src="https://www.devkuma.com/docs/spring-grpc/images/Spring-gRPC-spring-framework-initializr.png" alt="Spirng ininitializr"></p>
<p>화면 아래 부근에 “GENERATE” 버튼을 누르면, 설정한 프로젝트 파일을 다운로드를 받을 수 있다.</p>
<p>또는, 다음과 같이 <code>curl</code> 명령어를 사용하여 Spring Boot 초기 프로젝트를 생성할 수도 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>curl https://start.spring.io/starter.tgz <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">bootVersion</span><span style="color:#ce5c00;font-weight:bold">=</span>3.5.7 <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">dependencies</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-grpc <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">baseDir</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-grpc-2 <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">groupId</span><span style="color:#ce5c00;font-weight:bold">=</span>com.devkuma <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">artifactId</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-grpc <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">packageName</span><span style="color:#ce5c00;font-weight:bold">=</span>com.devkuma.grpc <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">applicationName</span><span style="color:#ce5c00;font-weight:bold">=</span>GrpcApplication <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">packaging</span><span style="color:#ce5c00;font-weight:bold">=</span>jar <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">language</span><span style="color:#ce5c00;font-weight:bold">=</span>kotlin <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">javaVersion</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">21</span> <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">description</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">"Demo project for Spring gRPC"</span> <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">=</span>gradle-project-kotlin <span style="color:#000;font-weight:bold">|</span> tar -xzvf -
</span></span></code></pre></div><h3 id="빌드-스크립트-설정">빌드 스크립트 설정</h3>
<p>빌드 스크립트에 gRPC 관련 라이브러리를 추가하고, protobuf 설정을 작성한다.</p>
<p><strong>build.gradle</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.google.protobuf.gradle.id</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">plugins</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">kotlin</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"jvm"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"1.9.25"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">kotlin</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"plugin.spring"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"1.9.25"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.boot"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"3.5.7"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.spring.dependency-management"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"1.1.7"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// gRPC
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"com.google.protobuf"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">version</span> <span style="color:#4e9a06">"0.9.5"</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">group</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"com.devkuma"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">version</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"0.0.1-SNAPSHOT"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">description</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"Demo project for Spring gRPC"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">java</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">toolchain</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">languageVersion</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">JavaLanguageVersion</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">of</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">21</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">repositories</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">mavenCentral</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">extra</span><span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">"springGrpcVersion"</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"0.12.0"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">dependencies</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-services"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.jetbrains.kotlin:kotlin-reflect"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.grpc:spring-grpc-spring-boot-starter"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// Spring gRPC Starter
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">testImplementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.boot:spring-boot-starter-test"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">testImplementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.jetbrains.kotlin:kotlin-test-junit5"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">testImplementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.grpc:spring-grpc-test"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">testRuntimeOnly</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.junit.platform:junit-platform-launcher"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Kotlin coroutines
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// gRPC + protobuf
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-kotlin-stub:1.4.3"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-protobuf:1.75.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-stub:1.75.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"io.grpc:grpc-netty-shaded:1.75.0"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">dependencyManagement</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">imports</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">mavenBom</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.grpc:spring-grpc-dependencies:</span><span style="color:#4e9a06">${property("springGrpcVersion")}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">kotlin</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">compilerOptions</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">freeCompilerArgs</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addAll</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"-Xjsr305=strict"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">protobuf</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">protoc</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">artifact</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"com.google.protobuf:protoc"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">plugins</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"grpc"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">artifact</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"io.grpc:protoc-gen-grpc-java"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"grpckt"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">artifact</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"io.grpc:protoc-gen-grpc-kotlin:1.4.3:jdk8@jar"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">generateProtoTasks</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">all</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">plugins</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"grpc"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">option</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"@generated=omit"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"grpckt"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">option</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"@generated=omit"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">withType</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Test</span><span style="color:#000;font-weight:bold">></span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">useJUnitPlatform</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="protobuf-파일-작성">Protobuf 파일 작성</h3>
<p><code>src/main/proto</code> 디렉토리에 gRPC 서비스와 메시지를 정의한 <code>.proto</code> 파일을 작성한다.</p>
<p>**src/main/proto/hello.proto</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-proto" data-lang="proto"><span style="display:flex;"><span><span style="color:#000">syntax</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"proto3"</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">option</span> <span style="color:#000">java_multiple_files</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">option</span> <span style="color:#000">java_package</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"com.devkuma.grpc"</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">option</span> <span style="color:#000">java_outer_classname</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"HelloProto"</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">service</span> <span style="color:#000">HelloService</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">rpc</span> <span style="color:#000">SayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">returns</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">HelloResponse</span><span style="color:#000;font-weight:bold">);</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">message</span> <span style="color:#000">HelloRequest</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">string</span> <span style="color:#000">name</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#204a87;font-weight:bold">message</span> <span style="color:#000">HelloResponse</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span> <span style="color:#204a87;font-weight:bold">string</span> <span style="color:#204a87;font-weight:bold">message</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span><span style="color:#a40000">
</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">
</span></span></span></code></pre></div><h2 id="grpc-서버-구현">gRPC 서버 구현</h2>
<h3 id="service-객체-생성">Service 객체 생성</h3>
<p><strong>src/main/kotlin/com/devkuma/grpc/service/HelloServiceImpl.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc.service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloRequest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloResponse</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloServiceGrpcKt</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">kotlinx.coroutines.Dispatchers</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">kotlinx.coroutines.withContext</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.stereotype.Service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Service</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">HelloServiceImpl</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloServiceGrpcKt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceCoroutineImplBase</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">override</span> <span style="color:#204a87;font-weight:bold">suspend</span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">request</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">HelloResponse</span> <span style="color:#000;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">withContext</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Dispatchers</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">Default</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">message</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"Hello, </span><span style="color:#4e9a06">${request.name}</span><span style="color:#4e9a06">! (from Spring gRPC Kotlin)"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">HelloResponse</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBuilder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">setMessage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">message</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="서버-설정">서버 설정</h3>
<p>기본 gRPC 포트는 <code>9090</code>이다.<br>
원하면 <code>application.yml</code>로 변경 가능하다.</p>
<p><strong>src/main/kotli/resources/application.yml</strong></p>
<pre tabindex="0"><code>spring:
grpc:
server:
port: 9090
</code></pre><h2 id="grpc-클라이언트-구현">gRPC 클라이언트 구현</h2>
<p>같은 프로젝트에서 테스트를 하기 위해, 클라이언트 설정 Bean을 추가한다.</p>
<h3 id="클라이언-설정">클라이언 설정</h3>
<p><strong>src/main/kotlin/com/devkuma/grpc/client/GrpcClientConfig.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloServiceGrpcKt</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.grpc.ManagedChannel</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Bean</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.grpc.client.GrpcChannelFactory</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">GrpcClientConfig</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Bean</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">channelFactory</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">GrpcChannelFactory</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">HelloServiceGrpcKt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceCoroutineStub</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">channel</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">ManagedChannel</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">channelFactory</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">createChannel</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"local"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">HelloServiceGrpcKt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceCoroutineStub</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">channel</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h4 id="grpc-stub을-호출하는-service-생성">gRPC Stub을 호출하는 Service 생성</h4>
<p>단순히 stub을 호출하는 Service를 생성한다.</p>
<p><strong>src/main/kotlin/com/devkuma/grpc/client/HelloGrpcClientService.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloRequest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.HelloServiceGrpcKt</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.stereotype.Service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Service</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">MyGrpcClientService</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">private</span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HelloServiceGrpcKt</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">HelloServiceCoroutineStub</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">suspend</span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">String</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">String</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">request</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HelloRequest</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBuilder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">setName</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">helloStub</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">request</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">message</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h2 id="테스트">테스트</h2>
<p>서버와 클라이언트를 하나의 프로젝트에 넣었기에, 테스트 파일 1개로 테스트가 가능하다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.grpc</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">com.devkuma.grpc.client.MyGrpcClientService</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">kotlinx.coroutines.runBlocking</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.junit.jupiter.api.Test</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.beans.factory.annotation.Autowired</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.boot.test.context.SpringBootTest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@SpringBootTest</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">GrpcIntegrationTest</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Autowired</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">lateinit</span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">MyGrpcClientService</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Test</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">testSayHello</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">runBlocking</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">result</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sayHello</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">assert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contains</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma"</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>Hello, devkuma!
</span></span></code></pre></div><h2 id="참고">참고</h2>
<p>위에 예제 코드는 <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-grpc-2" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>에서 확인해 볼 수 있다.</p>
Spring
Kotlin
gRPC
-
Spring, Kotlin를 활용하여 만든 WebMVC MCP Server에 인증 추가하기
https://www.devkuma.com/docs/spring-ai/mcp-server-auth/
Sun, 09 Nov 2025 10:06:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-ai/mcp-server-auth/
<h2 id="시작하기">시작하기</h2>
<p>앞에서는 <a href="https://www.devkuma.com/docs/spring-ai/mcp-server-webmvc/">WebMVC 전송 방식의 MCP Server</a>를 만들었다면, 여기서는 서버에 인증 기능을 추가헤 보도록 하겠다.</p>
<p>Spring-AI에서는 Spring Security으로 서버 인증으로 OAuth2와 ApiKey 인증 방식을 제공하고 있다.</p>
<blockquote>
<p>2025-11-10 기준으로 아직 MCP Security은 구현중에 있으며, 안정적이지 않다고 한다.</p></blockquote>
<p>여기에서는 WebMVC MCP Server에 ApiKey 인증 방식을 적용해 보겠다.</p>
<h2 id="webmvc-mcp-server에-인증-추가">WebMVC MCP Server에 인증 추가</h2>
<p>이번에도 기존 만들었던 WebMVC 방식의 MCP Server 코드를 그대로 사용하도록 하겠다.</p>
<h3 id="기존-코드-복사">기존 코드 복사</h3>
<p>기존에 작성한 코드가 있다면 디렉토리 통채로 복사하여 사용하도록 하겠다.</p>
<blockquote>
<p>작성한 코드가 없다면, <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-ai-mcp-server-webmvc" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>를 통채로 복사해서 사용해도 된다.</p></blockquote>
<p>코드를 복사하고, 디렉토리도 다음과 같이 변경한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>% mv spring-ai-mcp-server-webmvc spring-ai-mcp-server-auth
</span></span></code></pre></div><h3 id="빌드-스크립트">빌드 스크립트</h3>
<p>복사한 프로젝트의 <code>settings.gradle.kts</code> 파일에세 root 프로젝트 이름도 <strong>spring-ai-mcp-server-auth</strong> 로 변경한다.</p>
<p><strong>settings.gradle.kts</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">rootProject</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"spring-ai-mcp-server-auth"</span>
</span></span></code></pre></div><p>의존성 라이브러리에 Spring Security(<code>spring-boot-starter-security</code>), MCP Server Secutiry(<code>mcp-server-security</code>) 라이브러리를 추가한다.</p>
<p><strong>/build.gradle.kts</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">dependencies</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#ce5c00;font-weight:bold">..</span><span style="color:#000;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.ai:spring-ai-starter-mcp-server"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.ai:spring-ai-starter-mcp-server-webmvc"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// webmvc 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.boot:spring-boot-starter-security"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// auth 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springaicommunity:mcp-server-security:0.0.3"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// auth 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#ce5c00;font-weight:bold">..</span><span style="color:#000;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="어플리케이션-설정-파일">어플리케이션 설정 파일</h3>
<p><code>application.yml</code> 파일을 다음와 같이 변경한다.</p>
<p><strong>/src/main/resources/application.yml</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">spring</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">main</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic"># web-application-type: none</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># NOTE: You must disable the banner and the console logging to allow the STDIO transport to work !!!</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">banner-mode</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">off</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ai</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">mcp</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">server</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">my-weather-server</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">version</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">0.0.1</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">protocol</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">STATELESS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># webmvc 추가</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">logging</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic"># pattern:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic"># console:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">file</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">./log/spring-ai-starter-mcp-server-auth.log</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">level</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">org.springframework.security</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">TRACE</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div><p>로그 파일명(<code>spring-ai-starter-mcp-server-auth.log</code>)이 변경 되었고,<br>
Spring Security 로그 레벨만 추가 되었을 뿐 다른건 수정 된 것이 없다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#c4a000">logging</span><span style="color:#ce5c00;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#c4a000">level</span><span style="color:#ce5c00;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#c4a000">org.springframework.security</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">TRACE</span>
</span></span></code></pre></div><h3 id="mcp-서버-보안-설정-객체-생성">MCP 서버 보안 설정 객체 생성</h3>
<p>Spring Security 설정에 MCP Api Key를 등록한다.</p>
<p><strong>/src/main/kotlin/com/devkuma/ai/mcp/server/SecurityConfig.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.ai.mcp.server</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springaicommunity.mcp.security.server.apikey.ApiKeyEntityRepository</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springaicommunity.mcp.security.server.apikey.memory.ApiKeyEntityImpl</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springaicommunity.mcp.security.server.apikey.memory.InMemoryApiKeyEntityRepository</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springaicommunity.mcp.security.server.config.McpApiKeyConfigurer.mcpServerApiKey</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Bean</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.security.config.annotation.web.builders.HttpSecurity</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.security.config.annotation.web.configuration.EnableWebSecurity</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.security.web.SecurityFilterChain</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@EnableWebSecurity</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">SecurityConfig</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Bean</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">securityFilterChain</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">http</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">HttpSecurity</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">SecurityFilterChain</span> <span style="color:#000;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">http</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">authorizeHttpRequests</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">authorizeHttpRequestsCustomizer</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">authorizeHttpRequestsCustomizer</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">anyRequest</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">authenticated</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}.</span><span style="color:#000">with</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">mcpServerApiKey</span><span style="color:#000;font-weight:bold">(),</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">apiKey</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">apiKey</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">apiKeyRepository</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">apiKeyRepository</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">).</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">private</span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">apiKeyRepository</span><span style="color:#000;font-weight:bold">():</span> <span style="color:#000">ApiKeyEntityRepository</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">ApiKeyEntityImpl</span><span style="color:#000;font-weight:bold">></span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">apiKey</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">ApiKeyEntityImpl</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"test api key"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"api01"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">secret</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"mycustomapikey"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">InMemoryApiKeyEntityRepository</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">apiKey</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><ul>
<li>인증으로 위한 ID는 <code>api01</code>으로 하고 Secret(비밀키)는 <code>mycustomapikey</code>로 설정한 것을 확인할 수 있다.</li>
<li>ID, Secret는 <code>InMemoryApiKeyEntityRepository</code> 통하여, 메모리에 담기도록 하였다.</li>
</ul>
<blockquote>
<p>여기서는 임시로 어플리케이션 기동시에 메모리에 담기도록 저장고를 만들었지만, 실제에서는 다른 DB나, API 등 다른 방식으로 하기를 권한다.</p></blockquote>
<h2 id="테스트를-위한-mcp-client-구현">테스트를 위한 MCP Client 구현</h2>
<p>MCP Server는 완성이 되었고, 이제 클라이언트를 구현해 보겠다.</p>
<p>기존에 작성하였던 <code>HttpClient.kt</code> 클라이언트 파일에 인증을 추가한다.</p>
<p><strong>/src/test/kotlin/com/devkuma/ai/mcp/client/HttpClient.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.ai.mcp.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.McpClient</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.spec.McpSchema.CallToolRequest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">java.net.http.HttpRequest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">request</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HttpRequest</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">newBuilder</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">header</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Content-Type"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"application/json"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">header</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"X-API-key"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"api01.mycustomapikey"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">transport</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HttpClientStreamableHttpTransport</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"http://localhost:8080"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">requestBuilder</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">request</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">client</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">McpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sync</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">transport</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">initialize</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">ping</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// List and demonstrate tools
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">toolsList</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">listTools</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Available Tools = </span><span style="color:#4e9a06">$toolsList</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">alertResult</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">callTool</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">CallToolRequest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"get_weather"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">mapOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"city"</span> <span style="color:#000">to</span> <span style="color:#4e9a06">"seoul"</span><span style="color:#000;font-weight:bold">)))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"get_weather Response = </span><span style="color:#4e9a06">$alertResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">closeGracefully</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><ul>
<li>Request에 Header에 key는 <code>X-API-key</code>으로 설정하고, value은<code>api01.mycustomapikey</code>로 설정하였다.</li>
<li>설정된 Request에 Header는 <code>transport</code>에 <code>requestBuilder</code>로 설정이 되었다.</li>
</ul>
<p>Output:</p>
<pre tabindex="0"><code>... 생략 ...
Available Tools = ListToolsResult[tools=[Tool[name=get_weather, title=null, description=Return the weather of a given city., inputSchema=JsonSchema[type=object, properties={city={type=string, description=The city for which to get the weather}}, required=[city], additionalProperties=false, defs=null, definitions=null], outputSchema=null, annotations=null, meta=null]], nextCursor=null, meta=null]
get_weather Response = CallToolResult[content=[TextContent[annotations=null, text="The weather in seoul is good.", meta=null]], isError=false, structuredContent=null, meta=null]
... 생략 ...
</code></pre><p>실행을 해보면 이전에 했던 결과와 동일하게, 사용 가능한 Tool 목록이 표시되고, <code>get_weather</code> 도구를 호출하여 응답을 받은 값을 표시되는 것을 확인할 수 있다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html" target="_blank" rel="noopener">Spring | Model Context Protocol (MCP)<i class="fas fa-external-link-alt"></i></a></li>
<li><a href="https://docs.spring.io/spring-ai/reference/1.1/api/mcp/mcp-security.html" target="_blank" rel="noopener">Spring | MCP Security<i class="fas fa-external-link-alt"></i></a></li>
<li><a href="https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-webmvc-oauth2-server" target="_blank" rel="noopener">GitHub | spring-ai-examples | starter-webmvc-oauth2-server<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<p>위에 예제 코드는 <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-ai-mcp-server-auth" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>에서 확인해 볼 수 있다.</p>
Kotlin
Spring
Spring Security
AI
-
Spring, Kotlin를 활용하여 간단한 WebMVC MCP Server 만들기
https://www.devkuma.com/docs/spring-ai/mcp-server-webmvc/
Sun, 09 Nov 2025 09:39:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-ai/mcp-server-webmvc/
<h2 id="시작하기">시작하기</h2>
<p>앞에서는 Spring으로 <a href="https://www.devkuma.com/docs/spring-ai/mcp-server/">STDIO 방식의 MCP Server</a>를 만들었다면, 여기서는 WebMVC 전송 방식의 서버를 만들어 보도록 하겠다.</p>
<h3 id="mvp-프로토콜과-실행-모드">MVP 프로토콜과 실행 모드</h3>
<p>Spring-AI의 MCP Server 프레임워크는 다음과 같은 서버 프로토콜을 제공한다:</p>
<ul>
<li>STDIO
<ul>
<li>Standard Input/Output library</li>
<li>프로세스 내부에서 표준 입출력으로 통신</li>
</ul>
</li>
<li>SSE
<ul>
<li>Server-Sent Events(실시간 서버-클라이언트 통신 기술)</li>
<li>실시간 서버-클라이언트 통신</li>
</ul>
</li>
<li>Streamable-HTTP
<ul>
<li>전통적인 HTTP 통신을 확장해서 스트리밍처럼 작동하도록 만든 방식</li>
<li>HTTP POST/GET 요청으로 서버에서 여러 클라이언트 연결 처리</li>
</ul>
</li>
<li>Stateless Streamable-HTTP
<ul>
<li>AI 모델과 같은 애플리케이션을 위한 통신 프로토콜</li>
<li>단일 HTTP 엔드포인트를 통해 요청 전송과 응답 스트리밍을 동시에 수행</li>
<li>요청 간 세션 메모리를 유지하지 않음으로써 무상태성을 구현</li>
<li>요청 간 세션 상태를 유지하지 않아 MSA 환경에 적합</li>
</ul>
</li>
</ul>
<p>이 문서에서는 Stateless Streamable-HTTP 방식으로 구현하려고 한다.</p>
<h2 id="mcp-server을-webmvc-방식으로-변경">MCP Server을 WebMVC 방식으로 변경</h2>
<p>기존 만들었던 STDIO 방식의 MCP Server 코드를 그대로 사용하도록 하겠다.</p>
<h3 id="기존-코드-복사">기존 코드 복사</h3>
<p>기존에 작성한 코드가 있다면 디렉토리 통채로 복사하여 사용하도록 하겠다.</p>
<blockquote>
<p>작성한 코드가 없다면, <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-ai-mcp-server" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>를 통채로 복사해서 사용해도 된다.</p></blockquote>
<p>코드를 복사하고, 디렉토리도 다음과 같이 변경한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>% mv spring-ai-mcp-server spring-ai-mcp-server-webmvc
</span></span></code></pre></div><h3 id="빌드-스크립트">빌드 스크립트</h3>
<p>복사한 프로젝트의 <code>settings.gradle.kts</code> 파일에세 root 프로젝트 이름도 <strong>spring-ai-mcp-server-webmvc</strong> 로 변경한다.</p>
<p><strong>settings.gradle.kts</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">rootProject</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"spring-ai-mcp-server-webmvc"</span>
</span></span></code></pre></div><p>의존성 라이브러리에 Spring WebMVC MCP Sever(<code>spring-ai-starter-mcp-server-webmvc</code>) 라이브러리도 추가한다.</p>
<p><strong>/build.gradle.kts</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">dependencies</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#ce5c00;font-weight:bold">..</span><span style="color:#000;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.ai:spring-ai-starter-mcp-server"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.ai:spring-ai-starter-mcp-server-webmvc"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// webmvc 추가
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#ce5c00;font-weight:bold">..</span><span style="color:#000;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="어플리케이션-설정-파일">어플리케이션 설정 파일</h3>
<p><code>application.yml</code> 파일을 다음와 같이 변경한다.</p>
<p><strong>src/main/resources/application.yml</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">spring</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">main</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic"># web-application-type: none</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># NOTE: You must disable the banner and the console logging to allow the STDIO transport to work !!!</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">banner-mode</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">off</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ai</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">mcp</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">server</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">my-weather-server</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">version</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">0.0.1</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">protocol</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">STATELESS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># webmvc 추가</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">logging</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic"># pattern:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#8f5902;font-style:italic"># console:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">file</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">./log/spring-ai-starter-mcp-server-webmvc.log</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div><p>변경된 설정 내용을 확인해 보면, server 설정으로 <code>protocol: STATELESS</code>가 추가되었다.</p>
<p>그리고, yml 파일을 설정을 자세히 보면 다음 로그 설정도 코멘트 처리하여 제거한 것을 볼 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#c4a000">spring</span><span style="color:#ce5c00;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#c4a000">main</span><span style="color:#ce5c00;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#c4a000">web-application-type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">none</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c4a000">logging</span>
</span></span><span style="display:flex;"><span> <span style="color:#c4a000">pattern</span>
</span></span><span style="display:flex;"><span> <span style="color:#c4a000">console</span><span style="color:#ce5c00;font-weight:bold">:</span>
</span></span></code></pre></div><p>이 설정을 제거하면, 데몬 웹 프로세스가 기동되고, 콘솔에서 로그도 볼 수 있게 된다.</p>
<h2 id="테스트를-위한-mcp-client-구현">테스트를 위한 MCP Client 구현</h2>
<p>MCP Server는 완성이 되었고, 이제 클라이언트를 구현해 보겠다.</p>
<p>신규로 <code>HttpClient.kt</code> 파일명으로 클라이언트 생성한다.</p>
<p><strong>/src/test/kotlin/com/devkuma/ai/mcp/client/HttpClient.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.ai.mcp.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.McpClient</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.spec.McpSchema.CallToolRequest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">transport</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">HttpClientStreamableHttpTransport</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"http://localhost:8080"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">client</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">McpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sync</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">transport</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">initialize</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">ping</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// List and demonstrate tools
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">toolsList</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">listTools</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Available Tools = </span><span style="color:#4e9a06">$toolsList</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">alertResult</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">callTool</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">CallToolRequest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"get_weather"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">mapOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"city"</span> <span style="color:#000">to</span> <span style="color:#4e9a06">"seoul"</span><span style="color:#000;font-weight:bold">)))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"get_weather Response = </span><span style="color:#4e9a06">$alertResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">closeGracefully</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>코드 내용을 보면 <code>HttpClientStreamableHttpTransport</code> 생성시 인자로 http 주소를 넣고 있다.</p>
<p>클라이언트 전에, MCP Sever를 먼저 실행해서 프로세스로 켜놔야 한다.
Output:</p>
<pre tabindex="0"><code>... 생략 ...
Available Tools = ListToolsResult[tools=[Tool[name=get_weather, title=null, description=Return the weather of a given city., inputSchema=JsonSchema[type=object, properties={city={type=string, description=The city for which to get the weather}}, required=[city], additionalProperties=false, defs=null, definitions=null], outputSchema=null, annotations=null, meta=null]], nextCursor=null, meta=null]
get_weather Response = CallToolResult[content=[TextContent[annotations=null, text="The weather in seoul is good.", meta=null]], isError=false, structuredContent=null, meta=null]
... 생략 ...
</code></pre><p>STDIO와 동일하게, 사용 가능한 Tool 목록이 표시되고, <code>get_weather</code> 도구를 호출하여 응답을 받은 값을 표시되는 것을 확인할 수 있다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html" target="_blank" rel="noopener">Spring | Model Context Protocol (MCP)<i class="fas fa-external-link-alt"></i></a></li>
<li><a href="https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-webmvc-server" target="_blank" rel="noopener">GitHub | spring-ai-examples | starter-webmvc-server<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<p>위에 예제 코드는 <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-ai-mcp-server-webmvc" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>에서 확인해 볼 수 있다.</p>
Spring
Kotlin
AI
-
Claude
https://www.devkuma.com/docs/ai/claude/
Fri, 07 Nov 2025 11:51:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/claude/
<h2 id="개요">개요</h2>
<ul>
<li><strong>Claude</strong>는 인공지능(AI) 언어 모델(LLM) 계열로, 미국의 AI 연구 기업 <strong>Anthropic</strong>이 개발하였다.</li>
<li>2023년 3월 처음 공개되었으며 이후 여러 버전(Claude 2, Claude 3, Claude 4 등)으로 진화해 왔다.</li>
<li>개발 목표 중 하나가 **안전성(Safety)**과 <strong>책임 있는 AI 사용</strong>이며, 이를 위해 “헌법적 AI(Constitutional AI)” 접근법을 채택하였다.</li>
<li><strong>Claude</strong>이란 이름은 프랑스 수학자이자 정보 이론의 선구자인 <strong>Claude Shannon</strong>에서 따왔다.</li>
<li>ChatGPT처럼 텍스트 기반으로 대화, 요약, 번역, 코드 작성, 문서 분석 등을 수행하는 <strong>대화형 AI 모델</strong>이다.</li>
</ul>
<h2 id="버전-및-진화">버전 및 진화</h2>
<table>
<thead>
<tr>
<th>버전명</th>
<th>출시 시기</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Claude 1 (초기)</td>
<td>2023년 초</td>
<td>기본 형태 출시.</td>
</tr>
<tr>
<td>Claude 2</td>
<td>2023년 7월</td>
<td>성능·응답 길이 증가.</td>
</tr>
<tr>
<td>Claude 3 계열 (Opus, Sonnet, Haiku)</td>
<td>2024년</td>
<td>성능/속도별 모델 라인업 존재. 용도별 다양화.</td>
</tr>
<tr>
<td>Claude 4 계열 (예: Opus 4, Sonnet 4.5)</td>
<td>2025년</td>
<td>코드 생성 등 고급 작업에 강함.</td>
</tr>
</tbody>
</table>
<h2 id="특징-및-장점">특징 및 장점</h2>
<ul>
<li>자연어 대화, 코드 생성, 데이터 분석, 이미지 입력 처리 등 다양한 작업이 가능하도록 설계되어 있다.</li>
<li>최근 버전에서는 **맥락 창(context window)**이 매우 커졌고, 긴 문맥을 다루거나 복잡한 문제 해결에도 강해졌다. 예컨대 Claude Sonnet 4.5 버전은 입력/출력 토큰 단가와 맥락창 등이 개선되었다.</li>
<li>개발자 및 기업 고객용 API, 에이전트(agent) 활용, 툴(tool) 사용 연계 등 실무 활용도 고려되어 있다.</li>
</ul>
<h2 id="주요-특징">주요 특징</h2>
<ol>
<li>
<p><strong>안전성과 투명성 중시</strong></p>
<ul>
<li>사용자에게 해로운 답변을 피하고, 설명 가능한(reasonable) 답변을 제공하는 데 초점을 둠.</li>
</ul>
</li>
<li>
<p><strong>긴 문맥 처리</strong></p>
<ul>
<li>Claude 3 시리즈는 **최대 200,000 토큰(약 150~200쪽 분량)**까지 이해 가능하여, 긴 문서 분석에 강함.</li>
</ul>
</li>
<li>
<p><strong>직관적인 대화</strong></p>
<ul>
<li>사용자의 의도나 뉘앙스를 자연스럽게 파악하는 언어 능력이 뛰어남.</li>
</ul>
</li>
<li>
<p><strong>기업용 API 제공</strong></p>
<ul>
<li>Slack, Notion, Zapier 등과의 통합 기능을 통해 업무 자동화 및 문서 처리 지원.</li>
</ul>
</li>
</ol>
<h2 id="chatgpt와의-차이점">ChatGPT와의 차이점</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>ChatGPT (OpenAI)</th>
<th>Claude (Anthropic)</th>
</tr>
</thead>
<tbody>
<tr>
<td>개발사</td>
<td>OpenAI</td>
<td>Anthropic</td>
</tr>
<tr>
<td>대표 모델</td>
<td>GPT-4, GPT-5</td>
<td>Claude 3 시리즈</td>
</tr>
<tr>
<td>철학</td>
<td>효율성과 정확성 중심</td>
<td>안전성과 인간 중심적 설계</td>
</tr>
<tr>
<td>강점</td>
<td>코드 작성, 다양한 통합 기능</td>
<td>문서 이해력, 윤리적 제어</td>
</tr>
<tr>
<td>문맥 길이</td>
<td>약 128k 토큰(GPT-4 Turbo)</td>
<td>약 200k 토큰(Claude 3)</td>
</tr>
</tbody>
</table>
<h2 id="활용-사례">활용 사례</h2>
<ul>
<li>기업에서는 코딩 자동화, 문서 요약, 데이터 분석, 챗봇 응대 등으로 사용하고 있다. 예컨대 코드 리팩토링, 버그 수정 등에서도 언급된다.</li>
<li>일반 사용자도 웹/모바일 채팅 인터페이스로 자연스러운 대화를 나눌 수 있다.</li>
<li>한국 시장 진출 준비 중이며, 국내 기업 및 정책기구와의 협력도 진행 중이다.</li>
</ul>
<h2 id="한계-및-유의사항">한계 및 유의사항</h2>
<ul>
<li>아직 ‘완전한 인공지능’(AGI)이나 자의식 있는 시스템은 아니며, 오류 혹은 허위 정보(“환각(Hallucination)”) 가능성이 존재한다. 예컨대 모델이 잘못된 법적 인용을 생성한 사례가 보도된 바 있다.</li>
<li>기업·단체 이용 시 라이선스, 데이터 보안, 규제 준수 등이 중요하다.</li>
<li>안전성·윤리성 고려가 기업·개발자에게 요구되고 있으며, 특정 국가 기업에 대한 접근 제한 등도 논의되고 있다.</li>
</ul>
<h2 id="한국-사용자에게-유의할-점">한국 사용자에게 유의할 점</h2>
<ul>
<li>한국어 입력/출력도 가능하지만, 영어 등 원어권에 비해 응답 품질이 조금 떨어질 수 있으므로 중요한 업무에는 검토가 필요하다.</li>
<li>한국 시장 진출 예정이긴 하지만, 지금 당장은 국내 특화 버전이나 로컬화가 완전하지 않을 수 있다.</li>
<li>사용 목적에 따라 무료 또는 유료 플랜으로 나뉠 수 있으며, API 등은 비용이 발생할 수 있다.</li>
</ul>
AI
-
Spring AI
https://www.devkuma.com/docs/spring-ai/
Fri, 07 Nov 2025 10:49:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-ai/
<p><img src="https://www.devkuma.com/docs/spring/spring-boot.jpg" alt="Spring Boot"></p>
<p>Spring AI</p>
Spring Boot
Spring
Java
-
Spring, Kotlin를 활용하여 간단한 Standard MCP Server(STDIO) 만들기
https://www.devkuma.com/docs/spring-ai/mcp-server/
Fri, 07 Nov 2025 10:30:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/spring-ai/mcp-server/
<h2 id="시작하기">시작하기</h2>
<p>MCP Server는 AI 모델이 외부 리소스(도구, 데이터, API 등)에 접근할 수 있도록 연결해주는 표준화된 인터페이스를 제공하는 역할을 한다.</p>
<blockquote>
<p>MCP 서버에 대한 개념은 아래의 문서를 참고 바란다.<br>
<a href="https://www.devkuma.com/docs/ai/mcp-server/">AI 용어 | MCP Server</a></p></blockquote>
<p>이 문서에서는 Spring를 활용하여 간단한 MCP Server를 만들고, Claude에서 MCP Server에 연결하는 방법에 대해서 설명한다.<br>
<img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-architect.png" alt="MCP architect"></p>
<p>물론, Claude 외에 다른 AI 도구를 활용할 수도 있고, MCP Server도 Spring이 아니어도 된다. MCP는 단어 그대로 Model Content Protocol(모델 컨텐츠 규약)으로 이를 따르고 있어서 가능한 것이다.</p>
<p>MCP Server에서 연결되는 Resoures는 DB, API 등 다양하게 활용될 수 있다. 여기 문서에서는 Resoures는 연결까지는 다루지 않았다.</p>
<p>Spring-AI의 MCP Server 프레임워크는 서버 전송에 따라 아래 3가지 스타타터를 지원한다.</p>
<ul>
<li>Standard MCP Server
<ul>
<li>STDIO서버 전송을 지원</li>
<li><code>org.springframework.ai:spring-ai-starter-mcp-server</code></li>
</ul>
</li>
<li>WebMVC Server 전송
<ul>
<li>Spring MVC 기반 SSE(Server-Sent Events) 서버 전송과 옵션으로 STDIO 전송을 지원</li>
<li><code>org.springframework.ai:spring-ai-starter-mcp-server-webmvc</code></li>
</ul>
</li>
<li>WebFlux Server 표준
<ul>
<li>Spring WebFlux 기반 SSE(Server-Sent Events) 서버 전송과 옵션으로 STDIO 전송을 지원</li>
<li><code>org.springframework.ai:spring-ai-starter-mcp-server-webflux</code></li>
</ul>
</li>
</ul>
<h2 id="spring을-활용한-mcp-server-구현">Spring을 활용한 MCP Server 구현</h2>
<p>먼저 Spring와 Kotlin를 활용한 간단한 Standard MCP Server(STDIO)를 생성해 보겠다.</p>
<h3 id="프로젝트-생성">프로젝트 생성</h3>
<p>프로젝트 생성은 <a href="https://start.spring.io/#!type=gradle-project-kotlin&language=kotlin&platformVersion=3.5.7&packaging=jar&configurationFileFormat=yaml&jvmVersion=21&groupId=com.devkuma&artifactId=spring-ai-mcp-server&name=spring-ai-mcp-server&description=Demo%20project%20for%20Spring%20AI%20MCP%20Server&packageName=com.devkuma.ai.mcp.server&dependencies=spring-ai-mcp-server" target="_blank" rel="noopener">Spirng ininitializr<i class="fas fa-external-link-alt"></i></a>으로 생성할 수 있다.</p>
<p><img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-initializr.png" alt="Spirng ininitializr"></p>
<p>화면 아래 부근에 “GENERATE” 버튼을 누르면, 설정한 프로젝트 파일을 다운로드를 받을 수 있다.</p>
<p>또는, 다음과 같이 <code>curl</code> 명령어를 사용하여 Spring Boot 초기 프로젝트를 생성할 수도 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>curl https://start.spring.io/starter.tgz <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">bootVersion</span><span style="color:#ce5c00;font-weight:bold">=</span>3.5.7 <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">dependencies</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-ai-mcp-server <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">baseDir</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-ai-mcp-server <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">groupId</span><span style="color:#ce5c00;font-weight:bold">=</span>com.devkuma <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">artifactId</span><span style="color:#ce5c00;font-weight:bold">=</span>spring-ai-mcp-server <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">packageName</span><span style="color:#ce5c00;font-weight:bold">=</span>com.devkuma.ai.mcp.server <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">applicationName</span><span style="color:#ce5c00;font-weight:bold">=</span>McpServerApplication <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">packaging</span><span style="color:#ce5c00;font-weight:bold">=</span>jar <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">language</span><span style="color:#ce5c00;font-weight:bold">=</span>kotlin <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">javaVersion</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">21</span> <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">description</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">"Demo project for Spring AI MCP Server"</span> <span style="color:#4e9a06">\
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span>-d <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">=</span>gradle-project-kotlin <span style="color:#000;font-weight:bold">|</span> tar -xzvf -
</span></span></code></pre></div><h3 id="생성된-프로젝트-확인">생성된 프로젝트 확인</h3>
<h4 id="빌드-스크립트">빌드 스크립트</h4>
<p>의존성 라이브러리에 Spring MVC Server(<code>spring-ai-starter-mcp-server</code>) 라이브러리가 포함되어 있는 것을 볼 수 있다.</p>
<p><strong>/build.gradle.kts</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">dependencies</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#ce5c00;font-weight:bold">..</span><span style="color:#000;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">implementation</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"org.springframework.ai:spring-ai-starter-mcp-server"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#ce5c00;font-weight:bold">..</span><span style="color:#000;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Spring AI 라이브러리 버전은 따로 변경하지 않으면 디폴트로 예전 버전이 설정되는데, 여기서는 최신 버전으로 수정한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">extra</span><span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">"springAiVersion"</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"1.1.0-M4"</span>
</span></span></code></pre></div><blockquote>
<p>최신 버전은 <a href="https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-starter-mcp-server" target="_blank" rel="noopener">여기<i class="fas fa-external-link-alt"></i></a>에서 확인하면 된다.</p></blockquote>
<h3 id="service-객체-생성">Service 객체 생성</h3>
<p>MCP Client에서 호출을 하게 되는 서비스 객체를 생성한다.</p>
<p><strong>/src/main/kotlin/com/devkuma/ai/mcp/server/WeatherService.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.ai.mcp.server</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.ai.tool.annotation.Tool</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.ai.tool.annotation.ToolParam</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.stereotype.Service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Service</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">WeatherService</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Tool</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">name</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"get_weather"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">description</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"Return the weather of a given city."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">getWeather</span><span style="color:#000;font-weight:bold">(</span><span style="color:#5c35cc;font-weight:bold">@ToolParam</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">description</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"The city for which to get the weather"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">city</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">String</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">String</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 실제 날씨 정보를 가져오는 로직을 추가한다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#8f5902;font-style:italic">// 여기서는 간단히 "Good"을 반환한다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#4e9a06">"The weather in </span><span style="color:#4e9a06">$city</span><span style="color:#4e9a06"> is good."</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><ul>
<li>getWeather(..) 함수에서는 페리미터로 도시(city)를 전달 받고, 날씨의 상태를 고정으로 “good"이라고 응답하도록 만들었다.
<ul>
<li>여기에서는 응답값을 고정으로 넣었지만, 실제로는 다른 리소스(DB, API 등)에서 정보를 가져와서 응답하게 하면 된다.</li>
</ul>
</li>
<li><code>@Tool</code>에 넣는 Tool에 대한 설명과 <code>@ToolParam</code>에 넣은 페라미터 설명은 나중에 MCP 클라이언트가 서버에 접속을 했을 때, 어떤 도구가 있고 사용 방법은 어떻게 되는지 클라이언트에서 알려줄 수 있다.</li>
</ul>
<h3 id="mcp-서버-설정-객체-생성">MCP 서버 설정 객체 생성</h3>
<p>생성된 Service를 Spring 설정으로 AI Tool로 추가한다.</p>
<p><strong>/src/main/kotlin/com/devkuma/ai/mcp/server/McpConfig.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.ai.mcp.server</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.ai.tool.ToolCallbackProvider</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.ai.tool.method.MethodToolCallbackProvider</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Bean</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">org.springframework.context.annotation.Configuration</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#5c35cc;font-weight:bold">@Configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">McpConfig</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#5c35cc;font-weight:bold">@Bean</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">weatherTools</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">weatherService</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">WeatherService</span><span style="color:#000;font-weight:bold">):</span> <span style="color:#000">ToolCallbackProvider</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">MethodToolCallbackProvider</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">toolObjects</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">weatherService</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h3 id="어플리케이션-설정-파일">어플리케이션 설정 파일</h3>
<p><code>application.yml</code>를 다음과 같이 작성한다. (기존에 <code>application.properties</code>은 있다면 yml 파일로 변경한다.)</p>
<p><strong>/src/main/resources/application.yml</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">spring</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">main</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">web-application-type</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">none</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># NOTE: You must disable the banner and the console logging to allow the STDIO transport to work !!!</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">banner-mode</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">off</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ai</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">mcp</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">server</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">my-weather-server</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">version</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">0.0.1</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">logging</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">pattern</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">console</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">file</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">./log/spring-ai-starter-mcp-server.log</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div><ul>
<li>여기서 주의점은 <code>banner-mode</code>를 <code>off</code>로 설정해야 한다.</li>
</ul>
<h3 id="빌드-및-실행-파일-생성">빌드 및 실행 파일 생성</h3>
<p>코드 작성이 완료되었다면 빌드하고 jar 파일을 생성을 해야 하는데, IDEA로 gradle 빌드로 <code>boorJar</code>를 실행한다.</p>
<p>또는, 다음과 같이 터미널에서 명령어로 다음과 같이 실행한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>% ./gradlew clean bootJar
</span></span></code></pre></div><p>그러면, jar 파일이 아래 경로에 생성된다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>build/libs/spring-ai-mcp-server-0.0.1-SNAPSHOT.jar
</span></span></code></pre></div><h2 id="테스트를-위한-mcp-client-구현">테스트를 위한 MCP Client 구현</h2>
<p>MCP Server는 완성이 되었고, 이제 클라이언트를 구현해 보겠다.</p>
<p>신규로 <code>StdioClient.kt</code> 파일을 생성한다.</p>
<p><strong>/test/kotlin/com/devkuma/ai/mcp/client/StdioClient.kt</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.ai.mcp.client</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.McpClient</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.transport.ServerParameters</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.client.transport.StdioClientTransport</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.json.McpJsonMapper</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.modelcontextprotocol.spec.McpSchema.CallToolRequest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">stdioParams</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">ServerParameters</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">builder</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"java"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">args</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-Dspring.ai.mcp.server.stdio=true"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-Dspring.main.web-application-type=none"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-Dlogging.pattern.console="</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-jar"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"build/libs/spring-ai-mcp-server-0.0.1-SNAPSHOT.jar"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">transport</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">StdioClientTransport</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">stdioParams</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">McpJsonMapper</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">createDefault</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">client</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">McpClient</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sync</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">transport</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">build</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">initialize</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// List and demonstrate tools
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">toolsList</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">listTools</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Available Tools = </span><span style="color:#4e9a06">$toolsList</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">alertResult</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">callTool</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">CallToolRequest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"get_weather"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">mapOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"city"</span> <span style="color:#000">to</span> <span style="color:#4e9a06">"seoul"</span><span style="color:#000;font-weight:bold">)))</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"get_weather Response = </span><span style="color:#4e9a06">$alertResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">client</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">closeGracefully</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><ul>
<li>코드 내용을 보면 <code>java</code> 명령어로 jar를 실행하는 <code>stdioParams</code>를 설정하여, <code>StdioClientTransport</code> 생성시에 인자로 넣도 있다.</li>
<li>클라이언트를 하면 STDIO로 MCP Sever가 실행이 된다.</li>
</ul>
<p>Output:</p>
<pre tabindex="0"><code>... 생략 ...
Available Tools = ListToolsResult[tools=[Tool[name=get_weather, title=null, description=Return the weather of a given city., inputSchema=JsonSchema[type=object, properties={city={type=string, description=The city for which to get the weather}}, required=[city], additionalProperties=false, defs=null, definitions=null], outputSchema=null, annotations=null, meta=null]], nextCursor=null, meta=null]
get_weather Response = CallToolResult[content=[TextContent[annotations=null, text="The weather in seoul is good.", meta=null]], isError=false, structuredContent=null, meta=null]
... 생략 ...
</code></pre><ul>
<li>실행 결과 출력에는 사용 가능한 Tool 목록이 표시되고, <code>get_weather</code> 도구를 호출하여 응답을 받은 값을 표시되는 것을 확인할 수 있다.</li>
</ul>
<h2 id="claude에서-mcp-server에-연결">Claude에서 MCP Server에 연결</h2>
<p>빌드까지 완료가 되었다면 클로드 데스크탑에 MCP Server에 연결해 보고, 적용이 잘되어 있는지 확인해보도록 하겠다.</p>
<h3 id="연결-설정">연결 설정</h3>
<p>MCP Server 연결 설정은 json 파일에 등록해야 한다.</p>
<p>MacOS인 경우에는 <code>~/Library/Application Support/Claude/claude_desktop_config.json</code>에 설정을 하면된다. (파일이 없다면 새로 생성을 하고, 이미 있다면 추가한다.)</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"mcpServers"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"spring-ai-mcp-weather"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"command"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"java"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"args"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-Dspring.ai.mcp.server.stdio=true"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-Dspring.main.web-application-type=none"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-Dlogging.pattern.console="</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"-jar"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"{{앞에서 만든 프로젝트의 경로}}/spring-ai-mcp-server/build/libs/spring-ai-mcp-server-0.0.1-SNAPSHOT.jar"</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>생성한 어플리케이션은 java 명령어로 실행이 되고, 실행된 서버는 stdio를 통해서 클로드 데스크탑과 연결이 된다.</p>
<h3 id="연결-설정-확인">연결 설정 확인</h3>
<p>Claude를 실행하고 메뉴에서 “Claude > 설정 > 개발자"을 확인해 보면, 로컬 MCP 서버가 추가되어 있는 것을 확인 할 수 있다.<br>
(혹시 여기서 에러가 발생한다면 로그 버튼이 보이는데, 그 버튼을 클릭해서 내용을 확인하면 된다.)
<img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-claude-1.png" alt="Spring AI MCP Server Claude"></p>
<p>채팅 창에서는 설정 버튼을 누르면 “spring-ai-mcp-wearther"가 활성화 되어 있는 것을 볼 수 있다.
<img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-claude-2.png" alt="Spring AI MCP Server Claude"></p>
<h3 id="실행-확인">실행 확인</h3>
<p>이제 연결이 잘 된 것을 확인했으니, 입력을 해보도로 하겠다.</p>
<p>아래 창에서는 “서울의 날씨는 어때?“라고 입력을 하였다.
<img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-claude-3.png" alt="Spring AI MCP Server Claude"></p>
<p>그러면, 다음과 같이 “Claude가 spring-ai-mcp-weather에서 Get weather을(를) 사용하려고 합니다.“라는 메세지와 함께, 허용 여부를 묻는다.
<img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-claude-4.png" alt="Spring AI MCP Server Claude"></p>
<p>허용 버튼을 누르면, 응답으로 “서울 날씨는 좋습니다.“라는 응답하는 것을 볼 수 있다.<br>
“Get weather"를 누르면 요청과 응답에 대해서도 확인할 수 있다.
<img src="https://www.devkuma.com/docs/spring-ai/images/spring-ai-mcp-server-claude-5.png" alt="Spring AI MCP Server Claude"></p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://bcho.tistory.com/1471" target="_blank" rel="noopener">조대협의 블로그:티스토리 | MCP (Model Context Protocol) 2. 서버 개발하기<i class="fas fa-external-link-alt"></i></a></li>
<li><a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html" target="_blank" rel="noopener">Spring | Model Context Protocol (MCP)<i class="fas fa-external-link-alt"></i></a></li>
<li><a href="https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/weather/starter-stdio-server" target="_blank" rel="noopener">GitHub | spring-ai-examples | starter-stdio-server<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<p>위에 예제 코드는 <a href="https://github.com/devkuma/spring-tutorial/tree/main/spring-ai-mcp-server" target="_blank" rel="noopener">GitHub<i class="fas fa-external-link-alt"></i></a>에서 확인해 볼 수 있다.</p>
Kotlin
Spring
AI
-
VectorDB (벡터 데이터베이스)
https://www.devkuma.com/docs/vector-db/
Thu, 06 Nov 2025 18:12:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/vector-db/
<p><strong>VectorDB (벡터 데이터베이스)</strong> 는 최근 <strong>AI·머신러닝·LLM(대형 언어 모델)</strong> 분야에서 아주 중요한 역할을 하는 데이터베이스이다.<br>
간단히 말하면, <strong>숫자 벡터 형태의 데이터(=임베딩)를 효율적으로 저장하고 유사한 데이터를 빠르게 찾는 데 특화된 데이터베이스</strong>이다.</p>
<hr>
<h2 id="vectordb란">VectorDB란?</h2>
<p>VectorDB는 <strong>벡터(Vector) 데이터를 저장·검색·비교</strong>하는 데 최적화된 DB이다.<br>
보통 텍스트, 이미지, 오디오, 코드 등 <strong>비정형 데이터</strong>를 <strong>임베딩(embedding)</strong> 모델을 통해 고정된 길이의 벡터(숫자 배열)로 변환한 뒤,
이 벡터들을 VectorDB에 저장한다.</p>
<p>예를 들어:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>"오늘 날씨 어때?" → [0.12, -0.08, 0.56, ... , 0.33]
</span></span><span style="display:flex;"><span>"지금 비 와?" → [0.11, -0.07, 0.57, ... , 0.31]
</span></span></code></pre></div><p>이 두 벡터는 의미상 비슷하므로 <strong>벡터 간 거리(distance)</strong> 가 매우 가깝다.<br>
VectorDB는 이런 “유사도 기반 검색"을 빠르게 수행할 수 있도록 만들어진 시스템이다.</p>
<hr>
<h2 id="vectordb의-주요-기능">VectorDB의 주요 기능</h2>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>임베딩 저장</strong></td>
<td>텍스트나 이미지 등에서 생성한 벡터를 저장</td>
</tr>
<tr>
<td><strong>유사도 검색 (Similarity Search)</strong></td>
<td>쿼리 벡터와 가장 비슷한 벡터(=데이터)를 빠르게 찾아줌</td>
</tr>
<tr>
<td><strong>메타데이터 필터링</strong></td>
<td>벡터와 함께 태그, 카테고리 등 부가 정보를 저장하고, 검색 시 필터링 가능</td>
</tr>
<tr>
<td><strong>클러스터링/분류</strong></td>
<td>의미상 비슷한 벡터를 그룹화하거나 범주별로 분류</td>
</tr>
<tr>
<td><strong>LLM 연동</strong></td>
<td>LLM의 “context”나 “memory” 기능 구현에 자주 사용 (예: RAG 구조)</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="vectordb가-중요한-이유">VectorDB가 중요한 이유</h2>
<p>대형 언어 모델(예: GPT, Claude, Gemini 등)은 **한 번에 처리할 수 있는 입력량(Context length)**이 제한되어 있다.<br>
그래서 모든 정보를 LLM에 넣을 수 없다.</p>
<p>이때 VectorDB가 다음처럼 사용된다</p>
<blockquote>
<p>사용자의 질문 → 임베딩으로 변환 → VectorDB에서 관련 문서 검색 →<br>
결과를 LLM에 함께 넣어서 답변 생성 (이 구조를 <strong>RAG</strong>, Retrieval-Augmented Generation 이라고 함)</p></blockquote>
<p>즉, <strong>VectorDB는 LLM의 외부 기억장치처럼 작동</strong>한다.</p>
<hr>
<h2 id="대표적인-vectordb-제품들">대표적인 VectorDB 제품들</h2>
<table>
<thead>
<tr>
<th>제품명</th>
<th>특징</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Pinecone</strong></td>
<td>완전관리형(Managed), API 중심, RAG에 특화</td>
</tr>
<tr>
<td><strong>Weaviate</strong></td>
<td>오픈소스 + GraphQL API 지원, Hybrid Search 가능</td>
</tr>
<tr>
<td><strong>Milvus</strong></td>
<td>대규모 데이터 처리에 강함, 오픈소스</td>
</tr>
<tr>
<td><strong>Qdrant</strong></td>
<td>Rust 기반, 고성능 + 쉬운 REST/gRPC API</td>
</tr>
<tr>
<td><strong>FAISS</strong></td>
<td>Meta가 만든 라이브러리 (DB라기보다는 검색 엔진에 가까움)</td>
</tr>
<tr>
<td><strong>Chroma</strong></td>
<td>Python 친화적, LangChain 등과 쉽게 연동</td>
</tr>
<tr>
<td><strong>Redis Vector Search (Redis 7.2+)</strong></td>
<td>Redis 안에서 벡터 인덱싱 기능 제공</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="vectordb-검색의-핵심-개념">VectorDB 검색의 핵심 개념</h2>
<h3 id="벡터-유사도-계산-방법">벡터 유사도 계산 방법</h3>
<ul>
<li><strong>Cosine similarity (코사인 유사도)</strong> — 각 벡터의 방향이 얼마나 비슷한지</li>
<li><strong>Euclidean distance (유클리드 거리)</strong> — 두 벡터 간의 직선 거리</li>
<li><strong>Dot product (내적)</strong> — 주로 학습된 임베딩에서 사용</li>
</ul>
<h3 id="인덱싱-indexing">인덱싱 (Indexing)</h3>
<p>벡터는 보통 수천~수백만 개가 저장되므로,
검색을 빠르게 하기 위해 <strong>근사 최근접 탐색(ANN, Approximate Nearest Neighbor)</strong> 알고리즘을 사용한다.</p>
<p>대표적인 인덱스 알고리즘:</p>
<ul>
<li>HNSW (Hierarchical Navigable Small World)</li>
<li>IVF (Inverted File Index)</li>
<li>PQ (Product Quantization)</li>
</ul>
<hr>
<h2 id="vectordb--llm-예시-rag-구조">VectorDB + LLM 예시 (RAG 구조)</h2>
<pre class="mermaid">flowchart LR
A[사용자 질문] --> B[임베딩 생성기]
B --> C[VectorDB에서 유사 문서 검색]
C --> D[관련 문서 반환]
D --> E[LLM에 문서와 함께 질문 전달]
E --> F[정확하고 맥락 있는 답변 생성]</pre>
<p>이 구조를 통해 ChatGPT와 비슷한 “지식 기반 챗봇”을 직접 만들 수 있다.</p>
<hr>
<h2 id="실제-사용-예시-python--qdrant">실제 사용 예시 (Python + Qdrant)</h2>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">from</span> <span style="color:#000">qdrant_client</span> <span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">QdrantClient</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">from</span> <span style="color:#000">qdrant_client.models</span> <span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">PointStruct</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">client</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">QdrantClient</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">":memory:"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># 예시 벡터 저장</span>
</span></span><span style="display:flex;"><span><span style="color:#000">client</span><span style="color:#ce5c00;font-weight:bold">.</span><span style="color:#000">upsert</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">collection_name</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">"docs"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">points</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">[</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">PointStruct</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">id</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">vector</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0.1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">0.3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">0.5</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000">payload</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">{</span><span style="color:#4e9a06">"text"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"안녕하세요"</span><span style="color:#000;font-weight:bold">}),</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">PointStruct</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">id</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">vector</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0.11</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">0.29</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">0.51</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000">payload</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">{</span><span style="color:#4e9a06">"text"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"반가워요"</span><span style="color:#000;font-weight:bold">})</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># 쿼리 벡터와 유사한 데이터 검색</span>
</span></span><span style="display:flex;"><span><span style="color:#000">hits</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">client</span><span style="color:#ce5c00;font-weight:bold">.</span><span style="color:#000">search</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">collection_name</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">"docs"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">query_vector</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0.12</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">0.28</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">0.49</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000">limit</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hits</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><hr>
<h2 id="-요약">🧩 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>내용</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>목적</strong></td>
<td>벡터 기반 유사도 검색 및 AI 응용</td>
</tr>
<tr>
<td><strong>데이터 형태</strong></td>
<td>고차원 벡터 (embedding)</td>
</tr>
<tr>
<td><strong>주요 활용처</strong></td>
<td>RAG, 추천 시스템, 이미지 검색, 음성 검색</td>
</tr>
<tr>
<td><strong>핵심 기술</strong></td>
<td>ANN 인덱싱, 코사인 유사도, 메타데이터 필터</td>
</tr>
<tr>
<td><strong>대표 제품</strong></td>
<td>Pinecone, Qdrant, Milvus, Weaviate, Chroma</td>
</tr>
</tbody>
</table>
Database
VectorDB
-
개발과 설계 원칙
https://www.devkuma.com/docs/programming/develop-design/
Sun, 12 Oct 2025 23:15:05 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/programming/develop-design/
-
Modal과 Dialog의 차이점은?
https://www.devkuma.com/docs/modal-and-dailog/
Sun, 12 Oct 2025 23:15:05 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/modal-and-dailog/
<h2 id="modal">Modal</h2>
<p>모달은 사용자가 반드시 상호작용해야만 다른 작업으로 돌아갈 수 있는 **“차단형 UI 창”**을 말한다.</p>
<ul>
<li><strong>특징</strong>
<ul>
<li>배경의 콘텐츠는 비활성화되고 흐리게 처리됨.</li>
<li>사용자가 모달 내의 동작(확인, 취소 등)을 마치기 전까지 다른 화면 조작 불가.</li>
<li>강제적인 성격이 있음 → <strong>사용자의 흐름을 끊고 특정 작업을 완료하도록 요구</strong>.</li>
</ul>
</li>
<li><strong>예시</strong>
<ul>
<li>“정말 삭제하시겠습니까?“와 같은 확인 창</li>
<li>로그인/회원가입 창</li>
</ul>
</li>
</ul>
<h2 id="dialog">Dialog</h2>
<p>다이얼로그는 사용자에게 정보를 전달하거나 선택지를 제공하는 <strong>대화 상자</strong>를 통칭하는 개념이다.</p>
<ul>
<li><strong>특징</strong>
<ul>
<li><strong>모달 다이얼로그</strong>: 사용자의 입력을 강제하는 형태 (즉, 모달의 한 종류).</li>
<li><strong>모달리스(non-modal) 다이얼로그</strong>: 열려 있어도 사용자가 다른 화면을 계속 조작할 수 있음.</li>
<li>목적: 단순 안내, 선택 옵션 제공, 사용자와의 상호작용을 위한 가벼운 UI.</li>
</ul>
</li>
<li><strong>예시</strong>
<ul>
<li>저장 여부 묻는 알림창</li>
<li>“업데이트가 있습니다” 알림</li>
<li>툴에서 띄우는 옵션 설정 창</li>
</ul>
</li>
</ul>
<h2 id="modal과-dialog의-관계">Modal과 Dialog의 관계</h2>
<ul>
<li><strong>Modal ⊂ Dialog</strong></li>
<li>즉, 모달은 다이얼로그의 한 형태이다.</li>
<li>모든 모달은 다이얼로그이지만, 모든 다이얼로그가 모달은 아니다.</li>
</ul>
<hr>
<h2 id="정리">정리</h2>
<ul>
<li><code>Dialog</code>: 사용자와 정보를 주고받는 “대화 상자” → 일반적인 상위 개념</li>
<li><code>Modal</code>: 그 중에서도 사용자의 다른 동작을 막고 특정 입력을 강제하는 “차단형 대화 상자”</li>
</ul>
-
AI Q&A
https://www.devkuma.com/docs/ai/qna/
Sat, 30 Aug 2025 18:38:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/qna/
<p>AI에 대해 궁금한 점은 뭐든 정리한다.</p>
AI
-
바이브 코딩(Vibe Coding)
https://www.devkuma.com/docs/ai/vibe-coding/
Sat, 30 Aug 2025 17:55:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/vibe-coding/
<h2 id="바이브-코딩vibe-coding-정의">바이브 코딩(Vibe Coding) 정의</h2>
<ul>
<li><strong>바이브 코딩</strong>은 공식적인 학술 용어라기보다는, 최근 개발자 커뮤니티에서 쓰이는 표현으로, <strong>프로그래밍을 감각적으로, 흐름(Vibe, 바이브)에 맞춰 즐기면서 하는 코딩 방식</strong>을 뜻한다.</li>
<li>특히 AI 시대에 맞춰, 개발자가 코드 한 줄 한 줄을 직접 짜기보다 <strong>자연어로 목표/피드백을 주고 LLM이 코드를 생성·수정하도록 이끄는 개발 방식</strong>을 강조한다.
<ul>
<li>사람은 ‘무엇을 만들지’에 집중하고, 구현 세부는 AI가 맡는다.</li>
</ul>
</li>
<li>용어는 2025년 2월 <strong>Andrej Karpathy</strong>가 “코드는 잊고, 완전히 바이브에 맡긴다"는 취지로 언급하면서 대중화되었다.</li>
</ul>
<h2 id="개념적-특징">개념적 특징</h2>
<ol>
<li><strong>몰입 & 즉흥성</strong>
<ul>
<li>정해진 설계 없이, 떠오르는 아이디어나 감각에 따라 바로 코딩</li>
<li>음악의 <strong>즉흥 연주(jam session)</strong> 같은 느낌</li>
</ul>
</li>
<li><strong>흐름(Vibe, 바이브) 중심</strong>
<ul>
<li>“재밌어 보이는 것”, “지금 하고 싶은 것” 위주로 자유롭게 구현</li>
<li>코드의 완벽함보다 <strong>즐거움과 흐름</strong>이 중요</li>
</ul>
</li>
<li><strong>빠른 프로토타이핑</strong>
<ul>
<li>작동하는 최소 기능을 빠르게 만들고 실행</li>
<li>완벽한 아키텍처보다 <strong>일단 돌아가는 것</strong>을 목표</li>
</ul>
</li>
</ol>
<h2 id="바이브-코딩의-활용-맥락">바이브 코딩의 활용 맥락</h2>
<ul>
<li><strong>코딩 학습 초기</strong> → 자유롭게 실험하며 동기 유지</li>
<li><strong>해커톤/프로토타입 제작</strong> → 아이디어를 빠르게 구현해야 할 때</li>
<li><strong>창의적인 작업</strong> → 음악, 아트, 게임 등에서 실험적 구현</li>
<li><strong>AI 활용 코딩</strong> → ChatGPT 같은 AI와 대화하며 흐름대로 구현</li>
</ul>
<h2 id="워크플로-일반적인-진행-단계">워크플로 (일반적인 진행 단계)</h2>
<ol>
<li><strong>목표 서술</strong> → “슬랙 봇으로 휴가 승인/거절 처리"처럼 결과 중심 설명</li>
<li><strong>초안 생성</strong> → LLM이 프로젝트 뼈대 작성</li>
<li><strong>실행·피드백</strong> → 에러/누락된 기능을 자연어로 지적, 반복 수정</li>
<li><strong>기능 확장/리팩터링</strong> → 테스트 기준을 제시해 수렴</li>
</ol>
<p>→ 핵심은 <strong>대화형 지시·피드백</strong>을 기반으로 개발이 진행된다는 점.</p>
<h2 id="장단점">장단점</h2>
<h3 id="장점">장점</h3>
<ul>
<li>창의성 발휘와 몰입 용이</li>
<li>빠른 결과 확인 가능 → 프로토타이핑 최적</li>
<li>학습/실험 동기 부여 (재미있어 오래 지속 가능)</li>
<li>비개발자도 진입 장벽 낮음 (AI와 함께 개발 가능)</li>
<li>개발자는 세밀 구현보다 <strong>설계·품질 관리 역할</strong>로 이동</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>구조적 완성도/안정성 부족</li>
<li>버그·보안 취약점 발생 위험</li>
<li>내가 직접 안 짠 코드 → 유지보수/디버깅 어려움</li>
<li>다파일·레거시 연동 같은 복잡한 시스템엔 부적합</li>
</ul>
<h2 id="ai-보조-코딩과의-차이">AI 보조 코딩과의 차이</h2>
<ul>
<li><strong>AI 보조 코딩 (예: Copilot)</strong> → 내가 코드를 작성하면 AI가 보완·추천</li>
<li><strong>바이브 코딩</strong> → <strong>AI가 대부분의 코드를 작성</strong>, 인간은 목표·제약·테스트만 제시</li>
</ul>
<h2 id="실무에서의-가드레일-안전-장치">실무에서의 가드레일 (안전 장치)</h2>
<ol>
<li><strong>테스트 우선</strong> → 단위·통합·E2E 테스트 먼저 정의</li>
<li><strong>정적 분석/보안 점검</strong> → Linter, SAST, 의존성 취약점 자동 검사</li>
<li><strong>리뷰 & 기록</strong> → 요구사항·수용 기준·리스크를 문서화하고 최종 승인</li>
<li><strong>샌드박스 환경</strong> → 안전한 실행 공간, 민감 정보 차단</li>
<li><strong>프로덕션 이전 리팩터링</strong> → 별도 스프린트 편성</li>
<li><strong>도메인 지식 고정</strong> → API 스펙, 에러 케이스, 성능 기준을 지속 주입</li>
</ol>
<h2 id="적합부적합-사례">적합/부적합 사례</h2>
<ul>
<li><strong>적합</strong> → 해커톤, PoC, 개인 툴, UI/프론트엔드 프로토타입</li>
<li><strong>주의 필요</strong> → 금융, 의료, 임베디드 등 고신뢰/규제 환경</li>
</ul>
<h2 id="시작용-프롬프트-템플릿">시작용 프롬프트 템플릿</h2>
<ul>
<li>
<p><strong>목표</strong>: 무엇을, 누가, 왜 쓰는지</p>
</li>
<li>
<p><strong>기능</strong>: 필수·선택 요구사항 목록</p>
</li>
<li>
<p><strong>제약/기준</strong>: 보안, 성능, 접근성, 라이선스</p>
</li>
<li>
<p><strong>테크스택</strong>: 언어·프레임워크·DB·배포 방식</p>
</li>
<li>
<p><strong>수용 기준</strong>: 통과해야 할 테스트 시나리오</p>
</li>
<li>
<p><strong>작업 방식</strong>: 작은 단위 PR, 단계별 테스트, 커밋 규칙 준수</p>
</li>
<li>
<p>예시</p>
<ul>
<li>“Next.js + SQLite로 ‘개인 할 일 공유 보드’를 만들어줘. 필수 기능 5개와 E2E 테스트 3개를 먼저 제안하고, 각 기능을 TDD 사이클로 구현하자. 보안은 OAuth, XSS 방지, rate limit 포함.”</li>
</ul>
</li>
</ul>
<hr>
<h2 id="정리">정리</h2>
<ul>
<li><strong>바이브 코딩은 정석적 설계보다는 영감과 흐름에 맞춰, 특히 AI를 활용해 즉흥적으로 실험하고 빠르게 결과를 내는 개발 스타일</strong>이다.</li>
<li>프로토타입, 해커톤, 개인 프로젝트에 강력하지만, 프로덕션 수준에는 보안/테스트/리팩터링이 반드시 필요하다</li>
</ul>
AI
-
AI 모델에 GPU를 사용하는 이유
https://www.devkuma.com/docs/ai/gpu/
Sat, 30 Aug 2025 17:35:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/gpu/
<p>AI(특히 딥러닝) 모델은 방대한 데이터를 학습하고 추론하기 위해 <strong>막대한 양의 연산</strong>을 수행해야 한다. 이때 CPU만으로는 속도가 너무 느려 비효율적이기 때문에, <strong>대규모 병렬 연산에 특화된 GPU</strong>가 필수적으로 사용된다.</p>
<h2 id="gpu란-무엇인가">GPU란 무엇인가?</h2>
<ul>
<li><strong>GPU(Graphic Processing Unit)</strong> 는 원래 <strong>그래픽 연산</strong>(픽셀 렌더링, 3D 그래픽 처리)을 빠르게 수행하기 위해 만들어진 장치이다.</li>
<li>게임이나 영상에서 그래픽이 끊기지 않고 부드럽게 표현되는 것도 GPU의 빠른 병렬 연산 덕분이다.</li>
</ul>
<h2 id="cpu-vs-gpu-구조-비교">CPU vs GPU 구조 비교</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>CPU</th>
<th>GPU</th>
</tr>
</thead>
<tbody>
<tr>
<td>코어 개수</td>
<td>수 개(4 ~ 32개) 고성능 코어</td>
<td>수천 ~ 수만 개의 소형 코어</td>
</tr>
<tr>
<td>처리 방식</td>
<td>직렬 처리(Sequential)</td>
<td>병렬 처리(Parallel)</td>
</tr>
<tr>
<td>강점</td>
<td>일반적인 로직 수행, 복잡한 분기 처리</td>
<td>대량의 단순 반복 연산, 행렬/벡터 연산</td>
</tr>
<tr>
<td>AI 적합성</td>
<td>낮음</td>
<td>매우 높음</td>
</tr>
</tbody>
</table>
<p>딥러닝 학습에 필요한 <strong>행렬 곱셈, 벡터 연산</strong>이 GPU의 병렬 처리 방식과 딱 맞아떨어진다.</p>
<h2 id="gpu가-ai에-꼭-필요한-이유">GPU가 AI에 꼭 필요한 이유</h2>
<h3 id="병렬-연산-능력">병렬 연산 능력</h3>
<ul>
<li>딥러닝 모델은 수많은 파라미터와 뉴런 간 연결을 동시에 계산해야 한다.</li>
<li>GPU는 이를 한꺼번에 병렬 처리해 속도를 비약적으로 향상시킨다.</li>
</ul>
<h3 id="행렬벡터-연산-최적화">행렬/벡터 연산 최적화</h3>
<ul>
<li>신경망(Neural Network)은 수많은 <strong>행렬(Matrix)</strong>, <strong>벡터(Vector)</strong> 곱셈으로 구성된다.</li>
<li>예: <code>y = Wx + b</code> (가중치 행렬 × 입력 벡터 + 편향)</li>
<li>GPU는 본래 **그래픽 처리(픽셀 계산, 3D 렌더링)**를 위해 행렬 연산 최적화되어 있어, AI 연산과 잘 맞는다.</li>
</ul>
<h3 id="학습-속도-단축">학습 속도 단축</h3>
<ul>
<li>모델 학습 시 <strong>수백만 ~ 수십억 개 파라미터</strong> 업데이트 필요하다.</li>
<li>CPU만 사용하면 수 주 ~ 수 개월 걸릴 학습이 GPU를 사용하면 <strong>수 시간~~수 일</strong>로 단축된다.</li>
</ul>
<h3 id="대규모-데이터-처리">대규모 데이터 처리</h3>
<ul>
<li>AI는 이미지, 음성, 텍스트 등 <strong>고차원 데이터</strong>를 다룬다.</li>
<li>GPU는 대규모 데이터(batch) 연산을 동시에 처리할 수 있어 학습·추론 속도가 빠르다.</li>
</ul>
<h3 id="추론inference-성능-강화">추론(Inference) 성능 강화</h3>
<ul>
<li>학습뿐 아니라 <strong>실시간 서비스</strong>(챗봇 응답, 이미지/음성 인식, 자율주행 센서 데이터 분석 등)에서도 빠른 응답을 제공한다.</li>
</ul>
<h3 id="생태계-지원">생태계 지원</h3>
<ul>
<li><strong>PyTorch, TensorFlow</strong> 같은 대표적인 딥러닝 프레임워크가 <strong>CUDA(NVIDIA 라이브러리)</strong> 기반으로 GPU에 최적화되어 있다.</li>
<li>GPU 사용 시 자동으로 최적화된 커널을 활용할 수 있어 추가 성능 확보 가능하다.</li>
</ul>
<h2 id="gpu와-ai의-관계">GPU와 AI의 관계</h2>
<p>초기 AI 연구자들은 대량의 데이터를 학습시킬 때 CPU만으로는 한계가 있다는 것을 깨달았다. 이때 <strong>그래픽 연산용 GPU를 딥러닝 학습에 적용</strong>했는데, 병렬 연산 구조가 AI와 완벽히 맞아떨어졌다.
이후로 <strong>AI와 GPU는 떼려야 뗄 수 없는 관계</strong>가 되었으며, 현재 진행되는 대부분의 AI 연구와 서비스는 GPU 기반에서 이루어진다.</p>
<h2 id="gpu-외-ai-전용-하드웨어">GPU 외 AI 전용 하드웨어</h2>
<p>최근에는 GPU 외에도 <strong>AI 특화 칩</strong>들이 개발되어 활용되고 있다.</p>
<ul>
<li><strong>TPU (Tensor Processing Unit)</strong>: Google 개발, 텐서 연산 최적화</li>
<li><strong>NPU (Neural Processing Unit)</strong>: 모바일 기기용, 에너지 효율 최적화</li>
<li><strong>FPGA, ASIC</strong>: 특정 AI 연산에 특화된 맞춤형 칩</li>
</ul>
<p>하지만 여전히 <strong>범용성·성능·생태계 측면에서 GPU가 가장 널리 사용</strong>되고 있다.</p>
<h2 id="정리">정리</h2>
<ul>
<li><strong>CPU = 직렬 처리 강점 (범용 프로세서)</strong></li>
<li><strong>GPU = 병렬 연산 특화 (AI·딥러닝 최적화)</strong></li>
<li><strong>AI 모델에 GPU를 사용하는 이유 = 대규모 행렬/벡터 연산을 동시에 빠르게 처리하기 위해서</strong></li>
</ul>
AI
-
Google Gemini
https://www.devkuma.com/docs/ai/gemini/
Sat, 30 Aug 2025 17:05:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/gemini/
<h2 id="google-gemini란">Google Gemini란?</h2>
<ul>
<li><strong>Gemini</strong>는 Google에서 <strong>DeepMind</strong>와 <strong>Brain</strong> 팀의 역량을 통합해 개발한 다중 모드(multimodal) 대형 언어 모델(LLM)이다.</li>
<li>텍스트뿐만 아니라 오디오, 이미지, 동영상 등 다양한 형태의 정보를 이해하고 처리할 수 있는 것이 특징이다.</li>
<li>처음 공개된 것은 2023년 12월로, <strong>Gemini 1.0</strong>은 <strong>Ultra</strong>, <strong>Pro</strong>, <strong>Nano</strong> 등 세 가지 버전으로 출시되었다. 각각 복잡한 작업, 범용 작업, 장치 내 처리(On-device)를 목표로 했다.</li>
<li>이후 빠르게 발전을 거듭했으며, <strong>Gemini 2.5 Flash</strong>와 <strong>2.5 Pro</strong>가 현재 주요 버전으로 사용 중이다. Flash는 응답 속도 중심, Pro는 고도 추론과 코드 생성 기능을 제공하며, Audio 출력 및 보안 기능도 강화되었다.</li>
<li><a href="https://gemini.google.com/" target="_blank" rel="noopener">https://gemini.google.com/<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/gemini.jpg" alt="Google Gemini"></p>
<h2 id="gemini의-주요-특징">Gemini의 주요 특징</h2>
<h3 id="다중-모드multimodality"><strong>다중 모드(Multimodality)</strong></h3>
<ul>
<li>기존의 AI 모델들이 주로 텍스트에 한정된 것과 달리, Gemini는 텍스트, 이미지, 오디오, 동영상의 다양한 형태의 데이터를 동시에 이해하고 통합적으로 처리할 수 있다.</li>
<li>예를 들어, 동영상을 보면서 내용에 대해 질문하거나, 이미지와 텍스트를 함께 제공하며 특정 작업을 요청할 수 있다.</li>
</ul>
<h4 id="이미지-편집-nano-banana--gemini-25-flash-image">이미지 편집 (Nano-Banana / Gemini 2.5 Flash Image)</h4>
<ul>
<li>“<strong>Nano‑Banana</strong>“라 불리는 <strong>Gemini 2.5 Flash Image</strong> 모델은 사용자가 자연어로 이미지를 수정하거나 합성할 수 있도록 해서, 얼굴·사물 등의 특징을 일관되게 유지하면서 수정해주는 고급 기능을 제공한다.</li>
<li>예를 들어, 여러 이미지를 합치거나 배경 변경, 스타일·의상 수정 등의 작업이 가능하며, AI 생성 이미지에는 눈에 보이거나 보이지 않는 워터마크가 포함되어 있어 생성 여부를 확인할 수 있다.</li>
</ul>
<h4 id="음성-및-보이스-인터랙션">음성 및 보이스 인터랙션</h4>
<ul>
<li><strong>Gemini Live</strong> 기능은 음성을 이용한 실시간 대화형 인터페이스로, 특히 Pixel 9에서 화면 및 카메라 공유와 함께 사용할 수 있다.</li>
</ul>
<h3 id="다양한-모델">다양한 모델</h3>
<ul>
<li>Gemini는 사용 목적에 따라 여러 가지 모델로 나뉜다.
<ul>
<li><strong>Gemini Ultra:</strong> 가장 강력하고 복잡한 작업에 최적화된 모델이다.</li>
<li><strong>Gemini Pro:</strong> 광범위한 작업에 사용할 수 있는 균형 잡힌 성능의 모델이다.</li>
<li><strong>Gemini Flash:</strong> 비용 효율성과 빠른 응답 속도가 중요한 작업에 적합한 모델이다.</li>
</ul>
</li>
</ul>
<h3 id="뛰어난-성능">뛰어난 성능</h3>
<ul>
<li>Gemini는 복잡한 추론, 코딩, 수학 문제 해결 등 다양한 벤치마크에서 뛰어난 성능을 보여줍니다. 특히, 대규모 다중작업 언어 이해(MMLU) 벤치마크에서 인간 전문가의 점수를 능가하는 결과를 보여주기도 했다.</li>
</ul>
<h3 id="일상-비서-역할-강화">일상 비서 역할 강화</h3>
<ul>
<li><strong>Gemini for Home</strong>은 구글 어시스턴트를 대체하는 새로운 AI 기반 생활 도우미로, 일상 루틴 관리, 더 자연스러운 대화 및 스마트홈 기기 제어 기능을 포함합니다. 2025년 10월부터 초기 액세스 제공 예정이다.</li>
<li>또한 <strong>Android Auto</strong>에도 Gemini가 통합되어, 운전 중 음성 명령으로 메시지 전송, 이메일 확인 등 다양한 기능을 수행할 수 있다.</li>
</ul>
<h3 id="google-workspace-통합-및-다국어-대응">Google Workspace 통합 및 다국어 대응</h3>
<ul>
<li>Gemini는 <strong>Gmail</strong>, <strong>Calendar</strong>, <strong>Maps</strong>, <strong>Photos</strong>, <strong>YouTube</strong> 등을 연결해 여러 앱 사이를 오가며 작업을 도와준다. 일정 관리, 알람 설정, 통화, 발표 연습 같은 기능도 제공돼된다.</li>
<li>현재 40개 이상의 언어를 지원하며, 모바일 앱(Android, iOS)과 웹을 통해 이용할 수 있다. 또한 <strong>Gemini 2.5 Flash</strong>와 <strong>2.5 Pro</strong>는 과금 기반의 유료 모델로도 제공된다.</li>
</ul>
<h3 id="활용-분야"><strong>활용 분야</strong></h3>
<ul>
<li>Gemini는 다음과 같은 다양한 분야에서 활용될 수 있다.
<ul>
<li><strong>창의적인 작업:</strong> 글쓰기, 이미지 생성, 아이디어 브레인스토밍 등</li>
<li><strong>학습 및 연구:</strong> 복잡한 주제 요약, 논문 분석, 학습 계획 수립 등</li>
<li><strong>코딩:</strong> 코드 생성, 디버깅, 최적화 등</li>
<li><strong>고객 서비스:</strong> 질문에 대한 정확하고 유익한 답변 제공 등</li>
</ul>
</li>
</ul>
<p>Gemini는 Google AI Studio, Vertex AI 등 다양한 Google 클라우드 서비스와 연동되어 사용될 수 있으며, Google의 AI 어시스턴트인 Gemini에도 탑재되어 있다.</p>
<h2 id="경쟁력--비교-포인트">경쟁력 & 비교 포인트</h2>
<ul>
<li>구글은 Gemini가 OpenAI의 GPT‑4와 유사하거나 더 높은 벤치마크 성능을 낸다고 발표했지만, 실제 사용 경험은 용도에 따라 다를 수 있다.</li>
<li>Gemini는 특히 <strong>멀티모달 설계</strong>, <strong>긴 컨텍스트 창</strong>, <strong>강화된 이미지 및 음성 처리 능력</strong>에서 차별점을 보이고 있다.</li>
</ul>
<h2 id="요약">요약</h2>
<table>
<thead>
<tr>
<th>영역</th>
<th>특징 요약</th>
</tr>
</thead>
<tbody>
<tr>
<td>멀티모달 처리</td>
<td>텍스트, 이미지, 오디오, 영상, 코드 모두 이해/생성 가능</td>
</tr>
<tr>
<td>모델 구성</td>
<td>Gemini 1.0 (Ultra/Pro/Nano) → 2.5 Flash / Pro 등 최신 버전</td>
</tr>
<tr>
<td>이미지 편집</td>
<td>Nano‑Banana: 자연어 기반 편집, 특징 일관성 유지</td>
</tr>
<tr>
<td>음성 인터페이스</td>
<td>Gemini Live: 음성 기반 실시간 대화</td>
</tr>
<tr>
<td>일상 비서 기능</td>
<td>Gemini for Home, Android Auto 음성 지원</td>
</tr>
<tr>
<td>Workspace 연동</td>
<td>Gmail, Calendar 등과 통합, 다양한 앱 연결 가능</td>
</tr>
<tr>
<td>경쟁력</td>
<td>GPT‑4 대비 멀티모달 설계, 긴 컨텍스트, 높은 벤치마크</td>
</tr>
<tr>
<td>과금 모델</td>
<td>무료 + 프리미엄 플랜 (예: Gemini 2.5 Pro 등)</td>
</tr>
</tbody>
</table>
<h2 id="향후-흐름">향후 흐름</h2>
<p>Gemini는 앞으로 <strong>스마트홈</strong>, <strong>차량</strong>, <strong>생산성 도구</strong>, <strong>멀티미디어 생성</strong> 등 다양한 분야에 깊숙이 통합될 예정이며, 계속해서 새로운 기능 및 버전이 출시되고 있다.</p>
AI
Gemini
-
Ollama
https://www.devkuma.com/docs/ai/ollama/
Sat, 30 Aug 2025 17:05:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/ollama/
<h2 id="ollama란">Ollama란?</h2>
<p>Ollama는 최근 AI/LLM 개발자들 사이에서 많이 쓰이는 도구이다.</p>
<p><strong>Ollama</strong>는 <strong>로컬 환경에서 대형 언어 모델(LLM)을 간편하게 실행하고 관리할 수 있게 해주는 오픈소스 플랫폼</strong>이다.<br>
즉, OpenAI API 같은 클라우드 모델을 쓰지 않고도 <strong>내 PC(Mac/Linux/Windows)</strong> 에서 Llama·Mistral·Gemma·CodeLlama 같은 모델을 불러와 사용할 수 있게 해준다.</p>
<h2 id="ollama의-주요-특징">Ollama의 주요 특징</h2>
<ol>
<li><strong>로컬 실행 지원</strong>
<ul>
<li>인터넷 연결 없이도 모델을 실행 가능</li>
<li>기업 보안이나 개인 프라이버시에 유리</li>
</ul>
</li>
<li><strong>간단한 모델 배포</strong>
<ul>
<li><code>ollama run llama3</code> 같은 명령어 한 줄로 모델 실행</li>
<li>Docker처럼 <strong>모델 패키지 관리</strong>를 지원 (<code>Modelfile</code>이라는 설정 파일로 관리)</li>
</ul>
</li>
<li><strong>여러 모델 지원</strong>
<ul>
<li>Meta LLaMA, Mistral, Gemma, Code Llama, Phi 등 다양한 모델 다운로드 및 실행 가능</li>
</ul>
</li>
<li><strong>API 제공</strong>
<ul>
<li>REST API 형식으로 로컬 서버(<code>http://localhost:11434/api/generate</code>)를 열어서 다른 앱에서 호출 가능</li>
<li>즉, 로컬 OpenAI API 서버처럼 활용 가능</li>
</ul>
</li>
<li><strong>GPU 최적화</strong>
<ul>
<li>MPS(Mac), CUDA(NVIDIA GPU) 지원 → 속도 빠름</li>
<li>CPU에서도 실행 가능하지만 속도는 떨어짐</li>
</ul>
</li>
</ol>
<h2 id="ollama-사용">Ollama 사용</h2>
<h3 id="설치">설치</h3>
<ul>
<li>macOS/Linux/Windows도 패키지 제공
<ul>
<li><a href="https://ollama.com/download" target="_blank" rel="noopener">https://ollama.com/download<i class="fas fa-external-link-alt"></i></a></li>
</ul>
</li>
<li>macOS
<ul>
<li><code>brew install ollama</code></li>
</ul>
</li>
</ul>
<p>macOS 설치 완료 후 실행하면 메뉴 바에 ollama 아이콘 표시된다.<br>
<img src="https://www.devkuma.com/docs/ai/ollama-macos.png" alt="Ollama"></p>
<h3 id="모델-실행">모델 실행</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ollama run llama3
</span></span></code></pre></div><p>처음 실행하면 모델을 자동 다운로드 후 실행 (llama3 모델 용량 4.7GB)</p>
<h4 id="모델-다운로드">모델 다운로드</h4>
<p><a href="https://www.ollama.com/search" target="_blank" rel="noopener">공식 사이트<i class="fas fa-external-link-alt"></i></a>에서 사용할 model 검색후 마음에 드는 모델 설치</p>
<ul>
<li>추천 모델
<ul>
<li><a href="https://www.ollama.com/library/gpt-oss" target="_blank" rel="noopener">https://www.ollama.com/library/gpt-oss<i class="fas fa-external-link-alt"></i></a>
<ul>
<li>chatgpt 수준의 채팅이나 분석, 업무등을 원한다면 사용. 맥북에서 조금 느리지만 동작은 잘 함</li>
</ul>
</li>
<li><a href="https://www.ollama.com/library/phi4-mini" target="_blank" rel="noopener">https://www.ollama.com/library/phi4-mini<i class="fas fa-external-link-alt"></i></a>
<ul>
<li>단순 mcp server 호출 대상 탐색등의 간단한 작업에 어울림, tools 지원하는 경량 모델이면 어느것이어도 상관없음. qwen, deepseek는 사용 금지.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="api-호출-예-curl">API 호출 (예: curl)</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl http://localhost:11434/api/generate -d <span style="color:#4e9a06">'{
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> "model": "llama3",
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> "prompt": "Explain quantum computing in simple terms"
</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">}'</span>
</span></span></code></pre></div><h3 id="모델-관리">모델 관리</h3>
<ul>
<li><code>ollama list</code> → 설치된 모델 확인</li>
<li><code>ollama pull mistral</code> → 새로운 모델 다운로드</li>
<li><code>ollama create mymodel -f Modelfile</code> → 커스텀 모델 생성</li>
</ul>
<hr>
<h2 id="ollama와-다른-llm-실행-프레임워크-비교">Ollama와 다른 LLM 실행 프레임워크 비교</h2>
<table>
<thead>
<tr>
<th>도구</th>
<th>특징</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Ollama</strong></td>
<td>가장 간단한 설치/실행, 로컬 API 제공, 모델 패키지 관리</td>
</tr>
<tr>
<td><strong>LM Studio</strong></td>
<td>GUI 기반, 모델 선택과 실행 직관적</td>
</tr>
<tr>
<td><strong>vLLM</strong></td>
<td>고성능 서버 실행 최적화, 주로 대규모 배포에 사용</td>
</tr>
<tr>
<td><strong>Text Generation WebUI</strong></td>
<td>다양한 모델 실행 + Web UI 제공</td>
</tr>
<tr>
<td><strong>OpenAI API</strong></td>
<td>클라우드 기반, 최신 모델 사용 가능, 하지만 비용/프라이버시 이슈</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="활용-사례">활용 사례</h2>
<ul>
<li>개발 환경에서 <strong>로컬 AI 어시스턴트</strong> 구축</li>
<li>사내 보안 데이터와 연결한 <strong>내부 챗봇</strong></li>
<li><strong>LangChain, LlamaIndex</strong> 같은 프레임워크와 연동해 RAG 시스템 구축</li>
<li>프로토타이핑: OpenAI API 비용 안 쓰고 빠르게 실험</li>
</ul>
<h2 id="정리">정리</h2>
<p><strong>Ollama는 로컬에서 쉽게 LLM을 실행할 수 있게 해주는 “Docker for LLMs” 같은 플랫폼</strong>이다. 개인 연구부터 기업용 챗봇까지 다양하게 쓸 수 있다.</p>
AI
-
MCP Server
https://www.devkuma.com/docs/ai/mcp-server/
Sat, 30 Aug 2025 14:55:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/mcp-server/
<h2 id="mcp-서버란">MCP 서버란?</h2>
<p>MCP 서버는 <strong>AI 모델이 외부 리소스(도구, 데이터, API 등)에 접근할 수 있도록 연결해주는 표준화된 인터페이스를 제공하는 역할</strong>을 한다.<br>
쉽게 말하면, <strong>AI가 쓸 수 있는 도구 모음집을 제공하는 서버</strong>라고 할 수 있다.</p>
<h2 id="구조">구조</h2>
<p>MCP는 크게 <strong>클라이언트(Client)</strong> 와 <strong>서버(Server)</strong> 로 나뉘는데:</p>
<ul>
<li><strong>MCP 클라이언트</strong>
<ul>
<li>LLM 환경에 붙어 있는 애플리케이션 (예: IDE, Chat UI, Notebook 등)</li>
<li>사용자의 프롬프트를 받아 모델을 실행시키고, 필요하면 MCP 서버에 요청</li>
</ul>
</li>
<li><strong>MCP 서버</strong>
<ul>
<li>여러 <strong>리소스/도구/함수</strong>를 관리하고 제공</li>
<li>표준화된 프로토콜(JSON-RPC 기반)을 통해 클라이언트와 통신</li>
<li>예: DB 조회 서버, 파일 시스템 서버, API 호출 서버</li>
</ul>
</li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/mcp-client-server.png" alt="MCP Client Server"></p>
<h2 id="서버가-제공하는-것">서버가 제공하는 것</h2>
<p>MCP 서버는 크게 네 가지 기능을 제공한다:</p>
<ol>
<li><strong>리소스(Resources)</strong>
<ul>
<li>데이터베이스, 파일, 문서, API 응답 등</li>
<li>예: <code>resource://db/customers</code>, <code>resource://filesystem/project/README.md</code></li>
</ul>
</li>
<li><strong>도구(Tools)</strong>
<ul>
<li>호출 가능한 함수/액션</li>
<li>예: <code>searchCustomer(name)</code>, <code>sendEmail(to, subject, body)</code></li>
</ul>
</li>
<li><strong>프롬프트(Prompts)</strong>
<ul>
<li>미리 정의된 템플릿을 제공</li>
<li>예: “SQL 쿼리 생성용 프롬프트”</li>
</ul>
</li>
<li><strong>이벤트(Events)</strong>
<ul>
<li>서버에서 발생하는 알림이나 변경 이벤트를 실시간으로 전달</li>
</ul>
</li>
</ol>
<h2 id="동작-흐름-예시">동작 흐름 예시</h2>
<ol>
<li>사용자가 IDE 안에서: “DB에서 고객 ‘홍길동’ 검색해줘” 입력</li>
<li>LLM이 판단 → MCP 클라이언트를 통해 “searchCustomer” 도구 호출 요청</li>
<li><strong>MCP 클라이언트 → MCP 서버</strong> 요청 전달</li>
<li>MCP 서버가 실제 DB 조회 수행 후 결과 반환</li>
<li>결과를 LLM이 사용자 친화적 문장으로 정리해서 보여줌</li>
</ol>
<h2 id="mcp-서버-예시">MCP 서버 예시</h2>
<p>예를 들어, 파일 시스템 MCP 서버라면:</p>
<ul>
<li><strong>Resource</strong>: 프로젝트 폴더 내 파일들 (<code>/src/main.kt</code>, <code>/README.md</code>)</li>
<li><strong>Tools</strong>: 파일 읽기, 쓰기, 검색 기능 (<code>readFile</code>, <code>writeFile</code>)</li>
<li><strong>Prompts</strong>: “이 코드를 리팩토링 해줘” 같은 템플릿</li>
</ul>
<p>→ 이렇게 구성된 서버를 붙이면, AI는 프로젝트 파일을 직접 탐색/수정 가능해짐.</p>
<h2 id="비유">비유</h2>
<ul>
<li><strong>MCP 서버</strong> = “호텔 컨시어지”
<ul>
<li>손님(LLM)이 “관광지 추천해줘” 하면, 컨시어지가 여러 API/DB를 뒤에서 다뤄서 정리된 정보 제공</li>
</ul>
</li>
<li><strong>MCP 클라이언트</strong> = “호텔 프런트”
<ul>
<li>손님과 직접 대화하며, 요청을 받아서 컨시어지에게 전달</li>
</ul>
</li>
</ul>
<h2 id="정리">정리</h2>
<p><strong>MCP 서버는 LLM이 외부 도구와 데이터를 안전하고 일관된 방식으로 활용할 수 있도록 표준화된 인터페이스를 제공하는 백엔드 역할</strong>을 한다.</p>
AI
-
GitHub Copilot
https://www.devkuma.com/docs/ai/copilot/
Sat, 30 Aug 2025 14:50:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/copilot/
<h2 id="copilot이란">Copilot이란?</h2>
<ul>
<li><strong>GitHub Copilot</strong>은 GitHub와 OpenAI가 공동 개발한 <strong>AI 기반 코드 자동 완성 도구</strong>이다.</li>
<li>개발자가 코드를 작성할 때, <strong>주석이나 함수 시그니처, 맥락(Context)</strong> 을 분석하여 가장 적절한 코드를 자동으로 제안한다.</li>
<li>흔히 <strong>AI 페어 프로그래머(AI Pair Programmer)</strong> 라고 불린다.</li>
</ul>
<h2 id="주요-특징">주요 특징</h2>
<ol>
<li><strong>자동 코드 제안</strong>
<ul>
<li>한 줄, 함수 전체, 심지어 테스트 코드까지 자동 생성</li>
<li>반복되는 패턴을 빠르게 채워줌</li>
</ul>
</li>
<li><strong>주석 기반 개발</strong>
<ul>
<li><code>// 두 숫자를 더하는 함수 작성</code> 같은 자연어 주석을 입력하면 코드를 생성</li>
</ul>
</li>
<li><strong>맥락 이해</strong>
<ul>
<li>현재 파일, 프로젝트 내 다른 파일들을 참고해 더 자연스러운 제안 가능</li>
</ul>
</li>
<li><strong>다양한 언어/프레임워크 지원</strong>
<ul>
<li>Python, JavaScript, TypeScript, Go, Java, C#, C++ 등 광범위한 언어 지원</li>
</ul>
</li>
<li><strong>IDE 통합</strong>
<ul>
<li>VS Code, JetBrains IDE, Neovim 등 주요 개발 환경에 플러그인으로 제공</li>
</ul>
</li>
</ol>
<h2 id="활용-사례">활용 사례</h2>
<ul>
<li><strong>반복 작업 단축</strong>: boilerplate 코드, CRUD API, 테스트 코드 작성</li>
<li><strong>신규 언어 학습</strong>: 생소한 언어/프레임워크 문법을 빠르게 습득</li>
<li><strong>알고리즘 구현</strong>: 주석으로 요구사항 작성 후 코드 자동 생성</li>
<li><strong>리팩터링 보조</strong>: 더 나은 구현 방식 제안</li>
</ul>
<h2 id="장점">장점</h2>
<ul>
<li>개발 속도 향상: 일상적인 반복 코딩을 크게 줄여줌</li>
<li>학습 효과: 모르는 API나 문법을 예시로 보여줌</li>
<li>코드 일관성 유지: 팀 규칙에 맞게 보일러플레이트 자동화</li>
<li>테스트 작성 보조: TDD 사이클 가속화</li>
</ul>
<h2 id="한계단점">한계/단점</h2>
<ul>
<li><strong>정확성 불완전</strong>: 항상 올바른/최적의 코드가 나오지 않음</li>
<li><strong>보안 위험</strong>: 취약점 포함 가능 (예: SQL 인젝션 방어 누락)</li>
<li><strong>라이선스 문제</strong>: 공개 코드에서 학습했기에 일부 코드가 저작권 이슈 될 수 있음</li>
<li><strong>맥락 제한</strong>: 프로젝트 전체를 깊게 이해하기엔 한계 있음</li>
</ul>
<h2 id="비용">비용</h2>
<ul>
<li><strong>유료 구독제</strong> (2025년 현재 기준)
<ul>
<li>개인: 월 약 <strong>$10 (USD)</strong></li>
<li>기업: 월 약 <strong>$19 (USD) / 사용자</strong></li>
</ul>
</li>
<li>학생/오픈소스 기여자는 무료 제공</li>
</ul>
<h2 id="copilot과-바이브-코딩의-차이">Copilot과 바이브 코딩의 차이</h2>
<ul>
<li><strong>Copilot</strong>: 개발자가 주도, AI는 제안 (보조 도구)</li>
<li><strong>바이브 코딩</strong>: AI가 주도, 개발자는 목표와 피드백만 (창작 스타일)</li>
</ul>
<h2 id="비슷한-ai-코딩-도구">비슷한 AI 코딩 도구</h2>
<ul>
<li><strong>Amazon CodeWhisperer</strong> (AWS)</li>
<li><strong>Tabnine</strong></li>
<li><strong>Cursor IDE (ChatGPT 내장 IDE)</strong></li>
<li><strong>Replit Ghostwriter</strong></li>
</ul>
<h2 id="정리">정리</h2>
<ul>
<li><strong>GitHub Copilot은 “AI 페어 프로그래머"로, 주석·코드 맥락을 기반으로 실시간 코드 제안을 해주는 도구</strong>이다.</li>
<li>반복적이고 생산성 낮은 작업을 줄여주고, 초안 코드를 빨리 만들 수 있지만, 반드시 <strong>검증·리뷰·테스트</strong>가 필요하다.</li>
</ul>
AI
-
Function Calling (Tools)
https://www.devkuma.com/docs/ai/function-calling/
Sat, 30 Aug 2025 14:50:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/function-calling/
<h2 id="function-calling-도구-호출-이란">Function Calling (도구 호출) 이란?</h2>
<p>LLM이 단순히 텍스트만 생성하는 게 아니라, <strong>외부 함수(Function)나 API를 직접 호출</strong>할 수 있도록 하는 기능이다.<br>
예를 들어, OpenAI API에서 제공하는 <code>function_call</code> 기능이 대표적이다.</p>
<h3 id="동작-원리">동작 원리</h3>
<ol>
<li><strong>사용자 입력</strong>: “오늘 서울 날씨 알려줘”</li>
<li><strong>LLM 판단</strong>: “이건 날씨 API를 호출해야 하는구나”</li>
<li><strong>도구 호출</strong>: 미리 정의된 함수(예: <code>getWeather(location: string)</code>) 실행</li>
<li><strong>결과 수신</strong>: <code>{ "location": "Seoul", "temp": 28, "condition": "Sunny" }</code></li>
<li><strong>최종 응답 생성</strong>: “오늘 서울은 맑고 28도입니다 ☀️”</li>
</ol>
<p>즉, LLM은 <strong>자연어 → 함수 입력 변환</strong> 역할을 하고, 실제 계산·검색은 외부 함수/도구가 맡는다.</p>
<h3 id="장점">장점</h3>
<ul>
<li><strong>실시간 데이터 활용</strong>
<ul>
<li>LLM 자체는 훈련 시점 이후 지식을 모름. Function Call로 최신 API 연결 가능.</li>
</ul>
</li>
<li><strong>정확한 계산</strong>
<ul>
<li>LLM은 수학 연산에 약함. 대신 계산기 API를 호출하면 정확한 결과 반환.</li>
</ul>
</li>
<li><strong>업무 자동화</strong>
<ul>
<li>“메일 보내줘” → 이메일 API 호출</li>
<li>“DB에서 고객 목록 가져와” → DB 쿼리 실행</li>
</ul>
</li>
</ul>
<h2 id="mcp와-function-calling의-차이">MCP와 Function Calling의 차이</h2>
<ul>
<li>
<p><strong>Function Calling</strong></p>
<ul>
<li>개별 도구를 LLM이 직접 호출하는 방식</li>
<li>API나 함수 단위의 호출 정의 필요</li>
<li>예: <code>getWeather()</code>, <code>searchStockPrice()</code></li>
</ul>
</li>
<li>
<p><strong>MCP (Model Context Protocol)</strong></p>
<ul>
<li>여러 도구(Function/Resource)를 <strong>표준화된 방식으로 연결</strong>하는 프로토콜</li>
<li>각 도구를 “Provider"로 추상화하여, LLM이 일관된 방법으로 접근 가능</li>
<li>Function Call을 포함해서 “도구 호출"을 체계적으로 관리하는 상위 개념</li>
</ul>
</li>
</ul>
<h2 id="실제-예시">실제 예시</h2>
<h3 id="1-function-calling-단독-사용">1. Function Calling 단독 사용</h3>
<ul>
<li>
<p>OpenAI Function Call 예제:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"name"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"getWeather"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"description"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"현재 날씨 조회"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"parameters"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"type"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"object"</span><span style="color:#000;font-weight:bold">,</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"properties"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"location"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">"type"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">"string"</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">"required"</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">"location"</span><span style="color:#000;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>👉 모델이 <code>"서울"</code>을 location에 매핑해 API 실행</p>
</li>
</ul>
<h3 id="2-mcp-기반-function-calling">2. MCP 기반 Function Calling</h3>
<ul>
<li>MCP는 Function들을 <strong>Resource Registry</strong>로 묶어 관리</li>
<li>LLM이 “날씨 API"를 직접 알 필요 없이, MCP가 추상화된 <code>tools.weather</code> 같은 인터페이스 제공</li>
<li>여러 도구를 <strong>표준 프로토콜</strong>로 연결 → Function Call 혼란/중복 줄여줌</li>
</ul>
<h2 id="비유로-정리">비유로 정리</h2>
<ul>
<li><strong>Function Calling</strong> = 개별 “도구 사용법” (드라이버: 가위, 망치, 드라이버 같은 단일 도구)</li>
<li><strong>MCP</strong> = “공구함/작업대” (도구를 표준화된 방식으로 정리해 둔 시스템)</li>
</ul>
<h2 id="정리">정리</h2>
<p><strong>Function Calling은 개별 API를 호출하는 기술</strong>이고, <strong>MCP는 여러 Function(도구)들을 표준화된 방식으로 관리하고 연결해주는 상위 레이어</strong>라고 이해하면 된다.</p>
AI
-
AI Agent
https://www.devkuma.com/docs/ai/agent/
Sat, 30 Aug 2025 13:49:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/agent/
<h2 id="ai-agent란">AI Agent란?</h2>
<ul>
<li><strong>AI Agent</strong>는 단순히 답변만 생성하는 LLM(대규모 언어모델)과 달리, <strong>환경(Environment)과 상호작용하며 특정 목표를 달성하도록 설계된 지능형 시스템</strong>이다.</li>
<li>즉, <strong>스스로 판단하고 → 필요한 도구를 쓰고 → 행동하며 → 결과를 개선하는 AI</strong>라고 할 수 있다.</li>
</ul>
<h2 id="ai-agent의-핵심-요소">AI Agent의 핵심 요소</h2>
<ol>
<li><strong>목표(Goal)</strong>
<ul>
<li>에이전트가 수행해야 할 임무. (예: 고객 질문 답변, 보고서 작성, 코드 수정 등)</li>
</ul>
</li>
<li><strong>지각(Perception)</strong>
<ul>
<li>환경이나 입력을 이해하는 단계. (사용자 입력, 센서 데이터, API 응답 등)</li>
</ul>
</li>
<li><strong>행동(Action)</strong>
<ul>
<li>목표를 달성하기 위해 취하는 조치. (검색, 계산, 외부 API 호출, DB 업데이트 등)</li>
</ul>
</li>
<li><strong>피드백 루프(Feedback Loop)</strong>
<ul>
<li>결과를 평가하고 필요 시 다음 행동을 수정하는 과정.</li>
</ul>
</li>
</ol>
<h2 id="ai-agent와-단순-llm의-차이">AI Agent와 단순 LLM의 차이</h2>
<ul>
<li><strong>LLM</strong>: 질문 → 답변 (단순 Q&A)</li>
<li><strong>AI Agent</strong>: 질문 → 계획 세움 → 검색/도구 사용 → 여러 단계 실행 → 최종 답변</li>
</ul>
<p>예를 들어,</p>
<ul>
<li>LLM은 “서울 날씨 알려줘"에 대해 과거 학습 데이터를 기반으로 답할 수 있지만,</li>
<li>AI Agent는 <strong>실시간 API를 호출</strong>해 현재 기온과 날씨를 가져와 답할 수 있다.</li>
</ul>
<h2 id="ai-agent가-사용하는-기술">AI Agent가 사용하는 기술</h2>
<ul>
<li><strong>LLM (Large Language Model)</strong> → 자연어 이해 및 추론</li>
<li><strong>RAG (Retrieval-Augmented Generation)</strong> → 외부 지식 검색</li>
<li><strong>도구 사용(Plugins, APIs)</strong> → 계산기, 브라우저, 데이터베이스 등</li>
<li><strong>플래너(Planner)</strong> → 복잡한 작업을 여러 단계로 쪼개어 실행</li>
<li><strong>메모리(Memory)</strong> → 과거 대화/상태를 기억하여 연속적인 작업 수행</li>
</ul>
<h2 id="대표적인-ai-agent-예시">대표적인 AI Agent 예시</h2>
<ul>
<li><strong>ChatGPT + Tools (OpenAI)</strong> → 코드 실행, 웹 브라우징, 데이터 분석 가능</li>
<li><strong>AutoGPT, BabyAGI</strong> → 오픈소스 자율형 에이전트</li>
<li><strong>LangChain Agents</strong> → 여러 툴을 연결해 워크플로우 자동화</li>
<li><strong>Microsoft Copilot, Google Gemini Agents</strong> → 생산성 툴과 통합된 AI 비서</li>
</ul>
<h2 id="ai-agent의-활용-분야">AI Agent의 활용 분야</h2>
<ul>
<li><strong>업무 자동화</strong>: 이메일 요약, 일정 관리, 문서 생성</li>
<li><strong>고객 지원</strong>: FAQ 답변, 상담 업무 보조</li>
<li><strong>연구·분석</strong>: 논문 검색, 데이터 분석, 보고서 작성</li>
<li><strong>개발 보조</strong>: 코드 생성, 테스트, 디버깅</li>
<li><strong>로보틱스</strong>: 자율주행, 드론, 스마트 팩토리</li>
</ul>
<h2 id="정리">정리</h2>
<p><strong>AI Agent는 단순히 답변만 하는 LLM을 넘어, 목표를 세우고 도구를 활용하며 환경과 상호작용하는 지능형 시스템</strong>이다.<br>
즉, “<strong>실행 가능한 AI</strong>“라고 이해하면 된다.</p>
AI
-
Multi-Model
https://www.devkuma.com/docs/ai/multi-model/
Sat, 30 Aug 2025 13:14:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/multi-model/
<h2 id="multi-model이란">Multi-Model이란?</h2>
<p><strong>하나의 AI 시스템에서 여러 개의 모델을 함께 사용하는 접근 방식</strong>을 말한다.<br>
즉, 단일 모델에 모든 걸 맡기지 않고, <strong>각 모델의 강점을 조합</strong>해서 더 나은 성능이나 다양한 기능을 얻는 방법이다.</p>
<p>예들 들어, 텍스트뿐 아니라 이미지, 오디오, 비디오까지 함께 처리할 수 있는 모델이다.</p>
<h2 id="왜-필요한가">왜 필요한가?</h2>
<ol>
<li>
<p><strong>한 모델로는 부족한 경우</strong></p>
<ul>
<li>예: 이미지도 다루고 텍스트도 다뤄야 하는 경우</li>
</ul>
</li>
<li>
<p><strong>전문화된 모델 활용</strong></p>
<ul>
<li>대규모 범용 모델 + 도메인 특화 모델을 같이 사용</li>
</ul>
</li>
<li>
<p><strong>성능 최적화</strong></p>
<ul>
<li>무겁고 느린 모델은 핵심 추론에만, 가벼운 모델은 전처리·간단한 작업에 사용</li>
</ul>
</li>
<li>
<p><strong>비용 절감</strong></p>
<ul>
<li>항상 GPT-4 같은 초거대 모델을 쓰면 비쌈 → 일부는 작은 모델에게 맡기고, 어려운 부분만 큰 모델 사용</li>
</ul>
</li>
</ol>
<h2 id="multi-model의-종류">Multi-Model의 종류</h2>
<ol>
<li>
<p><strong>멀티 모달(Multi-Modal)과는 다름</strong></p>
<ul>
<li>Multi-Model ≠ Multi-Modal</li>
<li><em>Multi-Modal</em>: 이미지+텍스트+음성 등 <strong>여러 입력 형태</strong>를 처리하는 하나의 모델</li>
<li><em>Multi-Model</em>: <strong>여러 개의 모델을 조합</strong>해서 시스템 구성</li>
</ul>
</li>
<li>
<p><strong>구성 방식</strong></p>
<ul>
<li>
<p><strong>병렬(Ensemble)</strong>: 여러 모델이 동시에 답을 내고, 결과를 합쳐서 최종 결정</p>
<ul>
<li>예: 투표(Voting), 평균(Blending), 가중치 조합(Weighted Sum)</li>
</ul>
</li>
<li>
<p><strong>직렬(Pipeline)</strong>: 한 모델의 출력을 다른 모델의 입력으로 전달</p>
<ul>
<li>예: 이미지 캡션 모델 → 텍스트 요약 모델 → 질의응답 모델</li>
</ul>
</li>
<li>
<p><strong>하이브리드</strong>: 상황에 따라 모델 선택 (Router 모델)</p>
</li>
</ul>
</li>
</ol>
<h2 id="예시">예시</h2>
<ul>
<li><strong>검색 + 생성 (RAG)</strong>
<ul>
<li>검색 모델(벡터 검색) + 생성 모델(LLM)</li>
</ul>
</li>
<li><strong>Copilot류</strong>
<ul>
<li>코드 보조: 빠른 코드 완성은 작은 모델, 정교한 버그 수정은 GPT-4</li>
</ul>
</li>
<li><strong>자율주행</strong>
<ul>
<li>영상 인식 CNN + 행동 계획 RL 모델</li>
</ul>
</li>
<li><strong>헬스케어</strong>
<ul>
<li>의학적 지식 모델 + 일반 LLM 조합</li>
</ul>
</li>
</ul>
<h2 id="multi-model-vs-single-model">Multi-Model vs Single Model</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>Single Model</th>
<th>Multi-Model</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>구성</strong></td>
<td>하나의 모델이 모든 걸 수행</td>
<td>여러 모델이 역할 분담</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>단순, 관리 쉬움</td>
<td>정확도↑, 유연성↑, 최신 기술 활용 가능</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>범용 모델은 성능 한계</td>
<td>시스템 복잡, 조율 필요</td>
</tr>
</tbody>
</table>
<h2 id="정리">정리</h2>
<p><strong>Multi-Model은 여러 모델을 결합해, 각자의 장점을 살려 더 나은 결과를 내는 시스템 설계 방식</strong>이다.</p>
<p>예: “검색 모델 + 생성 모델”, “작은 모델 + 큰 모델”, “특화 모델 + 범용 모델"을 조합하는 식이다.</p>
AI
ChatGPT
LLM
-
RAG(Retrieval-Augmented Generation)
https://www.devkuma.com/docs/ai/rag/
Sat, 30 Aug 2025 13:09:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/rag/
<h2 id="rag-retrieval-augmented-generation-개념">RAG (Retrieval-Augmented Generation) 개념</h2>
<ul>
<li><strong>RAG = 검색(Retrieval) + 생성(Generation)</strong></li>
<li>LLM(대규모 언어모델)이 자기 내부 지식만으로 답을 생성하는 것이 아니라, 외부 데이터베이스(예: 문서, 벡터 DB, 위키, 사내 자료 등)에서 관련 정보를 검색한 후, 그 결과를 바탕으로 답변을 생성한다.</li>
</ul>
<p>즉, 단순히 “모델이 아는 것"만 쓰는 게 아니라, “필요할 때 외부에서 찾아보고 답하는” 똑똑한 비서 같은 개념이다</p>
<h2 id="왜-필요한가">왜 필요한가?</h2>
<ul>
<li>LLM의 지식 한계 극복
<ul>
<li>LLM은 학습 시점 이후의 최신 정보를 알지 못한다.</li>
<li>예를 들어, GPT 같은 모델은 학습 시점 이후의 최신 정보는 모른다.</li>
<li>RAG를 사용하면 DB/웹에서 찾아온 자료를 활용 가능하게 된다.</li>
</ul>
</li>
<li>환각(Hallucination) 줄이기
<ul>
<li>LLM은 모르는 것도 지어낼 때가 있다.</li>
<li>외부 근거 자료를 활용하면 답변 신뢰도를 높일 수 있다.</li>
<li>근거 없는 답변 대신, 실제 문서/DB를 근거로 답변 가능하다.</li>
</ul>
</li>
<li>맞춤형 지식 활용
<ul>
<li>기업 내부 문서, 보고서, 고객 FAQ, 논문, 코드베이스 등의 <strong>전용 데이터</strong>를 LLM이 사용할 수 있음.</li>
<li>사내 비밀 문서를 학습시키지 않고도 활용 가능하다.</li>
</ul>
</li>
</ul>
<h2 id="rag의-동작-구조">RAG의 동작 구조</h2>
<ul>
<li>질의(Query) 입력
<ul>
<li>사용자가 질문을 입력한다.</li>
</ul>
</li>
<li>검색(Retrieval) 단계
<ul>
<li>질문을 벡터화(임베딩) 후, 벡터 데이터베이스에서 관련 문서를 검색한다.</li>
<li>대표 DB: Pinecone, Weaviate, Milvus, FAISS 등.</li>
</ul>
</li>
<li>생성(Generation) 단계
<ul>
<li>LLM이 검색된 문서를 참고하여 답변을 생성하여 함께 전달한다.</li>
</ul>
</li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/rag.png" alt="RAG"></p>
<p>즉, <strong>“찾아서 → 참고해서 → 답변하는” 구조</strong>이다.</p>
<h2 id="예시">예시</h2>
<p>예를 들어, “우리 회사의 2023년 매출은 얼마야?“라는 질문이 들어오면:</p>
<ul>
<li>LLM 단독: “2023년 매출은 1억 달러입니다.” (근거 없음, 틀릴 수 있음)</li>
<li>RAG 활용: 회사 내부 재무 보고서를 검색 → 관련 데이터 가져옴 → “2023년 당사의 매출은 9,200억 원으로, 전년 대비 8% 성장했습니다.” (근거 있는 답변)</li>
</ul>
<h2 id="비유로-이해하기">비유로 이해하기</h2>
<ul>
<li><strong>LLM 단독</strong>: 기억력 좋은 사람, 하지만 최신 정보는 모를 수 있다.</li>
<li><strong>RAG 사용</strong>: 기억력 좋은 사람이 <strong>사전·검색 엔진</strong>을 참고해서 답변하는 것이다.</li>
</ul>
<h2 id="rag와-fine-tuning의-비교">RAG와 Fine-tuning의 비교</h2>
<ul>
<li>Fine-tuning: 모델 자체를 추가 학습 → 새로운 지식을 “내재화”</li>
<li>RAG: 모델은 그대로 두고, 외부 자료를 검색해서 활용</li>
</ul>
<table>
<thead>
<tr>
<th>방법</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fine-tuning</td>
<td>응답이 빠르고 자연스러움</td>
<td>데이터 업데이트할 때마다 재학습 필요</td>
</tr>
<tr>
<td>RAG</td>
<td>항상 최신/맞춤 정보 반영 가능, 빠른 구축</td>
<td>검색 품질에 따라 답변 품질 좌우</td>
</tr>
</tbody>
</table>
<p>실무에서는 RAG + 필요시 일부 Fine-tuning을 섞어서 많이 사용된다.</p>
<h2 id="rag-구현에-쓰이는-기술-스택">RAG 구현에 쓰이는 기술 스택</h2>
<ul>
<li>임베딩 모델: OpenAI Embeddings, Sentence-BERT 등</li>
<li>벡터 DB: Pinecone, Weaviate, Milvus, FAISS</li>
<li>LLM: GPT, Claude, LLaMA, Gemini 등</li>
<li>프레임워크: LangChain, LlamaIndex, Haystack</li>
</ul>
<h2 id="정리">정리</h2>
<ul>
<li>RAG는 LLM이 검색 시스템을 함께 사용해, 신뢰할 수 있고 최신 정보를 반영하는 답변을 생성하는 방식이다.</li>
<li>즉, 지식의 확장 & 신뢰성 보강을 위한 핵심 기술이다.</li>
</ul>
AI
RAG
-
LLM(Large Language Model)
https://www.devkuma.com/docs/ai/llm/
Sun, 24 Aug 2025 13:14:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/llm/
<h2 id="llm-개요">LLM 개요</h2>
<p>LLM(Large Language Models, 대규모 언어 모델)은 방대한 양의 텍스트 데이터를 학습하여 <strong>자연어를 이해하고 생성할 수 있는 인공지능 모델</strong>이다. 이들은 주로 <strong>딥러닝 기반의 트랜스포머 구조</strong>를 활용하는데, 따라서 인간의 언어 특성을 통계적으로 파악하여 높은 수준의 텍스트 생성 및 처리 능력을 갖추고 있다.</p>
<p>LLM은 오늘날 AI의 중추로서, 언어 기반 애플리케이션과 시스템 설계에서 매우 중요한 역할을 수행하고 있다.</p>
<h2 id="llm-작동-원리">LLM 작동 원리</h2>
<h3 id="학습-방식-및-트랜스포머-아키텍처">학습 방식 및 트랜스포머 아키텍처</h3>
<p>LLM은 수천억 개의 텍스트 예시를 통해 <strong>비지도 학습</strong> 방식으로 사전 학습(pre-training)을 수행한다.<br>
특히, <strong>트랜스포머 구조</strong>는 셀프 어텐션(self-attention)을 통해 문맥의 관계를 이해하며, 이전의 순환신경망(RNN)보다 병렬 처리가 가능하여 학습 효율이 매우 높다</p>
<h3 id="파라미터와-임베딩">파라미터와 임베딩</h3>
<p>‘대규모’라는 명칭은 수십억에서 수천억 개에 이르는 “파라미터(parameter)“의 크기를 의미한다. 이러한 방대한 매개변수를 통해 언어의 복잡한 맥락과 뉘앙스를 포착할 수 있다.
또한, “임베딩(embedding)“은 단어를 다차원 벡터로 변환하여 의미적 유사성을 수치적으로 표현함으로써 문맥 이해를 돕는다.</p>
<h2 id="응용-분야">응용 분야</h2>
<p>LLM은 매우 유연하게 활용될 수 있으며, 대표적인 응용 예시는 다음과 같다:</p>
<ul>
<li><strong>생성형 AI</strong>: 사용자 프롬프트에 따라 에세이, 번역, 요약 등의 텍스트 생성</li>
<li><strong>코드 생성</strong>: GitHub Copilot, AWS CodeWhisperer 등 자연어로부터 코드 작성 지원</li>
<li><strong>텍스트 분류 및 감정 분석</strong>: 고객 피드백 분류, 문서 클러스터링 등</li>
<li>기타: 지식 기반 질의 응답(KI-NLP), 챗봇, 고객 서비스 자동화 등</li>
</ul>
<h2 id="학습-방법의-종류">학습 방법의 종류</h2>
<p>LLM을 특정 용도에 맞추어 활용하는 방법에는 다음 세 가지가 있다:</p>
<ul>
<li><strong>제로샷 학습 (Zero-Shot)</strong>: 추가 학습 없이 일반적인 프롬프트만으로 다양한 작업 수행 가능</li>
<li><strong>퓨샷 학습 (Few-Shot)</strong>: 소량의 예제를 제공함으로써 성능을 향상</li>
<li><strong>미세 조정 (Fine-Tuning)</strong>: 특정 데이터로 파라미터를 추가 학습시켜 특화된 적용이 가능</li>
</ul>
<h2 id="중요성-및-기대-효과">중요성 및 기대 효과</h2>
<p>LLM의 도입은 기업과 조직에 다양한 이점을 가져다줄 수 있다:</p>
<ul>
<li><strong>업무 자동화</strong>: 고객 지원, 문서 요약, 콘텐츠 생성 등 언어 기반 작업의 자동화로 생산성 향상</li>
<li><strong>확장성 및 유연성</strong>: 하나의 모델이 번역, 요약, 질의 응답 등 여러 작업에 유연하게 대응</li>
<li><strong>혁신 유도</strong>: 지식 추출, 창작 보조, 대화형 인터페이스 등 다양한 미래 가능성에 기반 제공</li>
</ul>
<h2 id="한계-및-고려-사항">한계 및 고려 사항</h2>
<p>LLM 활용 시에는 다음과 같은 한계도 고려해야 한다:</p>
<ul>
<li><strong>높은 자원 요구</strong>: 수십억 개 파라미터 기반 모델의 학습 및 서비스 운영에는 상당한 컴퓨팅 자원이 필요하다.</li>
<li><strong>잠재적 편향 및 오류</strong>: 학습 데이터의 한계 또는 편향이 모델 출력에 반영될 수 있으며, 정확성에 대한 지속적인 개선이 필요하다.</li>
<li><strong>프라이버시 및 보안 우려</strong>: 사적이거나 민감한 데이터와의 연관 가능성에 대비해야 한다.</li>
</ul>
<hr>
<h2 id="요약">요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td>정의</td>
<td>방대한 텍스트 기반 딥러닝 모델로 자연어 이해·생성 가능</td>
</tr>
<tr>
<td>작동 원리</td>
<td>트랜스포머 기반, 셀프 어텐션·임베딩·수십억 파라미터</td>
</tr>
<tr>
<td>응용 분야</td>
<td>텍스트 생성, 코드 생성, 분류, 요약, 챗봇 등</td>
</tr>
<tr>
<td>학습 방식</td>
<td>Zero-Shot, Few-Shot, Fine-Tuning</td>
</tr>
<tr>
<td>장점</td>
<td>자동화, 확장성, 창의적 활용 가능성</td>
</tr>
<tr>
<td>한계</td>
<td>자원 요구, 편향·정확도 문제, 보안 위험 등</td>
</tr>
</tbody>
</table>
AI
ChatGPT
LLM
-
MCP (Model Context Protocol)
https://www.devkuma.com/docs/ai/mcp/
Sun, 24 Aug 2025 13:14:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/mcp/
<h2 id="mcp-개요">MCP 개요</h2>
<p>Model Context Protocol(MCP)는 <strong>AI, 특히 대규모 언어 모델(LLM)이 외부 데이터 소스 및 도구와 효과적으로 상호작용하도록 돕는 오픈 표준 프로토콜</strong>이다.</p>
<p>이 프로토콜은 애플리케이션이 LLM에 컨텍스트(Context)를 일관된 방식으로 전달할 수 있도록 설계되었다. 한마디로, <strong>AI용 USB-C 포트</strong>라는 은유로 설명되기도 한다. 이는 USB-C가 여러 장치를 통일된 포맷으로 연결하듯, MCP도 AI 모델과 다양한 리소스를 표준화된 방식으로 연결해 준다.</p>
<p>즉, <strong>AI 모델이 다양한 외부 시스템과 연결될 수 있도록 해주는 공용 인터페이스</strong>이다.</p>
<p><img src="https://www.devkuma.com/docs/ai/mcp-architecture.png" alt="MCP Architecture"></p>
<h3 id="주요-특징">주요 특징</h3>
<ol>
<li>
<p><strong>표준화된 인터페이스</strong></p>
<ul>
<li>모델이 “데이터 소스 / 도구 / 애플리케이션"에 접근할 수 있는 공용 규약 제공.</li>
</ul>
</li>
<li>
<p><strong>플러그형 구조</strong></p>
<ul>
<li>특정 애플리케이션에 종속되지 않고, 어떤 모델이든 MCP를 지원하면 같은 방식으로 확장 가능.</li>
</ul>
</li>
<li>
<p><strong>보안 & 제어</strong></p>
<ul>
<li>모델이 접근할 수 있는 범위를 제한하고, 사용자가 허용한 리소스에만 접근하도록 설계.</li>
</ul>
</li>
<li>
<p><strong>개발자 친화적</strong></p>
<ul>
<li>OpenAI, Anthropic 같은 여러 AI 모델에서 공용으로 사용 가능 → “한번 만든 MCP 도구는 어디서든 사용”.</li>
</ul>
</li>
</ol>
<h3 id="예시">예시</h3>
<ul>
<li>
<p>모델이 “데이터베이스 질의"가 필요할 때:</p>
<pre tabindex="0"><code>모델 → MCP → DB Adapter → Database
</code></pre></li>
<li>
<p>모델이 “웹 API 호출"을 해야 할 때:</p>
<pre tabindex="0"><code>모델 → MCP → HTTP Adapter → 외부 REST API
</code></pre></li>
</ul>
<p>즉, MCP는 <strong>AI의 플러그인 생태계를 표준화</strong>하는 기반 기술이라고 볼 수 있다.</p>
<h3 id="도입-배경과-필요성">도입 배경과 필요성</h3>
<p>AI 모델은 본질적으로 <strong>텍스트 기반</strong>으로만 입출력을 한다. 그러나 실제 활용에서는 DB 조회, API 호출, 파일 입출력 등 다양한 작업이 필요하다. 지금까지는 <strong>플러그인, LangChain, 자체 API 브릿지</strong> 같은 개별 솔루션을 써야 했는데, 이들을 <strong>표준화된 프로토콜</strong>로 묶은 것이 MCP이다.</p>
<p>기존에는 AI 애플리케이션이 외부 시스템과 상호작용하기 위해, <strong>모델마다, 도구마다 각각 맞춤형 통합 구현이 필요</strong>했다. 이로 인해 개발 및 유지보수의 복잡성이 크게 증가했고, 이를 <strong>M×N 문제</strong>라고 표현했다. MCP는 이를 <strong>M+N 구조로 단순화</strong>하여, AI 애플리케이션이 다양한 도구와 표준화된 방식으로 연결될 수 있도록 지원한다.</p>
<p><img src="https://www.devkuma.com/docs/ai/mcp-before-after.webp" alt="MCP Architecture"></p>
<ul>
<li>이미지 출처: <a href="https://www.descope.com/learn/post/mcp" target="_blank" rel="noopener">https://www.descope.com/learn/post/mcp<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h2 id="아키텍처-및-작동-원리">아키텍처 및 작동 원리</h2>
<h3 id="클라이언트-서버-구조">클라이언트-서버 구조</h3>
<p>MCP는 <strong>클라이언트-서버 아키텍처</strong>를 채택하고 있으며,</p>
<ul>
<li><strong>MCP 클라이언트</strong>는 AI 애플리케이션(예: Claude Desktop)이다.</li>
<li><strong>MCP 서버</strong>는 파일 시스템, 데이터베이스, API 등 외부 리소스를 제공한다.</li>
</ul>
<h3 id="통신-방식">통신 방식</h3>
<p>MCP는 <strong>JSON-RPC 2.0</strong> 기반으로 요청(Request)과 응답(Response)을 교환하며, 이는 <strong>표준화된 메시지 교환 방식을 통해 상호운용성을 향상</strong>시킨다.
또한, **로컬 프로세스 간 통신(stdio 기반)**과 <strong>HTTP + SSE(서버 전송 이벤트) 기반</strong> 통신을 모두 지원한다.</p>
<h3 id="서버의-역할">서버의 역할</h3>
<p>MCP 서버는 다음과 같은 기능을 수행한다.</p>
<ol>
<li><strong>Tool Registry</strong>: 사용 가능한 툴 및 기능의 목록 관리</li>
<li><strong>Authentication</strong>: 접근 권한 검증</li>
<li><strong>Request Handler</strong>: 클라이언트의 요청 처리</li>
<li><strong>Response Formatter</strong>: 결과를 AI 모델이 이해할 수 있는 형식으로 가공</li>
</ol>
<p>AI 애플리케이션은 서버에 “사용 가능한 도구 목록"을 요청하고, 이를 바탕으로 적절한 툴을 선택하고 활용할 수 있다.</p>
<h2 id="개발자-친화성과-확장성">개발자 친화성과 확장성</h2>
<p>Anthropic은 MCP를 <strong>오픈소스 표준</strong>으로 공개하였으며, <strong>Python, TypeScript, Java, Kotlin, C# 등 주요 언어용 SDK</strong>도 제공하고 있다. 이를 통해 <strong>클라이언트 및 서버 구현이 대체로 간단하다</strong>는 장점을 얻을 수 있다</p>
<h2 id="활용-효과-및-이점">활용 효과 및 이점</h2>
<ol>
<li><strong>명확한 지시</strong>: LLM이 어떤 데이터를 취급할지 명확히 지정 가능</li>
<li><strong>불명확성 제거</strong>: 여러 정보원을 명확히 구분하여 참조 가능</li>
<li><strong>특화된 처리 지원 가능</strong>: 특정 데이터 형식에 맞춘 전용 처리가 가능</li>
<li><strong>컨텍스트 예시</strong>: 파일 시스템, DB, 클라우드 서비스 등 다양한 컨텍스트를 함께 활용 가능</li>
</ol>
<p>이러한 이점 덕분에, <strong>AI의 활동 범위가 확장되고, 더 정확하고 문맥에 맞는 응답</strong>을 제공할 수 있게 된다.</p>
<h2 id="도입-시기-및-생태계-동향">도입 시기 및 생태계 동향</h2>
<ul>
<li>MCP는 <strong>2024년 11월에 Anthropic이 오픈소스로 공개</strong>하였으며, <strong>2025년 초부터 개발자 커뮤니티와 주요 AI 도구들에서의 채택이 급속히 증가</strong>하였다.</li>
<li>C#용 공식 SDK의 출시에 관한 언급도 있으며, 현재 수많은 MCP 서버가 운영 중이며, <strong>보안 아키텍처 및 데이터 보호</strong>도 중요한 관심사로 자리 잡고 있다.</li>
</ul>
<hr>
<h2 id="요약-표">요약 표</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>정의</strong></td>
<td>AI 모델과 외부 데이터/도구를 연결하는 오픈 표준 프로토콜</td>
</tr>
<tr>
<td><strong>도입 배경</strong></td>
<td>개별 통합의 복잡성을 해소하고 더 유연한 확장성 확보</td>
</tr>
<tr>
<td><strong>구조</strong></td>
<td>클라이언트-서버 구조, JSON-RPC, 표준 메시지 교환</td>
</tr>
<tr>
<td><strong>SDK</strong></td>
<td>Python, TS, Java, Kotlin, C# 등 제공</td>
</tr>
<tr>
<td><strong>이점</strong></td>
<td>명확성, 확장성, 자동화, 보안성 강화 등</td>
</tr>
<tr>
<td><strong>현재 동향</strong></td>
<td>공개 이후 급성장 중이며 보안 및 실용성 확대 중</td>
</tr>
</tbody>
</table>
<hr>
<p>궁금하신 부분이 더 있다면 언제든지 말씀해 주세요. 예를 들어, MCP 서버 직접 구축 사례나 기술 활용 흐름 등도 설명드릴 수 있습니다.</p>
AI
ChatGPT
MCP
-
SRE란?
https://www.devkuma.com/docs/sre/
Sun, 24 Aug 2025 13:07:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/sre/
<h2 id="sre란">SRE란?</h2>
<p>SRE(Site Reliability Engineering, 사이트 신뢰성 엔지니어링)은 IT 운영에 대한 소프트웨어 엔지니어링 접근 방식이다. SRE은 소프트웨어를 툴로 활용하여 시스템을 <strong>관리</strong>하고, 문제를 <strong>해결</strong>하고, 운영 태스크를 <strong>자동화</strong>한다.</p>
<p>SRE는 확장 가능하고 신뢰성이 높은 소프트웨어 시스템을 <strong>생성</strong>할 때 유용한 방법이다. 코드를 통해 대규모로 시스템을 <strong>관리</strong>할 수 있으므로 수천 대에서 수십만 대에 이르는 머신을 관리하는 시스템 관리자에게 더 큰 <strong>확장성</strong>과 <strong>지속가능성</strong>을 제공한다.</p>
<p>Google 엔지니어링 팀의 Gen Treynor Sloss가 창안한 개념이다.</p>
<h2 id="sre를-사용함으로써-있는-장점">SRE를 사용함으로써 있는 장점</h2>
<p>새 기능을 적시에 출시하고, 사용자가 이 기능을 <strong>안정적</strong>으로 사용하도록 할 수 있다.</p>
<h3 id="sre-엔지니어의-역할">SRE 엔지니어의 역할</h3>
<p>부가적인 운영 경험이 있는 소프트웨어 개발자, 소프트웨어 개발 기술을 갖춘 시스템 관리자 또는 IT 운영자와 같은 경력이 요구되는 독특한 역할이다.</p>
<p>SRE팀은 코드의 배포, 설정, 모니터링 방식뿐만 아니라 프로덕션 환경에서 서비스 가용성, 대기 시간, 변경 관리, 비상 대응 및 용량 관리를 담당한다.</p>
<p>새로 출시할 기능과, 서비스 수준 계약(SLA)를 사용하여 서비스 수준 지표(SLI) 및 서비스 수준 목표(SLO)를 통해 시스템의 신뢰성 요구 사항을 정의해야 할 시점을 결정할 수 있다.</p>
<p>SRE에서는 <strong>100% 신뢰성을 기대하지 않으며</strong> 장애에 대비해 계획을 마련한다.</p>
<h2 id="devops와-차이점">DevOps와 차이점</h2>
<p>DevOps는 신속한 고품질 서비스 제공을 통해 비즈니스 가치와 대응력을 향상시키기 위한 기업 문화, 자동화, 플랫폼 설계에 대한 <strong>접근 방식</strong>이다.</p>
<p>SRE는 DevOps의 <strong>구현</strong>으로 간주될 수 있다.</p>
<table>
<thead>
<tr>
<th></th>
<th>SRE</th>
<th>DevOps</th>
</tr>
</thead>
<tbody>
<tr>
<td>주요 관심</td>
<td>확장성, 운영 지표, 자동화</td>
<td>개발 배포 과정 통합</td>
</tr>
<tr>
<td>담당자</td>
<td>운영에 관심있는 개발팀</td>
<td>개발에 관심있는 운영팀</td>
</tr>
<tr>
<td>측정 지표</td>
<td>서비스 수준 목표(SLO)의 최대/최소치</td>
<td>주로 시스템 Telemetry</td>
</tr>
<tr>
<td>적용 기업</td>
<td>클라우드-네이티브 환경에서 IT 서비스 기업</td>
<td>온-프레미스에서 클라우드로 전향하는 기업</td>
</tr>
</tbody>
</table>
SRE
-
TypeScript 얕은 복사와 깊은 복사
https://www.devkuma.com/docs/typescript/shallow-deep-copy/
Sat, 16 Aug 2025 23:26:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/typescript/shallow-deep-copy/
<h2 id="복사-copying">복사 (Copying)</h2>
<p>프로그래밍에서 <strong>복사</strong>에는 크게 두 가지 방식이 있다.</p>
<ol>
<li><strong>깊은 복사(Deep Copy)</strong></li>
<li><strong>얕은 복사(Shallow Copy)</strong></li>
</ol>
<h3 id="깊은-복사-deep-copy">깊은 복사 (Deep Copy)</h3>
<ul>
<li>깊은 복사는 <strong>복사한 값이 바뀌더라도 원본 값은 영향을 받지 않는 복사 방식</strong>이다</li>
<li>TypeScript에서는 **원시 타입(primitive type)**인 <code>number</code>, <code>boolean</code>은 기본적으로 깊은 복사 방식으로 동작한다.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">original</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">copied</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">;</span>
</span></span><span style="display:flex;"><span><span style="color:#000">copied</span> <span style="color:#ce5c00;font-weight:bold">+=</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">copied</span><span style="color:#000;font-weight:bold">);</span> <span style="color:#8f5902;font-style:italic">// 1 3
</span></span></span></code></pre></div><blockquote>
<p>원본 <code>original</code>은 변하지 않고, <code>copied</code>만 변경된다.</p></blockquote>
<h3 id="얕은-복사-shallow-copy">얕은 복사 (Shallow Copy)</h3>
<ul>
<li>얕은 복사는 <strong>복사본과 원본이 같은 참조(reference)를 공유하는 복사 방식</strong>이다.</li>
<li>TypeScript에서 **객체(Object)와 배열(Array)**는 기본적으로 얕은 복사 방식으로 동작한다.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">originalArray</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">9</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">];</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">shallowCopiedArray</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">originalArray</span><span style="color:#000;font-weight:bold">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">shallowCopiedArray</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">originalArray</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">shallowCopiedArray</span><span style="color:#000;font-weight:bold">);</span>
</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// [0, 3, 9, 7] [0, 3, 9, 7]
</span></span></span></code></pre></div><blockquote>
<p>배열 <code>originalArray</code>가 <code>shallowCopiedArray</code>와 같은 참조를 공유하기 때문에, 하나를 수정하면 다른 하나도 영향을 받는다.</p></blockquote>
<h3 id="전개-연산자를-활용한-복사">전개 연산자(…)를 활용한 복사</h3>
<ul>
<li>**전개 연산자(<code>...</code>)**를 사용하면 <strong>1차원 배열에 한해 깊은 복사처럼 사용할 수 있다.</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">oArray</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">];</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">deepCopiedArray</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[...</span><span style="color:#000">oArray</span><span style="color:#000;font-weight:bold">];</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">deepCopiedArray</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">oArray</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">deepCopiedArray</span><span style="color:#000;font-weight:bold">);</span>
</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// [1, 2, 3, 4] [0, 2, 3, 4]
</span></span></span></code></pre></div><blockquote>
<p>원본 배열은 그대로 유지되고, 복사본만 변경된다.</p></blockquote>
<blockquote>
<p><strong>주의:</strong> 객체가 중첩된 배열이나 중첩 객체까지 완전히 복사하려면 전개 연산자만으로는 <strong>깊은 복사</strong>가 되지 않다.<br>
이 경우 <code>JSON.parse(JSON.stringify(obj))</code> 등을 사용한다.</p></blockquote>
<hr>
<h2 id="정리">정리</h2>
<table>
<thead>
<tr>
<th>타입/방법</th>
<th>복사 방식</th>
<th>특징</th>
</tr>
</thead>
<tbody>
<tr>
<td>number, boolean</td>
<td>깊은 복사</td>
<td>원본과 복사본 독립</td>
</tr>
<tr>
<td>객체(Object), 배열(Arra)</td>
<td>얕은 복사</td>
<td>원본과 복사본 참조 공유</td>
</tr>
<tr>
<td>전개 연산자 <code>...</code></td>
<td>1차원 깊은 복사</td>
<td>1차원 배열/객체에 대해 원본 보존</td>
</tr>
</tbody>
</table>
AI
-
인공지능 이해와 활용
https://www.devkuma.com/docs/ai/overview/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/overview/
<h2 id="서문">서문</h2>
<p>인공지능(AI: Artificial Intelligence)은 더 이상 미래의 기술이 아니라 현재 우리의 삶 속에 깊이 들어와 있는 핵심 기술이다. 여기서는 인공지능의 기본 개념에서부터 최신 응용 사례까지를 체계적으로 설명하여, 독자들이 AI의 본질을 이해하고 실제로 활용할 수 있도록 돕는 것을 목표로 한다.</p>
AI
-
AI 용어
https://www.devkuma.com/docs/ai/term/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/term/
<h2 id="헷갈리고-어려운-ai-관련-최근-용어들">헷갈리고 어려운 AI 관련 최근 용어들</h2>
<ul>
<li>AI(Artificial Intelligence)
<ul>
<li>컴퓨터가 사람의 지능을 모방하여 학습하고 문제를 해결하는 기술</li>
</ul>
</li>
<li>ML(Machine Learning)
<ul>
<li>AI가 데이터를 통해 자동 학습하고 예측하는 기술</li>
</ul>
</li>
<li>DL(Deep Learning)
<ul>
<li>ML의 한 종류로 ‘인공신경망’을 활용해 복잡한 패턴을 학습하는 기술</li>
</ul>
</li>
<li>AX(AI Transformation)
<ul>
<li>DX를 넘어 AI를 중심으로 기업의 변화를 추구</li>
</ul>
</li>
<li>AGI(Artificial General Intelligence)
<ul>
<li>특정 작업에 국한되지 않고, 사람처럼 여러 분야에서 지능적으로 문제를 해결할 수 있는 인공지능</li>
</ul>
</li>
<li>Gen(Generative) AI
<ul>
<li>Text, Image, Music, Video 등을 생성하는 인공지능</li>
</ul>
</li>
<li>Prompting
<ul>
<li>Gen AI에게 필요한 답을 얻도록, 질문하는 방법</li>
</ul>
</li>
<li>LLM(Large Language Model)
<ul>
<li>빅데이터를 기반으로 언어와 유사한 텍스트를 이해하고 만들어 내는 기술</li>
</ul>
</li>
<li>FM(Foundation Model)
<ul>
<li>다양하고 방대한 양의 데이터로 사전 학습된 모델 (언어 처리, 이미지 인식, Audio/Video 생성 등에 기초 모델로 사용)</li>
</ul>
</li>
<li>Hallucination(환각현상)
<ul>
<li>AI가 잘못된 결론에 도달하여, 현실에 없는 결과물(콘텐츠)를 생성해 내는 현상</li>
</ul>
</li>
</ul>
AI
-
AI 도구
https://www.devkuma.com/docs/ai/tool/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/tool/
<p>AI 도구</p>
AI
-
인공지능의 개념과 역사
https://www.devkuma.com/docs/ai/concept-and-history/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/concept-and-history/
<h2 id="인공지능의-정의">인공지능의 정의</h2>
<p>인공지능은 인간의 학습, 추론, 문제 해결 능력을 컴퓨터가 수행할 수 있도록 구현한 기술을 의미한다. 단순한 규칙 기반의 자동화에서 출발하여, 현재는 스스로 학습하고 적응하는 기계학습 및 딥러닝 기술로 발전하였다.</p>
<p><img src="https://www.devkuma.com/docs/ai/apple-siri-goolgle-assistant.png" alt="Siri, Google Assistant"></p>
<ul>
<li>사례: 마트폰 음성 비서(예: Siri, Google Assistant)는 단순 명령 수행을 넘어 사용자의 언어 패턴을 학습하여 점차 정교한 대화를 지원한다.</li>
</ul>
<h2 id="인공지능의-역사">인공지능의 역사</h2>
<ul>
<li>1950년대: 앨런 튜링의 “튜링 테스트” 제안</li>
<li>1960~70년대: 규칙 기반 전문가 시스템 등장</li>
<li>1980~90년대: 신경망 연구 재개 및 머신러닝 발전</li>
<li>2000년대 이후: 빅데이터와 GPU의 발전으로 딥러닝이 급성장</li>
<li>현재: 자연어 처리, 자율주행, 의료 진단 등 다양한 분야로 확산</li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/ai-history.png" alt="인공지능의 역사"></p>
<ul>
<li>도표 1: 인공지능 역사 연대표 (연도별 주요 기술 발전 흐름)</li>
<li>이미지 출처: <a href="https://spri.kr/posts/view/21643?code=industry_trend" target="_blank" rel="noopener">https://spri.kr/posts/view/21643?code=industry_trend<i class="fas fa-external-link-alt"></i></a></li>
</ul>
AI
-
인공지능의 핵심 기술
https://www.devkuma.com/docs/ai/core-technologies/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/core-technologies/
<h2 id="머신러닝">머신러닝</h2>
<p>머신러닝(ML, Machine Learning)은 <strong>컴퓨터가 명시적으로 프로그래밍되지 않아도, 데이터에서 패턴을 학습하고 예측이나 결정을 내릴 수 있는 기술</strong>이다. 즉, 사람이 일일이 규칙을 작성하지 않아도, 데이터를 통해 스스로 규칙과 연관성을 찾아내는 능력을 말한다. 대표적으로 지도학습, 비지도학습, 강화학습이 있다.</p>
<h3 id="머신러닝의-종류">머신러닝의 종류</h3>
<p>머신러닝은 주로 <strong>학습 방식</strong>에 따라 세 가지로 나눈다.</p>
<ol>
<li>
<p><strong>지도학습(Supervised Learning)</strong></p>
<ul>
<li><strong>정의</strong>: 입력 데이터와 그에 대응하는 정답(Label)을 함께 제공하여 모델을 학습시킨다.</li>
<li><strong>목적</strong>: 입력 데이터로부터 정답을 예측</li>
<li><strong>예시</strong>:
<ul>
<li>이메일 스팸 분류 (스팸/정상)</li>
<li>집 가격 예측 (면적, 위치 → 가격)</li>
</ul>
</li>
<li><strong>알고리즘</strong>: 선형회귀, 로지스틱 회귀, 결정트리, 랜덤포레스트, 서포트 벡터 머신 등</li>
</ul>
</li>
<li>
<p><strong>비지도학습(Unsupervised Learning)</strong></p>
<ul>
<li>
<p><strong>정의</strong>: 정답 없이 입력 데이터만으로 학습하여 숨겨진 패턴이나 구조를 찾습니다.</p>
</li>
<li>
<p><strong>목적</strong>: 데이터 군집화, 차원 축소</p>
</li>
<li>
<p><strong>예시</strong>:</p>
<ul>
<li>고객 세분화 (구매 패턴 기반 군집)</li>
<li>이상치 탐지 (사기 거래 발견)</li>
</ul>
</li>
<li>
<p><strong>알고리즘</strong>: K-평균 클러스터링, 계층적 군집화, PCA(주성분 분석) 등</p>
</li>
</ul>
</li>
<li>
<p><strong>강화학습(Reinforcement Learning)</strong></p>
<ul>
<li><strong>정의</strong>: 행동에 따른 보상(Reward)과 벌점(Penalty)을 이용해 최적의 전략을 학습한다.</li>
<li><strong>목적</strong>: 순차적 의사결정 최적화</li>
<li><strong>예시</strong>:
<ul>
<li>알파고 바둑</li>
<li>자율주행 자동차 경로 학습</li>
</ul>
</li>
<li><strong>알고리즘</strong>: Q-러닝, 딥 Q-네트워크(DQN), 정책 경사법 등</li>
</ul>
</li>
</ol>
<h3 id="머신러닝의-작동-원리">머신러닝의 작동 원리</h3>
<ol>
<li><strong>데이터 수집</strong>: 학습할 데이터를 모음 (예: 이미지, 텍스트, 센서 데이터)</li>
<li><strong>데이터 전처리</strong>: 결측치 제거, 정규화, 특징(Feature) 추출</li>
<li><strong>모델 선택</strong>: 문제 유형에 맞는 ML 알고리즘 선택</li>
<li><strong>학습(Training)</strong>: 데이터를 통해 모델이 규칙/패턴을 학습</li>
<li><strong>평가(Evaluation)</strong>: 테스트 데이터를 통해 모델 정확도 측정</li>
<li><strong>예측(Prediction)</strong>: 새로운 데이터에 대한 결과를 예측</li>
</ol>
<h3 id="머신러닝의-실제-사례">머신러닝의 실제 사례</h3>
<ul>
<li><strong>의료</strong>: MRI, CT 영상 분석을 통한 질병 진단 보조</li>
<li><strong>금융</strong>: 신용 점수 예측, 사기 거래 탐지</li>
<li><strong>전자상거래</strong>: 개인 맞춤형 상품 추천</li>
<li><strong>자율주행</strong>: 도로 객체 인식, 경로 결정</li>
<li><strong>자연어 처리</strong>: 기계 번역, 챗봇 대화 생성</li>
</ul>
<h3 id="머신러닝의-장점">머신러닝의 장점</h3>
<ul>
<li>데이터 기반 의사결정 가능</li>
<li>대규모 데이터 처리와 복잡한 패턴 학습에 효율적</li>
<li>반복 학습으로 성능 개선 가능</li>
</ul>
<h3 id="머신러닝의-한계">머신러닝의 한계</h3>
<ul>
<li><strong>데이터 의존성</strong>: 품질 좋은 데이터가 반드시 필요</li>
<li><strong>과적합(Overfitting)</strong>: 학습 데이터에만 특화되어 일반화 능력 부족</li>
<li><strong>설명력 부족</strong>: 모델이 왜 그런 결정을 내렸는지 이해하기 어려운 경우가 있음</li>
<li><strong>윤리 문제</strong>: 편향된 데이터로 인해 불공정한 결과가 발생할 수 있음</li>
</ul>
<h2 id="딥러닝">딥러닝</h2>
<p>딥러닝(Deep Learning)은 인공지능 분야에서 <strong>인간의 뇌 구조를 모방한 인공신경망(Artificial Neural Network)을 기반으로, 다층 구조를 통해 데이터의 복잡한 패턴과 특징을 학습하는 기술</strong>을 의미한다. 단순한 머신러닝 모델이 데이터의 기본적 관계를 학습하는 것에 비해, 딥러닝은 여러 층(Layer)을 거치며 점점 추상화된 특징을 학습함으로써 고차원적 문제 해결에 강점을 가진다. 이미지 인식, 음성 인식, 자연어 처리 등에서 탁월한 성능을 보여주고 있다.</p>
<h3 id="딥러닝의-핵심-원리">딥러닝의 핵심 원리</h3>
<ol>
<li>
<p><strong>인공신경망 구조</strong></p>
<ul>
<li>입력층(Input Layer): 데이터가 처음 들어오는 층</li>
<li>은닉층(Hidden Layer): 입력 데이터를 처리하고 특징을 추출하는 층</li>
<li>출력층(Output Layer): 최종 예측이나 분류 결과를 출력하는 층</li>
</ul>
</li>
<li>
<p><strong>학습 과정</strong></p>
<ul>
<li><strong>순전파(Forward Propagation)</strong>: 입력 데이터를 통해 출력값을 계산</li>
<li><strong>손실 함수(Loss Function)</strong>: 출력값과 실제값의 차이를 측정</li>
<li><strong>역전파(Backpropagation)</strong>: 오차를 기반으로 가중치(Weight)를 조정</li>
<li>반복적 학습을 통해 모델의 예측 정확도를 점차 향상</li>
</ul>
</li>
<li>
<p><strong>활성화 함수(Activation Function)</strong></p>
<ul>
<li>신경망의 각 노드에서 출력값을 변환하여 비선형성을 부여</li>
<li>대표적 함수: 시그모이드(Sigmoid), 렐루(ReLU), 하이퍼볼릭 탄젠트(Tanh)</li>
</ul>
</li>
</ol>
<h3 id="딥러닝의-특징">딥러닝의 특징</h3>
<ul>
<li><strong>자동 특징 추출</strong>: 데이터에서 유용한 특징을 사람이 일일이 정의하지 않아도 학습 가능</li>
<li><strong>다층 구조</strong>: 은닉층이 많을수록 복잡한 패턴 학습 가능</li>
<li><strong>대규모 데이터 적합</strong>: 빅데이터와 GPU 연산을 통해 효율적 학습</li>
</ul>
<h3 id="딥러닝의-주요-응용-분야">딥러닝의 주요 응용 분야</h3>
<ol>
<li><strong>이미지 인식</strong></li>
</ol>
<ul>
<li>자율주행차의 도로 객체 인식, 의료 영상 진단 등</li>
</ul>
<ol start="2">
<li><strong>음성 인식</strong>
<ul>
<li>음성 비서, 실시간 통역 시스템</li>
</ul>
</li>
<li><strong>자연어 처리</strong>
<ul>
<li>기계 번역, 챗봇, 문서 요약</li>
</ul>
</li>
<li><strong>추천 시스템</strong>
<ul>
<li>전자상거래 맞춤형 상품 추천, 동영상 추천 알고리즘</li>
</ul>
</li>
</ol>
<h3 id="딥러닝의-장점">딥러닝의 장점</h3>
<ul>
<li>복잡하고 고차원적인 데이터에서도 높은 정확도 달성 가능</li>
<li>특징 추출 자동화로 데이터 전처리 부담 감소</li>
<li>다양한 분야에서 기존 방법보다 월등한 성능 발휘</li>
</ul>
<h3 id="딥러닝의-한계">딥러닝의 한계</h3>
<ul>
<li><strong>학습에 많은 데이터 필요</strong>: 충분한 데이터가 없으면 성능 저하</li>
<li><strong>설명력 부족</strong>: 모델의 결정 과정이 불투명, ‘블랙박스’ 문제</li>
<li><strong>연산 비용과 시간 소모</strong>: GPU 및 대규모 연산 자원 필요</li>
<li><strong>과적합 위험</strong>: 학습 데이터에만 최적화되어 일반화 능력 부족</li>
</ul>
<h2 id="자연어-처리nlp">자연어 처리(NLP)</h2>
<p>자연어 처리(NLP, Natural Language Processing)는 <strong>인간이 사용하는 언어, 즉 자연어를 컴퓨터가 이해하고 분석하며 생성할 수 있도록 하는 인공지능 기술 분야</strong>를 의미한다. 이를 통해 기계는 텍스트와 음성 형태의 언어 데이터를 처리하고, 의미를 파악하거나 적절한 응답을 생성할 수 있다. 번역, 질의응답, 챗봇, 문서 요약 등 다양한 응용 사례를 갖고 있으며, 최근 GPT 계열 모델이 큰 성과를 거두고 있다.</p>
<h3 id="자연어-처리의-주요-목표">자연어 처리의 주요 목표</h3>
<ol>
<li>
<p><strong>언어 이해(Language Understanding)</strong></p>
<ul>
<li>입력된 문장의 의미, 문맥, 의도 등을 파악</li>
<li>예: 질문 답변 시스템, 감정 분석</li>
</ul>
</li>
<li>
<p><strong>언어 생성(Language Generation)</strong></p>
<ul>
<li>기계가 사람이 이해할 수 있는 자연스러운 문장을 생성</li>
<li>예: 챗봇 대화, 자동 문서 요약, 기계 번역</li>
</ul>
</li>
</ol>
<h3 id="자연어-처리의-핵심-기술">자연어 처리의 핵심 기술</h3>
<ol>
<li>
<p><strong>형태소 분석(Morphological Analysis)</strong></p>
<ul>
<li>문장을 구성하는 최소 의미 단위(형태소)로 분리</li>
<li>예: “나는 학교에 간다” → [나/는, 학교/에, 가/ㄴ다]</li>
</ul>
</li>
<li>
<p><strong>품사 태깅(Part-of-Speech Tagging)</strong></p>
<ul>
<li>각 단어에 명사, 동사, 형용사 등 품사를 부착</li>
<li>문장의 구조와 의미 분석 기반 마련</li>
</ul>
</li>
<li>
<p><strong>의미 분석(Semantic Analysis)</strong></p>
<ul>
<li>문장이나 단어의 의미를 컴퓨터가 이해하도록 변환</li>
<li>예: “은행” → 금융 기관인지 강가인지 판별</li>
</ul>
</li>
<li>
<p><strong>구문 분석(Syntactic Parsing)</strong></p>
<ul>
<li>문장의 문법적 구조를 분석하여 주어, 목적어, 동사 관계 확인</li>
</ul>
</li>
<li>
<p><strong>텍스트 임베딩(Text Embedding)</strong></p>
<ul>
<li>단어, 문장, 문서 등을 수치 벡터로 변환</li>
<li>기계 학습 모델이 자연어 데이터를 처리할 수 있게 함</li>
</ul>
</li>
<li>
<p><strong>언어 모델(Language Model)</strong></p>
<ul>
<li>문맥과 패턴을 학습하여 다음 단어 예측</li>
<li>GPT, BERT 등 최신 모델이 대표적</li>
</ul>
</li>
</ol>
<h3 id="자연어-처리의-응용-사례">자연어 처리의 응용 사례</h3>
<ul>
<li><strong>기계 번역</strong>: Google 번역, DeepL 등</li>
<li><strong>챗봇 및 대화형 AI</strong>: 고객 상담 자동화, 개인 튜터 AI</li>
<li><strong>텍스트 요약</strong>: 뉴스 기사, 논문, 보고서 자동 요약</li>
<li><strong>감정 분석</strong>: SNS, 리뷰 데이터를 기반으로 긍정·부정 분석</li>
<li><strong>음성 인식 및 음성 합성</strong>: Siri, Alexa, TTS(Text-to-Speech) 시스템</li>
</ul>
<h3 id="자연어-처리의-장점">자연어 처리의 장점</h3>
<ul>
<li>인간과 기계 간 자연스러운 상호작용 가능</li>
<li>방대한 텍스트 데이터를 자동으로 분석하고 활용 가능</li>
<li>번역, 검색, 추천 등 다양한 서비스 고도화 가능</li>
</ul>
<h3 id="자연어-처리의-한계">자연어 처리의 한계</h3>
<ul>
<li><strong>문맥 이해 한계</strong>: 복잡한 문맥이나 중의적 표현 이해 어려움</li>
<li><strong>언어 및 문화 편향 문제</strong>: 특정 언어·문화 데이터에 의존 시 편향 발생</li>
<li><strong>데이터 의존성</strong>: 품질 높은 학습 데이터 없이는 정확도 저하</li>
<li><strong>연산 자원 필요</strong>: 대규모 언어 모델 학습 시 막대한 연산 비용 필요</li>
</ul>
<h2 id="강화학습">강화학습</h2>
<p>강화학습은 보상과 벌점을 기반으로 최적의 행동 전략을 학습하는 기술이다. 알파고의 바둑 대국 승리 사례가 대표적이다.</p>
<p><img src="https://www.devkuma.com/docs/ai/alphago-leesedol.png" alt="알파고와 이세돌의 대국"></p>
<ul>
<li>사례: 알파고와 이세돌의 대국은 강화학습의 실제적 성과를 전 세계에 각인시킨 사건이었다.</li>
</ul>
<p>강화학습은 에이전트(Agent)가 환경과 상호작용하면서 경험을 통해 최적의 행동 전략을 학습하는 인공지능 기술을 의미한다. 에이전트는 현재의 상태(State)를 관찰하고, 가능한 여러 행동(Action) 중 하나를 선택하며, 그 선택에 대한 보상(Reward)이나 벌점(Penalty)을 받는다. 이러한 피드백을 통해 에이전트는 장기적으로 최대 보상을 얻을 수 있는 행동 정책(Policy)을 점진적으로 개선한다. 강화학습의 핵심은 정답을 명시적으로 알려주지 않아도, 반복적인 시행착오를 통해 스스로 최적 전략을 발견한다는 점에 있다. 대표적 사례로는 **알파고(AlphaGo)**가 있다. 알파고는 수많은 바둑 대국 시뮬레이션과 강화학습을 반복하여 바둑의 최적 수를 학습하였으며, 이 과정에서 인간 챔피언인 이세돌을 상대로 승리하는 성과를 이루어냈다. 이를 통해 강화학습이 복잡한 문제 해결에서도 효과적임을 입증하였다.</p>
AI
-
ChatGPT와 생성형 AI
https://www.devkuma.com/docs/ai/chat-gpt/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/chat-gpt/
<h2 id="gpt의-개념">GPT의 개념</h2>
<p>GPT(Generative Pre-trained Transformer)는 사전 학습을 통해 방대한 언어 패턴을 습득한 후, 새로운 문장을 생성할 수 있는 인공지능 모델이다.</p>
<ul>
<li><strong>Generative(생성형)</strong>: 새로운 문장을 생성할 수 있음을 의미한다. 질문에 대한 답변을 산출하거나 글쓰기를 요청받으면 새로운 문장을 만들어낼 수 있다.</li>
<li><strong>Pre-trained(사전 학습된)</strong>: 방대한 데이터로 미리 학습된 모델을 뜻한다. 책, 웹사이트, 뉴스 등 다양한 텍스트를 기반으로 언어의 규칙과 표현을 습득하였다.</li>
<li><strong>Transformer(트랜스포머)</strong>: 문장의 의미를 효과적으로 이해하고 처리하기 위해 설계된 모델 구조의 명칭이다. 이는 구글에서 개발한 기술이다.</li>
</ul>
<h3 id="작동-방식">작동 방식</h3>
<ol>
<li>
<p><strong>사전 학습(Pre-training)</strong>
GPT는 인터넷에 존재하는 방대한 텍스트 데이터를 학습하여 언어의 패턴, 문법, 의미 등을 습득한다. 예를 들어, “하늘이 파란 이유는"이라는 문장이 주어지면, 그 뒤에 “햇빛이 산란되기 때문이다"라는 서술이 자주 등장한다는 사실을 학습한다.</p>
</li>
<li>
<p><strong>추론(Inference)</strong>
사전 학습이 완료된 GPT는 사용자로부터 질문을 입력받으면, 그에 적합하고 자연스러운 답변을 생성한다.</p>
</li>
<li>
<p><strong>문맥 이해(Context Understanding)</strong>
GPT는 이전 문맥을 참조하여 상황에 맞는 응답을 하려고 한다. 예컨대, 대화 중 앞에서 “봄"에 관한 언급이 있었다면, 꽃이나 날씨와 같은 주제를 더 잘 연상하여 활용할 수 있다.</p>
</li>
</ol>
<h3 id="gpt의-장점">GPT의 장점</h3>
<ul>
<li>
<p><strong>다양한 언어 작업 수행 가능</strong>
질문 응답, 글쓰기, 번역, 요약, 문법 교정 등 여러 언어 처리 작업을 수행할 수 있다.</p>
</li>
<li>
<p><strong>자연스러운 표현 생성</strong>
GPT 기반 챗봇은 대화체 문장을 자연스럽게 산출할 수 있어, 사람과 대화하는 듯한 경험을 제공한다.</p>
</li>
<li>
<p><strong>신속한 반응</strong>
원하는 정보를 수 초 내에 생성하여 제공한다.</p>
</li>
</ul>
<h2 id="chatgpt의-등장">ChatGPT의 등장</h2>
<p>ChatGPT는 OpenAI가 개발한 <strong>대규모 언어 모델(LLM, Large Language Model)</strong> 기반의 대화형 인공지능 서비스이다. GPT(Generative Pre-trained Transformer) 기술을 바탕으로 인간과 유사한 자연스러운 대화를 수행하며, 질의응답, 문서 작성, 번역, 요약 등 다양한 언어 처리 작업을 지원한다. 자연스러운 문맥 이해와 다언어 지원 능력이 강점이다.</p>
<p><a href="https://chatgpt.com/" target="_blank" rel="noopener">https://chatgpt.com/<i class="fas fa-external-link-alt"></i></a></p>
<h3 id="명칭의-의미">명칭의 의미</h3>
<ul>
<li><strong>Chat</strong>: 사용자와의 대화 인터페이스를 의미한다.</li>
<li><strong>GPT</strong>: 문장 생성 능력을 갖추고 사전 학습된 트랜스포머 아키텍처 기반 모델이다.</li>
</ul>
<p>“GPT 기술을 기반으로 한 대화형 서비스"이라는 의미를 내포한다.</p>
<h3 id="특징-및-기능">특징 및 기능</h3>
<ul>
<li>다양한 활용성
<ul>
<li>질문 응답, 문장 작성, 번역, 요약, 프로그램 작성(코드 지원) 등이 가능하다.</li>
</ul>
</li>
<li>자연스러운 대화 능력
<ul>
<li>사람과 대화하는 듯한 자연스러운 응답을 생성할 수 있다.</li>
</ul>
</li>
<li>다언어 대응</li>
<li>한글을 포함한 다양한 언어로 의사소통이 가능하다.</li>
<li>기술적 유연성
<ul>
<li>다양한 형식의 출력—텍스트, 코드, 표, JSON 등—생성 가능하며, 노코드 환경에서도 활용할 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="주요-특징">주요 특징</h2>
<ul>
<li><strong>다양한 언어 작업 수행 가능</strong>
<ul>
<li>질문 응답, 글쓰기, 번역, 요약, 문법 교정, 코드 작성 등 폭넓은 활용이 가능하다.</li>
</ul>
</li>
<li><strong>자연스러운 대화 생성</strong>
<ul>
<li>사람과 대화하는 듯한 자연스러운 언어 표현을 산출할 수 있다.</li>
</ul>
</li>
<li><strong>다언어 지원</strong>
<ul>
<li>한국어를 포함한 다수의 언어로 의사소통이 가능하다.</li>
</ul>
</li>
<li><strong>출력 형식의 유연성</strong>
<ul>
<li>텍스트뿐 아니라 코드, 표, JSON 등 다양한 형식으로 결과를 제공할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="기술적-기반">기술적 기반</h3>
<ul>
<li><strong>사전 학습(Pre-training)</strong>: 웹, 도서, 논문, 소스 코드 등 방대한 텍스트 데이터를 기반으로 언어의 구조와 의미를 학습하였다.</li>
<li><strong>Transformer 아키텍처</strong>: 문맥 이해와 문장 생성을 위한 핵심 기술로, Self-Attention 메커니즘을 활용하여 단어 간 관계를 효과적으로 파악한다.</li>
</ul>
<h2 id="활용-사례">활용 사례</h2>
<p>ChatGPT의 활용 범위는 매우 다양하며, 여러 산업과 분야에서 실제로 적용되고 있다. 대표적인 활용 사례를 살펴보면 다음과 같다.</p>
<ol>
<li>
<p><strong>고객 서비스 자동화</strong></p>
<ul>
<li>기업의 고객 지원 부서에서는 ChatGPT를 활용하여 24시간 온라인 상담 서비스를 제공하고 있다. 이를 통해 고객의 기본적인 문의를 신속히 처리할 수 있으며, 상담원이 복잡한 문제에 집중할 수 있도록 지원한다.</li>
</ul>
</li>
<li>
<p><strong>교육 및 학습 지원</strong></p>
<ul>
<li>교육 현장에서 ChatGPT는 학생 개개인의 학습 속도와 수준에 맞춘 맞춤형 학습을 제공한다. 학생이 질문을 입력하면 AI가 이해하기 쉬운 답변을 제공하고, 추가 학습 자료를 추천함으로써 학습 효율을 높인다.</li>
</ul>
</li>
<li>
<p><strong>프로그래밍 코드 보조</strong></p>
<ul>
<li>프로그래머들은 ChatGPT를 활용하여 코드 작성 및 디버깅을 보조받을 수 있다. 반복적인 코드 작성 작업이나 함수 템플릿 생성, 오류 검토 등을 자동화함으로써 개발 속도를 향상시키고, 생산성을 높인다.</li>
</ul>
</li>
<li>
<p><strong>콘텐츠 생성 및 요약</strong></p>
<ul>
<li>ChatGPT는 기사 요약, 광고 문구 작성, 보고서 작성 등 다양한 콘텐츠 생성 업무를 수행할 수 있다. 또한, 방대한 문서를 요약하여 핵심 정보를 빠르게 전달함으로써 정보 처리 효율을 높인다.</li>
</ul>
</li>
</ol>
<h2 id="한계와-과제">한계와 과제</h2>
<p>ChatGPT의 활용에는 분명한 장점이 존재하지만, 동시에 다음과 같은 한계와 과제가 있다.</p>
<ol>
<li><strong>사실 오류 발생 가능성</strong></li>
</ol>
<ul>
<li>ChatGPT는 학습된 데이터에 기반하여 답변을 생성하므로, 때때로 잘못된 정보나 오류가 포함될 수 있다. 따라서 중요한 의사결정에 활용할 때는 추가 검증이 필요하다.</li>
</ul>
<ol start="2">
<li>
<p><strong>최신 정보 반영의 한계</strong></p>
<ul>
<li>모델이 학습된 시점 이후의 최신 정보나 사건은 반영되지 않을 수 있다. 실시간 정보가 필요한 분야에서는 보조적 도구로 활용하는 것이 적절하다.</li>
<li>다만, 최신 GPT 모델은 인터넷 검색 기능과 연동되어 이를 보완하기도 한다.</li>
</ul>
</li>
<li>
<p><strong>데이터 사용과 저작권 문제</strong></p>
<ul>
<li>ChatGPT가 학습한 데이터에는 저작권이 존재할 수 있으며, 생성된 콘텐츠 또한 지적 재산권 문제와 연결될 수 있다. 따라서 상업적 활용 시 법적 문제에 주의해야 한다.</li>
</ul>
</li>
<li>
<p><strong>예측 기반의 작동 원리</strong></p>
</li>
</ol>
<ul>
<li>GPT는 단어의 다음 위치를 확률적으로 예측하는 방식으로 작동한다. 따라서 인간과 같은 심층적 이해나 감정을 지니는 것은 아니며, 의미를 “이해"한다기보다는 패턴을 “예측"한다고 보는 것이 타당하다.</li>
</ul>
AI
ChatGPT
-
인공지능의 응용 분야
https://www.devkuma.com/docs/ai/applications/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/applications/
<h2 id="의료">의료</h2>
<p>의료 영상 분석, 신약 개발, 환자 맞춤형 치료</p>
<p><img src="https://www.devkuma.com/docs/ai/eye.png" alt="망막 이미지"></p>
<ul>
<li>사례: 구글의 딥마인드(DeepMind)가 개발한 AI는 안과 질환을 조기 발견하는 데 활용되고 있다.</li>
<li>이미지 출처: <a href="https://www.irobotnews.com/news/articleView.html?idxno=8041" target="_blank" rel="noopener">로봇신문" 구글 딥마인드, 인공지능 활용해 안과질환 조기 진단<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h3 id="의료-ai의-실제-사례">의료 AI의 실제 사례</h3>
<ul>
<li>영상 진단: AI가 엑스레이와 MRI 이미지를 판독해 초기 암을 조기 발견. 미국의 한 병원에서는 판독 오류율이 20% 감소.</li>
<li>신약 개발: 기존에는 10년 이상 걸리던 후보 물질 탐색을 AI 기반으로 수개월 내 단축.</li>
<li>맞춤형 치료: 환자의 유전자 정보를 분석해 가장 효과적인 항암제를 추천.</li>
</ul>
<p><img src="https://www.devkuma.com/docs/ai/new-drug-development.png" alt="망막 이미지"></p>
<ul>
<li>그림 3: AI 활용 전통적 신약 개발 프로세스 vs AI 기반 프로세스 비교</li>
</ul>
<h2 id="자율주행">자율주행</h2>
<p>자율주행 기술은 도로 상황을 실시간으로 인식하고, 이를 기반으로 최적의 주행 전략을 결정하며 교통 흐름을 효율적으로 관리하는 것을 목표로 한다. 카메라, 레이더, 라이다(LiDAR) 등의 센서를 통해 주변 환경을 탐지하고, AI 알고리즘이 이를 분석하여 차량의 속도, 방향, 차간 거리 등을 자동으로 조절한다. 이를 통해 교통사고를 줄이고, 도로 효율성을 높이며, 운전자와 보행자의 안전을 강화할 수 있다.</p>
<h2 id="금융">금융</h2>
<p>금융 분야에서 AI는 사기 거래 탐지, 투자 전략 자동화, 리스크 관리 등 다양한 영역에 적용되고 있다. 사기 탐지에서는 비정상적인 거래 패턴을 실시간으로 분석하여 의심 거래를 식별하며, 투자 전략 자동화에서는 시장 데이터를 기반으로 포트폴리오 최적화를 수행한다. 또한 AI는 금융 기관이 신용 위험, 시장 위험 등을 예측하고 관리하는 데 활용되어 금융 안정성과 운영 효율성을 동시에 높인다.</p>
<h2 id="제조업">제조업</h2>
<p>제조업에서는 스마트 공장 구현, 예지 보수, 품질 관리 자동화 등에 AI가 적용된다. 스마트 공장은 생산 설비와 물류 시스템을 AI로 연결하여 효율적인 공정 관리를 가능하게 한다. 예지 보수(Predictive Maintenance)는 센서 데이터를 분석하여 장비 고장을 사전에 예측하고, 유지보수 계획을 최적화함으로써 비용과 시간을 절감한다. 품질 관리 자동화는 생산 과정에서 발생할 수 있는 결함을 실시간으로 감지하여 제품 품질을 향상시킨다.</p>
<h2 id="교육">교육</h2>
<p>교육 분야에서는 AI를 활용하여 맞춤형 학습, 자동 채점, 지능형 튜터링 시스템 등을 제공한다. 학생 개개인의 학습 수준과 속도를 분석하여 개인화된 학습 콘텐츠를 추천하고, 과제와 시험을 자동으로 채점하여 교사의 업무 부담을 경감시킨다. 또한 지능형 튜터링 시스템은 학생의 학습 패턴을 분석하여 취약한 영역을 보완하고, 실시간 피드백을 제공함으로써 학습 효과를 극대화한다.</p>
AI
-
윤리와 미래 전망
https://www.devkuma.com/docs/ai/ethics-and-future-prospects/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/ethics-and-future-prospects/
<h2 id="윤리적-고려-사항">윤리적 고려 사항</h2>
<p>인공지능 기술의 발전과 확산은 다양한 윤리적 문제를 동반한다. 주요 고려 사항은 다음과 같다.</p>
<ol>
<li>
<p><strong>개인정보 보호</strong></p>
<ul>
<li>AI 시스템은 대량의 데이터에 의존하여 학습하고 작동한다. 이 과정에서 개인의 민감한 정보가 수집, 처리될 수 있으며, 정보 유출이나 오남용 위험이 존재한다. 따라서 AI 개발과 활용 시에는 개인정보 보호법 준수와 안전한 데이터 관리가 필수적이다.</li>
</ul>
</li>
<li>
<p><strong>알고리즘 편향</strong></p>
<ul>
<li>AI 모델은 학습 데이터에 포함된 편향을 그대로 학습할 수 있다. 이는 특정 집단이나 개인에 대한 불공정한 판단, 차별적 결과를 초래할 수 있으며, 사회적 신뢰를 저해할 수 있다. 이를 방지하기 위해서는 데이터 편향성을 검토하고, 공정성을 고려한 알고리즘 설계가 필요하다.</li>
</ul>
</li>
<li>
<p><strong>인간 노동 대체 문제</strong></p>
<ul>
<li>AI의 자동화 능력은 생산성과 효율성을 높이는 동시에 일부 직업의 역할을 대체할 수 있다. 이는 고용 구조 변화와 사회적 불평등 문제를 야기할 수 있으며, 이에 대한 정책적 대비와 재교육 프로그램 마련이 중요하다.</li>
</ul>
</li>
<li>
<p><strong>저작권 문제</strong></p>
<ul>
<li>AI는 콘테츠를 학습하고 생성하기 때문에 누군가의 콘텐츠 권리와 충돌할 가능성 있다. 이는 저작물을 저작권자의 허락 없이 복제하거나 무단으로 변경하면 저작권 침해가 될 수 있다.</li>
</ul>
</li>
</ol>
<h3 id="지브리-스타일은-저작권-문제가-될까">지브리 스타일은 저작권 문제가 될까?</h3>
<p>구체적인 <strong>표현</strong>은 보호되지만, 추상적인 <strong>아이디어</strong>는 보호되지 않는다.</p>
<ul>
<li>사상과 감정의 창적표현
<ul>
<li>예: 케릭터, 영화의 장면</li>
<li>저작권에 보호됨</li>
</ul>
</li>
<li>아이디어 기법, 방법
<ul>
<li>예: 작풍, 터치</li>
<li>저작권에 보호되지 않음</li>
</ul>
</li>
</ul>
<p>그러면, “지브리 스타일"은 인터넷에 올려도 되는가?
법적으로는 문제가 없더라도 AI 윤리의 괌점에서는 적절한 이용이라고 할 수 없다.</p>
<h2 id="인공지능의-미래">인공지능의 미래</h2>
<p>인공지능은 단순한 도구를 넘어 인간과 협력하며 지능적 결정을 지원하는 동반자로 진화할 것으로 전망된다. 미래 사회에서 AI는 의료, 교육, 산업 등 다양한 분야에서 인간의 역량을 보조하고 효율성을 극대화하는 역할을 수행할 것이다. 그러나 기술 발전의 속도가 매우 빠른 만큼, 사회적·법적 제도와 규범의 정비가 병행되지 않으면, 개인정보 침해, 불공정한 판단, 고용 불안 등 다양한 문제를 야기할 수 있다. 따라서 AI의 잠재적 이점을 최대화하고 부작용을 최소화하기 위해서는 기술적 혁신과 함께 윤리적, 법적, 사회적 준비가 필수적이다.</p>
AI
ChatGPT
-
맺음말
https://www.devkuma.com/docs/ai/conclusion/
Sat, 16 Aug 2025 22:33:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/ai/conclusion/
<h2 id="맺음말">맺음말</h2>
<p>인공지능은 이미 우리 사회 전반에 걸쳐 필수적인 기술로 자리잡았다. 본 도서를 통해 독자들이 AI의 개념과 원리를 올바르게 이해하고, 각자의 분야에서 효과적으로 활용할 수 있기를 기대한다.</p>
<h2 id="인공지능-학습">인공지능 학습</h2>
<ul>
<li><a href="https://www.linkedin.com/learning/what-is-generative-ai/generative-ai-is-a-tool-in-service-of-humanity" target="_blank" rel="noopener">LinkedIn Learning | Generative AI is a tool in service of humanity<i class="fas fa-external-link-alt"></i></a>
<ul>
<li>생성형 AI 기초에 대해 학습 과정</li>
</ul>
</li>
<li><a href="https://www.linkedin.com/learning/paths/applying-generative-ai-as-a-creative-professional" target="_blank" rel="noopener">LinkedIn Learning | Applying Generative AI as a Creative Professional<i class="fas fa-external-link-alt"></i></a>
<ul>
<li>창작자를 위한 생성형 AI 과정</li>
</ul>
</li>
<li><a href="https://www.gseek.kr/user/popular/popularTheme/course?p_prgrm_group_sn=18" target="_blank" rel="noopener">경기도평생학습포털 | 생성한 AI 강좌<i class="fas fa-external-link-alt"></i></a>
<ul>
<li>생성형 AI 강좌</li>
</ul>
</li>
</ul>
AI
ChatGPT
-
Kotlin 문자열 템플릿(string template)
https://www.devkuma.com/docs/kotlin/string-template/
Wed, 18 Sep 2024 14:10:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/string-template/
<p>문자열 리터럴 안에서 <code>"$변수명"</code>이나 <code>"${표현식}"</code>과 같이 작성하면 변수의 값이나 식의 평가 결과를 문자열로 확장할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">name</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#4e9a06">"devkuma"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Hello, </span><span style="color:#4e9a06">$name</span><span style="color:#4e9a06">!"</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Hello, devkuma!
</span></span></code></pre></div><p>이런 구조를 문자열 템플릿(string template)이라고 한다. <code>"${표현식}"</code>을 사용하는 방법을 이용하면 문자열 템플릿 안에서 함수 호출 등을 할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"You are </span><span style="color:#4e9a06">${p.age}</span><span style="color:#4e9a06"> years old."</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// getAge()의 호출
</span></span></span></code></pre></div><p>다만, 너무 복잡한 수식을 쓰려고 하면 가독성이 떨어지니 적당히 사용해야 한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">args</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">Array</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">String</span><span style="color:#000;font-weight:bold">>)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Hi, </span><span style="color:#4e9a06">${if (args.isEmpty()) "anonymous" else args[0]}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div>
Kotlin
-
Kotlin 문자열
https://www.devkuma.com/docs/kotlin/chapter/string/
Wed, 18 Sep 2024 14:10:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/chapter/string/
Kotlin
-
Kotlin 목록, 배열 길이 구하기
https://www.devkuma.com/docs/kotlin/list-array-length/
Sun, 15 Sep 2024 11:26:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/list-array-length/
<h2 id="size를-이용한-방법">size를 이용한 방법</h2>
<p>목록, 배열에서는 <code>size</code>는 변수를 제공하며, 길이 값을 저장되어 있다.</p>
<p>아래와 같이 <code>size</code> 변수로 목록, 배열의 길이를 가져올 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array size: </span><span style="color:#4e9a06">${array.size}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">list</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"a"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"b"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"c"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"d"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"e"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"list size: </span><span style="color:#4e9a06">${list.size}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array size: 5
</span></span><span style="display:flex;"><span>list size: 5
</span></span></code></pre></div><h2 id="count를-이용한-방법">count()를 이용한 방법</h2>
<p>목록, 배열에서는 <code>count()</code> 함수를 제공하며, 요소들의 개수를 반환한다.</p>
<p>아래와 같이 <code>count()</code>를 사용하여 목록, 배열의 길이를 가져올 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array count: </span><span style="color:#4e9a06">${array.count()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">list</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"a"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"b"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"c"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"d"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"e"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"list count: </span><span style="color:#4e9a06">${list.count()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array count: 5
</span></span><span style="display:flex;"><span>list count: 5
</span></span></code></pre></div>
Kotlin
-
Kotlin 목록(List) 선언, 초기화
https://www.devkuma.com/docs/kotlin/list/
Sun, 15 Sep 2024 00:54:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/list/
<h2 id="목록list">목록(List)</h2>
<p><code>listOf()</code>으로 목록을 생성한다.
배열 개수 고정인데 비해 목록은 <code>.add()</code> 및 <code>.remove()</code>으로 추가 삭제할 수 있다.
그러나 배열은 <code>nums[1] = 123</code>으로 값을 변경할 수 있지만, 목록은 <code>nums[1] = 222</code>으로 값을 변경할 수 없다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">nums</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">cols</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Red"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Green"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Blue"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">n</span> <span style="color:#204a87;font-weight:bold">in</span> <span style="color:#000">nums</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">n</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span> <span style="color:#204a87;font-weight:bold">in</span> <span style="color:#000">cols</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>1
</span></span><span style="display:flex;"><span>2
</span></span><span style="display:flex;"><span>3
</span></span><span style="display:flex;"><span>Red
</span></span><span style="display:flex;"><span>Green
</span></span><span style="display:flex;"><span>Blue
</span></span></code></pre></div><h2 id="주요-함수">주요 함수</h2>
<h3 id="filter-함수">filter 함수</h3>
<p>아래와 같이 목록에 필터를 걸어 짝수인 조건에 일치하는 요소를 추출할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">nums</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">listOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">6</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">9</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">nums</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">it</span> <span style="color:#000;font-weight:bold">%</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#000;font-weight:bold">})</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>[2, 4, 6, 8, 10]
</span></span></code></pre></div>
Kotlin
-
Kotlin 배열(Array) 선언, 초기화, 주요 함수
https://www.devkuma.com/docs/kotlin/array/
Sun, 15 Sep 2024 00:11:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/array/
<h2 id="배열array">배열(Array)</h2>
<p><code>arrayOf()</code>으로 배열을 생성한다. 배열의 개수는 고정이긴 하지만 성능이 우수하다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">nums</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">cols</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Red"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Green"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Blue"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">n</span> <span style="color:#204a87;font-weight:bold">in</span> <span style="color:#000">nums</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">n</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span> <span style="color:#204a87;font-weight:bold">in</span> <span style="color:#000">cols</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>1
</span></span><span style="display:flex;"><span>2
</span></span><span style="display:flex;"><span>3
</span></span><span style="display:flex;"><span>Red
</span></span><span style="display:flex;"><span>Green
</span></span><span style="display:flex;"><span>Blue
</span></span></code></pre></div><p>아래와 같이 변수 선언시에 <code>Array<Int></code> 타입을 입력하면, 뒤 부분의 타입은 <code><></code>으로 생략할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">Array</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Int</span><span style="color:#000;font-weight:bold">></span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">emptyArray</span><span style="color:#000;font-weight:bold"><>()</span>
</span></span></code></pre></div><p>대괄호 <code>[...]</code>로 배열의 요소에 접근할 수 있으며, 배열의 인덱스는 0부터 시작한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">nums</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">])</span>
</span></span><span style="display:flex;"><span><span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">cols</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">])</span>
</span></span></code></pre></div><h3 id="빈empty-배열-선언">빈(Empty) 배열 선언</h3>
<p>아래 함수로 특정 타입의 빈 배열을 선언할 수 있다.</p>
<pre tabindex="0"><code>val array1 = emptyArray<Int>()
val array2 = arrayOf<Int>()
``
```kotlin
fun main() {
val array1 = emptyArray<Int>()
println("array1: ${array1.contentToString()}")
val array2 = arrayOf<Int>()
println("array2: ${array2.contentToString()}")
}
</code></pre><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array1: []
</span></span><span style="display:flex;"><span>array2: []
</span></span></code></pre></div><h2 id="배열-선언-시-초기-값-설정">배열 선언 시, 초기 값 설정</h2>
<p><code>arrayOf<Type>(values)</code>로 배열에 초기 값을 설정할 수 있다.
이때, <code>(values)</code>로 타입을 유추할 수 있기 때문에 <code><Type></code>은 생략할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array1</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Int</span><span style="color:#000;font-weight:bold">>(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array1: </span><span style="color:#4e9a06">${array1.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array2</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Red"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Green"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Blue"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"arr: </span><span style="color:#4e9a06">${array2.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array1: [1, 2, 3, 4]
</span></span><span style="display:flex;"><span>array2: [Red, Green, Blue]
</span></span></code></pre></div><h2 id="동일한-값으로-배열-초기화">동일한 값으로 배열 초기화</h2>
<p><code>Array(size) { value }</code>는 배열을 <code>size</code>로 설정한 크기 만큼 생성하며, 요소들을 모두 설정한 <code>value</code>로 할당한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Array</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#0000cf;font-weight:bold">20</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array: </span><span style="color:#4e9a06">${array.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array: [20, 20, 20]2
</span></span></code></pre></div><h2 id="연속된-숫자로-배열-초기화">연속된 숫자로 배열 초기화</h2>
<p><code>Array(size) { lambda }</code>는 배열을 <code>size</code>로 설정한 크기 만큼 생성하며, 요소들을 모두 <code>lambda</code>의 반환 값으로 설정된다.</p>
<p><code>lambda</code>에 전달되는 <code>it</code>는 요소의 <code>index</code> 값이 되며, 아래와 같이 연속된 숫자로 배열을 초기화할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array1</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Array</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">it</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#204a87;font-weight:bold">it</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array1: </span><span style="color:#4e9a06">${array1.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array2</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Array</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">it</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#204a87;font-weight:bold">it</span> <span style="color:#000;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">10</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array2: </span><span style="color:#4e9a06">${array2.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array1: [0, 1, 2, 3, 4]
</span></span><span style="display:flex;"><span>array2: [10, 11, 12, 13, 14]
</span></span></code></pre></div><h2 id="null로-배열-초기화">Null로 배열 초기화</h2>
<p><code>arrayOfNulls<Type>(size)</code>로 <code>null</code>로 가득찬 배열을 생성할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOfNulls</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Int</span><span style="color:#000;font-weight:bold">>(</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"array: </span><span style="color:#4e9a06">${array.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>array: [null, null, null, null, null]
</span></span></code></pre></div><h2 id="기본-자료형-배열-선언">기본 자료형 배열 선언</h2>
<p>kotlin에는 <code>IntArray</code>, <code>BooleanArray</code>, <code>DoubleArray</code>, <code>LongArray</code> 등, 기본 자료형 배열을 제공한다.</p>
<p>이런 배열을 생성할 때는 <code>intArrayOf()</code>, <code>booleanArrayOf()</code> 등을 사용하여 생성할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">iniArray</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">IntArray</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">intArrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"iniArray: </span><span style="color:#4e9a06">${iniArray.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">booleanArray</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">BooleanArray</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">booleanArrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"booleanArray: </span><span style="color:#4e9a06">${booleanArray.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">doubleArray</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">DoubleArray</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">doubleArrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1.2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2.3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3.4</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"doubleArray: </span><span style="color:#4e9a06">${doubleArray.contentToString()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>iniArray: [1, 2, 3]
</span></span><span style="display:flex;"><span>booleanArray: [true, true, false]
</span></span><span style="display:flex;"><span>doubleArray: [1.2, 2.3, 3.4]
</span></span></code></pre></div><h2 id="주요-함수">주요 함수</h2>
<h3 id="foreach-함수">forEach 함수</h3>
<p><code>forEach</code> 함수는 목록에 반복 처리를 할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// lambda으로 사용한 방법이다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">element</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">$element</span><span style="color:#4e9a06">,"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">})</span> <span style="color:#8f5902;font-style:italic">// => 1,2,3,4,5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 괄호'()'은 생략할 수 있다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">element</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">$element</span><span style="color:#4e9a06">,"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// it으로 람다를 대신하여 객체를 받아올 수 있다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">$it</span><span style="color:#4e9a06">,"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 1,2,3,4,5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>1,2,3,4,5,
</span></span><span style="display:flex;"><span>1,2,3,4,5,
</span></span><span style="display:flex;"><span>1,2,3,4,5,
</span></span></code></pre></div><ul>
<li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/for-each.html" target="_blank" rel="noopener">forEach에 대한 kotlin 공식 문서<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h3 id="filter-함수">filter 함수</h3>
<p><code>filter</code> 함수는 목록에 조건을 걸어 그 조건에 맞는 새로운 목록을 반환한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#8f5902;font-style:italic">// => 1,2,3,4,5 배열을 생성한다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// filter의 조건에 충족되는 함수를 반환한다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">e</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000">e</span> <span style="color:#000;font-weight:bold">%</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#000;font-weight:bold">}).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 2,4
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 괄호'()'은 생략할 수 있다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">e</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000">e</span> <span style="color:#000;font-weight:bold">%</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#000;font-weight:bold">}.</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 2,4
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// lambda 사용하지 않고도 'it'으로 객체를 받아 올 수 이 있다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">it</span> <span style="color:#000;font-weight:bold">%</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#000;font-weight:bold">}.</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 2,4
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>2,4,
</span></span><span style="display:flex;"><span>2,4,
</span></span><span style="display:flex;"><span>2,4,
</span></span></code></pre></div><ul>
<li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter.html" target="_blank" rel="noopener">filter 함수에 대한 kotlin 공식 문서<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h3 id="map-함수">map 함수</h3>
<p><code>map</code> 함수는 목록의 요소를 변경하여 받아올 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// 이름과 연령을 갖는 클래스 정의
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">private</span> <span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">Person</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">name</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">String</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">age</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">Int</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">Person</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Charlie"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000;font-weight:bold">),</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">Person</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">31</span><span style="color:#000;font-weight:bold">),</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">Person</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"kimkc"</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">42</span><span style="color:#000;font-weight:bold">),</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// lambda으로 user 인스턴스를 받아 올수 있다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">user</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span> <span style="color:#000;font-weight:bold">}).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">${it}</span><span style="color:#4e9a06">,"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 괄호'()'은 생략할 수 있다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">user</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span> <span style="color:#000;font-weight:bold">}.</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">${it}</span><span style="color:#4e9a06">,"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 'it'으로 객체를 받아 올 수 있다(이 'it'는 user 인스턴스를 가르킨다).
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span> <span style="color:#000;font-weight:bold">}.</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"</span><span style="color:#4e9a06">${it}</span><span style="color:#4e9a06">,"</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Charlie,devkuma,kimkc,
</span></span><span style="display:flex;"><span>Charlie,devkuma,kimkc,
</span></span><span style="display:flex;"><span>Charlie,devkuma,kimkc,
</span></span></code></pre></div><ul>
<li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html" target="_blank" rel="noopener">map 함수에 대한 kotlin 공식 문서<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h3 id="sum-함수">sum 함수</h3>
<p><code>sum</code> 함수는 입력한 숫자를 합산한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Array<Int> 인스턴스 생성한다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 합산한다(1 + 2 + 3 + 4 + 5).
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sum</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#8f5902;font-style:italic">// => 15
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>15
</span></span></code></pre></div><ul>
<li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/take.html" target="_blank" rel="noopener">sum 함수에 대한 kotlin 공식 문서<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h3 id="take-함수">take 함수</h3>
<p><code>take</code> 함수는 목록의 요소를 입력한 숫자 만큼만 가져온다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">take</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 1
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">take</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 1, 2
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">take</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 1, 2, 3
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">take</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 1, 2, 3, 4
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">take</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#8f5902;font-style:italic">// => 1, 2, 3, 4, 5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>1
</span></span><span style="display:flex;"><span>12
</span></span><span style="display:flex;"><span>123
</span></span><span style="display:flex;"><span>1234
</span></span><span style="display:flex;"><span>12345
</span></span></code></pre></div><ul>
<li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/take.html" target="_blank" rel="noopener">take 함수에 대한 kotlin 공식 문서<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<h3 id="drop-함수">drop 함수</h3>
<p><code>drop</code> 함수는 목록의 요소를 앞에서 부터 입력한 숫자 만큼 제거하고 가져온다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">drop</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">{</span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)}</span> <span style="color:#8f5902;font-style:italic">// => 2, 3, 4, 5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">drop</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">{</span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)}</span> <span style="color:#8f5902;font-style:italic">// => 3, 4, 5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">drop</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">{</span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)}</span> <span style="color:#8f5902;font-style:italic">// => 4, 5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">drop</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">{</span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)}</span> <span style="color:#8f5902;font-style:italic">// => 5
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">drop</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">{</span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">it</span><span style="color:#000;font-weight:bold">)}</span> <span style="color:#8f5902;font-style:italic">// => 빈 Array
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>2345
</span></span><span style="display:flex;"><span>345
</span></span><span style="display:flex;"><span>45
</span></span><span style="display:flex;"><span>5
</span></span></code></pre></div><ul>
<li><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/drop.html" target="_blank" rel="noopener">drop 함수에 대한 kotlin 공식 문서<i class="fas fa-external-link-alt"></i></a></li>
</ul>
Kotlin
-
Kotlin 배열(Array)에서 최소값, 최대값 찾기
https://www.devkuma.com/docs/kotlin/array-get-min-max/
Sat, 14 Sep 2024 23:40:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotlin/array-get-min-max/
<h2 id="minornull-minornull를-이용한-방법">minOrNull(), minOrNull()를 이용한 방법</h2>
<p><code>Array</code>에서 제공하는 <code>minOrNull()</code>, <code>maxOrNull()</code> 함수는 배열의 최소값, 최대값을 반환한다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">66</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">34</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">70</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">42</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"min: </span><span style="color:#4e9a06">${array.minOrNull()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"max: </span><span style="color:#4e9a06">${array.maxOrNull()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>min: 10
</span></span><span style="display:flex;"><span>max: 70
</span></span></code></pre></div><div class="alert alert-primary" role="alert"><div class="h4 alert-heading" role="heading">Tip</div>
기존에 `Array`에서 제공하는 `min()`, `max()` 함수는 1.4 버전 이후로 deprecated 되었다.
</div>
<h2 id="정렬을-이용한-방법">정렬을 이용한 방법</h2>
<p><code>Array.sorted()</code> 함수는 오름차순으로 정렬된 배열을 반환한다.</p>
<p>오름차순으로 정렬되었기 때문에, Index의 첫번째 요소는 최소값이 되고, 마지막 요소는 최대값이 된다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">66</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">34</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">70</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">42</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">sortedArray</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sorted</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"min: </span><span style="color:#4e9a06">${sortedArray.first()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"max: </span><span style="color:#4e9a06">${sortedArray.last()}</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<pre tabindex="0"><code>min: 10
max: 70
</code></pre><h2 id="반복문을-이용한-방법">반복문을 이용한 방법</h2>
<p>for문으로 배열의 모든 요소를 반복하면서, 최소값과 최대값을 찾을 수도 있다.</p>
<p>아래와 같이 <code>min</code> 변수의 초기 값을 <code>Int</code>의 최대값, <code>max</code> 변수의 초기 값을 <code>Int</code>의 최소값으로 할당하고 for문으로 반복하면서 값을 찾는다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">main</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">array</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">arrayOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">66</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">34</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">70</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">42</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">max</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Int</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">MIN_VALUE</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">min</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">Int</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">MAX_VALUE</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">i</span> <span style="color:#204a87;font-weight:bold">in</span> <span style="color:#000">array</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">min</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">min</span> <span style="color:#000;font-weight:bold">></span> <span style="color:#000">i</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">i</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000">min</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">max</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">max</span> <span style="color:#000;font-weight:bold"><</span> <span style="color:#000">i</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">i</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000">max</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"min: </span><span style="color:#4e9a06">$min</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"max: </span><span style="color:#4e9a06">$max</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><p>Output:</p>
<pre tabindex="0"><code>min: 10
max: 70
</code></pre>
Kotlin
-
개발 문서 사이트
https://www.devkuma.com/docs/sites/developments/
Sun, 01 Sep 2024 10:28:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/sites/developments/
<h2 id="프로그래밍-시작">프로그래밍 시작</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://opentutorials.org/course/1" target="_blank" rel="noopener">생활코딩 / 오픈튜토리얼스<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
기초 프로그래밍 입문 강좌 사이트</h6>
<p class="card-text">
<p>무료 프로그래밍 학습 과정</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.w3schools.com" target="_blank" rel="noopener">W3schools<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
최대 WEB 프로그래밍 시작 지침</h6>
<p class="card-text">
<p>사용자 수가 많은 무료 WEB 개발 학습 사이트</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.tutorialspoint.com/tutorialslibrary.htm" target="_blank" rel="noopener">tutorialspoint<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
기초 프로그래밍 입문 강좌 사이트</h6>
<p class="card-text">
<p>무료 주제 프로그래밍 강좌가 많이 포함되어 있다.</p>
</p>
</div>
</div>
</div>
<h2 id="공식-기술-문서">공식 기술 문서</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.php.net/docs.php" target="_blank" rel="noopener">php<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
php 공식 문서</h6>
<p class="card-text">
<p>PHP 자습서 및 API 문서를 제공한다.</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.php.net/docs.php" target="_blank" rel="noopener">GO<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
GO 공식 문서</h6>
<p class="card-text">
<p>GO 공식 자습서 및 API 문서</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="모바일">모바일</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.swift.org/documentation/" target="_blank" rel="noopener">Swift<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
Swift 공식 문서</h6>
<p class="card-text">
<p>Apple Swift 공식 자습서 및 API 문서</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="java-기술-문서">Java 기술 문서</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://howtodoinjava.com/" target="_blank" rel="noopener">HowToDoInJava<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.javatpoint.com/" target="_blank" rel="noopener">Javatpoint<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.javadevjournal.com/" target="_blank" rel="noopener">javadevJournal<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
</div>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.baeldung.com/" target="_blank" rel="noopener">Baeldung<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://mkyong.com/" target="_blank" rel="noopener">Mkyong.com<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="web--프런트엔드-개발">WEB & 프런트엔드 개발</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://getbootstrap.com/" target="_blank" rel="noopener">Bootstrap<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
Bootstrap 공식 문서</h6>
<p class="card-text">
<p>CSS 프레임워크</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://vuejs.org/guide/introduction.html" target="_blank" rel="noopener">Vue.js<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
Vue.js 공식 문서</h6>
<p class="card-text">
<p>Javascript 프레임워크<br>
Vue.js 프레임워크 학습 문서</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="http://koxo.com" target="_blank" rel="noopener">KoXo<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
자바스크립트 매뉴얼</h6>
<p class="card-text">
<p>Javascript API 문서를 소개한다.</p>
</p>
</div>
</div>
</div>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://ko.javascript.info/" target="_blank" rel="noopener">javascript.info<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
javascript 기초 지식</h6>
<p class="card-text">
<p>기초 학습 JS, 최고의 강좌이다.</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://api.jquery.com/" target="_blank" rel="noopener">jQuery<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
jQuery 공식 문서</h6>
<p class="card-text">
<p>Javascript 프레임워크</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="데이터베이스">데이터베이스</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html" target="_blank" rel="noopener">Elasticsearch<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
엘라스틱 서치 공식 문서</h6>
<p class="card-text">
<p>엘라스틱 서치 검색 엔진에 대해서 소개한다.</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://dev.mysql.com/doc/" target="_blank" rel="noopener">MySQL<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
MySQL 공식 문서</h6>
<p class="card-text">
<p>MySQL에 대해서 소개한다.</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.postgresql.org/docs/" target="_blank" rel="noopener">PostgreSQL<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
PostgreSQL 공식 문서</h6>
<p class="card-text">
<p>PostgreSQL에 대해서 소개한다.</p>
</p>
</div>
</div>
</div>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.sqlite.org/docs.html" target="_blank" rel="noopener">SQLite<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
SQLite 공식 문서</h6>
<p class="card-text">
<p>파일형 경량 데이터베이스</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="서버">서버</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://docs.docker.com/" target="_blank" rel="noopener">Docker<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
도커 공식 문서</h6>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://kubernetes.io/ko/docs/home/" target="_blank" rel="noopener">kubernetes<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
쿠버네티스 공식 문서</h6>
<p class="card-text">
<p>K8s 클러스터 구축에 대한 소개</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="기술-커뮤니티한국어">기술 커뮤니티(한국어)</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://wikidocs.net" target="_blank" rel="noopener">WikiDocs<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
프로그래밍 서적 번역</h6>
<p class="card-text">
<p>프로그래밍 입문서들을 찾을 수 있다</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="기술-문서영문">기술 문서(영문)</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://roadmap.sh" target="_blank" rel="noopener">roadmap.sh<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
일본 개발 커뮤니터 사이트</h6>
<p class="card-text">
<p>초보자에서 학습 로드맴 제공<br>
<a href="https://roadmap.sh/backend" target="_blank" rel="noopener">Backend Developer<i class="fas fa-external-link-alt"></i></a></p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.rgagnon.com/howto.html" target="_blank" rel="noopener">Real’s How-to<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://ideone.com/" target="_blank" rel="noopener">ideone<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
</div>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://www.acmicpc.net/" target="_blank" rel="noopener">Backjoon Online Judge<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://webhacking.kr/" target="_blank" rel="noopener">Webhacking.kr<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://codecombat.com/play" target="_blank" rel="noopener">CODE COMBAT<i class="fas fa-external-link-alt"></i></a></h5>
<p class="card-text">
</p>
</div>
</div>
</div>
<h2 id="기술-커뮤니티일본어">기술 커뮤니티(일본어)</h2>
<div class="td-card-group card-group p-0 mb-4">
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://qiita.com/" target="_blank" rel="noopener">Qiita<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
일본 개발 커뮤니터 사이트</h6>
<p class="card-text">
<p>엔지니어의 지식을 기록하고 공유하기 위한 서비스이다.
프로그래밍에 관한 Tips, 노하우, 메모를 쉽게 기록 & 게시되어 있다.</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<h5 class="card-title">
<a href="https://dev.classmethod.jp/" target="_blank" rel="noopener">DevolpersIO<i class="fas fa-external-link-alt"></i></a></h5>
<h6 class="card-title ms-2 text-body-secondary">
기술 미디어 사이트</h6>
<p class="card-text">
<p>AWS, 클라우드, 생성 AI, 데이터 분석, 앱 개발 등의 기술 정보부터 DX, 이벤트 리포트, SaaS와 Python, Google Cloud 등 다양한 주제를 소개한다.</p>
</p>
</div>
</div>
<div class="td-card card border me-4">
<div class="card-body">
<p class="card-text">
</p>
</div>
</div>
</div>
Site
-
Sites
https://www.devkuma.com/docs/sites/
Sun, 01 Sep 2024 10:28:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/sites/
<p>유용한 사이트 모음</p>
Site
-
Kotest 라이프사이클 후크(Lifecycle Hooks)
https://www.devkuma.com/docs/kotest/framework/lifecycle-hooks/
Tue, 23 Apr 2024 00:37:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/kotest/framework/lifecycle-hooks/
<h2 id="테스트-라이프사이클-관리">테스트 라이프사이클 관리</h2>
<p>테스트 라이프사이클 관리는 각 테스트 케이스의 실행 전후에 필요한 작업을 수행하는 것을 의미한다. Kotest는 <code>beforeTest</code>와 <code>afterTest</code> 블록을 제공하여 테스트 라이프사이클을 관리할 수 있다. 이를 통해 각 테스트 케이스의 실행 전후에 필요한 설정 또는 정리 작업을 수행할 수 있다.</p>
<h2 id="라이프사이클-후크">라이프사이클 후크</h2>
<p>각 테스트 케이스의 실행 전후에 어떤 작업을 수행해야 하는 경우가 있다. 예를 들어, 데이터베이스 연결을 설정하거나 정리하는 작업, 테스트 시간을 측정 등의 필요한 설정 또는 정리 작업이 있을 수 있다. 이때 사용하는 것이 라이프사이클 후크(Lifecycle Hooks)이다.</p>
<p>Kotest는 스펙 내에서 직접 정의할 수 있는 다양한 종류의 후크를 제공한다. 배포 가능한 플러그인이나 재사용 가능한 후크 작성과 같은 더 발전된 경우의 <a href="https://www.devkuma.com/docs/kotest/framework/extensions/">확장(Extentions)</a>을 사용할 수도 있다.</p>
<p>이 섹션의 마지막에는 사용 가능한 후크 목록과 실행 시기가 나와 있다.</p>
<p>Kotest에서 후크를 사용하는 방법에는 여러 가지가 있다:</p>
<h3 id="dsl-메서드으로-작성하기">DSL 메서드으로 작성하기</h3>
<p>첫번째로 가장 간단한 방법은 스펙 내에서 사용 가능한 DSL 메서드를 사용하여 <code>TestListener</code>를 생성하고 등록하는 것이다. 예를 들어, 다음과 같이 <code>beforeTest</code>, <code>afterTest</code>를 직접 호출할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.kotest.tutorial.lifecycle.ex1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.core.spec.style.WordSpec</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">TestSpec</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">WordSpec</span><span style="color:#000;font-weight:bold">({</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">beforeTest</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Starting a test </span><span style="color:#4e9a06">$it</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#000">afterTest</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">test</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">result</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Finished spec with result </span><span style="color:#4e9a06">$result</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"this test"</span> <span style="color:#000">should</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"be alive"</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma is alive!"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">})</span>
</span></span></code></pre></div><p>다음은 예제를 실행한 결과이다:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Starting a test TestCase(descriptor=TestDescriptor... 생략 ...
</span></span><span style="display:flex;"><span>Starting a test TestCase(descriptor=TestDescriptor... 생략 ...
</span></span><span style="display:flex;"><span>Johnny5 is alive!
</span></span><span style="display:flex;"><span>Finished spec with result Success(duration=6ms)
</span></span><span style="display:flex;"><span>Finished spec with result Success(duration=51ms)
</span></span></code></pre></div><p>백그라운드에서 이러한 DSL 메서드는 적절한 함수를 재정의하고, 이 테스트 리스너가 실행되도록 등록한 <code>TestListener</code>의 인스턴스를 생성한다.</p>
<p>프로젝트 리스너의 인스턴스를 생성하는 DSL 메서드로 <code>afterProject</code>를 사용할 수 있지만, 프레임워크가 스펙을 감지하는 이 단계에 있을 때는 이미 프로젝트가 시작되었으므로 <code>beforeProject</code>는 없다!</p>
<h3 id="함수를-사용하는-dsl-메서드">함수를 사용하는 DSL 메서드</h3>
<p>이러한 DSL 메서드는 함수를 받으므로, 함수에 대한 로직을 가져와 여러 곳에서 재사용할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.kotest.tutorial.lifecycle.ex2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.core.spec.style.WordSpec</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.core.test.TestCase</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">startTest</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">suspend</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">TestCase</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-></span> <span style="color:#000">Unit</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Starting a test </span><span style="color:#4e9a06">$it</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">TestSpec</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">WordSpec</span><span style="color:#000;font-weight:bold">({</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// used once
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">beforeTest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">startTest</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"this test"</span> <span style="color:#000">should</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"be alive"</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma is alive!"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">})</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">OtherSpec</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">WordSpec</span><span style="color:#000;font-weight:bold">({</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// used twice
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">beforeTest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">startTest</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"this test"</span> <span style="color:#000">should</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"fail"</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"boom"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">})</span>
</span></span></code></pre></div><p>다음은 예제를 실행한 결과이다:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Starting a test TestCase(descriptor=TestDescriptor... 생략 ...
</span></span><span style="display:flex;"><span>boom
</span></span><span style="display:flex;"><span>Starting a test TestCase(descriptor=TestDescriptor... 생략 ...
</span></span><span style="display:flex;"><span>devkuma is alive!
</span></span></code></pre></div><h3 id="스펙에서-콜백-함수-재정의하기">스펙에서 콜백 함수 재정의하기</h3>
<p>두 번째 방법으로는 스펙에서 콜백 함수를 재정의하는 것이다. 아래 예제는 첫 번째 방법을 변형하였다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.kotest.tutorial.lifecycle.ex3</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.core.spec.style.WordSpec</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.core.test.TestCase</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">TestSpec</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">WordSpec</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">override</span> <span style="color:#204a87;font-weight:bold">suspend</span> <span style="color:#204a87;font-weight:bold">fun</span> <span style="color:#000">beforeTest</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">testCase</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000">TestCase</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Starting a test </span><span style="color:#4e9a06">$testCase</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">init</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"this test"</span> <span style="color:#000">should</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">"be alive"</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"devkuma is alive!"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span>
</span></span></code></pre></div><h2 id="라이프사이클-콜백">라이프사이클 콜백</h2>
<p>모든 콜백을 올바르게 이해하려면, 가능한 <code>TestType</code> 값을 이해하는 것이 중요하다:</p>
<ul>
<li><code>Container</code>: 다른 테스트를 포함할 수 있는 컨테이너</li>
<li><code>Test</code>: 중첩된 테스트를 포함할 수 없는 리프(Leaf) 테스트. 즉, 다른 테스트를 호출하지 않고 독립적으로 실행될 수 있는 테스트</li>
<li><code>Dynamic</code>: 컨테이너 또는 테스트가 될 수 있으며, 속성 테스트 또는 데이터 테스트와 같은 기능을 통해 테스트가 동적으로 추가될 때 사용</li>
</ul>
<p>Kotest에서는 테스트 수명주기에 따라 실행되는 여러 종류의 콜백 함수를 제공한다.</p>
<p>아래는 Kotest에서 사용되는 각 콜백에 대한 설명이다:</p>
<table>
<thead>
<tr>
<th>콜백</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>beforeContainer</code></td>
<td>컨테이너(Test container) 내의 모든 테스트가 실행되기 전에 호출되는 콜백이다.</td>
</tr>
<tr>
<td><code>afterContainer</code></td>
<td>컨테이너(Test container) 내의 모든 테스트가 실행된 후에 호출되는 콜백이다.</td>
</tr>
<tr>
<td><code>beforeEach</code></td>
<td>각 테스트 케이스(Test case) 실행 전에 호출되는 콜백이다. 각 테스트마다 별도의 설정을 수행하고 싶을 때 사용된다.</td>
</tr>
<tr>
<td><code>afterEach</code></td>
<td>각 테스트 케이스(Test case) 실행 후에 호출되는 콜백이다. 각 테스트마다 별도의 정리 작업을 수행하고 싶을 때 사용된다.</td>
</tr>
<tr>
<td><code>beforeAny</code></td>
<td>특정 컨테이너 또는 스펙 내의 모든 테스트가 실행되기 전에 호출되는 콜백이다. 특정 컨테이너나 스펙 내의 모든 테스트에 대한 공통적인 설정 작업을 수행하고자 할 때 사용된다.</td>
</tr>
<tr>
<td><code>afterAny</code></td>
<td>특정 컨테이너 또는 스펙 내의 모든 테스트가 실행된 후에 호출되는 콜백이다. 특정 컨테이너나 스펙 내의 모든 테스트에 대한 공통적인 정리 작업을 수행하고자 할 때 사용된다.</td>
</tr>
<tr>
<td><code>beforeTest</code></td>
<td>각 테스트 메소드(Test method) 실행 전에 호출되는 콜백이다. 이 함수를 사용하여 각 테스트의 설정을 수행할 수 있다.<br/>beforeAny와 동일한 동작을 한다.</td>
</tr>
<tr>
<td><code>afterTest</code></td>
<td>각 테스트 메소드(Test method) 실행 후에 호출되는 콜백이다. 이 함수를 사용하여 각 테스트의 정리 작업을 수행할 수 있다.<br/>afterAny와 동일한 동작을 한다.</td>
</tr>
<tr>
<td><code>beforeSpec</code></td>
<td>각 스펙(Spec) 실행 전에 호출되는 콜백이다. 스펙 전체에 대한 설정 작업을 수행하고자 할 때 사용된다.<br/>예를 들어, 데이터베이스 연결을 설정하거나 특정 자원을 초기화하는 작업을 수행할 수 있다.<br/><code>IsolationMode</code>가 <code>SingleInstance</code>인 경우, <code>prepareSpec</code>과 같은 동작을 한다.</td>
</tr>
<tr>
<td><code>afterSpec</code></td>
<td>각 스펙(Spec) 실행 후에 호출되는 콜백이다. 스펙 전체에 대한 정리 작업을 수행하고자 할 때 사용된다.<br/>예를 들어, 데이터베이스 연결을 닫거나 특정 자원을 정리하는 작업을 수행할 수 있다.<br/><code>IsolationMode</code>가<code> SingleInstance</code>인 경우, <code>finalizeSpec</code>과 같은 동작을 한다. 해당 Callback에서 예외가 발생하면 이후 <code>beforeSpec</code>, <code>AfterSpec</code>은 스킵된다.</td>
</tr>
<tr>
<td><code>prepareSpec</code></td>
<td>스펙(Spec)이 실행되기 전에 호출되는 콜백으로, 사용자 정의 초기화 작업을 수행하는 데 사용된다.<br/><code>IsolationMode</code>가 SingleInstance인 경우, beforeSpec과 같은 동작을 한다.</td>
</tr>
<tr>
<td><code>finalizeSpec</code></td>
<td>스펙(Spec)이 실행된 후에 호출되는 콜백으로, 사용자 정의 정리 작업을 수행하는 데 사용된다.<br/><code>IsolationMode</code>가 <code>SingleInstanc</code>e인 경우, <code>afterSpec</code>과 같은 동작을 한다.</td>
</tr>
<tr>
<td><code>beforeInvocation</code></td>
<td>각 테스트 메소드(Test method) 또는 테스트 케이스(Test case) 실행 전에 호출되는 콜백이다.<br/>invocation 설정이 없으면 beforeTest와 같은 동작을 한다.</td>
</tr>
<tr>
<td><code>afterInvocation</code></td>
<td>각 테스트 메소드(Test method) 또는 테스트 케이스(Test case) 실행 후에 호출되는 콜백이다.<br/>invocation 설정이 없으면 afterTest와 같은 동작을 한다.</td>
</tr>
</tbody>
</table>
<p>이러한 콜백들은 각 테스트 실행 전후나 스펙 실행 전후 등의 다양한 시점에 특정 동작을 수행할 수 있도록 도와준다. 각 콜백은 해당하는 시점에 호출되며, 필요에 따라 적절한 로직을 구현하여 사용할 수 있다.</p>
<p><code>beforeAny</code>과 <code>beforeTest</code>는 동일한 함수의 다른 이름일 뿐이지만, <code>beforeEach</code>와는 다르다. <code>beforeAny</code>, <code>beforeTest</code> 각각 <code>TestType.Test</code> 전에 호출되는 반면, <code>beforeEach</code>는 <code>TestType.Container</code>와 <code>TestType.Test</code> 전에 호출된다. <code>afterAny</code>, <code>afterTest</code> 및 <code>afterEach</code>에도 동일하게 적용된다.</p>
<p>다음 콜백 함수가 언제 실행되는 알수 있는 예시를 통해서 알아보겠다:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">package</span> <span style="color:#000">com.devkuma.kotest.tutorial.lifecycle.ex4</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.core.spec.style.StringSpec</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">import</span> <span style="color:#000">io.kotest.matchers.shouldBe</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">class</span> <span style="color:#000">CallbackTest</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">StringSpec</span><span style="color:#000;font-weight:bold">({</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 스펙이 실행되기 전에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">beforeSpec</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"beforeSpec: 스펙 실행 전 설정을 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 스펙 전체에 대한 설정 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 스펙이 실행된 후에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">afterSpec</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"afterSpec: 스펙 실행 후 정리를 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 스펙 전체에 대한 정리 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 테스트 실행 전에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">beforeTest</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">testCase</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"beforeTest: </span><span style="color:#4e9a06">${testCase.name.testName}</span><span style="color:#4e9a06"> 실행 전 설정을 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 여기에 테스트의 공통 설정 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 테스트 실행 후에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">afterTest</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">testCase</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">testResult</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"afterTest: </span><span style="color:#4e9a06">${testCase.name.testName}</span><span style="color:#4e9a06"> 실행 후 정리를 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"결과: </span><span style="color:#4e9a06">$testResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 여기에 테스트의 공통 정리 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 모든 테스트가 실행되기 전에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">beforeAny</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">testCase</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"beforeAny: </span><span style="color:#4e9a06">${testCase.name.testName}</span><span style="color:#4e9a06"> 테스트 실행 전 설정을 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 여기에 모든 테스트에 대한 공통 설정 작업을 추가다.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 모든 테스트가 실행된 후에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">afterAny</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">testCase</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">testResult</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"afterAny:</span><span style="color:#4e9a06">${testCase.name.testName}</span><span style="color:#4e9a06"> 테스트 실행 후 정리를 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"결과: </span><span style="color:#4e9a06">$testResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 여기에 모든 테스트에 대한 공통 정리 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 테스트 실행 전에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">beforeEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">testCase</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"beforeEach: </span><span style="color:#4e9a06">${testCase.name.testName}</span><span style="color:#4e9a06"> 테스트 실행 전 설정을 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 여기에 테스트의 개별 설정 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 각 테스트 실행 후에 호출되는 콜백
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">afterEach</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">testCase</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">testResult</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">-></span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"afterEach: </span><span style="color:#4e9a06">${testCase.name.testName}</span><span style="color:#4e9a06"> 테스트 실행 후 정리를 수행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"결과: </span><span style="color:#4e9a06">$testResult</span><span style="color:#4e9a06">"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 여기에 테스트의 개별 정리 작업을 추가.
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 테스트 케이스 1
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#4e9a06">"TestCase1"</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"TestCase1 실행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">result</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">2</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">result</span> <span style="color:#000">shouldBe</span> <span style="color:#0000cf;font-weight:bold">4</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// 테스트 케이스 2
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#4e9a06">"TestCase2"</span> <span style="color:#000;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">println</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"'TestCase2' 실행."</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">val</span> <span style="color:#000">list</span> <span style="color:#000;font-weight:bold">=</span> <span style="color:#000">mutableListOf</span><span style="color:#000;font-weight:bold"><</span><span style="color:#000">Int</span><span style="color:#000;font-weight:bold">>()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">list</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">list</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">list</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">size</span> <span style="color:#000">shouldBe</span> <span style="color:#0000cf;font-weight:bold">2</span>
</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">})</span>
</span></span></code></pre></div><p>다음은 위 예시를 실행한 결과이다:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>beforeSpec: 스펙 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>beforeEach: TestCase1 테스트 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>beforeTest: TestCase1 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>beforeAny: TestCase1 테스트 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>TestCase1 실행.
</span></span><span style="display:flex;"><span>afterAny:TestCase1 테스트 실행 후 정리를 수행. 결과: Success(duration=36ms)
</span></span><span style="display:flex;"><span>afterTest: TestCase1 실행 후 정리를 수행. 결과: Success(duration=36ms)
</span></span><span style="display:flex;"><span>afterEach: TestCase1 테스트 실행 후 정리를 수행. 결과: Success(duration=36ms)
</span></span><span style="display:flex;"><span>beforeEach: TestCase2 테스트 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>beforeTest: TestCase2 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>beforeAny: TestCase2 테스트 실행 전 설정을 수행.
</span></span><span style="display:flex;"><span>'TestCase2' 실행.
</span></span><span style="display:flex;"><span>afterAny:TestCase2 테스트 실행 후 정리를 수행. 결과: Success(duration=1ms)
</span></span><span style="display:flex;"><span>afterTest: TestCase2 실행 후 정리를 수행. 결과: Success(duration=1ms)
</span></span><span style="display:flex;"><span>afterEach: TestCase2 테스트 실행 후 정리를 수행. 결과: Success(duration=1ms)
</span></span><span style="display:flex;"><span>afterSpec: 스펙 실행 후 정리를 수행.
</span></span></code></pre></div><hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://kotest.io/docs/framework/lifecycle-hooks.html" target="_blank" rel="noopener">Lifecycle hooks | Kotest<i class="fas fa-external-link-alt"></i></a></li>
</ul>
Kotlin
Kotest
-
Lua 함수
https://www.devkuma.com/docs/lua/function/
Mon, 22 Apr 2024 01:48:00 +0900
[email protected] (kc kim)
https://www.devkuma.com/docs/lua/function/
<p>Lua뿐만 아니라 많은 프로그래밍 언어에서 빼놓을 수 없는 개념 중 하나로 함수가 있다. 사실 지금까지 함수를 여러 번 사용하였다. 예를 들어 <code>print</code>도 함수이고, <code>io.read</code>도 함수이다. 함수는 여러 명령어를 모아 하나의 덩어리로 정의한 것이다. 같은 처리를 여러 번 하고 싶을 때는 함수를 하나 정의하고 호출하면 된다.</p>
<h2 id="함수-만들기">함수 만들기</h2>
<p>이제 함수를 만들어 보자. 함수는 다음과 같이 정의한다.</p>
<p><strong>형식</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">함수명</span><span style="color:#000;font-weight:bold">(</span><span style="color:#a40000">인수</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#a40000">처리</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span></code></pre></div><p>이제 두 개의 값을 인수로 전달받아 그 합을 구하는 함수를 만들어 보자.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">sum</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">x</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">y</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">y</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">10</span>
</span></span><span style="display:flex;"><span><span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">20</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sum</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">" + "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">" = "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">result</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>10 + 20 = 30
</span></span></code></pre></div><p>먼저 <code>sum</code> 함수를 사용하기 전에 함수를 정의해야 한다는 점에 유의하자. 함수에 원하는 이름을 붙이면 된다. 단, 이미 사용 중인 함수나 변수 이름은 사용하지 말아야 한다. 예를 들어, <code>print</code>와 같은 이름을 붙이지 않는다. 함수 이름 지정 규칙은 변수와 동일하다.</p>
<div class="alert alert-primary" role="alert"><div class="h4 alert-heading" role="heading">C 언어와의 차이점</div>
- 프로토타입 선언이 없다
</div>
<p>이제 함수를 호출할 때, 아래와 같이 하였다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">sum</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p>함수를 호출할 때 인수라는 것을 전달한다. 이 인수는 어떤 것일까?</p>
<p><img src="https://www.devkuma.com/docs/lua/lua-function.png" alt="Lua Function"></p>
<p>위 그림은 인수의 모습을 나타낸 그림이다. 호출한 쪽의 인수를 인자(argument, 실제 인수)라고 하고, 호출받은 쪽의 인수를 매개변수(parameter, 임시 인수)라고 한다.</p>
<p>이 때, 인자의 값이 매개변수의 값으로 복사된다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">x</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">y</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">10</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">y</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">20</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">12</span>
</span></span><span style="display:flex;"><span><span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">22</span>
</span></span><span style="display:flex;"><span><span style="color:#000">func</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p>즉 위와 같이 호출하더라도 <code>x</code>,<code>y</code>는 <code>hoge</code>, <code>piyo</code>의 복사본이므로 내용을 아무리 다시 작성해도 호출 원본인 <code>hoge</code>, <code>piyo</code>에는 영향을 미치지 않는다.</p>
<p>인수와 관련하여, 어쩌면 아무것도 받을 필요가 없는 경우가 있을 수 있다. 이 경우, 인수를 비워두면 된다.</p>
<p>함수의 반환값에 대해서는 다음 장에서 설명한다.</p>
<h2 id="함수의-반환값">함수의 반환값</h2>
<p>함수에는 반환값이라는 것이 존재한다. 앞에서 설명한 프로그램과 아래 그림을 참고해보자.</p>
<p><img src="https://www.devkuma.com/docs/lua/lua-function.png" alt="Lua Function"></p>
<p><code>return</code>은 함수를 호출한 측에 값을 반환하는 명령어이다. 여기서 <code>x + y</code>의 계산 결과를 호출자에게 반환한다. 즉, 변수 <code>result</code>에는 계산 결과가 대입된다.</p>
<p>Lua는 여러 개의 값을 반환할 수 있다. 예를 들어, 다음과 같이 할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>10 20
</span></span></code></pre></div><p><code>hoge</code>에는 10이, <code>piyo</code>에는 20이 대입된다.</p>
<p>그렇다면 다음과 같은 코드를 작성하면 어떻게 될까?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p>이 경우 두 번째 반환값인 20은 사용되지 않고 사라진다.</p>
<p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>10 nil
</span></span></code></pre></div><p>또한, 다음과 같은 코드를 작성했다면 어떨까?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">10</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">20</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p>두 번째 반환값은 없다. 이 경우 <code>piyo</code>에는 <code>nil</code>이 할당된다. <code>piyo</code>는 20이 아니므로 주의해야 한다.</p>
<p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>10 nil
</span></span></code></pre></div><h2 id="아무것도-반환하지-않는-함수">아무것도 반환하지 않는 함수</h2>
<p>반환값 함수, 즉 아무것도 반환하지 않는 함수를 만들 수도 있다. 이 경우 <code>return</code>을 작성하지 않아도 된다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"함수가 호출되었다"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>함수가 호출되었다.
</span></span></code></pre></div><p>또한, 명시적으로 반환값이 없음을 나타내기 위해 <code>return</code>만 작성할 수도 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"함수가 호출되었다"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">func</span><span style="color:#000;font-weight:bold">()</span>
</span></span></code></pre></div><p>위의 두 코드는 완전히 동일한 코드이다.</p>
<h2 id="변수에-함수-대입하기">변수에 함수 대입하기</h2>
<p>Lua의 변수에는 어떤 값이라도 대입할 수 있다. 심지어 함수라도 대입이 가능하다. 예를 들어 다음과 같은 것도 가능하다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">sum</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">x</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">y</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">y</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">mul</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">x</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">y</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">y</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">sum</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge is "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">));</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"10 + 20 is "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mul</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"10 * 20 is "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000;font-weight:bold">))</span>
</span></span></code></pre></div><p><code>type</code> 함수는 현재 변수의 타입을 검사하는 함수이다. <code>hoge</code>에는 <code>sum</code> 함수와 <code>mul</code> 함수를 대입하고 있다. 그리고 <code>hoge</code>를 사용하여 함수에 접근할 수도 있다. C언어를 아는 사람은 함수 포인터와 같은 역할을 할 수 있다고 생각하면 된다.
<strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>hoge is function
</span></span><span style="display:flex;"><span>10 + 20 is 30
</span></span><span style="display:flex;"><span>10 * 20 is 200
</span></span></code></pre></div><h2 id="함수-안에서-함수-정의하기">함수 안에서 함수 정의하기</h2>
<p>Lua에서는 함수 내에서 함수를 정의할 수도 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">x</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">local</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">get</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">x</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">local</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">add</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">value</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">value</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">get</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">add</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">firstGetValue</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">firstAddValue</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000">secondGetValue</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">secondAddValue</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">func</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">30</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"first value : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">firstGetValue</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"second value : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">secondGetValue</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">firstAddValue</span><span style="color:#000;font-weight:bold">(</span> <span style="color:#0000cf;font-weight:bold">15</span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000">secondAddValue</span><span style="color:#000;font-weight:bold">(</span> <span style="color:#0000cf;font-weight:bold">20</span> <span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"first value : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">firstGetValue</span><span style="color:#000;font-weight:bold">())</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"second value : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">secondGetValue</span><span style="color:#000;font-weight:bold">())</span>
</span></span></code></pre></div><p><code>func</code> 함수는 <code>get</code>과 <code>add</code> 함수를 반환값으로 반환한다. 여기서 <code>local</code>이라는 새로운 키워드가 등장했다. 이 <code>local</code>에 대해서는 유효범위 장에서 자세히 설명한다. 간단히 설명하면, <code>local</code>이라는 키워드를 붙이면 로컬 변수(또는 로컬 함수)를 생성할 수 있다. 즉, <code>get</code> 함수도 <code>add</code> 함수도 <code>func</code> 함수 내에서만 호출할 수 있다는 뜻이다.</p>
<p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>first value : 10
</span></span><span style="display:flex;"><span>second value : 30
</span></span><span style="display:flex;"><span>first value : 25
</span></span><span style="display:flex;"><span>second value : 50
</span></span></code></pre></div><h2 id="무명-함수">무명 함수</h2>
<p>이름 없는 함수를 만들 수도 있다. 예를 들어, 다음과 같은 코드를 작성할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">createSquare</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">x</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">x</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#000">x</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">square</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">createSquare</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"10 * 10 is "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">square</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">))</span>
</span></span></code></pre></div><p><code>createSquare</code> 함수는 반환값에 <code>x * x</code>를 하는 함수를 반환한다.</p>
<p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>10 * 10 is 100
</span></span></code></pre></div><h2 id="기본-라이브러리">기본 라이브러리</h2>
<p>여기서는 기본 라이브러리에 제공되는 함수 중 일부를 소개한다.</p>
<h4 id="assert-함수">assert 함수</h4>
<p><strong>정의</strong></p>
<pre tabindex="0"><code>assert (v [, message])
</code></pre><p><code>assert</code> 함수는 <code>v</code>가 거짓 조건일 경우 에러를 발생시키는 함수이다.
<code>message</code>에는 에러 메시지를 전달한다. 에러 메시지는 생략할 수 있다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span><span style="color:#000">assert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Error #1"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">false</span>
</span></span><span style="display:flex;"><span><span style="color:#000">assert</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">"Error #2"</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>lua: test.lua:4: Error #2
</span></span><span style="display:flex;"><span>stack traceback:
</span></span><span style="display:flex;"><span> [C]: in function 'assert'
</span></span><span style="display:flex;"><span> test.lua:4: in main chunk
</span></span><span style="display:flex;"><span> [C]: in ?
</span></span></code></pre></div><h3 id="dofile-함수">dofile 함수</h3>
<p><strong>정의</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">dofile</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">filename</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><code>dofile</code> 함수는 <code>filename</code>으로 지정한 파일을 실행하는 함수이다. 예를 들어 다음과 같이 사용한다. 이번에는 두 개의 Lua 파일인 <code>test.lua</code>와 <code>call.lua</code>에 코드를 작성하였다.</p>
<p><strong>call.lua</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">testFunc</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#000">print</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"Hello"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#4e9a06">"World!"</span>
</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">end</span>
</span></span></code></pre></div><p><strong>test.lua</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">dofile</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"call.lua"</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">testFunc</span><span style="color:#000;font-weight:bold">()</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Hello
</span></span><span style="display:flex;"><span>World!
</span></span></code></pre></div><p><code>dofile</code>에서 <code>call.lua</code>를 불러오면 <code>test.lua</code> 측에서 <code>testFunc</code> 함수를 사용할 수 있다.</p>
<h3 id="type-함수">type 함수</h3>
<p><strong>정의</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">v</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><code>type</code> 함수는 데이터의 타입을 반환하는 함수이다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">nil</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">10</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"Hello"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge : "</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">))</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>hoge : nil
</span></span><span style="display:flex;"><span>hoge : number
</span></span><span style="display:flex;"><span>hoge : boolean
</span></span><span style="display:flex;"><span>hoge : string
</span></span></code></pre></div><h3 id="tonumber-함수">tonumber 함수</h3>
<p><strong>정의</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">tonumber</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span> <span style="color:#000;font-weight:bold">[,</span> <span style="color:#000">base</span><span style="color:#000;font-weight:bold">])</span>
</span></span></code></pre></div><p><code>tonumber</code> 함수는 인수 <code>e</code>를 숫자로 변환하는 함수이다. 숫자 변환이 가능하면 숫자를 반환하고, 그렇지 않으면 <code>nil</code>을 반환한다.</p>
<p>또한, <code>base</code>를 지정하면 e를 base 진법의 숫자로 변환한다. 생략하면 10이 자동으로 지정된다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">"25"</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">" type :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span><span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">tonumber</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"piyo :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">" type :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">))</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<pre tabindex="0"><code>hoge :25 type :string
piyo :25 type :number
</code></pre><h3 id="tostring-함수">tostring 함수</h3>
<p><strong>정의</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">tostring</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span><span style="color:#000;font-weight:bold">)</span>
</span></span></code></pre></div><p><code>tostring</code> 함수는 <code>e</code>를 문자열로 변환하는 함수이다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">25</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"hoge :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">" type :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">))</span>
</span></span><span style="display:flex;"><span><span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">tostring</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">hoge</span><span style="color:#000;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span><span style="color:#000">print</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">"piyo :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">" type :"</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#000">type</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">piyo</span><span style="color:#000;font-weight:bold">))</span>
</span></span></code></pre></div><p><strong>실행 결과:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>hoge :25 type :number
</span></span><span style="display:flex;"><span>piyo :25 type :string
</span></span></code></pre></div><p>숫자를 문자열로 변환할 때는 <code>tostring</code> 함수를 사용하지 않고 빈 문자열을 추가해도 된다.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#000">piyo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hoge</span> <span style="color:#ce5c00;font-weight:bold">..</span> <span style="color:#4e9a06">""</span>
</span></span></code></pre></div>
Lua