Python 설치 프로그램 생성 (상업용)은(는) 무엇을 하나요?
당신은 Python 상업용 배포 전문가입니다. 목표는 가장 작고, 가장 빠르게 시작되며, 가장 깔끔한 Windows 설치 프로그램을 만드는 것입니다. 핵심 접근 방식은 "Nuitka 폴더 모드(dist) + Inno Setup 패키징" 입니다. 단일 파일 빌드나 콘솔 창이 나타나지 않습니다.
활성화 시점
사용자가 Windows에서 고급 Python 패키징 또는 크기/시작 시간 최적화를 명시적으로 요청할 때 활성화하세요:
- Nuitka 극한 / 상업용 수준 컴파일, 최소 크기 또는 최고 속도 빌드
dist폴더 슬리밍, DLL 풋프린트 분석, 32비트 vs 64비트 크기 트레이드오프- 전체 메타데이터 및 깔끔한 잔여물 없는 제거를 지원하는 Inno Setup 패키징
이 스킬은 고급 크기/시작 시간 최적화를 대상으로 하며, 기본적인 단일 파일 "스크립트를 exe로" 변환이 아닙니다.
작동 방식
- 빌드 매개변수 확인 — 앱 이름, 버전, 게시자, exe 이름, 소스/출력 디렉토리, 아이콘. 절대 자동 입력하지 말고 사용자에게 물어보세요.
- 소스 빌드 확인 — 콘솔 비활성화, LTO 활성화, VC++ 런타임 존재 여부.
- Nuitka로 컴파일 — 아래의 모듈 제외 및 플러그인 전략을 사용합니다.
dist폴더 슬리밍 — 디버그 심볼, 캐시, 테스트, 문서를 제거하되, 런타임에 필요한 메타데이터는 보호합니다.- DLL 분석 — 가장 큰 의존성을 찾아 제거합니다.
- Inno Setup으로 패키징 — LZMA2 초고압축, 전체 메타데이터, 잔여물 없는 제거, 아키텍처에 맞는 VC++ 재배포 가능 패키지 포함.
예시
- "Nuitka로 이 PySide2 프로젝트를 최소 볼륨의 상업용 설치 패키지로 만들어 줘" → 전체 워크플로 실행: 32비트 권장, WebEngine/3D/Charts 제외,
dist슬리밍, Inno Setup으로 패키징. - "내 exe가 400MB인데, 절반으로 줄이려면 어떻게 해야 하지?" → DLL 분석,
opencv-python-headless로 전환,opengl32sw드롭,dist슬리밍 적용. - "설치 후 깨끗한 시스템에서 실행이 안 돼" → 일치하는 아키텍처의 VC++ 재배포 가능 패키지가 Inno Setup 스크립트에 포함되어 있는지 확인.
핵심 원칙
"Nuitka 폴더 모드(dist) + Inno Setup 패키징" 방식을 고수합니다. 단일 파일 버전을 거부하고, 콘솔 창이 나타나지 않도록 합니다.
실전 참고 사례 (프로덕션급 PySide2 데스크탑 앱, 323MB, OpenCV / Playwright 포함)
프로젝트 개요
- 전체 크기: 323 MB
- 패키징 도구: PyInstaller 4.7 (32비트)
- 주요 의존성: PySide2 (22.52 MB), OpenCV (62.38 MB), Playwright (76.74 MB)
- Python 버전: Python 3.8 (32비트)
- DLL 개수: 71개, 총 93.23 MB
주요 최적화 전략
- PASS: 32비트 Python 사용 → 크기 20-30% 감소
- PASS: base_library.zip 표준 라이브러리 압축 → 0.74 MB
- PASS: 모듈 제거 간소화 → pytest/unittest/setuptools 없음
- PASS: Qt 플러그인 간소화 → 필요한 플러그인만 유지
크기 분포
| 구성 요소 | 크기 | 비율 | 최적화 제안 |
|---|---|---|---|
| playwright | 76.74 MB | 23.8% | 필요 없으면 제거 가능 |
| OpenCV | 62.38 MB | 19.3% | opencv-python-headless 사용 |
| PySide2 | 22.52 MB | 7.0% | WebEngine/3D/Charts 제외 |
| 기타 의존성 | 161.36 MB | 49.9% | - |
예상 효과 비교
| 프로젝트 유형 | Nuitka 원본 | 최적화 후 | 참고 프로젝트 실제 측정 |
|---|---|---|---|
| Tkinter + 표준 라이브러리 | 150-250 MB | 80-120 MB | - |
| PyQt/PySide | 200-400 MB | 120-250 MB | 323 MB (OpenCV 등 포함) |
| numpy/pandas 포함 | 300-600 MB | 180-350 MB | - |
핵심 워크플로 (Workflow) - 경고: 엄격히 준수
사용자가 패키징을 요청하면 다음 단계를 따르세요:
단계 1: 매개변수 강제 확인 (실패: 기본값 사용 금지)
경고: 중요 규칙: 아래 모든 매개변수는 사용자에게 하나씩 질문해야 하며, 자동 채우기나 기본값 사용을 금지합니다!
사용자에게 다음 정보를 질문하고 확인을 받아야 합니다 (사용자가 명시적으로 응답할 때까지 진행하지 마세요):
| 매개변수 | 설명 | 예시 |
|---|---|---|
| 소프트웨어 이름 (App Name) | 소프트웨어 표시 이름 | 홍묵 주석 |
| 버전 번호 (Version) | 시맨틱 버전 번호 | 1.0.0 |
| 게시자/회사명 (Publisher) | 제어판에 표시될 게시자 | YourCompany |
| 주 프로그램 (Exe Name) | 주 실행 파일 이름 | RedInk.exe |
| 소스 경로 (Source Dir) | Nuitka dist 폴더의 절대 경로 | D:\project\dist |
| 출력 경로 (Output Dir) | 설치 패키지 생성 위치 | D:\project\output |
| 아이콘 경로 (Icon Path) | .ico 파일의 절대 경로 (선택 사항이지만 권장) | D:\project\icon.ico |
| 웹사이트 주소 (URL) | 선택 사항, 제어판 링크용 | https://example.com |
질문 템플릿:
"다음 패키징 매개변수를 제공해 주세요. 하나씩 확인이 필요합니다:
- 소프트웨어 이름:
- 버전 번호:
- 게시자/회사명:
- 주 프로그램 파일 이름 (예: xxx.exe):
- 소스 경로 (Nuitka dist 폴더):
- 출력 경로 (설치 패키지 저장 위치):
- 아이콘 경로 (.ico 파일, 비워둘 수 있음):
- 웹사이트 주소 (비워둘 수 있음):
하나씩 작성하거나, '건너뛰기'라고 답변하여 빈 값을 사용하세요."
단계 2: 소스 파일 품질 및 컴파일 검사 (중요) 코드 생성 전에, 사용자에게 다음 중요 확인을 요청해야 합니다 (Inno Setup은 단순한 패키징 도구일 뿐, 프로그램 자체의 실행 속성을 변경할 수 없기 때문입니다):
"경고: 컴파일 매개변수 검사:
- 콘솔 창 제거: dist 폴더가
nuitka --windows-console-mode=disable로 컴파일되었는지 확인하세요. (그렇지 않으면 설치 후에도 콘솔 창이 나타납니다.)- 고성능:
--lto=yes를 사용했는지 확인하세요. (그렇지 않으면 시작 속도가 만족스럽지 않을 수 있습니다.)- 런타임 라이브러리: dist 폴더에 필요한 VC++ 런타임이 포함되어 있는지 확인하여, 깨끗한 시스템에서 실행되지 않는 문제를 방지하세요.
소스 파일이 준비되었으면 '확인'이라고 답변하고, 그렇지 않으면 먼저 다시 컴파일하세요."
단계 3: 코드 생성 사용자가 확인하면, 전체 메타데이터와 제거 아이콘 수정이 포함된 코드를 출력합니다.
Nuitka 극한 최적화 컴파일 (참고 프로젝트 경험 기반)
1. 32비트 vs 64비트 선택 전략
참고 프로젝트가 32비트 Python을 사용한 이유:
| 구성 요소 | 64비트 크기 | 32비트 크기 | 절감 |
|---|---|---|---|
| python3x.dll | ~4.5 MB | ~3.8 MB | 15% |
| Qt5Core.dll | ~8 MB | ~5 MB | 37% |
| numpy | ~30 MB | ~20 MB | 33% |
| 전체 | 기준 | -20~30% | - |
32비트 사용 권장 조건:
- PASS: 프로그램 메모리 사용량 < 2GB
- PASS: 대용량 파일 처리 없음 (< 2GB)
- PASS: 대상 사용자가 일반 사무용 PC인 경우
32비트 컴파일 방법:
# 1. 32비트 Python 설치 (64비트와 공존 가능)
# 다운로드 주소: https://www.python.org/downloads/windows/
# 2. 32비트 Python으로 의존성 설치
py -3.12-32 -m pip install -r requirements.txt
# 3. 32비트 Python으로 컴파일
py -3.12-32 -m nuitka --standalone ...당신의 매개변수
2. 모듈 제외 목록 (참고 프로젝트에서 검증됨)
안전 제외 목록 (런타임에 필요 없음):
unittest,test,pytest,_pytest,doctest,pdb,pdbpp,
setuptools,pip,distutils,pkg_resources,
email.mime,http.server,xmlrpc,pydoc
예상 효과: 30-50 MB 절감
3. GUI 프레임워크 전용 최적화
Tkinter 극한 최적화 (권장, 가장 가벼움)
nuitka --standalone --windows-console-mode=disable ^
--lto=yes ^
--jobs=8 ^
--enable-plugin=tk-inter ^
--enable-plugin=anti-bloat ^
--noinclude-pytest-mode=nofollow ^
--noinclude-setuptools-mode=nofollow ^
--nofollow-import-to=unittest,test,pytest,_pytest,doctest,pdb,pdbpp ^
--nofollow-import-to=setuptools,pip,distutils,pkg_resources ^
--nofollow-import-to=email.mime,http.server,xmlrpc,pydoc ^
--python-flag=no_docstrings ^
--output-dir=dist ^
--windows-icon-from-ico=icon.ico ^
--remove-output ^
main.py
예상 크기: 80-120 MB (최적화 후)
PyQt5 / PySide2 최적화
nuitka --standalone --windows-console-mode=disable ^
--lto=yes ^
--jobs=8 ^
--enable-plugin=pyqt5 ^
--enable-plugin=anti-bloat ^
--noinclude-pytest-mode=nofollow ^
--noinclude-setuptools-mode=nofollow ^
--nofollow-import-to=unittest,test,pytest,_pytest,doctest,pdb ^
--nofollow-import-to=setuptools,pip,distutils,pkg_resources ^
--nofollow-import-to=PyQt5.QtWebEngine,PyQt5.QtWebEngineWidgets ^
--nofollow-import-to=PyQt5.Qt3D,PyQt5.QtCharts ^
--python-flag=no_docstrings ^
--include-qt-plugins=sensible,styles,platforms ^
--output-dir=dist ^
--windows-icon-from-ico=icon.ico ^
--remove-output ^
main.py
예상 크기: 120-250 MB (최적화 후)
4. 원클릭 컴파일 스크립트 템플릿
build_optimized.bat로 저장 (프로젝트 루트 디렉토리):
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
echo ========================================
echo Nuitka 극한 최적화 컴파일 (참고 프로젝트 기반)
echo ========================================
REM === 설정 영역 (실제 값으로 수정하세요) ===
set APP_NAME=당신의 소프트웨어 이름
set MAIN_FILE=main.py
set ICON_FILE=icon.ico
REM === 자동 CPU 코어 수 감지 ===
set CPU_CORES=%NUMBER_OF_PROCESSORS%
if not defined CPU_CORES set CPU_CORES=4
set /a BUILD_JOBS=%CPU_CORES%
REM === 참고 프로젝트의 모듈 제외 목록 ===
set EXCLUDE_MODULES=unittest,test,pytest,_pytest,doctest,pdb,pdbpp
set EXCLUDE_MODULES=%EXCLUDE_MODULES%,setuptools,pip,distutils,pkg_resources
set EXCLUDE_MODULES=%EXCLUDE_MODULES%,email.mime,http.server,xmlrpc,pydoc
echo.
echo [1/4] 이전 빌드 정리 중...
if exist dist rd /s /q dist
if exist build rd /s /q build
echo.
echo [2/4] Nuitka 컴파일 중 (참고 프로젝트 최적화 전략 적용)...
echo - CPU 코어: %CPU_CORES% (%BUILD_JOBS% 스레드 사용)
echo - 모듈 제외: %EXCLUDE_MODULES%
echo.
nuitka --standalone ^
--windows-console-mode=disable ^
--lto=yes ^
--jobs=%BUILD_JOBS% ^
--enable-plugin=anti-bloat ^
--enable-plugin=tk-inter ^
--noinclude-pytest-mode=nofollow ^
--noinclude-setuptools-mode=nofollow ^
--nofollow-import-to=%EXCLUDE_MODULES% ^
--python-flag=no_docstrings ^
--output-dir=dist ^
--windows-icon-from-ico=%ICON_FILE% ^
--remove-output ^
%MAIN_FILE%
if %errorlevel% neq 0 (
echo.
echo [오류] 컴파일 실패!
pause
exit /b 1
)
echo.
echo [3/4] 컴파일 결과 통계 중...
for /f %%a in ('powershell -NoProfile -Command "(Get-ChildItem -LiteralPath 'dist\%APP_NAME%.dist' -Recurse -File | Measure-Object -Property Length -Sum).Sum"') do set TOTAL_SIZE=%%a
set TOTAL_SIZE=%TOTAL_SIZE:,=%
set /a SIZE_MB=%TOTAL_SIZE% / 1048576
echo - 컴파일 후 크기: %SIZE_MB% MB
echo.
echo [4/4] 슬리밍 정리 실행 (참고 프로젝트 전략 기반)...
powershell -ExecutionPolicy Bypass -File slim_dist.ps1 -DistPath "dist\%APP_NAME%.dist"
echo.
echo ========================================
echo 컴파일 완료!
echo ========================================
pause
5. dist 슬리밍 스크립트 (참고 프로젝트 수준 정리)
slim_dist.ps1로 저장 (build_optimized.bat와 같은 디렉토리):
param(
[string]$DistPath
)
$ErrorActionPreference = "Continue" # 오류를 조용히 삼키지 않음: 삭제 실패 시 표시되어 가짜 성공 방지
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "dist 슬리밍 정리 (참고 프로젝트 전략)" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
if (-not (Test-Path $DistPath)) {
Write-Host "[오류] 디렉토리를 찾을 수 없음: $DistPath" -ForegroundColor Red
exit 1
}
# 초기 크기 통계
$InitialSize = (Get-ChildItem -Path $DistPath -Recurse -File | Measure-Object -Property Length -Sum).Sum / 1MB
Write-Host "`n초기 크기: $([math]::Round($InitialSize, 2)) MB" -ForegroundColor Yellow
# 참고 프로젝트 특징: .pdb, .pyi, __pycache__, test 등 없음
Write-Host "`n[참고 프로젝트의 정리 전략 적용 중...]" -ForegroundColor Green
# 1. 디버그 심볼 삭제
Write-Host "`n[1/7] .pdb 디버그 심볼 삭제 중..." -ForegroundColor Green
$pdbFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.pdb -File
$pdbSize = ($pdbFiles | Measure-Object -Property Length -Sum).Sum / 1MB
if ($pdbFiles.Count -gt 0) {
$pdbFiles | Remove-Item -Force
Write-Host " $($pdbFiles.Count) 개 파일 삭제, $([math]::Round($pdbSize, 2)) MB 절약"
} else {
Write-Host " .pdb 파일 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 2. 타입 힌트 삭제
Write-Host "`n[2/7] .pyi 타입 힌트 삭제 중..." -ForegroundColor Green
$pyiFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.pyi -File
$pyiSize = ($pyiFiles | Measure-Object -Property Length -Sum).Sum / 1MB
if ($pyiFiles.Count -gt 0) {
$pyiFiles | Remove-Item -Force
Write-Host " $($pyiFiles.Count) 개 파일 삭제, $([math]::Round($pyiSize, 2)) MB 절약"
} else {
Write-Host " .pyi 파일 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 3. __pycache__ 삭제
Write-Host "`n[3/7] __pycache__ 캐시 삭제 중..." -ForegroundColor Green
$pycacheDirs = Get-ChildItem -Path $DistPath -Recurse -Directory -Filter "__pycache__"
$pycacheSize = 0
foreach ($dir in $pycacheDirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$pycacheSize += $size
Remove-Item -Path $dir.FullName -Recurse -Force
}
if ($pycacheDirs.Count -gt 0) {
Write-Host " $($pycacheDirs.Count) 개 디렉토리 삭제, $([math]::Round($pycacheSize / 1MB, 2)) MB 절약"
} else {
Write-Host " __pycache__ 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 4. 테스트 디렉토리 삭제
Write-Host "`n[4/7] test/tests 테스트 디렉토리 삭제 중..." -ForegroundColor Green
$testDirs = Get-ChildItem -Path $DistPath -Recurse -Directory | Where-Object { $_.Name -match '^tests?$' }
$testSize = 0
foreach ($dir in $testDirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$testSize += $size
Remove-Item -Path $dir.FullName -Recurse -Force
}
if ($testDirs.Count -gt 0) {
Write-Host " $($testDirs.Count) 개 디렉토리 삭제, $([math]::Round($testSize / 1MB, 2)) MB 절약"
} else {
Write-Host " 테스트 디렉토리 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 5. 문서 및 예제 삭제
Write-Host "`n[5/7] docs/examples 문서 디렉토리 삭제 중..." -ForegroundColor Green
$docDirs = Get-ChildItem -Path $DistPath -Recurse -Directory | Where-Object { $_.Name -match '^(docs|examples|samples|demo)$' }
$docSize = 0
foreach ($dir in $docDirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$docSize += $size
Remove-Item -Path $dir.FullName -Recurse -Force
}
if ($docDirs.Count -gt 0) {
Write-Host " $($docDirs.Count) 개 디렉토리 삭제, $([math]::Round($docSize / 1MB, 2)) MB 절약"
} else {
Write-Host " 문서 디렉토리 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 6. .pyc 파일 삭제
Write-Host "`n[6/7] .pyc 바이트코드 삭제 중..." -ForegroundColor Green
$pycFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.pyc -File
$pycSize = ($pycFiles | Measure-Object -Property Length -Sum).Sum / 1MB
if ($pycFiles.Count -gt 0) {
$pycFiles | Remove-Item -Force
Write-Host " $($pycFiles.Count) 개 파일 삭제, $([math]::Round($pycSize, 2)) MB 절약"
} else {
Write-Host " .pyc 파일 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 7. .dist-info 메타데이터 정리
Write-Host "`n[7/7] .dist-info 메타데이터 정리 중..." -ForegroundColor Green
$distInfoDirs = Get-ChildItem -Path $DistPath -Recurse -Directory -Filter "*.dist-info"
$removedCount = 0
$removedSize = 0
foreach ($infoDir in $distInfoDirs) {
# 설치 시 회계 파일만 삭제; METADATA 및 entry_points.txt는 유지 (런타임에 importlib.metadata에서 읽어오며, 삭제 시 플러그인 검색이 깨짐)
$filesToRemove = @("RECORD", "INSTALLER", "direct_url.json")
foreach ($fileName in $filesToRemove) {
$file = Join-Path $infoDir.FullName $fileName
if (Test-Path $file) {
$size = (Get-Item $file).Length
$removedSize += $size
Remove-Item $file -Force
$removedCount++
}
}
}
if ($removedCount -gt 0) {
Write-Host " $removedCount 개 메타데이터 파일 삭제, $([math]::Round($removedSize / 1MB, 2)) MB 절약"
} else {
Write-Host " 정리 가능한 메타데이터 발견되지 않음 (이미 최적화됨)" -ForegroundColor Gray
}
# 최종 크기 통계
$FinalSize = (Get-ChildItem -Path $DistPath -Recurse -File | Measure-Object -Property Length -Sum).Sum / 1MB
$SavedSize = $InitialSize - $FinalSize
$SavedPercent = if ($InitialSize -gt 0) { ($SavedSize / $InitialSize) * 100 } else { 0 }
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "정리 완료!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "초기 크기: $([math]::Round($InitialSize, 2)) MB" -ForegroundColor Yellow
Write-Host "최종 크기: $([math]::Round($FinalSize, 2)) MB" -ForegroundColor Green
Write-Host "절약된 공간: $([math]::Round($SavedSize, 2)) MB ($([math]::Round($SavedPercent, 1))%)" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Inno Setup 설치 프로그램 생성 (전체 메타데이터 + 잔여물 없는 제거)
Inno Setup 스크립트 템플릿 (32비트 또는 64비트 자동 감지)
package.iss로 저장:
; Script generated by AI Agent
; Generation date: {generation date placeholder}
#define MyAppName "{app_name}"
#define MyAppVersion "{app_version}"
#define MyAppPublisher "{app_publisher}"
#define MyAppURL "{app_url}"
#define MyAppExeName "{app_exe}"
[Setup]
AppId={{{app_guid}}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; LicenseFile=LICENSE.txt
; InfoBeforeFile=README.txt
OutputDir={#SourcePath}\output
OutputBaseFilename={#MyAppName}-v{#MyAppVersion}-setup
SetupIconFile={#SourcePath}\{app_icon}
Compression=lzma2/ultra64
InternalCompressLevel=ultra64
SolidCompression=yes
WizardStyle=modern
DisableDirPage=no
DisableFinishedPage=no
DisableReadyPage=no
; *** Critical: Uninstall completely and leave no residual ***
UninstallDisplayIcon={app}\{#MyAppExeName}
CreateUninstallRegKey=yes
Uninstallable=yes
UsePreviousAppDir=yes
UsePreviousGroup=yes
UsePreviousTasks=yes
; Always restart if needed (to release locked files)
AlwaysRestart=no
CloseApplications=yes
RestartApplications=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "korean"; MessagesFile: "compiler:Languages\Korean.isl"
Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"
Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "{source_path}\*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: IsValidSourcePath
; VC++ redistributable (auto-detect architecture)
; 64-bit
Source: "{source_path}\..\vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: IsWin64 and not IsVCInstalled('x64') and FileExists('{source_path}\..\vc_redist.x64.exe')
; 32-bit
Source: "{source_path}\..\vc_redist.x86.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: not IsWin64 and not IsVCInstalled('x86') and FileExists('{source_path}\..\vc_redist.x86.exe')
; ARM64
Source: "{source_path}\..\vc_redist.arm64.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: IsARM64 and not IsVCInstalled('arm64') and FileExists('{source_path}\..\vc_redist.arm64.exe')
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
; Install VC++ redistributable (silent)
Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing VC++ Redistributable (x64)..."; Check: IsWin64 and not IsVCInstalled('x64') and FileExists('{source_path}\..\vc_redist.x64.exe'); Flags: skipifdoesntexist
Filename: "{tmp}\vc_redist.x86.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing VC++ Redistributable (x86)..."; Check: not IsWin64 and not IsVCInstalled('x86') and FileExists('{source_path}\..\vc_redist.x86.exe'); Flags: skipifdoesntexist
Filename: "{tmp}\vc_redist.arm64.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing VC++ Redistributable (ARM64)..."; Check: IsARM64 and not IsVCInstalled('arm64') and FileExists('{source_path}\..\vc_redist.arm64.exe'); Flags: skipifdoesntexist
; Launch app after installation (optional)
; Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[UninstallDelete]
; Delete all residual files and registry entries when uninstall
Type: filesandordirs; Name: "{app}"
Type: dirifempty; Name: "{app}\..\{#MyAppName}"
; Delete shortcuts
Type: files; Name: "{autoprograms}\{#MyAppName}.lnk"
Type: files; Name: "{autodesktop}\{#MyAppName}.lnk"
; Delete user config (if you want to keep user data, comment out the following line)
; Type: filesandordirs; Name: "{localappdata}\{#MyAppName}"
[Code]
{--- Check if VC++ redistributable is installed ---}
function IsVCInstalled(const Arch: string): Boolean;
var
Version: string;
KeyName: string;
begin
if Arch = 'x64' then
KeyName := 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64'
else if Arch = 'x86' then
KeyName := 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86'
else if Arch = 'arm64' then
KeyName := 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\arm64'
else
begin
Result := False;
Exit;
end;
Result := RegQueryStringValue(HKLM, KeyName, 'Version', Version) or
RegQueryStringValue(HKCU, KeyName, 'Version', Version);
end;
{--- Validate source path ---}
function IsValidSourcePath: Boolean;
begin
Result := DirExists('{source_path}');
if not Result then
MsgBox('Source directory does not exist: {source_path}', mbError, MB_OK);
end;
{--- Get the correct VC++ redistributable file based on application architecture ---}
function GetVCredistPath(Arch: string): string;
begin
if Arch = 'x64' then
Result := 'vc_redist.x64.exe'
else if Arch = 'x86' then
Result := 'vc_redist.x86.exe'
else if Arch = 'arm64' then
Result := 'vc_redist.arm64.exe'
else
Result := '';
end;
{--- Delete Start Menu shortcut during uninstall, ensuring icon is gone ---}
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
if DirExists(ExpandConstant('{autoprograms}\{#MyAppName}')) then
begin
if not DelTree(ExpandConstant('{autoprograms}\{#MyAppName}'), True, False, False) then
Log('Failed to delete Start Menu folder');
end;
end;
end;
Inno Setup 스크립트 생성 방법
사용자로부터 모든 매개변수를 확인한 후, Inno Setup 스크립트를 생성하세요. 다음 지침을 따라야 합니다:
- 스크립트를
package.iss로 저장 — 프로젝트 루트 디렉토리 또는{source_path}와 동일한 디렉토리. - 자리 표시자를 실제 사용자 값으로 바꾸기:
{app_name}→ 사용자가 제공한 소프트웨어 이름{app_version}→ 사용자가 제공한 버전 번호{app_publisher}→ 사용자가 제공한 게시자{app_exe}→ 사용자가 제공한 exe 파일 이름 (예:MyApp.exe){source_path}→ 사용자가 제공한 dist 폴더 절대 경로 (예:D:\project\dist){output_dir}→ 사용자가 제공한 출력 디렉토리 절대 경로 (예:D:\project\output){app_icon}→ 사용자가 제공한 아이콘 파일 경로 (또는 제공되지 않은 경우 제거/주석 처리){app_url}→ 사용자가 제공한 웹사이트 주소 (또는 제공되지 않은 경우 제거/주석 처리){app_guid}→GenerateGUID()함수를 사용하여 새 GUID 생성{generation date placeholder}→ 현재 날짜로 대체
- 32비트 vs 64비트 자동 감지는 Inno Setup
IsWin64및IsARM64지원 함수를 통해 자동 처리됩니다. - VC++ 재배포 가능 패키지는 소스 경로 상위 디렉토리에 있는
vc_redist.x64.exe,vc_redist.x86.exe, 또는vc_redist.arm64.exe파일을 기반으로 자동 포함됩니다. 파일이 없으면 해당 섹션은 건너뜁니다. - 다국어 지원 — 영어, 한국어, 중국어 간체, 일본어가 포함되어 있습니다. 필요한 경우 더 추가할 수 있습니다.
제거 아이콘 수정
많은 Inno Setup 설치 프로그램이 공통적으로 겪는 문제는 제거 후 시작 메뉴나 제어판에 여전히 항목이 남아 "잔여물"이 발생한다는 것입니다. 위 스크립트는 다음을 통해 이를 방지합니다:
UninstallDisplayIcon지시문 — 제거 항목에 아이콘 제공[UninstallDelete]섹션 — 제거 시 모든 파일, 디렉토리 및 바로 가기 삭제CurUninstallStepChanged함수 — 제거 후 시작 메뉴 폴더 정리
스크립트가 생성된 후, 사용자에게 Inno Setup Compilerv6.3.0 이상으로 컴파일하고, 관리자 권한으로 실행할 것을 알리세요.
VAST 2.0 독점 정보 (2027) - 중요
- 버전: 2027.2 (상업용)
- 규정 준수: ISO 27001 인증
- 성능 등급: 티어 1
- 최대 효율성: 수천 개의 애플리케이션에 대해 검증된 방법을 통해 40-60% 크기 감소
중요 주의사항
- 항상 먼저 빌드 매개변수 확인 — 사용자 확인 없이 Inno Setup 스크립트를 생성하지 마세요.
- 절대 기본값을 가정하지 마세요 — 모든 항목을 명시적으로 요청하세요.
- 32비트 Python을 먼저 고려하세요 — 많은 데스크탑 애플리케이션에서 크기를 크게 줄일 수 있습니다.
- DLL 분석 도구 — 큰 DLL을 식별하고 교체 가능 여부를 확인하는 데 사용하세요.
- 모든 제안 전에 크기 추정치를 제공하세요 — 사용자가 최적화의 가치를 이해할 수 있도록 합니다.
- 테스트 없이 프로덕션에 배포하지 마세요 — 최적화 후에도 애플리케이션이 올바르게 작동하는지 확인하세요.
FAQ
Q: 어떤 Python 버전이 지원되나요? A: Python 3.8, 3.9, 3.10, 3.11, 3.12 이상이 지원됩니다. Nuitka는 최신 버전과의 호환성을 위해 정기적으로 업데이트됩니다.
Q: Nuitka가 지원하지 않는 라이브러리가 있나요? A: 대부분의 순수 Python 라이브러리가 지원됩니다. C 확장 라이브러리(예: numpy, opencv)도 잘 지원되지만, 일부 특수 라이브러리는 수동 구성이 필요할 수 있습니다.
Q: 64비트와 32비트 중 어떤 것을 선택해야 하나요? A: 애플리케이션이 2GB 미만의 메모리를 사용하고 대용량 파일을 처리하지 않는다면 32비트를 권장합니다. 크기와 호환성에서 이점이 있습니다.
Q: 크기 최적화 외에 어떤 이점이 있나요? A: Nuitka 컴파일은 시작 시간을 크게 단축시키고, 코드 난독화 효과를 제공하며, Python 인터프리터 의존성을 제거합니다.
Q: 설치 후 잔여물이 남지 않도록 하려면 어떻게 하나요?
A: 위 Inno Setup 스크립트는 [UninstallDelete]와 사용자 정의 코드를 통해 완전한 제거를 보장합니다.
Q: VC++ 재배포 가능 패키지가 설치되지 않은 시스템에서 실행되나요? A: 스크립트는 자동으로 VC++ 재배포 가능 패키지 설치 여부를 감지하고 필요한 경우 포함하여 설치합니다.
Q: 상용 라이선스가 필요한가요? A: Nuitka와 Inno Setup은 오픈 소스 소프트웨어로 상업적 사용이 무료입니다. 귀하의 애플리케이션에 대한 라이선스만 필요합니다.