빠르고 안전한 CoAP 클라이언트
CoAP 클라이언트를 보안 설정하고 Qt Quick 사용자 인터페이스에서 사용하는 방법.

'Quick Secure CoAP Client'는 보안이 적용된 CoAP 클라이언트를 생성하고 Qt Quick 애플리케이션에서 이를 사용하는 방법을 보여줍니다.
참고: 현재 버전의Qt CoAP 는 QML API를 제공하지 않습니다. 하지만 예제에서 보여주는 것처럼 모듈의 C++ 클래스를 QML에서 사용할 수 있도록 할 수 있습니다.
예제 실행
다음 경로에서 예제를 실행할 수 있습니다.
- Qt Creator
Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 Qt Creator: 튜토리얼: 빌드 및 실행을 참조하십시오.
- Qt Extension for Visual Studio Code
Command Palette 에서 Qt: Open Qt examples 명령을 실행하고 목록에서 예제를 선택합니다. 자세한 내용은 Qt Extension for Visual Studio Code: 튜토리얼: 빌드 및 실행을 참조하십시오.
예제 애플리케이션을 실행하려면 먼저 보안 CoAP 서버를 설정해야 합니다. 사전 공유 키(PSK) 또는 인증서 인증 모드 중 하나를 지원하는 보안 CoAP 서버라면 어떤 서버에서든 예제를 실행할 수 있습니다. 보안 CoAP 서버 설정에 대한 자세한 내용은 ‘보안 CoAP 서버 설정’을 참조하십시오.
C++ 클래스를 QML에 노출하기
이 예제에서는 ` QCoapClient ` 클래스와 ` QtCoap ` 네임스페이스를 QML에 노출해야 합니다. 이를 위해 사용자 정의 래퍼 클래스를 생성하고 특수 등록 매크로를 사용하십시오.
` QCoapClient`을 감싸는 래퍼로 ` QmlCoapSecureClient ` 클래스를 생성합니다. 이 클래스는 선택된 보안 모드와 보안 구성 매개변수도 포함합니다. ` Q_INVOKABLE ` 매크로를 사용하여 여러 메서드를 QML에 노출합니다. 또한 ` QML_NAMED_ELEMENT ` 매크로를 사용하여 QML에서 이 클래스를 ` CoapSecureClient`로 등록합니다.
class QmlCoapSecureClient : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(CoapSecureClient)
public:
QmlCoapSecureClient(QObject *parent = nullptr);
~QmlCoapSecureClient() override;
Q_INVOKABLE void setSecurityMode(QtCoap::SecurityMode mode);
Q_INVOKABLE void sendGetRequest(const QString &host, const QString &path, int port);
Q_INVOKABLE void setSecurityConfiguration(const QString &preSharedKey, const QString &identity);
Q_INVOKABLE void setSecurityConfiguration(const QString &localCertificatePath,
const QString &caCertificatePath,
const QString &privateKeyPath);
Q_INVOKABLE void disconnect();
Q_SIGNALS:
void finished(const QString &result);
private:
QCoapClient *m_coapClient;
QCoapSecurityConfiguration m_configuration;
QtCoap::SecurityMode m_securityMode;
};그 후, QtCoap 네임스페이스를 등록하여 해당 네임스페이스에서 제공하는 열거형을 사용할 수 있도록 하십시오:
namespace QCoapForeignNamespace
{
Q_NAMESPACE
QML_FOREIGN_NAMESPACE(QtCoap)
QML_NAMED_ELEMENT(QtCoap)
}빌드 파일 조정
QML에서 사용자 정의 유형을 사용할 수 있도록 하려면 빌드 시스템 파일을 적절히 업데이트하십시오.
CMake
CMake 기반 빌드의 경우, ` CMakeLists.txt` 파일에 다음 내용을 추가하십시오:
qt_add_qml_module(quicksecureclient
URI CoapSecureClientModule
SOURCES
qmlcoapsecureclient.cpp qmlcoapsecureclient.h
QML_FILES
FilePicker.qml
Main.qml
)qmake
qmake 빌드의 경우, ` quicksecureclient.pro ` 파일을 다음과 같이 수정하십시오:
CONFIG += qmltypes
QML_IMPORT_NAME = CoapSecureClientModule
QML_IMPORT_MAJOR_VERSION = 1
...
qml_resources.files = \
qmldir \
FilePicker.qml \
Main.qml
qml_resources.prefix = /qt/qml/CoapSecureClientModule
RESOURCES += qml_resources새로운 QML 유형 사용
이제 C++ 클래스가 QML에 올바르게 노출되었으므로 새로운 유형을 사용할 수 있습니다.
클라이언트 생성
CoapSecureClient 는 Main.qml 파일에서 인스턴스화됩니다. 이 인스턴스는 QmlCoapSecureClient::finished() 신호를 처리하고 이에 따라 UI를 업데이트합니다:
CoapSecureClient {
id: client
onFinished: (result) => {
outputView.text = result;
statusLabel.text = "";
disconnectButton.enabled = true;
}
}QCoapClient 의 인스턴스는 사용자가 UI에서 보안 모드를 선택하거나 변경할 때 생성됩니다. QmlCoapSecureClient::setSecurityMode() 메서드는 보안 모드 중 하나가 선택되면 QML 코드에서 호출됩니다:
ButtonGroup {
id: securityModeGroup
onClicked: {
if ((securityModeGroup.checkedButton as RadioButton) === preSharedMode)
client.setSecurityMode(QtCoap.SecurityMode.PreSharedKey);
else
client.setSecurityMode(QtCoap.SecurityMode.Certificate);
}
}C++ 측에서는 이 메서드가 ` QCoapClient ` 객체를 생성하고, 해당 객체의 ` finished()` 및 ` error()` 신호에 연결합니다. 이 클래스는 두 신호를 내부적으로 처리한 후, 새로운 ` finished() ` 신호로 전달합니다.
void QmlCoapSecureClient::setSecurityMode(QtCoap::SecurityMode mode)
{
// Create a new client, if the security mode has changed
if (m_coapClient && mode != m_securityMode) {
delete m_coapClient;
m_coapClient = nullptr;
}
if (!m_coapClient) {
m_coapClient = new QCoapClient(mode);
m_securityMode = mode;
connect(m_coapClient, &QCoapClient::finished, this,
[this](QCoapReply *reply) {
if (!reply)
emit finished(tr("Something went wrong, received a null reply"));
else if (reply->errorReceived() != QtCoap::Error::Ok)
emit finished(errorMessage(reply->errorReceived()));
else
emit finished(reply->message().payload());
});
connect(m_coapClient, &QCoapClient::error, this,
[this](QCoapReply *, QtCoap::Error errorCode) {
emit finished(errorMessage(errorCode));
});
}
}요청 전송
Send Request 버튼을 클릭하여 선택한 보안 모드에 따라 보안 구성을 설정하고 GET 요청을 전송합니다:
Button {
id: requestButton
text: qsTr("Send Request")
enabled: securityModeGroup.checkState !== Qt.Unchecked
onClicked: {
outputView.text = "";
if ((securityModeGroup.checkedButton as RadioButton) === preSharedMode)
client.setSecurityConfiguration(pskField.text, identityField.text);
else
client.setSecurityConfiguration(localCertificatePicker.selectedFile,
caCertificatePicker.selectedFile,
privateKeyPicker.selectedFile);
client.sendGetRequest(hostComboBox.editText, resourceField.text,
parseInt(portField.text));
statusLabel.text = qsTr("Sending request to %1%2...").arg(hostComboBox.editText)
.arg(resourceField.text);
}
}setSecurityConfiguration 메서드에는 두 가지 오버로드가 있습니다.
PSK 모드에 대한 오버로드는 클라이언트 ID와 사전 공유 키를 설정할 뿐입니다:
void
QmlCoapSecureClient::setSecurityConfiguration(const QString &preSharedKey, const QString &identity)
{
QCoapSecurityConfiguration configuration;
configuration.setPreSharedKey(preSharedKey.toUtf8());
configuration.setPreSharedKeyIdentity(identity.toUtf8());
m_configuration = configuration;
}X.509 인증서용 오버로드는 인증서 파일과 개인 키를 읽어온 후 보안 구성을 설정합니다:
void QmlCoapSecureClient::setSecurityConfiguration(const QString &localCertificatePath,
const QString &caCertificatePath,
const QString &privateKeyPath)
{
QCoapSecurityConfiguration configuration;
const auto localCerts =
QSslCertificate::fromPath(QUrl(localCertificatePath).toLocalFile(), QSsl::Pem,
QSslCertificate::PatternSyntax::FixedString);
if (localCerts.isEmpty())
qCWarning(lcCoapClient, "The specified local certificate file is not valid.");
else
configuration.setLocalCertificateChain(localCerts.toVector());
const auto caCerts = QSslCertificate::fromPath(QUrl(caCertificatePath).toLocalFile(), QSsl::Pem,
QSslCertificate::PatternSyntax::FixedString);
if (caCerts.isEmpty())
qCWarning(lcCoapClient, "The specified CA certificate file is not valid.");
else
configuration.setCaCertificates(caCerts.toVector());
QFile privateKey(QUrl(privateKeyPath).toLocalFile());
if (privateKey.open(QIODevice::ReadOnly)) {
QCoapPrivateKey key(privateKey.readAll(), QSsl::Ec);
configuration.setPrivateKey(key);
} else {
qCWarning(lcCoapClient) << "Unable to read the specified private key file"
<< privateKeyPath;
}
m_configuration = configuration;
}보안 구성을 설정한 후, ` sendGetRequest ` 메서드는 요청 URL을 설정하고 ` GET ` 요청을 전송합니다:
void QmlCoapSecureClient::sendGetRequest(const QString &host, const QString &path, int port)
{
if (!m_coapClient)
return;
m_coapClient->setSecurityConfiguration(m_configuration);
QUrl url;
url.setHost(host);
url.setPath(path);
url.setPort(port);
m_coapClient->get(url);
}첫 번째 요청을 보낼 때 CoAP 서버와의 핸드셰이크가 수행됩니다. 핸드셰이크가 성공적으로 완료된 후에는 이후의 모든 메시지가 암호화되며, 핸드셰이크 성공 후 보안 구성을 변경해도 아무런 효과가 없습니다. 보안 구성을 변경하거나 호스트를 변경하려면 먼저 연결을 끊어야 합니다.
void QmlCoapSecureClient::disconnect()
{
if (m_coapClient)
m_coapClient->disconnect();
}이렇게 하면 핸드셰이크가 중단되고 열린 소켓이 닫힙니다.
X.509 인증서를 사용하는 인증의 경우, 인증서 파일을 지정해야 합니다. 이를 위해 FilePicker 컴포넌트가 사용됩니다. 이 컴포넌트는 텍스트 필드와 버튼을 결합하여, 버튼을 누르면 파일 대화 상자가 열리도록 합니다:
Item {
id: filePicker
property string dialogText
property alias selectedFile: filePathField.text
height: addFileButton.height
FileDialog {
id: fileDialog
title: qsTr("Please Choose %1").arg(filePicker.dialogText)
currentFolder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
fileMode: FileDialog.OpenFile
onAccepted: filePathField.text = fileDialog.selectedFile
}
RowLayout {
anchors.fill: parent
TextField {
id: filePathField
placeholderText: qsTr("<%1>").arg(filePicker.dialogText)
inputMethodHints: Qt.ImhUrlCharactersOnly
selectByMouse: true
Layout.fillWidth: true
}
Button {
id: addFileButton
text: qsTr("Add %1").arg(filePicker.dialogText)
onClicked: fileDialog.open()
}
}
}FilePicker Main.qml 파일에서는 인증서 및 개인 키를 입력할 필드를 생성하기 위해 이 컴포넌트가 여러 번 인스턴스화됩니다:
FilePicker {
id: localCertificatePicker
dialogText: qsTr("Local Certificate")
enabled: (securityModeGroup.checkedButton as RadioButton) === certificateMode
Layout.columnSpan: 2
Layout.fillWidth: true
}
FilePicker {
id: caCertificatePicker
dialogText: qsTr("CA Certificate")
enabled: (securityModeGroup.checkedButton as RadioButton) === certificateMode
Layout.columnSpan: 2
Layout.fillWidth: true
}
FilePicker {
id: privateKeyPicker
dialogText: qsTr("Private Key")
enabled: (securityModeGroup.checkedButton as RadioButton) === certificateMode
Layout.columnSpan: 2
Layout.fillWidth: true
}보안 CoAP 서버 설정
이 예제를 실행하려면 PSK 모드나 인증서 모드(또는 둘 다)를 지원하는 보안 CoAP 서버가 필요합니다. 다음과 같은 옵션이 있습니다:
- 예를 들어 libcoap, Californium, FreeCoAP 또는 DTLS를 지원하는 기타 CoAP 라이브러리를 사용하여 보안 CoAP 서버를 수동으로 빌드하고 실행합니다.
- Docker Hub에서 제공되는 기성 Docker 이미지를 사용하여, 본 예제에 적합한 보안 CoAP 서버를 빌드하고 실행할 수 있습니다. Docker 기반 CoAP 서버 사용에 필요한 단계는 다음과 같습니다.
PSK 모드용 서버 설정
다음 명령어는 Docker Hub에서 Californium plugtest (기본적으로 보안이 적용되지 않음)를 기반으로 한 보안 CoAP 서버용 Docker 컨테이너를 가져와 실행합니다:
docker run --name coap-test-server -d --rm -p 5683:5683/udp -p 5684:5684/udp tqtc/coap-californium-test-server:3.8.0CoAP 테스트 서버는 포트 5683 (비보안) 및 5684 (보안)에서 접속할 수 있습니다. IP 주소를 확인하는 방법은 ‘IP 주소 확인’ 섹션을 참조하십시오.
이 서버에서 예제를 실행하려면 사전 공유 키(pre-shared key)를 secretPSK 로, ID(identity)를 Client_identity 로 설정해야 합니다.
인증서 모드용 서버 설정
X.509 인증서를 사용한 인증 기능을 갖춘 보안 서버의 Docker 이미지는 FreeCoAP 라이브러리의 타임 서버 예제를 기반으로 합니다. 다음 명령어를 실행하면 Docker Hub에서 컨테이너를 가져와 시작합니다.
docker run --name coap-time-server -d --rm -p 5684:5684/udp tqtc/coap-secure-time-server:freecoapIP 주소를 확인하는 방법은 ‘IP 주소 확인’을 참조하십시오. CoAP 테스트 서버는 확인된 IP 주소의 5684번 포트 및 리소스 경로 /time 을 통해 접속할 수 있습니다.
이 서버로 예제를 실행하려면 서버에서 요구하는 인증서 파일을 지정해야 합니다. 해당 파일들은 도커 컨테이너 내 /root/certs 디렉터리에 있습니다. 이 파일들을 로컬 디렉터리로 복사하려면 다음 명령어를 사용하십시오:
docker cp <container_id>:/root/certs <local_directory_path>예:
$ docker cp 5e46502df88f:/root/certs ~/컨테이너 ID를 확인하는 방법은 아래에 설명되어 있습니다.
IP 주소 확인
Docker 컨테이너의 IP 주소를 확인하려면 먼저 ` docker ps ` 명령어를 실행하여 컨테이너 ID를 확인하십시오. 그러면 다음과 같은 결과가 출력됩니다:
$ docker ps
CONTAINER ID IMAGE
5e46502df88f tqtc/coap-californium-test-server:3.8.0그런 다음 다음 명령을 사용하여 IP 주소를 확인할 수 있습니다:
docker inspect <container_id> | grep IPAddress예를 들어:
$ docker inspect 5e46502df88f | grep IPAddress
...
"IPAddress": "172.17.0.2",
...Docker 컨테이너 종료
사용이 끝난 도커 컨테이너를 종료하려면 다음 명령어를 사용합니다:
docker stop <container_id>여기서 ` <container_id> `는 ` docker ps ` 명령어로 조회한 것과 동일합니다.
파일:
© 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.