최근 한 달 여 동안 Python을 이용해 몇 가지 작은 유틸리티를 만들어 보고 있습니다. 만들면서 새삼 느끼는 것은 Python이라는 언어는 그 언어를 도구로서 사용하여 기계 학습 등의 결과를 배포하는데는 매우 유용하지만, Python 기반의 스크립트, 즉 Python 코드 배포 작업은 가급적 하지 않는 것이 정신 건강에 좋다는 것입니다.

과거의 BASIC 기반의 언어가 그랬던 것처럼 인터프리터 기반의 언어가 가지는 숙명이라면 숙명이겠지만, 도구 자체의 배포가 까다롭다는 사실은 개발자가 아닌 사용자 입장에서는 전혀 매력적이지 않기 때문입니다. 기본적으로 Python은 런타임 도구가 아닌 실제 개발 도구에 해당하는 인터프리터와 표준 라이브러리, 그리고 필요한 경우 추가 라이브러리까지도 사용자가 직접 설치해야 하고, 더군다나 그 방법이 다른 애플리케이션처럼 설치 프로그램을 제공하는 것도 아니기에 더욱 더 까다롭게 느껴지는 것이 사실입니다.

Python™
Python™

이를 해결하기 위한 방법으로 Python 코드 배포 작업을 위해 실행 파일 형태로 패키징해 주는 몇 가지 도구들이 있긴 합니다.

그 중 가장 많이 사용되는 것이 pyinstaller로서, 최신 버전의 Python 기반 스크립트에도 무리 없이 바로 적용 가능하다는 장점이 있습니다. pyinstaller는 기본적으로 실행되는 진입 코드가 자신이 포함하고 있는 모든 스크립트 파일의 압축을 임시 디렉터리에 해제한 후, 해당 파일을 인터프리터로 직접 실행하는 구조를 가지고 있습니다. 다시 말해 스크립트 파일을 컴파일하거나 변환하는 것이 아니라 단순히 따라가며 필요한 스크립트 파일들의 목록을 작성하고 이를 하나로 묶어 주는 도구입니다. 따라서 실행 시에 스크립트 파일의 압축을 해제하는데 몇 초 정도의 시간이 필요하지만, 실행 이후에는 일반적인 Python 스크립트와 동일한 방식으로 실행됩니다.

pyinstaller
pyinstaller

하지만 소스 코드에 해당하는 스크립트 파일이 그대로 노출되기 때문에, 이를 원하지 않는 경우에는 선택하기 꺼려질 수도 있습니다. 또한 빌드 시에 오류가 발생하는지 여부를 전혀 판단하지 않기 때문에 스크립트를 작성할 때 모든 오류를 미리 철저하게 점검할 필요가 있습니다. 그리고 이러한 단점이 단점으로 여겨지지 않을 정도로 큰 단점이 하나 있는데, 단일 파일 옵션(-F 또는 --onefile)으로 빌드 시 파일의 크기가 매우 커진다는 점입니다. 물론 그럼에도 불구하고 높은 호환성과 배포 편의성을 고려한다면 가장 나은 선택일 가능성이 있습니다.

이와 비슷한 도구로 유명한 것은 cx_Freeze가 있으며, Windows에서만 사용 가능한 py2exe도 눈여겨 볼 만 합니다.

사실 Pythonista라면 Python이라는 언어의 특성 상 스크립트 파일의 노출을 크게 문제 삼을 이유가 없기 때문에, pyinstaller의 사용에 크게 거리낌이 없을 것입니다. 하지만 상용 애플리케이션을 개발하거나 다른 이유로 인해 소스 코드의 노출이 문제가 될 수 있다면, nuitka를 고려해 볼 수 있습니다. 그리고 만약 여러분이 nuitka를 선택하셨다면 지옥의 무한 궤도에 올라타신 것을 축하드립니다.

nuitka
nuitka

nuitka는 Python으로 작성된 스크립트를 C++11 코드로 변환하고 이를 컴파일하여 실행 파일로 만드는 일종의 코드 변환기입니다. 따라서 실제로 C++로 개발된 애플리케이션과 마찬가지로 바이너리 코드를 바로 실행하는 구조를 가지고 있으며, Python에서 추가 설치가 필요한 라이브러리 역시 바이트 코드 형태로 사용됩니다. 쉽게 말하자면 nuitka는 Python 스크립트를 C++ 소스 코드로 변환하는 변환기와 이를 컴파일해 주는 컴파일러를 실행해 주는 도구가 결합된 도구입니다.

당연한 이야기지만 C++ 코드 기반으로 컴파일된 실행 파일이기 때문에 스크립트를 실시간으로 번역하는 인터프리터 기반의 Python 스크립트보다 성능이 향상될 가능성이 높으며, 코드 상황에 따라 컴파일러에 의한 더 나은 최적화가 가능할 수도 있습니다. 또한 C++ 코드를 컴파일한 것이기 때문에 원본 스크립트가 노출될 일도 없습니다. 물론 C++ 코드로 디컴파일을 하려면 못할 것도 없겠지만, 그렇다고 그것이 원본 Python 스크립트인 것은 아니니까요.

하지만 스크립트를 작성하는 사람에 따라 온갖 괴랄한 테크닉이 적용되어 있을 수도 있고, 이를 C++ 소스 코드로 변환하는 변환기가 제대로 처리하지 못하는 문제가 발생할 가능성도 충분히 있습니다. 또한 변환기가 가지는 한계로 인해 최신 Python 코드를 사용하지 못할 수도 있습니다. 이 글을 작성하고 있는 5월 말 기준 최신 버전인 2.2.3도 Python 3.12를 지원하지 않고 있으므로, 정상적인 사용을 위해서는 Python 버전 3.11.9를 사용해야 합니다.

[UPDATE]
7월 초가 되었고, nuitka는 살짝 눈을 돌리면 갱신되는 질풍노도의 시기를 거쳐, 버전은 2.3.11이 되었습니다. 약속대로 2.3.0부터 Python 3.12를 정식으로 지원하기 시작했습니다. 그런데 야속하게도 기준이 되는 Python 배포인 CPython은 버전 3.13의 출시를 앞두고 있습니다. 물론 Python의 버전에 크게 휘둘릴 필요는 없지만, 3.13부터 지원되는 GIL(Global Interpreter Lock) 회피와 같은 기능을 활용하려면 조금 더 기다려야 할 것 같습니다.

requests
requests

또한 requests를 통해 지속적으로 API를 반복 호출해야 하는 경우가 있을 수 있는데, 이 때도 24시간에 한 번씩 certifi에서 인증 오류가 발생하기 때문에 이를 회피하기 위한 코드를 추가로 작성해 줄 필요가 있습니다. 이 외에도 종료 시그널을 던지게 되면 단일 파일 옵션으로 컴파일된 실행 파일의 경우 무조건 주 프로세스가 종료되어 버립니다. 따라서 다음과 같은 시그널을 무시하는 코드가 있더라도 제대로 동작하지 않습니다. (참고 설명) 따라서 이에 대한 대처 코드를 추가로 작성할 필요가 있습니다.

 signal.signal(signal.SIGINT, signal.SIG_IGN)

이와 같이 nuitka를 이용해 실행 파일 형태로 Python 코드 배포 작업을 하려는 경우, 실제 Python 스크립트의 오류도 모두 처리해 주어야 하지만, nuitka를 사용함으로써 발생하는 부수적인 작업이 그 이상으로 꽤 높은 난이도를 자랑합니다. 따라서 무턱대고 nuitka의 사용을 먼저 고려하는 것 보다는 특별히 고집해야 할 이유가 없다면 pyinstaller를 먼저 사용해 보시는 것을 권장합니다.

그리고 Chzzk Live Downloader 버전 0.50 이후로 Python의 범용성이 독이 되었던 것이 하나 있었는데, 바로 특정 키가 입력되면, 그 값을 인식해서 반응한다라는 정말 단순한 작업이 그것입니다. 의외로 Python에서는 비차단 입력(Non-Blocking Input)이 쉽지 않다는 것을 아시나요? 다시 말해 키 버퍼가 비어 있으면 계속 작업을 속행하고, 키 버퍼에 키의 값이 들어 있으면 그 값을 꺼내 처리하는 작업이 쉽지 않습니다. 물론 키 처리 방식이 운영 체제마다 다른 것도 그 원인이지만, Python이 운영 체제별로 처리하는 코드를 100% 통합하는 것이 여전히 어렵다는 것을 보여주는 사례이기도 합니다.

저는 다행히 Chzzk Live Downloader 버전 0.56에 이르러 눈에 띄는 함정은 일단 다 피한 것처럼 보이지만, 어디서 또 생각지도 못한 함정이 튀어나올지 몰라 매일매일 테스트를 계속 하고 있습니다. 실제로 주 동작 흐름에 해당하는 코드는 1주일 만에 버전 0.11로 완성했으나, 테스트 과정에서 발생한 nuitka 관련 문제와 키 입력 문제를 해결하는데 무려 45회의 업데이트를 한 달 동안 해야만 했으니까 말 다한 거죠.

여러분도 이러한 어려움을 모두 헤쳐 나오셔서 좋은 결과 있으시길 바랍니다.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.