이 페이지에서

QML 타입 컴파일러

Qt QML Compiler( qmltc)는 Qt에 포함된 도구로, QML 타입을 사용자 코드의 일부로 사전 컴파일되는 C++ 타입으로 변환합니다. qmltc를 사용하면 QQmlComponent 기반의 객체 생성 방식에 비해 컴파일러가 활용할 수 있는 최적화 기회가 더 많아져 런타임 성능이 향상될 수 있습니다. qmltc는 Qt Quick Compiler 툴체인의 일부입니다.

설계상 qmltc는 사용자 측 코드를 출력합니다. 이 코드는 C++ 애플리케이션에서 직접 활용되어야 하며, 그렇지 않으면 어떠한 이점도 얻을 수 없습니다. 이렇게 생성된 코드는 QML 문서에서 객체를 생성하기 위해 QQmlComponent 및 관련 API를 본질적으로 대체합니다. 자세한 내용은 ‘QML 애플리케이션에서 qmltc 사용하기’ 및 ‘생성된 출력의 기본 사항’에서 확인할 수 있습니다.

qmltc를 사용하려면:

  • 애플리케이션에 적합한 QML 모듈 을 생성합니다.
  • 예를 들어, CMake API를 통해 qmltc를 호출합니다.
  • #include 애플리케이션 소스 코드에 생성된 헤더 파일을 포함시킵니다.
  • 생성된 유형의 객체를 인스턴스화합니다.

이 워크플로우에서 qmltc는 일반적으로 빌드 과정에서 실행됩니다. 따라서 qmltc가 QML 문서를 거부할 경우(오류나 경고 때문이든, qmltc가 아직 지원하지 않는 구문 때문이든), 빌드 과정이 실패하게 됩니다. 이는 QML 모듈 생성 시 린팅 대상의 자동 생성을 활성화한 다음, qmllint를 실행하기 위해 해당 모듈을 "빌드"하려고 할 때 qmllint 오류가 발생하는 방식과 유사합니다.

경고: qmltc는 현재 기술 미리보기(Tech Preview) 단계에 있으므로 임의의 QML 프로그램을 컴파일하지 못할 수도 있습니다(자세한 내용은 ‘알려진 제한 사항’을 참조하십시오). qmltc가 실패하면, 애플리케이션이 qmltc 출력을 제대로 사용할 수 없기 때문에 아무것도 생성되지 않습니다. 프로그램에 오류(또는 해결할 수 없는 경고)가 포함되어 있다면, 컴파일을 가능하게 하려면 이를 수정해야 합니다. 일반적인 규칙은 모범 사례를 준수하고 qmllint의 조언을 따르는 것입니다.

참고: qmltc 는 생성된 C++ 코드가 과거 또는 미래 버전(패치 버전 포함) 간에 API, 소스 또는 바이너리 호환성을 유지할 것을 보장하지 않습니다. 또한, Qt의 Qml 모듈을 사용하는 qmltc로 컴파일된 애플리케이션은 비공개 Qt API에 대한 링크가 필요합니다. 이는 Qt의 Qml 모듈이 주로 QML을 통해 사용되기 때문에 일반적으로 공개 C++ API를 제공하지 않기 때문입니다.

QML 애플리케이션에서 qmltc 사용

빌드 시스템의 관점에서 볼 때, qmltc 컴파일을 추가하는 것은 qml 캐시 생성을 추가하는 것과 크게 다르지 않습니다. 간단히 말해, 빌드 프로세스는 다음과 같이 설명할 수 있습니다:

이 흐름도는 qml 타입 컴파일러를 사용하여 qml 및 C++ 파일을 컴파일하는 방법을 보여줍니다.

실제 컴파일 과정은 훨씬 더 복잡하지만, 이 다이어그램은 qmltc가 사용하는 핵심 구성 요소, 즉 QML 파일 자체와 qmltypes 정보가 포함된 qmldir을 잘 보여줍니다. 간단한 애플리케이션의 경우 일반적으로 다소 기초적인 qmldir을 사용하지만, 일반적으로 qmldir은 qmltc가 올바른 QML-to-C++ 변환을 수행하는 데 의존하는 필수적이고 체계적으로 정리된 타입 정보를 제공하는 복잡한 구조를 가질 수 있습니다.

그럼에도 불구하고, qmltc의 경우 빌드 단계를 하나 추가하는 것만으로는 충분하지 않습니다. 애플리케이션 코드도 QQmlComponent 이나 그보다 상위 수준의 대안 대신 qmltc가 생성한 클래스를 사용하도록 수정되어야 합니다.

qmltc를 사용하여 QML 코드 컴파일하기

Qt 6부터 Qt는 CMake를 사용하여 다양한 구성 요소를 빌드합니다. 사용자 프로젝트에서도 Qt를 사용하여 구성 요소를 빌드할 때 CMake를 사용할 수 있으며, 이를 권장합니다. 프로젝트에 바로 사용 가능한 qmltc 컴파일 지원을 추가하려면, 이 빌드 흐름이 적절한 QML 모듈과 그 인프라를 중심으로 이루어지기 때문에 CMake 기반의 빌드 흐름이 필요합니다.

qmltc 컴파일을 추가하는 가장 쉬운 방법은 애플리케이션을 위한 QML 모듈 생성 과정의 일부로 전용 CMake API를 사용하는 것입니다. 간단한 애플리케이션 디렉터리 구조를 예로 들어 보겠습니다:

.
├── CMakeLists.txt
├── myspecialtype.h     // C++ type exposed to QML
├── myspecialtype.cpp
├── myApp.qml           // main QML page
├── MyButton.qml        // custom UI button
├── MySlider.qml        // custom UI slider
└── main.cpp            // main C++ application file

이 경우 CMake 코드는 대개 다음과 같이 작성됩니다:

# Use "my_qmltc_example" as an application name:
set(application_name my_qmltc_example)

# Create a CMake target, add C++ source files, link libraries, etc...

# Specify a list of QML files to be compiled:
set(application_qml_files
    myApp.qml
    MyButton.qml
    MySlider.qml
)

# Make the application into a proper QML module:
qt6_add_qml_module(${application_name}
    URI QmltcExample
    QML_FILES ${application_qml_files}

    # Compile qml files (listed in QML_FILES) to C++ using qmltc and add these
    # files to the application binary:
    ENABLE_TYPE_COMPILER
    NO_GENERATE_EXTRA_QMLDIRS
)

# (qmltc-specific) Link *private* libraries that correspond to QML modules:
find_package(Qt6 COMPONENTS QmlPrivate QuickPrivate)
target_link_libraries(${application_name} PRIVATE Qt::QmlPrivate Qt::QuickPrivate)

생성된 C++ 코드 사용

QQmlComponent 의 인스턴스화와는 달리, qmltc의 출력물은 C++ 코드이므로 애플리케이션에서 직접 사용됩니다. 일반적으로 C++에서 새 객체를 생성하는 것은 ` QQmlComponent::create()`를 통해 새 객체를 생성하는 것과 동일합니다. 객체가 생성되면 C++에서 조작할 수 있으며, 예를 들어 ` QQuickWindow `와 결합하여 화면에 렌더링할 수도 있습니다.

컴파일된 타입이 필수 속성을 노출하는 경우, `qmltc`는 생성된 객체의 생성자에서 해당 속성에 대한 초기값을 요구합니다.

또한, qmltc 객체의 생성자에는 컴포넌트 속성의 초기값을 설정하기 위한 콜백을 전달할 수 있습니다.

myApp.qml 파일이 주어지면, (두 경우 모두) 애플리케이션 코드는 일반적으로 다음과 같이 보입니다:

#include <QtQml/qqmlcomponent.h>

QGuiApplication app(argc, argv);
app.setApplicationDisplayName(QStringLiteral("This example is powered by QQmlComponent :("));

QQmlEngine e;
// If the root element is Window, you don't need to create a Window.
// The snippet is for the cases where the root element is not a Window.
QQuickWindow window;

QQmlComponent component(&e);
component.loadUrl(
            QUrl(QStringLiteral("qrc:/qt/qml/QmltcExample/myApp.qml")));

QScopedPointer<QObject> documentRoot(component.create());
QQuickItem *documentRootItem = qobject_cast<QQuickItem *>(documentRoot.get());

documentRootItem->setParentItem(window.contentItem());
window.setHeight(documentRootItem->height());
window.setWidth(documentRootItem->width());
// ...

window.show();
app.exec();
#include "myapp.h" // include generated C++ header

QGuiApplication app(argc, argv);
app.setApplicationDisplayName(QStringLiteral("This example is powered by qmltc!"));

QQmlEngine e;
// If the root element is Window, you don't need to create a Window.
// The snippet is for the cases where the root element is not a Window.
QQuickWindow window;

QScopedPointer<QmltcExample::myApp> documentRoot(
    new QmltcExample::myApp(&e, nullptr, [](auto& component){
        component.setWidth(800);
}));

documentRoot->setParentItem(window.contentItem());
window.setHeight(documentRoot->height());
window.setWidth(documentRoot->width());
// ...

window.show();
app.exec();

QML 엔진

생성된 코드는 QQmlEngine 를 사용하여 QML 문서의 동적 부분(주로 JavaScript 코드)과 상호 작용합니다. 이 기능이 작동하기 위해 특별한 설정은 필요하지 않습니다. qmltc로 생성된 클래스 객체의 생성자에 전달되는 모든 QQmlEngine 인스턴스는 QQmlComponent(engine) 와 마찬가지로 올바르게 작동합니다. 이는 또한 QML 동작에 영향을 미치는 QQmlEngine methods 를 사용할 수 있음을 의미합니다. 하지만 주의할 점이 있습니다. QQmlComponent 기반의 객체 생성과는 달리, qmltc 자체는 코드를 C++로 컴파일할 때 QQmlEngine 에 의존하지 않습니다. 예를 들어, QQmlEngine::addImportPath("/foo/bar/") (일반적으로 스캔해야 할 추가 임포트 경로를 생성함)는 qmltc의 사전 컴파일(AOT) 과정에서 완전히 무시됩니다.

참고: qmltc 컴파일에 임포트 경로를추가하려면 , 대신 CMake 명령어의 관련 인수를 사용하는 것을 고려해 보십시오.

일반적으로 다음과 같이 생각하면 됩니다. ` QQmlEngine `는 애플리케이션 프로세스가 실행되는 것을 전제로 하는 반면, `qmltc`는 애플리케이션이 컴파일되기도 전에 작동하므로 애플리케이션 프로세스가 필요하지 않습니다. qmltc는 애플리케이션의 C++ 소스 코드를 내부적으로 분석하지 않으므로, 사용자께서 수행하는 특정 종류의 QML 조작에 대해 알 수 있는 방법이 없습니다. QQmlEngine 및 관련 런타임 루틴을 사용하여 QML에 타입을 노출하거나, 임포트 경로를 추가하는 등의 작업 대신, 실질적으로는 올바르게 동작하는 QML 모듈을 생성하고 선언적 QML 타입 등록을 사용해야 합니다.

경고: qmltc가 QQmlEngine 와 긴밀하게 연동되어 C++ 코드를생성하더라도 , 생성된 클래스는 QML에 추가로 노출되거나 QQmlComponent 를 통해 사용될 수 없습니다.

생성된 출력의 기본 사항

qmltc 기존 QML 실행 모델과의 호환성을 목표로 합니다. 이는 생성된 코드가 QQmlComponent 의 내부 설정 로직과 대체로 동등함을 의미하므로, 사용자는 현재와 동일한 방식, 즉 해당 QML 문서를 시각적으로 검토함으로써 QML 타입의 동작, 의미론 및 API를 이해할 수 있어야 합니다.

그러나 생성된 코드는 여전히 다소 혼란스러울 수 있으며, 특히 애플리케이션에서 C++ 측에서 qmltc 출력을 직접 사용해야 한다는 점을 고려하면 더욱 그렇습니다. 생성된 코드는 CMake 빌드 파일 구조와 생성된 C++ 형식이라는 두 부분으로 구성됩니다. 전자는 qmltc의 CMake API에서 다루며, 후자는 여기에서 다룹니다.

hello 속성을 가지고 있으며, 해당 속성을 출력하는 함수와 해당 유형의 객체가 생성될 때 발송되는 신호를 가진 간단한 HelloWorld 유형을 예로 들어 보겠습니다:

// HelloWorld.qml
import QtQml

QtObject {
    id: me
    property string hello: "Hello, qmltc!"

    function printHello(prefix: string, suffix: string) {
        console.log(prefix + me.hello + suffix);
    }

    signal created()
    Component.onCompleted: me.created();
}

이 QML 타입에 해당하는 C++ 대안을 제공하려면, C++ 클래스에는 QML 전용 메타 객체 시스템 매크로, ` hello ` 속성에 대한 ` Q_PROPERTY ` 데코레이터, ` Q_INVOKABLE ` C++ 출력 함수, 그리고 일반적인 Qt 신호 정의가 필요합니다. 마찬가지로, qmltc는 주어진 HelloWorld 타입을 대략 다음과 같이 변환할 것입니다:

class HelloWorld : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    Q_PROPERTY(QString hello READ hello WRITE setHello BINDABLE bindableHello)

public:
    HelloWorld(QQmlEngine* engine, QObject* parent = nullptr, [[maybe_unused]] qxp::function_ref<void(PropertyInitializer&)> initializer = [](PropertyInitializer&){});

Q_SIGNALS:
    void created();

public:
    QString hello();
    void setHello(const QString& hello_);
    QBindable<QString> bindableHello();
    Q_INVOKABLE void printHello(passByConstRefOrValue<QString> prefix, passByConstRefOrValue<QString> suffix);

    // ...
};

생성된 타입의 구체적인 세부 사항은 다를 수 있지만, 공통적인 측면은 그대로 유지됩니다. 예를 들어:

  • 문서 내의 QML 타입은 컴파일러가 인식할 수 있는 정보에 따라 C++ 타입으로 변환됩니다.
  • 속성은 ` Q_PROPERTY ` 선언을 포함한 C++ 속성으로 변환됩니다.
  • JavaScript 함수는 ` Q_INVOKABLE `을 사용하는 C++ 함수로 변환됩니다.
  • Qt Qml 신호는 C++ Qt 신호로 변환됩니다.
  • QML 열거형은 Q_ENUM 선언을 포함한 C++ 열거형으로 변환됩니다.

추가로, ` qmltc `가 클래스 이름을 생성하는 방식에 대해 자세히 살펴보겠습니다. 주어진 QML 유형에 대한 클래스 이름은 해당 유형을 정의하는 QML 문서에서 자동으로 추론됩니다. 즉, 확장자를 제외한 QML 파일 이름(첫 번째 ` .`까지, 이를 기본 이름이라고도 함)이 클래스 이름이 됩니다. 파일 이름의 대소문자 구분은 유지됩니다. 따라서 HelloWorld.qml 는 class HelloWorld 가 되고, helloWoRlD.qml 는 class helloWoRlD 가 됩니다. QML 관례에 따라, QML 문서 파일 이름이 소문자로 시작하는 경우, 생성된 C++ 클래스는 익명 클래스로 간주되어 QML_ANONYMOUS 로 표시됩니다.

현재로서는 생성된 코드를 C++ 애플리케이션 측에서 바로 사용할 수 있지만, 일반적으로 생성된 API 호출은 제한하는 것이 좋습니다. 대신, QML/JavaScript에서 애플리케이션 로직을 구현하고, QML에 노출된 수동으로 작성한 C++ 타입을 사용하는 것이 좋으며, 간단한 객체 인스턴스화에는 qmltc로 생성된 클래스를 활용하십시오. 생성된 C++ 코드를 사용하면 QML에서 정의된 해당 타입의 요소에 직접(그리고 대개 더 빠르게) 접근할 수 있지만, 이러한 코드를 이해하는 데는 어려움이 따를 수 있습니다.

알려진 제한 사항

qmltc는 많은 일반적인 QML 기능을 다루고 있지만, 아직 개발 초기 단계에 있어 지원되지 않는 부분도 있습니다.

QML로 정의된 타입(예: QtQuick.Controls)으로 구성된 가져온 QML 모듈은, 해당 QML 정의 타입이 qmltc에 의해 컴파일되었더라도 올바르게 컴파일되지 않을 수 있습니다. 현재로서는 QtQml 및 QtQuick 모듈은 물론, QML에 노출된 C++ 클래스만 포함하는 다른 모든 QML 모듈을 안정적으로 사용할 수 있습니다.

이 외에도 고려해야 할 몇 가지 근본적인 특이점이 있습니다:

  • Qt의 Qml 모듈은 대개 C++ 라이브러리에 의존하여 핵심 작업을 수행합니다. 이러한 라이브러리는 주로 QML을 통해 사용되기 때문에, 공개 C++ API를 제공하지 않는 경우가 많습니다. qmltc 사용자의 경우, 이는 앱이 비공개 Qt 라이브러리에 링크되어야 함을 의미합니다.
  • qmltc 코드 생성의 특성상, QML 플러그인은 컴파일 목적으로 사용할 수 없습니다. 대신, 플러그인을 사용하는 QML 모듈은 컴파일 시점에 플러그인 데이터에 접근할 수 있도록 보장해야 합니다. 이러한 QML 모듈은 선택적 플러그인을 갖게 됩니다. 대부분의 경우, 컴파일 시간 정보는 헤더 파일(C++ 선언 포함)과 링크 가능한 라이브러리(C++ 정의 포함)를 통해 제공될 수 있습니다. 사용자 코드는 (일반적으로 CMake를 통해) 헤더 파일의 경로를 포함하고 QML 모듈 라이브러리에 링크하는 역할을 담당합니다.

참고: 컴파일러가 기술 미리보기(tech preview) 단계에있으므로 , qmltc, 생성된 코드 또는 기타 관련 부분에서 버그가 발생할 수 있습니다. 이 경우 버그 보고서를 제출해 주시기 바랍니다.

© 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.