본문으로 건너뛰기
Previous
Next
MFC vs Win32 API | 차이점과 선택 가이드

MFC vs Win32 API | 차이점과 선택 가이드

MFC vs Win32 API | 차이점과 선택 가이드

이 글의 핵심

MFC는 Win32 API를 C++ 클래스로 감싼 프레임워크입니다. MFC는 생산성이 높지만 실행 파일이 크고, Win32 API는 저수준 제어가 가능하지만 코드가 복잡합니다. 프로젝트 요구사항에 따라 선택해야 합니다.

들어가며

MFC(Microsoft Foundation Class)는 Win32 API를 C++ 클래스로 감싼 프레임워크입니다. 1990년대부터 2000년대까지 Windows 애플리케이션 개발의 주류였습니다. 이 글에서는 MFC와 Win32 API의 차이점, 장단점, 선택 기준을 비교합니다.


1. 코드 복잡도 비교

1.1 간단한 윈도우 생성

Win32 API

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    // 윈도우 클래스 등록
    WNDCLASS wc = {};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"MyWindowClass";
    RegisterClass(&wc);
    
    // 윈도우 생성
    HWND hwnd = CreateWindowEx(
        0,
        L"MyWindowClass",
        L"My Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );
    
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    
    // 메시지 루프
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return 0;
}

MFC

#include <afxwin.h>

class CMyFrameWnd : public CFrameWnd
{
public:
    CMyFrameWnd()
    {
        Create(NULL, _T("My Window"));
    }
};

class CMyApp : public CWinApp
{
public:
    virtual BOOL InitInstance()
    {
        m_pMainWnd = new CMyFrameWnd;
        m_pMainWnd->ShowWindow(m_nCmdShow);
        m_pMainWnd->UpdateWindow();
        return TRUE;
    }
};

CMyApp theApp;

결과: MFC가 훨씬 간결합니다. 윈도우 클래스 등록, 메시지 루프가 자동 처리됩니다.


2. 컨트롤 생성 비교

2.1 버튼 추가

Win32 API

HWND hButton = CreateWindow(
    L"BUTTON",
    L"클릭",
    WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
    10, 10, 100, 30,
    hwnd,
    (HMENU)IDC_BUTTON,
    hInstance,
    NULL
);

// 메시지 처리
case WM_COMMAND:
    if (LOWORD(wParam) == IDC_BUTTON) {
        MessageBox(hwnd, L"버튼 클릭!", L"알림", MB_OK);
    }
    break;

MFC

class CMyDialog : public CDialog
{
protected:
    CButton m_button;
    
    virtual BOOL OnInitDialog()
    {
        CDialog::OnInitDialog();
        
        m_button.Create(_T("클릭"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            CRect(10, 10, 110, 40), this, IDC_BUTTON);
        
        return TRUE;
    }
    
    DECLARE_MESSAGE_MAP()
    
    afx_msg void OnBnClickedButton()
    {
        AfxMessageBox(_T("버튼 클릭!"));
    }
};

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_BN_CLICKED(IDC_BUTTON, &CMyDialog::OnBnClickedButton)
END_MESSAGE_MAP()

결과: MFC는 객체 지향 방식으로 더 직관적입니다.


3. 메모리 관리 비교

3.1 동적 할당

Win32 API

// 수동 메모리 관리
HWND hwnd = CreateWindow(...);
// ...
DestroyWindow(hwnd);

HBITMAP hBitmap = LoadBitmap(...);
// ...
DeleteObject(hBitmap);

HDC hdc = GetDC(hwnd);
// ...
ReleaseDC(hwnd, hdc);

MFC

// 자동 메모리 관리
CWnd* pWnd = new CWnd;
pWnd->Create(...);
// 소멸자에서 자동 정리

CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
// 소멸자에서 자동 DeleteObject

CDC* pDC = GetDC();
// ReleaseDC 자동 호출

결과: MFC는 RAII로 메모리 누수 위험이 적습니다.


4. 생산성 비교

4.1 다이얼로그 + DDX

Win32 API

case WM_COMMAND:
    if (LOWORD(wParam) == IDOK) {
        char name[256];
        GetDlgItemTextA(hwnd, IDC_EDIT_NAME, name, 256);
        
        char ageStr[16];
        GetDlgItemTextA(hwnd, IDC_EDIT_AGE, ageStr, 16);
        int age = atoi(ageStr);
        
        // 검증
        if (strlen(name) == 0) {
            MessageBoxA(hwnd, "이름을 입력하세요!", "오류", MB_ICONERROR);
            return TRUE;
        }
        if (age < 0 || age > 150) {
            MessageBoxA(hwnd, "나이가 올바르지 않습니다!", "오류", MB_ICONERROR);
            return TRUE;
        }
        
        // 데이터 사용...
    }
    break;

MFC (DDX/DDV)

class CMyDialog : public CDialog
{
public:
    CString m_strName;
    int m_nAge;
    
protected:
    virtual void DoDataExchange(CDataExchange* pDX)
    {
        CDialog::DoDataExchange(pDX);
        
        DDX_Text(pDX, IDC_EDIT_NAME, m_strName);
        DDV_MaxChars(pDX, m_strName, 50);
        
        DDX_Text(pDX, IDC_EDIT_AGE, m_nAge);
        DDV_MinMaxInt(pDX, m_nAge, 0, 150);
    }
    
    virtual void OnOK()
    {
        if (!UpdateData(TRUE)) {
            return;  // DDV 실패
        }
        
        // m_strName, m_nAge 사용...
        
        CDialog::OnOK();
    }
};

결과: MFC DDX/DDV로 데이터 교환과 검증이 자동화됩니다.


5. 성능 비교

항목Win32 APIMFC비고
실행 속도⭐⭐⭐⭐⭐⭐⭐⭐⭐MFC는 래퍼 오버헤드 있음 (미미)
메모리 사용량⭐⭐⭐⭐⭐⭐⭐⭐MFC 런타임 라이브러리 필요
실행 파일 크기50KB~1~5MBMFC 정적 링크 시 크기 증가
시작 시간⭐⭐⭐⭐⭐⭐⭐⭐⭐MFC 초기화 오버헤드

벤치마크 예시 (간단한 윈도우 생성 + 메시지 처리)

Win32 API:  50KB, 시작 시간 0.05초
MFC (정적): 2MB, 시작 시간 0.1초
MFC (동적): 100KB + mfc140.dll(2MB), 시작 시간 0.08초

6. 배포 및 의존성

6.1 Win32 API

장점:
- 실행 파일만 배포 (추가 DLL 불필요)
- 모든 Windows 버전에서 호환
- 크기가 작음 (임베디드 환경 적합)

단점:
- 없음 (Windows 기본 API)

6.2 MFC

정적 링크:
- 실행 파일에 MFC 포함 (1~5MB)
- 추가 DLL 불필요
- 라이선스 확인 필요

동적 링크:
- 실행 파일 작음 (100KB~)
- mfc140.dll 필요 (2MB)
- Visual C++ Redistributable 설치 필요

7. 학습 곡선

7.1 Win32 API

초급: 메시지 루프, 윈도우 프로시저 이해
중급: 컨트롤 생성, 리소스 관리
고급: GDI, 멀티스레딩, 소켓

난이도: ⭐⭐⭐⭐⭐

7.2 MFC

초급: CWinApp, CFrameWnd 기본 구조
중급: 다이얼로그, DDX/DDV, 컨트롤
고급: Document/View, 멀티스레딩

난이도: ⭐⭐⭐

결과: MFC가 진입 장벽이 낮습니다.


8. 프로젝트 유형별 선택 가이드

8.1 Win32 API 추천

✅ 임베디드/IoT 디바이스 (작은 실행 파일 필요)
✅ 시스템 유틸리티 (빠른 시작 속도)
✅ 드라이버 레벨 도구
✅ 저수준 제어가 필요한 경우
✅ 크로스 플랫폼 포팅 계획 (MFC는 Windows 전용)

8.2 MFC 추천

✅ 빠른 개발이 필요한 경우
✅ 복잡한 UI (다이얼로그, 컨트롤 많음)
✅ 레거시 MFC 프로젝트 유지보수
✅ Document/View 아키텍처 활용
✅ Visual Studio 리소스 에디터 사용

9. 혼합 사용

MFC와 Win32 API는 함께 사용할 수 있습니다.

// MFC 다이얼로그에서 Win32 API 호출
void CMyDialog::OnBnClickedButton()
{
    // Win32 API 직접 호출
    HWND hwnd = m_hWnd;
    SetWindowText(hwnd, _T("변경됨"));
    
    // 또는
    ::MessageBox(NULL, _T("Win32 API"), _T("알림"), MB_OK);
}

// Win32 API에서 MFC 클래스 사용
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_LBUTTONDOWN) {
        // MFC 클래스 사용
        CString str = _T("Hello, MFC!");
        ::MessageBox(hwnd, str, _T("알림"), MB_OK);
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

10. 실전 선택 시나리오

시나리오 1: 파일 암호화 도구

요구사항:
- 실행 파일 크기 최소화 (USB 배포)
- 빠른 시작 속도
- 간단한 UI (진행률 바, 버튼 2개)

추천: Win32 API
이유: 50KB 실행 파일, 빠른 시작

시나리오 2: 사내 업무 관리 시스템

요구사항:
- 복잡한 UI (여러 다이얼로그, 탭, 리스트뷰)
- 빠른 개발 (2개월 내 완성)
- 데이터베이스 연동

추천: MFC
이유: DDX/DDV, 리소스 에디터, 생산성 높음

시나리오 3: 하드웨어 모니터링 도구

요구사항:
- 실시간 성능 (CPU/메모리 모니터링)
- 작은 메모리 사용량
- 시스템 트레이 상주

추천: Win32 API
이유: 저수준 제어, 작은 메모리 footprint

11. 현대적 대안

2020년대에는 다음 기술이 주류입니다:

.NET (WinForms, WPF):
- C# 사용
- XAML UI
- 생산성 극대화

Qt:
- C++ 크로스 플랫폼
- 현대적 UI
- MFC보다 활발한 커뮤니티

Electron:
- JavaScript/HTML/CSS
- 크로스 플랫폼
- 웹 기술 활용

하지만 레거시 유지보수임베디드 환경에서는 여전히 Win32 API와 MFC가 사용됩니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • MFC 기초 | Microsoft Foundation Class 시작 가이드
  • Windows API 기초 | 메시지 루프와 윈도우 프로시저
  • C++ Best Practices | 모범 사례 완벽 가이드

이 글이 도움이 되셨나요? MFC와 Win32 API의 차이점을 이해하고 프로젝트에 맞는 선택을 하는 데 도움이 되었기를 바랍니다!

다음 글에서는 실전 프로젝트를 다루겠습니다. 🚀