2 Serialize? #
CObject는 모든 MFC 클래스의 기본 부모 클래스입니다. 이 클래스 맴버 중에는 Serialize()라는 메소드가 선언되어있는데, 이 메소드는 현재 객체의 내용을 디스크나 다른 저장공간에 기록하거나 읽어들이는 역할을 수행합니다. 당연히 CObject로 부터 상속받은 객체들은 자신을 저장하는 수단으로 이 메소드를 사용하는 것이 좋겠죠.
당연히 CDocument도 예외는 아닐 겁니다. 맨처음 new메뉴에서 생성한 MFC 초기 프로젝트는 내부적으로 이미 save/load 부분을 메뉴에 추가해놓은 상태라고 보면 됩니다. 클래스 위저드를 열어서 Doc 클래스의 맴버함수를 보면 Serialize() 가 기본적으로 추가되어있는 것을 볼 수 있습니다. 이 맴버 함수의 내용을 잠깐 보면 다음과 같습니다.
void CMFCExam0Doc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
}
}
todo 주석을 참조하면 저장과 읽기 작업에 대한 코드를 넣으라는 것을 알 수 있습니다. 바로 이 부분에다가 CDocument(위 코드에서는 CMFCExam0Doc)의 내용을 디스크에 저장하고 읽는 코드를 넣어주면 됩니다.
3 그럼 Serialize()는 언제 호출되는가? #
이것은 MFC 내부적으로 숨겨져있습니다. MFC 프레임워크 내부에서는 기본적으로 ID_FILE_NEW, ID_FILE_SAVE, ID_FILE_OPEN, ID_FILE_SAVE_AS라고 하는 메뉴항목이 첨가되어있는데, 이것은 왠만한 프로그램들이라면 모두 가지고 있는 메뉴겠지요. 각 항목을 선택하면 다음과 같은 순서대로 메소드들이 실행됩니다. (몰론 기본적으로 코드에서는 보이지 않습니다.)
위 '기본' 실행이 맘에 안든다면 각각의 맴버 함수를 오버라이딩하면 됩니다. 위 실행순서를 보면, 화일을 열거나 저장할때는 맨 마지막에 Serialize()가 호출되는 것을 볼 수 있습니다.
| ID_FILE_NEW | CWinApp::OnFileNew() → 신규 CDocument 객체 생성 → CDocument::OnNewDocument() |
| ID_FILE_OPEN | CWinApp::OnFileOpen() → 신규 CDocument 객체 생성 → CDocument::OnOpenDocument() → CDocument::Serialize() |
| ID_FILE_SAVE | CWinApp::OnFileSave() → CDocument::OnSaveDocument() → CDocument::Serialize() |
| ID_FILE_SAVE_AS | CWinApp::OnFileSaveAs() → 새로운 화일명 입력 → CDocument::OnNewDocument() |
위 '기본' 실행이 맘에 안든다면 각각의 맴버 함수를 오버라이딩하면 됩니다. 위 실행순서를 보면, 화일을 열거나 저장할때는 맨 마지막에 Serialize()가 호출되는 것을 볼 수 있습니다.
4 CArchive? #
앞서 보여주었던 Serialize() 메소드에서 인자로 CArchive라는 객체의 참조가 넘겨져오는 것을 볼 수 있습니다. 이것은 저장하거나 읽어들일 화일 그 자체를 나타냅니다. CArchive는 고맙게도 기존의 ANSI C에서 사용했던 함수와 매치되는 메소드를 가지고 있습니다. 다음 표를 참조하세요. (인자는 생략했습니다. MSDN 참조하시길...)
다음 코드는 CMFCExam0Doc에 m_Str1, m_Str2이라는 STL string 맴버 변수가 있다고 가정하고 만든 예제코드입니다.
| CArchive | ANSI C |
| Read() | fread() |
| Write() | fwrite() |
| ReadString() | fgets() |
| ReadString() | fputs() |
다음 코드는 CMFCExam0Doc에 m_Str1, m_Str2이라는 STL string 맴버 변수가 있다고 가정하고 만든 예제코드입니다.
void CMFCExam0Doc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
ar.WriteString(m_Str1.c_str());
ar.WriteString("\n");
ar.WriteString(m_Str2.c_str());
ar.WriteString("\n");
}
else
{
// TODO: add loading code here
CString tmpStr;
ar.ReadString(tmpStr);
m_Str1 = string((LPCTSTR)tmpStr);
ar.ReadString(tmpStr);
m_Str2 = string((LPCTSTR)tmpStr);
}
}
위 예제에서 m_Str1, m_Str2의 순서대로 적고 있음에 주의하시기 바랍니다. 일반 fread/fwrite 함수 사용과 거의 같다는 것을 알 수 있습니다. 만일 CArchive 객체를 사용하는 것이 영 그렇다고 느끼시는 분(일반적인 C 코딩에 익숙하신 분이나 자신의 라이브러리에서 이미 화일 저장함수가 만들어져 있을 경우)는 CDocument::OnOpenDocument()/CDocument::OnSaveDocument()를 오버라이딩해서 코드를 작성하시면 됩니다. (기본적으로 부모의 매소드를 다시한번 호출하는 것을 볼 수 있는데, 이는 삭제해주시면 됩니다. 왜냐하면 Serialize()를 자동으로 동작시키는 부분이 여기거든요.)
5 주의사항 #
- CArchive는 단방향으로밖에 읽을 수 없습니다. 즉, 한번 읽어들였으면 fseek()와 같은 함수로 되감기를 할 수 없다는 뜻입니다. 이런 처리가 필요하다면 CDocument::OnOpenDocument() / CDocument::OnSaveDocument()를 오버라이딩해서 직접 화일 입출력을 하는 방법을 택해야합니다.
- EOF 채크방법은 입출력 처리시 Read(), Write()함수는 지정한 버퍼보다 적게 읽혀졌을 경우, ReadString(), WriteString()은 반환값이 NULL일 경우를 채크하시면 됩니다.








![[http]](/wiki/imgs/http.png)
