QML 모듈 작성
CMake QML 모듈 API를 사용하여 QML 모듈을 선언해야 합니다:
- qmldir 및 *.qmltypes 파일을 생성합니다.
- QML_ELEMENT 로 주석이 달린 C++ 유형을 등록합니다.
- QML 파일과 C++ 기반 유형을 동일한 모듈에 결합합니다.
- 모든 QML 파일에 대해 qmlcachegen을 호출합니다.
- 모듈 내에서 미리 컴파일된 버전의 QML 파일을 사용합니다.
- 실제 파일 시스템과 리소스 파일 시스템 모두에 모듈을 제공하세요.
- 백업 라이브러리와 선택적 플러그인을 생성합니다. 런타임에 플러그인이 로드되지 않도록 백킹 라이브러리를 애플리케이션에 연결합니다.
위의 모든 작업은 개별적으로 구성할 수도 있습니다. 자세한 내용은 CMake QML 모듈 API를 참조하세요.
하나의 바이너리에 여러 QML 모듈
여러 QML 모듈을 동일한 바이너리에 추가할 수 있습니다. 각 모듈에 대한 CMake 대상을 정의한 다음 대상을 실행 파일에 연결합니다. 추가 타깃이 모두 정적 라이브러리인 경우 결과는 여러 QML 모듈을 포함하는 하나의 바이너리가 됩니다. 간단히 말해 이와 같은 애플리케이션을 만들 수 있습니다:
myProject | - CMakeLists.txt | - main.cpp | - main.qml | - onething.h | - onething.cpp | - ExtraModule | - CMakeLists.txt | - Extra.qml | - extrathing.h | - extrathing.cpp
먼저 main.qml에 Extra.qml의 인스턴스화가 포함되어 있다고 가정해 보겠습니다:
import ExtraModule Extra { ... }
추가 모듈은 정적 라이브러리여야 메인 프로그램에 연결할 수 있습니다. 따라서 ExtraModule/CMakeLists.txt에 이를 명시하세요:
# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause qt_add_library(extra_module STATIC) qt_add_qml_module(extra_module URI "ExtraModule" VERSION 1.0 QML_FILES Extra.qml SOURCES extrathing.cpp extrathing.h RESOURCE_PREFIX / )
이렇게 하면 두 개의 타겟이 생성됩니다: extra_module
백업 라이브러리용, extra_moduleplugin
플러그인용. 정적 라이브러리이기 때문에 플러그인은 런타임에 로드할 수 없습니다.
myProject/CMakeLists.txt에서 main.qml과 onething.h에 선언된 모든 유형이 속해 있는 QML 모듈을 지정해야 합니다:
qt_add_executable(main_program main.cpp) qt_add_qml_module(main_program VERSION 1.0 URI myProject QML_FILES main.qml SOURCES onething.cpp onething.h )
거기에서 추가 모듈의 하위 디렉터리를 추가합니다:
add_subdirectory(ExtraModule)
추가 모듈 연결이 올바르게 작동하도록 하려면 다음과 같이 해야 합니다:
- 추가 모듈에 심볼을 정의합니다.
- 기본 프로그램에서 심볼에 대한 참조를 만듭니다.
QML 플러그인에는 이 용도로 사용할 수 있는 심볼이 포함되어 있습니다. Q_IMPORT_QML_PLUGIN 매크로를 사용하여 이 심볼에 대한 참조를 만들 수 있습니다. main.cpp에 다음 코드를 추가합니다:
#include <QtQml/QQmlExtensionPlugin> Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
ExtraModulePlugin
는 생성된 플러그인 클래스의 이름입니다. Plugin
가 추가된 모듈 URI로 구성됩니다. 그런 다음 메인 프로그램의 CMakeLists.txt에서 백킹 라이브러리가 아닌 플러그인을 메인 프로그램에 링크합니다:
target_link_libraries(main_program PRIVATE extra_moduleplugin)
버전
QML에는 컴포넌트와 모듈에 버전을 할당하는 복잡한 시스템이 있습니다. 대부분의 경우 이 모든 것을 무시해야 합니다:
- 임포트 문에 버전을 추가하지 않기
- qt_add_qml_module에 버전을 지정하지 않기
- QML_ADDED_IN_VERSION 또는 QT_QML_SOURCE_VERSIONS 사용 안 함
- Q_REVISION 또는
REVISION()
속성을 사용하지 않음 Q_PROPERTY - 자격이 없는 액세스 피하기
- 가져오기 네임스페이스 넉넉하게 사용
버전 관리는 언어 자체 외부에서 처리하는 것이 이상적입니다. 예를 들어 서로 다른 QML 모듈 세트에 대해 별도의 가져오기 경로를 유지할 수 있습니다. 또는 운영 체제에서 제공하는 버전 관리 메커니즘을 사용하여 QML 모듈이 포함된 패키지를 설치하거나 제거할 수 있습니다.
경우에 따라 가져오는 버전에 따라 Qt 자체의 QML 모듈이 다른 동작을 보일 수 있습니다. 특히 프로퍼티가 QML 컴포넌트에 추가되었는데 코드에 같은 이름의 다른 프로퍼티에 대한 자격 없는 액세스가 포함된 경우 코드가 중단됩니다. 다음 예제에서는 topLeftRadius 프로퍼티가 Qt 6.7에 추가되었기 때문에 Qt 버전에 따라 코드가 다르게 동작합니다:
import QtQuick Item { // property you want to use property real topLeftRadius: 24 Rectangle { // correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7 objectName: "top left radius:" + topLeftRadius } }
여기서 해결책은 자격이 없는 액세스를 피하는 것입니다. 이러한 문제를 찾기 위해 qmllint를 사용할 수 있습니다. 다음 예제는 실제로 의미하는 프로퍼티에 안전하고 적격화된 방식으로 접근합니다:
import QtQuick Item { id: root // property you want to use property real topLeftRadius: 24 Rectangle { // never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius objectName: "top left radius:" + root.topLeftRadius } }
QtQuick 의 특정 버전을 가져와서 비호환성을 피할 수도 있습니다:
// make sure Rectangle has no topLeftRadius property import QtQuick 6.6 Item { property real topLeftRadius: 24 Rectangle { objectName: "top left radius:" + topLeftRadius } }
버전 관리로 해결되는 또 다른 문제는 서로 다른 모듈에서 가져온 QML 컴포넌트가 서로 섀도잉할 수 있다는 사실입니다. 다음 예에서 MyModule
에서 Rectangle
이라는 컴포넌트를 최신 버전에 도입하는 경우, 이 문서에서 만든 Rectangle
은 더 이상 QQuickRectangle
이 아니라 MyModule
에서 도입한 새로운 Rectangle
이 됩니다.
import QtQuick import MyModule Rectangle { // MyModule's Rectangle, not QtQuick's }
섀도잉을 피하는 좋은 방법은 다음과 같이 QtQuick
및/또는 MyModule
을 타입 네임스페이스로 가져오는 것입니다:
import QtQuick as QQ import MyModule as MM QQ.Rectangle { // QtQuick's Rectangle }
또는 고정 버전으로 MyModule
를 가져오고 새 컴포넌트가 QML_ADDED_IN_VERSION 또는 QT_QML_SOURCE_VERSIONS를 통해 올바른 버전 태그를 받는 경우에도 섀도잉을 피할 수 있습니다:
import QtQuick 6.6 // Types introduced after 1.0 are not available, like Rectangle for example import MyModule 1.0 Rectangle { // QtQuick's Rectangle }
이 기능을 사용하려면 MyModule
에서 버전을 사용해야 합니다. 몇 가지 주의해야 할 사항이 있습니다.
버전을 추가하는 경우 모든 곳에 추가하세요.
qt_add_qml_module에 VERSION
어트리뷰트를 추가해야 합니다. 버전은 모듈에서 제공하는 가장 최신 버전이어야 합니다. 동일한 메이저 버전의 이전 마이너 버전은 자동으로 등록됩니다. 이전 메이저 버전은 아래를 참조하세요.
모듈의 x.0
버전에 도입되지 않은 모든 유형에 QML_ADDED_IN_VERSION 또는 QT_QML_SOURCE_VERSIONS를 추가해야 하며, 여기서 x
은 현재 메이저 버전입니다.
버전 태그를 추가하는 것을 잊어버리면 모든 버전에서 컴포넌트를 사용할 수 있으므로 버전 관리가 비효율적입니다.
그러나 QML에 정의된 속성, 메서드 및 신호에 버전을 추가할 수 있는 방법은 없습니다. QML 문서에 버전을 추가하는 유일한 방법은 각 변경 사항에 대해 별도의 QT_QML_SOURCE_VERSIONS를 사용하여 새 문서를 추가하는 것입니다.
버전은 전이적이지 않습니다.
A
모듈의 컴포넌트가 다른 모듈 B
을 임포트하고 해당 모듈의 유형을 루트 요소로 인스턴스화하는 경우, 사용자가 A
의 어떤 버전을 임포트하든 상관없이 B
의 임포트 버전이 결과 컴포넌트에서 사용할 수 있는 속성과 관련이 있습니다.
A
모듈의 버전이 2.6
인 파일 TypeFromA.qml
을 예로 들어 보겠습니다:
import B 2.7 // Exposes TypeFromB 2.7, no matter what version of A is imported TypeFromB { }
이제 TypeFromA
의 사용자를 생각해 봅시다:
import A 2.6 // This is TypeFromB 2.7. TypeFromA { }
사용자는 버전 2.6
을 보길 원하지만 실제로는 기본 클래스 TypeFromB
의 버전 2.7
을 가져옵니다.
따라서 안전을 위해 프로퍼티를 직접 추가할 때뿐만 아니라 가져온 모듈의 버전을 변경할 때도 QML 파일을 복제하고 새 버전을 부여해야 합니다.
적격 액세스는 버전 관리를 적용하지 않음
버전 관리는 유형 또는 유형 자체의 멤버에 대한 자격이 없는 액세스에만 영향을 줍니다. topLeftRadius
의 예제에서 this.topLeftRadius
을 작성하면 Qt 6.7을 사용하는 경우 import QtQuick 6.6
을 사용하더라도 프로퍼티가 해결됩니다.
버전 및 개정
QML_ADDED_IN_VERSION 과 Q_REVISION 과 Q_PROPERTY 의 두 인수 변형인 REVISION()
을 사용하면 QMetaMethod::revision 과 QMetaProperty::revision 에 노출된 것처럼 metaobject's 개정판과 긴밀하게 결합된 버전만 선언할 수 있습니다. 즉, 유형 계층 구조의 모든 유형은 동일한 버전 관리 체계를 따라야 합니다. 여기에는 Qt 자체에서 제공하는 모든 타입을 상속하는 것도 포함됩니다.
qmlRegisterType 및 관련 함수를 사용하면 메타 객체 버전과 유형 버전 간의 매핑을 등록할 수 있습니다. 그런 다음 Q_REVISION 의 단일 인수 형식과 Q_PROPERTY 의 REVISION
어트리뷰트를 사용해야 합니다. 그러나 이는 다소 복잡하고 혼란스러울 수 있으므로 권장하지 않습니다.
동일한 모듈에서 여러 주요 버전 내보내기
개별 유형이 QT_QML_SOURCE_VERSIONS 또는 Q_REVISION 를 통해 추가된 특정 버전에서 다른 버전을 선언하더라도 기본적으로qt_add_qml_module은 VERSION 인수에 지정된 주요 버전을 고려합니다. 모듈이 두 개 이상의 버전에서 사용 가능한 경우 개별 QML 파일을 어떤 버전에서 사용할 수 있는지도 결정해야 합니다. 추가 주요 버전을 선언하려면 qt_add_qml_module
에 PAST_MAJOR_VERSIONS
옵션을 사용하거나 개별 QML 파일에 QT_QML_SOURCE_VERSIONS
속성을 사용할 수 있습니다.
set_source_files_properties(Thing.qml PROPERTIES QT_QML_SOURCE_VERSIONS "1.4;2.0;3.0" ) set_source_files_properties(OtherThing.qml PROPERTIES QT_QML_SOURCE_VERSIONS "2.2;3.0" ) qt_add_qml_module(my_module URI MyModule VERSION 3.2 PAST_MAJOR_VERSIONS 1 2 QML_FILES Thing.qml OtherThing.qml OneMoreThing.qml SOURCES everything.cpp everything.h )
MyModule
는 주요 버전 1, 2 및 3에서 사용할 수 있습니다. 사용 가능한 최대 버전은 3.2입니다. 양수 x가 있는 버전 1.x 또는 2.x는 모두 가져올 수 있습니다. Thing.qml 및 OtherThing.qml의 경우 명시적인 버전 정보를 추가했습니다. Thing.qml은 버전 1.4부터, OtherThing.qml은 버전 2.2부터 사용할 수 있습니다. 주 버전을 범프할 때 모듈에서 QML 파일을 제거할 수 있으므로 각 set_source_files_properties()
에도 이후 버전을 지정해야 합니다. OneMoreThing.qml에 대한 명시적인 버전 정보는 없습니다. 즉, 마이너 버전 0부터 모든 메이저 버전에서 OneMoreThing.qml을 사용할 수 있습니다.
이 설정을 사용하면 생성된 등록 코드가 각 주요 버전에 대해 qmlRegisterModule()를 사용하여 versions
모듈을 등록합니다. 이렇게 하면 모든 버전을 가져올 수 있습니다.
사용자 지정 디렉토리 레이아웃
QML 모듈을 구조화하는 가장 쉬운 방법은 URI로 명명된 디렉터리에 모듈을 보관하는 것입니다. 예를 들어 My.Extra.Module 모듈은 이 모듈을 사용하는 애플리케이션과 관련된 My/Extra/Module 디렉터리에 저장됩니다. 이렇게 하면 런타임과 모든 도구에서 쉽게 찾을 수 있습니다.
더 복잡한 프로젝트에서는 이 규칙이 너무 제한적일 수 있습니다. 예를 들어 프로젝트의 루트 디렉터리를 오염시키지 않기 위해 모든 QML 모듈을 한 곳에 그룹화하고 싶을 수 있습니다. 또는 여러 애플리케이션에서 단일 모듈을 재사용하고 싶을 수도 있습니다. 이러한 경우 RESOURCE_PREFIX
및 IMPORT_PATH와 함께 QT_QML_OUTPUT_DIRECTORY
을 사용할 수 있습니다.
QML 모듈을 특정 출력 디렉터리(예: 빌드 디렉터리 QT_QML_OUTPUT_DIRECTORY의 하위 디렉터리 "qml")로 수집하려면 최상위 수준 CMakeLists.txt에서 다음을 설정합니다:
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
QML 모듈의 출력 디렉터리가 새 위치로 이동합니다. 마찬가지로 qmllint
및 qmlcachegen
호출은 새 출력 디렉터리를 가져오기 경로로 사용하도록 자동으로 조정됩니다. 새 출력 디렉터리는 기본 QML 가져오기 경로의 일부가 아니므로 런타임에 명시적으로 추가해야 QML 모듈을 찾을 수 있습니다.
이제 물리적 파일 시스템이 처리되었으므로 QML 모듈을 리소스 파일 시스템의 다른 위치로 이동하고 싶을 수 있습니다. 이것이 바로 RESOURCE_PREFIX 옵션의 용도입니다. 각 qt_add_qml_module에서 별도로 지정해야 합니다. 그러면 QML 모듈이 지정된 접두사 아래에 배치되고 URI에서 생성된 대상 경로가 추가됩니다. 예를 들어 다음 모듈을 생각해 보세요:
qt_add_qml_module( URI My.Great.Module VERSION 1.0 RESOURCE_PREFIX /example.com/qml QML_FILES A.qml B.qml )
이렇게 하면 리소스 파일 시스템에 example.com/qml/My/Great/Module
디렉터리가 추가되고 위에 정의된 QML 모듈이 그 안에 배치됩니다. 모듈은 여전히 실제 파일 시스템에서 찾을 수 있으므로 QML 가져오기 경로에 resource 접두사를 반드시 추가할 필요는 없습니다. 하지만 대부분의 모듈은 리소스 파일 시스템에서 로드하는 것이 실제 파일 시스템에서 로드하는 것보다 빠르기 때문에 일반적으로 QML 가져오기 경로에 리소스 접두사를 추가하는 것이 좋습니다.
여러 개의 가져오기 경로가 있는 대규모 프로젝트에서 QML 모듈을 사용하려는 경우에는 추가 단계를 수행해야 합니다: 런타임에 가져오기 경로를 추가하더라도 qmllint
같은 도구는 이 경로에 액세스할 수 없으므로 올바른 종속성을 찾지 못할 수 있습니다. IMPORT_PATH
을 사용하여 고려해야 할 추가 경로에 대해 툴링에 알려주세요. 예를 들어
qt_add_qml_module( URI My.Dependent.Module VERSION 1.0 QML_FILES C.qml IMPORT_PATH "/some/where/else" )
런타임 파일 시스템 액세스 제거
모든 QML 모듈이 항상 리소스 파일 시스템에서 로드되는 경우 애플리케이션을 단일 바이너리로 배포할 수 있습니다.
QTP0001 정책이 NEW
로 설정된 경우 qt_add_qml_module()
의 RESOURCE_PREFIX
인수는 기본적으로 /qt/qml/
이므로 모듈은 리소스 파일 시스템의 :/qt/qml/
에 배치됩니다. 이것은 기본 QML 임포트 경로의 일부이지만 Qt 자체에서는 사용되지 않습니다. 애플리케이션 내에서 모듈을 사용하려면 이 위치가 적합합니다.
대신 사용자 지정 RESOURCE_PREFIX
을 지정한 경우 QML 가져오기 경로에 사용자 지정 리소스 접두사를 추가해야 합니다. 리소스 접두사를 여러 개 추가할 수도 있습니다:
QQmlEngine qmlEngine; qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix")); qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix")); // Use qmlEngine to load the main.qml file.
이는 모듈 이름 충돌을 피하기 위해 타사 라이브러리를 사용할 때 필요할 수 있습니다. 그 외의 모든 경우에는 사용자 지정 리소스 접두사를 사용하지 않는 것이 좋습니다.
:/qt-project.org/imports/
경로도 기본 QML 가져오기 경로의 일부입니다. 다른 프로젝트나 Qt 버전에서 많이 재사용되는 모듈의 경우 리소스 접두사로 :/qt-project.org/imports/
을 사용할 수 있습니다. 하지만 Qt의 자체 QML 모듈은 여기에 배치됩니다. 덮어쓰지 않도록 주의해야 합니다.
불필요한 가져오기 경로를 추가하지 마세요. 그러면 QML 엔진이 잘못된 위치에서 모듈을 찾을 수 있습니다. 이로 인해 특정 환경에서만 재현될 수 있는 문제가 발생할 수 있습니다.
사용자 정의 QML 플러그인 통합
QML 모듈에 image provider 을 번들로 제공하는 경우 QQmlEngineExtensionPlugin::initializeEngine() 메서드를 구현해야 합니다. 이렇게 하면 자체 플러그인을 작성해야 합니다. 이 사용 사례를 지원하기 위해 NO_GENERATE_PLUGIN_SOURCE를 사용할 수 있습니다.
자체 플러그인 소스를 제공하는 모듈을 고려해 보겠습니다:
qt_add_qml_module(imageproviderplugin VERSION 1.0 URI "ImageProvider" PLUGIN_TARGET imageproviderplugin NO_PLUGIN_OPTIONAL NO_GENERATE_PLUGIN_SOURCE CLASS_NAME ImageProviderExtensionPlugin QML_FILES AAA.qml BBB.qml SOURCES moretypes.cpp moretypes.h myimageprovider.cpp myimageprovider.h plugin.cpp )
내이미지프로바이더.h에서 다음과 같이 이미지 공급자를 선언할 수 있습니다:
class MyImageProvider : public QQuickImageProvider { [...] };
그런 다음 plugin.cpp에서 QQmlEngineExtensionPlugin 을 정의할 수 있습니다:
#include <myimageprovider.h> #include <QtQml/qqmlextensionplugin.h> class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) public: void initializeEngine(QQmlEngine *engine, const char *uri) final { Q_UNUSED(uri); engine->addImageProvider("myimg", new MyImageProvider); } };
이렇게 하면 이미지 공급자를 사용할 수 있습니다. 플러그인과 백킹 라이브러리는 모두 동일한 CMake 대상 imageproviderplugin에 있습니다. 이는 다양한 시나리오에서 링커가 모듈의 일부를 삭제하지 않도록 하기 위한 것입니다.
플러그인이 이미지 제공자를 생성하므로 더 이상 사소한 initializeEngine
함수가 없습니다. 따라서 플러그인은 더 이상 선택 사항이 아닙니다.
© 2025 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.