들어가며
어제는 시스템 권한 기능을 수행하기 위한 Windows Service를 구현했다.
Application과 Service 간 통신을 위해 JSON 기반 프로토콜을 문서화했고, 각 OS별로 필요한 시스템 기능도 추상화해 구현했다.
2025.12.10 - [개발] - Service 기반 시스템 권한 기능 구현기 — JSON 프로토콜과 OS 추상화 설계
Service 기반 시스템 권한 기능 구현기 — JSON 프로토콜과 OS 추상화 설계
들어가며지난 글에서 시스템 권한을 안전하게 사용하기 위한 아키텍처를 설계했다.2025.12.03 - [개발] - Windows·Linux에서 시스템 권한을 다루는 올바른 아키텍처 설계이번 글에서는 그 아키텍처를
prejudice.tistory.com
이번 글에서는 JSON 데이터의 Transport Layer를 구현하는 과정에서 겪은 문제점과 해결 과정을 정리해 보려 한다.
최종적으론 googletest로 유닛 테스트, 그리고 Windows Service 실동작 테스트까지 진행했다.
* 서비스 프로그램의 구현 CheckList
☐ IPC통신 기능 (↔Application, QLocalSocket)
☑ 시스템권한 기능
ㄴ☑ Service 프로토콜 설계/개발
ㄴ☑ 시간 쓰기 처리 기능
☐ Windows Installer에 추가 (후순위)
☐ Linux Installer에 추가 (후순위)
QLocalSocket의 문제점 ㅡ Named Pipe와 DACL 정책
Application ↔ Service 간 IPC를 구현할 때, 가장 먼저 떠오른 건 QLocalSocket이었다.
시스템 외부에서 접근이 제한되고, 추후 Linux 확장성을 고려해 적합하다고 판단했다.
IpcServer와 IpcClient 기본 코드를 작성해 Qt 디버그 모드로 Server를 실행하고, unitTest를 작성해 통신 연결을 시도했다.
첫 번째 통신은 정상적으로 동작했다. Client는 프로토콜에 맞춰 JSON파일을 요청하고 Server는 응답했다.
그러나 문제는 관리자 권한 테스트에서 발생했다.
- Server → 관리자 권한 실행
- Client → 사용자 권한 실행
이 구성에서 connectToServer()가 실패했다.
관련된 내용을 조사해 보니, Windows의 DACL 정책 때문이었다..
DACL(Discretionary Access Control List)이란?
Windwos에서는 서로 다른 권한 레벨의 세션 간에는 IPC 네임스페이스가 분리되는 보안정책이다.
QLocalSocket은 내부적으로 NamedPipe IPC를 사용하는 사실을 배웠다.
나의 경우도 보안정책으로 인해 통신이 제한되는 것을 알 수 있었다.
문제가 발생하는 원인을 알고 IPC를 QTcpSocket으로 Transport Layer를 교체해 문제를 해결했다.
다른 해결방법으로는, Windows NamedPipe API를 사용하면서 보안옵션 설정으로 해결할 수 있는 것 같다.
하지만 향후 Linux확장성, 크로스컴파일 편의성을 고려한다면 QTcpSocket을 이용하는 게 타당하다 생각한다.
시스템 권한 기능 Unit Test

IPC 테스트를 이어가기 전에, 시스템 권한 기능 자체가 정상 동작하는지 단위 테스트를 진행했다.
CMakeLists에 googleTest lib, QtCore, SystemExecutorWindows 파일을 링크해 필요한 환경을 구성했다.
클래스가 분리를 잘해놓은 덕분에 테스트는 독립적으로 쉽게 구성할 수 있었다.
#include <QCoreApplication>
#include <gtest/gtest.h>
#include <QDebug>
#include "SystemExecutorWindows.h"
// 윈도우 관리자 기능 실행을 테스트합니다.
// 관리자 권한으로 실행해야 합니다.
TEST(ExecutorWindowsTest, SetTime) {
SystemExecutorWindows executor;
QString errorMessage;
const bool success = executor.setTime({{"year", 2024}, {"month", 5}, {"day", 20}, {"hour", 10}, {"minute", 30}, {"second", 0}}, errorMessage);
if(success) {
qDebug() << "Time set successfully.";
} else {
qDebug() << "Failed to set time:" << errorMessage;
}
}
TEST(ExecutorWindowsTest, SetTimeZone) {
SystemExecutorWindows executor;
QString errorMessage;
// const bool success = executor.setTimeZone({{"tz_id", "America/New_York"}}, errorMessage);
const bool success = executor.setTimeZone({{"tz_id", "Asia/Seoul"}}, errorMessage);
if(success) {
qDebug() << "Timezone set successfully.";
} else {
qDebug() << "Failed to set timezone:" << errorMessage;
}
}
몇 줄 안 되는 코드로 테스트를 작성해 UnitTest를 진행했다.
테스트 프로그램을 관리자 권한으로 실행되면 성공 메시지를 출력하고 시스템 시간이 변경됐고,
사용자 권한으로 실행하면 "권한 없음" 메시지를 출력하며 성공적으로 테스트할 수 있었다.
굳이 자동화까지는 필요성을 못 느껴 테스트를 마무리했다.
IpcClient ↔ IpcServer 통합 Test

다음은 범위를 확장해, Server와 Client가 JSON 프로토콜을 통해 실제 기능을 수행하는지 테스트했다.
Flow:
- Qt Framework에서 IpcServer 실행
- googletest로 Client 동작 테스트
- 시간 설정/타임존 설정 명령을 실제로 수행
#include <QCoreApplication>
#include <gtest/gtest.h>
#include <QDebug>
#include "IpcClient.h"
TEST(ClientTest, SetTime) {
IpcClient ipcClient;
IpcResponse res = ipcClient.setTime(2024, 5, 20, 10, 30, 0);
if(res.success()) {
qDebug() << "Time set successfully.";
} else {
qDebug() << "Failed to set time:" << res.errorMsg;
}
ASSERT_TRUE(res.success());
}
TEST(ClientTest, SetTimeZone) {
IpcClient ipcClient;
IpcResponse res = ipcClient.setTimeZone(QString("America/New_York"));
if(res.success()) {
qDebug() << "Time set successfully.";
} else {
qDebug() << "Failed to set time:" << res.errorMsg;
}
ASSERT_TRUE(res.success());
}
Server상태를 꺼져있을 때, 사용자 권한으로 실행될 때, 관리자 권한으로 실행될 때 로 나눠 점검했다.
테스트 시나리오
- Server OFF
- Server ON + 사용자 권한
- Server ON + 관리자 권한
- Windows Service에 등록된 Server
마지막으로 수동으로 Windows Service에 등록 후 실제 동작을 검증했다.
## 서비스를 등록
sc create "SystemAgentService" binPath= "C:\~~~\Project\SystemAgent.exe"
## 서비스 실행
sc start "SystemAgentService"
## ClientTest(gtest) 를 사용해 Client요청에 따른 시스템권한 명령이 수행 되는지 테스트
회고.
이번 작업을 통해 다음 내용을 확실히 배울 수 있었다.
☑ 권한이 다른 프로세스 간 IPC에는 DACL 정책이 큰 영향을 준다
QLocalSocket이 단순히 “로컬 소켓”이 아니라 내부적으로 Named Pipe 기반이라는 점을 알게 되었고,
Windows 권한 모델에 따라 IPC 네임스페이스가 달라진다는 사실을 분명히 이해할 수 있었다.
☑ googletest 환경을 초기에 잘 설계해 두면 테스트가 매우 편하다
OS별 시스템 기능 클래스를 잘 분리해 둔 덕분에
단위 테스트뿐만 아니라 통합 테스트까지 빠르게 구성할 수 있었다.
☑ UnitTest를 통해 서비스의 최종 동작을 미리 검증할 수 있었다
서비스가 실제로 어떻게 동작하는지를 테스트 단계에서 미리 확인할 수 있었고,
덕분에 IPC Transport Layer를 QLocalSocket에서 QTcpSocket으로 변경하는 결정도
빠르고 정확하게 내릴 수 있었다.
* 서비스 프로그램의 구현 CheckList
☑ IPC통신 기능 (↔Application,QLocalSocketQTcpSocket)
ㄴ☑ 서비스 등록 & Unit 테스트
☑ 시스템권한 기능
ㄴ☑ Service 프로토콜 설계/개발
ㄴ☑ 시간 쓰기 처리 기능
☐ Windows Installer에 추가 (후순위)
☐ Linux Installer에 추가 (후순위)
ps. Installer에 서비스 등록을 추가하는건 API를 이용하는거라 글은 안적지 않을까...
'개발' 카테고리의 다른 글
| 개발 실무에서 UML을 어디까지 그려야 할까? (0) | 2025.12.31 |
|---|---|
| Linux 빌드 환경 구성하기 - Ubuntu 22.04에서 SVN 보안 문제 해결하기 (0) | 2025.12.24 |
| Service 기반 시스템 권한 기능 구현기 — JSON 프로토콜과 OS 추상화 설계 (1) | 2025.12.10 |
| Windows·Linux에서 시스템 권한을 다루는 올바른 아키텍처 설계 (1) | 2025.12.03 |
| 산업용 제어 장치에서의 인증서 처리와 OPC UA 보안 이해하기 (1) | 2025.11.28 |