<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발부터 자유까지</title>
    <link>https://kayath.tistory.com/</link>
    <description>공부하고 개발하고 최종적으로 자유를 꿈꾸는 개발자의 공간입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 16 Jun 2026 04:24:12 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ssehuun</managingEditor>
    <image>
      <title>개발부터 자유까지</title>
      <url>https://tistory1.daumcdn.net/tistory/6344618/attach/34364efe6b75408da5b63f9113a0e75f</url>
      <link>https://kayath.tistory.com</link>
    </image>
    <item>
      <title>클로드 코드 사용법(feat. Terminal)</title>
      <link>https://kayath.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 터미널에서 사용할때의 사용법을 익히고자 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로드 코드 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS, Linux, WSL 설치 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1775028984643&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -fsSL https://claude.ai/install.sh | bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;claude code 시작 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1775029033697&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd your-project
claude&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필수명령&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 253px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;명령&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;claude&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;대화형 모드 시작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;claude&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;! for &quot;bash command&quot;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;bash 명령어 동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;!cd work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;/ for &quot;claude command&quot;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;claude 명령어 동작&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;/help&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;@ for &quot;file path&quot;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;작업 디렉토리의 파일 언급&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;@/work/agent.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;claude &quot;task&quot;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;일화성 작업 실행&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;claude &quot;fix the build error&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;exit&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;claude code 종료&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;/clear&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;대화기록 지우기&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;/clear&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/46</guid>
      <comments>https://kayath.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 1 Apr 2026 18:00:49 +0900</pubDate>
    </item>
    <item>
      <title>[Python] time 함수 비교</title>
      <link>https://kayath.tistory.com/45</link>
      <description>&lt;div&gt;아래는 &lt;b&gt;time.time() / time.perf_counter() / time.process_time() / time.monotonic()&lt;/b&gt; 네 가지 타이머 함수의 &lt;b&gt;정확한 차이점 정리&lt;/b&gt;입니다.&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 비교표&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4418%; text-align: center;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%; text-align: center;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;width: 10.9303%; text-align: center;&quot;&gt;증가 방식&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%; text-align: center;&quot;&gt;시스템 시간 변경 영향&lt;/td&gt;
&lt;td style=&quot;width: 11.628%; text-align: center;&quot;&gt;일시정지&lt;br /&gt;(절전) 영향&lt;/td&gt;
&lt;td style=&quot;width: 9.76744%; text-align: center;&quot;&gt;CPU&lt;br /&gt;시간 기준&lt;/td&gt;
&lt;td style=&quot;width: 6.86051%; text-align: center;&quot;&gt;정밀도&lt;/td&gt;
&lt;td style=&quot;width: 17.7907%; text-align: center;&quot;&gt;사용 용도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4418%;&quot;&gt;
&lt;div&gt;time.time()&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;현재 시각 확인 (Unix timestamp)&lt;/td&gt;
&lt;td style=&quot;width: 10.9303%;&quot;&gt;실제 시간 기준&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;✔ 영향 받음 (NTP 동기화 등)&lt;/td&gt;
&lt;td style=&quot;width: 11.628%;&quot;&gt;✔ 영향 받음&lt;/td&gt;
&lt;td style=&quot;width: 9.76744%;&quot;&gt;✖&lt;/td&gt;
&lt;td style=&quot;width: 6.86051%;&quot;&gt;중&lt;/td&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;현재 시각 출력, 로깅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4418%;&quot;&gt;
&lt;div&gt;time.perf_counter()&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;가장 정확한 성능 측정용&lt;/td&gt;
&lt;td style=&quot;width: 10.9303%;&quot;&gt;모노토닉 타이머&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;✖ 영향 없음&lt;/td&gt;
&lt;td style=&quot;width: 11.628%;&quot;&gt;✖ 영향 없음&lt;/td&gt;
&lt;td style=&quot;width: 9.76744%;&quot;&gt;✖&lt;/td&gt;
&lt;td style=&quot;width: 6.86051%;&quot;&gt;매우 높음&lt;/td&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;코드 실행 시간 측정(성능 테스트)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4418%;&quot;&gt;
&lt;div&gt;time.process_time()&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;CPU 사용 시간 측정&lt;/td&gt;
&lt;td style=&quot;width: 10.9303%;&quot;&gt;CPU가 작업한 시간만 증가&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;✖ 영향 없음&lt;/td&gt;
&lt;td style=&quot;width: 11.628%;&quot;&gt;✖ 영향 없음&lt;/td&gt;
&lt;td style=&quot;width: 9.76744%;&quot;&gt;✔ CPU time only&lt;/td&gt;
&lt;td style=&quot;width: 6.86051%;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;알고리즘 CPU 사용량 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.4418%;&quot;&gt;
&lt;div&gt;time.monotonic()&lt;/div&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;뒤로 되돌아가지 않는 시간 측정&lt;/td&gt;
&lt;td style=&quot;width: 10.9303%;&quot;&gt;모노토닉 타이머&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;✖ 영향 없음&lt;/td&gt;
&lt;td style=&quot;width: 11.628%;&quot;&gt;✖ 영향 없음&lt;/td&gt;
&lt;td style=&quot;width: 9.76744%;&quot;&gt;✖&lt;/td&gt;
&lt;td style=&quot;width: 6.86051%;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;경과 시간 측정(시계 튐 방지)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. time.time()&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 시간을 초 단위 float로 반환 (UNIX timestamp)&lt;/li&gt;
&lt;li&gt;가장 일반적인 시간 함수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시스템 시간 변경에 영향을 받음&lt;/b&gt;&lt;br /&gt;&amp;rarr; NTP 동기화 / 관리자 설정 변경하면 값이 앞뒤로 튈 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정확한 소요 시간 측정용으로는 부적합&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1770688338005&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time
print(time.time()) # 1739193812.12345&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. time.perf_counter()&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python에서 가장 &lt;b&gt;정밀도가 높은 고해상도 타이머&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;부팅 후 증가하는 시간(정확한 소요 시간을 측정)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시스템 시간 변경, 절전, 일시정지에 영향 없음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;성능 측정(benchmark)에 가장 적합한 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 활용 예&lt;/h3&gt;
&lt;div&gt;
&lt;div data-testid=&quot;ComponentFluentProviderId&quot;&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1770688372440&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;start = time.perf_counter()
# 수행 코드
end = time.perf_counter()

print(end - start)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div data-overflow-item=&quot;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div data-overflow-item=&quot;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div data-overflow-item=&quot;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div data-overflow-item=&quot;&quot;&gt;
&lt;h3 id=&quot;language-badge&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; letter-spacing: -1px;&quot;&gt;3. time.process_time()&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CPU가 실제로 사용된 시간만 증가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;sleep(), I/O 대기 등 CPU 사용이 없는 시간은 카운트되지 않음&lt;/li&gt;
&lt;li&gt;절전, 시스템 시간 변경, real-time 흐름과 무관&lt;/li&gt;
&lt;li&gt;알고리즘의 CPU 소모량 측정에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1770688469365&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

start = time.process_time()
# CPU 많이 쓰는 작업
end = time.process_time()

print(end - start)  # CPU가 실제로 사용된 시간만 출력됨&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div data-testid=&quot;ComponentFluentProviderId&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-fluid-id=&quot;E&quot;&gt;
&lt;div style=&quot;text-align: left;&quot; contenteditable=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. time.monotonic()&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;절대로 뒤로 가지 않는(monotonic) 타이머&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;시스템 시간 변경 영향 없음&lt;/li&gt;
&lt;li&gt;perf_counter보다 정밀도가 낮을 수 있지만 목적은 동일:&lt;br /&gt;&amp;rarr; &lt;b&gt;경과 시간 측정용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;perf_counter()와 비슷하지만 보장하는 정밀도는 플랫폼마다 다름&lt;br /&gt;&amp;rarr; 일반적으로 perf_counter()가 좀 더 고정밀&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-testid=&quot;ComponentFluentProviderId&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1770688501706&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

start = time.monotonic()
# some work
end = time.monotonic()

print(end - start)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-overflow-item=&quot;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000; letter-spacing: -1px;&quot;&gt;  언제 어떤 걸 써야 하나?&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔ 현재 시각을 알고 싶다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;time.time()&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔ 코드 실행 시간을 정확하게 측정하고 싶다 (benchmark)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;time.perf_counter()&lt;/b&gt; (가장 추천)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔ 알고리즘이 CPU를 얼마나 썼는지 알고 싶다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;time.process_time()&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔ 시스템 시간 변경에도 영향을 받지 않는 경과 시간 측정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;b&gt;time.monotonic()&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Python</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/45</guid>
      <comments>https://kayath.tistory.com/45#entry45comment</comments>
      <pubDate>Tue, 10 Feb 2026 10:55:50 +0900</pubDate>
    </item>
    <item>
      <title>[Database] PostgreSQL 오류</title>
      <link>https://kayath.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.naver.com/ohj3423/223008733976&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/ohj3423/223008733976&lt;/a&gt;&lt;/p&gt;</description>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/44</guid>
      <comments>https://kayath.tistory.com/44#entry44comment</comments>
      <pubDate>Wed, 21 Jan 2026 21:13:14 +0900</pubDate>
    </item>
    <item>
      <title>MCP 구조 이해하기</title>
      <link>https://kayath.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;목차&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;#no0&quot;&gt;1. MCP 개념정리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;#no1&quot;&gt;2. MCP 아키텍처 이해하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;no0&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. MCP 개념정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3e3e3e; text-align: start;&quot;&gt;MCP (Model Context Protocol) is an open-source standard for connecting AI applications to external systems.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 AI 앱이 외부 시스템과 통신하기 위한 규약을 구현한 오픈소스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP를 이용하면 ChatGPT나 클로드 같은 AI 앱이 로컬 파일이나 DB의 데이터에 접근할 수 있고, 검색 엔진이나 계산기 같은 툴을 사용할 수 있고, 특화된 프롬프트를 통해 워크플로우가 특정 정보에 접근하여 태스크를 수행할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI앱을 위한 USB-C 포트라고 생각하면 된다. USB-C 포트가 전자 기기의 표준화된 방식을 제공하는 것처럼, MCP는 AI앱이 외부 시스템에 접근할 수 있는 표준화된 방식을 제공한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;1500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsRPSA/dJMcafyzKEz/BKKg1e6uCqnCGswqB89S01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsRPSA/dJMcafyzKEz/BKKg1e6uCqnCGswqB89S01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsRPSA/dJMcafyzKEz/BKKg1e6uCqnCGswqB89S01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsRPSA%2FdJMcafyzKEz%2FBKKg1e6uCqnCGswqB89S01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3840&quot; height=&quot;1500&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;1500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;no1&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1. MCP는 무엇을 할 수 있는가&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에이전트가 구글 캘린더, 노션에 접근하여 개인화된 AI 도구처럼 실행할 수 있게 도와준다&lt;/li&gt;
&lt;li&gt;클로드 코드가 피그마 디자인을 통해 웹앱을 생성해준다&lt;/li&gt;
&lt;li&gt;기업용 챗봇이 조직의 여러 DB에 접근하여 사용자들이 채팅으로 데이터를 분석할 수 있게 도와준다&lt;/li&gt;
&lt;li&gt;블랜더(3D SW)위에서 AI 모델들이 3D 디자인을 생성하고 3D 프린터로 만들어낼 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;no1&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2. 왜 MCP는 중요한가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생태계에서 어떤 위치에 있느냐에 따라 MCP는 다양한 이점을 제공할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자 - MCP는 AI 애플리케이션이나 에이전트를 구축하거나 통합할 때 개발 시간과 복잡성을 줄여줍니다.&lt;/li&gt;
&lt;li&gt;AI 애플리케이션 또는 에이전트 - MCP는 데이터 소스, 도구, 앱의 생태계에 접근할 수 있게 하여 기능을 강화하고 최종 사용자 경험을 향상시킵니다.&lt;/li&gt;
&lt;li&gt;최종 사용자 - MCP는 사용자의 데이터를 액세스하고 필요할 때 사용자를 대신해 작업을 수행할 수 있는, 보다 강력한 AI 애플리케이션이나 에이전트를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;no2&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. MCP 아키텍처 이해하기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-1. MCP의 컨셉&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-1-1. 참여자&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 클라이언트-서버 아키텍처를 따릅니다.&lt;br /&gt;MCP&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;호스트&lt;/b&gt;(예:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://www.anthropic.com/claude-code&quot;&gt;Claude Code&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://www.claude.ai/download&quot;&gt;Claude Desktop&lt;/a&gt;)는 하나 이상의 MCP 서버에 연결을 설정합니다.&lt;br /&gt;이때 MCP 호스트는 각 MCP 서버마다 하나의 MCP 클라이언트를 생성하며, 각 클라이언트는 해당 서버와의 전용 연결을 유지합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;로컬 MCP 서버&lt;/b&gt;(STDIO 전송 사용)는 일반적으로 단일 클라이언트를 서비스합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;원격 MCP 서버&lt;/b&gt;(Streamable HTTP 전송 사용)는 여러 클라이언트를 동시에 서비스할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 참여자:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;MCP 호스트&lt;/b&gt;: 하나 이상의 MCP 클라이언트를 관리하고 조정하는 AI 애플리케이션&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;MCP 클라이언트&lt;/b&gt;: MCP 서버와의 연결을 유지하며, MCP 호스트가 사용할 컨텍스트를 가져오는 구성 요소&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;MCP 서버&lt;/b&gt;: MCP 클라이언트에 컨텍스트를 제공하는 프로그램&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;br /&gt;Visual Studio Code는 MCP 호스트로 작동합니다.&lt;br /&gt;VS Code가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://docs.sentry.io/product/sentry-mcp/&quot;&gt;Sentry MCP 서버&lt;/a&gt;에 연결하면, VS Code 런타임은 해당 서버와의 연결을 유지하는 MCP 클라이언트 객체를 생성합니다. 또한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem&quot;&gt;로컬 파일시스템 서버&lt;/a&gt;에 연결할 때는 또 다른 MCP 클라이언트 객체를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rrjuI/dJMcahiRJ2W/9lWBgzMq057JKLHVNLHW6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rrjuI/dJMcahiRJ2W/9lWBgzMq057JKLHVNLHW6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rrjuI/dJMcahiRJ2W/9lWBgzMq057JKLHVNLHW6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrrjuI%2FdJMcahiRJ2W%2F9lWBgzMq057JKLHVNLHW6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;557&quot; height=&quot;173&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot;&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;MCP 서버는 실행 위치와 관계없이 컨텍스트 데이터를 제공하는 프로그램을 의미합니다.&lt;br /&gt;예를 들어 Claude Desktop이 파일시스템 서버를 실행하면, STDIO 전송을 사용하므로 동일한 머신에서 로컬로 실행됩니다. 반면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://docs.sentry.io/product/sentry-mcp/&quot;&gt;Sentry MCP 서버&lt;/a&gt;는 Sentry 플랫폼에서 원격으로 실행되며 Streamable HTTP 전송을 사용합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-1-2. &lt;/b&gt;&lt;b&gt;계층 (Layers)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 두 개의 계층으로 구성됩니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;데이터 계층 (Data Layer)&lt;/b&gt;: JSON-RPC 기반의 클라이언트-서버 통신 프로토콜을 정의하며, 생명주기 관리 및 도구(tools), 리소스(resources), 프롬프트(prompts), 알림(notifications) 등의 핵심 요소를 포함합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;전송 계층 (Transport Layer)&lt;/b&gt;: 클라이언트와 서버 간의 데이터 교환을 가능하게 하는 통신 메커니즘과 채널을 정의합니다. 연결 설정, 메시지 프레이밍, 인증 등을 포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 계층은 내부 계층, 전송 계층은 외부 계층으로 개념화됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;데이터 계층 (Data Layer)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 계층은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://www.jsonrpc.org/&quot;&gt;JSON-RPC 2.0&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기반의 교환 프로토콜을 구현합니다. 이 계층은 다음을 포함합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;생명주기 관리&lt;/b&gt;: 클라이언트와 서버 간의 연결 초기화, 기능 협상, 연결 종료를 처리합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;서버 기능&lt;/b&gt;: AI 동작을 위한 도구, 컨텍스트 데이터 리소스, 상호작용 템플릿 프롬프트 등을 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;클라이언트 기능&lt;/b&gt;: 서버가 클라이언트의 LLM 샘플링, 사용자 입력 요청, 로그 메시지 전송 등을 요청할 수 있게 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;유틸리티 기능&lt;/b&gt;: 실시간 업데이트 알림, 장기 실행 작업의 진행 상태 추적 등을 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;전송 계층 (Transport Layer)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전송 계층은 클라이언트와 서버 간의 통신 채널 및 인증을 관리합니다. 연결 설정, 메시지 프레이밍, 보안 통신을 담당합니다. MCP는 두 가지 전송 방식을 지원합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;STDIO 전송&lt;/b&gt;: 동일한 머신 내의 프로세스 간 표준 입출력 스트림을 사용하여 네트워크 오버헤드 없이 빠른 통신을 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;Streamable HTTP 전송&lt;/b&gt;: HTTP POST를 사용하여 클라이언트-서버 간 메시지를 전송하며, Server-Sent Events(SSE)를 통한 스트리밍 기능을 지원합니다.&lt;br /&gt;이 방식은 원격 서버 통신에 적합하며, Bearer 토큰, API 키, 커스텀 헤더 등 표준 HTTP 인증 방식을 지원합니다.&lt;br /&gt;MCP는 인증 토큰 획득을 위해 OAuth 사용을 권장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전송 계층은 통신 세부 사항을 추상화하여, 모든 전송 메커니즘에서 동일한 JSON-RPC 2.0 메시지 형식을 사용할 수 있게 합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-1-3.&amp;nbsp;&lt;/b&gt;&lt;b&gt;데이터 계층 프로토콜 (Data Layer Protocol)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP의 핵심은 MCP 클라이언트와 MCP 서버 간의 스키마 및 의미 체계를 정의하는 것입니다.&lt;br /&gt;개발자에게 가장 흥미로운 부분은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://app.eaip.lge.com/lgenie/public#primitives&quot;&gt;프리미티브(Primitives)&lt;/a&gt;&lt;/b&gt;로, MCP 서버에서 MCP 클라이언트로 컨텍스트를 공유하는 방법을 정의합니다.&amp;nbsp;MCP는 기본 RPC 프로토콜로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://www.jsonrpc.org/&quot;&gt;JSON-RPC 2.0&lt;/a&gt;을 사용합니다. 클라이언트와 서버는 요청(request)과 응답(response)을 주고받으며, 응답이 필요 없는 경우 알림(notification)을 사용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;생명주기 관리 (Lifecycle Management)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 상태를 유지하는(stateful) 프로토콜로, 생명주기 관리가 필요합니다. 그 목적은 클라이언트와 서버가 지원하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;기능(capabilities)&lt;/b&gt;을 협상하는 것입니다. 자세한 내용은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://app.eaip.lge.com/specification/latest/basic/lifecycle&quot;&gt;명세서&lt;/a&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #111111;&quot; href=&quot;https://app.eaip.lge.com/lgenie/public#example&quot;&gt;예시&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;프리미티브 (Primitives)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP의 프리미티브는 MCP의 가장 중요한 개념입니다. 이는 클라이언트와 서버가 서로에게 제공할 수 있는 기능을 정의하며, AI 애플리케이션과 공유할 수 있는 컨텍스트 정보의 유형과 수행 가능한 작업의 범위를 규정합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버가 노출할 수 있는 세 가지 핵심 프리미티브:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;도구 (Tools)&lt;/b&gt;: AI 애플리케이션이 호출할 수 있는 실행 가능한 함수 (예: 파일 작업, API 호출, 데이터베이스 쿼리)&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;리소스 (Resources)&lt;/b&gt;: AI 애플리케이션에 컨텍스트 정보를 제공하는 데이터 소스 (예: 파일 내용, DB 레코드, API 응답)&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;프롬프트 (Prompts)&lt;/b&gt;: 언어 모델과의 상호작용을 구조화하는 재사용 가능한 템플릿 (예: 시스템 프롬프트, few-shot 예시)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 프리미티브 유형은&lt;span&gt;&amp;nbsp;&lt;/span&gt;*/list,&lt;span&gt;&amp;nbsp;&lt;/span&gt;*/get,&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools/call&lt;span&gt;&amp;nbsp;&lt;/span&gt;등의 메서드를 통해 검색, 조회, 실행할 수 있습니다. 예를 들어, 클라이언트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools/list로 사용 가능한 도구를 나열한 후, 특정 도구를 실행할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;br /&gt;데이터베이스 관련 MCP 서버는 다음을 제공할 수 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;쿼리 실행 도구&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;데이터베이스 스키마 리소스&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;쿼리 예시를 포함한 프롬프트&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트가 노출할 수 있는 프리미티브:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;샘플링 (Sampling)&lt;/b&gt;: 서버가 클라이언트의 AI 애플리케이션에서 언어 모델 출력을 요청할 수 있게 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;유도 (Elicitation)&lt;/b&gt;&lt;span style=&quot;color: #333333; letter-spacing: 0px;&quot;&gt;: 서버가 사용자에게 추가 정보를 요청하거나 작업을 확인할 수 있게 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;로깅 (Logging)&lt;/b&gt;&lt;span style=&quot;color: #333333; letter-spacing: 0px;&quot;&gt;: 서버가 클라이언트로 로그 메시지를 전송할 수 있게 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유틸리티 프리미티브 (Utility Primitives):&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;작업 (Tasks, 실험적)&lt;/b&gt;: 장기 실행 작업의 상태 추적 및 결과 조회를 지원하는 내구성 있는 실행 래퍼입니다.&lt;br /&gt;(예: 복잡한 계산, 워크플로 자동화, 배치 처리 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;알림 (Notifications)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 서버와 클라이언트 간의 실시간 업데이트를 위한 알림을 지원합니다. 예를 들어, 서버의 도구 목록이 변경되면 서버는&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools/list_changed&lt;span&gt;&amp;nbsp;&lt;/span&gt;알림을 전송하여 클라이언트에게 이를 통보할 수 있습니다.이 알림은 JSON-RPC 2.0의 알림 메시지( &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;응답을 기대하지 않고)&lt;/span&gt;형식을 따르며 MCP 서버와 연결된 클라이언트들에게 실시간 업데이트를 보낼 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2-1-4.&amp;nbsp;&lt;/b&gt;&lt;b&gt;예시 (Example)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;데이터 계층 (Data Layer)&lt;/p&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;이 섹션은 MCP 클라이언트-서버 상호작용의 단계별 예시를 제공합니다. 생명주기 관리, 도구 작업, 알림을 JSON-RPC 2.0 메시지를 통해 설명합니다.&lt;/p&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #282c48;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1단계: 초기화 (생명주기 관리)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;initialize&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청을 보내 연결을 설정하고 지원 기능을 협상합니다. 서버는 이에 대한 응답으로 자신의 기능을 반환합니다. 이후 클라이언트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;notifications/initialized&lt;span&gt;&amp;nbsp;&lt;/span&gt;알림을 보내 준비 완료를 알립니다.&lt;/p&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 다음이 수행됩니다:&lt;/p&gt;
&lt;pre id=&quot;code_1768548450939&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 요청 초기화
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 1,
  &quot;method&quot;: &quot;initialize&quot;,
  &quot;params&quot;: {
    &quot;protocolVersion&quot;: &quot;2025-06-18&quot;,
    &quot;capabilities&quot;: {
      &quot;elicitation&quot;: {}
    },
    &quot;clientInfo&quot;: {
      &quot;name&quot;: &quot;example-client&quot;,
      &quot;version&quot;: &quot;1.0.0&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1768548505201&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 응답 초기화
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 1,
  &quot;result&quot;: {
    &quot;protocolVersion&quot;: &quot;2025-06-18&quot;,
    &quot;capabilities&quot;: {
      &quot;tools&quot;: {
        &quot;listChanged&quot;: true
      },
      &quot;resources&quot;: {}
    },
    &quot;serverInfo&quot;: {
      &quot;name&quot;: &quot;example-server&quot;,
      &quot;version&quot;: &quot;1.0.0&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;초기화 교환의 이해&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초기화 프로세스는 MCP의 수명 주기 관리에서 핵심적인 부분이며, 다음과 같은 여러 중요한 목적을 가지고 있습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;프로토콜 버전 협상(Protocol Version Negotiation)&lt;/b&gt;:&amp;nbsp;protocolVersion&amp;nbsp;필드(예: &amp;ldquo;2025-06-18&amp;rdquo;)는 클라이언트와 서버가 호환 가능한 프로토콜 버전을 사용하고 있음을 보장합니다. 이를 통해 서로 다른 버전 간의 통신 오류를 방지할 수 있습니다. 만약 호환 가능한 버전을 협상하지 못하면 연결은 종료되어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기능 탐색(Capability Discovery)&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;capabilities&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체는 각 측이 지원하는 기능을 선언할 수 있게 합니다. 여기에는 처리할 수 있는 기본 요소(도구, 리소스, 프롬프트 등)와 알림(notification) 기능 지원 여부가 포함됩니다. 이를 통해 지원되지 않는 작업을 피하고 효율적인 통신이 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;식별 정보 교환(Identity Exchange)&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;clientInfo와&lt;span&gt;&amp;nbsp;&lt;/span&gt;serverInfo&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체는 디버깅 및 호환성 확인을 위한 식별 및 버전 정보를 제공합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 예시에서의 기능 협상(capability negotiation)은 MCP 기본 요소(primitives)가 어떻게 선언되는지를 보여줍니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;클라이언트 기능(Client Capabilities)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&quot;elicitation&quot;: {}&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 클라이언트는 사용자 상호작용 요청을 처리할 수 있음을 선언합니다(즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elicitation/create&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드 호출을 받을 수 있음).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;서버 기능(Server Capabilities)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&quot;tools&quot;: {&quot;listChanged&quot;: true}&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 서버는&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools&lt;span&gt;&amp;nbsp;&lt;/span&gt;기본 요소를 지원하며, 도구 목록이 변경될 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools/list_changed&lt;span&gt;&amp;nbsp;&lt;/span&gt;알림을 보낼 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&quot;resources&quot;: {}&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 서버는 또한&lt;span&gt;&amp;nbsp;&lt;/span&gt;resources&lt;span&gt;&amp;nbsp;&lt;/span&gt;기본 요소를 지원하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;resources/list&lt;span&gt;&amp;nbsp;&lt;/span&gt;및&lt;span&gt;&amp;nbsp;&lt;/span&gt;resources/read&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 처리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;초기화가 성공적으로 완료되면, 클라이언트는 준비가 완료되었음을 알리기 위해 다음과 같은 알림을 보냅니다:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768548896960&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;method&quot;: &quot;notifications/initialized&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;AI 애플리케이션에서의 동작 방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;초기화 과정 동안, AI 애플리케이션의 MCP 클라이언트 매니저는 설정된 서버들과의 연결을 설정하고, 각 서버의 기능(capabilities)을 저장합니다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;이후 애플리케이션은 이 정보를 활용하여 어떤 서버가 특정 기능(도구, 리소스, 프롬프트 등)을 제공할 수 있는지, 그리고 실시간 업데이트를 지원하는지를 판단합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768548985141&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Pseudo Code
async with stdio_client(server_config) as (read, write):
    async with ClientSession(read, write) as session:
        init_response = await session.initialize()
        if init_response.capabilities.tools:
            app.register_mcp_server(session, supports_tools=True)
        app.set_server_ready(session)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #282c48;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2단계: 도구 검색 (&lt;span style=&quot;background-color: #ffffff; color: #282c48; text-align: start;&quot;&gt;프리미티브&lt;/span&gt;)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;연결이 설정되면, 클라이언트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools/list&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청을 보내 서버가 제공하는 도구 목록을 가져옵니다. 이 요청은 MCP의 도구 검색 메커니즘의 핵심적인 부분으로, 클라이언트가 서버에서 사용 가능한 도구들을 실제로 사용하기 전에 어떤 도구들이 있는지 이해할 수 있도록 해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768782076081&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Tools List Request
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 2,
  &quot;method&quot;: &quot;tools/list&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1768782101046&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Tools List Response
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 2,
  &quot;result&quot;: {
    &quot;tools&quot;: [
      {
        &quot;name&quot;: &quot;calculator_arithmetic&quot;,
        &quot;title&quot;: &quot;Calculator&quot;,
        &quot;description&quot;: &quot;Perform mathematical calculations including basic arithmetic, trigonometric functions, and algebraic operations&quot;,
        &quot;inputSchema&quot;: {
          &quot;type&quot;: &quot;object&quot;,
          &quot;properties&quot;: {
            &quot;expression&quot;: {
              &quot;type&quot;: &quot;string&quot;,
              &quot;description&quot;: &quot;Mathematical expression to evaluate (e.g., '2 + 3 * 4', 'sin(30)', 'sqrt(16)')&quot;
            }
          },
          &quot;required&quot;: [&quot;expression&quot;]
        }
      },
      {
        &quot;name&quot;: &quot;weather_current&quot;,
        &quot;title&quot;: &quot;Weather Information&quot;,
        &quot;description&quot;: &quot;Get current weather information for any location worldwide&quot;,
        &quot;inputSchema&quot;: {
          &quot;type&quot;: &quot;object&quot;,
          &quot;properties&quot;: {
            &quot;location&quot;: {
              &quot;type&quot;: &quot;string&quot;,
              &quot;description&quot;: &quot;City name, address, or coordinates (latitude,longitude)&quot;
            },
            &quot;units&quot;: {
              &quot;type&quot;: &quot;string&quot;,
              &quot;enum&quot;: [&quot;metric&quot;, &quot;imperial&quot;, &quot;kelvin&quot;],
              &quot;description&quot;: &quot;Temperature units to use in response&quot;,
              &quot;default&quot;: &quot;metric&quot;
            }
          },
          &quot;required&quot;: [&quot;location&quot;]
        }
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;도구 검색 요청 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;tools/list&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청은 매우 간단하며, 어떤 매개변수도 포함하지 않습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;도구 검색 응답 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;응답에는 각 사용 가능한 도구에 대한 포괄적인 메타데이터를 제공하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools&lt;span&gt;&amp;nbsp;&lt;/span&gt;배열이 포함되어 있습니다. 이 배열 기반 구조는 서버가 여러 도구를 동시에 노출하면서도 서로 다른 기능 간의 명확한 경계를 유지할 수 있도록 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;응답 내 각 도구 객체에는 다음과 같은 주요 필드가 포함됩니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;name&lt;/b&gt;: 서버 네임스페이스 내에서 도구를 고유하게 식별하는 식별자입니다. 이는 도구 실행의 기본 키 역할을 하며, 명확한 명명 규칙을 따르는 것이 좋습니다. (예: 단순히&lt;span&gt;&amp;nbsp;&lt;/span&gt;calculate가 아닌&lt;span&gt;&amp;nbsp;&lt;/span&gt;calculator_arithmetic)&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;title&lt;/b&gt;: 사용자가 볼 수 있도록 클라이언트가 표시할 수 있는 사람이 읽기 쉬운 도구 이름입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;description&lt;/b&gt;: 도구가 수행하는 작업과 사용 시점을 자세히 설명합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;inputSchema&lt;/b&gt;: 예상되는 입력 매개변수를 정의하는 JSON 스키마로, 타입 검증을 가능하게 하고 필수 및 선택 매개변수에 대한 명확한 문서를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;AI 애플리케이션에서의 작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 애플리케이션은 연결된 모든 MCP 서버로부터 사용 가능한 도구를 가져와 이를 통합된 도구 레지스트리로 결합합니다. 이렇게 하면 언어 모델이 수행할 수 있는 작업을 이해하고, 대화 중에 자동으로 적절한 도구 호출을 생성할 수 있게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768782476061&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Pseudo-code using MCP Python SDK patterns
available_tools = []
for session in app.mcp_server_sessions():
    tools_response = await session.list_tools()
    available_tools.extend(tools_response.tools)
conversation.register_available_tools(available_tools)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #282c48;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3단계: 도구 실행 (Tool Execution)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #282c48;&quot; data-ke-size=&quot;size18&quot;&gt;클라이언트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;tools/call&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청을 통해 도구를 실행할 수 있습니다. &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;이는 MCP의 primitives들이 실제로 어떻게 사용되는지를 보여줍니다. 사용 가능한 도구를 검색한 후, 클라이언트는 적절한 인자를 사용하여 해당 도구를 호출할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;도구 실행 요청 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;tools/call&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청은 클라이언트와 서버 간의 타입 안정성과 명확한 통신을 보장하는 구조화된 형식을 따릅니다. 여기서 중요한 점은 단순화된 이름이 아닌, 검색 응답에서 제공된 올바른 도구 이름(weather_current)을 사용한다는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768782983898&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Tool Call Request
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 3,
  &quot;method&quot;: &quot;tools/call&quot;,
  &quot;params&quot;: {
    &quot;name&quot;: &quot;weather_current&quot;,
    &quot;arguments&quot;: {
      &quot;location&quot;: &quot;San Francisco&quot;,
      &quot;units&quot;: &quot;imperial&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1768782998042&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Tool Call Response
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 3,
  &quot;result&quot;: {
    &quot;content&quot;: [
      {
        &quot;type&quot;: &quot;text&quot;,
        &quot;text&quot;: &quot;Current weather in San Francisco: 68&amp;deg;F, partly cloudy with light winds from the west at 8 mph. Humidity: 65%&quot;
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;도구 실행의 주요 요소&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요청 구조에는 다음과 같은 중요한 구성 요소들이 포함됩니다:&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;name&lt;/b&gt;: 검색 응답에서 제공된 도구 이름(weather_current)과 정확히 일치해야 합니다. 이를 통해 서버는 어떤 도구를 실행해야 하는지 올바르게 식별할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;arguments&lt;/b&gt;: 도구의&lt;span&gt;&amp;nbsp;&lt;/span&gt;inputSchema에 정의된 입력 매개변수를 포함합니다. 예를 들어:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;location: &amp;ldquo;San Francisco&amp;rdquo; (필수 매개변수)&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;units: &amp;ldquo;imperial&amp;rdquo; (선택 매개변수, 지정하지 않으면 기본값은 &amp;ldquo;metric&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;JSON-RPC 구조&lt;/b&gt;: 요청-응답 간의 상관관계를 위해 고유한 ID를 사용하는 표준 JSON-RPC 2.0 형식을 따릅니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;도구 실행 응답 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;응답은 MCP의 유연한 콘텐츠 시스템을 보여줍니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;content 배열&lt;/b&gt;: 도구 응답은 콘텐츠 객체의 배열을 반환하며, 텍스트, 이미지, 리소스 등 다양한 형식의 풍부한 응답을 지원합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;콘텐츠 유형(Content Types)&lt;/b&gt;: 각 콘텐츠 객체에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;type&lt;span&gt;&amp;nbsp;&lt;/span&gt;필드가 있습니다. 예를 들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;type&quot;: &quot;text&quot;는 일반 텍스트 콘텐츠를 의미하지만, MCP는 다양한 사용 사례를 위한 여러 콘텐츠 유형을 지원합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;구조화된 출력(Structured Output)&lt;/b&gt;: 응답은 AI 애플리케이션이 언어 모델 상호작용의 문맥으로 활용할 수 있는 실행 가능한 정보를 제공합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 실행 패턴을 통해 AI 애플리케이션은 서버 기능을 동적으로 호출하고, 언어 모델과의 대화에 통합할 수 있는 구조화된 응답을 받을 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;AI 애플리케이션에서의 작동 방식&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #353a60;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;언어 모델이 대화 중에 도구 사용을 결정하면, AI 애플리케이션은 해당 도구 호출을 가로채어 적절한 MCP 서버로 라우팅하고, 실행한 뒤 결과를 LLM에 다시 전달합니다. 이를 통해 LLM은 실시간 데이터에 접근하고 외부 세계에서 실제 작업을 수행할 수 있게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1768783539699&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Pseudo-code for AI application tool execution
async def handle_tool_call(conversation, tool_name, arguments):
    session = app.find_mcp_session_for_tool(tool_name)
    result = await session.call_tool(tool_name, arguments)
    conversation.add_tool_result(result.content)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #282c48;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #282c48;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4단계: 실시간 업데이트 (알림)&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 서버가 명시적인 요청 없이도 클라이언트에게 변경 사항을 알릴 수 있는 실시간 알림 기능을 지원합니다. 이는 MCP 연결을 동기화하고 반응성을 유지하는 핵심 기능인 알림 시스템을 보여줍니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;도구 목록 변경 알림 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;서버의 사용 가능한 도구가 변경될 때&amp;mdash;예를 들어 새로운 기능이 추가되거나, 기존 도구가 수정되거나, 일시적으로 사용할 수 없게 되는 경우&amp;mdash;서버는 연결된 클라이언트에게 이를 사전에 알릴 수 있습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;pre id=&quot;code_1768783787980&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Request
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;method&quot;: &quot;notifications/tools/list_changed&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;MCP 알림의 주요 기능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;응답 불필요&lt;/b&gt;: 알림에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;id&lt;span&gt;&amp;nbsp;&lt;/span&gt;필드가 없습니다. 이는 JSON-RPC 2.0의 알림 규칙을 따르며, 응답이 기대되거나 전송되지 않음을 의미합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;기능 기반(Capability-Based)&lt;/b&gt;: 이 알림은 초기화 시 도구 기능에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;listChanged&quot;: true로 선언한 서버에서만 전송됩니다(1단계에서 설명된 것처럼).&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;이벤트 기반(Event-Driven)&lt;/b&gt;: 서버는 내부 상태 변경에 따라 알림을 보낼 시점을 결정하므로, MCP 연결은 동적이고 반응적으로 유지됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;클라이언트의 알림 응답&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 알림을 수신하면, 클라이언트는 일반적으로 업데이트된 도구 목록을 요청하여 반응합니다. 이를 통해 클라이언트는 사용 가능한 도구에 대한 최신 정보를 지속적으로 유지할 수 있습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;pre id=&quot;code_1768784037862&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Response
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 4,
  &quot;method&quot;: &quot;tools/list&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;알림이 중요한 이유&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 알림 시스템은 여러 가지 이유로 매우 중요합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;동적 환경(Dynamic Environments)&lt;/b&gt;: 서버 상태, 외부 종속성, 또는 사용자 권한에 따라 도구가 추가되거나 제거될 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;효율성(Efficiency)&lt;/b&gt;: 클라이언트가 변경 사항을 주기적으로 확인(polling)할 필요 없이, 업데이트가 발생할 때 즉시 알림을 받습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;일관성(Consistency)&lt;/b&gt;: 클라이언트가 항상 서버의 사용 가능한 기능에 대한 정확한 정보를 유지하도록 보장합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #282c48;&quot;&gt;&lt;b&gt;실시간 협업(Real-time Collaboration)&lt;/b&gt;: 변화하는 상황에 적응할 수 있는 반응형 AI 애플리케이션을 가능하게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 알림 패턴은 도구뿐만 아니라 다른 MCP 기본 요소에도 확장되어, 클라이언트와 서버 간의 포괄적인 실시간 동기화를 지원합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;AI 애플리케이션에서의 작동 방식&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 애플리케이션이 도구 변경 알림을 받으면, 즉시 도구 레지스트리를 새로 고치고 LLM의 사용 가능한 기능을 업데이트합니다. 이를 통해 진행 중인 대화가 항상 최신 도구 세트에 접근할 수 있으며, LLM은 새 기능이 추가될 때마다 동적으로 적응할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768784069351&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Pseudo-code for AI application notification handling
async def handle_tools_changed_notification(session):
    tools_response = await session.list_tools()
    app.update_available_tools(session, tools_response.tools)
    if app.conversation.is_active():
        app.conversation.notify_llm_of_new_capabilities()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot;&gt;What is the Model Context Protocol (MCP)? - Model Context Protocol&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768537975165&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;What is the Model Context Protocol (MCP)? - Model Context Protocol&quot; data-og-description=&quot;What is the Model Context Protocol (MCP)?&quot; data-og-host=&quot;modelcontextprotocol.io&quot; data-og-source-url=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot; data-og-url=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwZoV5/dJMb81GQHH5/mGEFhdI93WAsQn9FjJ3LD1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/cLVPnc/dJMb82eGy6c/Vkk4XReKAK9zPU1o0tZlXK/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/boMlfK/dJMb8XRY4So/1jd8EhP7y3KNekTHe3L000/img.png?width=3840&amp;amp;height=1500&amp;amp;face=0_0_3840_1500&quot;&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwZoV5/dJMb81GQHH5/mGEFhdI93WAsQn9FjJ3LD1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/cLVPnc/dJMb82eGy6c/Vkk4XReKAK9zPU1o0tZlXK/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/boMlfK/dJMb8XRY4So/1jd8EhP7y3KNekTHe3L000/img.png?width=3840&amp;amp;height=1500&amp;amp;face=0_0_3840_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What is the Model Context Protocol (MCP)? - Model Context Protocol&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What is the Model Context Protocol (MCP)?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;modelcontextprotocol.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MLops</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/43</guid>
      <comments>https://kayath.tistory.com/43#entry43comment</comments>
      <pubDate>Fri, 16 Jan 2026 11:04:18 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 309. Best Time to Buy and Sell Stock with Cooldown</title>
      <link>https://kayath.tistory.com/42</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;You&amp;nbsp;are&amp;nbsp;given&amp;nbsp;an&amp;nbsp;array&amp;nbsp;prices&amp;nbsp;where&amp;nbsp;prices[i]&amp;nbsp;is&amp;nbsp;the&amp;nbsp;price&amp;nbsp;of&amp;nbsp;a&amp;nbsp;given&amp;nbsp;stock&amp;nbsp;on&amp;nbsp;the&amp;nbsp;ith&amp;nbsp;day. &lt;br /&gt;&lt;br /&gt;Find&amp;nbsp;the&amp;nbsp;maximum&amp;nbsp;profit&amp;nbsp;you&amp;nbsp;can&amp;nbsp;achieve.&amp;nbsp;You&amp;nbsp;may&amp;nbsp;complete&amp;nbsp;as&amp;nbsp;many&amp;nbsp;transactions&amp;nbsp;as&amp;nbsp;you&amp;nbsp;like&amp;nbsp;(i.e.,&amp;nbsp;buy&amp;nbsp;one&amp;nbsp;and&amp;nbsp;sell&amp;nbsp;one&amp;nbsp;share&amp;nbsp;of&amp;nbsp;the&amp;nbsp;stock&amp;nbsp;multiple&amp;nbsp;times)&amp;nbsp;with&amp;nbsp;the&amp;nbsp;following&amp;nbsp;restrictions: &lt;br /&gt;&lt;br /&gt;After&amp;nbsp;you&amp;nbsp;sell&amp;nbsp;your&amp;nbsp;stock,&amp;nbsp;you&amp;nbsp;cannot&amp;nbsp;buy&amp;nbsp;stock&amp;nbsp;on&amp;nbsp;the&amp;nbsp;next&amp;nbsp;day&amp;nbsp;(i.e.,&amp;nbsp;cooldown&amp;nbsp;one&amp;nbsp;day). &lt;br /&gt;Note:&amp;nbsp;You&amp;nbsp;may&amp;nbsp;not&amp;nbsp;engage&amp;nbsp;in&amp;nbsp;multiple&amp;nbsp;transactions&amp;nbsp;simultaneously&amp;nbsp;(i.e.,&amp;nbsp;you&amp;nbsp;must&amp;nbsp;sell&amp;nbsp;the&amp;nbsp;stock&amp;nbsp;before&amp;nbsp;you&amp;nbsp;buy&amp;nbsp;again). &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Example 1:&lt;br /&gt;Input:&amp;nbsp;prices&amp;nbsp;=&amp;nbsp;[1,2,3,0,2] &lt;br /&gt;Output:&amp;nbsp;3 &lt;br /&gt;Explanation:&amp;nbsp;transactions&amp;nbsp;=&amp;nbsp;[buy,&amp;nbsp;sell,&amp;nbsp;cooldown,&amp;nbsp;buy,&amp;nbsp;sell]&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Example 2:&lt;br /&gt;Input:&amp;nbsp;prices&amp;nbsp;=&amp;nbsp;[1] &lt;br /&gt;Output:&amp;nbsp;0 &lt;br /&gt;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;Constraints:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;= prices.length &amp;lt;= 5000&lt;/li&gt;
&lt;li&gt;0&amp;nbsp;&amp;lt;=&amp;nbsp;prices[i]&amp;nbsp;&amp;lt;=&amp;nbsp;1000&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Solution1&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1764238510667&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 해답은 맞으나 Input이 길어질 경우 Runtime Error
# Time complextivity: O(2^n)
# Space O(n)
# 예외 케이스: [48,12,60,93,97,42,25,64,17,56,85,93,9,48,52,42,58,85,81,84,69,36,1,54,23,15,72,15,11,94]

class Solution:
    def findProfit(self, i, buy, arr):
        n = len(arr)

        # Base case
        if i &amp;gt;= n:
            return 0

        # If we can buy
        if buy:
            take = -arr[i] + self.findProfit(i + 1, 0, arr)
            skip = self.findProfit(i + 1, 1, arr)
            return max(take, skip)

        # If we can sell
        else:
            sell = arr[i] + self.findProfit(i + 2, 1, arr)
            skip = self.findProfit(i + 1, 0, arr)
            return max(sell, skip)

    def maxProfit(self, prices: List[int]) -&amp;gt; int:
        return self.findProfit(0, 1, prices)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Solution2&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1764239575968&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Input: [48,12,60,93,97,42,25,64,17,56,85,93,9,48,52,42,58,85,81,84,69,36,1,54,23,15,72,15,11,94]
# Output: 428

class Solution:
    def findProfit(self, i, buy, arr, memo):
        n = len(arr)
        # Base case
        if i &amp;gt;= n:
            return 0
        # 이미 계산된 값이 있으면 반환
        if (i, buy) in memo:
            return memo[(i, buy)]
        # If we can buy
        if buy:
            take = -arr[i] + self.findProfit(i + 1, 0, arr, memo)
            skip = self.findProfit(i + 1, 1, arr, memo)
            memo[(i, buy)] = max(take, skip)
        # If we can sell
        else:
            sell = arr[i] + self.findProfit(i + 2, 1, arr, memo)
            skip = self.findProfit(i + 1, 0, arr, memo)
            memo[(i, buy)] = max(sell, skip)
        return memo[(i, buy)]

    def maxProfit(self, prices: list[int]) -&amp;gt; int:
        memo = {}
        return self.findProfit(0, 1, prices, memo)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Solution3&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1764243594647&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Bottom-Up 방식
# memo를 딕셔너리로 선언 후, key 초기화 필요

class Solution:
    def maxProfit(self, prices: List[int]) -&amp;gt; int:
        n = len(prices)
        if n == 0:
            return 0
        memo = {}
        memo[(n, 0)] = 0
        memo[(n, 1)] = 0
        memo[(n+1, 0)] = 0
        memo[(n+1, 1)] = 0

        for i in range(n - 1, -1, -1):
            # When we can buy
            memo[(i, 1)] = max(-prices[i] + memo[(i+1, 0)], memo[(i+1, 1)])
            # When we can sell
            memo[(i, 0)] = max(prices[i] + memo[(i+2, 1)], memo[(i+1, 0)])
        return memo[(0, 1)]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Solution4&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1764244474717&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Bottom-Up 방식
# dp 배열 사용

class Solution:
    def maxProfit(self, prices: List[int]) -&amp;gt; int:
        n = len(prices)
        dp = [[0 for _ in range(2)] for _ in range(n + 2)]

        for i in range(n - 1, -1, -1):
            # When we can buy
            dp[i][1] = max(-prices[i] + dp[i + 1][0], dp[i + 1][1])

            # When we can sell
            dp[i][0] = max(prices[i] + dp[i + 2][1], dp[i + 1][0])

        return dp[0][1]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>leetcode</category>
      <category>DP</category>
      <category>Dynamic Programming</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/42</guid>
      <comments>https://kayath.tistory.com/42#entry42comment</comments>
      <pubDate>Thu, 27 Nov 2025 21:01:07 +0900</pubDate>
    </item>
    <item>
      <title>[Algorithm] DP programming</title>
      <link>https://kayath.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 피보나치 수열을 재귀 함수로 구현&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763604730768&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
import time


def fibo_recur(x):
    if x &amp;lt;= 1:
        return x
    return fibo_recur(x-1)+fibo_recur(x-2)
    
 
 if __name__ == &quot;__main__&quot;:
    print(f&quot;start: {time.time()}&quot;)
    start = time.time()
    print(fibo_recur(40))
    end = time.time()
    print(f&quot;end: {end}, total: {end - start:.6f}초&quot;)


#### 출력값 ####
start: 1763604540.6283398
102334155
end: 1763604565.0303335, total: 24.401973초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 피보나치 수열을 재귀 함수와 메모이제이션 기법 추가(Top-Down)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763604864712&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
import time

sys.setrecursionlimit(10**8)

dp = [0] * 41

def fibo_memo(x):
    if dp[x] != 0:
        return dp[x]
    if x &amp;lt;= 1:
        dp[x] = x
    else:
        dp[x] = fibo_memo(x-1)+fibo_memo(x-2)
    return dp[x]

if __name__ == &quot;__main__&quot;:
    print(f&quot;start: {time.time()}&quot;)
    start = time.time()
    print(fibo_memo(40))
    end = time.time()
    print(f&quot;end: {end}, total: {end - start:.6f}초&quot;)

#### 출력 ####
start: 1763604814.879065
102334155
end: 1763604814.8791049, total: 0.000021초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 피보나치 수열을 반복문으로 구현(Bottom-Up)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763605814693&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
import time

sys.setrecursionlimit(10**8)

def fibo_iter(x):
    dp[0], dp[1] = 0, 1

    for i in range(2, x+1):
        dp[i] = dp[i-1]+dp[i-2]
    return dp[x]
    

if __name__ == &quot;__main__&quot;:
    print(f&quot;start: {time.time()}&quot;)
    start = time.time()
    print(fibo_iter(40))    
    end = time.time()
    print(f&quot;end: {end}, total: {end - start:.6f}초&quot;)

#### 출력 ####
start: 1763605799.6194804
102334155
end: 1763605799.6195118, total: 0.000013초&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/41</guid>
      <comments>https://kayath.tistory.com/41#entry41comment</comments>
      <pubDate>Thu, 20 Nov 2025 11:43:52 +0900</pubDate>
    </item>
    <item>
      <title>[Leetcode] 419. Battleships in a Board</title>
      <link>https://kayath.tistory.com/40</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Given&amp;nbsp;an&amp;nbsp;m&amp;nbsp;x&amp;nbsp;n&amp;nbsp;matrix&amp;nbsp;board&amp;nbsp;where&amp;nbsp;each&amp;nbsp;cell&amp;nbsp;is&amp;nbsp;a&amp;nbsp;battleship&amp;nbsp;'X'&amp;nbsp;or&amp;nbsp;empty&amp;nbsp;'.',&amp;nbsp;return&amp;nbsp;the&amp;nbsp;number&amp;nbsp;of&amp;nbsp;the&amp;nbsp;battleships&amp;nbsp;on&amp;nbsp;board. &lt;br /&gt;&lt;br /&gt;Battleships&amp;nbsp;can&amp;nbsp;only&amp;nbsp;be&amp;nbsp;placed&amp;nbsp;horizontally&amp;nbsp;or&amp;nbsp;vertically&amp;nbsp;on&amp;nbsp;board.&amp;nbsp;In&amp;nbsp;other&amp;nbsp;words,&amp;nbsp;they&amp;nbsp;can&amp;nbsp;only&amp;nbsp;be&amp;nbsp;made&amp;nbsp;of&amp;nbsp;the&amp;nbsp;shape&amp;nbsp;1&amp;nbsp;x&amp;nbsp;k&amp;nbsp;(1&amp;nbsp;row,&amp;nbsp;k&amp;nbsp;columns)&amp;nbsp;or&amp;nbsp;k&amp;nbsp;x&amp;nbsp;1&amp;nbsp;(k&amp;nbsp;rows,&amp;nbsp;1&amp;nbsp;column),&amp;nbsp;where&amp;nbsp;k&amp;nbsp;can&amp;nbsp;be&amp;nbsp;of&amp;nbsp;any&amp;nbsp;size.&amp;nbsp;At&amp;nbsp;least&amp;nbsp;one&amp;nbsp;horizontal&amp;nbsp;or&amp;nbsp;vertical&amp;nbsp;cell&amp;nbsp;separates&amp;nbsp;between&amp;nbsp;two&amp;nbsp;battleships&amp;nbsp;(i.e.,&amp;nbsp;there&amp;nbsp;are&amp;nbsp;no&amp;nbsp;adjacent&amp;nbsp;battleships). &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Example&amp;nbsp;1: &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oGmqD/dJMcacaq71o/kZ6jBKYjFSSwJiCFaKNwXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oGmqD/dJMcacaq71o/kZ6jBKYjFSSwJiCFaKNwXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oGmqD/dJMcacaq71o/kZ6jBKYjFSSwJiCFaKNwXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoGmqD%2FdJMcacaq71o%2FkZ6jBKYjFSSwJiCFaKNwXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;333&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Input:&amp;nbsp;board&amp;nbsp;=&amp;nbsp;[[&quot;X&quot;,&quot;.&quot;,&quot;.&quot;,&quot;X&quot;],[&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;X&quot;],[&quot;.&quot;,&quot;.&quot;,&quot;.&quot;,&quot;X&quot;]] &lt;br /&gt;Output:&amp;nbsp;2 &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Example&amp;nbsp;2: &lt;br /&gt;&lt;br /&gt;Input:&amp;nbsp;board&amp;nbsp;=&amp;nbsp;[[&quot;.&quot;]] &lt;br /&gt;Output:&amp;nbsp;0 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Constraints:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;m == board.length&lt;/li&gt;
&lt;li&gt;n == board[i].length&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= m, n &amp;lt;= 200&lt;/li&gt;
&lt;li&gt;board[i][j]&amp;nbsp;is&amp;nbsp;either&amp;nbsp;'.'&amp;nbsp;or&amp;nbsp;'X'. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solution&lt;/h2&gt;
&lt;pre id=&quot;code_1763391009937&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import List


nx = [1, 0, -1, 0]
ny = [0, 1, 0, -1]
global chk_mat, m, n

class Solution:
    def bfs(self, y, x, board):
        q = []
        q.append((x, y))
        
        while len(q) &amp;gt; 0:
            sx, sy = q.pop()
            # print(&quot;sx: &quot;, sx, &quot;sy: &quot;, sy)
            chk_mat[sy][sx] = True

            if board[sy][sx] == &quot;.&quot;:
                continue            

            for i in range(4):
                x = sx + nx[i]
                y = sy + ny[i]
                # print(&quot;new x: &quot;, x, &quot;new y: &quot;, y)
                if x &amp;lt; 0 or y &amp;gt;= m or y &amp;lt; 0 or x &amp;gt;= n:
                    continue
                if chk_mat[y][x] == False:
                    chk_mat[y][x] = True
                    q.append((x, y))

    def countBattleships(self, board: List[List[str]]) -&amp;gt; int:
        global chk_mat, m, n
        m = len(board) # y축
        n = len(board[0]) # x축
        cnt = 0
        chk_mat = [[False] * n for _ in range(m)]
        # print(&quot;chk_mat: &quot;, chk_mat)
        for y in range(m):
            for x in range(n):
                # print(&quot;y: &quot;, y, &quot;x: &quot;, x)
                if board[y][x] == &quot;.&quot; or chk_mat[y][x] == True:
                    continue
                if board[y][x] == &quot;X&quot;:
                    self.bfs(y, x, board)
                    cnt += 1
        return cnt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/battleships-in-a-board/description/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/battleships-in-a-board/description/&lt;/a&gt;&lt;/p&gt;</description>
      <category>leetcode</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/40</guid>
      <comments>https://kayath.tistory.com/40#entry40comment</comments>
      <pubDate>Tue, 18 Nov 2025 00:07:15 +0900</pubDate>
    </item>
    <item>
      <title>[Python] uv 기본 사용법</title>
      <link>https://kayath.tistory.com/39</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;uv 설치방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linux 환경&lt;/p&gt;
&lt;pre id=&quot;code_1762329060477&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Linux/macOS curl 명령어
curl -LsSf https://astral.sh/uv/install.sh | sh

# Linux/macOS uv 특정 버전 설치
curl -LsSf https://astral.sh/uv/0.8.22/install.sh | sh

# Linux/macOS wget 명령어
wget -qO- https://astral.sh/uv/install.sh | sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;windows 환경&lt;/p&gt;
&lt;pre id=&quot;code_1762329083981&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Windows uv 최신 버전 설치
powershell -ExecutionPolicy ByPass -c &quot;irm https://astral.sh/uv/install.ps1 | iex&quot;

# Windows uv 특정 버전 설치
powershell -ExecutionPolicy ByPass -c &quot;irm https://astral.sh/uv/0.8.22/install.ps1 | iex&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pip &amp;amp; docker 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759410668331&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pip 설치
pip install uv

# homebrew 설치
brew install uv

# docker 설치
docker pull ghcr.io/astral-sh/uv:0.8.22-python3.13-trixie-slim
참고 - https://docs.astral.sh/uv/guides/integration/docker/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;uv 제거&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1759411391498&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# uv 데이터 삭제 (Optional)
uv cache clean
rm -r &quot;$(uv python dir)&quot;
rm -r &quot;$(uv tool dir)&quot;

# Linux/macOS
rm ~/.local/bin/uv ~/.local/bin/uvx

# Windows
rm $HOME\.local\bin\uv.exe
rm $HOME\.local\bin\uvx.exe
rm $HOME\.local\bin\uvw.exe&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;uv 기본 사용법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 설치 버전 확인&lt;/p&gt;
&lt;pre id=&quot;code_1762329574066&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv로 프로젝트 생성&lt;/p&gt;
&lt;pre id=&quot;code_1762328998917&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv init my_project&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 가상환경 생성&lt;/p&gt;
&lt;pre id=&quot;code_1762329325936&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv venv
=&amp;gt; venv 이름으로 가상환경 폴더 생성

uv venv venv_fastmcp
=&amp;gt; venv_fastmcp 이름으로 가상환경 폴더 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 가상환경 활성화&lt;/p&gt;
&lt;pre id=&quot;code_1762329368784&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;source .venv/bin/activate  # macOS/Linux
.venv\Scripts\activate  # Windows&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 가상환경 비활성화&lt;/p&gt;
&lt;pre id=&quot;code_1762329384040&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;deactivate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 패키지 추가&lt;/p&gt;
&lt;pre id=&quot;code_1762329675434&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv add 패키지명&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 패키지 제거&lt;/p&gt;
&lt;pre id=&quot;code_1762333497777&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv remove 패키지명&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv 프로젝트 실행&lt;/p&gt;
&lt;pre id=&quot;code_1762332959718&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv run main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;uv Python 버전 관리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv로 python 설치&lt;/p&gt;
&lt;pre id=&quot;code_1762329605178&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로컬에 파이썬 버전의 latest 버전 설치하기
uv python install 3.10

# 가상환경에 파이썬 버전 설치하기
uv venv --python 3.14.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치된 파이썬 버전 리스트와 사용가능한 파이썬 버전 확인&lt;/p&gt;
&lt;pre id=&quot;code_1762329719731&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv python list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 특정 파이썬 버전 설정&lt;/p&gt;
&lt;pre id=&quot;code_1762330764059&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 아래 명령어를 해야 .python-version 에 사용할 버전이 명시됨
# 그렇지 않으면 기존 시스템에서 사용하던 파이썬 버전을 사용함
uv python pin 3.13&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.astral.sh/uv/guides/projects/#running-commands&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.astral.sh/uv/guides/projects/#running-commands&lt;/a&gt;&lt;/p&gt;</description>
      <category>Python</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/39</guid>
      <comments>https://kayath.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 5 Nov 2025 17:20:33 +0900</pubDate>
    </item>
    <item>
      <title>[Python] redis-py 설치 및 사용법</title>
      <link>https://kayath.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 dictionary 기반의 인메모리 오픈소스 데이터베이스다. 이 글에서 주로 설명하는 것은 파이썬 기반의 redis-py client 모듈을 설치해서 redis 사용방법을 알아볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 링크에서 Redis 서버를 설치가 전제되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis 데이터베이스 서버 설치&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 운영 환경마다 설치하는 명령어는 아래 링크를 참고하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 도커에 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 리눅스 OS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 맥 OS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Window OS (도커 설치 필요, 도커 환경에 설치와 동일)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Redis client 설치(v 6.4.0)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1760341930777&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install redis

# C로 컴파일된 파서를 사용해 빠른 파싱으로 성능이 좋음
pip install redis[hiredis]&lt;/code&gt;&lt;/pre&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터&amp;nbsp;말씀드리면,&amp;nbsp;`hiredis`는&amp;nbsp;C언어로&amp;nbsp;작성된&amp;nbsp;고성능&amp;nbsp;파서(Parser)이고,&amp;nbsp;`redis-py`의&amp;nbsp;기본&amp;nbsp;파서는&amp;nbsp;순수&amp;nbsp;Python으로&amp;nbsp;작성되었기&amp;nbsp;때문입니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;조금&amp;nbsp;더&amp;nbsp;자세히&amp;nbsp;설명해&amp;nbsp;드릴게요. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;언어의&amp;nbsp;근본적인&amp;nbsp;차이:&amp;nbsp;C&amp;nbsp;(컴파일&amp;nbsp;언어)&amp;nbsp;vs.&amp;nbsp;Python&amp;nbsp;(인터프리터&amp;nbsp;언어) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Redis&amp;nbsp;클라이언트와&amp;nbsp;서버는&amp;nbsp;특정&amp;nbsp;프로토콜(RESP:&amp;nbsp;REdis&amp;nbsp;Serialization&amp;nbsp;Protocol)을&amp;nbsp;통해&amp;nbsp;통신합니다.&amp;nbsp;클라이언트가&amp;nbsp;&quot;GET&amp;nbsp;mykey&quot;&amp;nbsp;같은&amp;nbsp;명령을&amp;nbsp;보내면,&amp;nbsp;Redis&amp;nbsp;서버는&amp;nbsp;&quot;$3\r\nbar\r\n&quot;와&amp;nbsp;같은&amp;nbsp;형식의&amp;nbsp;응답을&amp;nbsp;보냅니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;이&amp;nbsp;응답을&amp;nbsp;받아서&amp;nbsp;파이썬&amp;nbsp;객체(문자열,&amp;nbsp;리스트&amp;nbsp;등)로&amp;nbsp;변환하는&amp;nbsp;과정을&amp;nbsp;파싱(Parsing)이라고&amp;nbsp;합니다.&amp;nbsp;hiredis는&amp;nbsp;바로&amp;nbsp;이&amp;nbsp;파싱&amp;nbsp;작업을&amp;nbsp;처리하는&amp;nbsp;라이브러리입니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;hiredis&amp;nbsp;(C&amp;nbsp;언어): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;컴파일&amp;nbsp;언어:&amp;nbsp;코드가&amp;nbsp;실행되기&amp;nbsp;전에&amp;nbsp;컴퓨터가&amp;nbsp;직접&amp;nbsp;이해할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;기계어&amp;nbsp;코드로&amp;nbsp;번역(컴파일)됩니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;빠른&amp;nbsp;실행:&amp;nbsp;중간&amp;nbsp;과정&amp;nbsp;없이&amp;nbsp;CPU에서&amp;nbsp;바로&amp;nbsp;실행되므로&amp;nbsp;매우&amp;nbsp;빠릅니다.&amp;nbsp;특히&amp;nbsp;반복적이고&amp;nbsp;낮은&amp;nbsp;수준의&amp;nbsp;작업(바이트&amp;nbsp;스트림&amp;nbsp;처리&amp;nbsp;등)에서&amp;nbsp;압도적인&amp;nbsp;성능을&amp;nbsp;보입니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;메모리&amp;nbsp;효율:&amp;nbsp;메모리를&amp;nbsp;직접&amp;nbsp;제어하여&amp;nbsp;오버헤드가&amp;nbsp;적습니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;기본&amp;nbsp;파서&amp;nbsp;(Python&amp;nbsp;언어): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;인터프리터&amp;nbsp;언어:&amp;nbsp;코드를&amp;nbsp;한&amp;nbsp;줄씩&amp;nbsp;읽어&amp;nbsp;들여&amp;nbsp;해석(인터프리팅)하면서&amp;nbsp;실행합니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;실행&amp;nbsp;오버헤드:&amp;nbsp;코드를&amp;nbsp;해석하는&amp;nbsp;과정&amp;nbsp;자체가&amp;nbsp;추가적인&amp;nbsp;CPU&amp;nbsp;작업을&amp;nbsp;요구하므로&amp;nbsp;C보다&amp;nbsp;느립니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;편의성:&amp;nbsp;개발이&amp;nbsp;쉽고&amp;nbsp;유연하지만,&amp;nbsp;성능&amp;nbsp;면에서는&amp;nbsp;손해를&amp;nbsp;봅니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;비유하자면,&amp;nbsp;C로&amp;nbsp;작성된&amp;nbsp;hiredis는&amp;nbsp;원어민&amp;nbsp;통역사와&amp;nbsp;같습니다.&amp;nbsp;말을&amp;nbsp;듣는&amp;nbsp;즉시&amp;nbsp;바로&amp;nbsp;이해하고&amp;nbsp;전달할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;반면,&amp;nbsp;Python으로&amp;nbsp;작성된&amp;nbsp;기본&amp;nbsp;파서는&amp;nbsp;사전을&amp;nbsp;계속&amp;nbsp;찾아봐야&amp;nbsp;하는&amp;nbsp;번역가와&amp;nbsp;같습니다.&amp;nbsp;각&amp;nbsp;단어(데이터&amp;nbsp;조각)를&amp;nbsp;해석하는&amp;nbsp;데 &lt;br /&gt;&amp;nbsp;&amp;nbsp;시간이&amp;nbsp;더&amp;nbsp;걸립니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;성능&amp;nbsp;차이가&amp;nbsp;두드러지는&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;이러한&amp;nbsp;성능&amp;nbsp;차이는&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;상황에서&amp;nbsp;특히&amp;nbsp;중요해집니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;많은&amp;nbsp;수의&amp;nbsp;작은&amp;nbsp;응답을&amp;nbsp;처리할&amp;nbsp;때:&amp;nbsp;초당&amp;nbsp;수만&amp;nbsp;건의&amp;nbsp;간단한&amp;nbsp;GET/SET&amp;nbsp;요청을&amp;nbsp;처리한다면,&amp;nbsp;각&amp;nbsp;응답을&amp;nbsp;파싱하는&amp;nbsp;데&amp;nbsp;드는&amp;nbsp;작은&amp;nbsp;시간&amp;nbsp;차이가&amp;nbsp;누적되어&amp;nbsp;전체&amp;nbsp;처리량(Throughput)에&amp;nbsp;큰&amp;nbsp;영향을&amp;nbsp;미칩니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;크고&amp;nbsp;복잡한&amp;nbsp;응답을&amp;nbsp;처리할&amp;nbsp;때:&amp;nbsp;Redis에서&amp;nbsp;매우&amp;nbsp;큰&amp;nbsp;리스트(List)나&amp;nbsp;해시(Hash)를&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;가져올&amp;nbsp;때,&amp;nbsp;파싱해야&amp;nbsp;할&amp;nbsp;데이터의&amp;nbsp;양이&amp;nbsp;많아집니다.&amp;nbsp;이&amp;nbsp;경우&amp;nbsp;C로&amp;nbsp;구현된&amp;nbsp;hiredis의&amp;nbsp;빠른&amp;nbsp;처리&amp;nbsp;속도가&amp;nbsp;큰&amp;nbsp;이점을&amp;nbsp;가집니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;redis-py와&amp;nbsp;hiredis의&amp;nbsp;관계 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;redis-py&amp;nbsp;라이브러리는&amp;nbsp;이&amp;nbsp;점을&amp;nbsp;잘&amp;nbsp;알고&amp;nbsp;있어서,&amp;nbsp;사용자의&amp;nbsp;파이썬&amp;nbsp;환경에&amp;nbsp;`hiredis`가&amp;nbsp;설치되어&amp;nbsp;있으면&amp;nbsp;자동으로&amp;nbsp;`hiredis`를&amp;nbsp;파서로&amp;nbsp;사용합니다.&amp;nbsp;만약&amp;nbsp;설치되어&amp;nbsp;있지&amp;nbsp;않으면&amp;nbsp;순수&amp;nbsp;파이썬으로&amp;nbsp;구현된&amp;nbsp;기본&amp;nbsp;파서를&amp;nbsp;사용합니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;따라서&amp;nbsp;redis-py&amp;nbsp;사용자라면&amp;nbsp;코드&amp;nbsp;수정&amp;nbsp;없이&amp;nbsp;hiredis를&amp;nbsp;설치하는&amp;nbsp;것만으로&amp;nbsp;간단하게&amp;nbsp;성능&amp;nbsp;향상&amp;nbsp;효과를&amp;nbsp;누릴&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#&amp;nbsp;hiredis&amp;nbsp;설치&amp;nbsp;(이것만&amp;nbsp;추가하면&amp;nbsp;redis-py가&amp;nbsp;자동으로&amp;nbsp;사용) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;hiredis&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Redis 서버에 연결 및 확인&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1760346768077&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 연결확인
r.ping() # True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 및 추출&lt;/p&gt;
&lt;pre id=&quot;code_1760347067949&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;r.set('foo', 'bar') # True
r.get('foo') # bar&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 명령어&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- hset(name,&amp;nbsp;key=None,&amp;nbsp;value=None,&amp;nbsp;mapping=None,&amp;nbsp;items=None)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 설명&lt;/b&gt;: hash 이름에 키, 값을 세팅하고 딕셔너리 형태도 hash 이름에 맵핑할 수 있다. items 파라미터는 딕셔너리로 이루어진 리스트도 hash 이름에 맵핑할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 리턴값&lt;/b&gt;: field가 새로 추가되는 정수값을 리턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760405030179&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;res1 = r.hset(&quot;myhash&quot;, &quot;field1&quot;, &quot;Hello&quot;)
print(res1)
# &amp;gt;&amp;gt;&amp;gt; 1 (필드 field1이 1개 들어감)

res2 = r.hget(&quot;myhash&quot;, &quot;field1&quot;)
print(res2)
# &amp;gt;&amp;gt;&amp;gt; Hello

res3 = r.hset(&quot;myhash&quot;, mapping={&quot;field2&quot;: &quot;Hi&quot;, &quot;field3&quot;: &quot;World&quot;})
print(res3)
# &amp;gt;&amp;gt;&amp;gt; 2 (필드 field2, field3 2개 들어감)

res4 = r.hget(&quot;myhash&quot;, &quot;field2&quot;)
print(res4)
# &amp;gt;&amp;gt;&amp;gt; Hi

res5 = r.hget(&quot;myhash&quot;, &quot;field3&quot;)
print(res5)
# &amp;gt;&amp;gt;&amp;gt; World

res6 = r.hgetall(&quot;myhash&quot;)
print(res6)
# &amp;gt;&amp;gt;&amp;gt; { &quot;field1&quot;: &quot;Hello&quot;, &quot;field2&quot;: &quot;Hi&quot;, &quot;field3&quot;: &quot;World&quot; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- hget(name, key)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 설명&lt;/b&gt;: hash 이름에 맵핑되어 있는 key값으로 value값을 가져옴&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 리턴값&lt;/b&gt;: hash 이름에 맵핑되어 있는 key값으로 value값 or None&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760409308381&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;res7 = r.hset(&quot;myhash&quot;, &quot;field1&quot;, &quot;foo&quot;)
print(res7)
# &amp;gt;&amp;gt;&amp;gt; 1

res8 = r.hget(&quot;myhash&quot;, &quot;field1&quot;)
print(res8)
# &amp;gt;&amp;gt;&amp;gt; foo

res9 = r.hget(&quot;myhash&quot;, &quot;field2&quot;)
print(res9)
# &amp;gt;&amp;gt;&amp;gt; None&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- hdel(name, *keys)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 설명:&lt;/b&gt; hash 이름에 맵핑되어 있는 key를 통해 필드(key, value)를 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 리턴값:&lt;/b&gt; 정수값(hash 이름에 맵핑되어 있는 삭제 할 필드의 수)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760418246739&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;hdel1 = r.hset(&quot;myhash&quot;, &quot;field1&quot;, &quot;foo&quot;)
print(hdel1)
# &amp;gt;&amp;gt;&amp;gt; 1

hdel2 = r.hdel(&quot;myhash&quot;, &quot;field1&quot;)
print(hdel2)
# &amp;gt;&amp;gt;&amp;gt; 1

hdel3 = r.hdel(&quot;myhash&quot;, &quot;field2&quot;)
print(hdel3)
# &amp;gt;&amp;gt;&amp;gt; 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- xadd(name, fields, id='*', maxlen=None, approximate=True, nomkstream=False, minid=None, limit=None, ref_policy=None)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 설명:&amp;nbsp;&lt;/b&gt;스트림에 추가한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 파라미터&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;name &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;(&lt;/span&gt;Union[bytes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;str,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memoryview]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;/span&gt; : 스트림 이름&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;fields &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;(&lt;/span&gt;Dict[Union[bytes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;bytearray,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memoryview,&lt;span&gt;&amp;nbsp;&lt;/span&gt;str,&lt;span&gt;&amp;nbsp;&lt;/span&gt;int,&lt;span&gt;&amp;nbsp;&lt;/span&gt;float],&lt;span&gt;&amp;nbsp;&lt;/span&gt;Union[bytes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;bytearray,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memoryview,&lt;span&gt;&amp;nbsp;&lt;/span&gt;str,&lt;span&gt;&amp;nbsp;&lt;/span&gt;int,&lt;span&gt;&amp;nbsp;&lt;/span&gt;float]]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;/span&gt; : 스트림에 삽입되는 field/value 쌍으로 이루어진 딕셔너리&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;id &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;(&lt;/span&gt;Union[int,&lt;span&gt;&amp;nbsp;&lt;/span&gt;bytes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;str,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memoryview]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;/span&gt; : 기록이 삽입될 위치(기본적으로 추가됨)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;maxlen &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;(&lt;/span&gt;Optional[int]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;/span&gt; : 해당 사이즈를 넘는 멤버는 잘라냄&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;approximate &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;(&lt;/span&gt;bool&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;span&gt; &lt;/span&gt;&lt;/span&gt;: 실제 스트림 크기는 maxlen 크기보다 조금 클 수 있음&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;nomkstream &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;(&lt;/span&gt;bool&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;/span&gt; : true로 설정하면 스트림 minid를 생성하지 않음&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;minid&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;/span&gt;Optional[Union[int,&lt;span&gt;&amp;nbsp;&lt;/span&gt;bytes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;str,&lt;span&gt;&amp;nbsp;&lt;/span&gt;memoryview]]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;) : &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;조회할 스트림의 최소 id, &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;maxlen과 함께 지정할 수 없음&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;limit&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;/span&gt;Optional[int]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;) : &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;가져올 최대 항목 수를 지정&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ref_policy&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;/span&gt;Optional[Literal['KEEPREF',&lt;span&gt;&amp;nbsp;&lt;/span&gt;'DELREF',&lt;span&gt;&amp;nbsp;&lt;/span&gt;'ACKED']]&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;)&lt;span&gt; : &lt;span style=&quot;background-color: #ffffff; color: #353a60; text-align: start;&quot;&gt;트리밍 시 컨슈머 그룹을 위한 참조 정책&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #f8f9fb; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KEEPREF (default): When trimming, preserves references in consumer groups&amp;rsquo; PEL&lt;/li&gt;
&lt;li&gt;DELREF: When trimming, removes all references from consumer groups&amp;rsquo; PEL&lt;/li&gt;
&lt;li&gt;ACKED: When trimming, only removes entries acknowledged by all consumer groups&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 리턴값&lt;/b&gt;: 스트림에 멤버가 들어갈 때, id값(자동 생성)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760427518258&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# XADD 명령: mystream에 데이터 추가
id1 = r.xadd(&quot;mystream&quot;, {&quot;name&quot;: &quot;Sara&quot;, &quot;surname&quot;: &quot;OConnor&quot;})
print(&quot;첫 번째 ID:&quot;, id1)
# 첫 번째 ID:1760422182743-0

id2 = r.xadd(&quot;mystream&quot;, {&quot;field1&quot;: &quot;value1&quot;, &quot;field2&quot;: &quot;value2&quot;, &quot;field3&quot;: &quot;value3&quot;})
print(&quot;두 번째 ID:&quot;, id2)
# 두 번째 ID:1760422182743-1

# XLEN 명령: 스트림 길이 확인
length = r.xlen(&quot;mystream&quot;)
print(&quot;스트림 길이:&quot;, length)
# 스트림 길이:2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 명령어 성능 비교&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SET VS HSET 비교&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 117px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.124%; height: 20px; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 31.7054%; height: 20px; text-align: center;&quot;&gt;SET&lt;/td&gt;
&lt;td style=&quot;width: 52.1705%; height: 20px; text-align: center;&quot;&gt;HSET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.124%; height: 17px;&quot;&gt;데이터 구조&lt;/td&gt;
&lt;td style=&quot;width: 31.7054%; height: 17px;&quot;&gt;단순&amp;nbsp;Key-Value&lt;/td&gt;
&lt;td style=&quot;width: 52.1705%; height: 17px;&quot;&gt;&amp;nbsp;Key&amp;nbsp;안에&amp;nbsp;여러&amp;nbsp;Field-Value&amp;nbsp;쌍&amp;nbsp;(객체&amp;nbsp;구조)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.124%; height: 20px;&quot;&gt;값의 종류&lt;/td&gt;
&lt;td style=&quot;width: 31.7054%; height: 20px;&quot;&gt;문자열,&amp;nbsp;숫자&amp;nbsp;등&amp;nbsp;단일&amp;nbsp;값&lt;/td&gt;
&lt;td style=&quot;width: 52.1705%; height: 20px;&quot;&gt;여러&amp;nbsp;필드와&amp;nbsp;값으로&amp;nbsp;구성된&amp;nbsp;해시맵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.124%; height: 20px;&quot;&gt;주요&amp;nbsp;사용&amp;nbsp;목적&lt;/td&gt;
&lt;td style=&quot;width: 31.7054%; height: 20px;&quot;&gt;단일&amp;nbsp;정보&amp;nbsp;저장&amp;nbsp;(캐시,&amp;nbsp;세션&amp;nbsp;ID&amp;nbsp;등)&lt;/td&gt;
&lt;td style=&quot;width: 52.1705%; height: 20px;&quot;&gt;구조화된&amp;nbsp;객체&amp;nbsp;데이터&amp;nbsp;저장&amp;nbsp;(사용자&amp;nbsp;프로필,&amp;nbsp;상품&amp;nbsp;정보&amp;nbsp;등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.124%; height: 20px;&quot;&gt;업데이트&amp;nbsp;방식&lt;/td&gt;
&lt;td style=&quot;width: 31.7054%; height: 20px;&quot;&gt;전체&amp;nbsp;값을&amp;nbsp;통째로&amp;nbsp;덮어써야&amp;nbsp;함&lt;/td&gt;
&lt;td style=&quot;width: 52.1705%; height: 20px;&quot;&gt;특정&amp;nbsp;필드만&amp;nbsp;개별적으로&amp;nbsp;수정&amp;nbsp;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 16.124%; height: 20px;&quot;&gt;관련&amp;nbsp;명령어&lt;/td&gt;
&lt;td style=&quot;width: 31.7054%; height: 20px;&quot;&gt;GET,&amp;nbsp;INCR,&amp;nbsp;DECR,&amp;nbsp;MSET&lt;/td&gt;
&lt;td style=&quot;width: 52.1705%; height: 20px;&quot;&gt;HGET,&amp;nbsp;HGETALL,&amp;nbsp;HINCRBY,&amp;nbsp;HMSET&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SET을 사용하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 정보만 저장하면 될 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 페이지 전체 HTML 캐시: SET cache:page:home &quot;&amp;lt;html&amp;gt;...&quot;&lt;/li&gt;
&lt;li&gt;사용자 세션 ID: SET session:xyz &quot;user_id:100&quot;&lt;/li&gt;
&lt;li&gt;단순 카운터: INCR page:view_count (SET 기반 명령어)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HSET을 사용하는 경우: (대부분의 경우 더 좋은 선택)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체나 구조화된 데이터를 표현할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 정보: user:100 키 안에 name, email, age, last_login 필드 저장&lt;/li&gt;
&lt;li&gt;상품 정보: product:50 키 안에 name, price, stock_quantity 필드 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터의 일부만 자주 변경해야 할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HSET의 가장 큰 장점입니다. 예를 들어, 사용자 정보에서 last_login 필드만 업데이트하고 싶을 때, HSET user:100 last_login &quot;2025-09-24&quot; 명령어 하나로 끝낼 수 있습니다.&lt;/li&gt;
&lt;li&gt;만약 SET으로 모든 사용자 정보를 JSON 문자열로 저장했다면(SET user:100 '{&quot;name&quot;: &quot;Alice&quot;, ...}') 이 값을 업데이트하기 위해 다음과 같은 비효율적인 과정을 거쳐야 합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. GET으로 전체 JSON 문자열을 가져온다.&lt;/li&gt;
&lt;li&gt;2. 애플리케이션에서 JSON을 파싱한다.&lt;/li&gt;
&lt;li&gt;3. last_login 값을 변경한다.&lt;/li&gt;
&lt;li&gt;4. 다시 JSON 문자열로 변환한다.&lt;/li&gt;
&lt;li&gt;5. SET으로 전체 문자열을 다시 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능 및 메모리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 효율성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HSET은 적은 수의 필드를 가질 때 ziplist(최신 버전에서는 listpack)라는 특별한 방식으로 데이터를 압축하여 저장합니다. 따라서 여러 개의 SET 키를 만드는 것보다 하나의 `HSET` 키를 사용하는 것이 메모리를 훨씬 적게 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;성능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서 설명했듯이, 객체의 일부만 수정할 때 HSET이 GET -&amp;gt; 수정 -&amp;gt; SET 과정보다 훨씬 빠르고 원자적(atomic)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; ziplist와 listpack 비교&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ziplist와 listpack은 Redis의 내부 동작과 성능을 이해하는 데 매우 중요한 개념입니다.&lt;br /&gt;결론부터 말씀드리면, `listpack`은 `ziplist`의 치명적인 단점인 '연쇄 업데이트(Cascading Updates)' 문제를 해결하기 위해 등장한 새로운 데이터 구조입니다. 현재 최신 Redis 버전에서는 ziplist 대신 listpack을 기본으로 사용합니다.&lt;br /&gt;&lt;br /&gt;둘의 공통점은 메모리 절약입니다. 작은 크기의 Hash, List, Zset(Sorted Set) 데이터를 저장할 때, 일반적인 해시테이블이나 링크드 리스트보다 훨씬 적은 메모리를 사용하기 위해 고안된 특별한 데이터 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;ziplist란?&amp;nbsp;(구형&amp;nbsp;방식) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ziplist는&amp;nbsp;이름처럼&amp;nbsp;리스트와&amp;nbsp;비슷하지만,&amp;nbsp;실제로는&amp;nbsp;포인터를&amp;nbsp;사용하지&amp;nbsp;않고&amp;nbsp;하나의&amp;nbsp;연속된&amp;nbsp;메모리&amp;nbsp;블록에&amp;nbsp;모든&amp;nbsp;데이터를&amp;nbsp;압축해서&amp;nbsp;저장하는&amp;nbsp;구조입니다.&amp;nbsp;각&amp;nbsp;데이터&amp;nbsp;항목(entry)은&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;정보를&amp;nbsp;가집니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;previous_entry_length:&amp;nbsp;이전&amp;nbsp;항목의&amp;nbsp;길이를&amp;nbsp;저장.&amp;nbsp;이&amp;nbsp;값&amp;nbsp;덕분에&amp;nbsp;리스트의&amp;nbsp;끝에서부터&amp;nbsp;거꾸로&amp;nbsp;탐색하는&amp;nbsp;것이&amp;nbsp;가능합니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;encoding:&amp;nbsp;현재&amp;nbsp;항목의&amp;nbsp;데이터&amp;nbsp;타입과&amp;nbsp;길이에&amp;nbsp;대한&amp;nbsp;정보. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;content:&amp;nbsp;실제&amp;nbsp;데이터. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ziplist의 치명적인 문제: 연쇄 업데이트 (Cascading Updates)&lt;br /&gt;&amp;nbsp;&amp;nbsp;ziplist의&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;문제는&amp;nbsp;중간에&amp;nbsp;있는&amp;nbsp;데이터가&amp;nbsp;변경될&amp;nbsp;때&amp;nbsp;발생합니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;ziplist&amp;nbsp;안에&amp;nbsp;여러&amp;nbsp;개의&amp;nbsp;작은&amp;nbsp;항목(A,&amp;nbsp;B,&amp;nbsp;C,&amp;nbsp;D...)이&amp;nbsp;저장되어&amp;nbsp;있다고&amp;nbsp;가정해&amp;nbsp;봅시다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;항목&amp;nbsp;C는&amp;nbsp;자신의&amp;nbsp;데이터&amp;nbsp;바로&amp;nbsp;앞에&amp;nbsp;&quot;이전&amp;nbsp;항목&amp;nbsp;B의&amp;nbsp;길이&quot;&amp;nbsp;정보를&amp;nbsp;1바이트로&amp;nbsp;저장하고&amp;nbsp;있었습니다.&amp;nbsp;(예:&amp;nbsp;B의&amp;nbsp;길이가&amp;nbsp;60바이트라서&amp;nbsp;1바이트로&amp;nbsp;표현&amp;nbsp;가능) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;그런데&amp;nbsp;항목&amp;nbsp;B의&amp;nbsp;데이터가&amp;nbsp;업데이트되어&amp;nbsp;길이가&amp;nbsp;300바이트로&amp;nbsp;늘어났습니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;4.&amp;nbsp;이제&amp;nbsp;항목&amp;nbsp;C는&amp;nbsp;&quot;이전&amp;nbsp;항목&amp;nbsp;B의&amp;nbsp;길이&quot;를&amp;nbsp;더&amp;nbsp;이상&amp;nbsp;1바이트로&amp;nbsp;표현할&amp;nbsp;수&amp;nbsp;없고,&amp;nbsp;5바이트(길이&amp;nbsp;정보&amp;nbsp;인코딩용&amp;nbsp;1바이트&amp;nbsp;+&amp;nbsp;길이&amp;nbsp;4바이트)로&amp;nbsp;늘려서&amp;nbsp;저장해야&amp;nbsp;합니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;5.&amp;nbsp;그&amp;nbsp;결과,&amp;nbsp;항목&amp;nbsp;C&amp;nbsp;자체의&amp;nbsp;전체&amp;nbsp;길이가&amp;nbsp;변경됩니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;6.&amp;nbsp;항목&amp;nbsp;C의&amp;nbsp;길이가&amp;nbsp;바뀌었으니,&amp;nbsp;다음&amp;nbsp;항목인&amp;nbsp;D에&amp;nbsp;저장된&amp;nbsp;&quot;이전&amp;nbsp;항목&amp;nbsp;C의&amp;nbsp;길이&quot;&amp;nbsp;정보도&amp;nbsp;변경되어야&amp;nbsp;합니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;7.&amp;nbsp;이런&amp;nbsp;식으로&amp;nbsp;하나의&amp;nbsp;항목&amp;nbsp;변경이&amp;nbsp;뒤따라오는&amp;nbsp;모든&amp;nbsp;항목의&amp;nbsp;길이&amp;nbsp;정보를&amp;nbsp;연쇄적으로&amp;nbsp;변경시키고,&amp;nbsp;최악의&amp;nbsp;경우&amp;nbsp;모든&amp;nbsp;데이터의&amp;nbsp;재할당&amp;nbsp;및&amp;nbsp;복사가&amp;nbsp;필요하게&amp;nbsp;됩니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;이&amp;nbsp;연쇄&amp;nbsp;업데이트는&amp;nbsp;시간&amp;nbsp;복잡도가&amp;nbsp;O(N&amp;sup2;)에&amp;nbsp;달할&amp;nbsp;수&amp;nbsp;있어,&amp;nbsp;Redis가&amp;nbsp;순간적으로&amp;nbsp;멈칫하는&amp;nbsp;지연&amp;nbsp;시간(Latency)&amp;nbsp;급증의&amp;nbsp;원인이&amp;nbsp;되었습니다. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;listpack이란?&amp;nbsp;(신형&amp;nbsp;방식) &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;listpack은&amp;nbsp;ziplist의&amp;nbsp;연쇄&amp;nbsp;업데이트&amp;nbsp;문제를&amp;nbsp;해결하기&amp;nbsp;위해&amp;nbsp;설계된&amp;nbsp;새로운&amp;nbsp;데이터&amp;nbsp;구조입니다.&amp;nbsp;ziplist와&amp;nbsp;마찬가지로&amp;nbsp;하나의&amp;nbsp;연속된&amp;nbsp;메모리&amp;nbsp;블록을&amp;nbsp;사용하지만,&amp;nbsp;각&amp;nbsp;항목의&amp;nbsp;구조가&amp;nbsp;다릅니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;encoding:&amp;nbsp;데이터&amp;nbsp;타입과&amp;nbsp;길이에&amp;nbsp;대한&amp;nbsp;정보. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;content:&amp;nbsp;실제&amp;nbsp;데이터. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;entry-length:&amp;nbsp;자기&amp;nbsp;자신의&amp;nbsp;전체&amp;nbsp;길이를&amp;nbsp;저장. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;listpack이 문제를 해결하는 방법&lt;br /&gt;listpack의 핵심 아이디어는 &quot;이전 항목의 길이&quot;를 저장하지 않는 것입니다. 대신 &quot;자기 자신의 길이&quot;를 각 항목의 끝에 저장합니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;업데이트:&amp;nbsp;항목&amp;nbsp;B의&amp;nbsp;데이터가&amp;nbsp;변경되어도,&amp;nbsp;그&amp;nbsp;변경은&amp;nbsp;항목&amp;nbsp;B&amp;nbsp;내에서만&amp;nbsp;처리됩니다.&amp;nbsp;뒤따라오는&amp;nbsp;항목&amp;nbsp;C,&amp;nbsp;D는&amp;nbsp;아무런&amp;nbsp;영향을&amp;nbsp;받지&amp;nbsp;않습니다.&amp;nbsp;연쇄&amp;nbsp;업데이트가&amp;nbsp;원천적으로&amp;nbsp;발생하지&amp;nbsp;않습니다. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;* 역방향 탐색: 리스트의 끝에서 이전 항목으로 이동하려면, 현재 항목의 끝에 저장된 &quot;자기 자신의 길이&quot; 정보를 읽고 그 길이만큼 메모리 포인터를 뒤로 이동하면 됩니다. ziplist보다 약간의 계산이 더 필요하지만, 연쇄 업데이트라는 큰&amp;nbsp;문제를&amp;nbsp;해결하는&amp;nbsp;대가로는&amp;nbsp;사소한&amp;nbsp;비용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&amp;nbsp;언제&amp;nbsp;압축된&amp;nbsp;구조를&amp;nbsp;사용하는가? &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Redis는&amp;nbsp;데이터가&amp;nbsp;특정&amp;nbsp;조건을&amp;nbsp;만족할&amp;nbsp;때만&amp;nbsp;ziplist나&amp;nbsp;listpack&amp;nbsp;같은&amp;nbsp;압축&amp;nbsp;구조를&amp;nbsp;사용합니다.&amp;nbsp;그&amp;nbsp;조건을&amp;nbsp;벗어나면&amp;nbsp;일반적인&amp;nbsp;데이터&amp;nbsp;구조(예:&amp;nbsp;해시테이블)로&amp;nbsp;자동&amp;nbsp;변환됩니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;이 조건은 redis.conf 설정 파일에서 제어하며, 아래 두 조건 중 하나라도 초과하면 압축이 해제됩니다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;항목의&amp;nbsp;개수:&amp;nbsp;해시나&amp;nbsp;리스트에&amp;nbsp;들어있는&amp;nbsp;필드&amp;nbsp;또는&amp;nbsp;요소의&amp;nbsp;개수 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;각&amp;nbsp;항목의&amp;nbsp;크기:&amp;nbsp;필드나&amp;nbsp;요소&amp;nbsp;중&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;값의&amp;nbsp;바이트&amp;nbsp;크기 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;설정&amp;nbsp;예시&amp;nbsp;(`redis.conf`): &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;Hash&amp;nbsp;타입의&amp;nbsp;경우: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;hash-max-listpack-entries&amp;nbsp;256&amp;nbsp;(기본값은&amp;nbsp;512였으나&amp;nbsp;최근&amp;nbsp;변경) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;hash-max-listpack-value&amp;nbsp;64 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;=&amp;gt;&amp;nbsp;해시의&amp;nbsp;필드&amp;nbsp;개수가&amp;nbsp;256개를&amp;nbsp;초과하거나,&amp;nbsp;필드&amp;nbsp;값&amp;nbsp;중&amp;nbsp;64바이트를&amp;nbsp;초과하는&amp;nbsp;것이&amp;nbsp;하나라도&amp;nbsp;있으면&amp;nbsp;일반&amp;nbsp;해시테이블로&amp;nbsp;변환됩니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;List&amp;nbsp;타입의&amp;nbsp;경우: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;list-max-listpack-entries&amp;nbsp;256 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;list-max-listpack-value&amp;nbsp;64 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;=&amp;gt;&amp;nbsp;리스트의&amp;nbsp;요소&amp;nbsp;개수가&amp;nbsp;256개를&amp;nbsp;초과하거나,&amp;nbsp;요소&amp;nbsp;값&amp;nbsp;중&amp;nbsp;64바이트를&amp;nbsp;초과하는&amp;nbsp;것이&amp;nbsp;있으면&amp;nbsp;Quicklist&amp;nbsp;구조로&amp;nbsp;변환됩니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;Zset(Sorted&amp;nbsp;Set)&amp;nbsp;타입의&amp;nbsp;경우: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;zset-max-listpack-entries&amp;nbsp;128 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;zset-max-listpack-value&amp;nbsp;64 &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;gt;&amp;nbsp;참고:&amp;nbsp;오래된&amp;nbsp;Redis&amp;nbsp;버전이나&amp;nbsp;설정&amp;nbsp;파일에서는&amp;nbsp;*-max-ziplist-*&amp;nbsp;라는&amp;nbsp;이름의&amp;nbsp;설정을&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;원리는&amp;nbsp;동일합니다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;ziplist&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;listpack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;목표&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;작은&amp;nbsp;데이터&amp;nbsp;집합의&amp;nbsp;메모리&amp;nbsp;절약&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;작은&amp;nbsp;데이터&amp;nbsp;집합의&amp;nbsp;메모리&amp;nbsp;절약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;구조&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;각&amp;nbsp;항목이&amp;nbsp;이전&amp;nbsp;항목의&amp;nbsp;길이를&amp;nbsp;저장&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;각&amp;nbsp;항목이&amp;nbsp;자기&amp;nbsp;자신의&amp;nbsp;길이를&amp;nbsp;저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;뛰어난&amp;nbsp;메모리&amp;nbsp;효율성&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;뛰어난&amp;nbsp;메모리&amp;nbsp;효율성&amp;nbsp;+&amp;nbsp;안정적인&amp;nbsp;성능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;단점&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;연쇄&amp;nbsp;업데이트로&amp;nbsp;인한&amp;nbsp;성능&amp;nbsp;저하&amp;nbsp;위험&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;역방향&amp;nbsp;탐색&amp;nbsp;시&amp;nbsp;약간의&amp;nbsp;추가&amp;nbsp;계산&amp;nbsp;필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;결론&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Redis&amp;nbsp;7.0부터&amp;nbsp;사용&amp;nbsp;중단(deprecated)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;현재&amp;nbsp;Redis의&amp;nbsp;표준&amp;nbsp;압축&amp;nbsp;방식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출처&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redis 오픈소스 db설치 -&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 redis-py 6.4.0 -&amp;nbsp; &lt;a href=&quot;https://redis.readthedocs.io/en/stable/index.html&quot;&gt;redis-py 6.4.0 documentation&lt;/a&gt; /&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/latest/develop/clients/redis-py/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://redis.io/docs/latest/develop/clients/redis-py/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 모듈 -&amp;nbsp;&lt;a href=&quot;https://pypi.org/project/redis/&quot;&gt;redis &amp;middot; PyPI&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hiredis 모듈 - &lt;a href=&quot;https://github.com/redis/hiredis&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/redis/hiredis&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redis 문서 -&amp;nbsp;&lt;a href=&quot;https://redis.readthedocs.io/en/stable/commands.html#redis.commands.core.CoreCommands.hset&quot;&gt;https://redis.readthedocs.io/en/stable/commands.html#redis.commands.core.CoreCommands.hset&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redis 공식문서 -&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/latest/commands/hset/&quot;&gt;https://redis.io/docs/latest/commands/hset/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/37</guid>
      <comments>https://kayath.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 14 Oct 2025 17:05:09 +0900</pubDate>
    </item>
    <item>
      <title>[Python] async, sync, block, non-blocking 비교 및 사용방법</title>
      <link>https://kayath.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;목차: 파이썬으로 이해하는 I/O 모델 (Sync, Async, Blocking, Non-blocking)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작하며: 개발자에게 I/O 모델이 왜 중요한가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1-1. 문제의 발견: 느린 I/O 작업&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1-2. 핵심 비유: &quot;유능한 셰프(CPU)와 게으른 웨이터(I/O)&quot;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 관점의 분리: I/O를 이해하는 두 가지 축&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2-1. 결과 처리의 관점: Sync vs Async&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2-2. 제어권의 관점: Blocking vs Non-blocking&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 시작하며: 개발자에게 I/O 모델이 왜 중요한가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1. 문제의 발견: 느린 I/O 작업&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘못된 예&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 웹 요청, DB 조회, 파일 읽기 등 I/O 작업이 프로그램 전체를 멈추게 하는 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Python 코드로 보는 문제 상황: requests와 for문을 이용한 순차적 API 호출 예시&lt;/p&gt;
&lt;pre id=&quot;code_1757916725419&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
import time

# 각 API가 1초씩 걸린다고 가정하기 위해, 1초 지연시키는 URL 사용
URLS = [
   &quot;https://httpbin.org/delay/1&quot;,
   &quot;https://httpbin.org/delay/1&quot;,
   &quot;https://httpbin.org/delay/1&quot;,
]

def fetch_url(url: str):
   &quot;&quot;&quot;
   하나의 URL에서 데이터를 가져오는 간단한 함수
   &quot;&quot;&quot;
   print(f&quot;요청 시작: {url}, 현재 시각: {time.time()}&quot;)
   response = requests.get(url)
   print(f&quot;요청 완료: {url}, 상태 코드: {response.status_code}&quot;)
   return response.json()

def main():
   print(&quot;동기-블로킹 방식으로 API 호출을 시작합니다...&quot;)
   start_time = time.time()

   for url in URLS:
       fetch_url(url)

   end_time = time.time()
   print(f&quot;\n총 소요 시간: {end_time - start_time:.2f}초&quot;)

if __name__ == &quot;__main__&quot;:
   main()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1757916959692&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;동기-블로킹 방식으로 API 호출을 시작합니다...
요청 시작: https://httpbin.org/delay/1, 현재 시각: 1757920222.9375117
요청 완료: https://httpbin.org/delay/1, 상태 코드: 200
요청 시작: https://httpbin.org/delay/2, 현재 시각: 1757920225.2385561
요청 완료: https://httpbin.org/delay/2, 상태 코드: 200
요청 시작: https://httpbin.org/delay/3, 현재 시각: 1757920228.7930906
요청 완료: https://httpbin.org/delay/3, 상태 코드: 200

총 소요 시간: 9.77초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;이&amp;nbsp;코드의&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;문제점은&amp;nbsp;'기다림의&amp;nbsp;낭비'&amp;nbsp;입니다.&quot; &lt;br /&gt;&lt;br /&gt;1. 하나의 요청이 모든 것을 멈춘다: requests.get(url)가 호출되면, 우리 프로그램은 해당 API 서버가 응답을 줄 때까지 아무것도 하지 않고 멈춰서 기다립니다. &lt;br /&gt;&lt;br /&gt;2. CPU는 놀고 있다: 네트워크 응답을 기다리는 3초 동안, 우리 컴퓨터의 빠른 CPU는 사실상 아무 일도 하지 않고 그저 대기만 하고 있습니다. 이것은 엄청난 자원 낭비입니다. &lt;br /&gt;&lt;br /&gt;3. 순차적 대기: 첫 번째 API 요청이 완전히 끝나야만 비로소 두 번째 API 요청을 시작할 수 있습니다. 그리고 두 번째 요청이 끝날 때까지 또 멈춰서 기다립니다. &lt;br /&gt;&lt;br /&gt;&quot;마치 식당에서 손님 3명의 주문을 받는데, 첫 번째 손님의 요리가 나올 때까지 두 번째, 세 번째 손님의 주문을 받지 않고 주방만 쳐다보며 기다리는 비효율적인 웨이터와&amp;nbsp;같습니다.&quot; &lt;br /&gt;&lt;br /&gt;&quot;그렇다면, 웨이터가 세 명의 주문을 한 번에 받고, 주방에서 요리가 완성되는 대로 바로바로 서빙하는 방식은 어떨까요? 훨씬 효율적이겠죠. 이것이 바로&amp;nbsp;&lt;b&gt;비동기(Asynchronous)&amp;nbsp;프로그래밍이&amp;nbsp;해결하고자&amp;nbsp;하는&amp;nbsp;문제&lt;/b&gt;입니다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2. 핵심 비유: &quot;유능한 셰프(CPU)와 게으른 웨이터(I/O)&quot;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 매우 빠르지만, I/O 작업의 응답을 기다리며 시간을 낭비하는 상황 비유 &lt;br /&gt;이 구조를 배우는 이유 =&amp;gt; I/O를 효율적으로 처리하여 셰프(CPU)를 쉬지 않게 만들자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. Asynchronous 프로그래밍으로 해야하는건 알겠는데, Non-Blocking 과의 차이를 아시나요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. Synchronous 프로그래밍은 알겠는데 Blocking 과의 차이는 아시나요?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 관점의 분리: I/O를 이해하는 두 가지 축&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;827&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boakWn/btsQxcXMaed/MaccnLLKkeU8t3JGjC9DM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boakWn/btsQxcXMaed/MaccnLLKkeU8t3JGjC9DM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boakWn/btsQxcXMaed/MaccnLLKkeU8t3JGjC9DM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboakWn%2FbtsQxcXMaed%2FMaccnLLKkeU8t3JGjC9DM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;827&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;827&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1. 결과 처리의 관점: Sync vs Async&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정의: &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;작업 결과&lt;/b&gt;&lt;/span&gt;를 누가, 그리고 언제 확인하여 처리하는가?&lt;br /&gt;- Sync: 호출자가 직접 작업 완료를 확인하고 결과를 가져옴 (결과와 흐름이 동기화됨)&lt;br /&gt;- Async: 작업이 끝나면 호출된 쪽(커널/이벤트 루프)이 알려줌 (결과와 흐름이 비동기화됨)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epg05d/btsQAnwHMzK/8P2SQUOtJuZBlSF6Dj8at1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epg05d/btsQAnwHMzK/8P2SQUOtJuZBlSF6Dj8at1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epg05d/btsQAnwHMzK/8P2SQUOtJuZBlSF6Dj8at1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fepg05d%2FbtsQAnwHMzK%2F8P2SQUOtJuZBlSF6Dj8at1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;399&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;2-2. 제어권의 관점: Blocking vs Non-blocking&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정의: 함수 호출 후, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;제어권&lt;/b&gt;&lt;/span&gt;(실행 흐름)이 즉시 호출자에게 돌아오는가? &lt;br /&gt;- Blocking: 작업이 끝날 때까지 호출자가 '차단'됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Non-blocking: 작업 완료 여부와 상관없이 즉시 반환됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgfwSj/btsQytYPP70/3XCOXEKKAPXhmRfGfsE4h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgfwSj/btsQytYPP70/3XCOXEKKAPXhmRfGfsE4h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgfwSj/btsQytYPP70/3XCOXEKKAPXhmRfGfsE4h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgfwSj%2FbtsQytYPP70%2F3XCOXEKKAPXhmRfGfsE4h1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;311&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Synchrounous, blocking execution&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 순차적인 흐름, 하나의 함수 호출이 끝난 후 다음 함수 호출 되는 구조&lt;/p&gt;
&lt;pre id=&quot;code_1757935779212&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. SYNCHRONOUS EXECUTION
print(&quot;\n1. SYNCHRONOUS EXECUTION&quot;)
print(&quot;-&quot; * 30)

def sync_task(name, duration):
    &quot;&quot;&quot;Synchronous task that blocks execution&quot;&quot;&quot;
    print(f&quot;{print_time()} Starting {name}&quot;)
    time.sleep(duration)  # Simulates work - BLOCKS the thread
    print(f&quot;{print_time()} Finished {name}&quot;)
    return f&quot;Result from {name}&quot;

def run_sync_tasks():
    &quot;&quot;&quot;Run tasks synchronously - one after another&quot;&quot;&quot;
    start_time = time.time()
    
    # These run sequentially - each blocks until complete
    result1 = sync_task(&quot;Task 1&quot;, 2)
    result2 = sync_task(&quot;Task 2&quot;, 1)
    result3 = sync_task(&quot;Task 3&quot;, 1)
    
    total_time = time.time() - start_time
    print(f&quot;Sync total time: {total_time:.2f} seconds&quot;)
    print(f&quot;Results: {[result1, result2, result3]}&quot;)
    
async def main():
    &quot;&quot;&quot;Main function to demonstrate all concepts&quot;&quot;&quot;
    print(&quot;SYNCHRONOUS EXECUTION:&quot;)
    run_sync_tasks()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1758084840407&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. SYNCHRONOUS EXECUTION
------------------------------
SYNCHRONOUS EXECUTION:
13:52:40.803 Starting Task 1
13:52:42.805 Finished Task 1
13:52:42.805 Starting Task 2
13:52:43.806 Finished Task 2
13:52:43.806 Starting Task 3
13:52:44.807 Finished Task 3
Sync total time: 4.00 seconds&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. Asynchrounous, Non-blocking execution&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Network 통신, DB조회 등의&amp;nbsp; I/O 작업시 CPU가 다른 일을 할 수 있는 구조&lt;/p&gt;
&lt;pre id=&quot;code_1757935613378&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import asyncio
import time

# 2. ASYNCHRONOUS EXECUTION
print(&quot;\n\n2. ASYNCHRONOUS EXECUTION&quot;)
print(&quot;-&quot; * 30)

def print_time():
    &quot;&quot;&quot;Utility function to print current time with 1/10 second precision&quot;&quot;&quot;
    current_time = time.time()
    fractional_seconds = f&quot;{current_time:.1f}&quot;.split(&quot;.&quot;)[1]
    formatted_time = time.strftime(&quot;%Y-%m-%d %H:%M:%S&quot;, time.localtime(current_time))
    return f&quot;{formatted_time}.{fractional_seconds}&quot;

async def async_task(name, duration):
    &quot;&quot;&quot;Asynchronous task that doesn't block the event loop&quot;&quot;&quot;
    print(f&quot;{print_time()} Starting {name}&quot;)
    await asyncio.sleep(duration)  # Non-blocking sleep
    print(f&quot;{print_time()} Finished {name}&quot;)
    return f&quot;Result from {name}&quot;

async def run_async_tasks():
    &quot;&quot;&quot;Run tasks asynchronously - concurrently&quot;&quot;&quot;
    start_time = time.time()
    
    # These run concurrently - no blocking
    tasks = [
        async_task(&quot;Async Task 1&quot;, 2),
        async_task(&quot;Async Task 2&quot;, 1),
        async_task(&quot;Async Task 3&quot;, 1)
    ]
    
    results = await asyncio.gather(*tasks)
    
    total_time = time.time() - start_time
    print(f&quot;Async total time: {total_time:.2f} seconds&quot;)
    print(f&quot;Results: {results}&quot;)

async def main():
    print(&quot;ASYNCHRONOUS EXECUTION:&quot;)
    await run_async_tasks()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1757991960152&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2. ASYNCHRONOUS EXECUTION
------------------------------
2025-09-16 12:05:24.6 Starting Async Task 1
2025-09-16 12:05:24.6 Starting Async Task 2
2025-09-16 12:05:24.6 Starting Async Task 3
2025-09-16 12:05:25.6 Finished Async Task 2
2025-09-16 12:05:25.6 Finished Async Task 3
2025-09-16 12:05:26.6 Finished Async Task 1
Async total time: 2.00 seconds&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;b&gt;3. Synchrounous, Non-blocking execution&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- A 함수가 B 함수를 호출했을 때, B 함수는 제어권을 바로 돌려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- A 함수는 B 함수의 결과값을 얻기 위해 주기적으로 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- polling 구조에 주로 사&lt;/p&gt;
&lt;pre id=&quot;code_1757984895879&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time
import datetime

# 시간 출력을 위한 헬퍼 함수
def print_time():
   return datetime.datetime.now().strftime(&quot;%H:%M:%S.%f&quot;)[:-3]

# 1. 논블로킹 작업을 시뮬레이션하는 클래스
class NonBlockingIOTask:
    def __init__(self, name, duration):
        self._name = name
        self._duration = duration
        self._start_time = time.time()
        print(f&quot;{print_time()} | {self._name}: 작업 시작 (총 {self._duration}초 소요 예정). 호출자는 차단되지 않음.&quot;)

    def poll(self) -&amp;gt; bool:
        &quot;&quot;&quot;
        작업이 완료되었는지 확인하는 메소드.
        이 메소드 자체가 블로킹되지 않음.
        &quot;&quot;&quot;
        return time.time() &amp;gt;= self._start_time + self._duration

    def get_result(self) -&amp;gt; str:
        &quot;&quot;&quot;작업이 완료되었을 때만 결과를 반환&quot;&quot;&quot;
        if self.poll():
             return f&quot;Result from {self._name}&quot;
        return None

# 2. 메인 로직: 논블로킹 작업을 주기적으로 폴링(polling)
def run_sync_nonblocking():
    print(&quot;\nSYNCHRONOUS NON-BLOCKING 예제&quot;)
    print(&quot;-&quot; * 40)

    start_time = time.time()

    # 논블로킹 작업 생성. 이 호출은 즉시 반환됨.
    task = NonBlockingIOTask(&quot;Sync-Nonblocking Task&quot;, 2)

    # 작업이 완료될 때까지 계속해서 물어봄 (Polling)
    while not task.poll():
        # 이 루프 안에서 다른 작업을 할 수 있음을 보여줌
        print(f&quot;{print_time()} | 메인 루프: 작업 완료를 기다리는 중... (다른 작업 수행 가능)&quot;)
        time.sleep(0.5) # 너무 빠르게 계속 물어보면 CPU 낭비이므로 잠시 대기

    # 루프가 끝났다는 것은 작업이 완료되었다는 의미
    # 이제 동기적으로 결과를 가져옴
    result = task.get_result()

    print(f&quot;{print_time()} | {task._name}: 작업 최종 완료됨!&quot;)

    total_time = time.time() - start_time
    print(f&quot;\n총 소요 시간: {total_time:.2f}초&quot;)
    print(f&quot;결과: {result}&quot;)


if __name__ == &quot;__main__&quot;:
    run_sync_nonblocking()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1757986064844&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; SYNCHRONOUS NON-BLOCKING 예제
----------------------------------------
10:30:00.123 | Sync-Nonblocking Task: 작업 시작 (총 2초 소요 예정). 호출자는 차단되지 않음.
10:30:00.123 | 메인 루프: 작업 완료를 기다리는 중... (다른 작업 수행 가능)
10:30:00.625 | 메인 루프: 작업 완료를 기다리는 중... (다른 작업 수행 가능)
10:30:01.128 | 메인 루프: 작업 완료를 기다리는 중... (다른 작업 수행 가능)
10:30:01.630 | 메인 루프: 작업 완료를 기다리는 중... (다른 작업 수행 가능)
10:30:02.133 | Sync-Nonblocking Task: 작업 최종 완료됨!

총 소요 시간: 2.01초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. Asynchrounous, blocking execution&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt; time.sleep(2)가 실행되는 동안 observer_task는 단 한 번도 실행되지 못한다. 이벤트 루프가&lt;span&gt;&amp;nbsp;&lt;/span&gt;time.sleep(2)으로 blocking 되기 떄문&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Sync, Blocking 코드와 성능상 다를게 거의 없어서 거의 사용하지 않음&lt;/p&gt;
&lt;pre id=&quot;code_1757991627855&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import asyncio
import time
import datetime

def print_time():
    return datetime.datetime.now().strftime(&quot;%H:%M:%S.%f&quot;)[:-3]

async def observer_task():
    &quot;&quot;&quot;이 작업은 실행될 기회를 얻지 못할 것입니다.&quot;&quot;&quot;
    while True:
        print(f&quot;{print_time()} | [이벤트 루프] 관찰자 실행 중...&quot;)
        await asyncio.sleep(0.5)

async def main():
    print(&quot;잘못된 비동기 코드 예제 (이벤트 루프가 멈춤)&quot;)
    print(&quot;-&quot; * 40)
    start_time = time.time()

    # 관찰자 작업을 백그라운드에서 실행하도록 스케줄링
    asyncio.create_task(observer_task())

    print(f&quot;{print_time()} | [메인 코루틴] 이제 블로킹 함수 time.sleep()을 호출합니다...&quot;)

    # 이 코드는 이벤트 루프가 도는 스레드 자체를 멈춥니다!
    time.sleep(2)

    print(f&quot;{print_time()} | [메인 코루틴] 2초간의 블로킹이 끝났습니다.&quot;)

    total_time = time.time() - start_time
    print(f&quot;\n총 소요 시간: {total_time:.2f}초&quot;)

if __name__ == &quot;__main__&quot;:
   asyncio.run(main())&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1757991662855&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;잘못된 비동기 코드 예제 (이벤트 루프가 멈춤)
----------------------------------------
11:00:00.100 | [메인 코루틴] 이제 블로킹 함수 time.sleep()을 호출합니다...
(2초 동안 아무것도 출력되지 않고 프로그램이 완전히 멈춤)
11:00:02.100 | [메인 코루틴] 2초간의 블로킹이 끝났습니다.

총 소요 시간: 2.00초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;*** Asyncronous와 blocking 함수를 동시에 사용하고 싶다?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 왜냐면 많은 파이썬 패키지나 라이브러리가 sync 함수로 구현이 되어 있지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 이벤트 루프에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;loop.run_in_executor()가 별도의 스레드를 생성&lt;/span&gt; &amp;gt; 만들어 스레드 안에서 blocking 함수가 실행&lt;/p&gt;
&lt;pre id=&quot;code_1758064074558&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import asyncio
import time
import datetime

def blocking_function(duration):
    &quot;&quot;&quot;A function that blocks using time.sleep().&quot;&quot;&quot;
    print(f&quot;{datetime.datetime.now().time()} - 블로킹 함수 시작 (실행 시간: {duration}초)&quot;)
    time.sleep(duration)
    print(f&quot;{datetime.datetime.now().time()} - 블로킹 함수 종료&quot;)

async def main():
    start_time = time.time()
    print(f&quot;{datetime.datetime.now().time()} - 메인 프로그램 시작&quot;)

    loop = asyncio.get_running_loop() # 현재 OS에서 실행중인 이벤트 루프 반환

    await asyncio.gather(
        # 블로킹 함수를 별도 스레드에서 실행
        loop.run_in_executor(None, blocking_function, 2),
        non_blocking_task(&quot;비동기 작업&quot;, 3)
    )

    end_time = time.time()
    print(f&quot;{datetime.datetime.now().time()} - 메인 프로그램 종료&quot;)
    print(f&quot;총 실행 시간: {end_time - start_time:.2f}초&quot;)

    async def non_blocking_task(task_name, duration):
    &quot;&quot;&quot;A non-blocking task.&quot;&quot;&quot;
        print(f&quot;{datetime.datetime.now().time()} - {task_name}: 시작 (실행 시간: {duration}초)&quot;)
        await asyncio.sleep(duration)
        print(f&quot;{datetime.datetime.now().time()} - {task_name}: 종료&quot;)

if __name__ == &quot;__main__&quot;:
    asyncio.run(main())&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1758068518112&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;09:21:42.936728 - 메인 프로그램 시작
09:21:42.938211 - 블로킹 함수 시작 (실행 시간: 2초)
09:21:42.938420 - 비동기 작업: 시작 (실행 시간: 3초)
09:21:44.940556 - 블로킹 함수 종료
09:21:45.940198 - 비동기 작업: 종료
09:21:45.940362 - 메인 프로그램 종료
총 실행 시간: 3.00초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;실행 흐름&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;blocking_function&quot; (동기 작업)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;main() 함수가 asyncio.gather()를 호출 (gather는 두 가지 작업을 동시에 시작시킴)&lt;/li&gt;
&lt;li&gt;loop.run_in_executor를 통해 호출&lt;/li&gt;
&lt;li&gt;이벤트 루프는 이 함수를 직접 실행하지 않고 스레드 풀의 워커 스레드(worker thread) 중 하나에게 위임&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;non_blocking_task&quot; (비동기 작업)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;main() 함수가 asyncio.gather()를 호출 (gather는 두 가지 작업을 동시에 시작시킴)&lt;/li&gt;
&lt;li&gt;이 작업은 메인 스레드의 이벤트 루프가 직접 관리&lt;/li&gt;
&lt;li&gt;await syncio.sleep(3)을 만나면, 이벤트 루프는 이 작업을 잠시 멈추고 다른 일을 할 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;table style=&quot;border-collapse: collapse; width: 86.3974%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.938%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 30.5426%; text-align: center;&quot;&gt;non_blocking_task&lt;/td&gt;
&lt;td style=&quot;width: 46.6262%; text-align: center;&quot;&gt;blocking_function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.938%; text-align: center;&quot;&gt;실행 주체&lt;/td&gt;
&lt;td style=&quot;width: 30.5426%; text-align: center;&quot;&gt;메인 스레드(이벤트 루프)&lt;br /&gt;이벤트 루프를 막지 않음&lt;/td&gt;
&lt;td style=&quot;width: 46.6262%; text-align: center;&quot;&gt;별도 스레드(스레드 풀)&lt;br /&gt;별도 스레드에서 실행되어 이벤트 루프를 막지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;loop.run_in_executor(None, ...)의 역할&lt;/b&gt; &lt;br /&gt;- asyncio의 이벤트 루프는 기본적으로 하나의 메인 스레드에서 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만약 이벤트 루프가 관리하는 작업(task) 중 하나라도 time.sleep()과 같은 동기(blocking) 함수를 직접 호출하면, 이벤트 루프 전체가 그 시간 동안 멈춰버립니다. &lt;br /&gt;- loop.run_in_executor()는 이러한 문제를 해결하기 위해 사용됩니다. 이 함수는 시간이 오래 걸리거나 블로킹될 수 있는 동기 함수를 별도의 스레드(또는 프로세스)에서 실행하도록 지시합니다. &lt;br /&gt;- 첫 번째 인자로 None을 주면, asyncio는 기본적으로 내장된 ThreadPoolExecutor(스레드 풀 실행기)를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Documentor 프로젝트 시퀀스 다이어그램&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Main 스레드는 sync 방식으로 실행되고, Agent 가 동작하기 위한 스레드를 생성 후(Agent의&amp;nbsp; 추가될 때마다), 그 안에서 Async 방식으로 worker_loop라는 이벤트 루프를 생성 후 Agent가 실행됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Agent가 새로 생길때마맏 스레드가 새로 생성되므로, Agent가 계속 많아질 때는 context-switching의 발생으로 성능 저하&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Async함수로 구현이 되어 있지만 스레드로 Agent마다 나뉘어져 있기 때문에 제대로된 코루틴을 활용하지 못함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보완해야할 점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Agent가 생성때마다 스레드를 생성하는 것보다 코루틴 이벤트 루프에 넣어 코루틴을 활용하게 리팩토링 필요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DRvI0/btsQBiv4YSt/tp88zW4RfGEz2wbBCnDmvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DRvI0/btsQBiv4YSt/tp88zW4RfGEz2wbBCnDmvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DRvI0/btsQBiv4YSt/tp88zW4RfGEz2wbBCnDmvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDRvI0%2FbtsQBiv4YSt%2Ftp88zW4RfGEz2wbBCnDmvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;439&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kiyeon.tistory.com/26&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kiyeon.tistory.com/26&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.ibm.com/articles/l-async/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.ibm.com/articles/l-async/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 공식문서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.python.org/ko/3.13/library/asyncio-eventloop.html#asyncio.get_running_loop&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.python.org/ko/3.13/library/asyncio-eventloop.html#asyncio.get_running_loop&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.python.org/ko/3.13/library/asyncio-eventloop.html#asyncio.loop.run_in_executor&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.python.org/ko/3.13/library/asyncio-eventloop.html#asyncio.loop.run_in_executor&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.python.org/ko/3.13/library/asyncio-task.html#asyncio.gather&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.python.org/ko/3.13/library/asyncio-task.html#asyncio.gather&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python</category>
      <author>ssehuun</author>
      <guid isPermaLink="true">https://kayath.tistory.com/36</guid>
      <comments>https://kayath.tistory.com/36#entry36comment</comments>
      <pubDate>Wed, 17 Sep 2025 10:34:17 +0900</pubDate>
    </item>
  </channel>
</rss>