개발🧑‍💻/Swift

[Swift] 구조체와 클래스, 열거형 - 사용자 정의 타입

* 이 글은 "스위프트 프로그래밍 (3판)" 및 야곰닷넷의 "스위프트 기초 강의"The Swift Programming (번역본)을 공부하며 정리한 내용입니다.

 

 사용자 정의 타입은, 프로그래머가 데이터를 용도에 맞게 묶어 표현하고자 할 때 유용하게 쓰입니다. 구조체클래스프로퍼티메소드를 사용하여 구조화된 데이터 및 기능을 가질 수 있습니다. Swift에서 이 둘은 문법상 생김새는 비슷하나, 구조체는 값 타입, 클래스는 참조 타입이라는 것이 가장 큰 차이라고 할 수 있습니다. 값 타입과 참조 타입은 추후에 따로 다뤄보도록 하겠습니다.

구조체 (Struct)

struct SoccerPlayer {
    // Properties
    var name: String // 이름
    var age: Int // 나이
    var club: String // 소속팀
    
    let birthdate:String
    static let isHuman: Bool = true // 타입 프로퍼티
    
    // Methods
    func dribble(){
        print("dribble")
    }
    func pass(receiver:SoccerPlayer){
        print("\(self.name) passed to \(receiver.name)")
    }
    func tackle(){
        print("tackle")
    }
    // static 키워드를 붙이면 타입 프로퍼티 혹은 타입 메서드가 됩니다. 아래는 타입 메서드입니다.
    static func introduceSoccer(){
        print("Soccer is ...")
    }
 }

 

 위 예시는 SoccerPlayer라는 구조체를 정의하는 코드입니다. 구조체도 사용자가 정의한 타입이기 때문에, struct 키워드 이후 대문자 카멜케이스로 이름을 작성해주고 그 안에는 프로퍼티와 메서드들을 작성해주면 됩니다.

 

var bruno : SoccerPlayer = SoccerPlayer(name:"bruno",age:28,club:"Manchester United FC",birthDate: "1993-05-21")

// var로 선언되었기 때문에 인스턴스 내부의 var로 선언된 프로퍼티 값을 변경할 수 있다.
bruno.age = 99

// 인스턴스 내부의 let으로 선언된 프로퍼티의 값은 변경할 수 없다.
// bruno.birthDate = "1994-02-21"
// Cannot assign to property: 'isHuman' is a 'let' constant

let jegyun : SoccerPlayer = SoccerPlayer(name:"Jegyun",age:25,club:"Suwon Samsung F.C.",birthDate: "1997-03-17")

// 인스턴스가 let 으로 선언되었기 때문에 내부의 프로퍼티 값을 변경하지 못한다.
// jegyun.age = 100
// Cannot assign to property: 'jegyun' is a 'let' constant

 

 SoccerPlayer 구조체의 인스턴스를 생성하고 나서 내부의 프로퍼티에 접근하려면 마침표(.) 를 사용합니다. 메소드 또한 마찬가지입니다.

 

jegyun.dribble()
// "dribble"

jegyun.pass(receiver: bruno)
// "Jegyun passed to bruno"

// 타입 프로퍼티와 타입 메소드는 해당 타입에만 사용할 수 있다.
SoccerPlayer.introduceSoccer()
// "Soccer is ..."

SoccerPlayer.isHuman
// true

 

클래스 (Class)

 클래스도 구조체와 문법적인 측면에서는 거의 비슷합니다. 예를 들어보겠습니다.

class Person {
    var name: String = "someName"
    var age: Int = 20
    var weight: Double = 0.0
    
    let birthDate : String = "1997-03-17"
    
    static let isHuman: Bool = true
    
    func sayHello(){
        print("\(self.name) says Hello")
    }
    
    static func typeMethod(){
        print("Type Method - static : 재정의 불가")
    }
    
    class func classMethod(){
        print("Type Method - class : 재정의 가능")
    }
}

 마찬가지로 대문자 카멜케이스로 클래스를 작성합니다. 또한 메서드와 프로퍼티도 작성해줄 수 있습니다. 클래스의 타입 메서드에는 두 종류가 있는데, 상속 후 재정의가 가능한 class 타입 메서드, 그리고 상속 후 재정의가 불가능한 static 타입메서드가 있습니다. 

 

var jegyun:Person = Person()
let oliver:Person = Person()

jegyun === oliver // 식별 연산자(===) 를 통해 두 변수는 다른 값을 참조하고 있음을 알 수 있다.

jegyun.name = "Jegyun"
oliver.name = "Oliver"
// 클래스의 인스턴스는 참조 타입이므로 let으로 선언되었더라도 인스턴스 프로퍼티의 값 변경이 가능

// 하지만 참조정보는 변경할 수 없음 : Cannot assign to value: 'oliver' is a 'let' constant
// oliver = jegyun

print(jegyun.name)
print(oliver.name)
jegyun.sayHello() // "Jegyun says Hello"
oliver.sayHello() // "Oliver says Hello"

// 타입 메서드 사용
Person.typeMethod() // "Type Method - static : 재정의 불가"
Person.classMethod() // "Type Method - class : 재정의 가능"

// 인스턴스에서 타입 프로퍼티 및 타입 메서드 사용 불가
// jegyun.typeMethod()
// jegyun.classMethod()
// jegyun.isHuman = false

 구조체와 마찬가지로 인스턴스 생성 후 프로퍼티와 메서드를 사용합니다.

열거형 (Enum)

 열거형은 연관된 항목들을 묶어서 표현하는 타입입니다. 배열이나 딕셔너리와 다르게 열거형은 처음 정의해둔 항목 값 외에는 추가/수정이 불가합니다. 스위프트의 열거형은 다른 언어의 열거형과는 다르게 많은 기능을 할 수 있습니다. 선언하는 법은 다음과 같습니다.

enum SomeEnumeration {
    // enum 구현부
    case something1
    case something2
    case something3, something4, something5
    // ...
}

 

 Swift의 열거형은 switch 구문과 함께 유용하게 쓰일 수 있습니다.

 

enum Week {
    case mon
    case tue
    case wed,thu,fri,sat,sun
}

// 열거형 타입, 케이스를 모두 사용 가능
var day:Week = Week.wed

// 타입이 명확하다면 .case로 사용 가능
day = .fri
// 같은 타입인 Week의 내부 항목으로만 값 변경 가능
day = .sat


switch day {
case .mon, .tue, .wed, .thu, .fri:
    print("평일")
case .sat,.sun:
    print("주말")
}

 

 열거형의 각 항목은 자체로 하나의 값이지만 원시 값(Raw Value) 도 가질 수 있습니다. 

 

enum Sports : Int {
    case baseball = 0
    case soccer = 1
    case basektball = 2
    case football
}

// 일부 값만 원시값을 주게 되면 나머지는 Swift 가 처리함
// 정수 타입은 0부터 1씩 늘어난 값을 가짐
var num : Sports = .football
num.rawValue // 3


enum Week : String {
    case mon = "월"
    case tue = "화"
    case wed = "수"
    case thu = "목"
    case fri = "금"
    case sat = "토"
    case sun = "일"
    case someday
}

let today : Week = .wed

print("오늘은 \(today.rawValue)요일 입니다.") // 오늘은 수요일 입니다.

// 문자열 타입은 원시값을 지정해주지 않았다면, 항목 이름을 원시 값으로 가지게 됨
Week.someday.rawValue // someday

 정수, 문자열 타입 뿐만 아니라 Hashable 프로토콜을 따르는 모든 타입이 원시값의 타입으로 지정될 수 있습니다.

 

 

 원시값을 통해 초기화 할 수 있습니다. 원시 값을 갖는 열거형일 때, 원시 값의 정보를 안다면 원시 값을 통해 열거형 변수 또는 상수를 생성 할 수 있습니다.

 

// rawValue가 case에 해당하지 않을 수 있으므로 rawValue를 통해 초기화 한 인스턴스는 옵셔널 타입이다.
let monday : Week? = Week(rawValue: "월")
let tomorrow = Week(rawValue: "tomorrow") // nil

// 옵셔널 타입이므로 if let 구문을 사용할 수 있다.
if let day : Week = Week(rawValue: "tomorrow") {
    print("요일 : \(day)")
} else {
    print("해당하는 케이스 없음")
}

 

 Swift의 열거형에는 메서드를 추가할 수 있습니다. 

 

enum SoccerPosition {
    case defender
    case midfielder
    case forward
    
    func printPosition() {
        switch self {
        case .defender:
            print("수비수")
        case .midfielder:
            print("미드필더")
        case .forward:
            print("공격수")
        }
    }
}

SoccerPosition.defender.printPosition() // 수비수

 

'개발🧑‍💻 > Swift' 카테고리의 다른 글

[Swift] 어떻게 구조체 이름이 Data냐구요  (2) 2022.11.28
[Swift] final 파헤치기  (1) 2022.11.14
[Swift] 옵셔널  (0) 2021.11.12
[Swift] 함수  (0) 2021.11.09
[Swift] 데이터 타입 (1) - 기본  (0) 2021.10.20