<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>아카이브</title>
    <link>https://rayiarchive.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 26 Aug 2026 21:03:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Rayi</managingEditor>
    <image>
      <title>아카이브</title>
      <url>https://tistory1.daumcdn.net/tistory/4076390/attach/0cad38cc23c941898c923194097fd644</url>
      <link>https://rayiarchive.tistory.com</link>
    </image>
    <item>
      <title>tmux에 대해</title>
      <link>https://rayiarchive.tistory.com/302</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCsEwX/dJMcacxvCPN/QBrVhXdH2iU7kX9IywK4C1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCsEwX/dJMcacxvCPN/QBrVhXdH2iU7kX9IywK4C1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCsEwX/dJMcacxvCPN/QBrVhXdH2iU7kX9IywK4C1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCsEwX%2FdJMcacxvCPN%2FQBrVhXdH2iU7kX9IywK4C1%2Fimg.jpg&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;317&quot; height=&quot;305&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tmux (terminal multiplexer)는 &lt;b&gt;하나의 터미널 창을 여러 개의 가상 터미널 창들로 나누어 관리&lt;/b&gt;할 수 있게 해 주는 터미널 도구입니다. Linux에는 기본적으로 설치되어 있어 곧바로 사용할 수 있습니다.&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;보통 터미널에서 명령어를 입력하며 작업할 때는 여러 경로에서 작업하게 됩니다(가령 서버와 프론트엔드 경로를 각각 띄워놓는을 때). 이런 경우 vscode 개발환경이라면 터미널 창 우측의 터미널 목록을 이용해 여러 창을 번갈아가며 작업할 수 있는데, 이러면 &lt;b&gt;한 번에 두 개의 터미널 창을 동시에&lt;/b&gt; 볼 수는 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxoo73/dJMcaji7yq7/rADHgRKFa4473eNI4PhWiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxoo73/dJMcaji7yq7/rADHgRKFa4473eNI4PhWiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxoo73/dJMcaji7yq7/rADHgRKFa4473eNI4PhWiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxoo73%2FdJMcaji7yq7%2FrADHgRKFa4473eNI4PhWiK%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;724&quot; height=&quot;187&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 tmux를 사용하면 한 터미널 창을 분할하여 여러 터미널들을 동시에 볼 수 있게 됩니다. 예를 들어 여러 터미널 창을 띄워놓고 필요에 따라 옮길 수도 있고, 하나의 터미널 창을 상하좌우로 구분하여 나눠서 사용하는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CGU9E/dJMcahFwUvc/ONtmSPbob3OZ5XVoXBdd1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CGU9E/dJMcahFwUvc/ONtmSPbob3OZ5XVoXBdd1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CGU9E/dJMcahFwUvc/ONtmSPbob3OZ5XVoXBdd1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCGU9E%2FdJMcahFwUvc%2FONtmSPbob3OZ5XVoXBdd1k%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;554&quot; height=&quot;383&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;480&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;뿐만 아니라, tmux 창에서 싱행되는 작업은 &lt;b&gt;연결이 끊어져도 백그라운드에서 작업이 유지&lt;/b&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 data-ke-size=&quot;size16&quot;&gt;하지만 tmux 세션 안에서 실행되는 프로그램은 해당 세션을 나가고, 터미널 창을 닫아도 여전히 그 세션 안에서 실행됩니다. 그래서 tmux는 &lt;b&gt;원격 서버 작업이나 장시간 실행되는 프로세스&lt;/b&gt;를 다루는데에 필수적이라고 할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주요 구조&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;세션(Session): tmux의 작업 영역은 크게 세션 단위로 구분됩니다. 세션은 가장 큰 단위로 프로젝트별로 분리해 관리하며, SSH 접속이 끊겨도 백그라운드에서 유지됩니다.&lt;/li&gt;
&lt;li&gt;윈도우(Window): 브라우저의 탭과 같은 개념입니다.&lt;/li&gt;
&lt;li&gt;패널(Panel): 하나의 화면을 가로 또는 세로로 쪼개어 여러 화면을 동시에 보게 해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명령어&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록 보기&lt;/p&gt;
&lt;pre id=&quot;code_1787377093028&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tmux ls&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 생성&lt;/p&gt;
&lt;pre id=&quot;code_1787377139176&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tmux new -s [세션이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 삭제&lt;/p&gt;
&lt;pre id=&quot;code_1787377159644&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tmux kill-session -t [세션이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 진입&lt;/p&gt;
&lt;pre id=&quot;code_1787377193318&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tmux attach -t [세션이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 나가기&lt;/p&gt;
&lt;pre id=&quot;code_1787377231981&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;키보드 단축키: ctrl + b &amp;rarr; d&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분할 패널 닫기&lt;/p&gt;
&lt;pre id=&quot;code_1787377285245&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;키보드 단축키: ctrl + b &amp;rarr; x&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 기능 활성화&lt;/p&gt;
&lt;pre id=&quot;code_1787377319724&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;단축키: ctrl + b &amp;rarr; :
이후 명령어: set -g mouse on&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IDE</category>
      <category>IDE</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/302</guid>
      <comments>https://rayiarchive.tistory.com/302#entry302comment</comments>
      <pubDate>Sat, 22 Aug 2026 14:42:07 +0900</pubDate>
    </item>
    <item>
      <title>[FastAPI] lifespan - 생명주기</title>
      <link>https://rayiarchive.tistory.com/301</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;생명주기&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서의 생명주기는 &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;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;Startup (시작):&lt;/b&gt; 서버가 막 가동될 때입니다. 이때 보통 &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;5,0,0&quot;&gt;DB 연결&lt;/b&gt;이나 &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;5,0,0&quot;&gt;AI 모델 로드&lt;/b&gt;, &lt;b data-index-in-node=&quot;54&quot; data-path-to-node=&quot;5,0,0&quot;&gt;설정 파일 읽기&lt;/b&gt; 같은 무거운 작업을 미리 해둡니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;Running (운영):&lt;/b&gt; 서버가 사용자(클라이언트)들의 요청을 처리하며 열심히 일하는 단계입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;Shutdown (종료):&lt;/b&gt; 서버가 꺼질 때입니다. 이때 &lt;b data-index-in-node=&quot;31&quot; data-path-to-node=&quot;5,2,0&quot;&gt;DB 연결을 안전하게 닫거나&lt;/b&gt;, &lt;b&gt;임시 파일을 삭제&lt;/b&gt;하는 등 뒷정리를 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;asynccontextmanager&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 생명주기를 설정하는 함수는 비동기 함수로 정의합니다.&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;b&gt;contextmanager&lt;/b&gt;는 &quot;이 영역(Context) 안에서만 이 자원을 유효하게 하겠다&quot;는 약속을 정의합니다. 때문에 중간에 에러가 발생하더라도, &lt;b&gt;startup - running - shutdown 전반의 코드가 모두 실행&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;b&gt;async&lt;/b&gt;를 사용하는 이유는 비동기로 작동하는 FastAPI 특성상 그 &lt;b&gt;생명주기 또한 비동기 함수들을 호출하여 사용&lt;/b&gt;할 수 있어야 하기 때문입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778484568158&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    # [Startup] 서버가 켜질 때 실행되는 부분
    await get_pool()  # e.g. db 연결
    print(&quot;Database pool initialized&quot;)
    # ------------------------------------
    
    # ------------------------------------
    yield  # FastAPI 서버가 여기서 실행됨
    # ------------------------------------
    
    # [Shutdown] 서버가 꺼질 때 실행되는 부분
    await close_pool()  # e.g. db 연결 해제
    print(&quot;Database pool closed&quot;)
    # ------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 생명주기 함수는 FastAPI app을 생성할 때 매개변수로 전달하여 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778485453993&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI(lifespan=lifespan)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/Python</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/301</guid>
      <comments>https://rayiarchive.tistory.com/301#entry301comment</comments>
      <pubDate>Mon, 11 May 2026 16:44:30 +0900</pubDate>
    </item>
    <item>
      <title>[FastAPI] APIRouter - 라우팅 설정하기</title>
      <link>https://rayiarchive.tistory.com/300</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 각 엔드포인트들의 주소를 하나로 묶어 사용하려면 &lt;b&gt;APIRouter()&lt;/b&gt;&amp;nbsp;객체를 사용하면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;include_router( )&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 FastAPI 객체가 필요합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777705203066&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app의 &lt;b&gt;include_router( )&lt;/b&gt; 함수로 APIRouter( ) 객체를 연결할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777705046098&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from app.routes.news import news_router  # APIRouter 객체


app = FastAPI()

app.include_router(news_router, prefix=&quot;/news&quot;, tags=[&quot;news&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;news_router&amp;nbsp; |&lt;/b&gt;&amp;nbsp; APIRouter 객체 이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;prefix&amp;nbsp; |&lt;/b&gt;&amp;nbsp; 라우팅된 엔드포인트들이 공통으로 가질 경로(접두어). 이 경우 news_router에 속한 모든 엔드 포인트들은 &lt;b&gt;/news 경로를 루트로&lt;/b&gt; 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tags&amp;nbsp; |&lt;/b&gt;&amp;nbsp; Swagger 문서*에서 엔드포인트를 그룹지을 이름(태그). 한 번에 &lt;b data-index-in-node=&quot;40&quot; data-path-to-node=&quot;8&quot;&gt;여러 개의 태그&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;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;*Swagger 문서 :&lt;/b&gt; API가&amp;nbsp;어떤&amp;nbsp;데이터를&amp;nbsp;주고받는지&amp;nbsp;UI(화면)로&amp;nbsp;보여주고,&amp;nbsp;그&amp;nbsp;자리에서&amp;nbsp;바로&amp;nbsp;테스트까지&amp;nbsp;해볼&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해주는&amp;nbsp;도구&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;APIRouter&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app과 연결할 라우팅은 APIRouter( ) 객체로 관리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777706646558&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import APIRouter

news_router = APIRouter()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;APIRouter( )는 같은 라우팅으로 묶고자 하는 엔드포인트 함수 위에 &lt;b&gt;데코레이션으로 작성&lt;/b&gt;하여 사용합니다. 만약 app.include_router( )에서 news_router의 prefix가 &lt;b&gt;&quot;/news&quot;&lt;/b&gt;였다면,post_news_id( ) 엔드포인트는 &lt;b&gt;&quot;/news/id&quot;&lt;/b&gt;로 요청할 때 호출됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777706767220&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 해당 라우팅에서 루트 경로의 GET 메소드 엔드포인트 입니다.
@news_router.get(&quot;&quot;)
async def get_news():
   # ...
   
# 해당 라우팅에서 '/id' 경로의 POST 메소드 엔드포인트 입니다.
@news_router.post(&quot;/id&quot;)
async def post_news_id():
   # ...&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Backend/Python</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/300</guid>
      <comments>https://rayiarchive.tistory.com/300#entry300comment</comments>
      <pubDate>Sat, 2 May 2026 16:29:39 +0900</pubDate>
    </item>
    <item>
      <title>[FastAPI] FastAPI에 대하여</title>
      <link>https://rayiarchive.tistory.com/299</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nUt8q/dJMcadhzyGS/xmHMwKVBoZuFmhIllFB43k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nUt8q/dJMcadhzyGS/xmHMwKVBoZuFmhIllFB43k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nUt8q/dJMcadhzyGS/xmHMwKVBoZuFmhIllFB43k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnUt8q%2FdJMcadhzyGS%2FxmHMwKVBoZuFmhIllFB43k%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;747&quot; height=&quot;269&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FastAPI&lt;/b&gt;는 Python의 표준 타입 힌트* 기반의 API를 구축하기 위한 &lt;b&gt;웹 프레임워크&lt;/b&gt;입니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://rayiarchive.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Node.js&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;나 Go과 대등할 정도로 빠른 개발 속도와 처리 속도를 가지고 있어 &lt;b&gt;Fast&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;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;*표준 타입 힌트 :&lt;/b&gt; Python 3.6 버전부터 도입된 type hinting 문법을 API 개발에 그대로 활용하는 것. 자세한 내용은 아래 후술&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;간단한 작성방식&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Starlette과 Pydantic을 기반으로 하여 빠른 코드 작성을 지원할 뿐 아니라, 코드 중복을 최소화하고 코드 자체를 간결하게 만들어 직관적이고 배우기 쉬운 프레임워크를 설계하였습니다. 예를 들어 &lt;b&gt;서버의 라우팅 과정&lt;/b&gt;을 Flask와 비교하면 이름 식별자나 routing 파일 위치(Flask에서는 blueprint 위치)등을 작성하지 않는 것을 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777685830835&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Flask

news_router = Blueprint('news', __name__)

@news_bp.route('', methods=['GET'])
def get_news():&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1777685810876&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# FastAPI

news_router = APIRouter()

@news_router.get(&quot;&quot;) 
def get_news():&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;표준 타입 힌트&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 프레임워크에서는 데이터의 형식을 검증하기 위해 별도의 라이브러리나 복잡한 코드가 필요했지만, FastAPI는 &lt;b&gt;Python 기본 문법&lt;/b&gt;만으로 이를 해결합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777686374835&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int, q: str = None):  # 표준 타입 힌트
    return {
        &quot;item_id&quot;: item_id,
        &quot;q&quot;: q
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 item의 id에 해당하는 서버 요청을 실행하느 엔드포인트 함수입니다. 여기서 FastAPI는 표준 타입 힌트를 보고 다음 작업들을 자동으로 수행합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 파싱:&lt;/b&gt; URL이 &quot;/items/123&quot;과 같이 들어왔다면, &quot;123&quot;을 파이썬 정수 123으로 자동 변환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 검증:&lt;/b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;URL이 &quot;/items/abc&quot;와 같이 들어왔다면,&lt;/span&gt; 코드 실행 전 유효하지 않다는 에러를 클라이언트에 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동&amp;nbsp;완성:&lt;/b&gt; 개발환경 프로그램(VS Code 등)에서 해당 변수가 int임을 인지하고 관련 함수들을 추천해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 힌트로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://rayiarchive.tistory.com/174&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pydantic&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;의 사용자 지정 타입 또한 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;비동기 처리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 존재했던 Python 웹 프레임워크인 Flask나 Django는 &lt;b&gt;동기 방식(Synchronous)&lt;/b&gt;이지만, FastAPI는 &lt;b&gt;비동기 방식(Ascynchronous)&lt;/b&gt;입니다. Flask는 기본적으로 한 번에 하나의 요청을 처리하는 구조입니다. 하지만 FastAPI는 서버가 기다리는 시간을 허비하지 않기 때문에, 빠른 처리가 가능해집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;서버 실행&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 자체 내장 서버가 없기 때문에, uvicorn같은 외부 서버를 이용해 따로 실행해주어야 합니다.&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;pre id=&quot;code_1777704126034&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uvicorn app.main:app --reload --host 0.0.0.0 --port 8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python 코드 내에서 실행하려면 이런 식으로 작성하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777704307355&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import uvicorn

if __name__ == &quot;__main__&quot;:
	uvicorn.run(&quot;app.main:app&quot;, host=&quot;0.0.0.0&quot;, port=8000, reload=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;app.main:app&amp;nbsp; |&lt;/b&gt;&amp;nbsp; &lt;b&gt;./app/main.py&lt;/b&gt;의 &lt;b&gt;app&lt;/b&gt; 객체를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;host&amp;nbsp; |&lt;/b&gt;&amp;nbsp; 서버가 실행될 주소입니다. (0.0.0.0은 모든 외부 기기에 대해 접속을 허용하는 상태입니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;port&amp;nbsp; |&lt;/b&gt;&amp;nbsp; 서버가 요청을 받을 포트 번호 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;reload&amp;nbsp; |&lt;/b&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;a href=&quot;https://fastapi.tiangolo.com/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://fastapi.tiangolo.com/ko/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777684706926&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;FastAPI&quot; data-og-description=&quot;FastAPI framework, high performance, easy to learn, fast to code, ready for production&quot; data-og-host=&quot;fastapi.tiangolo.com&quot; data-og-source-url=&quot;https://fastapi.tiangolo.com/ko/&quot; data-og-url=&quot;https://fastapi.tiangolo.com/ko/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iwTmW/dJMb83SmoS2/gl0wKJZkkcf0PRXRjj2wqK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/c2ScBK/dJMb85vSt43/9HZeKLwam2RmvKQbPBCJOk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bClR8m/dJMb9hC4J0E/IKYlKi8cMbnZQkTvVYRyi0/img.png?width=1024&amp;amp;height=369&amp;amp;face=0_0_1024_369&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastapi.tiangolo.com/ko/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iwTmW/dJMb83SmoS2/gl0wKJZkkcf0PRXRjj2wqK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/c2ScBK/dJMb85vSt43/9HZeKLwam2RmvKQbPBCJOk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bClR8m/dJMb9hC4J0E/IKYlKi8cMbnZQkTvVYRyi0/img.png?width=1024&amp;amp;height=369&amp;amp;face=0_0_1024_369');&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;FastAPI&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI framework, high performance, easy to learn, fast to code, ready for production&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastapi.tiangolo.com&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>Backend/Python</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/299</guid>
      <comments>https://rayiarchive.tistory.com/299#entry299comment</comments>
      <pubDate>Sat, 2 May 2026 10:50:42 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Uv에 대하여</title>
      <link>https://rayiarchive.tistory.com/298</link>
      <description>&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;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brMSRA/dJMcaciov0U/1lMI0Pcrl1LbeSllxeg0KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brMSRA/dJMcaciov0U/1lMI0Pcrl1LbeSllxeg0KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brMSRA/dJMcaciov0U/1lMI0Pcrl1LbeSllxeg0KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrMSRA%2FdJMcaciov0U%2F1lMI0Pcrl1LbeSllxeg0KK%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;718&quot; height=&quot;404&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Uv&lt;/b&gt;는 Rust 기반의 패키지 관리 프로그램으로, Python의 &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 data-ke-size=&quot;size16&quot;&gt;Python은 역사가 오래된 언어이기 때문에, &lt;b&gt;pip&lt;/b&gt;나 &lt;b&gt;conda&lt;/b&gt;와 같은 대표적인 패키지 관리 프로그램이 등장하기 이전에도 여러 종류의 프로그램이 존재했습니다. 이들은 모두 기능이나 특징이 조금씩 달라서, 단일화된 Python 생태계를 유지하는데 어려움을 주게 되었습니다.&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;이런 맥락에서 등장한 Uv는 &lt;b&gt;기존의 패키지 관리 프로그램들을 하나로 통합&lt;/b&gt;하는 것을 목표로 만들어졌습니다. 우리가 pip나 conda에서 하던 것 처럼 &lt;b&gt;패키지와 라이브러리&lt;/b&gt;를 쉽게 설치할 수 있을 뿐 아니라, &lt;b&gt;가상환경&lt;/b&gt;을 생성하거나 &lt;b&gt;Python의 버전&lt;/b&gt; 또한 다양하게 설치하고 전환할 수 있습니다. 그러면서도 Rust의 특징을 살려 &lt;b&gt;패키지 설치 및 의존성 해결 속도&lt;/b&gt;가 매우 빠른 것을 강점으로 내세우고 있습니다. 공식 벤치마크에서는 pip보다 10배에서 최대 100배 까지 빠르다고 묘사하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfgAv1/dJMcaarlMVV/J5CGku0k32YHlqLieBdSH0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfgAv1/dJMcaarlMVV/J5CGku0k32YHlqLieBdSH0/img.jpg&quot; data-alt=&quot;Trio 패키지의 의존성을 해결할 때 소요 시간 비교&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfgAv1/dJMcaarlMVV/J5CGku0k32YHlqLieBdSH0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfgAv1%2FdJMcaarlMVV%2FJ5CGku0k32YHlqLieBdSH0%2Fimg.jpg&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;496&quot; height=&quot;107&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Trio 패키지의 의존성을 해결할 때 소요 시간 비교&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범용성과 우수한 처리속도 덕분에 Uv는 현재 차세대 패키지 관리 통합 도구로 주목받고 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows기준으로, PowerShell에 아래 명령어를 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775701537396&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;powershell -ExecutionPolicy ByPass -c &quot;irm https://astral.sh/uv/install.ps1 | iex&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 pip가 설치되어 있다면, pip를 이용해 설치할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775701584845&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install uv&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트는 아래와 같이 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775701636702&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv self update&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;uv lock&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv는 의존성을 해결할 때 해당 프로젝트에 존재하는 &lt;b&gt;uv.lock&lt;/b&gt; 파일을 참고합니다. uv.lock에는 해당 프로젝트에 어떤 의존성 파일들이 설치되었는지에 대한 정보가 기록되어 있습니다. pip에서의 &lt;b&gt;requirements.txt&lt;/b&gt;라고 생각하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uv.lock을 생성하려면 프로젝트를 초기화해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702518480&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화시 프로젝트에 대한 정보를 기록한 &lt;b&gt;pyproject.toml&lt;/b&gt; 파일이 생성됩니다. 프로젝트의 의존성 파일들이 여기 &lt;b&gt;dependencies&lt;/b&gt; 리스트에 기록됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702585649&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pyproject.toml
[project]
name = &quot;rabbit&quot;
version = &quot;0.1.0&quot;
description = &quot;Add your description here&quot;
readme = &quot;README.md&quot;
requires-python = &quot;&amp;gt;=3.13&quot;
dependencies = [
    &quot;a2a-sdk==0.3.25&quot;,
    &quot;aiofile==3.9.0&quot;,
    &quot;aiofiles==25.1.0&quot;,
    &quot;aiohappyeyeballs==2.6.1&quot;,
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 pyproject.toml를 기반으로 uv.lock 파일을 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702684175&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv lock&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1775702704171&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# uv.lock
version = 1
revision = 3
requires-python = &quot;&amp;gt;=3.13&quot;
resolution-markers = [
    &quot;python_full_version &amp;gt;= '3.14'&quot;,
    &quot;python_full_version &amp;lt; '3.14'&quot;,
]

[[package]]
name = &quot;a2a-sdk&quot;
version = &quot;0.3.25&quot;
source = { registry = &quot;https://pypi.org/simple&quot; }
dependencies = [
    { name = &quot;google-api-core&quot; },
    { name = &quot;httpx&quot; },
    { name = &quot;httpx-sse&quot; },
    { name = &quot;protobuf&quot; },
    { name = &quot;pydantic&quot; },
]
sdist = { url = &quot;https://files.pythonhosted.org/packages/55/83/3c99b276d09656cce039464509f05bf385e5600d6dc046a131bbcf686930/a2a_sdk-0.3.25.tar.gz&quot;, hash = &quot;sha256:afda85bab8d6af0c5d15e82f326c94190f6be8a901ce562d045a338b7127242f&quot;, size = 270638, upload-time = &quot;2026-03-10T13:08:46.417Z&quot; }
wheels = [
    { url = &quot;https://files.pythonhosted.org/packages/bd/f9/6a62520b7ecb945188a6e1192275f4732ff9341cd4629bc975a6c146aeab/a2a_sdk-0.3.25-py3-none-any.whl&quot;, hash = &quot;sha256:2fce38faea82eb0b6f9f9c2bcf761b0d78612c80ef0e599b50d566db1b2654b5&quot;, size = 149609, upload-time = &quot;2026-03-10T13:08:44.7Z&quot; },
]

[[package]]
name = &quot;aiofile&quot;
...&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;uv sync&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 clone 하거나 내려받았을 때 의존성을 해결하고자 한다면 아래와 같이 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702823158&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv sync&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 pip install과 다른 점은, 우선 &lt;b&gt;속도가 훨씬 빠릅니다&lt;/b&gt;. 그리고 철저히 uv.lock의 기준을 따라서 &lt;b&gt;추가 설치 뿐만 아니라 버전 수정과 삭제 또한 수행&lt;/b&gt;합니다. 좀 더 엄격하게 의존성을 해결한다고 볼 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;uv add&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리를 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702966432&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv add [lib1] [lib2] ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이전에 requirements.txt를 사용해왔다면, 마이그레이션 할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702758728&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv add -r requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 requirements.txt가 없다면, pip freeze를 이용해 의존성 목록을 뽑아낸 다음 일괄 설치할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775703462788&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv pip freeze --exclude-editable | ForEach-Object { uv add $_ }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때까지 설치한 라이브러리 목록은 &lt;b&gt;list&lt;/b&gt;로 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775702996206&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv list&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;uv python&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 버전의 python을 설치하거나, 현재 가상환경에서의 파이썬 버전을 고정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775703065446&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uv python [version]  # 설치
uv python pin [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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.astral.sh/uv/&quot;&gt;https://docs.astral.sh/uv/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775701545342&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;uv&quot; data-og-description=&quot;An extremely fast Python package and project manager, written in Rust. Installing Trio's dependencies with a warm cache. A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more. 10-100x faster than pip. Provides comprehens&quot; data-og-host=&quot;docs.astral.sh&quot; data-og-source-url=&quot;https://docs.astral.sh/uv/&quot; data-og-url=&quot;https://docs.astral.sh/uv/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.astral.sh/uv/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.astral.sh/uv/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;uv&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An extremely fast Python package and project manager, written in Rust. Installing Trio's dependencies with a warm cache. A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more. 10-100x faster than pip. Provides comprehens&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.astral.sh&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;
&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>Language/Python</category>
      <category>python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/298</guid>
      <comments>https://rayiarchive.tistory.com/298#entry298comment</comments>
      <pubDate>Thu, 9 Apr 2026 12:12:12 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Library | 11. typing</title>
      <link>https://rayiarchive.tistory.com/297</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;typing&lt;/b&gt;은 Python에 &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 data-ke-size=&quot;size16&quot;&gt;Python은 실행 전까지 변수의 타입을 체크하지 않는 동적 타이핑을 사용하지만, typing을 쓰면 코드를 실행하지 않고도 mypy 같은 정적 분석 도구를 통해 오류를 잡아낼 수 있습니다.&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.9 버전 이상 부터 &lt;b&gt;기본적인 타입에 대해서는 typing 없이도 직접 타입을 명시&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775612506687&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def process_scores(names: list[str], scores: dict[str, int]) -&amp;gt; float:
    return sum(scores.values()) / len(names)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;타입 종류&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 typing에서 지원하는 타입들에 대한 목록입니다.&lt;/p&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;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;Annotated&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;부가정보&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;Annotated[&lt;span&gt;int&lt;/span&gt;, &lt;span&gt;&quot;range(0, 100)&quot;&lt;/span&gt;]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;Any&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;타입 제한 없음&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;Any&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;AsyncGenerator&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;비동기 제네레이터.&lt;br /&gt;await 함수 실행 + yield 반환 모두&lt;br /&gt;시행하는 함수의 반환 타입으로 사용&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;AsyncGenerator[Event, None]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;Callable&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;호출 가능한 값. 주로 함수&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;Callable[[int&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;int&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;],&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;int]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;Dict&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;딕셔너리&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;Dict[str,&amp;nbsp;Any]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;List&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;리스트&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;List[str]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;Literal&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;특정 값&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;Literal[&quot;A&quot;, &quot;Running&quot;, &quot;light&quot;]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.9845%;&quot;&gt;Optional&lt;/td&gt;
&lt;td style=&quot;width: 33.6821%;&quot;&gt;None이 될 수도 있는 값&lt;/td&gt;
&lt;td style=&quot;width: 43.3333%;&quot;&gt;Optional[int]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;제네릭 타입&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 구조를 먼저 선언한 뒤, 구조 내부에 들어갈 타입은 나중에 결정하고 싶을 때 사용합니다. TypeVar와 함께 쓰입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1775612924458&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import TypeVar, Generic

T = TypeVar('T')  # 이제부터 T는 타입을 의미함

class Box(Generic[T]):  # 클래스 내의 T는 나중에 사용자가 지정한 타입이 들어감
    def __init__(self, content: T):
        self.content = content

int_box = Box[int](10)  # 이제 T는 int를 의미함&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/Python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/297</guid>
      <comments>https://rayiarchive.tistory.com/297#entry297comment</comments>
      <pubDate>Wed, 8 Apr 2026 11:10:29 +0900</pubDate>
    </item>
    <item>
      <title>[n8n] n8n에 대하여</title>
      <link>https://rayiarchive.tistory.com/296</link>
      <description>&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;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxY6ny/dJMcaaY1AdA/F5Fhj7wC6GVUOpKh5yvCW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxY6ny/dJMcaaY1AdA/F5Fhj7wC6GVUOpKh5yvCW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxY6ny/dJMcaaY1AdA/F5Fhj7wC6GVUOpKh5yvCW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxY6ny%2FdJMcaaY1AdA%2FF5Fhj7wC6GVUOpKh5yvCW0%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;818&quot; height=&quot;327&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;n8n(n eight n)&lt;/b&gt;은 API, DB, SaaS와 같은 &lt;b&gt;여러가지 서비스를 서로 연결해서 자동적으로 업무를 수행&lt;/b&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 data-ke-size=&quot;size16&quot;&gt;자유도가 높다는 것은 진입 장벽 또한 높다는 의미이지만, 근래 LLM의 보편화로 인해 코드 생성이나 디버깅 난이도가 낮아짐에 따라 진입 장벽도 낮아져 대중적으로 많이 쓰이게 되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;MCP 자동화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n8n을 비롯한 워크플로우 자동화 도구가 많이 쓰이게 된 것은 AI agent 개발이 보편화 되면서, &lt;b&gt;MCP 서버*&lt;/b&gt;로 많은 기능들을 자동으로 처리하기 시작했기 때문입니다. 각 MCP가 제공하는 정보를 종합하고, 복잡한 업무 흐름을 자동화하려면 이들을 연결해주는 도구가 필요하기 마련입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;*MCP 서버:&lt;/b&gt; AI agent가 외부 서비스의 기능을 빌려와 더 다양한 기능을 사용할 수 있도록 만들어주는 서버&lt;/span&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;span style=&quot;color: #409d00;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;특히 n8n은 Slack, Google sheet, Gmail과 같은 보편적 서비스 뿐 아니라 AI agent를 위해 개발된 최신 서비스들까지 천 여개가 넘는 서비스들을 바로 연동할 수있는 범용성을 가지고 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;노드 구조&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vTwl3/dJMcaiQf3dF/DkAQ8ix52tueKKDbnDNq4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vTwl3/dJMcaiQf3dF/DkAQ8ix52tueKKDbnDNq4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vTwl3/dJMcaiQf3dF/DkAQ8ix52tueKKDbnDNq4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvTwl3%2FdJMcaiQf3dF%2FDkAQ8ix52tueKKDbnDNq4k%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;745&quot; height=&quot;419&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n8n의 각 작업은 노드(node)라는 단위로 구성됩니다. 각 노드는 &lt;b&gt;JSON&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;b&gt;Trigger :&lt;/b&gt; 특정 이벤트에 맞게 워크플로우의 작업을 개시합니다. 트리거 노드는 주황색 번개 아이콘으로 표시됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Action :&lt;/b&gt; 워크플로우의 작업을 수행합니다. 주로 데이터 처리나 외부 시스템 실행 등의 작업이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 하나의 워크플로우는 &lt;b&gt;Trigger &amp;rarr; Action1 &amp;rarr; Action2 &amp;rarr; &amp;hellip;&lt;/b&gt; 과 같은 구성으로 이루어져 있다고 생각할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FHANy/dJMcac3AQLb/SkY6DKtPTUIkm3LEJyCBz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FHANy/dJMcac3AQLb/SkY6DKtPTUIkm3LEJyCBz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FHANy/dJMcac3AQLb/SkY6DKtPTUIkm3LEJyCBz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFHANy%2FdJMcac3AQLb%2FSkY6DKtPTUIkm3LEJyCBz1%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;726&quot; height=&quot;372&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 워크플로우를 GUI로 다룰 수 있지만, 필요에 따라 JavaScript 코드나 HTTP 요청을 직접 수정할 수도 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. WSL + Docker Desktop 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n8n은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://rayiarchive.tistory.com/242&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker*&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;를 통해 간단하게 설치할 수 있습니다. &lt;b&gt;Docker Desktop&lt;/b&gt;이 없다면, 먼저 설치해 주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;*Docker:&lt;/b&gt; &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는&amp;nbsp;&lt;/span&gt;오픈 소스 가상화 플랫폼&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.docker.com/products/docker-desktop/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774791357078&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Docker Desktop: The #1 Containerization Tool for Developers | Docker&quot; data-og-description=&quot;Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.&quot; data-og-host=&quot;www.docker.com&quot; data-og-source-url=&quot;https://www.docker.com/products/docker-desktop/&quot; data-og-url=&quot;https://www.docker.com/products/docker-desktop/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pbgyo/dJMb89ydytH/Qb0M7KZcKkqI5ZYMLyif1K/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/bIo0nu/dJMb84p8D7W/roRmwIxQxtNyWvli6vqTL1/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/x2bvC/dJMb82MC3xc/dTC1TSYVvetUb6ZkiBQAv1/img.png?width=2320&amp;amp;height=1456&amp;amp;face=0_0_2320_1456&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.docker.com/products/docker-desktop/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pbgyo/dJMb89ydytH/Qb0M7KZcKkqI5ZYMLyif1K/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/bIo0nu/dJMb84p8D7W/roRmwIxQxtNyWvli6vqTL1/img.png?width=1110&amp;amp;height=580&amp;amp;face=0_0_1110_580,https://scrap.kakaocdn.net/dn/x2bvC/dJMb82MC3xc/dTC1TSYVvetUb6ZkiBQAv1/img.png?width=2320&amp;amp;height=1456&amp;amp;face=0_0_2320_1456');&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;Docker Desktop: The #1 Containerization Tool for Developers | Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 설치 옵션 중에서 &lt;b&gt;Use WSL2 instead of Hyper-V&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;n8n은 Linux 환경에서 표준적으로 작동합니다. Windows 환경에서는 WSL을 통해 n8n을 설치 및 실행할 수 있습니다. 이를 위해 WSL 또한 함께 설치해주어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774792030065&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wsl --install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 wsl과 docker 모두 버전이 정상적으로 나오면 설치가 성공적으로 완료된 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774792109107&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wsl --status

// 출력값(예시)
기본 배포: Ubuntu-24.04
기본 버전: 2

docker --version

// 출력값(예시)
Docker version 28.4.0, build d8eb465&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 컨테이너 생성 및 실행&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1774790517905&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker volume create n8n_data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n8n의 데이터를 저장할 &lt;b&gt;docker 볼륨을 생성&lt;/b&gt;합니다. &lt;b&gt;n8n_data&lt;/b&gt;는 볼륨의 이름입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774790750218&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d -it --rm \
  --name n8n \
  -p 5678:5678 \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 설정에 따라 &lt;b&gt;컨테이너를 생성 후 실행&lt;/b&gt;합니다. &lt;b&gt;docker.n8n.io/n8nio/n8n&lt;/b&gt;는 컨테이너로 생성할 이미지 파일 이름이며, 이는 n8n에서 공식으로 제작한 파일명입니다. 몇 가지 설정을 해 두었는데, 각각의 의미는 아래와 같습니다.&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: 12.6744%;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;-d&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;백그라운드에서 컨테이너가 실행됨.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;-it&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;터미널에서 로그를 실시간으로 볼 수 있음.&lt;br /&gt;&amp;nbsp; -i: interactive (입력 유지)&lt;br /&gt;&amp;nbsp; -t: terminal 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;--rm&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;컨테이너는 종료될 때 자동 삭제됨. (1회용일 때 적합)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;--name&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;n8n&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;컨테이너 이름.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;-p&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;5678:5678&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;포트 지정.&lt;br /&gt;내 PC의 5678 포트와 n8n 컨테이너의 5678 포트를 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;-v&lt;/td&gt;
&lt;td style=&quot;width: 32.093%;&quot;&gt;n8n_data:/home/node/.n8n&lt;/td&gt;
&lt;td style=&quot;width: 55.2326%;&quot;&gt;볼륨 n8n_data의 저장 위치를 /home/node/.n8n으로 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 아래와 같이 출력됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774792353952&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[license SDK] Skipping renewal on init: license cert is not initialized
Registered runner &quot;JS Task Runner&quot; (6KarhEyyibsJaxQMlKtWP)
Version: 2.13.4
Building workflow dependency index...
Finished building workflow dependency index. Processed 0 draft workflows, 0 published workflows.

Editor is now accessible via:
http://localhost:5678

Press &quot;o&quot; to open in Browser.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 실행&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;b&gt;http://localhost:5678&lt;/b&gt;에 접속하면 n8n의 GUI 인터페이스를 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ri6aV/dJMcaiW1XZD/TuiSJI5voyCfBM2o5vyP8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ri6aV/dJMcaiW1XZD/TuiSJI5voyCfBM2o5vyP8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ri6aV/dJMcaiW1XZD/TuiSJI5voyCfBM2o5vyP8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fri6aV%2FdJMcaiW1XZD%2FTuiSJI5voyCfBM2o5vyP8K%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;287&quot; height=&quot;488&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Automation</category>
      <category>automation</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/296</guid>
      <comments>https://rayiarchive.tistory.com/296#entry296comment</comments>
      <pubDate>Sun, 29 Mar 2026 12:06:07 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Library | 10. logging</title>
      <link>https://rayiarchive.tistory.com/295</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;logging&lt;/b&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 data-ke-size=&quot;size16&quot;&gt;코드의 진행상황을 출력하는 로그는 &lt;b&gt;print()&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;하지만 logging을 사용하면 출력 터미널에 기록이 나타나는 것에서 멈추지 않고 &lt;b&gt;파일로 남기거나, DB에 남기는 것&lt;/b&gt;도 가능해집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;getLogger( )&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;logging은 기본적으로 Logger 객체를 생성하는 것에서 시작합니다. Logger 객체는 &lt;b&gt;getLogger()&lt;/b&gt;함수로 생성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774512018525&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import logging

logger = logging.getLogger(__name__)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logger를 통해 띄울 수 있는 로그의 종류는 아래와 같습니다&lt;/p&gt;
&lt;pre id=&quot;code_1774512222461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    logger.debug(&quot;This is a debug message&quot;)  # 디버그
    logger.info(&quot;This is a info message&quot;)  # 정보
    logger.warning(&quot;This is a warning message&quot;)  # 경고
    logger.error(&quot;This is a error message&quot;)  # 에러
    logger.critical(&quot;This is a critical message&quot;)  # 치명적 에러&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;basicConfig( )&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이를 실행하면 &lt;b&gt;경고&lt;/b&gt;와 &lt;b&gt;에러&lt;/b&gt;, 그리고 &lt;b&gt;치명적 에러&lt;/b&gt; 로그만 출력됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774593440386&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;This is a warning message
This is a error message
This is a critical message&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;b&gt;로그가 출력되는 최소 기준선이 기본적으로 경고(warning) 레벨까지 설정&lt;/b&gt;되어 있기 때문입니다. Logger는 이 기준선 이상의 로그만 터미널에 출력하게 됩니다.&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;Logger의 &lt;b&gt;basicConfig( )&lt;/b&gt;함수는 이 기준선을 포함하여 출력 형식, 일자 등 세부사항들을 설정할 수 있습니다.&lt;/p&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;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&lt;b&gt;매개변수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 34.0697%;&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;level&lt;/td&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;logging.DEBUG | ... | logging.CRITICAL&lt;/td&gt;
&lt;td style=&quot;width: 34.0697%;&quot;&gt;로그 출력 기준선 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;format&lt;/td&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;width: 34.0697%;&quot;&gt;전체 출력 형식 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;datefmt&lt;/td&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;width: 34.0697%;&quot;&gt;날짜 출력 형식 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;filename&lt;/td&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;width: 34.0697%;&quot;&gt;로그를 저장할 파일 이름&lt;br /&gt;설정할 시 터미널에 출력되지 않는 대신 파일로 저장됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;filemode&lt;/td&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;'a' | 'w'&lt;/td&gt;
&lt;td style=&quot;width: 34.0697%;&quot;&gt;a : 이어서 쓰기&lt;br /&gt;w : 새로 쓰기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1774512856693&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    filename='test.log',
    filemode='a'
)
logger = logging.getLogger(__name__)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;asctime&amp;nbsp; |&lt;/b&gt;&amp;nbsp; 로그가 생성된 시간&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;levelname&amp;nbsp; |&lt;/b&gt;&amp;nbsp; 로그 레벨(debug, info 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;name&amp;nbsp; |&lt;/b&gt;&amp;nbsp; Logger 객체의 이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;lineno&amp;nbsp; |&lt;/b&gt;&amp;nbsp; 로그가 발생한 소스코드의 줄 번호&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;message&amp;nbsp; |&lt;/b&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;이제 새로운 Logger는 이렇게 로그를 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774513379310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2026-03-26 17:22:38 [DEBUG] __main__:11 - This is a debug message
2026-03-26 17:22:38 [INFO] __main__:12 - This is a info message
2026-03-26 17:22:38 [WARNING] __main__:13 - This is a warning message
2026-03-26 17:22:38 [ERROR] __main__:14 - This is a error message
2026-03-26 17:22:38 [CRITICAL] __main__:15 - This is a critical message&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;핸들러&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핸들러는 로그를 어느 위치에 어떤 방식으로 남길지 결정하는 객체입니다. basicConfig()는 기본적으로 rootLoggerHandler를 사용하는데, 이를 바꾸면 파일을 저장하면서도 터미널에서 로그를 출력할 수 있습니다.&lt;/p&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;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;핸들러&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;로그 출력 위치&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;StreamHandler&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;터미널에 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;FileHandler&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;파일에 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;RotatingFileHanlder&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;파일에 출력, 파일이 커지면 새 파일을 자동으로 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;TimedRotatingFileHandler&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;파일에 출력, 경과 시간을 넘기면 새 파일을 자동으로 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1774594091031&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import logging
import sys
from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

log_format = logging.Formatter(
    '%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 터미널 출력용 핸들러 설정
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(log_format)  # 출력 형태 설정
console_handler.setLevel(logging.DEBUG)  # 출력 기준 설정
logger.addHandler(console_handler)  # Logger에 핸들러 추가

# 파일 출력용 핸들러 설정
file_handler = TimedRotatingFileHandler(
    filename='test.log',
    when='midnight',     # 매일 자정에 로그 파일 교체
    interval=1,          # 1일 간격으로 교체
    backupCount=30,      # 최대 30개의 백업 파일 유지
    encoding='utf-8'     # 인코딩 설정
)
file_handler.setFormatter(log_format)  # 출력 형태 설정
file_handler.setLevel(logging.INFO)  # 출력 기준 설정
logger.addHandler(file_handler)  # Logger에 핸들러 추가&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/Python</category>
      <category>python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/295</guid>
      <comments>https://rayiarchive.tistory.com/295#entry295comment</comments>
      <pubDate>Thu, 26 Mar 2026 17:25:33 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Library | 09. dotenv</title>
      <link>https://rayiarchive.tistory.com/294</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;dotenv는 Python이 &lt;b&gt;많은 양의 환경변수를 관리할 수 있도록&lt;/b&gt; .env 파일을 사용하게 하는 라이브러리입니다.&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;dotenv는 별도로 설치가 필요합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774511205672&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install python-dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 변수가 한두 개일 때는 시스템 환경 변수에 추가해서 사용할 수 있습니다. 하지만 환경 변수가 많아지면 일일이 추가하는게 힘들어지므로, &lt;b&gt;.env 파일에 모두 저장하여 관리&lt;/b&gt;하는 경우도 있습니다. dotenv는 Python이 .env 파일에 접근할 수 있도록 해줍니다.&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;.env 파일이 이렇게 구성되어 있다고 합시다.&lt;/p&gt;
&lt;pre id=&quot;code_1774511166350&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ENV1=asdfghjk
ENV2=qwertyuiop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서 &lt;b&gt;load_dotenv( )&lt;/b&gt; 함수를 이용해 .env 파일을 불러올 수 있습니다. 불러온 환경변수들은 특정 변수에 저장되는 것은 아니고, os 라이브러리의 &lt;b&gt;enviro&lt;/b&gt;n 객체를 통해 접근할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774511328243&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import dotenv
import os

dotenv.load_dotenv()

env_1 = os.environ.get('ENV1')
env_2 = os.environ.get('ENV2')&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Language/Python</category>
      <category>python</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/294</guid>
      <comments>https://rayiarchive.tistory.com/294#entry294comment</comments>
      <pubDate>Thu, 26 Mar 2026 16:49:02 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 완전범죄</title>
      <link>https://rayiarchive.tistory.com/293</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A도둑과 B도둑이 팀을 이루어 모든 물건을 훔치려고 합니다. 단, 각 도둑이 물건을 훔칠 때 남기는 흔적이 누적되면 경찰에 붙잡히기 때문에, 두 도둑 중 누구도 경찰에 붙잡히지 않도록 흔적을 최소화해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물건을 훔칠 때 조건은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;물건 i를 훔칠 때,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A도둑이 훔치면 &lt;code&gt;info\[i\]\[0\]&lt;/code&gt;개의 A에 대한 흔적을 남깁니다.&lt;/li&gt;
&lt;li&gt;B도둑이 훔치면 &lt;code&gt;info\[i\]\[1\]&lt;/code&gt;개의 B에 대한 흔적을 남깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 물건에 대해 A도둑과 B도둑이 남기는 흔적의 개수는 1 이상 3 이하입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경찰에 붙잡히는 조건은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A도둑은 자신이 남긴 흔적의 누적 개수가 &lt;code&gt;n&lt;/code&gt;개 이상이면 경찰에 붙잡힙니다.&lt;/li&gt;
&lt;li&gt;B도둑은 자신이 남긴 흔적의 누적 개수가 &lt;code&gt;m&lt;/code&gt;개 이상이면 경찰에 붙잡힙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 물건을 훔칠 때 생기는 흔적에 대한 정보를 담은 2차원 정수 배열 &lt;code&gt;info&lt;/code&gt;, A도둑이 경찰에 붙잡히는 최소 흔적 개수를 나타내는 정수 &lt;code&gt;n&lt;/code&gt;, B도둑이 경찰에 붙잡히는 최소 흔적 개수를 나타내는 정수 &lt;code&gt;m&lt;/code&gt;이 매개변수로 주어집니다. 두 도둑 모두 경찰에 붙잡히지 않도록 모든 물건을 훔쳤을 때, A도둑이 남긴 흔적의 누적 개수의 최솟값을 return 하도록 solution 함수를 완성해 주세요. 만약 어떠한 방법으로도 두 도둑 모두 경찰에 붙잡히지 않게 할 수 없다면 -1을 return해 주세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;info&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;n&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;m&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;result&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[[1,&amp;nbsp;2],&amp;nbsp;[2,&amp;nbsp;3],&amp;nbsp;[2,&amp;nbsp;1]]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[[1,&amp;nbsp;2],&amp;nbsp;[2,&amp;nbsp;3],&amp;nbsp;[2,&amp;nbsp;1]]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[[3,&amp;nbsp;3],&amp;nbsp;[3,&amp;nbsp;3]]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;[[3,&amp;nbsp;3],&amp;nbsp;[3,&amp;nbsp;3]]&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;조건&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;le; &lt;code&gt;info&lt;/code&gt;의 길이 &amp;le; 40
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;info\[i\]&lt;/code&gt;는 물건 i를 훔칠 때 생기는 흔적의 개수를 나타내며, &lt;code&gt;\[A에 대한 흔적 개수, B에 대한 흔적 개수\]&lt;/code&gt;의 형태입니다.&lt;/li&gt;
&lt;li&gt;1 &amp;le; 흔적 개수 &amp;le; 3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1 &amp;le; &lt;code&gt;n&lt;/code&gt; &amp;le; 120&lt;/li&gt;
&lt;li&gt;1 &amp;le; &lt;code&gt;m&lt;/code&gt; &amp;le; 120&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;답&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP로 해결할 수 있습니다.&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;b&gt;A의 흔적 합&lt;/b&gt;을 상태값으로, 그 때 &lt;b&gt;B가 가질 수 있는 최소 흔적 합&lt;/b&gt;을 값으로 두는 1차원 배열을 준비합니다.&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;흔적 합은 최대 120까지 가질 수 있으므로, DP값의 &lt;b&gt;초기값은 121(INF)&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;단, 흔적 합 0은 불가능한 경우이므로 &lt;b&gt;0&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774244994224&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(info, n, m):
    dp = [121] * n
    dp[0] = 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 물건 마다 두 가지 경우에 대해 DP를 업데이트 해야 합니다. 업데이트는 &lt;b&gt;새로운 DP 배열인 NDP&lt;/b&gt;에서 진행하고, 업데이트가 모두 끝나면 &lt;b&gt;이전단계 DP에 NDP를 덮어 씁니다&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;b&gt;경우 1.&lt;/b&gt; A가 물건을 훔칠 때 : 모든 DP 경우에 대해 t_a만큼 증가한 경우(&lt;b&gt;ndp[i + t_a]&lt;/b&gt;)를 업데이트합니다. B가 물건을 훔치지 않았기 때문에 B의 흔적합은 따로 증가하지 않습니다.&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;b&gt;경우 2.&lt;/b&gt; B가 물건을 훔칠 때 : 모든 DP 경우에 대해 현재 상태(&lt;b&gt;i&lt;/b&gt;)를 업데이트 합니다. B가 물건을 훔쳤으므로 B의흔적 합은 &lt;b&gt;t_b&lt;/b&gt;만큼 증가합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1774244640856&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(info, n, m):
    # ...
    for t_a, t_b in info:
        ndp = [121] * n
        
        for i in range(n):
            if dp[i] == 121:
                continue
            # case 1.
            if i + t_a &amp;lt; n:
                ndp[i + t_a] = min(ndp[i + t_a], dp[i])
            # case 2.
            ndp[i] = min(ndp[i], dp[i] + t_b)
        
        dp = ndp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP가 완성되면 A의 흔적합을 1부터 n까지 순회하며 최초로 B의 흔적합이 m보다 작아지는 경우를 찾습니다.&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;그 때의 A 흔적합 i를 반환하고, 그 값이 없다면 -1을 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1774245466175&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(info, n, m):
    ## ...
    for i in range(n):
        if dp[i] &amp;lt; m:
            return i
    return -1&lt;/code&gt;&lt;/pre&gt;</description>
      <category>CS/PS</category>
      <author>Rayi</author>
      <guid isPermaLink="true">https://rayiarchive.tistory.com/293</guid>
      <comments>https://rayiarchive.tistory.com/293#entry293comment</comments>
      <pubDate>Mon, 23 Mar 2026 15:00:15 +0900</pubDate>
    </item>
  </channel>
</rss>