열거형(Enum) 고급편


swieeft | 2020. 03. 04. 17:00

오늘은 열거형의 고급 활용에 대해 알아보려고 합니다. 저번 기본편에서는 열거형을 만들고, 사용하는 법을 배웠는데 이번엔 좀 더 깊이 들어가 열거형을 어떻게 더 사용할 수 있는지 알아보려고 합니다.

열거형 기본편을 확인하고 싶으신 분들은 아래 링크를 참고 해주세요.

[Swift] 열거형(Enum) 기본편

프로토콜


스위프트 열거형은 하나의 데이터 타입으로 정의 될 수 있는데요. 그 중에 구조체와 유사한 점들이 많이 있습니다.

메서드나 프로퍼티(열거형은 연산 프로퍼티만 가능)를 정의할 수 있으며, 둘 다 Call by Value로 메모리 주소를 복사하지 않고 값이 복사되는 형태를 가지고 있습니다.

그리고 스위프트에서 가장 많이 활용 되는 프로토콜을 적용할 수 있다는 점이 유사합니다. 스위프트 문법을 공부하다 보면 POP에 대한 이야기가 많이 언급 되는 것을 확인할 수 있는데, 열거형에서도 프로토콜을 채택할 수 있어 프로토콜의 여러 장점을 활용할 수 있습니다.

  • 프로토콜 선언

    이번엔 분식집, 고기집, 양식집 메뉴판을 만들어 보려고 합니다.

    모든 메뉴판에는 기본적으로 메뉴 이름을 보여줘야 되고, 다음 메뉴를 볼 수 있게 넘기는 기능이 필요합니다. 이 기능은 모든 메뉴판에 필수적으로 들어가야 되기 때문에 프로토콜로 선언 해주어 모든 메뉴판에서 채택하도록 합니다.

    메뉴이름 프로퍼티에 get만 있는 것은 열거형에서는 연산 프로퍼티만 사용할 수 있기 때문입니다.

  • 열거형 프로토콜 채택

    이제 메뉴판프로토콜을 각 메뉴판에 채택하여 메뉴이름, 다음메뉴를 구현하도록 합니다. 프로토콜 채택은 구조체, 클래스와 마찬가지로 열거형 이름 옆에 콜론(:)을 한 뒤 프로토콜을 입력하면 됩니다.

  • 사용하기

    프로토콜을 채택하여 만든 프로퍼티와 메서드 사용하는 방법은 기존에 직접 정의 했을 때 처럼 사용하면 됩니다.

익스텐션(extension)


열거형도 익스텐션을 사용하여 케이스와 메서드, 프로퍼티를 분리하여 가독성을 높일 수 있습니다.

  • 익스텐션 사용하기

    케이스와 메서드, 프로퍼티를 익스텐션을 사용해서 분리할 수 있습니다. 또한 여러 프로토콜을 채택한 열거형이라면 각 프로토콜별로 익스텐션을 만들어서 관리할 수도 있습니다.

  • 프로토콜 익스텐션 만들기

    프로토콜 자체를 익스텐션하여 프로토콜이 요구하는 사항을 모두 한꺼번에 구현할 수 있는 방법이 프로토콜 익스텐션입니다.

    프로토콜 익스텐션을 활용하면 중복되는 코드를 피할 수 있고, 프로토콜을 채택한 곳에서 프로토콜이 요구하는 사항을 모두 구현해 줄 필요가 없습니다.

    메뉴판프로토콜을 채택 한 후 메뉴이름을 구현하지 않았지만 오류가 나지 않는 것을 확인 할 수 있습니다.

  • 프로토콜 익스텐션 재정의

    만약 메뉴이름을 각 항목의 이름이 아닌 좀 더 멋진 메뉴 이름으로 바꾸고 싶다면 열거형 안에서 재정의 해주면 됩니다.

제네릭 열거형


스위프트의 열거형은 제네릭을 적용할 수 있습니다. 스위프트 문법 중에 제네릭 열거형을 활용한 대표적인 예는 옵셔널(Optional)이라고 할 수 있습니다.

  • 옵셔널 구현 형태

    옵셔널(Optional) 공식문서를 보시면 간단하게 아래와 같이 구현 되어 있는 것을 볼 수 있습니다.

    <Wrapped>가 제네릭 파라메터이고 이 제네릭 파라메터로 다양한 타입의 옵셔널을 만들 수 있게 되는 것입니다.

  • 옵셔널 열거형 사용

    옵셔널을 구현할 때 단순하게 변수 타입에 ?를 붙이면 되지만 아래와 같은 방법으로도 사용 가능합니다.

재귀적 /간접 타입 열거형


재귀적 / 간접 타입을 사용하면 열거형의 각 항목의 연관 값으로 열거형 타입을 지정해줄 수 있습니다. 열거형은 구조체와 마찬가지로 크기가 일정한 데이터 타입이라고 볼 수 있습니다.

하지만 자기 자신을 참조하는 열거형은 잠재적으로 무한한 크기를 가질 수 있어 컴파일러에게 해당 타입이 재귀적 / 간접 타입이라는 것을 명시 해주어야 합니다.

  • 재귀적 / 간접 타입 열거형 선언

    재귀적 / 간접 타입을 사용하기 위해서는 indirect 키워드를 사용해야 합니다.

    열거형 전체 항목에 적용하려면 enum 앞에 선언하고, 특정 항목에만 적용하려면 case 앞에 선언 해주면 됩니다.

    • 전체에 적용

    • 특정 항목에만 적용

  • 재귀적 / 간접 타입 열거형 사용

재귀적 / 간접 타입 열거형은 아직 저도 100% 이해한 부분이 아니라서 내용이 부실합니다. 좀 더 공부해서 내용을 더 추가하도록 하겠습니다.

열거형 연관 값으로 비교하기


열거형의 케이스만으로 비교를 할 경우엔 if T.a == T.b 처럼 쉽게 비교 연산을 할 수 있습니다.

하지만 연관 값이 들어 있을 경우 비교하는 로직이 복잡해질 수 있는데 이 때 직접 비교 연산자를 만들어서 사용하면 좀 더 간편하게 연관 값을 비교할 수 있습니다.

  • 비교 연산자 만들기

    비교 연산자를 만드는건 구조체나 다른 곳에서 만드는 방식과 동일하게 func ==() -> Bool을 사용하면 됩니다.

    비교 연산자 메서드 안에서 switch 문을 이용해 연관 값을 추출한 후 where을 통해 비교하고자 하는 값을 비교하여 결과를 리턴 해주면 됩니다.

  • 비교 연산자 사용하기

    위에 비교 연산자를 만들었다면 사용하는 방법은 일반적인 비교 연산자와 동일합니다.

커스텀 생성자


만약 API를 통해 String, Int 등으로 된 값을 받아 와서 열거형으로 변환을 하여 사용하려고 하면 어떻게 해야 될까요?

기본편에서 배웠던 정적 메서드를 이용해서 하는 방법도 있지만 그것보다 더 깔끔하게 사용하는 방법이 커스텀 생성자를 이용하는 것입니다.

  • 커스텀 생성자 만들기

    생성자 만드는 방법은 구조체나 클래스의 생성자를 만드는 방법과 동일하게 init을 만들어 주면 됩니다. 아래는 서버에서 받은 에러 코드 값으로 에러 메시지를 찾는 열거형입니다.

  • 커스텀 생성자 사용하기

  • 실패할 수 있는 생성자 만들기

    열거형에도 실패할 수 있는 생성자를 만들 수 있습니다. init 뒤에 ?만 붙여 주면 원하는 초기화 값이 없을 경우 nil을 반환하게 됩니다.

마무리


기나긴 열거형을 마무리하게 되었습니다. 저도 열거형에 대한 글을 정리하려고 검색을 하니 제가 알지 못 했던 사용 방법도 많이 있어서 쉽게 생각하고 글을 작성 했다가 장장 두 편에 걸쳐서 글을 작성하게 되었네요.

스위프트를 사용하면서 열거형은 잘 활용하면 더 깔끔하고 직관적인 코드 작성이 가능한 것 같습니다. 저도 진행하고 있는 프로젝트에서 열거형을 사용하면 좋아질 수 있는 부분을 찾아서 바꿔 보려고 합니다.

여러분도 기존에 쓰던 방식이 편하다고 그냥 두지 마시고 바꿔 볼 수 있는 부분들을 찾아서 바꾸다 보면 열거형에 많이 익숙해지고 더 좋은 코드를 만들 수 있을 것 같습니다.

그럼 달콤한 코딩 되세요!

참고자료


열거형의 고급 활용과 모범 사례

What are indirect enums?

indirect enums and structs

Swift - 프로토콜 지향 프로그래밍 - yagom’s blog