자바스크립트 window.open 호출 시 QWebEnginePage 타입 반환과 권한 설정을 올바르게 적용하지 않으면 웹뷰 팝업과 링크 기능이 무력화되며, 이 글을 통해 안전한 페이지 관리, 메모리 효율, 사용자 경험(UX) 개선 전략을 적용할 수 있습니다
1. 문제 진단: 왜 PySide6 웹뷰에서 링크 클릭이 무시될까?
파이썬으로 데스크탑 애플리케이션을 개발할 때, 웹 콘텐츠를 표시하기 위해 QWebEngineView를 사용하는 경우가 많습니다. 그러나 링크 클릭 시 아무 반응이 없거나, 팝업이 열리지 않으며 아래와 같은 특정 RuntimeWarning 메시지를 마주하게 될 수 있습니다.
RuntimeWarning: Invalid return value in function 'QWebEnginePage.createWindow',
expected PySide6.QtWebEngineCore.QWebEnginePage,
got PySide6.QtWebEngineWidgets.QWebEngineView.
이 문제는 단순한 경고를 넘어 웹뷰의 핵심 기능을 무력화시키는 심각한 설계 오류입니다. QWebEngineView와 QWebEnginePage의 타입 불일치 원인 분석을 통해 확인해 보면, Qt 내부 로직이 새 창을 띄우기 위해 페이지 객체를 요구하는데 개발자가 뷰(View) 위젯을 반환할 때 발생합니다.
2. 근본 원인: QtWebEngine의 계층 구조 이해
성공적인 파이썬 데스크탑 앱 웹뷰 내 자바스크립트 window.open 활성화를 위해서는 QtWebEngine을 구성하는 3대 요소의 관계를 명확히 알아야 합니다.
| 구성 요소 | 주요 역할 | SEO 및 개발 시 유의사항 |
|---|---|---|
| QWebEngineView | 사용자에게 보여지는 브라우저 위젯 (UI) | 화면 출력 및 레이아웃 담당 |
| QWebEnginePage | 웹 페이지의 논리적 동작 및 이벤트 처리 | createWindow 팝업 로직의 핵심 |
| QWebEngineProfile | 쿠키, 캐시, 사용자 설정 등 브라우저 환경 | 세션 유지 및 개인화 설정 담당 |
팝업 처리는 QWebEnginePage가 담당하며, createWindow() 함수는 반드시 동일한 타입의 페이지 객체를 반환해야 시스템이 정상적으로 새 창의 내용을 로드할 수 있습니다.
3. 해결 방법: PySide6 QWebEnginePage 상속 및 커스텀 구현
가장 완벽한 해결책은 PySide6 QWebEnginePage 상속을 통한 커스텀 브라우저 창 관리 클래스를 만드는 것입니다. 이를 통해 시스템이 요구하는 정확한 타입을 반환하고 팝업 이벤트를 가로챌 수 있습니다.
3.1. 커스텀 페이지 클래스 정의
아래 코드는 새 창 요청이 올 때 현재 뷰의 페이지를 교체하거나 새 창을 생성하여 반환하는 로직을 담고 있습니다.
from PySide6.QtWebEngineCore import QWebEnginePage
class SafeWebEnginePage(QWebEnginePage):
def __init__(self, profile, parent_view):
super().__init__(profile, parent_view)
self.parent_view = parent_view
def createWindow(self, type):
# 반드시 QWebEnginePage 타입의 객체를 반환해야 함
new_page = SafeWebEnginePage(self.profile(), self.parent_view)
self.parent_view.setPage(new_page)
return new_page
3.2. 자바스크립트 팝업 허용 설정
코드 구현 외에도 파이썬 데스크탑 앱 웹뷰 내 자바스크립트 window.open 활성화 방법으로 설정을 변경해야 합니다.
from PySide6.QtWebEngineWidgets import QWebEngineSettings
settings = self.webview.settings()
settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True)
settings.setAttribute(QWebEngineSettings.AllowRunningInsecureContent, True)
4. 실전 최적화: 메모리 관리와 성능 개선
웹뷰를 장시간 실행할 경우 메모리 누수 방지를 위한 QtWebEngineCore 객체 수명 주기 최적화가 필수적입니다. 창이 닫힐 때 페이지 객체가 적절히 소멸되도록 연결해야 합니다.

또한, 공유 프로필(Shared Profile)을 사용하여 여러 페이지가 동일한 캐시와 쿠키를 사용하도록 설정하면 로딩 속도를 획기적으로 개선할 수 있습니다. 이는 성능 최적화 측면에서도 매우 권장되는 방식입니다.
PySide6 웹뷰 링크 오류 해결의 핵심은 타입 일관성 유지에 있습니다. target=”_blank” 요청에 대해 시스템이 기대하는 QWebEnginePage를 정확히 반환하고, 자바스크립트 권한을 적절히 부여합니다.
Q1: 왜 QWebEngineView를 반환하면 안 되나요?
A1: Qt의 내부 설계상 createWindow는 페이지의 ‘콘텐츠 로직’을 담을 그릇인 Page 객체를 먼저 요구합니다. View는 그 Page를 보여주는 틀일 뿐이므로 타입 불일치 에러가 발생합니다.
Q2: 모든 링크를 새 브라우저(크롬 등)로 열게 할 수 있나요?
A2: 네, createWindow 내에서 QDesktopServices.openUrl()을 사용하고 None을 반환하면 시스템 기본 브라우저로 링크를 보낼 수 있습니다.
Q3: window.open() 호출 시 창 크기를 조절할 수 있나요?
A3: 자바스크립트에서 넘겨준 파라미터를 QWebEnginePage의 geometryChangeRequested 시그널을 통해 받아 메인 윈도우 크기를 조절할 수 있습니다.
Q4: 메모리 사용량이 너무 높은데 해결 방법이 있을까요?
A4: 사용하지 않는 페이지 객체는 명시적으로 deleteLater()를 호출하고, 하나의 QWebEngineProfile을 여러 페이지가 공유하도록 설계하세요.
Q5: 보안상 특정 도메인의 팝업만 허용하고 싶습니다.
A5: createWindow 함수 내부에서 요청된 URL을 검사하여 허용 리스트(Whitelist)에 있는 경우에만 Page 객체를 생성하여 반환하면 됩니다.