🔥 중첩 타입 실전 예제

493자
6분

중첩 타입을 실제로 어떻게 활용할 수 있는지 예시를 통해 자세히 살펴볼까요? 아래 예제에서는 BlackjackCard라는 구조체를 정의하고 있습니다. 이 구조체는 블랙잭 게임에서 사용되는 카드를 모델링한 것이에요. BlackjackCard 구조체는 SuitRank라는 두 개의 중첩된 열거형 타입을 포함하고 있죠.

블랙잭에서 에이스 카드는 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)

Rankvalues라는 계산 속성도 정의하고 있는데요, 이 속성은 Values 구조체의 인스턴스를 반환합니다. 이 계산 속성은 카드의 숫자를 고려하여 적절한 값으로 초기화된 새로운 Values 인스턴스를 만들어 반환해요. jack, queen, king, ace에 대해서는 특별한 값을 사용하고, 숫자 카드에 대해서는 해당 숫자의 원시 Int 값을 사용하죠.

BlackjackCard 구조체 자체는 ranksuit라는 두 개의 속성을 가지고 있어요. 또한 description이라는 계산 속성도 정의하고 있는데, 이 속성은 ranksuit에 저장된 값을 사용하여 카드의 이름과 값에 대한 설명을 만들어 냅니다. description 속성은 옵셔널 바인딩을 사용하여 두 번째 값을 표시할 것이 있는지 확인하고, 있다면 해당 값에 대한 추가 설명을 삽입해요.

BlackjackCard는 사용자 정의 이니셜라이저가 없는 구조체이기 때문에, 구조체 타입을 위한 멤버와이즈 이니셜라이저에서 설명한 것처럼 암시적 멤버와이즈 이니셜라이저를 가지게 됩니다. 이 이니셜라이저를 사용하여 theAceOfSpades라는 새로운 상수를 초기화할 수 있어요.

let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// "theAceOfSpades: suit is ♠, value is 1 or 11" 출력
swift

RankSuitBlackjackCard 내부에 중첩되어 있지만, 그들의 타입은 컨텍스트에서 추론될 수 있기 때문에, 이 인스턴스의 초기화는 열거형 케이스를 케이스 이름(.ace.spades)만으로 참조할 수 있게 됩니다. 위의 예제에서 description 속성은 스페이드 에이스가 1 또는 11의 값을 가진다는 것을 정확히 보고하고 있죠.

이렇게 중첩 타입을 활용하면 관련된 타입들을 논리적으로 그룹화하고, 코드의 가독성과 유지보수성을 높일 수 있습니다. 특히 위의 예제처럼 도메인 모델을 구현할 때 중첩 타입을 사용하면 도메인의 개념과 규칙을 명확하게 표현할 수 있어요.