๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
SEO/seo-tips

ํŒŒ์ด์ฌ PySide6 ์›น๋ทฐ์—์„œ ๋งํฌ๊ฐ€ ์•ˆ ์—ด๋ฆด ๋•Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

PySide6 ์›น๋ทฐ์—์„œ ๋งํฌ๊ฐ€ ์•ˆ ์—ด๋ฆด ๋•Œ?

ํŒŒ์ด์ฌ์œผ๋กœ ๋ฐ์Šคํฌํƒ‘ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ, ์›น ์ฝ˜ํ…์ธ ๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด QWebEngineView๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋งํฌ ํด๋ฆญ ์‹œ ์•„๋ฌด ๋ฐ˜์‘์ด ์—†๊ฑฐ๋‚˜, ํŒ์—…์ด ์—ด๋ฆฌ์ง€ ์•Š์œผ๋ฉฐ, ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ๋งˆ์ฃผํ•˜๊ฒŒ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

RuntimeWarning: Invalid return value in function
'QWebEnginePage.createWindow', expected PySide6.QtWebEngineCore.QWebEnginePage,
got PySide6.QtWebEngineWidgets.QWebEngineView.

๊ฒ‰๋ณด๊ธฐ์—” ๋‹จ์ˆœํ•œ ๊ฒฝ๊ณ ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ์ด ๋ฌธ์ œ๋Š” ์›น๋ทฐ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๋ฌด๋ ฅํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” ์˜ค๋ฅ˜์˜ ์›์ธ๋ถ€ํ„ฐ ํ•ด๊ฒฐ์ฑ…๊นŒ์ง€, ์‰ฝ๊ฒŒ ๋”ฐ๋ผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ด์ฌ ๋ฐ์Šคํฌํƒ‘ ์•ฑ์—์„œ ํŒ์—…์ด ์•ˆ ๋  ๋•Œ

1. ๋ฌธ์ œ ์ง„๋‹จ: ์™œ ์ด๋Ÿฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ• ๊นŒ?

1.1 ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ํ•ด์„

์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์š”์•ฝ

createWindow() ํ•จ์ˆ˜์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด ํƒ€์ž…์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

QWebEnginePage๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์‹ค์ˆ˜๋กœ QWebEngineView๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

1.2 ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋Œ€ํ‘œ ์ƒํ™ฉ

  • <a href="..." target="_blank"> ๋งํฌ ํด๋ฆญ ์‹œ
  • JavaScript window.open() ํ˜ธ์ถœ ์‹œ
  • HTML ํŒ์—… ์ฐฝ ์ƒ์„ฑ ์‹œ๋„

1.3 ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฌด์„œ์šด ์ด์œ 

  • ๋””๋ฒ„๊น…์ด ์–ด๋ ต๋‹ค: ํ”„๋กœ๊ทธ๋žจ์€ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ์กฐ์šฉํžˆ ์ด์ƒ ๋™์ž‘์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ๋Šฅ ์ œํ•œ: ์ƒˆ ์ฐฝ์ด ์•„๋‹Œ ํ˜„์žฌ ์ฐฝ์—์„œ ๋งํฌ๊ฐ€ ์—ด๋ฆฝ๋‹ˆ๋‹ค.
  • ํ”Œ๋žซํผ ๊ฐ„ ๋ถˆ์ผ์น˜: ์šด์˜์ฒด์ œ์— ๋”ฐ๋ผ ์ฆ์ƒ์ด ๋‹ค๋ฅด๊ฒŒ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๊ทผ๋ณธ ์›์ธ: Qt์˜ ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ

2.1 QWebEngine์˜ 3๋Œ€ ์š”์†Œ

QWebEngineView ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๋Š” ์œ„์ ฏ
QWebEnginePage ํŽ˜์ด์ง€ ๋™์ž‘, ํŒ์—… ์ฒ˜๋ฆฌ ๋“ฑ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๋‹ด๋‹น
QWebEngineProfile

ํŒ์—… ์ฒ˜๋ฆฌ๋Š” QWebEnginePage๊ฐ€ ๋‹ด๋‹นํ•˜๋ฉฐ, createWindow()๋Š” ๋ฐ˜๋“œ์‹œ QWebEnginePage ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2.2 ํƒ€์ž… ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ 

  1. ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ์ฐฝ ์š”์ฒญ (target="_blank" ๋˜๋Š” JS)
  2. Qt๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ createWindow() ํ˜ธ์ถœ
  3. ๋ฐ˜ํ™˜๊ฐ’์ด ์ž˜๋ชป๋˜๋ฉด ๊ฒฝ๊ณ  ๋ฐœ์ƒ + ๋™์ž‘ ์‹คํŒจ

3. ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์˜ฌ๋ฐ”๋ฅธ ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜ํ•˜๊ธฐ

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):
        new_page = SafeWebEnginePage(self.profile(), self.parent_view)
        self.parent_view.setPage(new_page)
        return new_page  # ๋ฐ˜๋“œ์‹œ QWebEnginePage ๋ฐ˜ํ™˜

3.2 ์›น๋ทฐ์— ์ ์šฉํ•˜๊ธฐ

profile = QWebEngineProfile("MyProfile", self)
webpage = SafeWebEnginePage(profile, self.webview)
self.webview.setPage(webpage)

3.3 ํŒ์—… ํ—ˆ์šฉ ๋ฐ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

from PySide6.QtWebEngineWidgets import QWebEngineSettings
settings = self.webview.settings()
settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True)
webpage.windowCloseRequested.connect(self.handle_window_close)

ํŒŒ์ด์ฌ ๋งํฌ ์•ˆ๋ ๋•Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

4. ์‹ค์ „ ์˜ˆ์ œ

import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineProfile

class SafeWebEnginePage(QWebEnginePage):
    def __init__(self, profile, parent_view):
        super().__init__(profile, parent_view)
        self.parent_view = parent_view

    def createWindow(self, type):
        new_page = SafeWebEnginePage(self.profile(), self.parent_view)
        self.parent_view.setPage(new_page)
        return new_page

class BrowserWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.webview = QWebEngineView()
        self.setCentralWidget(self.webview)

        profile = QWebEngineProfile("Default", self)
        webpage = SafeWebEnginePage(profile, self.webview)
        self.webview.setPage(webpage)
        self.webview.setUrl("https://example.com")

app = QApplication(sys.argv)
window = BrowserWindow()
window.show()
sys.exit(app.exec())

ํ…Œ์ŠคํŠธ HTML ์ฝ”๋“œ

<a href="https://python.org" target="_blank">์ •์ƒ ๋งํฌ</a>
<button onclick="window.open('https://qt.io', '_blank')">JS ํŒ์—…</button>
<a href="https://google.com" target="newwin">์ด๋ฆ„ ์ง€์ • ์ฐฝ</a>

5. ๋ฌธ์ œ ์˜ˆ๋ฐฉ์„ ์œ„ํ•œ ์‚ฌ๋ก€

ํƒ€์ž… ์ผ๊ด€์„ฑ ์œ ์ง€

๋ฐ˜ํ™˜๊ฐ’์€ ๋ฐ˜๋“œ์‹œ QWebEnginePage ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

if isinstance(new_page, QWebEnginePage):
    self.parent_view.setPage(new_page)

์ฐธ์กฐ ์ˆœํ™˜ ๋ฐฉ์ง€

def __del__(self):
    self.parent_view = None

์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€

def createWindow(self, type):
    try:
        # ์ •์ƒ ์ฒ˜๋ฆฌ
    except Exception as e:
        print(f"์ฐฝ ์ƒ์„ฑ ์˜ค๋ฅ˜: {e}")
        return super().createWindow(type)

์›Œ๋“œํ”„๋ ˆ์Šค ๋ธ”๋กœ๊ทธ ์ด์ „ ์ž๋™ํ™” ํ”„๋กœ๊ทธ๋žจ ๊ณต์œ  ์‹œ๊ฐ„ ์ ˆ์•ฝ๊ณผ ์ตœ์ ํ™” ํŒ

๋ธ”๋กœ๊ทธ ์ด์ „ ์ž๋™ํ™” ์†Œํ”„ํŠธ์›จ์–ด๋กœ ์›Œ๋“œํ”„๋ ˆ์Šค ์ฝ˜ํ…์ธ  ์ด์ „ํ•˜๊ธฐ ๋ธ”๋กœ๊ทธ๋ฅผ ์šด์˜ํ•˜๋ฉด์„œ ๋ธ”๋กœ๊ทธ ์ด์ „์— ๋Œ€ํ•œ ํ•„์š”์„ฑ์„ ๋А๋ผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ, ์ฃผ๋กœ ์„ฑ๋Šฅ ํ–ฅ์ƒ, ๋””์ž์ธ ๋ณ€๊ฒฝ,

everydayhub.tistory.com

6. ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•

ํŽ˜์ด์ง€ ํ’€๋ง

_page_pool = []
def createWindow(self, type):
    if _page_pool:
        return _page_pool.pop()
    return SafeWebEnginePage(self.profile(), self.parent_view)

ํ”„๋กœํ•„ ์žฌ์‚ฌ์šฉ

_shared_profile = QWebEngineProfile("Shared", self)
def createWindow(self, type):
    return SafeWebEnginePage(_shared_profile, self.parent_view)

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

_shared_profile = QWebEngineProfile("Shared", self)
def createWindow(self, type):
    return SafeWebEnginePage(_shared_profile, self.parent_view)

์•ˆ์ •์ ์ธ ์›น๋ทฐ ์•ฑ์„ ์œ„ํ•œ ํ•œ ๊ฑธ์Œ

์ด ๊ธ€์—์„œ ์†Œ๊ฐœํ•œ createWindow ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ๋‹จ์ˆœํ•œ ๊ฒฝ๊ณ ๋ฅผ ๋„˜์–ด์„œ, ์›น๋ทฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์™„์„ฑ๋„์™€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฒฐ์ •์ง“๋Š” ์ค‘์š”ํ•œ ์š”์†Œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

'SEO > seo-tips' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[ ๊ฒ€์ƒ‰ ์ตœ์ ํ™” ํ•„์ˆ˜ ] ๋ธ”๋กœ๊ทธ์—์„œ โ€˜์ค‘๋ณต ์ฝ˜ํ…์ธ โ€™๋Š” ํ˜ธํ™˜๋งˆ๋งˆ๋ณด๋‹ค ๋ฌด์„ญ๋‹ค  (5) 2025.04.21
[ ๋ธ”๋กœ๊ทธ ์ˆ˜์ต ํ–ฅ์ƒ ๋ฐฉ๋ฒ• ] ๊ตฌ๊ธ€์˜ โ€˜๋ ˆ์ผ ๊ด‘๊ณ โ€™, ์™œ ํ‹ฐ์Šคํ† ๋ฆฌ์—์„  ์ •์ฑ… ์œ„๋ฐ˜์ผ๊นŒ?  (1) 2025.04.21
ํ‹ฐ์Šคํ† ๋ฆฌ ๋ธ”๋กœ๊ทธ์— ๋„ค์ด๋ฒ„ ์• ๋“œํฌ์ŠคํŠธ ๊ด‘๊ณ  ์Šน์ธ๋ฐ›๋Š” ๋ฐฉ๋ฒ• ๊ณต๊ฐœ?  (5) 2025.04.20
AI๋กœ ๋ธ”๋กœ๊ทธ ๊ธ€ ์ œ์ž‘์‹œ, ๋ชจ๋ฅด๋ฉด ๋ฌด์กฐ๊ฑด ์†ํ•ด ๋ณด๋Š” '๊ผญ' ์•Œ์•„์•ผ ํ•  ๊ฟ€ํŒ ๊ณต๊ฐœ!  (2) 2025.04.11
[ ๊ตฌ๊ธ€ ์ƒ‰์ธ ๋ฌธ์ œ ] ๊ตฌ๊ธ€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์—…๋ฐ์ดํŠธ์™€ ์ƒ‰์ธ ๋ฌธ์ œ ํ•ด๊ฒฐ ์ „๋žต  (1) 2025.04.09
[์ด๋ชจํ‹ฐ์ฝ˜ ๋ชจ์Œ] ์ด๋ชจํ‹ฐ์ฝ˜์œผ๋กœ ๋ธ”๋กœ๊ทธ ์ฝ˜ํ…์ธ ๋ฅผ ๋” ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ  (2) 2025.03.26
๋ธ”๋กœ๊ทธ ์ตœ์ ํ™” ๋ฐฉ๋ฒ• ๊ธฐ๋ณธ SEO ํ†ต๊ณ„ ์ˆ˜์น˜๋กœ ํŠธ๋ž˜ํ”ฝ๊ณผ ์ „ํ™˜์œจ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ  (1) 2025.03.06