프로젝트 구조
이 장에서는 예제 응용 프로그램의 기초가 될 새로운 다중 프로젝트 솔루션을 만들 것입니다. 사용자 인터페이스와 비즈니스 로직을 분리하여 Model View Controller 패턴을 적용할 것입니다. 또한 Qt의 단위 테스트 프레임워크인 QtTest를 소개하고 이를 솔루션에 통합하는 방법을 보여줍니다. 이 장에서는 다음 사항을 다룰 것입니다.
- Projects, MVC, and unit testing
- Creating a library project
- Creating a unit tests project
- Creating a user interface project
- Mastering MVC
- The QObject base class
- QML
- Controlling project output
프로젝트, MVC 및 단위 테스트
이전 장에서 만든 스크래치패드 응용 프로그램은 .pro 파일로 표시되는 Qt 프로젝트입니다. 비즈니스 환경에서 기술 솔루션은 일반적으로 회사 이니셔티브의 일부로 개발되며 이러한 이니셔티브는 일반적으로 프로젝트라고도 합니다. 혼란을 최소화하기 위해(그리고 프로젝트라는 단어가 나타나는 횟수!), 우리는 .pro 파일로 정의된 Qt 프로젝트를 의미하는 프로젝트와 비즈니스 의미의 프로젝트를 언급하는 이니셔티브라는 단어를 사용할 것입니다.
우리가 작업할 이니셔티브는 일반적인 클라이언트 관리 시스템이 될 것입니다. 공급업체가 고객을 관리하고 의료 서비스가 환자를 관리하는 등 여러 애플리케이션에 맞게 조정하고 용도를 변경할 수 있습니다. 실제 LOB(Line of Business) 응용 프로그램에서 반복적으로 발견되는 일반적인 작업을 수행하며 주로 데이터를 추가, 편집 및 삭제합니다.
스크래치패드 애플리케이션은 단일 프로젝트 내에 완전히 캡슐화되어 있습니다. 더 작은 응용 프로그램의 경우 이것은 완벽하게 실행 가능합니다. 그러나 더 큰 코드 기반, 특히 여러 개발자가 관련된 경우에는 관리하기 쉬운 부분으로 쪼개는 것이 좋습니다.
우리는 MVC(Model View Controller) 아키텍처 패턴의 초경량 구현을 사용할 것입니다. 이전에 MVC를 접한 적이 없다면 주로 사용자 인터페이스에서 비즈니스 로직을 분리하는 데 사용됩니다. 사용자 인터페이스(보기)는 명령을 스위치보드 스타일 클래스(컨트롤러)에 전달하여 데이터를 검색하고 필요한 작업을 수행합니다. 컨트롤러는 차례로 데이터, 논리 및 규칙에 대한 책임을 데이터 개체(모델)에 위임합니다.
그림1
핵심은 컨트롤러에 명령을 보내고 모델에 저장된 데이터를 표시해야 하기 때문에 뷰가 컨트롤러와 모델에 대해 알고 있다는 것입니다. 컨트롤러는 작업을 위임해야 하므로 모델에 대해 알고 있지만 뷰에 대해서는 모릅니다. 모델은 컨트롤러나 뷰에 대해 아무것도 모릅니다.
비즈니스 환경에서 이러한 방식으로 애플리케이션을 설계하는 주요 이점은 전담 UX 전문가가 보기에 대해 작업하는 동안 프로그래머가 비즈니스 로직에 대해 작업할 수 있다는 것입니다. 두 번째 이점은 비즈니스 논리 계층이 UI에 대해 아무것도 모르기 때문에 논리 계층에 영향을 주지 않고 사용자 인터페이스를 추가, 편집 및 완전히 대체할 수 있다는 것입니다. 좋은 사용 사례는 데스크톱 애플리케이션을 위한 "풀 팻(full fat)" UI와 모바일 장치를 위한 컴패니언 "반 팻(half fat)" UI를 갖는 것입니다. 둘 다 동일한 비즈니스 로직을 사용할 수 있습니다. 이 모든 것을 염두에 두고 UI와 비즈니스 로직을 별도의 프로젝트로 물리적으로 분리할 것입니다.
또한 자동화된 단위 테스트를 솔루션에 통합하는 방법도 살펴볼 것입니다. 단위 테스트 및 테스트 주도 개발(TDD)은 최근에 인기를 얻었으며 비즈니스 환경에서 애플리케이션을 개발할 때 코드와 함께 단위 테스트를 작성하게 될 가능성이 높습니다. 그렇지 않은 경우 많은 가치가 있으므로 실제로 제안해야 합니다. 이전에 단위 테스트를 해본 적이 없다고 걱정하지 마세요. 매우 간단하며 나중에 이 책에서 더 자세히 논의할 것입니다.
마지막으로 이러한 하위 프로젝트를 함께 집계하여 개별적으로 열 필요가 없도록 하는 방법이 필요합니다. 우리는 다른 프로젝트를 하나로 묶는 것 외에는 아무것도 하지 않는 우산 솔루션 프로젝트로 이를 달성할 것입니다. 이것이 우리의 프로젝트를 배치하는 방법입니다:
그림2
프로젝트 생성
이전 장에서 몇 개의 텍스트 파일을 만드는 것만으로 새 프로젝트를 설정하는 것이 얼마나 쉬운지 살펴보았습니다. 그러나 우리는 Qt Creator를 사용하여 새로운 솔루션을 만들 것입니다. 새 프로젝트 마법사를 사용하여 최상위 솔루션과 단일 하위 프로젝트를 만드는 과정을 안내합니다.
상단 메뉴에서 File > New File or Project 또는 프로젝트를 선택한 다음 Projects > Other Project > Subdirs Project 를 선택하고 선택…을 클릭합니다.
그림3
Subdirs 프로젝트는 최상위 솔루션 프로젝트에 필요한 템플릿입니다. 이름을 cm로 지정하고 qt 프로젝트 폴더에 생성합니다.
그림4
키트 선택 창에서 설치한 Desktop Qt 5.10.0 MinGW 32비트 키트를 확인합니다. 설치되어 있는 경우 시험해보고 싶은 추가 키트를 자유롭게 선택하십시오. 그러나 필수는 아닙니다. 다음을 클릭하십시오.
그림5
논의한 바와 같이 버전 제어는 이 책의 범위를 벗어나므로 프로젝트 관리 창의 버전 제어에 추가 드롭다운에서 없음을 선택합니다. 마침 및 하위 프로젝트 추가를 클릭합니다.
그림6
사용자 인터페이스 프로젝트를 첫 번째 하위 프로젝트로 추가합니다. 마법사는 방금 수행한 단계와 거의 동일한 패턴을 따르므로 다음을 수행합니다.
- Projects > Application > Qt Quick Application - Empty을 선택하고 선택 합니다.
- 프로젝트 위치 대화 상자에서 이름을 cm-ui(클라이언트 관리 - 사용자 인터페이스용)로 지정하고 위치를 새 cm 폴더로 두고 다음을 클릭합니다.
- 빌드 시스템 정의 대화 상자에서 빌드 시스템 qmake를 선택하고 다음을 클릭하십시오.
- 프로젝트 세부 정보 정의 대화 상자에서 QT 5.9의 기본 최소 Qt 버전과 Qt 가상 키보드 사용 상자를 선택 취소한 상태로 두고 다음을 클릭합니다.
- Kit선택 대화 상자에서 Desktop Qt 5.10.0 MinGW 32비트 Kit와 함께 시도하려는 다른 Kit를 선택하고 다음을 클릭합니다.
- 마지막으로 프로젝트 관리 대화 상자에서 버전 제어를 건너뛰고(<없음>으로 유지) 마침을 클릭합니다.
이제 최상위 솔루션과 UI 프로젝트가 실행 중이므로 다른 하위 프로젝트를 추가해 보겠습니다. 다음과 같이 비즈니스 논리 프로젝트를 다음에 추가합니다.
- 프로젝트 창에서 최상위 cm 폴더를 마우스 오른쪽 버튼으로 클릭하고 New Subproject를 선택합니다.
- Projects > Library > C++ Library를 선택 합니다.
- 소개 및 프로젝트 위치 대화 상자에서 공유 라이브러리를 유형으로 선택하고 이름을 cm-lib로 지정하고 <Qt 프로젝트>/cm에 만들고 다음을 클릭합니다.
- 필수 모듈 선택 대화 상자에서 기본값인 QtCore를 그대로 사용하고 다음을 클릭합니다.
- 클래스 정보 대화 상자에서 새 클래스를 생성하여 시작할 수 있습니다. client.h 헤더 파일과 client.cpp 소스 파일과 함께 클래스 이름을 Client로 지정하고 다음을 클릭합니다.
- 마지막으로 프로젝트 관리 대화 상자에서 버전 제어를 건너뛰고(<없음>으로 유지) 마침을 클릭합니다.
마지막으로 단위 테스트 프로젝트를 만드는 프로세스를 반복합니다.
- New Subproject....
- Projects > Other Project > Qt Unit Test.
- 프로젝트 이름 cm-tests.
- QtCore 및 QtTest를 포함합니다.
- testCase1 테스트 슬롯과 client-tests.cpp 파일 이름으로 ClientTests 테스트 클래스를 만듭니다. Type as Test로 설정하고 Generate initialization and cleanup code을 선택하십시오.
- 버전 관리를 건너뛰고 마침.
통과해야 할 대화 상자가 많았지만 이제 뼈대 솔루션이 제자리에 있습니다. 프로젝트 폴더는 다음과 같아야 합니다.
그림7
이제 콘텐츠를 추가하기 전에 각 프로젝트를 차례로 살펴보고 약간의 조정을 할 것입니다.
cm-lib
먼저 파일 탐색기로 이동하여 cm-lib 아래에 source라는 새 하위 폴더를 만듭니다. cm-lib_global.h를 거기로 이동하십시오. 소스에 모델이라는 다른 하위 폴더를 만들고 두 클라이언트 클래스 파일을 모두 거기로 이동합니다.
그런 다음 Qt Creator로 돌아가 cm-lib.pro를 열고 다음과 같이 편집합니다.
QT -= gui
TARGET = cm-lib TEMPLATE = lib
CONFIG += c++14
DEFINES += CMLIB_LIBRARY
INCLUDEPATH += source
SOURCES += source/models/client.cpp
HEADERS += source/cm-lib_global.h source/models/client.h
라이브러리 프로젝트이므로 기본 GUI 모듈을 로드할 필요가 없으므로 QT 변수를 사용하여 제외합니다. TARGET 변수는 바이너리 출력을 제공하려는 이름입니다(예: cm-lib.dll). 선택 사항이며 제공하지 않으면 기본적으로 프로젝트 이름이 지정되지만 명시적으로 지정하겠습니다. 다음으로 스크래치패드 애플리케이션에서 본 것처럼 앱의 TEMPLATE를 사용하는 대신 이번에는 lib를 사용하여 라이브러리를 제공합니다. CONFIG 변수를 통해 C++14 기능을 추가합니다.
cm-lib_global.h 파일은 공유 라이브러리 기호를 내보내는 데 사용할 수 있는 유용한 전처리기 상용구이며 곧 사용하게 될 것입니다. DEFINES 변수에서 CMLIB_LIBRARY 플래그를 사용하여 이 내보내기를 트리거합니다.
마지막으로, 약간 이동한 후 새 파일 위치를 설명하기 위해 SOURCES 및 HEADERS 변수 목록을 약간 다시 작성했으며 소스 폴더(모든 코드가 있을 위치)를 INCLUDEPATH에 추가하여 경로는 #include 문을 사용할 때 검색됩니다.
프로젝트 창에서 cm-lib 폴더를 마우스 오른쪽 버튼으로 클릭하고 qmake 실행을 선택합니다. 완료되면 다시 마우스 오른쪽 버튼을 클릭하고 재구축을 선택합니다. 모든 것이 푸르고 행복해야 합니다.
cm-test
새 소스/모델 하위 폴더를 만들고 client-tests.cpp를 거기로 이동합니다. Qt Creator로 다시 전환하고 cm-tests.pro를 편집하십시오.
QT += testlib QT -= gui
TARGET = client-tests
TEMPLATE = app
CONFIG += c++14
CONFIG += console
CONFIG -= app_bundle
INCLUDEPATH += source
SOURCES += source/models/client-tests.cpp
이것은 라이브러리가 아닌 콘솔 앱을 원한다는 점을 제외하고 cm-lib와 거의 동일한 접근 방식을 따릅니다. GUI 모듈은 필요하지 않지만 Qt 테스트 기능에 액세스하기 위해 testlib 모듈을 추가합니다.
이 하위 프로젝트에는 아직 많은 것이 없지만 qmake를 실행하고 성공적으로 재구축할 수 있어야 합니다.
cm-ui
이번에는 소스와 보기라는 두 개의 하위 폴더를 만듭니다. main.cpp를 소스로 이동하고 main.qml을 보기로 이동하십시오. qml.qrc의 이름을 views.qrc로 바꾸고 cm-ui.pro를 편집합니다.
QT += qml quick
TEMPLATE = app
CONFIG += c++14
INCLUDEPATH += source
SOURCES += source/main.cpp
RESOURCES += views.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = $$PWD
우리의 UI는 qml과 빠른 모듈이 필요한 QML로 작성되었으므로 추가합니다. RESOURCES 변수를 편집하여 이름이 바뀐 리소스 파일을 선택하고 QML_IMPORT_PATH 변수도 편집합니다. 이 변수는 사용자 지정 QML 모듈에 들어갈 때 자세히 다룰 것입니다.
다음으로, 우리가 main.qml 파일을 views 폴더로 옮겼다는 사실을 고려하여 views.qrc를 편집하십시오. 마우스 오른쪽 버튼을 클릭하고 다음으로 열기 > 일반 텍스트 편집기를 기억하십시오.
<RCC>
<qresource prefix="/">
<file>views/main.qml</file>
</qresource>
</RCC>
마지막으로 파일 이동을 설명하기 위해 main.cpp의 줄도 편집해야 합니다.
engine.load(QUrl(QStringLiteral("qrc:/views/main.qml")));
이제 qmake를 실행하고 cm-ui 프로젝트를 다시 빌드할 수 있습니다. 실행하기 전에 여러 프로젝트가 열려 있으므로 빌드 구성 버튼을 간단히 살펴보겠습니다.
그림
이제 키트 및 빌드 옵션과 함께 실행할 실행 파일도 선택해야 합니다. cm-ui가 선택되었는지 확인한 다음 응용 프로그램을 실행합니다.
그림
안녕하세요 월드입니다. 상당히 고무적이지 않은 내용이지만, 우리는 멀티 프로젝트 솔루션을 구축하고 행복하게 실행하고 있습니다. 이는 훌륭한 시작입니다. 더 이상 즐길 수 없을 때 응용 프로그램을 닫으십시오!
이제 솔루션 구조가 준비되었으므로 MVC 구현을 시작하겠습니다. 보시다시피 설정이 매우 간단하고 매우 쉽습니다.
먼저 cm-ui > Resources > views.qrc > / > views,를 확장하고 main.qml을 마우스 오른쪽 버튼으로 클릭하고 이름 바꾸기를 선택하고 파일 이름을 MasterView.qml로 바꿉니다. 프로젝트 편집에 대한 메시지가 표시되면 예를 선택하여 계속 진행합니다.
그림
오류 메시지가 표시되면 파일은 프로젝트 창에 여전히 main.qml로 표시되지만 파일 시스템에서는 파일 이름이 변경됩니다.
다음으로 views.qrc를 편집합니다(Open With > Plain Text Editor). 내용을 다음과 같이 바꿉니다.
<RCC>
<qresource prefix="/views">
<file alias="MasterView.qml">views/MasterView.qml</file>
</qresource>
</RCC>
main.cpp에서 이 QML 파일을 로드하는 방법을 기억한다면 구문은 qrc:<prefix><filename>입니다. 이전에는 / 접두사와 views/main.qml 상대 파일 이름이 있었습니다. 이것은 우리에게 qrc:/views/main.qml을 제공했습니다.
/ 접두사는 그다지 설명적이지 않습니다. 점점 더 많은 QML 파일을 추가함에 따라 의미 있는 접두사가 있는 블록으로 구성하는 것이 정말 유용합니다. 비구조화된 리소스 블록을 사용하면 보기를 통해 드릴다운해야 할 때 보았듯이 프로젝트 창이 보기 흉하고 탐색하기 더 어려워집니다. view.qrc > / > 보기. 따라서 첫 번째 단계는 접두사의 이름을 /에서 /view로 바꾸는 것입니다.
그러나 접두어가 /view이고 상대 파일 이름이 views/main.qml이므로 URL은 이제 qrc:/views/views/main.qml입니다.
이것은 이전보다 더 나쁩니다. 우리는 여전히 views.qrc에 깊은 폴더 구조를 가지고 있습니다. 다행히 파일에 별칭을 추가하여 이 두 가지 문제를 모두 해결할 수 있습니다. 상대 경로 대신 리소스의 별칭을 사용할 수 있으므로 main.qml의 별칭을 할당하면 views/main.qml을 단순히 main.qml로 교체하여 qrc:/views/main.qml을 제공할 수 있습니다. .
간결하고 설명적이며 프로젝트 창도 더 깔끔해졌습니다.
따라서 업데이트된 버전의 views.qrc로 돌아가서 우리가 수행한 파일 이름 변경과 일치하도록 파일 이름을 main.qml에서 MasterView.qml로 간단히 업데이트했으며 바로 가기 별칭도 제공했기 때문에 보기를 두 번 지정할 필요가 없습니다.
이제 이러한 변경 사항을 반영하기 위해 main.cpp의 코드를 업데이트해야 합니다.
engine.load(QUrl(QStringLiteral("qrc:/views/MasterView.qml")));
qmake를 실행하고 빌드 및 실행하여 아무 것도 손상되지 않았는지 확인할 수 있어야 합니다.
다음으로, MasterController 클래스를 생성할 것이므로 cm-lib 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Add New… > C++ > C++ Class 선택합니다.
그림
찾아보기… 버튼을 사용하여 소스/컨트롤러 하위 폴더를 만듭니다.
QObject를 기본 클래스로 선택하고 포함하면 Qt Creator가 우리를 위해 일부 상용구 코드를 작성합니다. 나중에 언제든지 직접 추가할 수 있으므로 새 클래스를 만드는 데 필요한 부분이라고 생각하지 마십시오.
버전 관리를 건너뛰고 클래스를 생성했으면 다음과 같이 선언하고 정의합니다. MasterController는 아직 특별히 흥미로운 작업을 수행하지 않으며, 단지 기초 작업을 하고 있습니다.
master-controller.h는 다음과 같습니다.
#ifndef MASTERCONTROLLER_H
#define MASTERCONTROLLER_H
#include <QObject>
#include <cm-lib_global.h>
namespace cm {
namespace controllers {
class CMLIBSHARED_EXPORT MasterController : public QObject {
Q_OBJECT
public: explicit MasterController(QObject* parent = nullptr);
};
}
}
#endif
Qt Creator가 제공한 기본 구현에 실제로 추가한 것은 Qt Creator가 cm-lib_global.h에 작성한 CMLIBSHARED_EXPORT 매크로뿐입니다. 공유 라이브러리 내보내기를 처리하고 클래스를 네임스페이스에 넣습니다.
저는 항상 프로젝트 이름을 루트 네임스페이스로 사용하고 소스 디렉토리 내 클래스 파일의 물리적 위치를 반영하는 추가 네임스페이스를 사용합니다. 따라서 이 경우 클래스가 source/ 디렉토리에 있으므로 cm::controllers를 사용합니다. 컨트롤러.
mastercontroller.cpp는 아래와 같습니다.
#include "master-controller.h"
namespace cm {
namespace controllers {
MasterController::MasterController(QObject* parent) : QObject(parent) { }
}
}
나는 구현 파일에서 약간 비정통적인 스타일을 사용합니다. 대부분의 사람들은 네임스페이스 cm::controllers를 사용하여 추가합니다. .cpp 파일의 맨 위에 있습니다. 코드는 IDE에서 접을 수 있기 때문에 종종 네임스페이스 범위 내에 코드를 넣는 것을 좋아합니다. 가장 안쪽 네임스페이스 범위(이 예제의 컨트롤러)를 반복하면 C#에서와 마찬가지로 코드를 축소 가능한 영역으로 나눌 수 있습니다. 기능적 차이는 없으므로 선호하는 스타일을 사용하십시오.
QObject
그렇다면 계속해서 나타나는 이 펑키한 QObject는 무엇입니까? 글쎄, 그것은 모든 Qt 객체의 기본 클래스이며 무료로 몇 가지 강력한 기능을 제공합니다.
QObjects는 부모 객체가 자식 객체의 소유권을 가정하는 객체 계층 구조로 스스로를 구성합니다. 즉, 메모리 관리에 대해 걱정할 필요가 없습니다. 예를 들어, QObject에서 파생된 Address의 부모이기도 한 QObject에서 파생된 Client 클래스의 인스턴스가 있는 경우 클라이언트가 소멸될 때 주소도 자동으로 소멸됩니다.
QObject는 어느 정도 유형 검사를 허용하고 QML과의 상호 작용을 위한 백본인 메타데이터를 전달합니다. 또한 이벤트가 신호로 내보내지고 구독된 대리자를 슬롯이라고 하는 이벤트 구독 메커니즘을 통해 서로 통신할 수 있습니다.
지금 기억해야 할 것은 UI에서 상호 작용하려는 위치에 작성하는 모든 사용자 정의 클래스에 대해 QObject에서 파생되는지 확인하는 것입니다. QObject에서 파생할 때마다 다른 작업을 수행하기 전에 항상 마법의 Q_OBJECT 매크로를 클래스에 추가해야 합니다. QObject를 효과적으로 사용하기 위해 이해할 필요가 없는 매우 복잡한 상용구 코드를 주입합니다.
이제 한 하위 프로젝트(cm-lib의 MasterController)에서 다른 하위 프로젝트(cm-ui)의 코드를 참조해야 하는 시점에 도달했습니다. 먼저 #include 문에 대한 선언에 액세스할 수 있어야 합니다. cm-ui.pro에서 INCLUDEPATH 변수를 다음과 같이 편집합니다.
INCLUDEPATH += source
../cm-lib/source