본문 바로가기
C++

[C++] Enum 의 진화와 실무적인 절충안

by Oz Driver 2026. 2. 26.

C++ 프로젝트에서 상태값이나 타입을 정의할 때 enum은 거의 필수적으로 사용됩니다.
하지만 프로젝트 규모가 커질수록 enum을 어떻게 관리하느냐가 코드의 가독성과 유지보수성에 직접적인 영향을 줍니다. 이번 글에서는 전통적인 방식에서 시작해 C++11의 변화, 그리고 실무에서 자주 사용하는 절충안까지 흐름으로 정리해 보겠습니다.

 

1. 전통적인 C 스타일 enum 

가장 단순하고 직관적인 방식입니다.

enum MonsterType 
{
    Slime,
    Skeleton,
    Boss,
    Max
};


초기에는 문제가 없어 보이지만, 프로젝트가 커지면 열거자 이름이 전역 공간으로 노출되면서 충돌 위험이 생깁니다.
예를 들어 다른 시스템에서도 비슷한 이름을 사용하게 됩니다.

enum ItemType 
{
    Potion,
    Scroll,
    Boss, // 몬스터 보스가 아닌 아이템 보스 등급
    Max
};


이처럼 서로 다른 의미인데도 이름이 겹치면 컴파일 에러가 발생합니다.
그 이유는 C 스타일 enum의 열거자들이 이름 공간에 묶여 있지 않기 때문입니다.
즉 컴파일러 입장에서는 다음과 비슷하게 보입니다.

const int Slimes   = 0;
const int Skeleton = 1;
const int Boss     = 2;
const int Max      = 3;

const int Potion   = 0;
const int Scroll   = 1;
const int Boss     = 2; // 동일한 이름, 컴파일 에러
const int Max      = 3; // 동일한 이름, 컴파일 에러


이 문제는 파일이 많아지고 시스템이 분리될수록 실제로 자주 발생합니다.

 

2. C++11 의 해결책 : enum class

이름 충돌 문제를 근본적으로 해결하기 위해 C++11에서 enum class가 도입되었습니다.

enum class MonsterType 
{
    Slime,
    Skeleton,
    Boss,
    Max
};


이제 열거자는 반드시 소속을 명시해야 합니다.

MonsterType monsterType = MonsterType::Slime;


이 방식은 이름 충돌을 완전히 차단하고 타입 안전성도 제공합니다.
하지만 실무에서 자주 부딪히는 지점이 있습니다.

 

배열 인덱스로 사용할 때나 사용시에 타입 캐스팅을 해야 한다는 점입니다.

MonsterData monsterTable[static_cast<int>(MonsterType::Max)];

// 또는 간단히 (int) 로 타입캐스팅
monsterTable [(int)MonsterType::Slime].hp = 100;

monsterTable [static_cast<int>(MonsterType::Slime)].hp = 100;


결국 enum class 는 정수처럼 사용할 수 없기 때문에 어떤 방식이든 타입 캐스팅이 필요합니다.
코드가 길어지는 것 자체보다, 반복 사용 시 가독성이 흐트러진다는 점이 더 크게 느껴질 수 있습니다.

 

3. 실무 절충안 : namespace + 익명 enum

namespace 를 선언 후, 이름없는 enum 으로 나열합니다.

enum 다음에 열거형 명칭을 써도 되지만 예를 들어, MonsterType::Type::Slime 처럼 오히려 코드가 더 조악해지기 때문에 굳이 쓸 필요는 없습니다.

namespace MonsterType  
{
    enum 
    {
        Slime = 0,
        Skeleton,
        Boss,
        Max
    };
}


사용은 매우 단순합니다.

MonsterData mosterTable[ MonsterType::Max];
mosterTable[ MonsterType::Slime].hp = 50;
mosterTable[ MonsterType::Skeleton].hp = 150;


캐스팅 없이 정수 인덱스로 바로 사용할 수 있고, namespace 가 이름 충돌을 막아 줍니다.
코드를 읽을 때도 의도가 직관적으로 들어옵니다.

 

enum 값은 컴파일을 거치면, int 로 취급되기 때문에 변수로 저장할 필요가 있을 때는 int 변수로 대입하면 됩니다. 

다만, currentLevel 에 Level::max 에 존재하지 않는 값도 넣을 수가 있기 때문에 주의가 필요합니다. 

namespace Level
{
	enum
	{
		Baby,
		Veteran,
		Nightmare,
		max
	};
}

int currentLevel = Level::Baby;

// 주의 
// 컴파일은 통과하지만, 사용시 문제 발생
currentLevel = 10;

 

또한 using namespace 는 피해야 합니다.

using namespace MonsterType  ;


이렇게 하면 다시 전역으로 이름이 퍼지면서 충돌 위험이 생깁니다.

 

마무리

enum 은 단순한 문법 요소처럼 보이지만, 프로젝트가 커질수록 코드 구조와 충돌 관리에 직접적인 영향을 줍니다.
전통 enum 은 단순하지만 이름 충돌 위험이 있고, 
enum class 는 안전하지만 정수처럼 사용할 수 없어 캐스팅이 필요합니다.
namespace + 익명 enum 방식은 실무에서 자주 쓰이는 절충안으로, 충돌을 막으면서도 간결한 사용성을 제공합니다.