들어가며
지난 글에서는 Qt의 근간이 되는 Event Loop의 동작 원리를 정리했다.
2026.01.23 - [Qt] - Qt Event Loop 동작 원리 정리 ㅡ 타이머, 이벤트, 스레드 까지
Qt Event Loop 동작 원리 정리 ㅡ 타이머, 이벤트, 스레드 까지
들어가며Qt는 크로스 플랫폼 애플리케이션 개발 프레임워크로,동일한 코드로 Windows, Linux, macOS, Embedded Linux 등 다양한 운영체제에서 동작하는 App을 만들 수 있다. QObject, Signal/Event, QTimer 등을 사용
prejudice.tistory.com
이번 글에서는 조금 더 클래스 관점에서 접근해,
Qt Framework의 핵심이 되는 QObject와
이를 가능하게 하는 moc(Meta-Object Compiler) 개념을 정리해 보려 한다.
QObject란 무엇인가
Qt에서 사용하는 대부분의 객체는 공통적으로 QObject라는 객체 모델을 상속받는다.
QObject의 핵심 역할은 다음과 같다.
- Sinal / Slot 기반 객체 간 통신 제공
- Event Loop와 연계된 이벤트 처리
- 객체 트리 기반 메모리 관리
즉, Signal / Slot을 사용하는 클래스라면 반드시 QObject를 상속해야 한다.
Q_OBJECT 매크로의 역할
Sinal / Slot은 단순한 C++ 함수 호출이 아니라,
Qt Event Loop 런타임에서 동적으로 처리되는 메커니즘이다.
이때 QObject가 런타임에서 동작할 수 있도록 도와주는 것이
바로 Q_OBJECT 매크로이다.
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT // Signal / Slot을 사용하기 위해 반드시 필요
public:
explicit MyObject(QObject* parent = nullptr) : QObject(parent) {}
signals:
void valueChanged(int value); // 시그널 선언
public slots:
void onValueChanged(int value) // 슬롯 선언
{
qDebug() << "Value changed to:" << value;
}
};
Q_OBJECT가 없으면:
- Sinal / Slot 사용 불가
- invokeMethod 불가
- Property 시스템 사용 불가
즉, QObject는 Q_OBJECT와 함께 사용할 때 비로소 완성된다.
QObject와 일반 C++ 클래스의 차이
겉으로 보면 QObject를 상속한 클래스도 일반적인 C++ 클래스와 크게 달라 보이지 않는다.
하지만 내부적으로는 Qt의 메타 객체 시스템(Meta-Object System)에 의해 관리된다는 점에서 본질적인 차이가 있다.
일반 C++ 클래스의 특징
- 컴파일 타임에 타입 정보가 고정됨
- 런타임에 클래스 구조를 동적으로 조회하기 어려움
- 객체 간 통신은 직접 함수 호출로 구현
QObject 기반 클래스의 추가 기능
- Signal / Slot 기반 비동기 통신
- 확장된 런타임 타입 정보
- 문자열 기반 메서드 호출 (invokeMethod)
- 동적 속성(Property) 시스템
- Parent–Child 객체 트리 기반 메모리 관리
즉, QObject는 단순한 베이스 클래스가 아니라 이벤트 기반 객체 모델의 핵심이라고 볼 수 있다.
moc (Meta-Object Compiler)
C++ 컴파일러만으로는
Signal / Slot, Property 같은 동적 기능을 구현할 수 없다.
이를 보완하기 위해 Qt는 moc라는 별도의 전처리 도구를 사용한다.
빌드 과정은 다음과 같다.
- Qt 빌드 시스템(qmake / CMake)이 Q_OBJECT 매크로를 발견
- 해당 헤더를 moc에 전달
- moc가 메타 정보를 담은 C++ 소스 파일 생성
- 생성된 moc 파일이 함께 컴파일됨
예를 들어 다음 클래스가 있다면,
class MyObject : public QObject
{
Q_OBJECT
signals:
void valueChanged(int value);
};
moc는 내부적으로 다음과 같은 코드를 생성한다.
// moc_MyObject.cpp (자동 생성)
const QMetaObject MyObject::staticMetaObject = {
// 시그널, 슬롯, 메서드 정보 테이블
};
이 메타 객체 정보 덕분에 Qt는 런타임에서 객체 구조를 탐색하고 호출할 수 있다.
좀 더 정확히 알아보면 Qt의 객체 모델은 C++을 기반으로 생성된다.
C++는 기본적으로 런타임 리플렉션 기능을 제공하지 않기 때문에
Qt는 moc를 통해 가상의 메타 객체 테이블과 간접 호출 코드를 C++소스로 생성해 컴파일한다.
런타임 리플렉션(Runtime Reflection)
리플렉션이란 프로그램이 실행 중에 자기 자신의 구조를 조사하고, 호출하고, 조작할 수 있는 능력을 의미한다.
예를 들어
- 클래스 이름 조회
- 함수(메서드) 목록 확인
- 문자열 기반 메서드 호출
같은 기능이 가능하다면, 그 언어 또는 프레임워크는 런타임 리플렉션을 지원한다고 말한다.
C++의 기본 철학은 컴파일 타임 타입 안정성과 성능을 중시하는 것으로, 기본적으로 런타임 리플렉션을 지원하지 않는다.
예를 들어 C++에서는 아래와 같은 함수 호출을 지원하지 않는다.
std::string funcName = "valueChanged";
obj.call(funcName); // ❌ 불가능
"컴파일 시점에 만들어졌는데 왜 런타임 리플렉션?"
리플렉션은 '언제 만들어졌냐'가 아니라 '어떻게 접근하느냐'의 문제다.
moc 코드는 컴파일 시 생성되지만 그 정보는 런타임에 조회, 해석, 호출된다.
Qt에서의 런타임 리플렉션
Qt는 언어 차원의 리플렉션 대신,
moc를 통해 생성된 메타 객체 테이블을 이용해 런타임 리플렉션을 구현했다.
이 덕분에 다음과 같은 기능들이 가능해진다.
- QMetaObject::invokeMethod(obj, "methodName")
- UI 파일 기반 자동 연결 ( connectByName )
- QML ↔ C++ 연동
또한 이 구조를 이해하면,
- 컴파일 타임에 연결하는 connect
- 런타임에 탐색하는 invokeMethod
사이의 성능 차이가 발생하는 이유도 자연스럽게 이해할 수 있다.
회고
QObject를 가볍게 정리하려던 글이었지만,
그 과정에서 Qt가 추구하는 객체 모델과 설계 철학을 깊이 이해할 수 있었다.
- 왜 Qt는 메타 객체 시스템을 도입했는지
- 왜 Signal / Slot이 함수 호출이 아닌 구조로 설계되었는지
- C++과 Python / Java의 런타임 모델 차이
Event Loop와 함께 Qt 내부 구조를 이해하는데 큰 도움이 되는 주제였다.
다음에는 QObject구조가 QML까지 어떻게 확장되는지, C++에서 공식적으로 제공하는 런타임 리플렉션에 대해 공부하고 싶다.
부족한 부분이나 더 궁금한 주제가 있다면 댓글로 남겨주세요.
직접 공부해서 다음 글로 정리해 보려고 합니다.
'Qt' 카테고리의 다른 글
| Qt6 QML 따라하기 ㅡ 프로젝트 생성부터 C++ 연동까지 (0) | 2026.02.10 |
|---|---|
| Qt QML 내부 동작 원리 이해하기 ㅡ Property Binding, Event Loop, Scene Graph (0) | 2026.02.02 |
| Qt Event Loop 동작 원리 정리 ㅡ 타이머, 이벤트, 스레드 까지 (0) | 2026.01.23 |
| Qt IPC 성능 비교 실험기 ㅡ QSharedMemory, QLocalSocket, QTcpSocket, QRemoteObject (1) | 2026.01.19 |
| Qt6 DLL 생성부터 gtest 유닛 테스트까지 ㅡ 프로젝트 구성 따라하기 (0) | 2026.01.14 |