Post

2026-03-19 TIL (18일차)

2026-03-19 TIL (18일차)

디버깅의 중요성

도전 과제로 넘어가면서 필수 기능들을 여러 클래스(Class)로 분리하고 연결하기 시작했습니다. 프로젝트의 규모가 커질수록 디버깅의 중요성이 커졌습니다.

💡 클래스 분리 시 주의점 기능이 여러 클래스로 쪼개지면, 오류가 났을 때 “어느 클래스의 어느 함수에서부터 잘못된 데이터가 넘어왔는지” 호출 스택을 따라가며 추적하는 능력이 필수적입니다. 디버그 모드와 중단점을 적극 활용



STL map

C++의 std::mapKeyValue을 한 쌍으로 묶어 저장하는 자료구조입니다. 내부적으로 정렬되어 있으며, 데이터를 빠르게 검색할 때 유용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <map>

// 1. 맵 선언 (Key: 물약 이름, Value: 재고 수량)
std::map<std::string, int> potionStock;

// 2. 데이터 추가 및 수정
potionStock["힐링포션"] = 5; 
potionStock.insert({"마나포션", 3});

// 3. 데이터 검색 (find)
auto it = potionStock.find("힐링포션");
if (it != potionStock.end()) {
    std::cout << "힐링포션 재고: " << it->second << "개\n"; // it->first는 Key, it->second는 Value
}

// 4. 데이터 순회
for (const auto& pair : potionStock) {
    std::cout << pair.first << " : " << pair.second << "\n";
}

레시피 중복 검색 오류

과제 오류

검색어와 전혀 일치하지 않는 재료가 포함된 레시피가 검색 결과에 노출됨

문제코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    std::vector<PotionRecipe> Recipes_Results;
    for (int i = 0; i < this->recipes_.size(); i++)
    {
        bool isMatch = false;
        if (name == recipes_[i].GetpotionName())
        {
            isMatch = true;
        }
        else
        {
            for (int j = 0; j < this->recipes_.size(); j++)
            {
                for (int z = 0; z < this->recipes_[j].Getingredients().size(); z++)
                {
                    if (this->recipes_[j].Getingredients()[z] == name)
                    {
                        isMatch = true;
                        break;
                    }
                }
            }

        }

        if (isMatch)
        {
            Recipes_Results.push_back(recipes_[i]);
        }
    }

else 내부에서 현재 확인 중인 레시피(i)의 재료만 검사해야 하는데, 전체 레시피(j)를 처음부터 다시 순회하도록 이중 루프를 잘못 작성하여 발생한 논리 오류.

1
2
3
4
5
6
7
8
9
10
11
else 
        {
            for (int z = 0; z < this->recipes_[i].Getingredients().size(); z++)
            {
                if (this->recipes_[i].Getingredients()[z] == name)
                {
                    isMatch = true;
                    break;
                }
            }
        }

수정

상수의 정확성

AlchemyWorkshop 클래스에서 RecipeManager의 함수를 호출할 때 컴파일 에러가 발생했습니다. 원인은 C++의 엄격한 상수 보장 규칙(const Correctness) 때문이었습니다.

차이: 함수 맨 뒤의 const

클래스의 멤버 함수 맨 뒤에 붙는 const는 단순한 키워드가 아니라 컴파일러와의 약속입니다.

함수 뒤의 const가 가지는 의미

  • SearchRecipes(...) const; 내부에 있는 멤버 변수(데이터)들을 절대 수정하지 않고, 안전하게 읽기 (읽기 전용 선언)
  • FindRecipeByName(...); 필요하다면 언제든 클래스 내부 데이터를 수정(수정 가능성 내포)

에러 이유

문제는 AlchemyWorkshopGetStockByName 함수에서 시작되었습니다.

1
2
3
4
5
// 1. 여기서 const 약속을 했습니다.
int AlchemyWorkshop::GetStockByName(const std::string& potionName) const {
    // 2. 그런데 내부에서 const가 없는 함수를 호출하려 했습니다.
    PotionRecipe* recipe = recipeManager_.FindRecipeByName(potionName); 
}

GetStockByName은 맨 뒤에 const를 붙임으로써상태를 절대 바꾸지 않겠다고 선언했습니다.
그런데 그 내부에서 const가 안 붙은 FindRecipeByName을 부르려고 하니, 컴파일러가 에러가 발생했습니다.

FindRecipeByName 뒤에 const를 붙이면 되잖아?

포인터의 딜레마 발생

이유: 함수에 const를 붙여서 내부 원본을 건드리지 않겠다고 선언해 놓고, 정작 반환값으로는 원본을 마음대로 뜯어고칠 수 있는 포인터를 호출자에게 넘겨주려 했기 때문입니다.

해결법

객체지향적 설계 분리

HasRecipe 함수 추가: 이 물약이 존재하는지 확인만하고 싶을 때 사용할, const 전용 함수를 구현.

1
2
3
4
5
6
7
8
9
10
// [RecipeManager.h] const가 붙은 안전한 읽기 전용 함수 추가
bool HasRecipe(const std::string& name) const; 

// [AlchemyWorkshop.cpp] 
int AlchemyWorkshop::GetStockByName(const std::string& potionName) const {
    if (recipeManager_.HasRecipe(potionName) == false) {
        return -1; // 장부에 없는 물약
    }
    return stockManager_.GetStock(potionName);
}
This post is licensed under CC BY 4.0 by the author.