메뉴 닫기

MFC WM_CLOSE 메시지로 안전한 종료 처리하기


MFC WM_CLOSE 메시지로 안전한 종료 처리하기

📌 종료 메시지 처리 제대로 알고 쓰면, 예외 없는 프로그램 마무리가 가능합니다

윈도우 프로그램을 만들다 보면 종료 처리를 어떻게 할지 고민하게 되죠.
단순히 창을 닫는 것이 아니라, 메모리 해제나 파일 저장 등 중요한 마무리 작업이 따라오기 때문입니다.
그런데 MFC에서는 이를 위해 WM_CLOSE라는 메시지를 제공한다는 사실, 알고 계셨나요?
많은 분들이 단순히 ‘닫기 버튼을 누르면 창이 닫힌다’고만 생각하지만, 실제로는 이 WM_CLOSE 메시지를 어떻게 처리하느냐에 따라 프로그램의 안정성이 좌우됩니다.
오늘은 이 메시지의 흐름과 OnClose 함수의 역할까지 함께 알아보려 합니다.

이 글에서는 MFC에서 WM_CLOSE 메시지가 발생하는 시점과 이를 어떻게 활용해 자원 해제, 저장 처리, 사용자 확인 팝업 등을 구현할 수 있는지 자세히 설명드릴게요.
그리고 OnClose 함수 안에서 어떤 코드를 넣어야 예외 없이 깔끔한 종료가 가능한지도 예제를 통해 안내드립니다.
MFC 입문자뿐 아니라 기존 개발자 분들도 한 번쯤 점검해보면 좋을 내용이니 끝까지 함께 해주세요.







🧩 WM_CLOSE란 무엇인가요?

MFC에서 WM_CLOSE는 윈도우가 종료되기 직전에 발생하는 메시지입니다.
사용자가 창의 ‘X’ 버튼을 클릭하거나 Alt+F4를 눌렀을 때, 또는 PostMessage(WM_CLOSE)처럼 프로그래밍적으로 종료를 요청했을 때도 이 메시지가 생성됩니다.
즉, 애플리케이션이 닫히는 거의 모든 상황에서 이 메시지가 먼저 전달됩니다.

이 메시지를 통해 우리는 단순한 창 닫기 이상의 작업을 할 수 있습니다.
예를 들어, 사용자에게 저장 여부를 묻는 팝업을 띄운다거나, 열려 있는 파일을 닫고 메모리를 정리하는 등의 정리 과정을 수행할 수 있죠.
이러한 정리 작업을 하지 않으면 프로그램은 닫히더라도 백그라운드에서 자원이 누수되거나, 데이터가 손실될 위험이 있습니다.

💬 WM_CLOSE는 단순 종료 신호가 아닌, 애플리케이션에 마지막 인사를 건네는 중요한 메시지입니다.

MFC에서는 CWnd::OnClose()라는 가상 함수를 통해 이 메시지를 처리할 수 있도록 되어 있습니다.
이 함수는 WM_CLOSE 메시지가 발생했을 때 자동으로 호출되며, 우리가 원하는 종료 처리 코드를 이 함수 안에 작성할 수 있습니다.

💡 TIP: WM_CLOSE는 종료 전 사용자 동의, 로그 저장, 자원 해제 등의 안전한 종료 루틴을 작성할 수 있는 유일한 기회입니다.


🔍 MFC에서 WM_CLOSE의 전달 흐름

MFC 프로그램에서 WM_CLOSE 메시지는 여러 단계를 거쳐 흐르게 됩니다.
우선 사용자의 종료 요청이 감지되면 이 메시지는 운영체제에서 해당 윈도우로 전달되고, MFC의 메시지 매핑 시스템에 따라 CWnd::OnClose() 함수가 호출됩니다.

MFC는 내부적으로 윈도우 메시지를 처리하기 위한 메시지 맵(Message Map) 구조를 사용합니다.
이 구조는 메시지와 처리 함수를 연결하는 역할을 하며, ON_WM_CLOSE() 매크로를 통해 WM_CLOSE와 OnClose를 매핑하게 됩니다.

CODE BLOCK
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    ON_WM_CLOSE()
END_MESSAGE_MAP()

void CMyDialog::OnClose()
{
    // 종료 전 정리 코드 작성
    CDialogEx::OnClose(); // 기본 처리 호출
}

이처럼 WM_CLOSE는 메시지 → 메시지 맵 → OnClose 함수로 이어지는 명확한 경로를 따르며,
개발자는 OnClose 안에서 자원 해제, 사용자 확인, 로그 기록 등을 자유롭게 구현할 수 있습니다.

만약 CWinApp::OnClose처럼 최상위 애플리케이션 클래스가 아닌, 다이얼로그 또는 프레임 창에서 처리하고 싶다면 해당 클래스의 OnClose를 오버라이드하여 직접 정의하면 됩니다.
단, 기본 처리 함수 baseClass::OnClose() 호출을 잊지 않도록 주의하세요.

⚠️ 주의: OnClose 함수 안에서 DestroyWindow()를 호출하거나 기본 처리를 누락하면 창이 완전히 닫히지 않거나, 메모리 누수가 발생할 수 있습니다.







🧹 OnClose 함수에서 해야 할 작업

MFC에서 OnClose 함수는 프로그램 종료 전 마지막으로 실행되는 중요한 위치입니다.
여기서는 단순히 윈도우를 닫는 것 외에도 다음과 같은 필수 정리 작업을 수행할 수 있습니다.

  • 💾열려 있는 파일 저장 및 닫기
  • 🧠동적 할당된 메모리 해제
  • 📤사용자 설정 값 저장 (예: 윈도우 크기, 경로 등)
  • 🔌연결된 리소스 닫기 (예: 데이터베이스, 소켓 등)
  • 📝로그 기록 또는 상태 저장

위 항목을 무시하면 프로그램은 정상적으로 닫히더라도 사용자는 데이터 손실이나 오류 메시지를 경험할 수 있습니다.
특히 대화형 애플리케이션일수록 이러한 정리 작업은 필수입니다.

예를 들어 아래와 같은 코드는 다이얼로그 기반 프로그램에서 종료 전에 사용자 확인 메시지를 보여주고, 필요한 정리를 한 후 종료하도록 처리한 예입니다.

CODE BLOCK
void CMyDialog::OnClose()
{
    if (AfxMessageBox(_T("프로그램을 종료하시겠습니까?"), MB_YESNO | MB_ICONQUESTION) == IDYES)
    {
        // 예: 파일 닫기, 설정 저장, 리소스 정리 등
        SaveConfig();
        CloseOpenFiles();
        CDialogEx::OnClose(); // 기본 종료 처리 호출
    }
    // 아니오를 선택하면 창은 닫히지 않음
}

이처럼 OnClose는 종료 전 중요한 로직을 담는 핵심 구간이므로, 작성 시 예외 처리를 충분히 고려하는 것이 좋습니다.


📁 종료 전 자원 정리 실전 예제

MFC 애플리케이션에서는 다양한 자원을 동적으로 생성하고 사용하는 경우가 많습니다.
예를 들어 파일 핸들, 메모리 버퍼, 데이터베이스 연결, 네트워크 소켓 등이 이에 해당합니다.
이 자원들을 프로그램 종료 전 OnClose에서 안전하게 해제하지 않으면, 메모리 누수나 비정상 종료가 발생할 수 있습니다.

실제 종료 처리를 위한 정리 작업 코드는 다음과 같이 구성할 수 있습니다.
이 예제는 파일을 저장하고 메모리를 해제한 후, 로그를 남긴 다음 종료하는 절차를 보여줍니다.

CODE BLOCK
void CMyDialog::OnClose()
{
    // 1. 작업 파일 저장
    if (m_pFile && m_pFile->IsOpened())
    {
        m_pFile->WriteString(_T("종료 시점 데이터 저장"));
        m_pFile->Close();
        delete m_pFile;
        m_pFile = nullptr;
    }

    // 2. 동적 메모리 해제
    if (m_pBuffer)
    {
        delete[] m_pBuffer;
        m_pBuffer = nullptr;
    }

    // 3. 로그 기록
    WriteLog(_T("사용자가 종료 버튼을 클릭했습니다."));

    // 4. 기본 종료 처리
    CDialogEx::OnClose();
}

이처럼 종료 시점을 잘 활용하면 프로그램이 완전히 종료되더라도 필요한 기록은 남기고, 자원은 깨끗하게 해제하여 안정성과 신뢰성을 높일 수 있습니다.

💎 핵심 포인트:
OnClose 안에 포함되는 자원 정리 코드는 가능한 명확하고 예외에 강한 방식으로 구현해야 합니다.
파일, 메모리, 네트워크처럼 외부와 연결되는 요소는 반드시 nullptr 또는 안전한 상태로 초기화해주는 것이 좋습니다.







📌 사용자의 종료 확인 팝업 구현

종료 버튼을 눌렀을 때, 사용자에게 확인 창을 보여주는 것은 아주 기본적이지만 중요한 UX 요소입니다.
특히 아직 저장하지 않은 작업이 있을 경우, 사용자가 실수로 종료 버튼을 눌렀을 때 이를 막아줄 수 있는 역할을 하죠.

MFC에서는 AfxMessageBox 함수를 활용하여 간단하게 확인 팝업을 구현할 수 있습니다.
종료 요청이 들어왔을 때 사용자의 선택에 따라 종료 여부를 제어하는 구조를 만드는 것이 핵심입니다.

CODE BLOCK
void CMyDialog::OnClose()
{
    int nRet = AfxMessageBox(_T("저장하지 않은 변경사항이 있습니다.\n종료하시겠습니까?"),
                             MB_YESNO | MB_ICONWARNING);

    if (nRet == IDYES)
    {
        SaveCurrentData(); // 필요 시 저장
        CDialogEx::OnClose(); // 정상 종료
    }
    // 아니오 선택 시 종료되지 않음
}

위 코드처럼 메시지 박스를 활용하면 사용자의 실수를 막고, 중요한 데이터 손실을 방지할 수 있습니다.
이 방식은 다이얼로그 기반 프로그램뿐 아니라, 프레임 기반 응용프로그램에도 동일하게 적용 가능합니다.

💡 TIP: 종료 확인 팝업에 “다시 보지 않기” 체크박스를 추가하면 사용자 편의성을 높일 수 있으며, 설정 값은 레지스트리나 설정 파일에 저장해 유지할 수 있습니다.

이처럼 OnClose 함수에서 사용자 확인을 넣는 것은 단순한 기능 이상으로, 사용자 신뢰를 높이는 중요한 설계 요소 중 하나입니다.


자주 묻는 질문 (FAQ)

WM_CLOSE와 WM_DESTROY는 무엇이 다른가요?
WM_CLOSE는 사용자의 종료 요청에 반응해 애플리케이션이 닫히기 전에 호출되는 메시지이고, WM_DESTROY는 윈도우가 실제로 파괴될 때 발생하는 메시지입니다.
OnClose 함수는 꼭 구현해야 하나요?
필수는 아니지만, 종료 전 정리 작업이 필요한 경우에는 반드시 오버라이드하여 자원 해제, 저장, 사용자 확인 등의 처리를 구현해야 합니다.
OnClose에서 DestroyWindow를 직접 호출해도 되나요?
일반적으로는 기본 클래스의 OnClose를 호출하는 것이 안전합니다.
직접 DestroyWindow를 호출하면 메시지 순서가 꼬일 수 있어 예기치 못한 오류가 발생할 수 있습니다.
종료 팝업을 띄우는 타이밍은 언제가 좋을까요?
WM_CLOSE 메시지가 발생했을 때 OnClose 함수 안에서 사용자 확인 팝업을 띄우는 것이 가장 이상적입니다.
메시지 전달 흐름 상 가장 먼저 개입할 수 있는 위치이기 때문입니다.
WM_CLOSE는 어떤 방식으로 발생하나요?
사용자가 창의 X 버튼을 누르거나 Alt+F4 키를 누르면 시스템에서 자동으로 WM_CLOSE 메시지를 해당 윈도우에 보냅니다.
코드로 PostMessage를 통해 직접 보낼 수도 있습니다.
WM_QUIT과 WM_CLOSE는 어떤 관계인가요?
WM_CLOSE는 창 닫기 요청 메시지이고, WM_QUIT은 메시지 루프를 종료시켜 애플리케이션 전체를 마무리하는 메시지입니다.
일반적으로 WM_CLOSE에서 DestroyWindow가 호출되면 이후 WM_DESTROY, WM_QUIT 순으로 발생합니다.
모든 자원 정리를 OnClose에 넣는 게 좋을까요?
일반적인 자원 정리는 OnClose에서 처리하되, 메시지 루프와 관련된 최종 정리는 WM_DESTROY 또는 ExitInstance에서 수행하는 것이 더 안전합니다.
OnClose에서 예외 처리를 어떻게 해야 하나요?
파일 저장, 메모리 해제 등의 코드에는 예외 가능성이 있으므로 try-catch 문을 통해 오류에 안전하게 대응하도록 구현하는 것이 좋습니다.


🧠 WM_CLOSE 활용으로 안전한 종료 처리 마무리하기

MFC 환경에서 WM_CLOSE 메시지를 정확하게 이해하고 활용하는 것은 애플리케이션의 안정성과 신뢰도를 높이는 핵심입니다.
단순히 창을 닫는 기능에 그치지 않고, 종료 전 자원 정리, 사용자 확인, 로그 기록 등 다양한 마무리 작업을 통합할 수 있기 때문이죠.
특히 OnClose 함수 내에서 적절한 예외 처리와 조건 분기를 적용하면, 사용자 경험을 해치지 않으면서도 예외 상황에도 강한 프로그램을 만들 수 있습니다.

이번 글을 통해 WM_CLOSE의 의미와 흐름, OnClose 함수에서 해야 할 작업, 그리고 실전 예제와 팁까지 자세히 살펴보았는데요.
앞으로 여러분이 개발하는 MFC 애플리케이션에서도 보다 안전하고 전문적인 종료 처리를 구현하시길 바랍니다.
꼼꼼한 종료 처리는 디버깅 시간을 줄여주고, 사용자에게 신뢰를 줄 수 있는 중요한 기술입니다.


🏷️ 관련 태그:MFC, WM_CLOSE, OnClose, 메시지 처리, 종료 메시지, CWnd, 다이얼로그 종료, 메모리 해제, 예외 처리, 사용자 확인