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 API | MFC | 비고 |
|---|---|---|---|
| 실행 속도 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | MFC는 래퍼 오버헤드 있음 (미미) |
| 메모리 사용량 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | MFC 런타임 라이브러리 필요 |
| 실행 파일 크기 | 50KB~ | 1~5MB | MFC 정적 링크 시 크기 증가 |
| 시작 시간 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 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의 차이점을 이해하고 프로젝트에 맞는 선택을 하는 데 도움이 되었기를 바랍니다!
다음 글에서는 실전 프로젝트를 다루겠습니다. 🚀