PySide6로 데스크톱 앱 만들기 #1 PySide6란 — Qt와 파이썬으로 데스크톱 앱
모던 파이썬 기초까지 마치고 나면 파이썬으로 웬만한 자동화 스크립트와 CLI 도구는 만들 수 있습니다. 그런데 도구를 동료나 가족에게 건네주는 순간 벽을 만납니다. 터미널을 열어 본 적 없는 사람에게 “가상 환경을 활성화하고 스크립트를 실행하세요"라고 안내할 수는 없습니다. 버튼과 입력창이 있는 창 하나가 필요해지는 시점이고, 이 시리즈는 그 창을 만드는 법을 다룹니다. 파이썬 트랙의 데스크톱 확장입니다.
총 7편으로 구성됩니다.
- #1 PySide6란 — Qt와 파이썬으로 데스크톱 앱 ← 이번 글
- #2 위젯과 레이아웃 — 화면을 조립하는 법
- #3 시그널과 슬롯 — 이벤트 처리의 핵심
- #4 Qt Designer와 UI 파일 — 화면을 그려서 불러오기
- #5 모델과 뷰 — 리스트·테이블에 데이터 연결
- #6 스레드와 타이머 — 멈추지 않는 UI
- #7 패키징과 배포 — PyInstaller로 실행 파일 만들기
이번 글은 파이썬 GUI 선택지 중에서 PySide6를 고르는 근거를 세우고, 설치 후 첫 창을 띄워 이벤트 루프가 무엇인지 확인하는 데까지 진행합니다.
파이썬 GUI의 선택지 #
파이썬으로 데스크톱 화면을 만드는 방법은 크게 세 갈래입니다.
| 갈래 | 대표 | 특징 |
|---|---|---|
| 표준 라이브러리 | tkinter | 설치 불필요. 다만 위젯 종류가 적고 기본 외관이 낡았습니다 |
| Qt 바인딩 | PySide6, PyQt6 | 성숙한 위젯 셋, 네이티브에 가까운 외관, 상업 소프트웨어 실적 다수 |
| 웹 기술 기반 | Electron 계열, pywebview | 웹 프론트엔드 재사용 가능. 대신 브라우저 엔진을 함께 배포해 무겁습니다 |
간단한 내부 도구라면 tkinter로도 충분합니다. 그러나 위젯이 다양해지고 화면이 커질수록 tkinter는 손이 많이 가고, 웹 기반은 “메모장 수준의 앱이 수백 MB"가 되는 무게 문제가 따라옵니다. 그 사이에서 네이티브 수준의 성능과 풍부한 위젯을 파이썬 문법 그대로 쓰는 선택지가 Qt 바인딩이고, 이 시리즈는 그중 PySide6를 사용합니다.
PySide6와 PyQt6 — 같은 Qt, 다른 라이선스 #
Qt를 파이썬에서 쓰는 바인딩은 두 개가 공존합니다. 이름이 비슷해 혼란스럽지만, API는 거의 같고 결정적 차이는 누가 만들고 어떤 라이선스로 배포하는가입니다.
| 구분 | PySide6 | PyQt6 |
|---|---|---|
| 제작 주체 | Qt Group (Qt 공식) | Riverbank Computing (서드파티) |
| 라이선스 | LGPL v3 (상용 앱 무료 배포 가능) | GPL v3 또는 유료 상용 라이선스 |
| 공식 명칭 | Qt for Python | PyQt |
| API 차이 | Signal, Slot 표기 등 미세한 차이 | pyqtSignal, pyqtSlot 표기 |
핵심은 라이선스입니다. LGPL인 PySide6는 소스 코드를 공개하지 않는 상용 앱을 무료로 만들어 팔 수 있지만, GPL인 PyQt6로 같은 일을 하려면 상용 라이선스를 구매해야 합니다. 학습 자료를 검색하면 PyQt 글이 더 많이 나오지만 API가 거의 같아 상호 참고가 가능하고, 새로 시작한다면 공식 지원과 라이선스 부담이 없는 PySide6가 무난한 선택입니다.
설치 #
프로젝트 디렉터리를 만들고 PySide6를 설치합니다. 모던 파이썬 트랙에서 쓴 uv를 그대로 사용합니다.
uv init todo-app
cd todo-app
uv add pyside6pip install pyside6설치가 끝나면 수백 개의 Qt 모듈이 함께 들어옵니다. 이 시리즈에서 주로 쓰는 것은 위젯이 모인 QtWidgets, 시그널과 타이머 같은 코어 기능의 QtCore 두 모듈입니다.
첫 창 띄우기 #
바로 창을 하나 띄워 보겠습니다. 20줄이면 됩니다.
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("첫 PySide6 앱")
self.resize(400, 300)
button = QPushButton("클릭해 보세요")
self.setCentralWidget(button)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())uv run python main.py버튼 하나가 꽉 채운 창이 뜨면 성공입니다. 짧은 코드지만 구조가 이 시리즈 전체의 뼈대이므로 한 줄씩 확인해 두겠습니다.
QApplication— 앱 전체를 관리하는 객체입니다. 어떤 위젯보다 먼저, 프로그램당 정확히 하나만 만듭니다.QMainWindow— 메뉴 바, 상태 바, 중앙 위젯 영역을 갖춘 최상위 창입니다. 이를 상속한 클래스에 화면 구성을 담는 방식이 표준 패턴입니다.setCentralWidget— 창의 중앙 영역에 위젯을 배치합니다. 지금은 버튼 하나지만, 다음 글부터는 레이아웃으로 조립한 위젯 묶음이 들어갑니다.window.show()— 창은 만들었다고 표시되지 않습니다. show를 호출해야 화면에 나타납니다.
app.exec() — 이벤트 루프의 의미 #
마지막 줄의 app.exec()가 GUI 프로그램과 스크립트의 갈림길입니다. 지금까지의 파이썬 스크립트는 위에서 아래로 실행하고 끝나면 종료했습니다. 그런데 GUI 앱은 사용자가 언제 버튼을 누를지, 언제 창을 닫을지 알 수 없으므로 끝나지 않고 기다리는 구조가 필요합니다.
app.exec()는 이벤트 루프를 시작합니다. 마우스 클릭, 키보드 입력, 창 크기 변경 같은 이벤트가 들어올 때마다 해당 위젯에 전달하고, 다시 다음 이벤트를 기다리는 무한 반복입니다. 창을 닫으면 루프가 종료 코드를 반환하며 끝나고, sys.exit()가 그 코드를 운영체제에 넘겨 줍니다.
이 구조에서 한 가지 규칙이 따라 나옵니다. 이벤트 루프를 오래 막으면 앱 전체가 멈춥니다. 버튼 클릭에 연결한 함수가 10초 걸리는 계산을 하면 그동안 화면 갱신도 클릭 처리도 되지 않습니다. 이 문제와 해법은 #6에서 스레드를 다루며 정면으로 확인합니다.
마무리 #
이번 글의 핵심은 세 가지입니다.
- 파이썬 GUI 선택지 중 PySide6는 Qt 공식 바인딩이며, LGPL 라이선스라 상용 앱까지 무료로 만들 수 있습니다.
- GUI 앱의 표준 뼈대는 QApplication 하나, QMainWindow 상속 클래스, show 호출, 그리고
app.exec()입니다. app.exec()는 이벤트를 기다리며 분배하는 이벤트 루프를 시작하고, 이 루프를 막으면 앱이 멈춥니다.
다음 글인 “PySide6로 데스크톱 앱 만들기 #2 위젯과 레이아웃 — 화면을 조립하는 법"에서는 버튼 하나짜리 창을 넘어, 입력창과 목록과 버튼을 레이아웃으로 조립해 할 일 앱의 화면 뼈대를 세우겠습니다. 이 앱은 시리즈 내내 확장해 나갈 실습 재료입니다.