이번 과제에서는 아래 3가지 정도는 항상 고려하면서 진행하는걸 권장 드립니다.
- 어떤 의도로 코드를 구현했는지
- 수정을 해야 한다면, 기존 코드의 수정을 최소화 하면서 수정할 수 있는 방안이 있는지
- 객체지향 프로그래밍 언어라면 SOLID 원칙을 잘 준수하고 있는지
이번에 해볼 과제는 다음과 같은 순서로 진행 됩니다.
- 제공된 연금술 공방의 물약 관리 프로그램 코드를 분석
- 제공된 코드를 활용해서 간단한 물약 이름 검색 기능을 추가한다.
- 물약 지급/공병 반환 기능(도전 기능)을 추가한다.
기본적으로 제공 되는 물약 관리 프로그램 코드는 아래와 같습니다.
#include <iostream>
#include <vector>
#include <string>
// PotionRecipe 클래스: 재료 목록을 vector<string>으로 변경
class PotionRecipe {
public:
std::string potionName;
std::vector<std::string> ingredients; // 단일 재료에서 재료 '목록'으로 변경
// 생성자: 재료 목록을 받아 초기화하도록 수정
PotionRecipe(const std::string& name, const std::vector<std::string>& ingredients)
: potionName(name), ingredients(ingredients) {}
};
// AlchemyWorkshop 클래스: 레시피 목록을 관리
class AlchemyWorkshop {
private:
std::vector<PotionRecipe> recipes;
public:
// addRecipe 메서드: 재료 목록(vector)을 매개변수로 받도록 수정
void addRecipe(const std::string& name, const std::vector<std::string>& ingredients) {
recipes.push_back(PotionRecipe(name, ingredients));
std::cout << ">> 새로운 레시피 '" << name << "'이(가) 추가되었습니다." << std::endl;
}
// 모든 레시피 출력 메서드
void displayAllRecipes() const {
if (recipes.empty()) {
std::cout << "아직 등록된 레시피가 없습니다." << std::endl;
return;
}
std::cout << "\n--- [ 전체 레시피 목록 ] ---" << std::endl;
for (size_t i = 0; i < recipes.size(); ++i) {
std::cout << "- 물약 이름: " << recipes[i].potionName << std::endl;
std::cout << " > 필요 재료: ";
// 재료 목록을 순회하며 출력
for (size_t j = 0; j < recipes[i].ingredients.size(); ++j) {
std::cout << recipes[i].ingredients[j];
// 마지막 재료가 아니면 쉼표로 구분
if (j < recipes[i].ingredients.size() - 1) {
std::cout << ", ";
}
}
std::cout << std::endl;
}
std::cout << "---------------------------\n";
}
};
int main() {
AlchemyWorkshop myWorkshop;
while (true) {
std::cout << "⚗️ 연금술 공방 관리 시스템" << std::endl;
std::cout << "1. 레시피 추가" << std::endl;
std::cout << "2. 모든 레시피 출력" << std::endl;
std::cout << "3. 종료" << std::endl;
std::cout << "선택: ";
int choice;
std::cin >> choice;
if (std::cin.fail()) {
std::cout << "잘못된 입력입니다. 숫자를 입력해주세요." << std::endl;
std::cin.clear();
std::cin.ignore(10000, '\n');
continue;
}
if (choice == 1) {
std::string name;
std::cout << "물약 이름: ";
std::cin.ignore(10000, '\n');
std::getline(std::cin, name);
// 여러 재료를 입력받기 위한 로직
std::vector<std::string> ingredients_input;
std::string ingredient;
std::cout << "필요한 재료들을 입력하세요. (입력 완료 시 '끝' 입력)" << std::endl;
while (true) {
std::cout << "재료 입력: ";
std::getline(std::cin, ingredient);
// 사용자가 '끝'을 입력하면 재료 입력 종료
if (ingredient == "끝") {
break;
}
ingredients_input.push_back(ingredient);
}
// 입력받은 재료가 하나 이상 있을 때만 레시피 추가
if (!ingredients_input.empty()) {
myWorkshop.addRecipe(name, ingredients_input);
} else {
std::cout << ">> 재료가 입력되지 않아 레시피 추가를 취소합니다." << std::endl;
}
} else if (choice == 2) {
myWorkshop.displayAllRecipes();
} else if (choice == 3) {
std::cout << "공방 문을 닫습니다..." << std::endl;
break;
} else {
std::cout << "잘못된 선택입니다. 다시 시도하세요." << std::endl;
}
}
return 0;
}
필수 기능
기존 연금술 공방 관리 시스템에 검색 기능을 추가해주세요.
- 물약 이름으로 검색이 가능해야 합니다.
- 재료로 검색이 가능해야 합니다. 특정 재료가 포함된 모든 레시피를 찾을 수 있어야 합니다.
- 물약 이름이 동일한 경우는 없다고 가정합니다.
필수 기능 구현 완료 후 AlchemyWorkshop 클래스에 검색 관련 메서드가 추가된 전체적인 구조를 그리면 아래와 같습니다.
어디까지나 참조일 뿐 꼭 이대로 구현할 필요는 없습니다.
우선 주어진 코드를 클래스별로 헤더, 소스 파일을 나눴다.
헤더에는 선언만, 소스에서 기능 구현 부분을 작성했다. 위가 헤더 파일, 아래가 소스 파일이다.
PotionRecipe
#pragma once
#include <vector>
#include <string>
// PotionRecipe 클래스: 재료 목록을 vector<string>으로 변경
class PotionRecipe
{
public:
std::string potionName;
std::vector<std::string> ingredients; // 단일 재료에서 재료 '목록'으로 변경
// 생성자: 재료 목록을 받아 초기화하도록 수정
PotionRecipe(const std::string& name, const std::vector<std::string>& ingredients);
};
#include "PotionRecipe.h"
#include <vector>
#include <string>
using namespace std;
PotionRecipe::PotionRecipe(const string& name, const vector<string>& ingredients)
: potionName(name), ingredients(ingredients)
{
}
따로 추가한 코드는 없다. 과제에서 주어진 코드를 나누기만 함.
AlchemyWorkshop
#pragma once
#include "PotionRecipe.h"
#include <vector>
#include <string>
// AlchemyWorkshop 클래스: 레시피 목록을 관리
class AlchemyWorkshop
{
private:
std::vector<PotionRecipe> recipes;
public:
// addRecipe 메서드: 재료 목록(vector)을 매개변수로 받도록 수정
void addRecipe(const std::string& name, const std::vector<std::string>& ingredients);
// 모든 레시피 출력 메서드
void displayAllRecipes() const;
// 아래 두 함수가 이름, 재료로 검색하기 위한 함수 - 필수 기능
void searchRecipeByName(std::string name);
void searchRecipeByIngredient(std::string ingredient);
};
#include "AlchemyWorkshop.h"
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// PotionRecipe 객체를 원소로 갖는 recipes 벡터
void AlchemyWorkshop::addRecipe(const std::string& name, const std::vector<std::string>& ingredients)
{
recipes.push_back(PotionRecipe(name, ingredients));
cout << ">> 새로운 레시피 '" << name << "'이(가) 추가되었습니다." << endl;
}
void AlchemyWorkshop::displayAllRecipes() const
{
if (recipes.empty())
{
cout << "아직 등록된 레시피가 없습니다." << endl;
return;
}
cout << "--- [ 전체 레시피 목록 ] ---" << endl;
for (size_t i = 0; i < recipes.size(); ++i)
{
cout << "- 물약 이름: " << recipes[i].potionName << endl;
cout << " > 필요 재료: ";
// 재료 목록을 순회하며 출력
for (size_t j = 0; j < recipes[i].ingredients.size(); ++j)
{
cout << recipes[i].ingredients[j];
// 마지막 재료가 아니면 쉼표로 구분
if (j < recipes[i].ingredients.size() - 1)
{
cout << ", ";
}
}
cout << endl;
}
}
// 필수 기능 - 이름으로 검색 기능 추가하기
void AlchemyWorkshop::searchRecipeByName(std::string name)
{
for (size_t i = 0; i < recipes.size(); ++i)
{
if (recipes[i].potionName == name) // recipes에 있는 PotionRacipe의 potionName과 입력받은 name의 값이 같은지 판단
{
cout << name << "을(를) 찾았습니다." << endl; // 같으면 물약 찾았다고 출력
cout << name << "에 필요한 재료: ";
for (size_t j = 0; j < recipes[i].ingredients.size(); ++j) // 찾은 물약에 필요한 재료 찾기
{
if (j != recipes[i].ingredients.size() - 1) // 마지막 재료 출력에는 쉼표 출력하지 않기
{
cout << recipes[i].ingredients[j] << ", ";
}
else
{
cout << recipes[i].ingredients[j];
}
}
cout << endl;
return; // 검색한 포션을 찾으면 함수 종료
}
}
cout << "해당 이름의 물약을 찾을 수 없습니다." << endl;
}
// 필수 기능 - 재료로 검색 기능 추가하기 포함된 재료가 있는 레시피는 모두 불러오기
void AlchemyWorkshop::searchRecipeByIngredient(std::string ingredient)
{
for (size_t i = 0; i < recipes.size(); ++i) // recipes의 사이즈(현재 들어있는 물약만큼 반복)
{
auto findit = find(recipes[i].ingredients.begin(), recipes[i].ingredients.end(), ingredient); // 재료 찾기
if (findit != recipes[i].ingredients.end()) // 검색한 재료가 들어있는 물약을 찾았으면
{
cout << ingredient << "이(가) 포함된 물약: " << recipes[i].potionName << endl;
cout << "레시피: ";
for (size_t j = 0; j < recipes[i].ingredients.size(); ++j) // 마지막 원소에 쉼표 제거
{
if (j != recipes[i].ingredients.size() - 1)
{
cout << recipes[i].ingredients[j] << ", ";
}
else
{
cout << recipes[i].ingredients[j];
}
}
cout << endl;
}
}
}
addRecipe과 displayAllRecipes 함수는 건들지 않았다.
아래 searchRecipeByName - 이름으로 물약을 검색하고 그 물약의 레시피를 출력하는 함수
searchRecipeByIngredient - 재료를 검색하고 검색한 재료가 있는 모든 물약의 레시피를 출력하는 함수를 구현했다.
main
#include "AlchemyWorkshop.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
AlchemyWorkshop myWorkshop;
while (true)
{
cout << "--------------------" << endl;
cout << "연금술 공방 관리 시스템" << endl;
cout << "1. 레시피 추가" << endl;
cout << "2. 모든 레시피 출력" << endl;
cout << "3. 레시피 이름으로 검색" << endl; // 3번에 이름 검색 추가
cout << "4. 레시피 재료로 검색" << endl; // 4번에 재료 검색 추가
cout << "5. 종료" << endl;
cout << "선택: ";
int choice;
cin >> choice; // 사용자에게 입력 값 받음.
if (cin.fail()) // 입력받아야 할 자료형과 다른 형식이 입력됐을 때 fail이 true가 됨.
{
cout << "잘못된 입력입니다. 숫자를 입력해주세요." << endl;
cin.clear(); // clear는 입력 스트림의 오류 상태를 초기화 하는 함수.
cin.ignore(10000, '\n'); // ignore은 입력 버퍼를 초기화 시키는 함수. 10000글자까지 \n전 까지 초기화시킴.
continue; // 반복문의 처음으로
}
if (choice == 1)
{
string name;
cout << "물약 이름: ";
cin.ignore(10000, '\n');
getline(cin, name); // 사용자가 엔터를 칠때까지 한 줄 단위로 입력을 받음
// 여러 재료를 입력받기 위한 로직
vector<string> ingredients_input; // 입력받은 재료를 넣기 위한 벡터
string ingredient;
cout << "필요한 재료들을 입력하세요. (입력 완료 시 '끝' 입력)" << endl;
while (true)
{
cout << "재료 입력: ";
getline(cin, ingredient); // 재료 입력
// 사용자가 '끝'을 입력하면 재료 입력 종료
if (ingredient == "끝")
{
break;
}
ingredients_input.push_back(ingredient);
}
// 입력받은 재료가 하나 이상 있을 때만 레시피 추가
if (!ingredients_input.empty()) // 벡터가 비어있지 않을 때 true
{
myWorkshop.addRecipe(name, ingredients_input);
}
else
{
cout << ">> 재료가 입력되지 않아 레시피 추가를 취소합니다." << endl;
}
}
else if (choice == 2)
{
myWorkshop.displayAllRecipes();
}
else if (choice == 3) // 이름 검색 추가부분
{
string findpotion; // 검색할 이름을 입력받기 위한 문자열 변수 선언
cout << "검색할 물약의 이름을 입력하세요: ";
cin.ignore(10000, '\n');
cin >> findpotion;
myWorkshop.searchRecipeByName(findpotion);
}
else if (choice == 4) // 재료 검색 추가부분
{
string findingredient; // 검색할 재료를 입력받기 위한 문자열 변수 선언
cout << "검색할 재료를 입력하세요: ";
cin.ignore(10000, '\n');
cin >> findingredient;
myWorkshop.searchRecipeByIngredient(findingredient);
}
else if (choice == 5)
{
cout << "공방 문을 닫습니다..." << endl;
break;
}
else
{
cout << "잘못된 선택입니다. 다시 시도하세요." << endl;
}
}
return 0;
}
3번에 있던 종료를 5번으로 내리고 3, 4번에 이름검색, 재료검색 기능을 넣었다.
실행결과
모두 원하는대로 작동이 된다.
처음에는 AlchemyWorkshop.cpp의 이름을 검색하는 함수에서 find 알고리즘을 사용해서 이름을 검색했었는데 오류가 발생했었다.
오류를 인터넷에 검색해봤지만 그때는 이해가 잘 안됐었는데 필수 기능을 완료한 지금 다시 생각해보니 recipes 벡터의 원소는 PotionRacipe 객체이고 입력받는건 string이라서 오류가 발생한 것 같다.
'숙제' 카테고리의 다른 글
5번 과제(UE_LOG로 2차원 움직임 출력하기) (2) | 2025.08.28 |
---|---|
C++ 4번 과제 도전 기능 (3) | 2025.08.27 |
C++ 3번 과제 테스트(인벤토리 시스템 구현) (5) | 2025.08.26 |
C++ 3번 과제(인벤토리 시스템 구현) (8) | 2025.08.25 |
C++ 숙제 (영화 데이터를 관리하는 프로그램) (0) | 2025.08.21 |