자바스크립트에서 동적 QML 객체 생성

QML은 JavaScript 내에서 객체를 동적으로 생성하는 기능을 지원합니다. 이는 필요할 때까지 객체의 인스턴스화를 지연시켜 애플리케이션 시작 시간을 개선하는 데 유용합니다. 또한 사용자 입력이나 기타 이벤트에 반응하여 시각적 객체를 동적으로 생성하고 장면에 추가할 수 있습니다.

오브젝트 동적 생성

자바스크립트에서 객체를 동적으로 생성하는 방법에는 두 가지가 있습니다. Qt.createComponent ()를 호출하여 Component 객체를 동적으로 생성하거나 Qt.createQmlObject()를 사용하여 QML 문자열에서 객체를 생성할 수 있습니다. QML 문서에 정의된 기존 컴포넌트가 있고 해당 컴포넌트의 인스턴스를 동적으로 생성하려는 경우 컴포넌트를 생성하는 것이 더 좋습니다. 그렇지 않으면 QML 문자열에서 객체를 만드는 것은 런타임에 객체 QML 자체가 생성될 때 유용합니다.

컴포넌트 동적으로 생성하기

QML 파일에 정의된 컴포넌트를 동적으로 로드하려면 Qt object 에서 Qt.createComponent() 함수를 호출합니다. 이 함수는 QML 파일의 URL을 유일한 인수로 받고 이 URL에서 Component 객체를 만듭니다.

Component 객체가 있으면 해당 객체의 createObject() 메서드를 호출하여 컴포넌트의 인스턴스를 만들 수 있습니다. 이 함수는 하나 또는 두 개의 인수를 받을 수 있습니다:

  • 첫 번째 인수는 새 객체의 부모입니다. 부모는 그래픽 객체(예: Item 유형)이거나 비그래픽 객체(예: QtObject 또는 C++ QObject 유형)일 수 있습니다. 그래픽 부모 객체가 있는 그래픽 객체만 시각적 캔버스에 렌더링됩니다. Qt Quick 비주얼 캔버스에 렌더링됩니다. 나중에 부모를 설정하려면 null 을 이 함수에 안전하게 전달하면 됩니다.
  • 두 번째는 선택 사항이며 객체의 초기 속성 값을 정의하는 속성-값 쌍의 맵입니다. 이 인수로 지정된 속성 값은 객체 생성이 완료되기 전에 객체에 적용되므로 다른 속성 바인딩을 활성화하기 위해 특정 속성을 초기화해야 하는 경우 발생할 수 있는 바인딩 오류를 방지할 수 있습니다. 또한 객체가 생성된 후에 속성 값과 바인딩을 정의하는 것과 비교할 때 약간의 성능 이점이 있습니다.

다음은 예시입니다. 먼저 간단한 QML 컴포넌트를 정의하는 Sprite.qml 이 있습니다:

import QtQuick

Rectangle { width: 80; height: 50; color: "red" }

기본 애플리케이션 파일인 main.qmlSprite 객체를 생성하는 componentCreation.js JavaScript 파일을 가져옵니다:

import QtQuick
import "componentCreation.js" as MyScript

Rectangle {
    id: appWindow
    width: 300; height: 300

    Component.onCompleted: MyScript.createSpriteObjects();
}

다음은 componentCreation.js 입니다. QML 파일이 네트워크를 통해 로드되어 즉시 준비되지 않은 경우 createObject()를 호출하기 전에 status 컴포넌트가 Component.Ready 인지 여부를 확인합니다.

var component;
var sprite;

function createSpriteObjects() {
    component = Qt.createComponent("Sprite.qml");
    if (component.status == Component.Ready)
        finishCreation();
    else
        component.statusChanged.connect(finishCreation);
}

function finishCreation() {
    if (component.status == Component.Ready) {
        sprite = component.createObject(appWindow, {x: 100, y: 100});
        if (sprite == null) {
            // Error Handling
            console.log("Error creating object");
        }
    } else if (component.status == Component.Error) {
        // Error Handling
        console.log("Error loading component:", component.errorString());
    }
}

로드할 QML 파일이 로컬 파일이라고 확신하는 경우 finishCreation() 함수를 생략하고 createObject()를 즉시 호출할 수 있습니다:

function createSpriteObjects() {
    component = Qt.createComponent("Sprite.qml");
    sprite = component.createObject(appWindow, {x: 100, y: 100});

    if (sprite == null) {
        // Error Handling
        console.log("Error creating object");
    }
}

두 경우 모두 동적으로 생성된 객체가 시각적(Qt Quick) 객체이므로 appWindow 을 부모 인수로 전달하여 createObject()을 호출합니다. 생성된 객체는 main.qml 에서 appWindow 객체의 자식이 되어 장면에 나타납니다.

상대 경로를 가진 파일을 사용하는 경우 경로는 Qt.createComponent()가 실행되는 파일에 상대적이어야 합니다.

동적으로 생성된 객체에 신호를 연결하거나 신호를 받으려면 connect() 메서드를 사용합니다. 자세한 내용은 메서드 및 신호에 신호 연결하기를 참조하세요.

incubateObject() 함수를 통해 차단하지 않고 컴포넌트를 인스턴스화할 수도 있습니다.

QML 문자열에서 객체 만들기

경고: QML 문자열에서 오브젝트를 생성할 때마다 엔진이 전달된 QML 문자열을 컴파일해야 하므로 매우 느립니다. 또한 프로그래밍 방식으로 QML 코드를 구성할 때 잘못된 QML을 생성하기가 매우 쉽습니다. 문자열 조작으로 새 컴포넌트를 생성하는 것보다 QML 컴포넌트를 별도의 파일로 유지하고 프로퍼티와 메서드를 추가하여 동작을 커스터마이징하는 것이 훨씬 낫습니다.

QML이 런타임까지 정의되지 않은 경우 다음 예제에서와 같이 Qt.createQmlObject() 함수를 사용하여 QML 문자열에서 QML 객체를 만들 수 있습니다:

const newObject = Qt.createQmlObject(`
    import QtQuick

    Rectangle {
        color: "red"
        width: 20
        height: 20
    }
    `,
    parentItem,
    "myDynamicSnippet"
);

첫 번째 인수는 생성할 QML 문자열입니다. 새 파일에서와 마찬가지로 사용하려는 유형을 가져와야 합니다. 두 번째 인수는 새 객체의 부모 객체이며, 컴포넌트에 적용되는 부모 인수 의미론은 createQmlObject() 에도 유사하게 적용됩니다. 세 번째 인수는 새 객체와 연결할 파일 경로로, 오류 보고에 사용됩니다.

QML 문자열이 상대 경로를 사용하여 파일을 가져오는 경우 경로는 부모 객체(메서드의 두 번째 인수)가 정의된 파일에 상대적이어야 합니다.

중요: 정적 QML 애플리케이션을 빌드할 때 QML 파일을 검사하여 가져오기 종속성을 감지합니다. 이렇게 하면 필요한 모든 플러그인과 리소스가 컴파일 시점에 해결됩니다. 그러나 명시적 가져오기 문(QML 파일 맨 위에 있는 문)만 고려되며 문자열 리터럴로 묶인 가져오기 문은 고려되지 않습니다. 따라서 정적 빌드를 지원하려면 Qt.createQmlObject()를 사용하는 QML 파일에 문자열 리터럴 내부 외에 파일 상단에 필요한 모든 임포트가 명시적으로 포함되어 있는지 확인해야 합니다.

동적으로 생성된 개체 유지 관리

동적으로 생성된 개체를 관리할 때는 생성 컨텍스트가 생성된 개체보다 오래 유지되도록 해야 합니다. 그렇지 않으면 생성 컨텍스트가 먼저 소멸되면 동적 객체의 바인딩 및 신호 처리기가 더 이상 작동하지 않습니다.

실제 생성 컨텍스트는 객체가 생성되는 방식에 따라 다릅니다:

  • Qt.createComponent()가 사용되는 경우 생성 컨텍스트는 이 메서드가 호출되는 QQmlContext 입니다.
  • Qt.createQmlObject()가 호출되면 생성 컨텍스트는 이 메서드에 전달된 부모 객체의 컨텍스트입니다.
  • Component{} 객체가 정의되고 해당 객체에서 createObject() 또는 incubateObject()가 호출되는 경우 생성 컨텍스트는 Component 가 정의된 컨텍스트입니다.

또한 동적으로 생성된 객체는 다른 객체와 동일하게 사용될 수 있지만 QML에서는 ID가 없습니다.

동적으로 객체 삭제하기

많은 사용자 인터페이스에서는 시각적 개체의 불투명도를 0으로 설정하거나 삭제하는 대신 시각적 개체를 화면 밖으로 이동하는 것으로 충분합니다. 그러나 동적으로 생성된 개체가 많은 경우 사용하지 않는 개체를 삭제하면 성능 면에서 이점을 얻을 수 있습니다.

편리한 QML 객체 팩토리(예: LoaderRepeater)에서 동적으로 생성된 객체는 절대로 수동으로 삭제해서는 안 됩니다. 또한 직접 동적으로 만들지 않은 개체는 삭제하지 않아야 합니다.

destroy() 메서드를 사용하여 항목을 삭제할 수 있습니다. 이 메서드에는 개체를 삭제하기 전 대략적인 지연 시간(밀리초)을 지정하는 선택적 인수(기본값은 0)가 있습니다.

다음은 예시입니다. application.qmlSelfDestroyingRect.qml 컴포넌트의 인스턴스 5개를 생성합니다. 각 인스턴스는 NumberAnimation 를 실행하고 애니메이션이 완료되면 루트 객체에서 destroy() 를 호출하여 스스로 소멸합니다:

application.qml
import QtQuick

Item {
    id: container
    width: 500; height: 100

    Component.onCompleted: {
        var component = Qt.createComponent("SelfDestroyingRect.qml");
        for (var i=0; i<5; i++) {
            var object = component.createObject(container);
            object.x = (object.width + 10) * i;
        }
    }
}
SelfDestroyingRect.qml
import QtQuick

Rectangle {
    id: rect
    width: 80; height: 80
    color: "red"

    NumberAnimation on opacity {
        to: 0
        duration: 1000

        onRunningChanged: {
            if (!running) {
                console.log("Destroying...")
                rect.destroy();
            }
        }
    }
}

또는 application.qmlobject.destroy() 을 호출하여 생성된 객체를 파괴할 수도 있습니다.

해당 객체 내의 객체에서 destroy()를 호출하는 것이 안전하다는 점에 유의하세요. 객체는 destroy()가 호출되는 즉시 소멸되는 것이 아니라 해당 스크립트 블록의 끝과 다음 프레임 사이에 정리됩니다(0이 아닌 지연을 지정하지 않은 경우).

또한 SelfDestroyingRect 인스턴스가 이와 같이 정적으로 생성된 경우에도 유의하세요:

Item {
    SelfDestroyingRect {
        // ...
    }
}

객체가 동적으로 생성된 경우에만 동적으로 삭제할 수 있으므로 오류가 발생할 수 있습니다.

Qt.createQmlObject()로 생성된 객체도 마찬가지로 destroy() 을 사용하여 소멸할 수 있습니다:

const newObject = Qt.createQmlObject(`
    import QtQuick

    Rectangle {
        color: "red"
        width: 20
        height: 20
    }
    `,
    parentItem,
    "myDynamicSnippet"
);
newObject.destroy(1000);

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