<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko">
  <id>https://titanium-haiku-594.appspot.com/</id>
  <title>Blog - Posts in Python</title>
  <updated>2024-07-16T17:59:53.624933+00:00</updated>
  <link href="https://titanium-haiku-594.appspot.com/"/>
  <link href="https://titanium-haiku-594.appspot.com/blog/category/python/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.8">ABlog</generator>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/static-website/</id>
    <title>정적 웹 서비스를 파이썬으로 Serving 하기</title>
    <updated>2024-05-16T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="serving"&gt;

&lt;p&gt;여러 이유가 있겠지만 정적인 파일로만 구성된 웹 사이트를 서비스해야할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 때 정적인 웹 사이트를 생성하고 서비스 하는 여러 방법이 있겠지만 이 글에서는 파이썬으로 작성된 static3를 사용합니다.&lt;/p&gt;
&lt;p&gt;static3은 파이썬으로 작성된 정적 웹 콘텐츠를 쉽게 서비스하기 위한 라이브러입니다. 이 구성을 사용하려면 다음과 같은 디렉터리 구성이 필요합니다.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;static_root&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;____&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;____&lt;/span&gt; &lt;span class="n"&gt;www&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;____&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;____&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;우리는 여기서 static3를 사용할 것이므로 requirements.txt에 다음 내용을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;static3
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;그리고 main.py는 다음과 같이 구성합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Cling&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Cling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;www&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이것으로 정적 웹 사이트를 구성하기 위한 준비가 완료되었습니다.&lt;/p&gt;
&lt;p&gt;GCP를 사용한다면 앱엔진에 업로드할 수 있습니다.&lt;/p&gt;
&lt;p&gt;App Engine에 업로드하려면 다음 명령을 입력합니다. static_root에서 다음 명령을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;gcloud&lt;span class="w"&gt; &lt;/span&gt;app&lt;span class="w"&gt; &lt;/span&gt;deploy
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;static3은 기본적으로 브라우저가 / 를 요청하면 web 디렉터리의 index.html을 서비스하고 이 외의 모든 경우는 web 디렉터리에서 브라우저로부터 요청받은 경로를 찾아서 서비스합니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 구글 앱 엔진에 업로드하는 것만 다루었지만 파이썬 WSGI 기반 컨테이너를 구동할 수 있으면 모든 환경에서 사용할 수 있습니다.&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/static-website/"/>
    <summary>여러 이유가 있겠지만 정적인 파일로만 구성된 웹 사이트를 서비스해야할 수 있습니다.이 때 정적인 웹 사이트를 생성하고 서비스 하는 여러 방법이 있겠지만 이 글에서는 파이썬으로 작성된 static3를 사용합니다.static3은 파이썬으로 작성된 정적 웹 콘텐츠를 쉽게 서비스하기 위한 라이브러입니다. 이 구성을 사용하려면 다음과 같은 디렉터리 구성이 필요합니다.</summary>
    <category term="static" label="static"/>
    <category term="static3" label="static3"/>
    <published>2024-05-16T00:00:00+09:00</published>
  </entry>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/python-setuptools/</id>
    <title>파이썬 패키징의 시작점 - setuptools</title>
    <updated>2024-05-15T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="setuptools"&gt;

&lt;p&gt;파이썬 모듈 배포는 다음과 같은 방법이 주로 사용됩니다.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;패키지 파일로 묶어서 배포&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;압축 파일로 묶어서 배포&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 글에서는 패키지 파일로 묶어서 배포하는 방법에 오랫동안 사용되어온 setuptools 패키지를 살펴보겠습니다. setuptools가 하는 일은 파이썬 소스 코드, 관련 리소스 파일을 패키징 파일 및 압축 파일로 만들어줍니다.&lt;/p&gt;
&lt;section id="id1"&gt;
&lt;h2&gt;파이썬 스크립트를 어떻게 배포할 수 있지?&lt;/h2&gt;
&lt;p&gt;초기의 파이썬 설계에서는 모듈을 배포하는 일이 중요하게 여겨지지 않았지만 파이썬 모듈 배포 요구가 생기면서 파이썬 핵심 개발팀은 1998년 말에 distutils-sig 그룹을 만들어 배포 관련 기능을 논의하기 시작합니다. 이 논의의 결과물인 distutils가 개발되어 1.5.2 버전 때는 외부 패키지로 2000년에 파이썬 1.6에 공식 라이브러리로 포함되어 배포됩니다.&lt;/p&gt;
&lt;p&gt;distutils가 개발되던 때에도 다른 파이썬 패키징 도구가 고유한 규격을 가지고 개발되긴 했지만 distutils가 파이썬 패키징 표준으로 인정받게 된 것은 다른 패키징 도구에 없던 C 확장 라이브러리를 컴파일하는 기능이 포함되어 있었기 때문이었습니다. distutils를 사용한 모듈 배포에는 다음 2개의 파일이 필요합니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;setup.py&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;requirements.txt&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 2개의 파일의 역할은 다음과 같습니다. &lt;cite&gt;setup.py&lt;/cite&gt; 는 파이썬 소스 코드와 파이썬 패키지 디렉터리들을 패키징 파일로 만드는데 사용하고 &lt;cite&gt;requirements.txt&lt;/cite&gt; 는 배포된 파이썬 모듈을 사용하는데 필요한 외부 패키지 이름과 버전을 명시하는데 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;setup.py&lt;/cite&gt; 은 파이썬 코드로 작성하며 distutils에서 가져온 setup 함수에 만들려고 하는 파이썬 패키지 정보를 함수 인자로 전달합니다.&lt;/p&gt;
&lt;p&gt;그리고 다음과 같은 명령어로 파이썬 바이너리 패키지를 만듭니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;bdist
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이렇게 하면 .tar.gz 형식으로 배포 파일이 만들어집니다. distutils로 만들어진 모듈 파일을 배포하려면 .tar.gz 파일과 requirements.txt를 함께 배포하면 되었습니다.&lt;/p&gt;
&lt;section id="distutils"&gt;
&lt;h3&gt;distutils 구조 설명&lt;/h3&gt;
&lt;p&gt;distutils로 파이썬 모듈들을 배포하려면 다음과 같이 setup.py를 작성해야 합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;distutils.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;py_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;setup 함수에는 여러 인자를 전달할 수 있지만 예시에서는 name, version, py_modules 인자를 전달했습니다. name은 패키지의 이름, version은 패키지의 버전을 문자열로 지정합니다. 마지막으로 py_modules 인자에는 list 타입 인자를 전달했습니다.&lt;/p&gt;
&lt;p&gt;py_modules 인자에 전달하는 리스트의 요소값은 패키지에 포함하려는 모듈을 명시합니다. 이 때 모듈은 파이썬 파일을 의미하며 여기에는 모듈의 이름만 명시하면 되기 때문에 .py 확장자는 빼고 명시합니다.&lt;/p&gt;
&lt;p&gt;만약 패키지에 포함하려는 파이썬 모듈이 &lt;cite&gt;setup.py&lt;/cite&gt; 와 같은 레벨에 있으면 앞의 코드를 사용하고 하위 패키지에 있으면 package_dir 인자와 packages 인자를 전달합니다.&lt;/p&gt;
&lt;p&gt;이렇게 &lt;cite&gt;setup.py&lt;/cite&gt; 를 만들었으면 python 명령으로 패키징을 합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;bdist
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;하지만 distutils에는 여러 문제가 있었습니다. 대표적으로 파이썬 코드를 패키징할 순 있었지만 종속성 패키지를 requirements.txt로 따로 관리해야 하며 메타 파일은 패키지에 포함할 수 없었습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id2"&gt;
&lt;h3&gt;setuptools의 등장&lt;/h3&gt;
&lt;p&gt;distutils는 파이썬 모듈을 쉽게 배포할 수 있도록 도와주긴 했지만 얼마 지나지 않아 distutils가 가진 문제에 부딪치면서 새로운 배포 도구 필요성이 증대되었습니다. 이에 &lt;cite&gt;Phillip Eby&lt;/cite&gt; 가 distutils를 확장한 setuptools 0.3a1을 2004년에 발표했습니다.&lt;/p&gt;
&lt;p&gt;setuptools는 distutils의 구조와 기능을 가져왔기 때문에 distutils에서 작성했던 &lt;cite&gt;setup.py&lt;/cite&gt; 는 아래와 같이 바꿔쓸 수 있습니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;setuptools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;py_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;setup 함수를 가져오는 위치를 제외하고는 distutils와 setuptools 모든게 동일한 것을 볼 수 있습니다. 개별 모듈이 아니라 패키지를 사용할 때도 같습니다. 패키징 파일을 만드는 것도 distutils를 사용할 때와 같습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;bdist
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;distutils에 비해 진보적이고 패키지 설치 시 종속성 관리, 메타 데이터 등을 포함할 수 있는 기능을 가지고 있었기에 많은 사람들의 지지를 받았고 2004년부터 파이썬 재단은 distutils 대신에 setuptools를 쓰는 것을 권장하기 시작했습니다.&lt;/p&gt;
&lt;p&gt;setuptools는 파이썬 2.4와 3.4 이전에는 따로 설치해서 사용할 수 밖에 없었지만 파이썬 2.4와 3.4가 출시되면서 setuptools가 설치되기 시작했습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="egg"&gt;
&lt;h3&gt;egg의 등장&lt;/h3&gt;
&lt;p&gt;setuptools는 0.3a2와 함께 &lt;cite&gt;egg&lt;/cite&gt; 라는 독자 패키지 파일 포맷과 이 패키지 파일을 파이썬 환경에 설치할 수 있도록 하는 &lt;cite&gt;easy_install&lt;/cite&gt; 프로그램을 제공하기 시작했습니다.&lt;/p&gt;
&lt;p&gt;distutils를 사용해 만들어진 패키지 파일 배포는 여러 문제가 있었습니다. 그 중 하나가 패키지가 C 확장을 포함하고 있는 경우입니다. 이 경우 C 확장이 어떤 파이썬 버전에 어떤 운영체제에서 동작하는지 여부 등을 알 수 없기도 했고 설치된 파이썬 패키지가 어떤 파일들을 포함하고 있는지 정보를 전혀 알 수 없다는 것이었습니다.&lt;/p&gt;
&lt;p&gt;이런 문제를 해결하기 위해 파이썬 커뮤니티에서 제안된 것이 egg 포맷입니다. egg 포맷은 패키지의 메타 정보와 패키지 소스 파일, 컴파일된 C 확장 등을 포함하는 zip 압축 파일입니다. 다른 언어에서 개념을 빌려온다면 .jar 와 비슷한 구조입니다.&lt;/p&gt;
&lt;p&gt;egg 포맷은 C 확장을 포함한 모듈들이 독자적으로 묶여있기 때문에 배포하기도 쉽고 사용하기도 쉬웠습니다. 자바의 jar를 사용할 때처럼 PYTHONPATH 환경 변수에 egg 파일의 경로만 담아두어도 파이썬 인터프리터에서 사용할 수 있었을 정도였습니다.&lt;/p&gt;
&lt;p&gt;egg 포맷은 패키지를 설명하기 위해 패키지의 메타 데이터를 저장하기 &lt;cite&gt;.egg-info&lt;/cite&gt; 이름을 가진 디렉터리를 만듭니다. 이 디렉터리에는 패키지를 설명하기 위한 메타 데이터를 .txt 파일 형식으로 관리합니다.&lt;/p&gt;
&lt;p&gt;대표적으로 다음과 같은 메타 데이터 파일이 .egg-info 디렉터리에 만들어집니다. 패키지 설정에 따라 파일들이 만들어지지 않을 수도 있습니다. &lt;a class="footnote-reference brackets" href="#id5" id="id3" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;1&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;top_level.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SOURCES.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;requires.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;setup_requires.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dependency_links.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;namespace_packages.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;entry_point.txt&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PKG-INFO&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;egg 파일은 다음과 방법으로 생성합니다.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python setup.py bdist_egg
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 명령을 통해 egg 파일이 만들어지면 easy_install 명령을 사용해 egg 파일을 파이썬 환경에 설치할 수 있습니다.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ easy_install sample.egg
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;easy_install은 egg 파일 뿐만 아니라 .tar.gz 로 묶인 패키지 파일 설치도 가능합니다. easy_install 이전엔 파이썬 패키지를 설치하려면 패키지 파일의 압축을 풀고 &lt;cite&gt;setup.py&lt;/cite&gt; 스크립트를 직접 실행했어야 했습니다.&lt;/p&gt;
&lt;p&gt;egg 와 easy_install의 규격은 파이썬 표준안인 PEP로 받아들여지진 않았지만 몇 년 동안 파이썬 중앙 패키지 저장소인 PyPI 및 SVN 저장소로부터 패키지를 설치하는데 유용하게 사용되었습니다.&lt;/p&gt;
&lt;p&gt;하지만 easy_install 은 패키지 설치에만 최적화 되어 있을 뿐 이미 설치된 패키지를 제거하지 못하는 문제가 있었습니다.&lt;/p&gt;
&lt;p&gt;이 글의 뒤에서 소개하지만 egg 포맷의 대체인 wheel 포맷을 사용하는 현재 .egg-info 디렉터리는 현재도 사용되고 있습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="pip"&gt;
&lt;h3&gt;pip의 등장&lt;/h3&gt;
&lt;p&gt;파이썬 가상 환경 패키지인 virtualenv를 개발한 Ian Bicking은 easy_install에 있었던 여러 문제를 해결한 도구인 pip를 2008년에 공개했습니다. pip(package installer for Python)는 파이썬 패키지를 설치하고 삭제하는 기능 외에도 파이썬 환경에 설치된 패키지 목록 등을 볼 수 있는 기능을 제공했습니다.&lt;/p&gt;
&lt;p&gt;pip가 easy_install에 비해서 좋은 도구였기에 setuptools 때와 같이 곧 많은 사람들의 지지를 얻었습니다. 이 지지를 발판 삼아 pip는 Python 2.4와 Python 3.4에 포함되어 배포되기 시작했습니다.&lt;/p&gt;
&lt;p&gt;pip 1.1 버전과 현재의 pip는 기능의 차이가 있지만 install 기능과 uninstall 기능이 들어온 것만으로도 pip는 easy_install 과 비교해 단점들이 채워졌습니다.&lt;/p&gt;
&lt;p&gt;pip 이전에 setup.py를 사용해 파이썬 환경에 개발중인 프로그램을 편집 모드로 설치하려면 &lt;code class="code shell docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;develop&lt;/span&gt;&lt;/code&gt; 명령을 실행했었습니다. pip가 나온 뒤에는 &lt;code class="code shell docutils literal notranslate"&gt;&lt;span class="pre"&gt;pip&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt; &lt;span class="pre"&gt;-e&lt;/span&gt; &lt;span class="pre"&gt;.&lt;/span&gt;&lt;/code&gt; 명령을 사용하기 때문에 만약 파이썬 패키징 도구로 setuptools 가 사용되지 않더라도 상관없습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="pypa"&gt;
&lt;h3&gt;pypa의 등장&lt;/h3&gt;
&lt;p&gt;setuptools, virtualenv, pip 의 등장은 파이썬 생태계에 있어 매우 놀라운 변화를 가져왔지만 어디까지나 개인이 개발한 도구였던 탓에 유지 보수 등의 문제가 있었습니다. 이에 따라 &lt;cite&gt;Ian Bicking&lt;/cite&gt; 이 개발하고 &lt;cite&gt;led by Carl Meyer&lt;/cite&gt;, &lt;cite&gt;Brian Rosner&lt;/cite&gt; and &lt;cite&gt;Jannis Leidel&lt;/cite&gt; 이 유지보수한 &lt;cite&gt;virtualenv&lt;/cite&gt;, &lt;cite&gt;pip&lt;/cite&gt; 를 이어받기 위한 논의가 촉발됐습니다.&lt;/p&gt;
&lt;p&gt;이 논의의 결과로 2011년 2월 18일에 PyPA가 출범하게 됩니다. PyPA가 출범하게 되면서 파이썬 핵심 개발팀은 패키징에 관한 기능은 PyPA에 미루고 핵심 기능 개발에 매진하게 됩니다. 2024년 현재 PyPA는 파이썬 패키징과 관련해 가장 권위있고 패키징 표준에 활발하게 참여하는 단체입니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="wheel"&gt;
&lt;h3&gt;wheel 포맷의 등장&lt;/h3&gt;
&lt;p&gt;PyPA 출범 이후 파이썬 패키징 생태계는 &lt;cite&gt;.egg&lt;/cite&gt; 포맷이 가지고 있던 여러 단점을 보완하기 위해 새로운 바이너리 포맷을 만들게 됩니다. Distutils-SIG 그룹은 새로운 바이너리 포맷을 위한 태그 호환성 기준을 2012년 9월 8일에 &lt;cite&gt;PEP 425&lt;/cite&gt; 로 발표하고 2012년 9월 20일에 &lt;cite&gt;PEP 427&lt;/cite&gt; 을 발표함으로서 egg 포맷을 뒤로 하게 됩니다.&lt;/p&gt;
&lt;p&gt;wheel 바이너리 포맷은 &lt;cite&gt;wheel&lt;/cite&gt; 이라는 패키지를 설치해야만 사용할 수 있지만 wheel 패키지는 setuptools를 사용해 만들 수 있습니다. setup.py를 사용한 경우 다음 명령어로 만듭니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;bdist_wheel
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;wheel 포맷 이전에 가장 많이 쓰였던 패키징 포맷이 egg 포맷이었기 때문에 프로그램을 pip 명령을 사용해 파이썬 환경에 설치하면 .egg-info 디렉터리가 생성되는 등 아직 egg 포맷의 흔적이 남아있습니다.&lt;/p&gt;
&lt;p&gt;egg 포맷은 .egg 파일에 포함되었던 &lt;cite&gt;.egg-info&lt;/cite&gt; 디렉터리를 포함하지만 wheel 포맷은 &lt;cite&gt;.dist-info&lt;/cite&gt; 디렉터리를 포함하도록 변경되었습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id4"&gt;
&lt;h3&gt;마치며..&lt;/h3&gt;
&lt;p&gt;이 글에서는 파이썬 패키징 생태계에서 가장 오랫동안 사용되어온 setuptools와 관련 생태계를 살펴보았습니다. 과거와 달리 최근의 파이썬 패키징은 setuptools에만 의존하고 있지 않습니다.&lt;/p&gt;
&lt;p&gt;파이썬 패키징 도구는 과거와 달리 더 이상 독점적이지 않은데다 setuptools 자체가 파이썬의 표준 패키징 도구는 아닙니다. 이에 더해 파이썬은 setuptools 외에도 여러 빌드 도구를 사용하는 것을 권장하고 있습니다.&lt;/p&gt;
&lt;p&gt;필자가 알고 있는 빌드 도구로는 flit, poetry, pdm 등이 있습니다. 이들 도구에 대해선 다음에 설명할 기회가 있을것으로 생각합니다.&lt;/p&gt;
&lt;p&gt;이번 글은 여기에서 마치겠습니다.&lt;/p&gt;
&lt;p&gt;from. 파이썬 수다장이 날다의 아저씨&lt;/p&gt;
&lt;p class="rubric"&gt;각주&lt;/p&gt;
&lt;aside class="footnote-list brackets"&gt;
&lt;aside class="footnote brackets" id="id5" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id3"&gt;1&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;egg가 처음 만들어졌을때와 달리 만들어지는 파일이 다릅니다. 2024년 현재 기준으로 적었습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/python-setuptools/"/>
    <summary>파이썬 모듈 배포는 다음과 같은 방법이 주로 사용됩니다.패키지 파일로 묶어서 배포압축 파일로 묶어서 배포</summary>
    <category term="distutils" label="distutils"/>
    <category term="python" label="python"/>
    <category term="setuptools" label="setuptools"/>
    <category term="venv" label="venv"/>
    <category term="virtualenv" label="virtualenv"/>
    <published>2024-05-15T00:00:00+09:00</published>
  </entry>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/python-virtualenv/</id>
    <title>파이썬 가상 환경 이야기</title>
    <updated>2024-05-11T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="id1"&gt;

&lt;section id="id2"&gt;
&lt;h2&gt;가상 환경 개요&lt;/h2&gt;
&lt;p&gt;필자가 처음 컴퓨터 프로그램 언어를 배웠을 때는 모든 프로그램은 한 개 파일만 만들면 되는 줄 알고 있었고 “Hello World”를 화면에 출력하는 것만으로 “프로그램 동작하네?” 하면서 금방 흥미를 잃고 말았었습니다.&lt;/p&gt;
&lt;p&gt;C, C++ 언어와 같은 컴파일 언어는 컴파일러에게 소스 코드에서 링크할 라이브러리의 경로를 제공하면 되기에 운영체제에 같은 라이브러리가 버전별로 설치가 되어 있어도 큰 문제가 되지 않습니다.&lt;/p&gt;
&lt;p&gt;이런 점에선 자바 가상 머신을 사용하는 자바 언어도 컴파일 시에 링크할 라이브러리 경로만 알려주면 되기 때문에 프로그램을 만드는 과정에서 여러 버전을 가진 라이브러리가 있어도 상관없습니다.&lt;/p&gt;
&lt;p&gt;하지만 파이썬의 경우는 조금 다릅니다. 파이썬은 import 또는 from import 를 사용해 프로그램이 실행될 동안에 참고할 라이브러리를 가져옵니다. 파이썬은 import가 실행되면 파이썬 라이브러리 저장소에서 라이브러리를 찾습니다. 파이썬의 라이브러리 저장소는 파이썬 인터프리터 경로에 있는 site-packages 경로 아래에서 찾습니다. &lt;a class="footnote-reference brackets" href="#id11" id="id3" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;1&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;예를 들어 파이썬 프로그램 A1과 B1 이란 이름을 가지는 프로그램을 개발하고 있고 프로그램이 request-ceta 라이브러리 1.0과 2.0를 사용하고 각 프로그램이 라이브러리 버전에 종속성을 강하게 가진다고 가정해보겠습니다.&lt;/p&gt;
&lt;p&gt;프로그램 A1&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;request-ceta 1.0&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;프로그램 B1&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;request-ceta 2.0&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;프로그램 A1과 B1을 전역 파이썬 환경에서 개발한다면 전역 파이썬 환경에 request-ceta 라이브러리의 1.0과 2.0 라이브러리를 설치해야 하지만 하나의 파이썬 환경에 request-ceta 라이브러리 1.0과 2.0을 동시에 설치하는 것은 불가능합니다. 일반적으로 라이브러리는 상위 버전이 하위 버전의 기능을 포함하는 하위 호환성을 갖추고 있는 경우가 많기에 request-ceta 2.0 라이브러리를 설치해도 문제가 되지 않을수도 있습니다.&lt;/p&gt;
&lt;p&gt;그런데 프로그램 B1을 먼저 개발하고 프로그램 A1을 나중에 개발하는 경우라면 프로그램 A1의 설치 환경을 구성할 때 request-ceta 2.0 라이브러리가 사라지고 request-ceta 1.0 라이브러리가 대신 설치됩니다.&lt;/p&gt;
&lt;p&gt;이렇게 되면 프로그램 B1을 이어서 추가로 개발하거나 실행하려고 하면 라이브러리 버전 종속성 때문에 프로그램 B1의 개발 환경을 다시 구성해야 합니다.&lt;/p&gt;
&lt;p&gt;라이브러리 종속성은 개발하는 프로그램에만 있는 것은 아닙니다. 라이브러리간에도 종속성을 가지고 있는 경우도 있기 때문에 파이썬 프로그램을 개발할 때는 프로그램마다 별도의 파이썬 환경을 가지는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;바로 이런 이유 때문에 가상 환경이라는 개념이 등장합니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id4"&gt;
&lt;h2&gt;파이썬의 가상 환경&lt;/h2&gt;
&lt;p&gt;프로그램을 개발하는 언어는 파이썬 외에도 여러개가 있다보니 운영체제에 파이썬 환경을 하나만 설치하는데 이렇게 하면 프로그램 종속성 문제가 발생하기 쉽습니다. 운영체제가 윈도우 환경이면 전역 환경에 파이썬 패키지를 설치하는 것이 가능하지만 운영체제가 리눅스 환경이면 PEP 668의 규약에 의해서 파이썬 전역 환경에 패키지를 설치하는 것이 금지되어 있습니다.&lt;/p&gt;
&lt;p&gt;물론 PEP 668 이전에 라이브러리 종속성 문제를 해결하기 위해 파이썬 커뮤니티는 종속성 문제를 해결하기 위해 특별한 개념을 도입했습니다. &lt;cite&gt;파이썬 가상 환경&lt;/cite&gt; 이라는 개념이 바로 그것입니다. 파이썬 가상 환경은 파이썬 인터프리터는 시스템에 설치된 파이썬 버전을 공유하고 라이브러리는 가상 환경 디렉터리 안의 site-packages 디렉터리에서 찾습니다.&lt;/p&gt;
&lt;p&gt;파이썬에서 가상 환경을 만들어주는 라이브러리로 가장 유명한 것은 virtualenv 입니다. virtualenv는 2007년 0.8이 발표되었으며 2024년 현재도 개발이 이루어지고 있습니다. 파이썬 가상 환경은 파이썬 3.2 버전이 발표될 때 까지만 해도 별도의 외부 패키지로 제공됐었지만 3.3부터 virtualenv의 기능이 파이썬 내장 라이브러리인 venv로 공식 포함됐습니다.&lt;/p&gt;
&lt;p&gt;지금 여러분이 사용하는 파이썬 환경이 3.3 이후 버전을 사용한다면 virtualenv를 사용하지 않아도 가상 환경을 만들 수 있습니다.&lt;/p&gt;
&lt;section id="id5"&gt;
&lt;h3&gt;파이썬 가상 환경 만들기&lt;/h3&gt;
&lt;p&gt;파이썬 가상 환경을 만들 수 있는 방법은 다음과 같은 방법이 있습니다&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;venv 모듈을 사용하기&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;virtualenv 패키지를 사용하기&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pyenv에 있는 virtualenv 기능을 사용하기&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;물론 이 방법 외에도 다른 방법이 있지만 이런 방법에 대해선 추후 다른 글에서 설명하겠습니다. 이 글에서는 virtualenv 패키지와 파이썬 공식 venv 모듈을 사용하는 방법만 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;virtualenv 패키지를 설치하는 방법도 여러가지가 있어 이 글에서 전부 소개하지 않으며 이 글에서는 여러분의 파이썬 환경에 virtualenv 패키지가 설치되어 있다고 가정합니다.&lt;/p&gt;
&lt;section id="virtualenv"&gt;
&lt;h4&gt;virtualenv 패키지를 사용해 파이썬 가상 환경 만들기&lt;/h4&gt;
&lt;p&gt;virtualenv 패키지가 파이썬 환경에 설치되어 있다면 다음과 같은 명령으로 쉽게 설치할 수 있습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;virtualenv&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;가상환경&lt;span class="w"&gt; &lt;/span&gt;이름&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;virtualenv 패키지는 virtualenv라는 명령을 사용해서 파이썬 가상 환경을 생성합니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;가상환경 이름&amp;gt;은 영문자로 시작하는 이름을 사용하는 것이 좋습니다. 파이썬 가상 환경이 디렉터리로 생성되기 때문입니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="venv"&gt;
&lt;h4&gt;venv 모듈을 사용해 파이썬 가상 환경 만들기&lt;/h4&gt;
&lt;p&gt;파이썬 내장 모듈인 venv를 사용해서 만드는 방법은 다음과 같습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;venv&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;가상환경&lt;span class="w"&gt; &lt;/span&gt;이름&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;venv 모듈은 파이썬 내장 모듈이기 때문에 모듈이 있는 파이썬 인터프리터 경로를 명시해야 합니다. 리눅스와 맥은 인터프리터 경로로 python 이 아니라 python3 을 사용해야 합니다.&lt;/p&gt;
&lt;p&gt;윈도우는 python 명령을 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;lt;가상환경 이름&amp;gt;은 영문자로 시작하는 이름을 사용하는 것이 좋습니다. 파이썬 가상 환경이 디렉터리로 생성되기 때문입니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id6"&gt;
&lt;h3&gt;파이썬 가상 환경 진입 하기&lt;/h3&gt;
&lt;p&gt;파이썬 가상 환경을 만드는 것은 virtualenv 패키지와 venv 모듈은 동일한 구조를 가지는 가상 환경 디렉터리가 만들어지기 때문에 가상 환경 진입은 윈도우일때와 리눅스/맥 일 때 환경이 다릅니다.&lt;/p&gt;
&lt;section id="id7"&gt;
&lt;h4&gt;윈도우에서 진입 하기&lt;/h4&gt;
&lt;p&gt;윈도우에서 파이썬 가상 환경 진입은 “명령 프롬프트”를 사용할 때와 “파워셸”일 때가 조금 다릅니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id12"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;명령프롬프트 일 때 파이썬 가상 환경 진입&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;가상환경&lt;span class="w"&gt; &lt;/span&gt;이름&amp;gt;&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;파워셸에서 파이썬 가상 환경을 진입하려면 윈도우에서 ExecutionPolicy 정책이 RemoteSigned로 설정 되어 있어야 합니다. ExecutionPolicy 정책의 값이 어떤 값으로 설정되어 있는지 확인합니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id13"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;ExecutionPolicy 정책 설정 확인&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Get-ExecutionPolicy
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;ExecutionPolicy 정책 설정 값이 Restricted 인 경우 명령으로 정책 설정을 변경합니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id14"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;ExecutionPolicy 정책 설정 변경&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Set-ExecutionPolicy&lt;span class="w"&gt; &lt;/span&gt;RemotedSigned&lt;span class="w"&gt; &lt;/span&gt;-Scope&lt;span class="w"&gt; &lt;/span&gt;User
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Get-ExecutionPolicy 명령의 결과가 RemotedSigned로 나오면 정책 설정 값을 변경하지 않아도 됩니다. 정책 설정 변경은 윈도우를 재설치하거나 새로운 컴퓨터로 변경하지 않는 이상 계속 유효합니다.&lt;/p&gt;
&lt;p&gt;파워셸에서 파이썬 가상 환경 진입은 다음과 같은 명령어로 합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="se"&gt;\&amp;lt;&lt;/span&gt;가상환경&lt;span class="w"&gt; &lt;/span&gt;이름&amp;gt;&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\A&lt;/span&gt;ctivate.ps1
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이걸로 파워셸 설정이 완료됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id8"&gt;
&lt;h4&gt;리눅스/맥에서 진입하기&lt;/h4&gt;
&lt;p&gt;리눅스나 맥은 파워셸 진입 명령은 터미널 프로그램이나 셸 종류에 상관없이 아래 명령으로 실행합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;가상환경&lt;span class="w"&gt; &lt;/span&gt;이름&amp;gt;/bin/activate
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이렇게 하면 파이썬 가상 환경으로 진입하며 셸 프롬프트가 &lt;cite&gt;(&amp;lt;가상환경 이름&amp;gt;) $&lt;/cite&gt; 와 같이 표현됩니다.&lt;/p&gt;
&lt;p&gt;그리고 셸이 파이썬 가상 환경에 진입하면 python, pip 명령 등의 실행 경로는 파이썬 가상 환경에서 찾아서 실행하게 됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id9"&gt;
&lt;h3&gt;파이썬 가상 환경에서 빠져 나오기&lt;/h3&gt;
&lt;p&gt;파이썬 가상 환경에서 빠져나오는 것은 윈도우, 리눅스, 맥일 때가 모두 같습니다. 셸 프롬프트는 편의상 $로 표기했습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;deactivate
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이것으로 파이썬 가상 환경에서 빠져나오게 됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id10"&gt;
&lt;h2&gt;마치며&lt;/h2&gt;
&lt;p&gt;이 글에서 소개한 파이썬 가상 환경 만들기 외에도 다양한 도구를 사용해서 파이썬 가상 환경을 만들고 진입하는 방법이 있습니다. 이런 도구는 다른 글에서 다를 수 있게 되길 바랍니다.&lt;/p&gt;
&lt;p&gt;파이썬 가상 환경에 대한 이야기는 이걸로 마칩니다.&lt;/p&gt;
&lt;p&gt;from. 파이썬 수다장이 날다의 아저씨&lt;/p&gt;
&lt;p class="rubric"&gt;각주&lt;/p&gt;
&lt;aside class="footnote-list brackets"&gt;
&lt;aside class="footnote brackets" id="id11" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id3"&gt;1&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;엄밀히 말하면 파이썬에서 라이브러리를 찾는 경로는 site-packages 에서 먼저 찾지는 않습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/python-virtualenv/"/>
    <summary>필자가 처음 컴퓨터 프로그램 언어를 배웠을 때는 모든 프로그램은 한 개 파일만 만들면 되는 줄 알고 있었고 “Hello World”를 화면에 출력하는 것만으로 “프로그램 동작하네?” 하면서 금방 흥미를 잃고 말았었습니다.C, C++ 언어와 같은 컴파일 언어는 컴파일러에게 소스 코드에서 링크할 라이브러리의 경로를 제공하면 되기에 운영체제에 같은 라이브러리가 버전별로 설치가 되어 있어도 큰 문제가 되지 않습니다.이런 점에선 자바 가상 머신을 사용하는 자바 언어도 컴파일 시에 링크할 라이브러리 경로만 알려주면 되기 때문에 프로그램을 만드는 과정에서 여러 버전을 가진 라이브러리가 있어도 상관없습니다.</summary>
    <category term="python" label="python"/>
    <category term="venv" label="venv"/>
    <category term="virtualenv" label="virtualenv"/>
    <published>2024-05-11T00:00:00+09:00</published>
  </entry>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/python-pyenv/</id>
    <title>파이썬 버전을 관리하는 PYENV</title>
    <updated>2024-05-10T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="pyenv"&gt;

&lt;p&gt;파이썬 언어를 사용해 프로그램을 개발하다보면 프로그램이 배포되는 환경마다 제각기 다른 파이썬 버전을 사용하는걸 쉽게 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;이런 경우 내 컴퓨터에 설치된 파이썬 버전과 프로그램이 배포되는 서버의 파이썬 버전을 맞추는 것이 가장 좋은 해결책이지만 윈도우를 제외한 리눅스나 맥은 운영체제에 파이썬이 기본으로 배포되어 배포 환경을 생각하지 않고 개발에 임하기 쉽습니다.&lt;/p&gt;
&lt;p&gt;특히 리눅스의 경우 배포본이 최신에 가까울 수록 가장 최근에 배포된 파이썬 버전이 포함되기도 합니다. 필자의 사용하는 리눅스 &lt;a class="footnote-reference brackets" href="#f1" id="id1" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;1&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; 컴퓨터만 해도 파이썬은 3.10.12 버전이 배포되어 있습니다. 맥의 경우 3.9 버전이 기본으로 배포되어 있습니다.&lt;/p&gt;
&lt;p&gt;물론 윈도우는 파이썬과 파이썬으로 만든 프로그램이 사용되지 않기 때문에 파이썬이 기본으로 설치되어 있지는 않습니다.&lt;/p&gt;
&lt;p&gt;상황이 이렇다보니 프로그램이 배포될 환경과 개발하는 환경의 파이썬 버전을 맞추는 것이 무엇보다 중요해졌습니다.&lt;/p&gt;
&lt;p&gt;파이썬은 공식 &lt;a class="reference external" href="https://python.org"&gt;웹 사이트&lt;/a&gt; 에서 필요로 하는 버전을 다운로드 받을 수 있습니다. 파이썬 웹 사이트에서 배포하는 파이썬 다운로드는 기본적으로 다음의 형태로만 다운로드 받을 수 있습니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;소스 압축 파일 버전(gzip, xz)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;맥용 설치 버전(pkg)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;윈도우 인스톨러 버전(exe)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;윈도우 임베디드 버전(zip)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;지금 뭔가 이상한 부분을 발견하지 못하셨나요? 리눅스는 파이썬 웹 사이트에서 패키징된 파일을 제공하고 있지 않습니다. 이 때문에 리눅스에서 제공하고 있는 파이썬은 어디까지나 배포본을 만드는 사업자가 소스 코드를 다운로드 받아 컴파일해서 패키징 한것을 제공하고 있습니다.&lt;/p&gt;
&lt;p&gt;그런데 어떤 파이썬 버전은 “소스 압축 파일 버전”만 제공하고 설치 파일은 제공하지 않는 경우도 있습니다. 이런 경우 해당 파이썬 버전을 반드시 써야 한다면 파이썬을 컴파일해서 사용할 수 밖에 없습니다.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/home/jiho/PycharmProjects/flypysuda/blog/python-pyenv.rst&lt;/span&gt;, line 32)&lt;/p&gt;
&lt;p&gt;No directive entry for “note” in module “docutils.parsers.rst.languages.ko”.
Using English fallback for directive “note”.&lt;/p&gt;
&lt;/aside&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;참고&lt;/p&gt;
&lt;p&gt;파이썬의 릴리즈 주기는 기본적으로 prerelease - bugfix - security - eol 단계로 구분됩니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;prerelease - 공식 출시 직전&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;bugfix - 공식 출시는 되었지만 파이썬에 있는 버그를 수정&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;security - 보안 기능 패치(보안 버그가 있을 때만 제공)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eol - 더 이상 유지보수하지 않아 다운로드할 수는 있지만 쓰는 것은 권장하지 않음&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;파이썬 재단은 파이썬 출시에 있어 prerelease부터 bugfix 단계에 있는 버전들은 맥과 윈도우에서 실행 가능한 설치 파일을 제공하지만 security 단계부터는 소스 코드 압축 버전만 제공합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;소스 코드를 다운로드 받아 컴파일해서 사용하는 것은 2가지 어려움이 있습니다.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;리눅스나 맥은 컴파일 환경 구축이 쉽지만 윈도우는 리눅스나 맥에 비해 컴파일 환경 구축이 상대적으로 어렵습니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;소스 코드 컴파일도 어렵지만 컴파일 이후 설치 부터 삭제까지 전반적인 관리의 어려움이 생깁니다.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;미리 컴파일된 파이썬 패키지를 제공하는 리눅스의 경우에도 모든 파이썬 버전의 패키징 파일을 제공하지 않기 때문에 특정 파이썬 버전이 필요하면 반드시 컴파일 단계가 필요합니다.&lt;/p&gt;
&lt;p&gt;그래서 파이썬을 쉽게 컴파일하고 사용할 수 있도록 도와주는 버전 관리자가 필요합니다. 이 글에서는 파이썬 버전 관리자로 PyEnv를 다룹니다.&lt;/p&gt;
&lt;section id="id3"&gt;
&lt;h2&gt;PyEnv 설치하기&lt;/h2&gt;
&lt;p&gt;pyenv는 POSIX 표준을 따르는 리눅스와 맥에서는 별도의 pyenv installer를 사용해서 설치할 수 있고 윈도우에서는 pyenv를 설치하고 사용할 수 없습니다. 윈도우에서 사용하려면 WSL에 올라간 리눅스에 설치하거나 Git Bash 등을 사용해야 합니다. 윈도우에서의 설치 방법은 차후 다룰 기회가 있을것입니다.&lt;/p&gt;
&lt;p&gt;이 글에서 다루는 pyenv 개발 환경 구축은 맥과 데비안/우분투 계열의 리눅스 배포본에서의 설치 방법만 다룹니다.&lt;/p&gt;
&lt;p&gt;pyenv를 설치하려면 운영체제에 git, curl이 설치되어 있어야 합니다.&lt;/p&gt;
&lt;section id="id4"&gt;
&lt;h3&gt;데비안/우분투 계열 리눅스 배포본에 pyenv 설치하기&lt;/h3&gt;
&lt;p&gt;먼저 터미널 프로그램을 실행해서 셸을 시작합니다.&lt;/p&gt;
&lt;p&gt;리눅스에 curl과 git이 설치되어 있지 않을 수 있으므로 다음 명령으로 설치해줍니다.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ sudo apt install curl git
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;curl과 git이 설치되어 있지 않으면 pyenv 설치 중 &lt;code class="code shell docutils literal notranslate"&gt;&lt;span class="pre"&gt;bash:&lt;/span&gt; &lt;span class="pre"&gt;curl:&lt;/span&gt; &lt;span class="pre"&gt;command&lt;/span&gt; &lt;span class="pre"&gt;not&lt;/span&gt; &lt;span class="pre"&gt;found&lt;/span&gt;&lt;/code&gt; 메시지 또는 &lt;code class="code shell docutils literal notranslate"&gt;&lt;span class="pre"&gt;pyenv:&lt;/span&gt; &lt;span class="pre"&gt;Git&lt;/span&gt; &lt;span class="pre"&gt;is&lt;/span&gt; &lt;span class="pre"&gt;not&lt;/span&gt; &lt;span class="pre"&gt;installed,&lt;/span&gt; &lt;span class="pre"&gt;can't&lt;/span&gt; &lt;span class="pre"&gt;continue.&lt;/span&gt;&lt;/code&gt; 와 같은 메시지가 나타나고 pyenv 설치가 중단됩니다.&lt;/p&gt;
&lt;p&gt;curl과 git을 설치했으면 pyenv 설치 명령을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;https://pyenv.run&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;그럼 잠시 뒤에 설치 프로그램이 pyenv를 Github 저장소를 복제하면서 pyenv를 시스템에 설치해줍니다. 설치가 완료되면 마지막에 다음과 같은 내용이 셸에 출력됩니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;WARNING:&lt;span class="w"&gt; &lt;/span&gt;seems&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;still&lt;span class="w"&gt; &lt;/span&gt;have&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;added&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pyenv&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;path.

&lt;span class="c1"&gt;# Load pyenv automatically by appending&lt;/span&gt;
&lt;span class="c1"&gt;# the following to&lt;/span&gt;
&lt;span class="c1"&gt;# ~/.bash_profile if it exists, otherwise ~/.profile (for login shells)&lt;/span&gt;
&lt;span class="c1"&gt;# and ~/.bashrc (for interactive shells) :&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYENV_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.pyenv&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PYENV_ROOT&lt;/span&gt;/bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PYENV_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Restart your shell for the changes to take effect.&lt;/span&gt;

&lt;span class="c1"&gt;# Load pyenv-virtualenv automatically by adding&lt;/span&gt;
&lt;span class="c1"&gt;# the following to ~/.bashrc:&lt;/span&gt;

&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;virtualenv-init&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 메시지는 pyenv를 내랴받아 설치했으니 pyenv 명령을 어디서든 실행 가능하도록 PATH 환경 변수에 추가하고 pyenv 초기화 명령을 사용중인 셸의 설정 파일에 추가하라는 메시지입니다. 화면에 출력된 내용은 bash 셸의 설정 파일에 추가하는 내용입니다.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;참고&lt;/p&gt;
&lt;p&gt;셸의 설정 파일은 여러분이 사용하고 있는 셸에 파일명이 다릅니다. 사용중인 셸은 &lt;code class="code shell docutils literal notranslate"&gt;&lt;span class="pre"&gt;echo&lt;/span&gt; &lt;span class="pre"&gt;$SHELL&lt;/span&gt;&lt;/code&gt; 명령으로 확인합니다. 이 명령의 실행 결과가 bash로 끝나면 셸 설정 파일명은 &lt;cite&gt;.bashrc&lt;/cite&gt; 이고, zsh로 끝나면 &lt;cite&gt;.zshrc&lt;/cite&gt; 입니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;다음 명령을 셸에 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;export PYENV_ROOT=&amp;quot;$HOME/.pyenv&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;~/.bashrc
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[[ -d $PYENV_ROOT/bin ]] &amp;amp;&amp;amp; export PATH=&amp;quot;$PYENV_ROOT/bin:$PATH&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;~/.bashrc
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;eval &amp;quot;$(pyenv init -)&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;~/.bashrc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;위 코드에서는 여러분이 bash 셸을 쓴다고 가정했지만 실제 사용하는 셸에 따라 셸 설정 파일명을 바르게 입력하시길 바랍니다.&lt;/p&gt;
&lt;p&gt;이걸로 데비안/리눅스에서 pyenv 설치가 완료되었습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id5"&gt;
&lt;h3&gt;맥에 pyenv 설치하기&lt;/h3&gt;
&lt;p&gt;맥은 Xcode Command Line Tools를 설치하면 curl과 git이 설치됩니다. 다음 명령으로 Xcode Command Line Tools를 설치합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;xcode-select&lt;span class="w"&gt; &lt;/span&gt;--install
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 명령이 실행되고 나면 프로그램을 설치할것인지 물어보는 화면이 표시되면 도구 설치에 동의하고 지나가면 됩니다.&lt;/p&gt;
&lt;p&gt;이제 pyenv를 설치합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;https://pyenv.run&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;그럼 잠시 뒤에 설치 프로그램이 pyenv를 Github 저장소를 복제하면서 pyenv를 시스템에 설치해줍니다. 설치가 완료되면 마지막에 다음과 같은 내용이 셸에 출력됩니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;WARNING:&lt;span class="w"&gt; &lt;/span&gt;seems&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;still&lt;span class="w"&gt; &lt;/span&gt;have&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;added&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pyenv&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;path.

&lt;span class="c1"&gt;# Load pyenv automatically by appending&lt;/span&gt;
&lt;span class="c1"&gt;# the following to&lt;/span&gt;
&lt;span class="c1"&gt;# ~/.bash_profile if it exists, otherwise ~/.profile (for login shells)&lt;/span&gt;
&lt;span class="c1"&gt;# and ~/.bashrc (for interactive shells) :&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYENV_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.pyenv&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PYENV_ROOT&lt;/span&gt;/bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PYENV_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Restart your shell for the changes to take effect.&lt;/span&gt;

&lt;span class="c1"&gt;# Load pyenv-virtualenv automatically by adding&lt;/span&gt;
&lt;span class="c1"&gt;# the following to ~/.bashrc:&lt;/span&gt;

&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;virtualenv-init&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;최근의 맥은 기본 셸 프로그램이 zsh 이므로 &lt;cite&gt;.zshrc&lt;/cite&gt; 파일에 pyenv 초기화 명령들을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;export PYENV_ROOT=&amp;quot;$HOME/.pyenv&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;~/.zshrc
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[[ -d $PYENV_ROOT/bin ]] &amp;amp;&amp;amp; export PATH=&amp;quot;$PYENV_ROOT/bin:$PATH&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;~/.zshrc
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;eval &amp;quot;$(pyenv init -)&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;~/.zshrc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이제 터미널 프로그램을 다시 실행하면 pyenv를 사용할 수 있습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id6"&gt;
&lt;h2&gt;pyenv를 사용하기&lt;/h2&gt;
&lt;p&gt;pyenv는 다양한 파이썬 버전을 설치하고 관리해주기 때문에 설치된 파이썬 버전 간의 전환 작업에 필요한 여러 명령을 알고 있어야 합니다.&lt;/p&gt;
&lt;p&gt;여기에서는 pyenv 사용에 필요한 다음의 서브 명령을 살펴보겠습니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;install&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;uninstall&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;shell&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;version&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;versions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;which&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;whence&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;update&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;차례대로 pyenv 서브 명령이 어떤 일을 하는지 간략히 살펴보겠습니다.&lt;/p&gt;
&lt;section id="install"&gt;
&lt;h3&gt;install&lt;/h3&gt;
&lt;p&gt;pyenv를 실행중인 사용자 계정에 파이썬 인터프리터를 설치하는 명령입니다. 이 서브 명령은 -l 옵션 인자 또는 설치하려는 파이썬 버전을 인자로 받습니다.&lt;/p&gt;
&lt;p&gt;pyenv로 설치 가능한 파이썬 인터프리터 종류는 계속 업데이트되고 있기에 여러분이 설치하려는 인터프리터 이름을 확인해두시는게 좋습니다. -l 옵션 인자를 주어 확인 가능합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 명령의 실행 결과로 매우 긴 내용이 화면에 출력됩니다. 이 중에 가장 처음에 출력되는 것은 &lt;cite&gt;2.&lt;/cite&gt; 로 시작하는 것이 나오고 &lt;cite&gt;3.&lt;/cite&gt; 으로 시작하는 것들과 알파벳으로 시작하는 파이썬 인터프리터 이름이 나오는데 여기에서 나오는 이름이 pyenv install 명령으로 설치할 수 있는 인터프리터 이름입니다.&lt;/p&gt;
&lt;p&gt;2 또는 3으로 시작하는 숫자 버전은 C로 구현된 파이썬 버전을 말하며 python.org에서 공식적으로 배포되는 버전을 말합니다. pyenv는 공식 파이썬 버전 외에도 다양한 곳에서 배포되는 파이썬 인터프리터를 설치할 수 있으므로 여러분이 관심을 가지고 확인해보시면 좋을것 같습니다.&lt;/p&gt;
&lt;p&gt;install 서브 명령은 기본적으로 설치하려는 파이썬 버전을 정확히 적어주는 것이 명확하지만 단순히 최신 버전을 설치하려고 하면 Major 버전 또는 Major.Minor 버전을 명시하는 방법으로도 설치할 수 있습니다.&lt;/p&gt;
&lt;p&gt;예를 들어 다음과 같이 입력하는 식입니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id11"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;파이썬 2.x의 최근 버전 설치하기&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="literal-block-wrapper docutils container" id="id12"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;파이썬 3.10.x의 최근 버전 설치하기&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이렇게 명령을 입력하는 것만으로 2.7.18 또는 3.10.14 버전의 소스 코드가 다운로드 되어서 실행됩니다.&lt;/p&gt;
&lt;p&gt;install 명령은 파이썬 소스 코드를 다운로드 받아 컴파일을 진행하기 때문에 이 명령을 실행하기 전에 반드시 점검해야 하는 2가지 사항이 있습니다.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;컴파일러에게 전달할 컴파일 옵션 등은 미리 운영체제 환경 변수로 저장해놓거나 인라인 환경 변수로 전달해야 합니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파이썬 소스 코드 컴파일에 필요한 시스템 라이브러리는 미리 설치해둡니다.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;파이썬 소스 코드 컴파일러에 옵션을 제공해야 하면 PYTHON_CONFIGURE_OPTS 환경 변수를 install 서브 명령에 전달합니다.&lt;/p&gt;
&lt;p&gt;install 서브 명령에 PYTHON_CONFIGURE_OPTS 환경 변수를 전달하는 방법은 다음의 두 가지 방법을 사용할 수 있습니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id13"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;셸의 환경 변수로 만들면 pyenv가 환경 변수를 인식합니다.&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_CONFIGURE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;--enable-shared
$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="literal-block-wrapper docutils container" id="id14"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;pyenv install 서브 명령을 실행하기 전에 가장 앞에 셸 환경 변수를 선언합니다.&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_CONFIGURE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;--enable-shared&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 두 가지 방법 중에 어떤 방법을 사용해도 좋지만 필자는 인라인 방식으로 pyenv install 명령이 실행되는 동안에만 일시적으로 환경 변수가 살아있는 두 번째 방법을 선호합니다.&lt;/p&gt;
&lt;p&gt;PYTHON_CONFIGURE_OPTS 환경 변수에 제공한 값은 pyenv가 파이썬 소스 컴파일을 하기 위해 내부적으로 실행하는 configure 스크립트에 인자로 전달됩니다.&lt;/p&gt;
&lt;p&gt;여러분은 이 중에 편한 방식으로 사용하시면 됩니다.&lt;/p&gt;
&lt;p&gt;운영체제 별로 파이썬 컴파일에 필요한 라이브러리 설치 방법이 달라지며 실제 설치는 뒤에서 다루겠습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="uninstall"&gt;
&lt;h3&gt;uninstall&lt;/h3&gt;
&lt;p&gt;uninstall 서브 명령은 pyenv install 서브 명령을 사용해 설치된 파이썬 버전을 지우는데 사용합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;uninstall&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;unintall 서브 명령에 전달하는 인자는 install 명령에 사용했던것과 동일해야 합니다. 설치되지 않은 것은 지울 수 없기 때문입니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="shell"&gt;
&lt;h3&gt;shell&lt;/h3&gt;
&lt;p&gt;shell 서브 명령은 현재 사용중인 셸에서 실행할 파이썬 버전을 변경하는데 사용합니다. shell 서브 명령에 전달하는 인자는 install 서브 명령으로 설치된 파이썬 인터프리터 이름 입니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;shell 서브 명령이 사용되고 나면 python 명령이나 pip 등의 명령은 shell 서브 명령에 전달한 파이썬 인터프리터 경로의 명령이 실행됩니다.&lt;/p&gt;
&lt;p&gt;단, shell 명령으로 변경된 파이썬 인터프리터는 셸이 동작하고 있는 터미널이 종료되면 해제됩니다. 이 글에서 다루진 않지만 셸이 종료되어도 특정 파이썬 인터프리터 버전을 유지하려면 local 서브 명령을 사용하면 됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="version"&gt;
&lt;h3&gt;version&lt;/h3&gt;
&lt;p&gt;version 서브 명령은 현재 셸에서 사용중인 파이썬 인터프리터 버전이 무엇인지 확인하는데 사용합니다. 다른 명령들과 이 명령은 인자를 받지 않습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;version
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;local 서브 명령이나 global 서브 명령 등이 사용되지 않았다면 터미널을 다시 실행했다면 system이 나올 것입니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="versions"&gt;
&lt;h3&gt;versions&lt;/h3&gt;
&lt;p&gt;versions 서브 명령은 pyenv install 명령을 사용해 설치된 모든 파이썬 인터프리터 목록을 보는데 사용합니다. pyenv가 파이썬 가상 환경도 관리할 수 있기 때문에 pyenv로 가상 환경도 만들었다면 versions 서브 명령이 실행됐을 때 파이썬 가상 환경도 이름도 보여지게 됩니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;versions
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;versions 서브 명령도 실행할 때 인자를 전달받지 않습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="which"&gt;
&lt;h3&gt;which&lt;/h3&gt;
&lt;p&gt;which 서브 명령은 실행 인자를 받아 실행 인자로 주어진 명령이 실제 위치를 반환합니다. 예를 들어 shell 서브 명령에 실행 인자로 3.10.14를 주어 셸의 파이썬 환경을 바꿔줍니다. 그 다음 셸에서 python 명령을 입력했을 때 해당 명령이 어디에 있는지 찾으려면 다음과 같이 명령하면 됩니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;python
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 명령의 실행 결과는 &lt;code class="code shell docutils literal notranslate"&gt;&lt;span class="pre"&gt;/home/jiho/.pyenv/versions/3.10.14/bin/python&lt;/span&gt;&lt;/code&gt; 와 비슷한 형태로 표시됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="whence"&gt;
&lt;h3&gt;whence&lt;/h3&gt;
&lt;p&gt;whence 서브 명령은 인자로 주어진 실행 파일의 이름이 pyenv 환경에 설치된 파이썬 환경 중에 어디에 설치되어 있는지 확인하기 위해 사용합니다. 예를 들어 3.10.13버전과 3.11.4 버전에 http 명령이 있고 3.9.8 버전에는 http 명령이 없는 상태에서 다음과 같이 명령을 입력하면 화면에는 3.10.13과 3.11.4만 나오게 됩니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;whence&lt;span class="w"&gt; &lt;/span&gt;http
&lt;span class="m"&gt;3&lt;/span&gt;.10.13
&lt;span class="m"&gt;3&lt;/span&gt;.11.4
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;특정 파이썬 환경에만 설치한 명령이 있는 경우 whence 서브 명령을 사용하면 pyenv로 설치한 파이썬 환경을 순회하면서 찾지 않아도 됩니다. 필자가 이 기능을 알았다면 좋았을 기능입니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="update"&gt;
&lt;h3&gt;update&lt;/h3&gt;
&lt;p&gt;update 서브 명령은 pyenv 자체를 업데이트 하는데 사용하는 명령이며 실행 인자를 받지 않습니다. pyenv는 설치되는 시점에 pyenv로 설치 가능한 파이썬 버전 정보가 디스크에 파일로 남기 때문에 pyenv를 업데이트 하지 않으면 새로 나온 파이썬 버전이 있을 경우 설치할 수 없습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;update
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;update 서브 명령은 자주 실행하지 않아도 되며 새로 나온 버전의 파이썬 인터프리터를 설치할 때만 사용하면 됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id7"&gt;
&lt;h2&gt;pyenv로 파이썬 설치하기&lt;/h2&gt;
&lt;p&gt;이제 본격적으로 pyenv로 파이썬을 설치해볼 차례입니다. 필자는 가능한 한 4개 정도의 마이너 파이썬 버전을 시스템에 설치해두길 권장합니다. 예를 들면 3.9부터 3.12까지 설치하는 것입니다. 이렇게 파이썬 버전을 여러개 설치하는 것을 권장하는건 개발중인 파이썬 프로그램이 사용하는 라이브러리 일부는 특정 마이너 버전이나 특정 패치 버전 이상을 요구하는 경우가 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;보통은 패치 버전까지는 라이브러리에서 가리지 않지만 마이너 버전까지는 일부 라이브러리가 설치 조건 등의 일부로 사용하기에 여러분도 이런 점을 감안하여 파이썬 인터프리터 버전을 설치해두시길 권장합니다.&lt;/p&gt;
&lt;section id="id8"&gt;
&lt;h3&gt;데비안/우분투 계열 배포본에서 pyenv로 파이썬 설치하기&lt;/h3&gt;
&lt;p&gt;데비안/우분투 계열 배포본에서 파이썬을 컴파일 하려면 여러 시스템 라이브러리를 미리 설치해둡니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;libbz2-dev&lt;span class="w"&gt; &lt;/span&gt;libncurses-dev&lt;span class="w"&gt; &lt;/span&gt;libreadline-dev&lt;span class="w"&gt; &lt;/span&gt;libsqlite3-dev&lt;span class="w"&gt; &lt;/span&gt;liblzma-dev&lt;span class="w"&gt; &lt;/span&gt;tk-dev&lt;span class="w"&gt; &lt;/span&gt;libffi-dev&lt;span class="w"&gt; &lt;/span&gt;libssl-dev
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 다음엔 install 서브 명령으로 원하는 파이썬 버전을 설치하면 됩니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_CONFIGURE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;--enable-shared&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;필자는 pyenv로 파이썬을 컴파일 할 때는 항상 PYTHON_CONFIGURE_OPTS 환경 변수에 &lt;cite&gt;–enable-shared&lt;/cite&gt; 값을 주는데 이 값은 설치하는 파이썬이 공유 라이브러리 모드로 컴파일되도록 합니다. 일부 파이썬 라이브러리는 파이썬이 공유 라이브러리 모드로 컴파일 되지 않으면 동작되지 않는 경우가 있기 때문입니다.&lt;/p&gt;
&lt;p&gt;이제 잠시 기다리면 파이썬 컴파일이 완료됩니다. 파이썬은 설치했으니 다음엔 shell 서브 명령을 사용해 파이썬 인터프리터 버전을 지정하면 됩니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id9"&gt;
&lt;h3&gt;맥에서 pyenv로 파이썬 설치하기&lt;/h3&gt;
&lt;p&gt;맥의 경우 파이썬 컴파일에 필요한 라이브러리는 homebrew를 사용해 설치하는 것이 권장됩니다. homebrew를 설치하는 방법은 이 글에서 다루지 않으니 다른 문서를 참고해주세요.&lt;/p&gt;
&lt;p&gt;homebrew가 설치되어 있다면 다음 명령으로 파이썬 컴파일에 필요한 라이브러리를 설치합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;bzip2&lt;span class="w"&gt;  &lt;/span&gt;ncurses&lt;span class="w"&gt; &lt;/span&gt;readline&lt;span class="w"&gt; &lt;/span&gt;sqlite&lt;span class="w"&gt; &lt;/span&gt;xz&lt;span class="w"&gt; &lt;/span&gt;openssl@3
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;brew로 라이브러리를 설치하는 것은 조금 시간이 소요될 수 있으므로 느긋하게 기다리시길 바랍니다. brew로 라이브러리 설치가 완료되면 install 서브 명령으로 파이썬을 설치할 차례입니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_CONFIGURE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;--enable-shared&lt;span class="w"&gt; &lt;/span&gt;pyenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.10
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;파이썬 설치 명령은 리눅스와 동일하며 인자로 PYTHON_CONFIGURE_OPTS 환경 변수의 인자로 전달하는 것도 같으므로 이 부분은 가능한 꼭 기억해서 쓰시면 좋습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id10"&gt;
&lt;h2&gt;마치며&lt;/h2&gt;
&lt;p&gt;이것으로 파이썬 버전을 쉽게 관리하고 쉽게 사용할 수 있게 하는 pyenv에 대한 소개를 마칩니다. 나머지 기능은 여러분이 직접 실습하면서 확인해보시길 바랍니다.&lt;/p&gt;
&lt;p&gt;from. 파이썬 수다장이&lt;/p&gt;
&lt;p class="rubric"&gt;각주&lt;/p&gt;
&lt;aside class="footnote-list brackets"&gt;
&lt;aside class="footnote brackets" id="f1" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id1"&gt;1&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;필자는 우분투 22.04에 기반한 리눅스 배포본을 사용하고 있습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/python-pyenv/"/>
    <summary>파이썬 언어를 사용해 프로그램을 개발하다보면 프로그램이 배포되는 환경마다 제각기 다른 파이썬 버전을 사용하는걸 쉽게 볼 수 있습니다.이런 경우 내 컴퓨터에 설치된 파이썬 버전과 프로그램이 배포되는 서버의 파이썬 버전을 맞추는 것이 가장 좋은 해결책이지만 윈도우를 제외한 리눅스나 맥은 운영체제에 파이썬이 기본으로 배포되어 배포 환경을 생각하지 않고 개발에 임하기 쉽습니다.특히 리눅스의 경우 배포본이 최신에 가까울 수록 가장 최근에 배포된 파이썬 버전이 포함되기도 합니다. 필자의 사용하는 리눅스 1 컴퓨터만 해도 파이썬은 3.10.12 버전이 배포되어 있습니다. 맥의 경우 3.9 버전이 기본으로 배포되어 있습니다.</summary>
    <category term="pyenv" label="pyenv"/>
    <category term="python" label="python"/>
    <published>2024-05-10T00:00:00+09:00</published>
  </entry>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/python-firestore-api-emulator-using/</id>
    <title>python firestore API에서 에뮬레이터 사용하기</title>
    <updated>2024-05-02T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="python-firestore-api"&gt;

&lt;p&gt;파이썬 프로그램을 개발하다보면 인터넷에 접속해있지 않은 상황에서 로컬 컴퓨터에 실행된 에뮬레이터나 개발 서버를 사용해야 할 때가 있습니다. 웹 서버 정도는 인터넷에 연결되어 있지 않아도 금방 띄울 수 있지만 Google Firestore 같은 것들은 에뮬레이터를 사용해야만 데이터에 접근할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 firestore 에뮬레이터가 설치되어 있다고 가정합니다. 설치할 패키지는 다음과 같습니다.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;firestore&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;아래 코드는 실제 구글 Firestore API를 사용해 프로그램에 접근했을 때 로컬 서버에 실행된 에뮬레이터에 접근하도록 만든 코드입니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;grpc&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;firestore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.cloud.firestore_v1.services.firestore&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;firestore_client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.cloud.firestore_v1.services.firestore.transports.grpc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FirestoreGrpcTransport&lt;/span&gt;

&lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insecure_channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;localhost:8080&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FirestoreGrpcTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;firestore_db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;firestore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;firestore_db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_firestore_api_internal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;firestore_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirestoreClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;에뮬레이터에 등록된 데이터만 추출해서 확인이 필요했는데 구글 클라우드 문서엔 관련 내용을 찾을 수 없어서 여기저기 뒤적거려보니 스택오버플로우에 4년전에 달린 글이 있어 그대로 적용해봤더니 API가 여러모로 업데이트 되어서 동작하지 않았지만 예시 코드를 기반으로 수정해가면서 해보니까 동작되는 것을 확인했습니다.&lt;/p&gt;
&lt;p&gt;이 글이 오늘도 삽을 들고 어디를 파야하나 고민되는 분들에게 도움이 되었으면 합니다.&lt;/p&gt;
&lt;p&gt;from. 파이썬 수다장이 날다의 아저씨&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/python-firestore-api-emulator-using/"/>
    <summary>파이썬 프로그램을 개발하다보면 인터넷에 접속해있지 않은 상황에서 로컬 컴퓨터에 실행된 에뮬레이터나 개발 서버를 사용해야 할 때가 있습니다. 웹 서버 정도는 인터넷에 연결되어 있지 않아도 금방 띄울 수 있지만 Google Firestore 같은 것들은 에뮬레이터를 사용해야만 데이터에 접근할 수 있습니다.이 글에서는 firestore 에뮬레이터가 설치되어 있다고 가정합니다. 설치할 패키지는 다음과 같습니다.아래 코드는 실제 구글 Firestore API를 사용해 프로그램에 접근했을 때 로컬 서버에 실행된 에뮬레이터에 접근하도록 만든 코드입니다.</summary>
    <category term="emulator" label="emulator"/>
    <category term="firestore" label="firestore"/>
    <category term="python" label="python"/>
    <published>2024-05-02T00:00:00+09:00</published>
  </entry>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/python-packaging-history/</id>
    <title>파이썬 패키징 - 과거, 현재, 미래</title>
    <updated>2024-05-01T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="id1"&gt;

&lt;p&gt;본 문서는 &lt;a class="reference external" href="https://bernat.tech/posts/pep-517-518/"&gt;https://bernat.tech/posts/pep-517-518/&lt;/a&gt; 글을 옮긴 것입니다(오역이 있을 수 있습니다).&lt;/p&gt;
&lt;p&gt;pip install을 실행하면 정확히 어떤 일이 일어나는지 궁금해 본 적이 있나요? 이 게시물에서는 이전의 설치 단계와 PEP-517 및 PEP-518의 채택으로 인해 모든 단계가 어떻게 변경되는지에 대해 자세히 설명합니다.&lt;/p&gt;
&lt;p&gt;이전 게시물에서 소스 트리, 소스 배포 및 휠의 세 가지 컨텐츠 설치 방법에 대해 설명했습니다. 마지막 두 가지 유형만 중앙 Python 리포지토리인 PyPI에 업로드됩니다. 그러나 (예를 들어, pip에 git 프로토콜을 추가하면) 소스 트리를 손에 넣을 수 있습니다. wheel이 다른 방식보다 나은 점은 사용자 컴퓨터에서 빌드 작업을 수행할 필요가 없다는 것입니다. wheel은 다운로드하여 압축을 푸는 것입니다.&lt;/p&gt;
&lt;section id="id2"&gt;
&lt;h2&gt;파이썬 패키지 빌드하기&lt;/h2&gt;
&lt;p&gt;이제 빌드가 발생하는 위치(사용자 또는 개발자 컴퓨터)에 관계없이 패키지를 빌드해야 합니다(sdist 또는 wheel). 그러기 위해선 빌더가 필요합니다. 역사적으로 타사 패키지의 필요성은 일찍부터 드러났습니다. Python 1.6과 함께 2000년에 Python이 배터리를 포함한다는 원칙에 따라 distutils 패키지가 Python 표준 라이브러리에 추가되었습니다. 빌드 논리가 포함된 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 파일의 개념을 도입했으며 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;cmd&lt;/span&gt;&lt;/code&gt; 를 통해 트리거됩니다.&lt;/p&gt;
&lt;p&gt;사용자는 코드를 라이브러리로 패키징할 수 있었지만, 종속성 자동 설치와 같은 기능은 없었습니다. 또한, 개선 라이프사이클은 코어 인터프리터 릴리즈 사이클과 직접 연관되어 있었습니다. 2004년에 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setuptools&lt;/span&gt;&lt;/code&gt; 도구가 만들어지고, distutils 도구 위에 구축되며, 다른 우수한 기능으로 확장되었습니다. 그것은 순식간에 널리 퍼져서 대부분의 파이썬 설치는 코어 인터프리터 자체와 함께 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setuptools&lt;/span&gt;&lt;/code&gt; 을 제공하기 시작했습니다.&lt;/p&gt;
&lt;p&gt;옛날에는 모든 패키지가 소스 배포판이었습니다. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Wheel&lt;/span&gt;&lt;/code&gt; 를 사용한 배포는 훨씬 뒤인 2014년에 이루어졌습니다. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;distutils&lt;/span&gt;&lt;/code&gt; 는 매우 숙련된 소수의 사람들만이 패키징을 할 때 만들어졌습니다. 매우 유연하고 필수적입니다. 패키지 생성 프로세스의 모든 단계를 수정하기 위해 python 스크립트를 작성합니다.&lt;/p&gt;
&lt;p&gt;하지만 이것의 단점은 배우고 이해하는 것이 결코 쉽지 않다는 것이입니다. 이는 Python의 인기가 높아지고 Python의 내부 작업에 능숙하지 못한 사용자가 늘어남에 따라 점점 더 이슈가 되었습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id3"&gt;
&lt;h2&gt;빌드 요구 사항&lt;/h2&gt;
&lt;p&gt;pip로 소스 배포를 설치하기 위해 대부분 다음의 명령을 수행했습니다.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;패키지 발견&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;소스 배포판을 다운로드하고 압축을 풉니다&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;압축을 푼 폴더에서 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt;&lt;/code&gt; 을 실행합니다.(빌드 + 설치 수행)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;개발자는 배포판을 생성하기 위해 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;sdist&lt;/span&gt;&lt;/code&gt; 를 수행하고 중앙 저장소에 업로드하기 위해 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;upload&lt;/span&gt;&lt;/code&gt; 를 수행했습니다(upload 명령은 2013년부터 비보안 HTTP 연결을 사용한 업로드로 인해 더 이상 사용되지 않으며 Twine 도구를 선호하여 업로드 명령도 최종 사용자가 실제 업로드 전에 생성된 패키지를 검사할 수 없도록 했습니다).&lt;/p&gt;
&lt;p&gt;pip가 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt;&lt;/code&gt; 을 실행할 때 패키지를 설치할 때 python 인터프리터를 사용하여 실행하였다. 따라서 빌드 작업은 해당 인터프리터 내에서 이미 사용할 수 있는 모든 타사 패키지에 액세스할 수 있었습니다. 특히 호스트 python 인터프리터에 설치된 setuptools 도구 버전을 정확히 사용했습니다. 패키지가 현재 설치된 버전보다 최신 릴리스에서 사용할 수 있는 setuptools 도구 기능을 사용한 경우 설치를 완료할 수 있는 유일한 방법은 설치된 setuptools 도구를 먼저 업데이트하는 것이었습니다.&lt;/p&gt;
&lt;p&gt;새 릴리스에 다른 패키지를 손상시키는 버그가 포함된 경우 잠재적으로 문제가 발생할 수 있습니다. 특히 설치된 패키지를 사용자가 변경할 수 없는 시스템에서 문제가 발생합니다. 그런 다음 빌더(예: setuptools)가 cython과 같은 다른 도우미 패키지를 사용하려고 할 때 발생하는 문제도 있었습니다.&lt;/p&gt;
&lt;p&gt;이러한 도움말 중 하나라도 누락된 경우 일반적으로 패키지 import 실패 오류와 함께 빌드가 중단되었습니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;setup_build.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;Cython.Build&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cythonize&lt;/span&gt;
&lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="n"&gt;Cython&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;개발자 측에서 이러한 빌드 종속성을 제공할 방법이 없었습니다. 또한 사용자가 런타임에 사용하지 않을 경우에도 모든 패키징 빌드 종속성을 설치해야 함을 의미했습니다. 이 문제를 해결하기 위해 PEP-518이 만들어졌습니다.&lt;/p&gt;
&lt;p&gt;PEP 518의 아이디어는 호스트 python과 함께 현재 설치된 패키지를 사용해 빌드하는 대신 패키지는 빌드 작업에 필요한 사항을 명시할 수 있는 기능을 제공한다는 개념입니다. 이것을 호스트 파이썬에서 사용할 수 있도록 하는 대신 패키징 명령을 실행하기 위해 격리된 파이썬 환경(virtualenv)을 만듭니다.&lt;/p&gt;
&lt;p&gt;이제 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt;&lt;/code&gt; 이 다음과 같이 됩니다.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;임시 폴더 생성&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;격리된 python 환경을 생성합니다(서드 파티의 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;site-packages&lt;/span&gt;&lt;/code&gt; 에서). &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;-m&lt;/span&gt; &lt;span class="pre"&gt;virtualenv&lt;/span&gt; &lt;span class="pre"&gt;our_build_env&lt;/span&gt;&lt;/code&gt; , 이 python 실행 파일을 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python_isolated&lt;/span&gt;&lt;/code&gt; 로 참조하겠습니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;빌드 종속성 설치&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python_isolated&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;bdist_wheel&lt;/span&gt;&lt;/code&gt; 을 통해 설치할 수 있는 휠 생성&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;python의 사이트 패키지로 wheel을 추출합니다.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이것으로 우리는 런타임 파이썬 환경 내에 실제로 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cython&lt;/span&gt;&lt;/code&gt; 을 설치하지 않고도 cython에 의존하는 패키지를 설치할 수 있습니다. 빌드 종속성을 지정하는 파일 및 방법은 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pyproject.toml&lt;/span&gt;&lt;/code&gt; 메타데이터 파일입니다.&lt;/p&gt;
&lt;p&gt;개발자 머신에서 소스 배포 또는 wheel을 생성할 때도 동일한 메커니즘을 사용할 수 있습니다. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pip&lt;/span&gt; &lt;span class="pre"&gt;wheel&lt;/span&gt; &lt;span class="pre"&gt;.&lt;/span&gt; &lt;span class="pre"&gt;--no-deps&lt;/span&gt;&lt;/code&gt; 명령은 빌드 시스템의 종속성을 충족하는 격리된 파이썬을 자동 생성하고 해당 환경 내에서 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;bdist_wheel&lt;/span&gt;&lt;/code&gt; 또는 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;sdist&lt;/span&gt;&lt;/code&gt; 명령을 호출합니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id4"&gt;
&lt;h2&gt;패키징 도구의 다양성&lt;/h2&gt;
&lt;p&gt;그런데 여기에 문제가 하나 더 있습니다. 이러한 모든 작업은 여전히 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 의 실행이라고 하는 20년 전에 도입된 메커니즘을 거칩니다. 전체 생태계는 여전히 이전 버전과의 호환성을 유지하기 위해 많은 것을 변경할 수 없는 distutils 및 setuptools 인터페이스를 기반으로 구축됩니다.&lt;/p&gt;
&lt;p&gt;또한 패키징하는 동안 임의의 사용자 측 Python 코드를 실행하는 것은 위험하므로 경험이 적은 사용자가 디버그하기 어려운 미묘한 오류로 이어집니다. 명령형 빌드 시스템은 20년 전 우리가 모든 사용 사례를 인식하지 못했을 때 유연성에 탁월했습니다. 그럼에도 불구하고 이제 우리가 잘 이해하고 있으므로 아마도 다양한 사용 사례에 대해 매우 강력하고 쉬운 패키지 빌더를 만들 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;Paul Ganssle(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setuptools&lt;/span&gt;&lt;/code&gt; 및 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dateutil&lt;/span&gt;&lt;/code&gt; 의 유지 관리자)의 말을 인용하자면:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;이상적으로 기본 옵션은 99%의 경우에 잘 작동하는 선언적 빌드 구성이며 유연성이 필요할 때 명령형 시스템으로
폴백할 수 있는 옵션이 있습니다. 이 시점에서 명령형 빌드 옵션을 사용해야 하는 경우 코드 냄새가 나는 세계로
이동할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 의 가장 큰 문제는 대부분의 사람들이 선언적으로 사용하고 명령적으로 사용할 때 빌드 시스템에
버그를 도입하는 경향이 있다는 것입니다. 이에 대한 한 가지 예: Python 2.7 전용 종속성이 있는 경우
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 에서 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sys.version&lt;/span&gt;&lt;/code&gt; 을 사용하여 조건부로 종속성을 지정하고 싶을 수 있지만 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sys.version&lt;/span&gt;&lt;/code&gt; 은
빌드를 실행한 인터프리터만 참조합니다. 대신 설치 요구 사항에 대해 선언적 환경 마커를 사용해야 합니다.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypi.org/project/flit/"&gt;flit&lt;/a&gt; 은 2015년에 도입하면서 이미 이 가정이 옳았다는 것을 증명했습니다. 이는 Python을 처음 접하는 많은 사람들이 가장 좋아하는 패키징 도구가 되었으며, 새로운 사용자가 많은 총기를 피할 수 있도록 합니다. 그러나 이 지점에 도달하기 위해 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;flit&lt;/span&gt;&lt;/code&gt; 은 distutils/setuptools 위에 다시 빌드해야 하므로 구현이 간단하지 않고 코드베이스가 상당히 많은 shim 계층이 많습니다(예를 들어 소스 배포에 대한 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 를 계속 생성합니다).&lt;/p&gt;
&lt;p&gt;이제 이러한 족쇄에서 해방되고 다른 사람들이 자신의 사용 사례에 맞게 쉽게 패키징할 수 있는 패키징 도구를 빌드하도록 권장하여 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 를 기본값이 아닌 예외로 만들 때입니다. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setuptools&lt;/span&gt;&lt;/code&gt; 는 이를 선도하는 유일한 사용자 인터페이스인 &lt;a class="reference external" href="https://github.com/pypa/setuptools/pull/1675"&gt;setup.cfg 를 제공할 계획&lt;/a&gt; 이며, PEP-517 시스템이 설치된 경우 대부분의 경우 setup.py를 남용하는 것을 피해야 합니다. 모든 것을 setuptools 및 distutils에 다시 연결하지 않고 새 빌드 백엔드 생성을 용이하게 하기 위해 &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0517/"&gt;PEP-517&lt;/a&gt; 이 생성되었습니다. 빌더를 백엔드와 프론트엔드로 분리합니다. 프론트엔드는 선언된 모든 빌드 종속성을 충족하는 격리된 파이썬 환경을 제공합니다. 백엔드는 프론트엔드가 소스 배포 또는 wheel을 생성하기 위해 격리된 환경에서 호출할 수 있는 후크를 제공합니다.&lt;/p&gt;
&lt;p&gt;또한 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;setup.py&lt;/span&gt;&lt;/code&gt; 파일 및 해당 명령을 통해 백엔드와 대화하는 대신 Python 모듈 및 함수로 이동합니다. 모든 패키징 백엔드는 최소한 두 가지 메서드인 &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0517/#build-wheel"&gt;build_wheel&lt;/a&gt; 와 &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0517/#id9"&gt;build_sdist&lt;/a&gt; 를 구현하는 python 개체 API를 제공해야 합니다. API 객체 포인트는 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pyproject.toml&lt;/span&gt;&lt;/code&gt; 파일 안의 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;build-backend&lt;/span&gt;&lt;/code&gt; 키를 통해 지정됩니다.&lt;/p&gt;
&lt;div class="highlight-toml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[build-system]&lt;/span&gt;
&lt;span class="n"&gt;requires&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flit&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;build-backend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;flit.api:main&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;위의 코드는 프론트엔드에 대해 격리된 파이썬 환경 내에서 위의 코드를 실행하여 백엔드를 확보할 수 있음을 의미합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;flit.api&lt;/span&gt;
&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c1"&gt;# build wheel via&lt;/span&gt;
&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build_wheel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# build source distribution via&lt;/span&gt;
&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build_sdist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;공식 API를 어디에서 어떻게 노출할 것인지는 백엔드에 달려 있습니다.&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/jiho/PycharmProjects/flypysuda/blog/python-packaging-history.rst&lt;/span&gt;, line 2); &lt;em&gt;&lt;a href="#id5"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate explicit target name: “flit”.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;&lt;a class="reference external" href="https://flit.readthedocs.io/en/latest/"&gt;flit&lt;/a&gt; 은 flit.buildapi를 통해 수행합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://setuptools.readthedocs.io/en/latest/history.html#v40-8-0"&gt;setuptools&lt;/a&gt; 는 setuptools.build_meta(나중에 읽어야 하는 이유)에서 두 가지 변형을 제공합니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://poetry.eustace.io/docs/pyproject/#poetry-and-pep-517"&gt;poetry&lt;/a&gt; 는 poetry.masonry.api를 통해 수행합니다.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이를 통해 프론트엔드에서 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;distutils&lt;/span&gt;&lt;/code&gt; 의 레거시 결정에 더 이상 바인딩되지 않는 패키징 도구를 가질 수 있습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="tox"&gt;
&lt;h2&gt;tox와 패키징&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://tox.readthedocs.io/en/latest/"&gt;tox는 테스트 도구&lt;/a&gt; 이며 대부분의 프로젝트에서 주어진 패키지의 여러 Python 인터프리터 버전과의 호환성을 보장하기 위해 사용합니다. 또한 검사 중인 패키지가 설치된 Python 환경을 빠르게 생성할 수 있으므로 오류를 더 빠르게 재현할 수 있습니다.&lt;/p&gt;
&lt;p&gt;패키지를 테스트하려면 먼저 소스 배포를 빌드해야 합니다. PEP-518과 PEP-517은 모두 상황을 더 좋게 만들지만 일부 사용 사례에서는 이 기능을 켜면 패키징이 깨질 수 있습니다. 따라서 버전 3.3.0에서 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tox&lt;/span&gt;&lt;/code&gt; 가 격리된 빌드를 추가했을 때 현재로서는 기본적으로 활성화하지 않기로 결정했습니다. 수동으로 활성화해야 합니다(2021년에 발표된 버전 4에서는 기본 옵션으로 활성화되었습니다).&lt;/p&gt;
&lt;p&gt;적절한 requires 및 build-backend가 있는 pyproject.toml을 지정했으면 tox.ini 내에서 isolated_build 플래그를 켜야 합니다.&lt;/p&gt;
&lt;div class="highlight-ini notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[tox]&lt;/span&gt;
&lt;span class="na"&gt;isolated_build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;그 후 &lt;a class="reference external" href="https://tox.readthedocs.io/en/latest/#system-overview"&gt;패키징 단계&lt;/a&gt; 의 tox는 소스 배포를 빌드합니다(PEP-518에 따라 격리된 Python 환경에 빌드 종속성을 제공함). 이후 PEP-517에 명시된 대로 빌드 백엔드를 호출합니다. 그렇지 않으면 tox는 tox가 설치된 동일한 인터프리터로 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;python&lt;/span&gt; &lt;span class="pre"&gt;setup.py&lt;/span&gt; &lt;span class="pre"&gt;sdist&lt;/span&gt;&lt;/code&gt; 명령을 호출하여 소스 배포를 빌드하는 이전 방법을 사용합니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id8"&gt;
&lt;h2&gt;결론&lt;/h2&gt;
&lt;p&gt;Python Packaging Authority는 이 모든 것이 합리적이고 사용자 친화적이고 오류가 없으며 안정적인 빌드를 갖기를 바랍니다. 이 표준에 대한 사양은 2015년과 2017년 사이에 긴 스레드로 작성되고 토론되었습니다. 제안은 가장 많은 혜택을 받을 만큼 충분히 좋은 것으로 간주되었지만 일부 덜 주류 사용 사례는 간과될 수 있었습니다.&lt;/p&gt;
&lt;p&gt;귀하의 사용 사례가 그러한 경우 필요하다고 생각되는 경우 PEP가 언제든지 향상될 수 있다는 점에 대해 걱정하지 마십시오. 이 시리즈의 다음 게시물에서는 이 두 PEP를 출시하는 동안 커뮤니티에서 부딪힌 몇 가지 문제점을 살펴보겠습니다. 이것들은 배운 교훈으로 작용해야 하며 아직 해야 할 일이 있다는 것을 보여주어야 합니다. 아직 모든 것이 완벽하지는 않습니다. 그러나 우리는 점점 나아지고 있습니다. 도울 수 있다면 패키징 커뮤니티에 가입하고 함께 더 나은 환경을 만들어 갑시다!&lt;/p&gt;
&lt;p&gt;from. 파이썬 수다장이 날다의 아저씨&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/python-packaging-history/"/>
    <summary>본 문서는 https://bernat.tech/posts/pep-517-518/ 글을 옮긴 것입니다(오역이 있을 수 있습니다).pip install을 실행하면 정확히 어떤 일이 일어나는지 궁금해 본 적이 있나요? 이 게시물에서는 이전의 설치 단계와 PEP-517 및 PEP-518의 채택으로 인해 모든 단계가 어떻게 변경되는지에 대해 자세히 설명합니다.이전 게시물에서 소스 트리, 소스 배포 및 휠의 세 가지 컨텐츠 설치 방법에 대해 설명했습니다. 마지막 두 가지 유형만 중앙 Python 리포지토리인 PyPI에 업로드됩니다. 그러나 (예를 들어, pip에 git 프로토콜을 추가하면) 소스 트리를 손에 넣을 수 있습니다. wheel이 다른 방식보다 나은 점은 사용자 컴퓨터에서 빌드 작업을 수행할 필요가 없다는 것입니다. wheel은 다운로드하여 압축을 푸는 것입니다.</summary>
    <category term=""/>
    <category term="python" label="python"/>
    <published>2024-05-01T00:00:00+09:00</published>
  </entry>
  <entry>
    <id>https://titanium-haiku-594.appspot.com/blog/creating-windows-services-with-python/</id>
    <title>파이썬으로 윈도우 서비스 만들기</title>
    <updated>2024-04-30T00:00:00+09:00</updated>
    <author>
      <name>search5</name>
    </author>
    <content type="html">&lt;section id="id1"&gt;

&lt;p&gt;윈도우나 리눅스와 같은 운영체제는 운영체제 시작과 함께 백그라운드로 동작하는 프로그램이 있습니다. 일반적으로 이런 프로그램이 하는 일은 운영체제가 시작하면서 반드시 해야 하는 일이 많습니다. 예를 들면 네트워크 통신을 할 수 있도록 통신 기능을 활성화하거나 웹 페이지나 데이터베이스 서버 프로그램을 실행하는 기능을 가지고 있습니다.&lt;/p&gt;
&lt;p&gt;리눅스와 맥은 이렇게 시작되는 프로그램을 데몬(Daemon)이라고 하고 윈도우는 서비스(Service)라고 합니다.&lt;/p&gt;
&lt;p&gt;제가 이 글을 작성하게 된 것은 Trac(&lt;a class="reference external" href="https://trac.edgewall.org"&gt;https://trac.edgewall.org&lt;/a&gt;) User 메일에 올라온 다음 메일 때문이었습니다.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Hi all,
Does anyone have experience with running trac as a windows service?

I&amp;#39;ve tried various methods including the python script provided in trac hacks, .bat files etc.

The issue I hit is that it starts trac ok, but stopping it stops the service but tracd keeps running in the background thus trac doesn&amp;#39;t stop.

Any thoughts on how to fix this or working examples would be appreciated. Just need a way to ensure trac starts and stops as a service rather than just using cmd. Stand alone server btw.
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;tracd 명령으로 윈도우에서 시작했는데 tracd를 실행한 터미널을 종료했음에도 tracd가 종료되지 않는데 어떻게 하면 좋을지에 대한 의견을 구하는 것이었습니다.&lt;/p&gt;
&lt;p&gt;그래서 직접 만들어 보기로 했습니다.&lt;/p&gt;
&lt;p&gt;윈도우가 시작하면서 실행하는 많은 서비스가 있지만 여기서 그 서비스를 다 설명할 것은 아니고, 이 글에선 파이썬으로 윈도우 서비스를 만들고 동작시키는 방법을 살펴보겠습니다.&lt;/p&gt;
&lt;section id="id2"&gt;
&lt;h2&gt;준비물&lt;/h2&gt;
&lt;p&gt;이 글을 시작하기에 앞서 다음과 같은 준비가 필요합니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;윈도우 10 이상&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파이썬 3.9 이상&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령 프롬프트 또는 파워셸&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;파이썬은 &lt;a class="reference external" href="https://www.python.org"&gt;https://www.python.org&lt;/a&gt; 에서 배포하는 python-버전-amd64.exe 실행 파일로 설치되어 있고 Python 실행 경로가 PATH 환경 변수에 포함되어 있어야 합니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id3"&gt;
&lt;h2&gt;파이썬 유틸리티 설치&lt;/h2&gt;
&lt;p&gt;이 문서의 내용을 따라하기 위해 몇 가지 파이썬 프로그래밍 유틸리티 설치가 필요합니다. 설치할 유틸리티는 pipx와 pipenv 입니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Invoke-WebRequest&lt;span class="w"&gt; &lt;/span&gt;-Uri&lt;span class="w"&gt; &lt;/span&gt;https://bit.ly/4ctb7ek&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex
PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Invoke-WebRequest&lt;span class="w"&gt; &lt;/span&gt;-Uri&lt;span class="w"&gt; &lt;/span&gt;https://bit.ly/3y1qTgL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex
PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Add-EnvPath&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Env&lt;/span&gt;:AppData&lt;span class="se"&gt;\$&lt;/span&gt;PY_VER&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="w"&gt; &lt;/span&gt;User
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 명령의 실행이 끝나면 다음과 같이 pipx를 설치합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pipx
PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;ensurepath
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 명령의 실행이 끝나면 파워셸을 종료했다가 다시 실행합니다. 파워셸을 다시 실행했으면 pipenv를 설치합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pipenv
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="id4"&gt;
&lt;h2&gt;프로젝트 폴더 및 파이썬 개발 환경 구성&lt;/h2&gt;
&lt;p&gt;우선 윈도우 서비스 프로그램을 만들기 위한 프로젝트 폴더를 만듭니다. 이 글에서는 trac_service라는 이름을 사용합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;New-Item&lt;span class="w"&gt; &lt;/span&gt;-ItemType&lt;span class="w"&gt; &lt;/span&gt;Directory&lt;span class="w"&gt; &lt;/span&gt;-Path&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;:HOMEPATH\trac_service&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;그리고 trac_service 폴더로 이동합니다. 그리고 pipenv를 사용해 파이썬 가상 환경을 생성하면서 윈도우 서비스 개발에 필요한 라이브러리를 설치합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;:HOMEPATH\trac_service&amp;quot;&lt;/span&gt;
PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pipenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pywin32
PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pipenv&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pyinstaller
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이로서 파이썬 가상 환경 생성까지 마쳤습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="service-logo"&gt;
&lt;h2&gt;&lt;img alt="service_logo" src="https://titanium-haiku-594.appspot.com/_images/Services-Icon-Big-256.png" style="width: 80px;" /&gt; 윈도우 서비스 기본 클래스 생성하기&lt;/h2&gt;
&lt;p&gt;파이썬으로 윈도우 서비스를 만드려면 pywin32가 제공하는 win32serviceutil, servicemanager, win32event, win32service 모듈이 필요합니다.&lt;/p&gt;
&lt;p&gt;이들 모듈은 윈도우 운영체제 내부에 접근할 수 있는 기능을 제공합니다.&lt;/p&gt;
&lt;p&gt;윈도우 서비스는 win32serviceutil 모듈의 ServiceFramework 클래스를 상속받으면 만들 수 있습니다. ABC 클래스는 SMWinservice 클래스를 직접 초기화하지 못하게 하기 위해서 상속받았습니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id8"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;smservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32serviceutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;servicemanager&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32event&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32service&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;상속받는 클래스의 이름은 편의상 SMWinservice로 합니다. 이 클래스를 사용해 윈도우 서비스를 등록하려면 클래스 내부에 클래스 변수 3개를 선언해야 합니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id9"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;smservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32serviceutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;servicemanager&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32event&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32service&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;_svc_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pythonService&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_display_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_description_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service Description&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;클래스 변수는 _svc_name_, _svc_display_name_, _svc_description_ 를 선언하며 _svc_name_은 띄워쓰기 없이 영어로 시작하는 이름을 입력해야 합니다. 이 이름이 윈도우에서 서비스을 식별하는 이름이 되기 때문입니다.&lt;/p&gt;
&lt;p&gt;_svc_display_name_ 은 윈도우 서비스 관리자를 실행했을 때 관리자가 서비스를 식별하는 이름입니다. 관리자가 식별하는 이름이기 때문에 띄워쓰기가 있어도 상관없습니다. _svc_description_ 은 이 서비스가 어떤 일을 하는 서비스인지 자세하게 쓰시면 됩니다.&lt;/p&gt;
&lt;p&gt;이 변수들은 SMWinservice 클래스를 상속하는 다른 클래스에서 덮어쓰기 할 것이므로 여기서는 간단히 입력해도 됩니다.&lt;/p&gt;
&lt;p&gt;클래스 변수를 선언했으면 기본 클래스가 가져야 하는 7개 메서드를 선언합니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id10"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;smservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32serviceutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;servicemanager&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32event&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32service&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;Base class to create winservice in Python&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;_svc_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pythonService&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_display_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_description_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service Description&amp;#39;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SvcStop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SvcDoRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_command_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이들 7개 메서드 중 반드시 선언해야 하는 메서드는 __init__, SvcDoRun, SvcStop 메서드입니다. Svc* 메서드들은 윈도우 시스템에서 직접 호출하기 때문에 반드시 필요합니다.&lt;/p&gt;
&lt;p&gt;나머지 메서드는 클래스를 쉽게 사용하고 확장할 수 있게 하는 메서드입니다.&lt;/p&gt;
&lt;p&gt;이제 __init__, SvcStop, SvcDoRun 메서드의 내용을 채우겠습니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id11"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;smservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32serviceutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;servicemanager&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32event&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32service&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_svc_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pythonService&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_display_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_description_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service Description&amp;#39;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hWaitStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;win32event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefaulttimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SvcStop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReportServiceStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SERVICE_STOP_PENDING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;win32event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hWaitStop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SvcDoRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogMsg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EVENTLOG_INFORMATION_TYPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYS_SERVICE_STARTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_svc_name_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_command_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleCommandLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;__init__ 메서드에서는 SMWinservice 클래스를 초기화합니다. __init__ 메서드는 args 인자를 반드시 받아야 합니다.&lt;/p&gt;
&lt;p&gt;그리고 부모 클래스를 초기화하고 객체 내부에 윈도우 시스템이 다룰 “이벤트” 객체를 선언하고 반환값을 객체 내부의 hWaitStop 필드 변수에 저장합니다. 이벤트 객체의 선언은 win32event 모듈의 CreateEvent 함수를 호출해서 만듭니다.&lt;/p&gt;
&lt;p&gt;그런 다음 파이썬의 socket 모듈을 들고 와서 기본 타임아웃 시간을 60초로 선언합니다. 타임아웃 지정은 setdefaulttimeout 함수를 사용하며 이 함수는 윈도우 서비스 프로그램이 60초 이내에 실행되지 않으면 문제가 발생했다는 사실을 알리는데 사용합니다.&lt;/p&gt;
&lt;p&gt;다음으로 SvcDoRun 메서드를 설명하겠습니다. 이 메서드는 윈도우 서비스가 시작하면 호출되는 메서드입니다. 윈도우 서비스는 시작되면 메모리에서 무한 루프 상태로 동작해야 합니다. 이에 따라 이 메서드 안에서 무한 루프로 동작하는 프로그램의 실행 로직을 배치합니다.&lt;/p&gt;
&lt;p&gt;여기에서는 SvcDoRun 메서드에 실제 실행 로직을 배치하지 않고 객체 메서드인 start와 main 메서드를 호출하도록 했습니다. 이렇게 실행되는 메서드를 분리하면 프로그램의 유지보수성이 높아지기에 권장되는 방식입니다.&lt;/p&gt;
&lt;p&gt;SvcDoRun 메서드에서는 start와 main 메서드 호출 사이에 윈도우 이벤트 로그에 로그를 남기기 위해 servicemanager 모듈의 LogMsg 함수를 호출하고 있습니다.&lt;/p&gt;
&lt;p&gt;다음과 같은 용법으로 사용합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogMsg&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;LogType&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;EventId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;[LogType]과 [EventId]는 pywin32의 servicemanager에서 제공하는 상수를 사용합니다. SvcDoRun 메서드에서는 [LogType]으로 servicemanager.EVENTLOG_INFORMATION_TYPE를 사용하고 [EventId]로 servicemanager.PYS_SERVICE_STARTED를 사용했습니다.&lt;/p&gt;
&lt;p&gt;그리고 [Log Message]는 로그 항목에 표시될 실제 로그 내용을 전달합니다.&lt;/p&gt;
&lt;p&gt;SvcDoRun에서 호출하는 start와 main 메서드는 다음과 같은 일을 합니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;start - 무한 루프로 동작하는 프로그램을 실행하기 전에 미리 해야 하는 일을 선언합니다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;main - 무한 루프로 동작하는 프로그램의 실행 로직을 여기에 넣습니다.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제 SvcStop 메서드를 살펴보겠습니다. 이 메서드는 윈도우 서비스가 중지 요청되었을 때 실행되는 메서드입니다. 이 메서드는 호출되자마자 객체의 stop 메서드를 호출합니다. stop 메서드는 우리가 나중에 재정의하는 메서드로 우리가 실행한 무한 루프 프로그램을 중지시키는데 사용하는 메서드입니다.&lt;/p&gt;
&lt;p&gt;그런 다음 윈도우에게 당신이 중지 요청한 서비스가 종료되기를 기다리고 있다고 알려야 합니다. 이를 위해 부모 클래스의 ReportServiceStatus 함수에 win32service 모듈의 SERVICE_STOP_PENDING 상수를 전달합니다.&lt;/p&gt;
&lt;p&gt;win32event 모듈의 SetEvent 함수에게 __init__ 메서드에서 만들었던 이벤트 객체인 hWaitStop 필드 변수를 전달하는 것으로 SvcStop 메서드 구현이 완료됩니다.&lt;/p&gt;
&lt;p&gt;마지막으로 parse_command_line 메서드를 구현합니다. 이 메서드는 우리가 만든 윈도우 서비스를 관리하기 위해 호출하는 메서드입니다. 이 메서드는 파이썬 클래스의 “클래스 메서드”로 선언되었기 때문에 메서드의 인자로 cls가 전달되었습니다. win32serviceutil의 HandleCommandLine 함수에 메서드의 인자로 받은 cls를 전달하기만 하면 됩니다.&lt;/p&gt;
&lt;p&gt;이것으로 윈도우 서비스 기본 클래스 구현이 완료되었습니다.&lt;/p&gt;
&lt;p&gt;지금까지 작성한 내용을 바탕으로 완성한 파일의 내용은 다음과 같습니다(완성된 코드에는 프로그램의 이해를 돕기 위한 주석이 많이 포함되어 있습니다).&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id12"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;smservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32serviceutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;servicemanager&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32event&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32service&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;Base class to create winservice in Python&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;_svc_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pythonService&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_display_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_description_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python Service Description&amp;#39;&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_command_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        ClassMethod to parse the command line&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleCommandLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        Constructor of the winservice&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="n"&gt;win32serviceutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceFramework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hWaitStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;win32event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefaulttimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SvcStop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        Called when the service is asked to stop&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReportServiceStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;win32service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SERVICE_STOP_PENDING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;win32event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hWaitStop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SvcDoRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        Called when the service is asked to start&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogMsg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EVENTLOG_INFORMATION_TYPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYS_SERVICE_STARTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_svc_name_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        Override to add logic before the start&lt;/span&gt;
&lt;span class="sd"&gt;        eg. running condition&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        Override to add logic before the stop&lt;/span&gt;
&lt;span class="sd"&gt;        eg. invalidating running condition&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        Main class to be ovverridden to add logic&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="id5"&gt;
&lt;h2&gt;커스텀 윈도우 서비스 클래스 만들기&lt;/h2&gt;
&lt;p&gt;윈도우 서비스의 기본 클래스를 생성했으면 다음으로 실제 우리가 등록할 서비스 클래스를 만듭니다. 우리가 만들 서비스 클래스는 앞에서 만든 SMWinservice 클래스를 상속받아 만듭니다.&lt;/p&gt;
&lt;p&gt;여기에서 만드는 커스텀 윈도우 서비스 클래스는 Trac(&lt;a class="reference external" href="https://trac.edgewall.org"&gt;https://trac.edgewall.org&lt;/a&gt;) 데몬을 실행하는 tracd 명령을 실행합니다.&lt;/p&gt;
&lt;p&gt;그렇기 때문에 여기에서 재정의하는 하는 것은 클래스 변수 _svc_name_, _svc_display_name_, _svc_description_와 start, main, stop 메서드입니다. 예시로 명명한 파일 이름은 tracservice.py 입니다.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id13"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;tracservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;smservice&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMWinservice&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TracService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_svc_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;TracWinService&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_display_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Trac on Windows&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_description_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Trac Windows Service&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;_svc_name_, _svc_display_name_, _svc_description_ 는 적절한 내용으로 입력하시면 되는데 여기서는 Trac을 띄울 것이므로 Trac에 관한 내용으로 채웠습니다.&lt;/p&gt;
&lt;p&gt;여기에서 재정의 하는 main 메서드가 호출되면 프로그램이 무한 루프로 실행되어야 합니다. 예를 들면 다음과 같은 무한 루프 로직이 동작해야 합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# 여기에서 무한 루프 내에서 실행할 문장 기술&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;main 메서드 내부에 무한 루프로 동작하는 코드를 직접 추가해도 되지만 “윈도우 서비스”는 어디까지나 서비스 시작시 무한 루프로 실행되도록 프로그램은 별도의 실행 파일로 분리하는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;별도의 실행 파일로 로직을 분리하지 않으면 나중에 프로그램이 업데이트 되었을 때 여러 어려움이 발생합니다.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;윈도우 서비스를 다시 인스톨하거나 서비스 업데이트 필요&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로그램의 실제 로직 추가로 프로그램 용량이 매우 커지고 서비스 프로그램의 유지보수가 어려워짐&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;그리고 tracd 명령은 한 번 실행되면 메모리에서 무한 루프로 실행되기 때문에 여기에서는 main 메서드 내에 따로 while 문을 사용하지 않았습니다.&lt;/p&gt;
&lt;p&gt;실제 코드를 구현하기에 앞서 Trac은 다음과 같이 설치되어 있다고 가정합니다. 이 글에서는 Trac의 설치 및 환경 구성은 살펴보지 않습니다.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Trac 패키지가 설치된 파이썬 가상 환경 경로 : %HOMEPATH%trac-venv&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trac 환경 : %HOMEPATH%tracreposproject&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 설치되어 있는 trac 환경은 다음 명령으로 실행합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;:HOMEPATH&lt;span class="se"&gt;\t&lt;/span&gt;rac-venv&lt;span class="se"&gt;\t&lt;/span&gt;racd&lt;span class="w"&gt; &lt;/span&gt;--port&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;:HOMEPATH&lt;span class="se"&gt;\t&lt;/span&gt;rac&lt;span class="se"&gt;\r&lt;/span&gt;epos&lt;span class="se"&gt;\p&lt;/span&gt;roject
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이렇게 하면 8000번 포트로 trac 서버가 실행됩니다.&lt;/p&gt;
&lt;p&gt;start 메서드부터 살펴보겠습니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;daemon_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;daemon_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;config.json&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;start 메서드에서는 main 메서드를 호출하기 전에 tracd 프로그램을 실행하기 위한 사전 작업을 준비합니다. tracd 명령은 파이썬 subprocess 모듈의 Popen 클래스를 사용해 실행합니다. Popen 클래스는 초기화되면 운영체제를 대신해 실행중인 프로세스를 제어할 수 있도록 Popen 객체를 반환합니다.&lt;/p&gt;
&lt;p&gt;이에 따라 Popen 클래스를 초기화 하기 전에 Popen 객체를 담아놓을 필드 변수 exec_process를 start 메서드 안에서 선언합니다. 이렇게 만든 exec_process 필드 변수는 윈도우 서비스가 중지 요청되면 stop 메서드 안에서 사용됩니다.&lt;/p&gt;
&lt;p&gt;start 메서드에서 하나 더 유심히 볼 부분은 _env_path_를 선언한 부분입니다.&lt;/p&gt;
&lt;p&gt;사실 서비스 클래스를 윈도우 서비스로 등록하고 나면 서비스 클래스는 main 메서드 안에서 tracd 명령이 어떤 경로에 있는지, trac 환경이 어떤 경로에 있는지 알 수 없습니다.&lt;/p&gt;
&lt;p&gt;그래서 tracd 명령이 있는 경로와 trac 환경을 파이썬 환경에 하드 코딩하는 방법이 사용되기도 합니다. 하지만 이 경우 나중에 시간이 지나고 나면 trac 패키지를 설치한 가상 환경이 어디에 있는지 Trac 환경이 어디에 있는지 알기 어렵습니다.&lt;/p&gt;
&lt;p&gt;그렇기 때문에 여기에서는 파이썬 가상 환경이 설치된 경로와 tracd 명령을 config.json으로 담아놓고 start 메서드 실행시 config.json이 있는 경로를 절대 경로로 고정해뒀습니다.&lt;/p&gt;
&lt;p&gt;config.json 파일의 경로는 커스텀 윈도우 서비스 파일(ex. tracservice.exe)이 있는 경로에 있다고 가정합니다.&lt;/p&gt;
&lt;section id="id6"&gt;
&lt;h3&gt;설정 파일 만들기&lt;/h3&gt;
&lt;div class="literal-block-wrapper docutils container" id="id14"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;config.json&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;exec_start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tracd --port 8000 C:\\Users\\gildong\\trac\\repos\\project&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;python_home&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C:\\Users\\gildong\\trac-venv&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;config.json 파일에서는 exec_start 키와 python_home 키를 가지고 있으며 python_home은 파이썬 가상 환경의 경로, exec_start는 tracd 실행 명령을 적었습니다. 참고로 경로는 역슬래시()를 2개씩 사용해야 합니다.&lt;/p&gt;
&lt;p&gt;이제 main 메서드를 살펴봅니다. main 메서드는 config.json 파일을 읽어들여 exec_start 키의 내용으로 프로그램을 실행합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; File Not Found&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;config_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;python_home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;python_home&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exec_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;exec_start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exec_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;python_home&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Scripts&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exec_start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;exec_env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;exec_env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;shlex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exec_command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;exec_env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;main 메서드가 처음 실행되면 우선 config.json이 있는지 검사합니다. 만약 config.json을 찾을 수 없다면 ValueError 예외를 발생시키고 윈도우 서비스를 시작하지 않습니다. 커스텀 서비스 클래스가 실행할 정보를 확인할 수 없으니 당연한 수순입니다.&lt;/p&gt;
&lt;p&gt;파일을 찾았으면 config_obj에 config.json을 읽어 파이썬 딕셔너리로 보관해둡니다.&lt;/p&gt;
&lt;p&gt;그런 다음 config_obj에 저장해둔 python_home과 exec_start 키를 읽어들인 다음 최종적으로 실행할 tracd 실행 명령을 만들어둡니다. 여기에서는 tracd가 [python_home]Scripts에 있다고 생각하기 때문에 경로는 [python_home]Scripts[exec_start]가 됩니다.&lt;/p&gt;
&lt;p&gt;혹시라도 tracd가 참고하는 윈도우 환경 변수가 필요할 수 있기 때문에 운영체제 환경 변수를 복사하고 config.json에 env 키(JSON Object)의 내용을 병합해서 exec_env에 담아둡니다.&lt;/p&gt;
&lt;p&gt;실행할 경로도 다 만들어졌겠다 이제 Popen 클래스를 사용해 exec_command를 실행합니다. subprocess는 첫 번째 인자의 값은 특별한 일이 없으면 실행 명령에 있는 공백을 구분 문자로 해서 MutableSequence로 제공해야 합니다.&lt;/p&gt;
&lt;p&gt;위에 있는 config.json을 예시로 든다면 Popen에 전달해야 하는 값은 &lt;code class="code highlight python docutils literal highlight-python"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C:\Users\gildong&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;rac-venv\Scripts&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;racd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;--port&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;8000&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;C:\Users\gildong&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;rac&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;epos\project&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/code&gt; 형태로 전달해야 합니다. 그런데 이렇게 명령을 분할해주는건 아무래도 불편하기 때문에 shlex 모듈의 split 함수를 사용해서 하나의 긴 문자열을 MutableSequence로 변경해줄 수 있습니다.&lt;/p&gt;
&lt;p&gt;단, 이 글에서는 윈도우을 실행 환경으로 두고 있기 때문에 &lt;code class="code highlight python docutils literal highlight-python"&gt;&lt;span class="n"&gt;shlex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;/code&gt; 함수를 실행할 때 posix 인자에 False를 따로 제공했습니다. 윈도우에서 Popen를 실행한다면 꼭 잊지 않으셔야 합니다. 나머지 인자에 대한 설명은 파이썬 공식 문서를 참조하세요.&lt;/p&gt;
&lt;p&gt;main 메서드의 마지막입니다. 서두에서 언급한 것처럼 main 메서드는 무한 루프 상태로 시작해야 하는데 subprocess로 tracd를 실행해서 self.exec_process에 담아놓는다고 끝난게 아닙니다. Popen 클래스로 실행한 명령이 어떤식으로든 끝날 때까지 파이썬 프로그램이 기다리게 해야 합니다.&lt;/p&gt;
&lt;p&gt;바로 이 때, Popen 객체의 wait 메서드를 호출합니다. wait 메서드는 Popen 클래스로 실행한 명령이 끝날 때까지 파이썬 프로그램을 끝내지 말고 대기하라는 메서드입니다.&lt;/p&gt;
&lt;p&gt;결과적으로 main 메서드에서 우리가 무한 루프를 돌리지 않아도 wait 메서드가 대신 tracd가 내부적으로 실행하는 무한 루프가 끝날때까지 기다립니다.&lt;/p&gt;
&lt;p&gt;마지막으로 stop 메서드를 구현합니다. stop 메서드는 SvcStop 메서드가 처음 호출하도록 되어 있는거 기억하시죠?&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;stop 메서드에서는 단순히 self.exec_process에 담아놓은 Popen 객체의 terminate 메서드를 호출하는 것 뿐입니다. terminate 메서드는 Popen이 잡고 있는 프로세스에게 종료 명령을 내리는 것입니다.&lt;/p&gt;
&lt;p&gt;마지막으로 tracservice.py 파일의 끝에 다음 내용으로 커스텀 윈도우 서비스 클래스를 윈도우 서비스 관리자가 제어할 수 있도록 해줘야 합니다.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Windows Service Register&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mode&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;TracService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_command_line&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrepareToHostSingle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TracService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartServiceCtrlDispatcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;argparse 명령의 첫 번째 실행 인자가 전달되면 앞에서 만든 parse_command_line 메서드를 호출하게 하고 윈도우 서비스 관리자가 직접 실행하게 하는거면 servicemanager 모듈의 몇 개 함수를 순차적으로 호출시켜줍니다.&lt;/p&gt;
&lt;p&gt;완성된 tracservice.py 파일 구현이 끝나면 아래와 같아야 합니다(물론 완성된 코드엔 import 해야 하는 코드 및 로깅을 위한 코드나 주석이 일부 포함되어 있습니다).&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id15"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;tracservice.py&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;shlex&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;smservice&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMWinservice&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32serviceutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;servicemanager&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TracService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SMWinservice&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_svc_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;TracWinService&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_display_name_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Trac on Windows&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_svc_description_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Trac Windows Service&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;event_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogMsg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logging_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;EVENTLOG_INFORMATION_TYPE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logging_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;EVENTLOG_ERROR_TYPE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logging_audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;EVENTLOG_AUDIT_SUCCESS&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;daemon_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;daemon_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;config.json&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_svc_name_&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Service Stopped&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_svc_name_&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Service Execution Failed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; File Not Found&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;config_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_env_path_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;python_home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;python_home&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;exec_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;exec_start&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;exec_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;python_home&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Scripts&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exec_start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

        &lt;span class="c1"&gt;# self.logging_audit(exec_command)&lt;/span&gt;

        &lt;span class="n"&gt;exec_env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;exec_env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;shlex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exec_command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;exec_env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging_audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;StdOut: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logging_audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;StdErr: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Windows Service Register&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mode&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;TracService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_command_line&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrepareToHostSingle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TracService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;servicemanager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartServiceCtrlDispatcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;커스텀 서비스 클래스 구현까지 모두 끝났습니다. 이제 윈도우 서비스 관리자에 커스텀 서비스 클래스를 등록시킬 시간입니다.&lt;/p&gt;
&lt;p&gt;윈도우 서비스 관리자에 커스텀 서비스 클래스를 등록하려면 반드시 exe 파일로 변환해야 합니다.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="exe"&gt;
&lt;h2&gt;커스텀 서비스 클래스 파일을 exe로 만들기&lt;/h2&gt;
&lt;p&gt;커스텀 서비스 클래스 파일을 exe로 만들기 위한 사전 작업은 앞에서 다했기 때문에 여기에서 아래 명령을 내려 exe 파일을 만듭니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pipenv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;pyinstaller&lt;span class="w"&gt; &lt;/span&gt;-F&lt;span class="w"&gt; &lt;/span&gt;onefile&lt;span class="w"&gt; &lt;/span&gt;--hidden-import&lt;span class="w"&gt; &lt;/span&gt;win32timzone&lt;span class="w"&gt; &lt;/span&gt;--uac-admin&lt;span class="w"&gt; &lt;/span&gt;tracservice.py
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이렇게 만들어진 파일은 명령을 실행한 디렉터리 아래의 dist 폴더에 만들어집니다. 앞에서 살펴본 config.json은 여기 dist 폴더에 넣어놓으면 됩니다.&lt;/p&gt;
&lt;p&gt;단, dist 폴더의 내용은 시스템 관리자가 관리하는 폴더임을 나타낼 수 있는 곳에 옮겨놓는게 좋습니다.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="id7"&gt;
&lt;h2&gt;윈도우 서비스 관리자 사용하기&lt;/h2&gt;
&lt;p&gt;윈도우 서비스 관리자에 등록하는 명령은 아래와 같습니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;tracservice.exe&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;새로 업데이트된 윈도우 커스텀 서비스 클래스 파일(exe 파일)로 기존 윈도우 서비스 관리자의 내용을 변경하려면 다음 명령을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;tracservice.exe&lt;span class="w"&gt; &lt;/span&gt;update
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;더 이상 윈도우 커스텀 서비스를 사용하지 않는다면 다음 명령으로 윈도우 서비스 관리자에서 삭제합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;tracservice.exe&lt;span class="w"&gt; &lt;/span&gt;remove
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;윈도우 커스텀 서비스 클래스를 시작하려면 다음 명령을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;tracservice.exe&lt;span class="w"&gt; &lt;/span&gt;start
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;윈도우 커스텀 서비스 클래스를 중지하려면 다음 명령을 입력합니다.&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;tracservice.exe&lt;span class="w"&gt; &lt;/span&gt;stop
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;이 글에서 윈도우 커스텀 서비스를 파이썬으로 만드는 방법에 대해 살펴봤습니다. 당연히 어려우셨으리가 생각하지만 이런 것 하나하나가 여러분의 실력을 향상시키는데 도움이 되길 희망합니다.&lt;/p&gt;
&lt;p&gt;from. 파이썬 수다장이 날다의 아저씨&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://titanium-haiku-594.appspot.com/blog/creating-windows-services-with-python/"/>
    <summary>윈도우나 리눅스와 같은 운영체제는 운영체제 시작과 함께 백그라운드로 동작하는 프로그램이 있습니다. 일반적으로 이런 프로그램이 하는 일은 운영체제가 시작하면서 반드시 해야 하는 일이 많습니다. 예를 들면 네트워크 통신을 할 수 있도록 통신 기능을 활성화하거나 웹 페이지나 데이터베이스 서버 프로그램을 실행하는 기능을 가지고 있습니다.service_logo</summary>
    <category term="python" label="python"/>
    <category term="pywin32" label="pywin32"/>
    <category term="service" label="service"/>
    <category term="tracd" label="tracd"/>
    <category term="windows" label="windows"/>
    <published>2024-04-30T00:00:00+09:00</published>
  </entry>
</feed>
