Programming/C, C++,MFC

[MFC] MFC 연습을 위한 초 간단한 주소록 만들어보기

변화의 물결1 2026. 6. 2. 10:55

 

안녕하세요.

  

 새로운 업무를 하게 되면서, 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    

 

AddrBook.zip
0.15MB

반응형