들어가며
Qt는 크로스 플랫폼 애플리케이션 개발 프레임워크로,
동일한 코드로 Windows, Linux, macOS, Embedded Linux 등 다양한 운영체제에서 동작하는 App을 만들 수 있다.
QObject, Signal/Event, QTimer 등을 사용해 개발하다 보면
어느 순간 "Qt 내부에서는 어떻게 동작하는 걸까?"라는 궁금증이 생기기 마련이다.
이 글에서는 Qt가 내부적으로 어떤 흐름을 거쳐 이벤트를 처리하는지,
Qt Event Loop를 중심으로 정리해 보려 한다.
Qt Event Loop
Qt로 개발하다 보면 자연스럽게 접하게 되는 용어가 Event Loop이다.
Event Loop는 Qt에서 이벤트가 처리되는 방식을 설명하는 추상적인 개념이며,
실제 동작 흐름은 다음과 같이 구성된다.
OS (Win32 / epoll / select)
↓
QAbstractEventDispatcher
↓
Qt Event Queue
↓
QObject::event()
여기서 QAbstractEventDispatcher는
운영체제의 이벤트 시스템과 Qt Event Loop를 연결하는 역할을 한다.
QTimer로 알아보는 Event Loop
예를 들어 QTimer를 사용해 1000ms 동작하도록 설정했다고 가정해 보자.
Qt는 이 타이머 정보를 OS 이벤트 대기 함수에 등록한다.
1000ms 동안 다른 이벤트가 없다면, Event Loop는 OS 커널에게 제어를 넘기고 대기 상태로 들어간다.
이후 타이머가 만료되면 OS에서 이벤트가 발생하고,
QAbstractEventDispatcher가 이를 감지해 Qt Event Queue에 이벤트를 전달한다.
결과적으로 QTimer::event()가 호출되고, 우리가 개발한 로직이 실행된다.
이처럼 Qt는 이벤트 기반(Event-driven) 구조로 동작하며,
이벤트가 없을 때는 불필요한 CPU사용을 하지 않도록 최적화되어 있다.
*Qt는 while (1) { event(); } 이런 식의 Busy Waiting으로 동작하지 않는다.
이때 한 단계 더 생각해 보자.
"OS에 등록한 이벤트가 딱 1000ms 이후 실행될까?"
당연히 OS 또한 이벤트 호출에 대한 타이머 해상도가 존재하기 때문에 정확 1000ms에 호출되지 않는다.
예를 들어 Windows의 기본 타이머 해상도는 15.6ms로,
QTimer 역시 호출 시점에 일정 수준의 오차가 발생할 수 있다.
Event Loop가 깨어나는 조건
Qt Event Loop는 이벤트가 없으면 실행되지 않는다.
Event Loop가 깨어나는 경우는 딱 네 가지이다.
- OS 이벤트 도착 (마우스, 키보드, 윈도우 메시지)
- 타이머 만료 (QTimer)
- 다른 스레드에서 Queued Signal/Slot 발생
- 명시적인 WakeUp() 호출
OS의 호출로 깨어나면,
Event Queue에 쌓여 있는 이벤트를 순서대로 처리한다.
Nested Event Loop의 위험성
그렇다면 메인이벤트 처리 중에 Event Loop를 한번 더 실행하면 어떻게 될까?
EventQueue
┣ QObject::event() #1번 이벤트
┣ QObject::event() #2번 이벤트
┃ ┗ QEventLoop loop;
┃ loop.exec(); #Nested Event Loop 시작
┃ ┣ QObject::event() #3번 이벤트
┃ ┗ QObject::event() #4번 이벤트
┣QObject::event()#3번 이벤트 : 2번에서 실행되면서 Queue에서 제거됨
┗QObject::event()#4번 이벤트 : 2번에서 실행되면서 Queue에서 제거됨
이처럼 Event Queue의 흐름이 꼬이면서 예기치 않은 동작이 발생할 수 있다.
그래서 Qt에서는 QEventLoop::exec()를 직접 호출하는 구조를 신중하게 설계해야 한다.
Qt에서는 QDialog::exec() 같은 API가 내부적으로 nested loop를 사용하기 때문에 modal dialog 사용을 조심해야 한다.
또한 특정 event에 너무 많은 Slot이 연결되어 있거나,
하나의 이벤트 처리 로직이 지나치게 무거운 경우
다른 이벤트들이 Queue에 쌓이기만 하고 실행되지 않을 수 있다는 점도 생각해 보자.
Qt Event Loop와 Thread
Event Loop는 하나만 존재해야 하는 것은 아니다.
이전에 정리했던 글에서도 다뤘듯이,
QObject는 자신이 속한 Thread마다 Event Loop를 가질 수 있다.
2025.12.08 - [Qt] - Qt Signal/Slot과 Multi-Thread 동작 원리 - invokeMethod로 겪은 문제와 올바른 함수 설계
Qt Signal/Slot과 Multi-Thread 동작 원리 - invokeMethod로 겪은 문제와 올바른 함수 설계
들어가며우리 회사에서는 Qt Framework를 이용해 Windows와 Linux에서 동작하는 애플리케이션을 개발한다. Qt에서 사용하는 Signal/Slot 방식과 Multi Thread에서 어떻게 동작하는지, 처음 Qt에서 QMetaObject::invo
prejudice.tistory.com
즉, Qt에서는 여러 Thread에서 각각의 Event Loop가 동시에 동작할 수 있으며,
Signal/Slot 연결 방식에 따라 동기/비동기 동작이 결정된다.
*위 글을 보면 Signal/Slot 연결에 따른 여러 이벤트루프의 동기화/비동기화 개념을 알 수 있다.
회고
이 글을 정리하면서 다음 질문들에 대해 나름의 답을 구할 수 있었다.
- Event Loop는 언제 깨어나고, 이벤트가 없을 때는 어떻게 동작하는가?
- Qt에서 짧은 Signal/Slot 기반의 설계가 왜 권장되는가?
- 멀티스레드 환경에서 비동기 설계가 갖는 장점은 무엇인가?
다음에는 현재 개념을 확장시켜서
QObject와 moc파일의 역할,
Qt Quick(QML)에서 프레임 단위 갱신으로 최적화한 방법 등을 공부해 봐야 겠다.
'Qt' 카테고리의 다른 글
| Qt QML 내부 동작 원리 이해하기 ㅡ Property Binding, Event Loop, Scene Graph (0) | 2026.02.02 |
|---|---|
| Qt QObject와 moc의 동작 원리 이해하기 ㅡ Signal/Slot과 런타임 리플렉션 (0) | 2026.01.28 |
| Qt IPC 성능 비교 실험기 ㅡ QSharedMemory, QLocalSocket, QTcpSocket, QRemoteObject (1) | 2026.01.19 |
| Qt6 DLL 생성부터 gtest 유닛 테스트까지 ㅡ 프로젝트 구성 따라하기 (0) | 2026.01.14 |
| Qt 4 · Qt 5 · Qt 6 차이점 정리 — 레거시 컴파일 실무 경험 (0) | 2026.01.07 |