
DirectX 11 · 약 3분
Win32 메시지 루프: GetMessage와 PeekMessage를 고르는 기준
종료와 오류를 빠뜨리지 않는 메시지 처리, 게임 갱신과 운영체제 이벤트가 공존하는 구조를 살펴봅니다.
Wolfgadeok · 업데이트
메시지는 입력만을 뜻하지 않는다
창이 있는 스레드는 키보드와 마우스뿐 아니라 크기 변경, 그리기 요청, 종료 등 여러 메시지를 처리합니다. 큐는 단순히 프로세스마다 하나라고 설명하기보다 창을 만든 스레드의 메시지 큐를 기준으로 이해해야 합니다. 또한 모든 메시지가 반드시 큐를 거치는 것은 아니므로 WndProc 안에서 다시 다른 처리가 호출될 가능성도 고려합니다.
TranslateMessage는 주로 키 입력에서 문자 메시지를 만들도록 돕는 함수이며 마우스와 키보드를 통째로 해석해 원래 MSG를 수정하는 일반 변환기가 아닙니다. DispatchMessage는 대상 창의 프로시저로 메시지 처리를 이어 줍니다. 직접 처리하지 않는 메시지는 보통 DefWindowProc로 넘겨 기본 창 동작을 유지합니다.
GetMessage의 세 가지 반환 결과
GetMessage는 메시지가 없을 때 기다립니다. 이는 임의로 매우 짧게 sleep하는 것과 다르며 이벤트 중심 프로그램에서 유용합니다. 반환값은 일반 메시지 수신, WM_QUIT에 의한 종료, 오류를 구분합니다. 단순히 while(GetMessage(...))만 쓰면 음수 오류도 참으로 취급할 수 있으므로 아래처럼 명시적으로 나누는 편이 이해하기 쉽습니다.
예제 · cpp
MSG message{};
for (;;) {
const BOOL result = GetMessageW(&message, nullptr, 0, 0);
if (result == -1) {
// GetLastError()를 기록하고 실패 경로로 종료합니다.
return 1;
}
if (result == 0) {
return static_cast<int>(message.wParam);
}
TranslateMessage(&message);
DispatchMessageW(&message);
}게임은 메시지 사이에도 진행된다
플레이어가 키를 누르지 않아도 오크는 다가오고 시간이 흘러야 합니다. 그래서 게임 루프에서는 기다리지 않는 PeekMessage로 대기 중인 메시지를 처리하고 게임 갱신을 이어 가는 구조를 사용할 수 있습니다. PM_REMOVE를 지정하면 읽은 메시지를 큐에서 제거합니다. WM_QUIT를 만나면 렌더링을 한 번 더 진행하지 말고 루프를 종료해야 합니다.
그러나 PeekMessage를 쓰는 것만으로 좋은 게임 루프가 되지는 않습니다. 아무 일도 하지 않으며 무제한 반복하면 CPU를 소모합니다. 목표 프레임률, 표시 동기화와 대기 정책을 갖추고 최소화 상태의 처리를 정합니다. 입력 메시지가 과도할 때 게임 갱신이 굶지 않는지도 점검해야 합니다. ‘게임은 컴퓨터의 시간을 최대한 빼앗아야 한다’는 접근은 목표가 아닙니다.
- 창의 X 버튼을 누르면 종료 메시지가 처리되고 프로세스가 남지 않는지 봅니다.
- 입력이 없을 때도 필요한 게임 갱신이 지속되는지 봅니다.
- 최소화·복원 후 입력이 멈추거나 CPU 사용량이 비정상적으로 높지 않은지 봅니다.
- WndProc에서 파일 로딩처럼 긴 작업을 하지 않도록 책임을 분리합니다.
참고 자료
예제는 학습용이며, 모든 환경에서 실행 검증된 완성 프로젝트는 아닙니다. 적용 전 사용 중인 버전과 프로젝트 환경에서 동작을 확인하세요.