왜 굳이 Python을 컴파일하는가
Python으로 데스크톱 앱을 만들고 일반 사용자가 실행하길 바란다면, “pip install”은 선택지가 아닙니다. 그들에게는 Python이 설치되어 있지 않고, 설치하고 싶지도 않으며, 그럴 필요도 없어야 합니다. 더블클릭으로 실행되는 진짜 .exe가 필요합니다. 그러려면 컴파일이 필요합니다. 인터프리터로 실행되는 Python 코드와 그 의존성을, 대상 컴퓨터에 Python이 설치되어 있지 않아도 실행되는 형태로 바꾸는 것입니다.
PyInstaller로는 충분하지 않았던 이유
PyInstaller는 제 첫 시도였고, 기술적으로는 동작합니다. 문제는 실제로 하는 일입니다. Python 인터프리터와 모든 의존성을 하나의 패키지로 묶은 다음, 앱을 시작할 때마다 임시 폴더에 풀어 놓거나(--onefile 모드), 낱개 파일이 가득한 폴더로 배포합니다(--onedir 모드). 어느 쪽이든 시작할 때 컴파일된 기계어가 아니라 내장된 Python 런타임을 통해 해석되는 바이트코드를 실행하고 있는 셈입니다. 시작이 굼뜬 느낌이었고, 임시 폴더로 푸는 단계 때문에 첫 실행에서 눈에 띄는 지연이 생겼으며, 백신 소프트웨어는 “%TEMP%에 자신을 풀고 실행을 시작한다”는 동작을 악성코드의 전형적인 휴리스틱으로 취급합니다. 그래서 제가 피하려던 바로 그 오탐 문제가 생겼습니다.
올바른 접근: Nuitka --standalone
Nuitka는 진짜 Python-C 컴파일러입니다. 인터프리터를 묶어 넣지 않고, Python 코드를 C로 번역해 실제 C 컴파일러로 컴파일한 뒤 진짜 네이티브 실행 파일로 링크합니다. 결과물은 내장 런타임 안에서 해석되는 바이트코드가 아니라 컴파일된 기계어로 실행됩니다. 시작은 거의 즉시이며, 무엇보다 중요한 점은 실행 중에 임시 폴더로 자신을 푸는 일이 전혀 없다는 것입니다. 이는 백신 휴리스틱의 가장 큰 단일 트리거를 없애 줍니다. 가장 중요한 플래그는 --standalone입니다. .exe와 그 바로 옆의 네이티브 의존성이 들어 있는 자급자족형 폴더가 만들어지며, 그대로 배포하거나 설치 프로그램으로 감쌀 수 있습니다.
실제로 하는 방법
저를 몇 시간씩 붙잡은 3가지 실수
⚠️ --enable-plugin=tk-inter를 빼먹기
GUI에 Tkinter나 CustomTkinter를 쓰는데 이 플래그를 생략하면, 컴파일된 앱은 요란한 오류를 내지 않습니다. 그저 GUI 툴킷을 불러오지 못해 조용히 충돌하거나 빈 창을 보여 줄 수 있습니다. Nuitka는 기본적으로 GUI 프레임워크 지원을 포함하지 않으므로 프레임워크별로 직접 켜야 합니다.
⚠️ --standalone 대신 --onefile 사용하기
--onefile은 폴더 대신 .exe 하나가 나오기 때문에 솔깃합니다. 공개 배포할 것에는 쓰지 마세요. Onefile 빌드는 실행 중에 여전히 임시 디렉터리로 자신을 풀며, 이는 PyInstaller에서 오탐을 일으킨 것과 같은 동작입니다. 코드가 완전히 깨끗한데도 그 풀어내는 패턴만으로 Windows Defender가 Wacatac이나 Sabsik으로 표시하는 것을 본 적이 있습니다. --standalone은 이를 완전히 피합니다.
⚠️ 에셋 경로를 sys.executable이 아닌 __file__로 해석하기
일반적인 Python 스크립트에서 __file__은 소스 파일의 위치를 가리킵니다. 컴파일된 독립 실행형 빌드에서는 이 가정이 깨집니다. Path(sys.executable).parent를 사용해 사용자의 컴퓨터에서 .exe가 실제로 있는 곳을 찾고, 설정 파일, 아이콘, 번들된 에셋을 그 위치를 기준으로 불러오세요. 개발 중에 소스가 우연히 있던 위치를 기준으로 하지 마세요.
이렇게 하면 실제로 얻는 것
ReelNox Studio와 BackDrop_은 모두 Nuitka --standalone 빌드로 출시되며 DigiCert EV 인증서로 코드 서명되어 있습니다. 시작은 즉각적이고, Windows Defender는 두 설치 프로그램 중 어느 것도 표시하지 않으며, 사용자가 신경 써야 하는 번들 Python 런타임이 필요했던 적도 없습니다. 핵심은 이 조합입니다. --standalone(--onefile이 아니라)과 제대로 된 코드 서명. 이 둘이 함께 Python 앱을 네이티브 Windows 실행 파일로 배포할 때 따라오는 성능 문제와 신뢰 문제를 모두 해결합니다.
About
Patrick Chen — Sublimearts.io의 독립 개발자. ReelNox Studio, BackDrop_, Time Precision을 실제 Windows 사용자에게 전달하면서 제가 실제로 마주친 빌드 문제에 대해 씁니다. 흔한 튜토리얼이 아닙니다.