본문 바로가기

별걸다하는 IT/프로그래밍언어

[C++] ENUM 열거형 사용법 알아보자, enum 왜 사용을 권장하는가? enum 비교하기, typedef enum 문법

반응형

안녕하세요 양햄찌 블로그 주인장입니다.

오늘은 ENUM 사용법에 대해 포스팅을 진행해보려고 해요.

ENUM이란 무엇인가 

enum은 우리가 흔히 열거형이라고 하죠, 

enumeration에서 나온 키워드입니다. enumeration또한 열거라는 뜻을 가지고 있어요.

enum 사용 문법을 먼저 볼게요. 

변수를 선언하듯, enum도 변수처럼 변수명(열거형이름)과 함께 선언해줘야 합니다.  

bool 타입이 true와 false가 있는 것처럼, 이렇게 선언할 경우 열거형이름 타입에 값1, 값2, .. 값n이 있다고 정의가 된거랑 같은 의미라고 볼 수 있어요. enum은 자료형을 유저가 원하는 방식으로 정의하는 방법인거죠.

사실상, enum의 맨 앞의 값(값1)은 상수 0에 매핑되어있습니다. enum으로 정의했을 경우 맨 앞의 값은 0, 그 뒤로 갈수록 1씩 증가한 상수 값과 동일해요. 그런데 내가 만약 특정 값을 부여하고 싶다, 하면 '='연산자를 이용해서 부여할 수 있습니다.

 

아주 간단하게 enum정의 예시를 살펴볼게요.

enum 요일 
{
	월,화,수,목,금,토,일
};

월,화,수,목,금,토,일 해당 값들은 요일이라는 공통분모에 묶여있으니, enum을 이처럼 정의할 수 있는거죠.

이러면, '요일' 타입에 '월'이라는 값이 있는거예요.  

이렇게 정의를 했을 경우, '월'이라는 값에는 상수 0이 매핑됩니다.

만약 if (요일::월 == 0) 하면 true가 되는거죠.

enum 요일 
{
	월=1,화,수,목,금,토,일=10
};

'월'에 해당하는 상수를 1로 지정하고 싶을 경우 이처럼 = 연산자를 사용해 지정해주면 되며, 

지정이 안된 부분은 이전 값 기준으로 1씩 증가합니다.

위의 경우 월은 1, 화는 2 수는 3 목은 4 금은 5 토는 6 일은 10이 되겠죠?

ENUM은 왜 사용하는가?

가독성 효율성 때문인데요 이해를 위해 예시를 하나 확인해봅시다.

자, 제가 ATM의 메뉴 화면 부분을 개발한다고 해볼게요.

#include <iostream>
int main()
{
    int choice = 0;
    do 
    {
        //메뉴 화면 
        std::cout << "#####################\n";
        std::cout << "### 1. 예금출금     ##\n";        
        std::cout << "### 2. 예금조회     ##\n";       
        std::cout << "### 3. 예금입금     ##\n";        
        std::cout << "### 4. 나가기       ##\n";        
        std::cout << "#####################\n";     
        std::cout << "메뉴를 입력하시오 "; 
        std::cin >> choice; 

        //메뉴 선택에 따른 동작
        switch(choice)
        {
            case 1: withdraw(); break;
            case 2: balanceInquiry(); break;
            case 3: deposit(); break;
            case 4: exit(100); break;
        }
    } while (true);
    return 0;
}

enum을 사용하지 않고 대충 만들어보았어요.

1을 누르면 예금출금 로직을 타고, 2를 누르면 예금조회 로직을타는거죠! 

위 소스에선 '//메뉴화면' 부분에서 출력을 해주니까 아~ 1이 예금출금이구나 2가 예금 조회구나 ~ 이렇게 알 수 있는데,,

만약 소스 개수가 엄청 많고, 해당 값들을 다른 모듈에서 공통적으로 사용한다면, 

아 메뉴코드는 몇번까지 있었지..? 1번이 예금출금이던가 예금입금이던가..? 이렇게 헷갈릴 수 있겠죠?? 

추후 새로운 요청이 들어와서 메뉴를 추가개발한다 했을 때, 흠 메뉴코드 4번을 현재 쓰고 있나..? 이번 신규 메뉴를 4번으로 정의해도 괜찮나..? 5번을 써야하나? 이렇게 헷갈릴 수 있고 이력관리가 잘 안되어있다면 찾는데에도 많은 시간을 잡아먹을 수 있어요.

 

enum을 사용해서 메뉴코드를 개편해봅시다.

#include <iostream>
enum menu_type
{
    withdraw = 1,
    balanceInquiry,
    deposit,
    quit
};
int main()
{
    int choice;
    do
    {
        //메뉴 화면 
        std::cout << "#####################\n";
        std::cout << "### 1. 예금출금     ##\n";
        std::cout << "### 2. 예금조회     ##\n";
        std::cout << "### 3. 예금입금     ##\n";
        std::cout << "### 4. 나가기       ##\n";
        std::cout << "#####################\n";
        std::cout << "메뉴를 입력하시오 ";
        std::cin >> choice;

        //메뉴 선택에 따른 동작
        switch (choice)
        {
        case menu_type::withdraw: //---> 경우가 1일때가 아닌, '메뉴타입이 withdraw일때'로 가독성이↑
            withdraw(); // 내부로직 처리 
            break;
        case menu_type::balanceInquiry:
            balanceInquiry();// 내부로직 처리 
            break;
        case menu_type::balanceInquiry: 
            deposit();// 내부로직 처리 
            break;
        case menu_type::quit:
            exit(100); break;
        }
    } while (true);
    return 0;
}

짠 menu_type정의 부분만 보고도, 바운더리가 명확해서 아 메뉴타입은 총 4개로 구성되어있구나, 알 수 있어요.

새로운 메뉴가 추가된다 해도, 숫자가 겹칠지 아닐지 전혀 신경쓸 필요 없고 마지막에 한 줄만 추가해주면 되는거죠!

 

또 main본문에 '//메뉴 선택에 따른 동작' 부분을 보면

기존에 case1: 이였던 부분이 case menu_type::withdraw:로 바꼈죠?

case 1만 보고서는 이게 어떤 메뉴를 클릭한 건지 알 수 없지만, 이렇게 enum을 써서 변경하게 되면, 아 출금 버튼을 눌렀을 때에 대한 조건문이구나! 이렇게 코드를 쉽게 이해할 수 있어요.

ENUM은 왜 사용하는가 두번째 예시2

결국 enum이란 C언어에서 유저가 정의하는 하나의 타입이라고 볼 수 있는데요,

 

보통 개발시 관리를 위해 값을 코드화해서 정말 많이 사용하는데

코드화하다보니 이게 어떤 걸 의미하는지 명확하지 않을 때가 많아요. 코드방식을 사용하면 해당 코드가 의미하는 값을 외우고 있거나 따로 문서로 관리해야하는 단점이 있습니다.

 

예를 들어, 은행에서 카드상태코드라는 필드가 있다고 합시다.

해당필드 타입은 정수로 0값은 정상, 1은 해지, 2는 분실 3은 정지 이렇게 코드식으로 관리를 해요.

어떤 로직에서 '만약 카드타입이 분실이라면 결제 거래 거절'이런 방어로직을 추가하고 싶다면,

if (temp_card_status != 2) 이런식으로 갈텐데, 업무를 모르는 사람이 보면 2가 뭐지...? 2가 뭐길래 방어로직을 만든거지..? 이렇게 생각할 수 있겠죠.

하지만 열거형을 이용하면, if(temp_card_status != card_status::lost ) 소스만 보고도 아 '분실이아니면'이구나 이해할 수 있겠죠?

#include <iostream>
enum CardStatus
{
	정상 = 0,
	해지 = 1,
	분실 = 2,
	정지 = 3
};
bool isValidCard(enum CardStatus card_st)
{
	if (card_st != 정상)
	{
		std::cerr << "사용불가능한 카드입니다" << std::endl;
		return false;
	}
	std::cout << "정상카드" << std::endl;
	return true;
}
int main()
{
	enum CardStatus temp_card_st = 0; //컴파일에러
	enum CardStatus card_st = 정상; //OK
	isValidCard(card_st);
}

enum타입은 비교뿐만 아니라 대입에서도 유용합니다. 

열거형이 아니라면, 특정 카드에 대한 카드타입을 지정해줘야할 때, 이 카드가 정지카드라면 정지상태를 넣어줘야하고 정상이면 정상상태값을 넣어줘야하겠죠?!

그런데 만약 코드방식을 사용하면 기존 정지에 해당하는 값을 3으로 정의했는데 순간적으로 5로 착각하고

기존에 없는 5를 대입하거나 하면 컴파일 에러는 안나도 심각한 장애를 유발시킬 수 있겠죠.

 

enum타입은 대입시 enum에서 정의된 열거형만 대입될 수 있어요. 

어떤 상태값이 지정된건지 주석을 따로 달지 않아도 코드만으로 쉽게 이해가 되고, 

열거형이 아니면 자동으로 컴파일 에러를 발생시키기 때문에 장애를 사전방지할 수 있습니다.

ENUM은 사용을 권장하는 이유 요약

1. 가독성이 향상되어 소스코드 이해가 높아집니다.

2. 관련 있는 값들은 하나의 변수명으로 묶여있기 때문에 한 곳에서 한 번에 구성을 파악할 수 있습니다.

3. 안전성 - enum타입에는 enum값만 대입할 수 있기 때문에 실수로 다른 값이 들어가는 것을 방지해줍니다..

4. 상수값이 아닌 사람이 기억하기 쉬운 문자열로 매핑시킴으로써 코드값을 기억하는 것보다 기억이 용이합니다.

5. enum 타입은 컴파일시점에서 바뀌기 때문에 int로 정의했을 때보다 성능상 하락도 없고, 오히려 다른 타입일 경우 성능이 더 좋을 수 있습니다.

enum 타입 선언 효율적으로 사용하기 - typedef

//정의
enum DayWeek
{
	MON, TUE, WED, THU, FRI, SAT, SUM
}
//enum타입변수 2개 선언
enum DayWeek dayWeek1;
enum DayWeek dayWeek2;

enum을 정의하고 enum타입 변수를 선언할 때에는 코드가 이렇게 되는데요,

변수 하나 정의하는데 매번 enum DayWeek 사용해주기 귀찮죠. 

 

typedef 키워드를 통해서 enum사용을 좀 더 효율적으로 바꿀 수 있습니다.

애초에 typedef가 긴거를 닉넴처럼 명명해서 좀 짧게 쓸 수 있도록 해주는 역할을 하잖아요 ㅎㅎ

//enum타입변수 선언
DayWeek dayWeek1;
//선언과 초기화
DayWeek dayWeek2 = TUE;
DayWeek dayWeek3 = 3; //열거자가 아니라 대입 불가 컴파일에러!

enum타입 자료형 선언할 때 좀 더 간편해진 것을 알 수 있죠? 

int dayweek 처럼 마치 일반 변수를 선언하는 것처럼 사용할 수 있습니다.

알아두면 좋은 점 or 정리

■ cin으로 enum타입을 입력받을 수는 없습니다. <<연산자 오버로딩이 되어있지 않기 때문~

연산자 오버로딩으로 '<<'를 정의 해주면 enum타입 또한 받게 만들 수 있어요.

 

참고로 기본적인 enum은 대입연산자만 오버로딩 되어있습니다.

■ 열거체에는 정의된 열거자만 대입될 수 있습니다.

 

typedef 키워드를 사용하면 좀 더 간편하게 enum을 선언하고 사용할 수 있습니다.

 

열거자에 명시적으로 값을 지정하지 않으면 시작은 0부터, 1씩 증가하는 상수값을 갖게 됩니다.

열거자 전체 또는 일부 원하는 부분에 값을 지정할 수 있습니다. 이때 지정되지 않은 열거자는 그 앞 열거자 값 +1의 값을 갖게 됩니다.

 

 참고로 열거자를 정수로 형변환할 수 있습니다. (열거자 자체가 숫자값을 가지므로)

int dayToday = int(WED); 
enum CardStatus temp_card_st = CardStatus(2); 

요렇게~~ 값을 반대로 enum타입으로 형변환도 가능!

 

기존의 enum의 문제를 보완하고 개선한 enum class라는게 C++11부터 생겼는데요~ 다음 포스팅에서는 enum과 enum 클래스에 대해 알아보도록 합시다.

 

오늘 포스팅은 여기까지입니다. 도움이 되었다면 공감은 어떤가요? 작성자에게 큰 힘이 됩니다.

그럼 다음 포스팅에서 봐요~!

반응형