모델 및 뷰: 데이터 가져오기를 위해 워커 스레드를 사용하는 목록 모델

워커 스레드를 사용하여 데이터를 가져오는 반응형 UI로 목록 모델을 구현하는 방법을 설명합니다.

앨범 아트, 곡명, 아티스트 이름, 앨범명이 포함된 곡 목록이 표시된 애플리케이션 스크린샷

이 예에서는 QAbstractListModel 을 상속하는 사용자 지정 항목 모델을 소개합니다. 이 모델은 별도의 QThread 에 있는 작업자 객체에서 데이터를 가져와 느린 데이터 소스에서 데이터를 가져옵니다.

스레드된 노래 목록 예제 개요

데이터 소스에서 가져오는 노래당 100밀리초의 지연을 추가하여 느린 데이터 소스를 시뮬레이션합니다. 즉, 3600곡의 전체 목록을 로드하는 데 6분이 걸리므로 애플리케이션을 여는 것이 비현실적입니다. 이 지연은 작업자 스레드에 배치된 QObject 을 사용하여 뷰의 보이는 영역에 대한 데이터만 가져오면 완화됩니다.

작업자 객체에는 대기열에 보관하는 가져오기 요청 수에 대한 제한이 있습니다. 이렇게 하면 노래 목록에서 현재 보이는 부분의 요소만 가져오기 때문에 사용자가 목록의 일부를 이미 지나쳤을 때 목록의 보이지 않는 부분이 로드될 때까지 기다릴 필요가 없습니다.

이 예의 초점은 뷰의 소스 모델에 있습니다. 뷰 자체는 간단한 델리게이트가 있는 수정되지 않은 QML ListView 입니다. 스레드 사용은 모델 데이터 처리 구현 뒤에 숨겨져 있으며 ListView 는 스레드 기반 모델에 적응할 수 있도록 사용자 지정할 필요가 없습니다.

또한 모델에 초점을 맞추기 때문에 Qt Quick Controls 은 모든 플랫폼에서 유니버설 스타일을 사용하도록 설정되어 동일한 UI 동작을 보장합니다.

import QtQuick
import QtQuick.Controls.Universal

작동 방식

노래 목록 데이터를 제공하는 비즈니스 로직은 모델에 간단한 ID 기반 인터페이스를 제공하는 DataStorage 클래스로 분리되어 있습니다.

QList<int> idList();
MediaElement item(int id) const;
std::optional<int> currentlyFetchedId() const;

모델이 데이터스토리지에 데이터를 요청하면 먼저 스토리지에 이미 사용 가능한 데이터가 있는지 확인합니다. 데이터가 있으면 비스레드 모델에서와 마찬가지로 데이터가 즉시 반환됩니다. 데이터를 찾을 수 없는 경우 DataStorage는 작업자 개체에 dataFetchNeeded() 신호를 보내고 이미 존재하는 데이터 목록에 빈 항목을 추가합니다. 빈 항목을 추가하면 동일한 목록 항목에 대한 추가 신호가 워커에 전송되지 않습니다.

if (!m_items.contains(id)) {
    m_items.insert(id, MediaElement{});
    emit dataFetchNeeded(m_idList.indexOf(id));
}
return m_items.value(id);

QueueWorker - 작업자 스레드 객체는 자신에게 신호를 전송하여 수신한 dataFetchNeeded() 신호를 처리하므로, 느린 데이터 읽기 작업을 시작하기 전에 이미 QEventQueue에 있는 모든 신호를 수신할 수 있습니다.

동적 모델에 접근 방식 적용

데이터 소스에서 항목을 추가, 이동 또는 제거할 수 있는 경우(이 경우 RemoteMedia)로 솔루션을 확장하려면 QAbstractItemModel::rowsMoved(), QAbstractItemModel::rowsInserted()와 일치하는 신호와 ThreadedListModel 내부의 QAbstractItemModel::beginRemoveRows() 및 QAbstractItemModel::endRemoveRows()를 트리거하는 두 개의 신호로 DataStorage를 업데이트해야 합니다.

삽입 및 이동을 위해 ThreadedListModel은 QAbstractItemModel::beginInsertRows()를 호출한 다음 ID 목록에 새 ID를 추가하고 QAbstractItemModel::endInsertRows()를 호출하면 됩니다. ThreadedListModel은 ID 목록의 복사본을 보유하고 ID별로 스토리지에 액세스하므로 스토리지에서 시작과 끝을 알릴 필요가 없습니다. 마찬가지로 ThreadedListModel은 QAbstractItemModel::beginMoveRows()를 호출하고 ID 목록에서 ID를 이동한 다음 QAbstractItemModel::endMoveRows()를 호출할 수 있습니다.

제거는 조금 더 복잡한 경우입니다. 뷰는 실제로 제거되기 전에 제거할 데이터를 요청할 수 있어야 합니다. 따라서 DataStorage는 제거에 대한 경고를 신호하여 모델이 QAbstractItemModel::beginRemoveRows()를 호출하도록 해야 합니다. 이 단계에서 ThreadedListModel은 하나 이상의 data() 호출을 받을 수 있습니다. 직접 연결 신호에 대한 호출이 DataStorage에서 반환되면 DataStorage는 항목을 제거한 다음 모델이 QAbstractItemModel::endRemoveRows()를 호출하도록 트리거하는 다른 신호로 모델에 다시 신호를 보내도 좋습니다.

예제 실행하기

에서 예제를 실행하려면 Qt Creator에서 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 Qt Creator: 튜토리얼을 참조하세요 : 빌드 및 실행을 참조하세요.

예제 프로젝트 @ code.qt.io

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