λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ₯– Bread Basics/Swift

Swift 곡식 λ¬Έμ„œ 정리 - 상속 (Inheritance)

by BreadDev 2025. 4. 11.
728x90

μ•ˆλ…•ν•˜μ„Έμš”. μ˜€λŠ˜μ€ Swift '상속(Inheritance)'에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ˜ μ€‘μš”ν•œ νŠΉμ„±μΈ 상속이 Swiftμ—μ„œλŠ” μ–΄λ–»κ²Œ κ΅¬ν˜„λ˜λŠ”μ§€, λ‹€μ–‘ν•œ μ˜ˆμ œμ™€ ν•¨κ»˜ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ“Œ μƒμ†μ΄λž€? (Inheritance)

상속은 ν•œ ν΄λž˜μŠ€κ°€ λ‹€λ₯Έ 클래슀의 λ©”μ„œλ“œ, ν”„λ‘œνΌν‹°, 그리고 λ‹€λ₯Έ νŠΉμ„±μ„ λ¬Όλ €λ°›λŠ” 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ˜ 핡심 κ°œλ…μž…λ‹ˆλ‹€. Swiftμ—μ„œ 상속은 ν΄λž˜μŠ€μ—μ„œλ§Œ κ°€λŠ₯ν•œ νŠΉλ³„ν•œ κΈ°λŠ₯으둜, 상속을 λ°›λŠ” 클래슀λ₯Ό ν•˜μœ„ 클래슀(subclass), 상속을 μ œκ³΅ν•˜λŠ” 클래슀λ₯Ό μƒμœ„ 클래슀(superclass)라고 ν•©λ‹ˆλ‹€.

상속(Inheritance): κΈ°μ‘΄ 클래슀의 νŠΉμ„±(ν”„λ‘œνΌν‹°, λ©”μ„œλ“œ λ“±)을 μƒˆ ν΄λž˜μŠ€κ°€ λ¬Όλ €λ°›λŠ” 것

ν•˜μœ„ 클래슀(Subclass): λ‹€λ₯Έ ν΄λž˜μŠ€μ—μ„œ μƒμ†λ°›λŠ” 클래슀

μƒμœ„ 클래슀(Superclass): λ‹€λ₯Έ ν΄λž˜μŠ€μ—κ²Œ νŠΉμ„±μ„ λ¬Όλ €μ£ΌλŠ” 클래슀

πŸ“Œ κΈ°λ³Έ 클래슀 μ •μ˜ (Base Class)

상속 κ΄€κ³„μ˜ μ‹œμž‘μ μ΄ λ˜λŠ” 클래슀λ₯Ό **κΈ°λ³Έ 클래슀(Base Class)**라고 ν•©λ‹ˆλ‹€. Swiftμ—μ„œλŠ” λ‹€λ₯Έ μ–Έμ–΄λ“€κ³Ό 달리 λͺ¨λ“  ν΄λž˜μŠ€κ°€ μžλ™μœΌλ‘œ μƒμ†ν•˜λŠ” λ²”μš© κΈ°λ³Έ ν΄λž˜μŠ€κ°€ μ—†μŠ΅λ‹ˆλ‹€.

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // κΈ°λ³Έ κ΅¬ν˜„μ€ 아무것도 ν•˜μ§€ μ•ŠμŒ
    }
}

let someVehicle = Vehicle()
print("Vehicle: \(someVehicle.description)")
// 좜λ ₯: "Vehicle: traveling at 0.0 miles per hour"

κΈ°λ³Έ 클래슀(Base Class): λ‹€λ₯Έ ν΄λž˜μŠ€λ‘œλΆ€ν„° 상속받지 μ•Šμ€ 클래슀

μ €μž₯ ν”„λ‘œνΌν‹°(Stored Property): μΈμŠ€ν„΄μŠ€μ˜ μΌλΆ€λ‘œ 값을 μ €μž₯ν•˜λŠ” ν”„λ‘œνΌν‹°(예: currentSpeed)

계산 ν”„λ‘œνΌν‹°(Computed Property): 값을 μ €μž₯ν•˜μ§€ μ•Šκ³  κ³„μ‚°ν•˜μ—¬ μ œκ³΅ν•˜λŠ” ν”„λ‘œνΌν‹°(예: description)

πŸ“Œ ν•˜μœ„ 클래슀 λ§Œλ“€κΈ° (Subclassing)

ν•˜μœ„ ν΄λž˜μŠ€λŠ” μƒμœ„ 클래슀의 λͺ¨λ“  νŠΉμ„±μ„ 상속받고, μΆ”κ°€λ‘œ μƒˆλ‘œμš΄ νŠΉμ„±μ„ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μœ„ 클래슀λ₯Ό μ •μ˜ν•  λ•ŒλŠ” 클래슀 이름 뒀에 콜둠(:)을 뢙이고 μƒμœ„ 클래슀 이름을 λͺ…μ‹œν•©λ‹ˆλ‹€.

class Bicycle: Vehicle {
    var hasBasket = false
}

let bicycle = Bicycle()
bicycle.hasBasket = true      // Bicycle의 μƒˆ ν”„λ‘œνΌν‹°
bicycle.currentSpeed = 15.0   // Vehicleμ—μ„œ 상속받은 ν”„λ‘œνΌν‹°
print("Bicycle: \(bicycle.description)")
// 좜λ ₯: "Bicycle: traveling at 15.0 miles per hour"

ν•˜μœ„ ν΄λž˜μŠ€λŠ” 또 λ‹€λ₯Έ ν•˜μœ„ 클래슀의 μƒμœ„ ν΄λž˜μŠ€κ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€.

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}

let tandem = Tandem()
tandem.hasBasket = true                // Bicycleμ—μ„œ 상속
tandem.currentNumberOfPassengers = 2   // Tandem의 μƒˆ ν”„λ‘œνΌν‹°
tandem.currentSpeed = 22.0             // Vehicleμ—μ„œ 상속
print("Tandem: \(tandem.description)")
// 좜λ ₯: "Tandem: traveling at 22.0 miles per hour"

ν•˜μœ„ ν΄λž˜μŠ€ν™”(Subclassing): κΈ°μ‘΄ 클래슀λ₯Ό 기반으둜 μƒˆ 클래슀λ₯Ό λ§Œλ“œλŠ” κ³Όμ •

상속 계측(Inheritance Hierarchy): μ—¬λŸ¬ λ‹¨κ³„μ˜ μƒμ†μœΌλ‘œ ν˜•μ„±λœ ν΄λž˜μŠ€λ“€μ˜ 관계

πŸ“Œ μž¬μ •μ˜ (Overriding)

ν•˜μœ„ ν΄λž˜μŠ€λŠ” μƒμœ„ ν΄λž˜μŠ€λ‘œλΆ€ν„° 상속받은 νŠΉμ„±(λ©”μ„œλ“œ, ν”„λ‘œνΌν‹°, μ„œλΈŒμŠ€ν¬λ¦½νŠΈ)을 μžμ‹ λ§Œμ˜ κ΅¬ν˜„μœΌλ‘œ λŒ€μ²΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό **μž¬μ •μ˜(Overriding)**라고 ν•©λ‹ˆλ‹€.

Swiftμ—μ„œ μž¬μ •μ˜λ₯Ό ν•  λ•ŒλŠ” 항상 override ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λŠ” μ˜λ„μ μΈ μž¬μ •μ˜μž„μ„ λͺ…ν™•νžˆ ν•˜κ³ , μ‹€μˆ˜λ‘œ μž¬μ •μ˜ν•˜λŠ” 것을 λ°©μ§€ν•©λ‹ˆλ‹€.

λ©”μ„œλ“œ μž¬μ •μ˜ (Overriding Methods)

class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}

let train = Train()
train.makeNoise()
// 좜λ ₯: "Choo Choo"

override ν‚€μ›Œλ“œ: μƒμœ„ 클래슀의 νŠΉμ„±μ„ μž¬μ •μ˜ν•¨μ„ λͺ…μ‹œμ μœΌλ‘œ ν‘œμ‹œν•˜λŠ” ν‚€μ›Œλ“œ

ν”„λ‘œνΌν‹° μž¬μ •μ˜ (Overriding Properties)

상속받은 ν”„λ‘œνΌν‹°λ₯Ό μž¬μ •μ˜ν•  λ•ŒλŠ” getter와 setterλ₯Ό λͺ¨λ‘ μ œκ³΅ν•˜κ±°λ‚˜, ν”„λ‘œνΌν‹° κ΄€μ°°μžλ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
// 좜λ ₯: "Car: traveling at 25.0 miles per hour in gear 3"

super: μƒμœ„ 클래슀의 λ©”μ„œλ“œ, ν”„λ‘œνΌν‹°, μ„œλΈŒμŠ€ν¬λ¦½νŠΈμ— μ ‘κ·Όν•  λ•Œ μ‚¬μš©ν•˜λŠ” ν‚€μ›Œλ“œ

getter/setter μž¬μ •μ˜: μƒμœ„ ν΄λž˜μŠ€μ—μ„œ μ •μ˜λœ ν”„λ‘œνΌν‹°μ˜ 읽기/μ“°κΈ° λ™μž‘μ„ λ³€κ²½ν•˜λŠ” 것

ν”„λ‘œνΌν‹° κ΄€μ°°μž μž¬μ •μ˜ (Overriding Property Observers)

상속받은 ν”„λ‘œνΌν‹°μ— ν”„λ‘œνΌν‹° κ΄€μ°°μžλ₯Ό μΆ”κ°€ν•˜μ—¬ 값이 변경될 λ•Œ νŠΉμ • λ™μž‘μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// 좜λ ₯: "AutomaticCar: traveling at 35.0 miles per hour in gear 4"

ν”„λ‘œνΌν‹° κ΄€μ°°μž(Property Observer): ν”„λ‘œνΌν‹° κ°’μ˜ λ³€ν™”λ₯Ό κ°μ§€ν•˜μ—¬ νŠΉμ • μ•‘μ…˜μ„ μ‹€ν–‰ν•˜λŠ” λ©”μ»€λ‹ˆμ¦˜

didSet: ν”„λ‘œνΌν‹° 값이 λ³€κ²½λœ 직후에 ν˜ΈμΆœλ˜λŠ” κ΄€μ°°μž

willSet: ν”„λ‘œνΌν‹° 값이 λ³€κ²½λ˜κΈ° 직전에 ν˜ΈμΆœλ˜λŠ” κ΄€μ°°μž

ν”„λ‘œνΌν‹° κ΄€μ°°μž μ‚¬μš© μ‹œ μ£Όμ˜μ‚¬ν•­:

  • μƒμˆ˜ μ €μž₯ ν”„λ‘œνΌν‹°λ‚˜ 읽기 μ „μš© 계산 ν”„λ‘œνΌν‹°μ—λŠ” κ΄€μ°°μžλ₯Ό μΆ”κ°€ν•  수 μ—†μŠ΅λ‹ˆλ‹€.
  • 같은 ν”„λ‘œνΌν‹°μ— λŒ€ν•΄ setter와 ν”„λ‘œνΌν‹° κ΄€μ°°μžλ₯Ό λ™μ‹œμ— μž¬μ •μ˜ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

πŸ“Œ μž¬μ •μ˜ λ°©μ§€ (Preventing Overrides)

νŠΉμ • λ©”μ„œλ“œ, ν”„λ‘œνΌν‹°, μ„œλΈŒμŠ€ν¬λ¦½νŠΈκ°€ ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ μž¬μ •μ˜λ˜λŠ” 것을 λ°©μ§€ν•˜κ³  μ‹Άλ‹€λ©΄ final ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

class Vehicle {
    final var maximumSpeed = 150.0
    final func stop() {
        currentSpeed = 0.0
    }
}

클래슀 자체λ₯Ό 상속이 λΆˆκ°€λŠ₯ν•˜κ²Œ λ§Œλ“€κ³  μ‹Άλ‹€λ©΄, 클래슀 μ„ μ–Έ μ•žμ— final ν‚€μ›Œλ“œλ₯Ό λΆ™μž…λ‹ˆλ‹€.

final class SportsCar {
    // 이 ν΄λž˜μŠ€λŠ” 상속될 수 μ—†μŒ
}

final ν‚€μ›Œλ“œ: 클래슀, λ©”μ„œλ“œ, ν”„λ‘œνΌν‹°, μ„œλΈŒμŠ€ν¬λ¦½νŠΈκ°€ ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ μž¬μ •μ˜λ˜λŠ” 것을 λ°©μ§€ν•˜λŠ” ν‚€μ›Œλ“œ

πŸ“Œ μƒμ†μ˜ ν™œμš©κ³Ό μž₯점

상속은 μ½”λ“œ μž¬μ‚¬μš©μ„±μ„ 높이고 계측적 ꡬ쑰λ₯Ό λ§Œλ“€μ–΄ κ΄€λ ¨ νƒ€μž…λ“€ μ‚¬μ΄μ˜ 관계λ₯Ό λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. ν•˜μ§€λ§Œ κ³Όλ„ν•œ 상속은 μ½”λ“œλ₯Ό λ³΅μž‘ν•˜κ²Œ λ§Œλ“€ 수 μžˆμœΌλ―€λ‘œ, ν•„μš”ν•œ κ²½μš°μ—λ§Œ 적절히 μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

상속을 μ‚¬μš©ν•˜κΈ° 쒋은 경우:

  • is-a 관계가 성립할 λ•Œ (예: μžμ „κ±°λŠ” νƒˆκ²ƒμ΄λ‹€)
  • 곡톡 κΈ°λŠ₯을 μ—¬λŸ¬ ν΄λž˜μŠ€κ°€ κ³΅μœ ν•  λ•Œ
  • λͺ…ν™•ν•œ 계측 ꡬ쑰가 ν•„μš”ν•  λ•Œ

상속보닀 ν•©μ„±(Composition)을 κ³ λ €ν•΄μ•Ό ν•  경우:

  • has-a 관계가 성립할 λ•Œ (예: μžμ „κ±°λŠ” λ°”κ΅¬λ‹ˆλ₯Ό κ°€μ§€κ³  μžˆλ‹€)
  • μœ μ—°ν•œ κΈ°λŠ₯ 쑰합이 ν•„μš”ν•  λ•Œ
  • 단일 μƒμ†μ˜ μ œν•œμ„ 극볡해야 ν•  λ•Œ

πŸ“Œ 정리

  • Swift의 상속 λ©”μ»€λ‹ˆμ¦˜μ€ 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ˜ 핡심 원칙을 λ°˜μ˜ν•˜λ©΄μ„œλ„ νƒ€μž… μ•ˆμ „μ„±μ„ μœ μ§€ν•˜λŠ” λ°©μ‹μœΌλ‘œ κ΅¬ν˜„λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
  • override와 final 같은 ν‚€μ›Œλ“œλ₯Ό 톡해 μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν‘œν˜„ν•˜κ³  μ‹€μˆ˜λ₯Ό λ°©μ§€ν•  수 있으며, μƒμœ„ 클래슀의 κΈ°λŠ₯을 ν™•μž₯ν•˜κ±°λ‚˜ μˆ˜μ •ν•˜λŠ” μœ μ—°ν•œ 방법을 μ œκ³΅ν•©λ‹ˆλ‹€.