PyInstaller로 파이썬 스크립트를 exe 실행 파일로 변환하는 완벽 가이드

Python 스크립트를 다른 사람에게 전달할 때마다 "Python 설치하세요", "pip로 의존성 설치하세요"라고 안내하는 것은 비효율적입니다. PyInstaller를 사용하면 .py 파일을 독립 실행 가능한 .exe로 변환하여, Python이 설치되지 않은 환경에서도 바로 실행할 수 있습니다. 이 글에서는 PyInstaller 기본 사용법부터 배포 시 자주 발생하는 리소스 경로 문제, 모듈 누락 에러 해결까지 실무에서 필요한 핵심 내용만 다룹니다.


PyInstaller로 파이썬 스크립트를 exe 실행 파일로 변환하는 완벽 가이드




1. PyInstaller 설치 및 기본 빌드

PyInstaller는 Python 스크립트와 의존성 라이브러리를 하나로 묶어 실행 파일을 생성하는 도구입니다. 설치는 pip 한 줄로 끝납니다.

pip install pyinstaller

기본 빌드 명령은 다음과 같습니다.

pyinstaller my_script.py

실행하면 다음 3가지 산출물이 생성됩니다.

  • dist/: 최종 실행 파일이 위치하는 폴더. 배포 시 이 폴더 내용을 전달합니다.
  • build/: 중간 빌드 파일. 삭제해도 무방합니다.
  • .spec: 빌드 설정 파일. 리소스 포함, 숨겨진 모듈 추가 등 고급 설정 시 수정합니다.

기본 빌드는 여러 파일로 구성된 디렉토리 형태로 출력됩니다. 단일 실행 파일로 만들려면 --onefile(-F) 옵션을 사용하십시오.

pyinstaller --onefile my_script.py

GUI 프로그램의 경우 콘솔 창이 함께 뜨는 것을 방지하려면 --noconsole(-w) 옵션을 추가합니다.

pyinstaller --onefile --noconsole my_script.py

단축 옵션으로는 -F -w를 사용할 수 있습니다.

2. 리소스 경로 처리 문제 해결

PyInstaller로 빌드한 .exe는 실행 시 임시 폴더(sys._MEIPASS)에 압축 해제되어 동작합니다. 따라서 .py 실행 시와 .exe 실행 시 리소스 파일 경로가 달라지며, 이를 처리하지 않으면 FileNotFoundError가 발생합니다.

다음 함수를 사용하여 실행 환경에 따라 올바른 경로를 반환하도록 처리하십시오.

import sys
import os

def get_base_path():
    if getattr(sys, 'frozen', False):
        # exe 상태일 경우
        return sys._MEIPASS
    else:
        # 스크립트 상태일 경우
        return os.path.dirname(__file__)

CONFIG_PATH = os.path.join(get_base_path(), 'config', 'config.txt')

getattr(sys, 'frozen', False)는 현재 실행 파일이 PyInstaller로 빌드된 .exe인지 판별하는 표준 방법입니다. 이 코드를 적용하면 개발 환경(.py)과 배포 환경(.exe) 모두에서 정상적으로 리소스를 로드할 수 있습니다.

만약 설정 파일, 이미지, 사운드 등 외부 리소스를 .exe에 포함시키려면 .spec 파일의 datas 항목을 수정하거나, 배포 시 exe와 함께 해당 폴더를 압축하여 전달하는 방식을 권장합니다.

# .spec 파일 내부 예시
a = Analysis(
    ['my_script.py'],
    datas=[('./config', 'config'), ('./sounds', 'sounds')],
    hiddenimports=[],
    ...
)

3. 모듈 누락 에러(ModuleNotFoundError) 해결

PyInstaller는 정적 분석으로 import 구문을 추적하지만, 동적 import(런타임에 결정되는 모듈)는 감지하지 못합니다. 이 경우 빌드는 성공하지만 실행 시 ModuleNotFoundError가 발생합니다.

해결 방법은 다음 3가지입니다.

  • CLI 옵션 사용: --hidden-import 옵션으로 누락된 모듈을 명시합니다.
pyinstaller --onefile --hidden-import=PIL._tkinter_finder my_script.py
  • .spec 파일 수정: hiddenimports 리스트에 모듈명을 추가합니다.
a = Analysis(
    ['my_script.py'],
    hiddenimports=['PIL._tkinter_finder', 'sklearn.utils._typedefs'],
    ...
)
  • Hook 파일 작성: 특정 패키지의 서브모듈을 전부 포함해야 할 경우, hook-패키지명.py 파일을 작성합니다.
# hook-sklearn.py
from PyInstaller.utils.hooks import collect_submodules
hiddenimports = collect_submodules('sklearn')

디버깅 시에는 --noconsole 옵션을 제거하여 콘솔 출력을 확인하거나, 디렉토리 모드로 빌드하여 dist 폴더 내부를 직접 확인하는 방법을 권장합니다.

4. 실행 파일 용량 최적화 및 배포 전략

PyInstaller는 의존성 라이브러리를 모두 포함하기 때문에 실행 파일 크기가 수십 MB에 달할 수 있습니다. 용량을 줄이려면 다음 방법을 사용하십시오.

  • 깨끗한 가상환경에서 빌드: 프로젝트에 필요한 패키지만 설치한 가상환경(venv)에서 빌드하면 불필요한 라이브러리가 포함되지 않습니다.
  • UPX 압축: PyInstaller는 기본적으로 UPX 압축을 지원합니다. --upx-dir 옵션으로 UPX 경로를 지정하면 추가 압축이 가능합니다.
  • 대체 도구 검토: Nuitka, cx_Freeze 등은 PyInstaller보다 작은 실행 파일을 생성할 수 있습니다.

배포 시에는 다음 구조를 권장합니다.

MyApp_Package/
├─ main.exe
├─ config/
│  └─ config.txt
├─ sounds/
└─ logs/

플랫폼 호환성 주의사항: PyInstaller로 빌드한 실행 파일은 빌드한 플랫폼과 동일한 환경에서만 실행됩니다. Windows에서 빌드한 .exe는 macOS나 Linux에서 실행되지 않으며, 32비트/64비트, Python 버전도 일치해야 합니다. 대상 환경에서 직접 빌드하거나, Docker 등을 활용한 크로스 빌드 환경을 구성하십시오.

요약 및 Action Item

PyInstaller는 Python 스크립트를 독립 실행 파일로 변환하는 가장 간단한 도구입니다. --onefile로 단일 파일을 생성하고, --noconsole로 GUI 프로그램의 콘솔을 숨기며, sys._MEIPASS를 활용해 리소스 경로를 처리하고, --hidden-import로 동적 모듈 누락을 해결하면 대부분의 배포 문제를 해결할 수 있습니다. 지금 당장 가상환경을 생성하고, 필요한 패키지만 설치한 뒤 pyinstaller --onefile --noconsole your_script.py 명령을 실행하여 첫 번째 실행 파일을 만들어보십시오.


#함께 읽으면 좋은 글

Python pip install 속도 느림 해결 방법: 국내 미러 서버 설정 완벽 가이드 : 바로보기

댓글