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 | - |
핵심 워크플로우 - 경고: 엄격히 실행
사용자가 패키징을 요청하면 다음 단계를 따릅니다.
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은 단순 패키징 도구이므로 프로그램 자체의 실행 속성을 변경할 수 없기 때문):
"경고: 컴파일 매개변수 검사:
- 콘솔 창 제거:
nuitka --windows-console-mode=disable로 dist 폴더를 컴파일했는지 확인하세요. (그렇지 않으면 설치 후에도 검은 콘솔 창이 나타납니다.)- 고성능:
--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: 대상 사용자가 일반 사무용 컴퓨터인 경우
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 코어 자동 감지 ===
REM Windows 내장 환경 변수 사용 (wmic는 Win11 22H2+에서 제거됨, 감지 실패 시 --jobs=0으로 단일 스레드 컴파일)
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
# 크기가 50MB 미만이면 추가 안내
if ($FinalSize -lt 50) {
Write-Host "`n참고: 최종 크기가 50MB 미만입니다. 좋은 최적화 결과입니다!" -ForegroundColor Green
}
6. DLL 분석 스크립트
analyze_dlls.ps1로 저장 (build_optimized.bat와 같은 디렉터리):
param(
[string]$DistPath
)
if (-not (Test-Path $DistPath)) {
Write-Host "[오류] 디렉터리를 찾을 수 없음: $DistPath" -ForegroundColor Red
exit 1
}
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "DLL 풋프린트 분석" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
$dllFiles = Get-ChildItem -Path $DistPath -Recurse -Include *.dll -File
$totalDllSize = ($dllFiles | Measure-Object -Property Length -Sum).Sum / 1MB
$dllCount = $dllFiles.Count
# 상위 20개 DLL 표시 (크기 순)
$topDlls = $dllFiles | Sort-Object -Property Length -Descending | Select-Object -First 20
Write-Host "`n총 DLL 개수: $dllCount"
Write-Host "총 DLL 크기: $([math]::Round($totalDllSize, 2)) MB"
Write-Host "`n상위 20개 DLL 목록:"
Write-Host ("{0,-60} {1,10} {2,10}" -f "파일 이름", "크기(MB)", "누적 비율")
Write-Host ("-" * 80)
$cumulativeSize = 0
foreach ($dll in $topDlls) {
$sizeMB = $dll.Length / 1MB
$cumulativeSize += $sizeMB
$cumulativePercent = if ($totalDllSize -gt 0) { ($cumulativeSize / $totalDllSize) * 100 } else { 0 }
Write-Host ("{0,-60} {1,10:F2} {2,9:F1}%" -f $dll.Name, $sizeMB, $cumulativePercent)
}
Write-Host "`n최적화 제안:"
if ($totalDllSize -gt 100) {
Write-Host "- DLL 크기가 매우 큽니다. 32비트 Python 사용을 고려하세요." -ForegroundColor Yellow
}
if ($dllFiles | Where-Object { $_.Name -like "*opengl*" }) {
Write-Host "- OpenGL DLL이 감지되었습니다. 'opengl32sw'를 제거할 수 있는지 확인하세요." -ForegroundColor Yellow
}
if ($dllFiles | Where-Object { $_.Name -like "*opencv*" }) {
Write-Host "- OpenCV DLL이 감지되었습니다. 'opencv-python-headless' 사용을 고려하세요." -ForegroundColor Yellow
}
6. Inno Setup 설치 패키지 (전체 메타데이터 + 깔끔한 제거)
Inno Setup 스크립트 생성
사용자 확인 후 다음 Inno Setup 스크립트를 생성합니다:
; Inno Setup 설치 스크립트 (참고 프로젝트 스타일)
; 설치 프로그램 크기 최소화, 깔끔한 제거, 전체 메타데이터
#define MyAppName "{APP_NAME}"
#define MyAppVersion "{VERSION}"
#define MyAppPublisher "{PUBLISHER}"
#define MyAppURL "{URL}"
#define MyAppExeName "{EXE_NAME}"
#define MyAppAssocName MyAppName + " File"
[Setup]
; 참고: InstallDir에 'Program Files'를 사용할 경우 64비트 시스템에서 자동으로 'Program Files (x86)'로 리디렉션되지 않음
; 32비트 앱의 경우 {pf32}를 사용하고 64비트 앱의 경우 {pf}를 사용
AppId={{{E6E1B2C3-8A9B-4D5E-8F7C-2A9B4C5D6E7F}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableDirPage=yes
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
; 필요한 경우 관리자 권한 요청 주석 해제
; PrivilegesRequired=admin
OutputDir={#MyAppName}-installer
OutputBaseFilename={#MyAppName}-v{#MyAppVersion}-setup
SetupIconFile={#ICON_PATH}
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma2/ultra64
InternalCompressLevel=ultra64
SolidCompression=yes
WizardStyle=modern
[Languages]
Name: "korean"; MessagesFile: "compiler:Languages\Korean.isl"
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
[Files]
Source: "{SOURCE_DIR}\*\n"; DestDir: "{app}\n"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[UninstallRun]
[Code]
// 깨끗한 제거를 위한 사용자 정의 함수
function UninstallCleanup(): Boolean;
var
ResultCode: Integer;
begin
// 추가 정리 작업이 필요한 경우 여기에 추가
Result := True;
end;
function InitializeUninstall(): Boolean;
begin
Result := True;
end;
function CurUninstallStepChanged(CurUninstallStep: TUninstallStep): Boolean;
begin
Result := True;
if CurUninstallStep = usPostUninstall then
begin
// 제거 후 추가 정리
end;
end;
사용 방법:
.iss파일을 Inno Setup 컴파일러로 엽니다.- '컴파일' → '컴파일'을 선택합니다.
OutputDir에서 생성된 설치 프로그램을 찾습니다.
참고 사항
- 경고: dist 폴더를 슬리밍할 때,
importlib.metadata가 런타임에 플러그인 검색에 필요할 수 있으므로METADATA및entry_points.txt파일을 절대 삭제하지 마십시오. - 호환성: 이 스킬은 Windows 전용입니다. macOS/Linux 배포가 필요한 경우 알려주시면 교차 플랫폼 전략을 제공해 드리겠습니다.
- 성능: LTO(링크 타임 최적화)는 컴파일 시간을 늘리지만 시작 속도를 크게 향상시킵니다. 프로덕션 빌드에는 항상
--lto=yes를 사용하는 것이 좋습니다.