🔥 중첩 타입 실전 예제
중첩 타입을 실제로 어떻게 활용할 수 있는지 예시를 통해 자세히 살펴볼까요? 아래 예제에서는 BlackjackCard
라는 구조체를 정의하고 있습니다. 이 구조체는 블랙잭 게임에서 사용되는 카드를 모델링한 것이에요. BlackjackCard
구조체는 Suit
와 Rank
라는 두 개의 중첩된 열거형 타입을 포함하고 있죠.
블랙잭에서 에이스 카드는 1 또는 11의 값을 가질 수 있습니다. 이 특징은 Rank
열거형 내부에 중첩된 Values
라는 구조체로 표현되어 있어요.
struct BlackjackCard { // 중첩된 Suit 열거형 enum Suit: Character { case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣" } // 중첩된 Rank 열거형 enum Rank: Int { case two = 2, three, four, five, six, seven, eight, nine, ten case jack, queen, king, ace struct Values { let first: Int, second: Int? } var values: Values { switch self { case .ace: return Values(first: 1, second: 11) case .jack, .queen, .king: return Values(first: 10, second: nil) default: return Values(first: self.rawValue, second: nil) } } } // BlackjackCard 속성과 메서드 let rank: Rank, suit: Suit var description: String { var output = "suit is \(suit.rawValue)," output += " value is \(rank.values.first)" if let second = rank.values.second { output += " or \(second)" } return output } }
swift
Suit
열거형은 카드의 네 가지 모양을 나타내며, 각 케이스의 원시값으로 해당 모양의 심볼을 표현하는 Character
를 사용하고 있습니다.
Rank
열거형은 카드의 13가지 숫자를 나타내며, 각 케이스의 원시값으로는 카드의 숫자값을 표현하는 Int
를 사용하고 있어요. (잭, 퀸, 킹, 에이스 카드에는 이 원시값이 사용되지 않습니다.)
앞서 언급했듯이, Rank
열거형은 자체적으로 Values
라는 중첩 구조체를 추가로 정의하고 있습니다. 이 구조체는 대부분의 카드가 하나의 값을 가지지만, 에이스 카드는 두 개의 값을 가진다는 사실을 캡슐화하고 있죠. Values
구조체는 이를 표현하기 위해 두 개의 속성을 정의하고 있어요.
first
:Int
타입second
:Int?
타입 (옵셔널Int
)
Rank
는 values
라는 계산 속성도 정의하고 있는데요, 이 속성은 Values
구조체의 인스턴스를 반환합니다. 이 계산 속성은 카드의 숫자를 고려하여 적절한 값으로 초기화된 새로운 Values
인스턴스를 만들어 반환해요. jack
, queen
, king
, ace
에 대해서는 특별한 값을 사용하고, 숫자 카드에 대해서는 해당 숫자의 원시 Int
값을 사용하죠.
BlackjackCard
구조체 자체는 rank
와 suit
라는 두 개의 속성을 가지고 있어요. 또한 description
이라는 계산 속성도 정의하고 있는데, 이 속성은 rank
와 suit
에 저장된 값을 사용하여 카드의 이름과 값에 대한 설명을 만들어 냅니다. description
속성은 옵셔널 바인딩을 사용하여 두 번째 값을 표시할 것이 있는지 확인하고, 있다면 해당 값에 대한 추가 설명을 삽입해요.
BlackjackCard
는 사용자 정의 이니셜라이저가 없는 구조체이기 때문에, 구조체 타입을 위한 멤버와이즈 이니셜라이저에서 설명한 것처럼 암시적 멤버와이즈 이니셜라이저를 가지게 됩니다. 이 이니셜라이저를 사용하여 theAceOfSpades
라는 새로운 상수를 초기화할 수 있어요.
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades) print("theAceOfSpades: \(theAceOfSpades.description)") // "theAceOfSpades: suit is ♠, value is 1 or 11" 출력
swift
Rank
와 Suit
가 BlackjackCard
내부에 중첩되어 있지만, 그들의 타입은 컨텍스트에서 추론될 수 있기 때문에, 이 인스턴스의 초기화는 열거형 케이스를 케이스 이름(.ace
와 .spades
)만으로 참조할 수 있게 됩니다. 위의 예제에서 description
속성은 스페이드 에이스가 1
또는 11
의 값을 가진다는 것을 정확히 보고하고 있죠.
이렇게 중첩 타입을 활용하면 관련된 타입들을 논리적으로 그룹화하고, 코드의 가독성과 유지보수성을 높일 수 있습니다. 특히 위의 예제처럼 도메인 모델을 구현할 때 중첩 타입을 사용하면 도메인의 개념과 규칙을 명확하게 표현할 수 있어요.