기존 시스템 분석
기존 주소록 관리 프로그램은 C++로 구현되었으나, 고정 크기 배열(MAX = 1000)을 사용하여 확장성이 부족했으며, 입력 검증이 미흡해 비정상 종료 위험이 존재했습니다. 또한 system("cls") 및 system("pause")를 사용해 Windows에 종속적이었고, 데이터가 메모리에만 저장되어 프로그램 종료 시 모든 정보가 소실되었습니다. 특히 이름 기반 검색/삭제 방식은 동명이인 처리에 한계가 있었습니다.
핵심 개선 사항
- 동적 메모리 관리:
std::vector<Person>로 전환하여 용량 제한 해소 - 입력 검증 강화: 나이(0~150), 성별(1/2), 공백 포함 문자열 처리를 위한
getline()적용 - 데이터 영속성: 텍스트 파일(
contacts.txt)을 통한 자동 저장/복원 기능 추가 - 크로스 플랫폼 호환성:
system()대신 표준 라이브러리 기반의 화면 정리 및 일시정지 구현 - 동명이인 처리: 이름으로 검색 시 인덱스 목록 제공 및 사용자 선택 메커니즘 도입
- 부분 수정 기능: 필드별 선택적 업데이트 지원 (빈 입력 시 기존 값 유지)
재구성된 코드 구조
헤더 파일 (addressbook.h)
#pragma once
#include <string>
#include <vector>
enum class Sex { Male = 1, Female = 2 };
struct Contact {
std::string name;
Sex gender;
int age;
std::string phoneNumber;
std::string homeAddress;
};
class ContactManager {
private:
std::vector<Contact> entries;
const std::string dataFile = "contacts.txt";
Sex promptGender() const;
std::vector<size_t> searchByName(const std::string& target) const;
public:
ContactManager();
~ContactManager();
void createEntry();
void listAll() const;
void removeEntry();
void searchEntry() const;
void updateEntry();
void eraseAll();
void loadEntries();
void persistEntries() const;
static void waitForInput();
static void refreshDisplay();
};
구현 파일 (addressbook.cpp)
#include "addressbook.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <limits>
using namespace std;
ContactManager::ContactManager() { loadEntries(); }
ContactManager::~ContactManager() { persistEntries(); }
Sex ContactManager::promptGender() const {
int input;
while (true) {
cout << "성별 (1=남, 2=여): ";
cin >> input;
if (!cin.fail() && (input == 1 || input == 2)) {
return static_cast<Sex>(input);
}
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "유효하지 않은 입력입니다." << endl;
}
}
vector<size_t> ContactManager::searchByName(const string& target) const {
vector<size_t> matches;
for (size_t i = 0; i < entries.size(); ++i) {
if (entries[i].name == target) matches.push_back(i);
}
return matches;
}
void ContactManager::createEntry() {
Contact newContact;
cout << "이름: ";
cin.ignore();
getline(cin, newContact.name);
newContact.gender = promptGender();
cout << "나이 (0-150): ";
while (!(cin >> newContact.age) || newContact.age < 0 || newContact.age > 150) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "유효한 나이를 입력하세요: ";
}
cout << "전화번호: ";
cin.ignore();
getline(cin, newContact.phoneNumber);
cout << "주소: ";
getline(cin, newContact.homeAddress);
entries.push_back(newContact);
cout << "등록 완료!" << endl;
waitForInput();
refreshDisplay();
}
void ContactManager::listAll() const {
if (entries.empty()) {
cout << "등록된 연락처가 없습니다." << endl;
} else {
for (size_t i = 0; i < entries.size(); ++i) {
const auto& c = entries[i];
cout << "[" << i << "] "
<< c.name << " | "
<< (c.gender == Sex::Male ? "남" : "여") << " | "
<< c.age << "세 | "
<< c.phoneNumber << " | "
<< c.homeAddress << endl;
}
}
waitForInput();
refreshDisplay();
}
// 삭제/검색/수정 함수는 동명이인 처리 로직 포함
// 파일 입출력은 각 필드를 별도 줄로 저장
void ContactManager::persistEntries() const {
ofstream output(dataFile);
for (const auto& contact : entries) {
output << contact.name << '\n'
<< static_cast<int>(contact.gender) << '\n'
<< contact.age << '\n'
<< contact.phoneNumber << '\n'
<< contact.homeAddress << '\n';
}
}
void ContactManager::waitForInput() {
cout << "\n계속하려면 Enter 키를 누르세요...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cin.get();
}
void ContactManager::refreshDisplay() {
cout << string(50, '\n'); // 간이 화면 정리
}
메인 함수 (main.cpp)
#include "addressbook.h"
#include <iostream>
using namespace std;
void displayMenu() {
cout << "\n========== 연락처 관리 시스템 ==========\n"
<< "1. 새 연락처 등록\n"
<< "2. 전체 목록 보기\n"
<< "3. 연락처 삭제\n"
<< "4. 연락처 검색\n"
<< "5. 정보 수정\n"
<< "6. 전체 삭제\n"
<< "0. 종료\n"
<< "====================================\n";
}
int main() {
ContactManager manager;
int option;
while (true) {
displayMenu();
cout << "선택: ";
cin >> option;
if (cin.fail()) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "숫자만 입력 가능합니다.\n";
continue;
}
switch (option) {
case 1: manager.createEntry(); break;
case 2: manager.listAll(); break;
case 3: manager.removeEntry(); break;
case 4: manager.searchEntry(); break;
case 5: manager.updateEntry(); break;
case 6: manager.eraseAll(); break;
case 0:
cout << "프로그램을 종료합니다.\n";
return 0;
default:
cout << "0~6 사이의 숫자를 입력하세요.\n";
}
}
}
주요 기술적 개선점
코드는 클래스 기반 설계로 재구성되어 캡슐화를 달성했으며, const 올바른 사용과 명확한 함수 네이밍(isExit → searchByName)으로 가독성을 향상시켰습니다. 삭제 로직에서 벡터의 erase() 메서드를 사용해 메모리 안전성을 확보했고, 파일 입출력 시 각 필드를 별도 줄로 저장함으로써 파싱 오류를 최소화했습니다. 특히 동명이인 처리를 위해 검색 결과 인덱스 목록을 반환하고 사용자에게 선택지를 제공하는 방식으로 실제 사용성 문제를 해결했습니다.