안녕하세요.
새로운 업무를 하게 되면서, MFC를 사용해 보게 되었습니다.
요즘은 MFC를 많이 사용하지 않는 것 같아 자료들이 점점 줄어들고 있는 것 같습니다.
현장에서도 Qt 혹은 C#과 같이 상위 프레임워크로 넘어가는 것 같지만, 이미 개발된 현장에서는 유지 보수 등 이유로 아직 사용하는 것 같습니다.
MFC를 실무에서 사용해 본 적이 없어, 널널한 개발자 TV 님의 강의를 보면서 주소록 만들기를 따라 해 보았습니다.
기본 주소록 추가, 검색 기능과 간단하게 파일 저장, 불러오기 심화내용으로 기능을 추가해 보았습니다.
그리고 간단한 버그도 수정을 하였습니다.
기본 MFC로 만드는 내용은 아래의 강의를 참고하시면 도움이 될 것이라고 생각됩니다.
영상이 1시간이지만, 처음 배우시는 분에게는 하나씩 따라 해 보는 것이 도움이 될 것입니다.
아래 내용은 파일 저장할 때 사용한 내용을 추가로 남겨 보았습니다.
1. 직렬화(Serialization)
메모리 상에 존재하는 클래스 객체(데이터)를 파일로 저장하거나, 반대로 파일에서 데이터를 읽어와 객체로 복원하는 과정을 직렬화(Serialization)라고 합니다.
MFC는 이를 쉽게 구현할 수 있도록 CArchive라는 클래스와 함께 특정 매크로들을 제공하는데, 그 핵심적으로 DECLARE_SERIAL과 IMPLEMENT_SERIAL가 있습니다.
2. 구현 내용
기본 자료 구조체 헤더파일에 추가로
DECLARE_SERIAL(CUserData); 선언과
CUserData() {} 기본 생성자,
CObject의 Serialize 오버라이딩
이렇게 추가해 주었습니다.
직렬화에 필요한 함수들과 런타임 클래스 정보(CRuntimeClass)를 자동으로 선언해 줍니다.
#pragma once
#include <afx.h>
class CUserData :
public CObject
{
DECLARE_SERIAL(CUserData);
private:
CString m_strName;
CString m_strPhone;
public :
CUserData() {}
CUserData(CString _name, CString _phone) {
m_strName = _name;
m_strPhone = _phone;
}
CUserData(const CUserData& rhs) {
this->m_strName = rhs.m_strName;
this->m_strPhone = rhs.m_strPhone;
}
CString getName() { return m_strName; }
CString getPhone() { return m_strPhone; }
// CObject의 Serialize 오버라이딩
virtual void Serialize(CArchive& ar) {
CObject::Serialize(ar);
if (ar.IsStoring()) {
// 파일에 쓸 때 (저장)
ar << m_strName << m_strPhone;
}
else {
// 파일에서 읽을 때 (불러오기)
ar >> m_strName >> m_strPhone;
}
}
};
CUserData.cpp 파일에서는 아래와 같이 코드를 추가해 줍니다.
(Serialize() 함수 구현 부분은 cpp 파일 부분에서 해도 됩니다.)
// 형식: IMPLEMENT_SERIAL(클래스이름, 부모클래스이름, 버전번호)
IMPLEMENT_SERIAL(CUserData, CObject, 1)
IMPLEMENT_SERIAL(CUserData, CObject, 1)에 들어가는 3가지 인자의 의미는 다음과 같습니다.
1) CUserData (클래스 이름): 직렬화를 적용할 대상 클래스입니다.
2) CObject (부모 클래스 이름): MFC에서 직렬화를 하려면 대상 클래스가 반드시 CObject를 상속받아야 합니다. 부모의 기능을 이어받기 위해 명시합니다.
3) 1 (버전 번호 / Schema Number): 데이터 버전 관리자입니다.
버전을 2로 올리면, 코드에서 옛날 버전(1) 번호를 읽어서 기존 파일 구조와 달라 프로그램이 튕기는(Crashing) 것을 막아줄 수 있습니다. 아래는 참고할 수 있는 예제 부분이며, 첨부한 코드에는 추가하지 않았습니다.
// 1 버전을 2로 올려줍니다.
IMPLEMENT_SERIAL(CUserData, CObject, 2)
void CUserData::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring()) // 저장할 때 (언제나 최신 버전(2)으로 저장)
{
ar << m_strName << m_nAge << m_strPhone;
}
else // 읽어올 때 (옛날 파일과 새 파일 모두 대응)
{
// 2버전을 현재 읽어오려는 파일의 버전을 확인합니다.
UINT nVersion = ar.GetObjectSchema();
switch (nVersion)
{
case 1: // 옛날 프로그램(버전 1)으로 저장된 파일을 열었을 때
ar >> m_strName >> m_nAge;
m_strPhone = _T(""); // 새 변수는 기본값으로 초기화
break;
case 2: // 새 프로그램(버전 2)으로 저장된 파일을 열었을 때
ar >> m_strName >> m_nAge >> m_strPhone;
break;
default:
// 알 수 없는 미래의 버전일 때 예외 처리
AfxThrowArchiveException(CArchiveException::badSchema);
break;
}
}
}
3. 기타 구현 내용
CAddrBookDoc 클래스에서 LoadAddressBook()와 SaveAddressBook() 함수로 m_ptrList에 있는 내용을 파일로 저장하는 코드를 작성했습니다.
CAddrBookView 클래스에서 저장과 불러오기 메뉴를 선택했을 때 파일 다이얼로그를 불러와서 저장하고 불러오기 하는 함수를 호출하는 부분을 처리했습니다.
이 코드도 완벽한 것이 아니기 때문에 심화학습으로 이렇게 가능할 수 있구나 참고 정도가 되었으면 합니다.

감사합니다.
<참고 사이트>
https://youtu.be/l5iYOqyRWEU?list=PLXvgR_grOs1CyJDnWeUTqbmKG1VFQM72e
'Programming > C, C++,MFC' 카테고리의 다른 글
| [C++] std::list 기본 사용법 알아보기 (4) | 2025.07.29 |
|---|---|
| [C++] std::array 기본 사용법 알아보기 (1) | 2025.07.22 |
| [C++] const 객체를 선언 시 멤버 함수 에러 상황과 this 포인터 이해하기 (7) | 2025.07.18 |
| [C/C++] 구조체(struct) 기초 확인하기 (3) | 2025.07.12 |
| [C++] 람다(Lambda)가 없던 시절 사용했던 방법 알아보기 (2) | 2025.07.11 |