QML의 싱글톤
QML에서 싱글톤은 인스턴스 생성( engine) 시 최대 한 번만 생성되는 객체입니다. 이 가이드에서는 싱글톤을 생성하고 사용하는 방법을 설명합니다. 또한 싱글톤을 다룰 때의 모범 사례도 소개합니다.
QML에서 싱글톤은 어떻게 생성할 수 있나요?
QML에서 싱글톤을 생성하는 방법에는 두 가지가 있습니다. QML 파일에서 싱글톤을 정의하거나, C++에서 등록할 수 있습니다.
QML에서 싱글톤 정의하기
QML에서 싱글턴을 정의하려면 먼저
pragma Singleton를 파일 상단에 추가해야 합니다. 한 가지 더 필요한 단계가 있습니다. QML 모듈의 qmldir 파일에 항목을 추가해야 합니다.
qt_add_qml_module 사용 (CMake)
CMake를 사용할 경우, qmldir은 qt_add_qml_module에 의해 자동으로 생성됩니다. 해당 QML 파일을 싱글톤으로 처리해야 함을 지정하려면, 해당 파일에 대한 QT_QML_SINGLETON_TYPE 파일 속성을 설정해야 합니다:
set_source_files_properties(MySingleton.qml
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)set_source_files_properties 에 여러 파일을 한 번에 전달할 수 있습니다:
set(plain_qml_files
MyItem1.qml
MyItem2.qml
FancyButton.qml
)
set(qml_singletons
MySingleton.qml
MyOtherSingleton.qml
)
set_source_files_properties(${qml_singletons}
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
qt_add_qml_module(myapp
URI MyModule
QML_FILES ${plain_qml_files} ${qml_singletons}
)참고: set_source_files_properties는 다음 단계 전에 호출되어야 합니다 qt_add_qml_module
qt_add_qml_module을 사용하지 않는 경우
qt_add_qml_module 를 사용하지 않는 경우, qmldir 파일을 수동으로 생성해야 합니다. 해당 파일에서 싱글톤을 적절히 표시해야 합니다:
module MyModule
singleton MySingleton 1.0 MySingleton.qml
singleton MyOtherSingleton 1.0 MyOtherSingleton.qml자세한 내용은 객체 유형 선언을 참조하십시오.
C++에서 싱글톤 정의하기
C++에서 QML로 싱글톤을 노출하는 방법에는 여러 가지가 있습니다. 주요 차이점은 QML 엔진에서 필요할 때 클래스의 새 인스턴스를 생성해야 하는지, 아니면 기존 객체를 QML 프로그램에 노출해야 하는지에 따라 달라집니다.
싱글톤을 제공하기 위해 클래스 등록하기
싱글톤을 정의하는 가장 간단한 방법은 ` QObject `을 상속받고 기본 생성자가 있는 클래스를 만들고, 이를 ` QML_SINGLETON ` 및 ` QML_ELEMENT ` 매크로로 표시하는 것입니다.
class MySingleton : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_ELEMENT
public:
MySingleton(QObject *parent = nullptr) : QObject(parent) {
// ...
}
};이렇게 하면 해당 파일이 속한 QML 모듈에 MySingleton 클래스가 MySingleton 라는 이름으로 등록됩니다. 다른 이름으로 노출하고 싶다면 대신 QML_NAMED_ELEMENT 을 사용할 수 있습니다.
클래스를 기본 생성 가능하게 만들 수 없거나, 싱글톤이 인스턴스화되는 ` QQmlEngine `에 접근해야 하는 경우, 대신 정적 `create` 함수를 사용할 수 있습니다. 이 함수의 시그니처는 ` MySingleton *create(QQmlEngine *, QJSEngine *)`이어야 하며, 여기서 ` MySingleton `는 등록될 클래스의 유형입니다.
class MyNonDefaultConstructibleSingleton : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_NAMED_ELEMENT(MySingleton)
public:
MyNonDefaultConstructibleSingleton(QJSValue id, QObject *parent = nullptr)
: QObject(parent)
, m_symbol(std::move(id))
{}
static MyNonDefaultConstructibleSingleton *create(QQmlEngine *qmlEngine, QJSEngine *)
{
return new MyNonDefaultConstructibleSingleton(qmlEngine->newSymbol(u"MySingleton"_s));
}
private:
QJSValue m_symbol;
};참고: create함수는 QJSEngine 와 QQmlEngine 매개변수를 모두 받습니다. 이는 역사적인 이유 때문입니다. 두 매개변수는 모두 실제로 QQmlEngine 인 동일한 객체를 가리킵니다.
C++에서 인스턴스화된 객체를 싱글톤으로 노출하기
QQmlEngine 가 인스턴스를 생성하도록 두는 대신, C++에서 싱글톤의 인스턴스화를 직접 제어하는 것이 유용할 수 있습니다. 이러한 싱글톤은 엔진에 의해 인스턴스화될 필요가 없으므로, 기본 생성자나 정적 create 함수를 생략할 수 있습니다. 이 경우, 싱글톤 선언에 QML_UNCREATABLE 매크로를 포함해야 합니다:
class MyNonDefaultConstructibleSingleton : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_NAMED_ELEMENT(MySingleton)
QML_UNCREATABLE("Provided by C++")
public:
MyNonDefaultConstructibleSingleton(BackendObject* backend, QObject *parent = nullptr)
: QObject(parent)
, m_backend(backend)
{}
private:
class BackendObject* backend;
};그런 다음, 엔진을 시작하기 전에 사용할 인스턴스를 설정합니다:
MyNonDefaultConstructibleSingleton singleton(backend);
QQmlApplicationEngine engine;
engine.setExternalSingletonInstance("MyModule", "MySingleton", &singleton);
engine.loadFromModule("MyModule", "Main");기존 객체를 싱글톤으로 노출하기
때로는 타사 API를 통해 생성되었을 수 있는 기존 객체가 있는 경우가 있습니다. 이 경우 올바른 선택은 단일 싱글톤을 생성하여 해당 객체들을 그 속성으로 노출하는 것입니다( ‘관련 데이터 그룹화’ 참조). 하지만, 예를 들어 노출해야 할 객체가 단 하나뿐인 경우처럼 해당 방법이 적합하지 않다면, 다음 접근 방식을 사용하여 ` MySingleton ` 유형의 인스턴스를 엔진에 노출하십시오. 먼저 싱글턴을 ` foreign type`로 노출합니다:
struct SingletonForeign
{
Q_GADGET
QML_FOREIGN(MySingleton)
QML_SINGLETON
QML_NAMED_ELEMENT(MySingleton)
QML_UNCREATABLE("Provided from C++")
};그런 다음 MySingleton을 인스턴스화하여 엔진에 설정하면 됩니다:
MySingleton instance = getSingletonInstance();
QQmlApplicationEngine engine;
engine.setExternalSingletonInstance("MyModule", "MySingleton", &instance);
engine.loadFromModule("MyModule", "Main");참고: 이 경우 단순히 ` qmlRegisterSingletonInstance `을 사용하고 싶은 유혹이 매우 클 수있습니다 . 그러나 다음 섹션에 나열된 명령형 타입 등록의 함정에 주의하십시오.
명령형 타입 등록
Qt 5.15 이전에는 싱글톤을 포함한 모든 타입이 ` qmlRegisterType ` API를 통해 등록되었습니다. 특히 싱글톤은 qmlRegisterSingletonType 또는 qmlRegisterSingletonInstance 중 하나를 통해 등록되었습니다. 각 타입마다 모듈 이름을 반복해야 하는 사소한 불편함과 클래스 선언과 등록이 강제로 분리된다는 점 외에도, 이 접근 방식의 가장 큰 문제는 툴링 친화적이지 않다는 것이었습니다: 컴파일 시점에 모듈의 타입에 대한 모든 필수 정보를 정적으로 추출하는 것이 불가능했습니다. 선언적 등록 방식은 이 문제를 해결했습니다.
참고: 명령형 qmlRegisterType API에 대한 사용 사례가 하나 남아 있습니다 . 이는 비-QObject 유형의 싱글톤을 var 속성으로 노출하는 방법입니다. the QJSValue based qmlRegisterSingletonType overload . 대신 다음과 같은 대안을 권장합니다. 해당 값을 (QObject) 기반 싱글톤의 속성으로 노출하여 타입 정보를 확보할 수 있도록 하십시오.
싱글톤에 접근하기
싱글톤은 QML과 C++ 모두에서 접근할 수 있습니다. QML에서는 해당 싱글톤을 포함하는 모듈을 임포트해야 합니다. 그 후, 이름을 통해 싱글톤에 접근할 수 있습니다. JavaScript 컨텍스트 내에서 싱글톤의 속성을 읽거나 쓰기는 일반 객체와 동일한 방식으로 수행됩니다:
import QtQuick
import MyModule
Item {
x: MySingleton.posX
Component.onCompleted: MySingleton.ready = true;
}싱글톤의 속성에 바인딩을 설정하는 것은 불가능하지만, 필요한 경우 Binding 요소를 사용하여 동일한 결과를 얻을 수 있습니다:
import QtQuick
import MyModule
Item {
id: root
Binding {
target: MySingleton
property: "posX"
value: root.x
}
}참고: 싱글톤 속성에 바인딩을 설정할 때는주의가 필요합니다. 두 개 이상의 파일에서 바인딩을 설정할 경우, 결과는 정의되지 않습니다.
싱글톤 사용(또는 미사용) 지침
싱글톤을 사용하면 여러 곳에서 액세스해야 하는 데이터를 엔진에 노출할 수 있습니다. 여기에는 요소 간의 간격과 같은 전역적으로 공유되는 설정이나, 여러 곳에 표시되어야 하는 데이터 모델이 포함될 수 있습니다. 유사한 사용 사례를 해결할 수 있는 컨텍스트 속성과 비교할 때, 싱글톤은 타입이 지정되어 있다는 장점이 있으며, QML Language Server와 같은 툴링의 지원을 받는다는 장점이 있으며, 일반적으로 런타임 성능도 더 빠릅니다.
모듈에 싱글턴을 너무 많이 등록하지 않는 것이 좋습니다. 싱글턴은 일단 생성되면 엔진 자체가 소멸될 때까지 유지되며, 전역 상태의 일부이므로 공유 상태라는 단점이 따릅니다. 따라서 애플리케이션 내 싱글턴의 수를 줄이기 위해 다음 기법을 고려해 보십시오:
관련 데이터를 그룹화하기
노출하려는 객체마다 싱글톤을 하나씩 추가하면 상당한 양의 상용구 코드가 발생합니다. 대부분의 경우, 노출하려는 데이터를 하나의 싱글톤 속성으로 묶어두는 것이 더 합리적입니다. 예를 들어, 로컬 도서용 하나와 원격 소스용 두 개, 총 세 개의 ` abstract item models`를 노출해야 하는 전자책 리더를 만들려고 한다고 가정해 봅시다. 기존 객체를 노출하기 위한 과정을 세 번 반복하는 대신, 하나의 싱글톤을 생성하여 메인 애플리케이션을 시작하기 전에 설정할 수 있습니다:
class GlobalState : QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QAbstractItemModel* localBooks MEMBER localBooks)
Q_PROPERTY(QAbstractItemModel* digitalStoreFront MEMBER digitalStoreFront)
Q_PROPERTY(QAbstractItemModel* publicLibrary MEMBER publicLibrary)
public:
QAbstractItemModel* localBooks;
QAbstractItemModel* digitalStoreFront;
QAbstractItemModel* publicLibrary
};
int main() {
QQmlApplicationEngine engine;
auto globalState = engine.singletonInstance<GlobalState *>("MyModule", "GlobalState");
globalState->localBooks = getLocalBooks();
globalState->digitalStoreFront = setupLoalStoreFront();
globalState->publicLibrary = accessPublicLibrary();
engine.loadFromModule("MyModule", "Main");
}객체 인스턴스 사용
지난 섹션에서는 세 개의 모델을 싱글톤의 멤버로 노출하는 예제를 살펴보았습니다. 이는 모델을 여러 곳에서 사용해야 하거나, 우리가 제어할 수 없는 외부 API를 통해 모델이 제공되는 경우에 유용할 수 있습니다. 하지만 모델을 한 곳에서만 사용해야 한다면, 인스턴스화 가능한 유형으로 만드는 것이 더 합리적일 수 있습니다. 앞서 예제를 다시 살펴보면, 인스턴스화 가능한 RemoteBookModel 클래스를 추가한 다음, 책 브라우저 QML 파일 내에서 이를 인스턴스화할 수 있습니다:
// remotebookmodel.h
class RemoteBookModel : public QAbstractItemModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
// ...
};// bookbrowser.qml
Row {
ListView {
model: RemoteBookModel { url: "www.public-lib.example"}
}
ListView {
model: RemoteBookModel { url: "www.store-front.example"}
}
}초기 상태 전달
싱글톤을 사용하여 QML에 상태를 전달할 수는 있지만, 해당 상태가 애플리케이션의 초기 설정에만 필요한 경우에는 자원이 낭비됩니다. 이 경우, 종종 ` QQmlApplicationEngine::setInitialProperties`을 사용할 수 있습니다. 예를 들어, 해당 명령줄 플래그가 설정된 경우 ` Window::visibility `을 전체 화면으로 설정하고 싶을 수 있습니다:
QQmlApplicationEngine engine;
if (parser.isSet(fullScreenOption)) {
// assumes root item is ApplicationWindow
engine.setInitialProperties(
{ "visibility", QVariant::fromValue(QWindow::FullScreen)}
);
}
engine.loadFromModule("MyModule, "Main");
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.