오늘은 열거형의 고급 활용에 대해 알아보려고 합니다. 저번 기본편에서는 열거형을 만들고, 사용하는 법을 배웠는데 이번엔 좀 더 깊이 들어가 열거형을 어떻게 더 사용할 수 있는지 알아보려고 합니다.
열거형 기본편을 확인하고 싶으신 분들은 아래 링크를 참고 해주세요.
스위프트 열거형은 하나의 데이터 타입으로 정의 될 수 있는데요. 그 중에 구조체와 유사한 점들이 많이 있습니다.
메서드나 프로퍼티(열거형은 연산 프로퍼티만 가능)를 정의할 수 있으며, 둘 다 Call by Value로 메모리 주소를 복사하지 않고 값이 복사되는 형태를 가지고 있습니다.
그리고 스위프트에서 가장 많이 활용 되는 프로토콜을 적용할 수 있다는 점이 유사합니다. 스위프트 문법을 공부하다 보면 POP에 대한 이야기가 많이 언급 되는 것을 확인할 수 있는데, 열거형에서도 프로토콜을 채택할 수 있어 프로토콜의 여러 장점을 활용할 수 있습니다.
프로토콜 선언
이번엔 분식집, 고기집, 양식집 메뉴판을 만들어 보려고 합니다.
모든 메뉴판에는 기본적으로 메뉴 이름을 보여줘야 되고, 다음 메뉴를 볼 수 있게 넘기는 기능이 필요합니다. 이 기능은 모든 메뉴판에 필수적으로 들어가야 되기 때문에 프로토콜로 선언 해주어 모든 메뉴판에서 채택하도록 합니다.
메뉴이름 프로퍼티에 get만 있는 것은 열거형에서는 연산 프로퍼티만 사용할 수 있기 때문입니다.
열거형 프로토콜 채택
이제 메뉴판프로토콜을 각 메뉴판에 채택하여 메뉴이름, 다음메뉴를 구현하도록 합니다. 프로토콜 채택은 구조체, 클래스와 마찬가지로 열거형 이름 옆에 콜론(:)을 한 뒤 프로토콜을 입력하면 됩니다.
사용하기
프로토콜을 채택하여 만든 프로퍼티와 메서드 사용하는 방법은 기존에 직접 정의 했을 때 처럼 사용하면 됩니다.
열거형도 익스텐션을 사용하여 케이스와 메서드, 프로퍼티를 분리하여 가독성을 높일 수 있습니다.
익스텐션 사용하기
케이스와 메서드, 프로퍼티를 익스텐션을 사용해서 분리할 수 있습니다. 또한 여러 프로토콜을 채택한 열거형이라면 각 프로토콜별로 익스텐션을 만들어서 관리할 수도 있습니다.
프로토콜 익스텐션 만들기
프로토콜 자체를 익스텐션하여 프로토콜이 요구하는 사항을 모두 한꺼번에 구현할 수 있는 방법이 프로토콜 익스텐션입니다.
프로토콜 익스텐션을 활용하면 중복되는 코드를 피할 수 있고, 프로토콜을 채택한 곳에서 프로토콜이 요구하는 사항을 모두 구현해 줄 필요가 없습니다.
메뉴판프로토콜을 채택 한 후 메뉴이름을 구현하지 않았지만 오류가 나지 않는 것을 확인 할 수 있습니다.
프로토콜 익스텐션 재정의
만약 메뉴이름을 각 항목의 이름이 아닌 좀 더 멋진 메뉴 이름으로 바꾸고 싶다면 열거형 안에서 재정의 해주면 됩니다.
스위프트의 열거형은 제네릭을 적용할 수 있습니다. 스위프트 문법 중에 제네릭 열거형을 활용한 대표적인 예는 옵셔널(Optional)이라고 할 수 있습니다.
옵셔널 구현 형태
옵셔널(Optional) 공식문서를 보시면 간단하게 아래와 같이 구현 되어 있는 것을 볼 수 있습니다.
<Wrapped>가 제네릭 파라메터이고 이 제네릭 파라메터로 다양한 타입의 옵셔널을 만들 수 있게 되는 것입니다.
옵셔널 열거형 사용
옵셔널을 구현할 때 단순하게 변수 타입에 ?를 붙이면 되지만 아래와 같은 방법으로도 사용 가능합니다.
재귀적 / 간접 타입을 사용하면 열거형의 각 항목의 연관 값으로 열거형 타입을 지정해줄 수 있습니다. 열거형은 구조체와 마찬가지로 크기가 일정한 데이터 타입이라고 볼 수 있습니다.
하지만 자기 자신을 참조하는 열거형은 잠재적으로 무한한 크기를 가질 수 있어 컴파일러에게 해당 타입이 재귀적 / 간접 타입이라는 것을 명시 해주어야 합니다.
재귀적 / 간접 타입 열거형 선언
재귀적 / 간접 타입을 사용하기 위해서는 indirect 키워드를 사용해야 합니다.
열거형 전체 항목에 적용하려면 enum 앞에 선언하고, 특정 항목에만 적용하려면 case 앞에 선언 해주면 됩니다.
전체에 적용
특정 항목에만 적용
재귀적 / 간접 타입 열거형 사용
재귀적 / 간접 타입 열거형은 아직 저도 100% 이해한 부분이 아니라서 내용이 부실합니다. 좀 더 공부해서 내용을 더 추가하도록 하겠습니다.
열거형의 케이스만으로 비교를 할 경우엔 if T.a == T.b 처럼 쉽게 비교 연산을 할 수 있습니다.
하지만 연관 값이 들어 있을 경우 비교하는 로직이 복잡해질 수 있는데 이 때 직접 비교 연산자를 만들어서 사용하면 좀 더 간편하게 연관 값을 비교할 수 있습니다.
비교 연산자 만들기
비교 연산자를 만드는건 구조체나 다른 곳에서 만드는 방식과 동일하게 func ==() -> Bool을 사용하면 됩니다.
비교 연산자 메서드 안에서 switch 문을 이용해 연관 값을 추출한 후 where을 통해 비교하고자 하는 값을 비교하여 결과를 리턴 해주면 됩니다.
비교 연산자 사용하기
위에 비교 연산자를 만들었다면 사용하는 방법은 일반적인 비교 연산자와 동일합니다.
만약 API를 통해 String, Int 등으로 된 값을 받아 와서 열거형으로 변환을 하여 사용하려고 하면 어떻게 해야 될까요?
기본편에서 배웠던 정적 메서드를 이용해서 하는 방법도 있지만 그것보다 더 깔끔하게 사용하는 방법이 커스텀 생성자를 이용하는 것입니다.
커스텀 생성자 만들기
생성자 만드는 방법은 구조체나 클래스의 생성자를 만드는 방법과 동일하게 init을 만들어 주면 됩니다. 아래는 서버에서 받은 에러 코드 값으로 에러 메시지를 찾는 열거형입니다.
커스텀 생성자 사용하기
실패할 수 있는 생성자 만들기
열거형에도 실패할 수 있는 생성자를 만들 수 있습니다. init 뒤에 ?만 붙여 주면 원하는 초기화 값이 없을 경우 nil을 반환하게 됩니다.
기나긴 열거형을 마무리하게 되었습니다. 저도 열거형에 대한 글을 정리하려고 검색을 하니 제가 알지 못 했던 사용 방법도 많이 있어서 쉽게 생각하고 글을 작성 했다가 장장 두 편에 걸쳐서 글을 작성하게 되었네요.
스위프트를 사용하면서 열거형은 잘 활용하면 더 깔끔하고 직관적인 코드 작성이 가능한 것 같습니다. 저도 진행하고 있는 프로젝트에서 열거형을 사용하면 좋아질 수 있는 부분을 찾아서 바꿔 보려고 합니다.
여러분도 기존에 쓰던 방식이 편하다고 그냥 두지 마시고 바꿔 볼 수 있는 부분들을 찾아서 바꾸다 보면 열거형에 많이 익숙해지고 더 좋은 코드를 만들 수 있을 것 같습니다.
그럼 달콤한 코딩 되세요!
Swift - 프로토콜 지향 프로그래밍 - yagom’s blog