Python으로 개발한 애플리케이션을 다른 사용자들에게 배포할 때, Python이 설치되지 않은 환경에서도 실행할 수 있도록 해야 하는 경우가 많다. PyInstaller는 이러한 문제를 해결해주는 강력한 도구로, Python 코드를 독립 실행 가능한 executable 파일로 변환해준다.
PyInstaller란?
PyInstaller는 Python 애플리케이션을 Windows, Linux, macOS에서 실행 가능한 독립형 executable로 변환해주는 packaging tool이다. PyInstaller는 Python 프로그램과 모든 의존성을 하나의 패키지로 묶어서, 대상 시스템에 Python이 설치되지 않아도 프로그램이 실행될 수 있도록 한다.
주요 특징
- Cross-platform 지원: Windows, Linux, macOS에서 모두 사용 가능하다
- One-file 배포: 모든 것을 하나의 executable 파일로 패키징할 수 있다
- 의존성 자동 탐지: 필요한 library와 module을 자동으로 찾아서 포함한다
- 다양한 Python 버전 지원: Python 3.8~3.13을 지원한다
- GUI 애플리케이션 지원: Tkinter, PyQt, wxPython 등을 지원한다
PyInstaller 설치
pip를 통한 설치
1
| pip install pyinstaller
|
설치 확인
정상적으로 설치되었다면 PyInstaller의 버전 정보가 출력된다.
기본 사용법
간단한 Python 파일 변환
가장 기본적인 사용법은 다음과 같다:
1
| pyinstaller your_script.py
|
이 명령어는 다음과 같은 결과를 생성한다:
dist/your_script/
디렉토리에 실행 파일과 필요한 library들이 생성된다build/
디렉토리에 중간 빌드 파일들이 생성된다your_script.spec
파일이 생성된다
One-file 배포
모든 것을 하나의 executable 파일로 묶으려면 --onefile
옵션을 사용한다:
1
| pyinstaller --onefile your_script.py
|
이 경우 dist/
디렉토리에 단일 executable 파일이 생성된다.
고급 옵션들
주요 Command Line 옵션들
--onefile
vs --onedir
1
2
3
4
5
| # 하나의 파일로 패키징 (기본값은 --onedir)
pyinstaller --onefile script.py
# 디렉토리 형태로 패키징 (기본값)
pyinstaller --onedir script.py
|
--windowed
(GUI 애플리케이션용)
1
2
| # Windows에서 console 창 숨김 (GUI 애플리케이션용)
pyinstaller --windowed your_gui_app.py
|
Icon 설정
1
2
| # Custom icon 설정
pyinstaller --onefile --icon=app.ico your_script.py
|
추가 데이터 파일 포함
1
2
3
4
5
| # 데이터 파일 포함
pyinstaller --onefile --add-data "data.txt;." your_script.py
# Linux/macOS에서는 콜론(:) 사용
pyinstaller --onefile --add-data "data.txt:." your_script.py
|
Hidden imports 설정
1
2
| # 자동으로 탐지되지 않는 module 강제 포함
pyinstaller --onefile --hidden-import=module_name your_script.py
|
고급 옵션 예제
1
2
3
4
5
6
7
8
| pyinstaller \
--onefile \
--windowed \
--icon=app.ico \
--name="MyApplication" \
--add-data "resources/*;resources/" \
--hidden-import=pkg_resources.py2_warn \
your_script.py
|
.spec 파일 활용
PyInstaller는 처음 실행할 때 .spec
파일을 생성한다. 이 파일을 수정하면 더 세밀한 제어가 가능하다.
기본 .spec 파일 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| # -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['your_script.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='your_script',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='your_script',
)
|
.spec 파일 수정 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| # -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'],
pathex=['/path/to/your/project'],
binaries=[],
datas=[
('resources', 'resources'),
('config.ini', '.'),
],
hiddenimports=[
'pkg_resources.py2_warn',
'requests',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
'tkinter',
'matplotlib',
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='MyApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # GUI 애플리케이션
icon='app.ico',
)
|
.spec 파일로 빌드하기
1
| pyinstaller your_script.spec
|
실무 사용 예제
1. Flask Web Application 패키징
1
2
3
4
5
6
7
8
9
10
11
| # app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, World!"
if __name__ == '__main__':
app.run(debug=True)
|
1
2
| # Flask 애플리케이션 패키징
pyinstaller --onefile --add-data "templates;templates" --add-data "static;static" app.py
|
2. Tkinter GUI 애플리케이션 패키징
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # gui_app.py
import tkinter as tk
from tkinter import messagebox
def show_message():
messagebox.showinfo("Information", "Hello from PyInstaller!")
root = tk.Tk()
root.title("Sample GUI App")
button = tk.Button(root, text="Click Me!", command=show_message)
button.pack(pady=20)
root.mainloop()
|
1
2
| # GUI 애플리케이션 패키징 (console 창 숨김)
pyinstaller --onefile --windowed --icon=app.ico gui_app.py
|
3. 외부 라이브러리를 사용하는 애플리케이션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # data_processor.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def process_data():
data = pd.DataFrame({
'x': np.random.randn(100),
'y': np.random.randn(100)
})
plt.scatter(data['x'], data['y'])
plt.savefig('output.png')
print("Data processed and saved to output.png")
if __name__ == '__main__':
process_data()
|
1
2
| # 복잡한 의존성을 가진 애플리케이션 패키징
pyinstaller --onefile --hidden-import=pandas --hidden-import=numpy --hidden-import=matplotlib data_processor.py
|
최적화 및 문제 해결
파일 크기 최적화
불필요한 모듈 제외
1
| pyinstaller --onefile --exclude-module tkinter --exclude-module matplotlib your_script.py
|
UPX 압축 사용
1
2
| # UPX 설치 후
pyinstaller --onefile --upx-dir=/path/to/upx your_script.py
|
.spec 파일에서 제외 설정
1
2
3
4
5
6
| excludes=[
'tkinter',
'unittest',
'test',
'distutils',
]
|
일반적인 문제들과 해결방법
ModuleNotFoundError
문제: 런타임에 특정 모듈을 찾을 수 없다는 오류가 발생한다.
해결방법:
1
| pyinstaller --onefile --hidden-import=missing_module your_script.py
|
데이터 파일 경로 문제
문제: 패키징된 애플리케이션에서 데이터 파일을 찾을 수 없다.
해결방법:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import sys
import os
def resource_path(relative_path):
""" PyInstaller로 패키징된 애플리케이션에서 리소스 경로를 가져온다 """
try:
# PyInstaller에서 생성된 임시 폴더
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# 사용 예제
data_file = resource_path('data/config.txt')
|
DLL 관련 오류
문제: Windows에서 특정 DLL 파일을 찾을 수 없다는 오류가 발생한다.
해결방법:
1
2
| # 특정 DLL 포함
pyinstaller --onefile --add-binary "path/to/library.dll;." your_script.py
|
Import 최적화
1
2
3
4
5
| # 필요한 모듈만 import
from module import specific_function
# 전체 모듈 import 피하기
# import module # 피하자
|
Lazy Loading 활용
1
2
3
| def heavy_function():
import heavy_library # 필요할 때만 import
return heavy_library.process()
|
Virtual Environment 사용
PyInstaller 사용 시 가상환경을 사용하는 것이 좋다. 이렇게 하면 불필요한 패키지들이 포함되는 것을 방지할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Virtual environment 생성
python -m venv pyinstaller_env
# Virtual environment 활성화
# Windows
pyinstaller_env\Scripts\activate
# Linux/macOS
source pyinstaller_env/bin/activate
# 필요한 패키지만 설치
pip install pyinstaller
pip install -r requirements.txt
# PyInstaller 실행
pyinstaller --onefile your_script.py
|
배포 및 테스트
다양한 환경에서 테스트
패키징된 애플리케이션은 다음 환경에서 테스트해야 한다:
- Python이 설치되지 않은 깔끔한 시스템
- 다른 버전의 Windows/Linux/macOS
- 32-bit와 64-bit 시스템
배포 패키지 준비
1
2
3
4
5
6
7
| # 최종 배포용 디렉토리 구조 예제
MyApplication/
├── MyApp.exe
├── README.md
├── LICENSE
└── docs/
└── user_manual.pdf
|
Installer 생성 (선택사항)
NSIS나 Inno Setup을 사용하여 Windows용 installer를 만들 수 있다:
1
2
3
4
5
6
7
8
9
10
11
12
13
| ; NSIS 스크립트 예제
!define APPNAME "MyApplication"
!define VERSION "1.0.0"
Name "${APPNAME}"
OutFile "${APPNAME}_${VERSION}_installer.exe"
InstallDir "$PROGRAMFILES\${APPNAME}"
Section "Main"
SetOutPath $INSTDIR
File "dist\MyApp.exe"
CreateShortcut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\MyApp.exe"
SectionEnd
|
보안 고려사항
Code Obfuscation
PyInstaller로 패키징된 실행 파일은 쉽게 역공학될 수 있다. 중요한 코드가 있다면 obfuscation을 고려해야 한다:
1
2
3
4
| # PyArmor를 사용한 코드 난독화
pip install pyarmor
pyarmor obfuscate your_script.py
pyinstaller --onefile dist/your_script.py
|
민감한 정보 처리
1
2
3
4
5
6
| # 환경변수나 외부 설정 파일 사용
import os
API_KEY = os.environ.get('API_KEY')
if not API_KEY:
raise ValueError("API_KEY environment variable not set")
|
결론
PyInstaller는 Python 애플리케이션을 독립 실행 가능한 프로그램으로 배포하는 강력하고 편리한 도구이다. 기본적인 사용법부터 고급 최적화 기법까지 다양한 옵션을 제공하므로, 프로젝트의 요구사항에 맞게 적절히 활용할 수 있다.
성공적인 배포를 위해서는:
- Virtual environment 사용으로 깔끔한 의존성 관리
- .spec 파일 활용으로 세밀한 설정 제어
- 다양한 환경에서의 철저한 테스트
- 적절한 최적화를 통한 파일 크기 관리
이러한 점들을 고려하여 PyInstaller를 활용하면, Python 애플리케이션을 효과적으로 배포할 수 있다.