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

Swift 곡식 λ¬Έμ„œ 정리 - ν”„λ‘œν† μ½œ (Protocols)

by BreadDev 2025. 4. 13.
728x90

μ•ˆλ…•ν•˜μ„Έμš”. μ˜€λŠ˜μ€ Swift의 'ν”„λ‘œν† μ½œ(Protocols)'에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ“Œ ν”„λ‘œν† μ½œμ΄λž€?

ν”„λ‘œν† μ½œμ€ νŠΉμ • μž‘μ—…μ΄λ‚˜ κΈ°λŠ₯에 ν•„μš”ν•œ λ©”μ„œλ“œ, ν”„λ‘œνΌν‹°, 기타 μš”κ΅¬μ‚¬ν•­μ˜ 청사진을 μ •μ˜ν•©λ‹ˆλ‹€. 클래슀, ꡬ쑰체, μ—΄κ±°ν˜•μ€ μ΄λŸ¬ν•œ ν”„λ‘œν† μ½œμ„ '채택(adopt)'ν•˜μ—¬ μš”κ΅¬μ‚¬ν•­μ„ 'μ€€μˆ˜(conform)'ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν”„λ‘œν† μ½œμ€ 무엇을 ν•΄μ•Ό ν•˜λŠ”μ§€λ§Œ μ •μ˜ν•˜κ³ , μ–΄λ–»κ²Œ κ΅¬ν˜„ν•΄μ•Ό ν•˜λŠ”μ§€λŠ” 각 νƒ€μž…μ— λ§‘κΉλ‹ˆλ‹€. 이λ₯Ό 톡해 μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…λ“€μ΄ κ³΅ν†΅λœ κΈ°λŠ₯을 μ œκ³΅ν•˜λ©΄μ„œλ„ 각자의, 이λ₯Ό μœ μ—°ν•˜κ²Œ κ΅¬ν˜„ν•  수 μžˆλŠ” λ‹€ν˜•μ„±μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

πŸ“Œ ν”„λ‘œν† μ½œ ꡬ문 (Protocol Syntax)

ν”„λ‘œν† μ½œμ€ λ‹€μŒκ³Ό 같이 μ •μ˜ν•©λ‹ˆλ‹€:

protocol SomeProtocol {
    // ν”„λ‘œν† μ½œ μš”κ΅¬μ‚¬ν•­ μ •μ˜
}

νƒ€μž…μ΄ ν”„λ‘œν† μ½œμ„ 채택할 λ•ŒλŠ” νƒ€μž… 이름 뒀에 콜둠(:)을 뢙이고 ν”„λ‘œν† μ½œ 이름을 λͺ…μ‹œν•©λ‹ˆλ‹€:

struct SomeStructure: SomeProtocol {
    // ꡬ쑰체 μ •μ˜
}

// μ—¬λŸ¬ ν”„λ‘œν† μ½œ 채택 μ‹œ 콀마둜 ꡬ뢄
struct AnotherStructure: FirstProtocol, AnotherProtocol {
    // ꡬ쑰체 μ •μ˜
}

// ν΄λž˜μŠ€κ°€ μƒμœ„ 클래슀λ₯Ό κ°€μ§„ 경우, μƒμœ„ 클래슀λ₯Ό λ¨Όμ € λͺ…μ‹œ
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
    // 클래슀 μ •μ˜
}

μ°Έκ³ : ν”„λ‘œν† μ½œ 이름은 λ‹€λ₯Έ Swift νƒ€μž…μ²˜λŸΌ λŒ€λ¬Έμžλ‘œ μ‹œμž‘ν•©λ‹ˆλ‹€(예: Equatable, Hashable).

πŸ“Œ ν”„λ‘œνΌν‹° μš”κ΅¬μ‚¬ν•­ (Property Requirements)

ν”„λ‘œν† μ½œμ€ νŠΉμ • 이름과 νƒ€μž…μ˜ ν”„λ‘œνΌν‹°λ₯Ό μš”κ΅¬ν•  수 있으며, ν•΄λ‹Ή ν”„λ‘œνΌν‹°κ°€ μ €μž₯ ν”„λ‘œνΌν‹°μΈμ§€ 계산 ν”„λ‘œνΌν‹°μΈμ§€λŠ” μ§€μ •ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 단, ν”„λ‘œνΌν‹°κ°€ 읽기 μ „μš©μΈμ§€(gettable) λ˜λŠ” 읽기-μ“°κΈ° κ°€λŠ₯ν•œμ§€(gettable & settable)λŠ” λͺ…μ‹œν•΄μ•Ό ν•©λ‹ˆλ‹€.

protocol SomeProtocol {
    var mustBeSettable: Int { get set }  // 읽기-μ“°κΈ° κ°€λŠ₯
    var doesNotNeedToBeSettable: Int { get }  // 읽기 μ „μš©
    static var someTypeProperty: Int { get set }  // νƒ€μž… ν”„λ‘œνΌν‹°
}

예제: ν”„λ‘œνΌν‹° μš”κ΅¬μ‚¬ν•­ κ΅¬ν˜„

protocol FullyNamed {
    var fullName: String { get }
}

// ꡬ쑰체둜 κ΅¬ν˜„
struct Person: FullyNamed {
    var fullName: String
}

// 클래슀둜 κ΅¬ν˜„ (계산 ν”„λ‘œνΌν‹° μ‚¬μš©)
class Starship: FullyNamed {
    var prefix: String?
    var name: String
    
    init(name: String, prefix: String? = nil) {
        self.name = name
        self.prefix = prefix
    }
    
    var fullName: String {
        return (prefix != nil ? prefix! + " " : "") + name
    }
}

let ncc1701 = Starship(name: "Enterprise", prefix: "USS")
print(ncc1701.fullName)  // "USS Enterprise" 좜λ ₯

μ€‘μš”: 읽기-μ“°κΈ° μš”κ΅¬μ‚¬ν•­(get set)이 μžˆλŠ” ν”„λ‘œνΌν‹°λŠ” μƒμˆ˜ μ €μž₯ ν”„λ‘œνΌν‹°λ‚˜ 읽기 μ „μš© 계산 ν”„λ‘œνΌν‹°λ‘œ κ΅¬ν˜„ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

πŸ“Œ λ©”μ„œλ“œ μš”κ΅¬μ‚¬ν•­ (Method Requirements)

ν”„λ‘œν† μ½œμ€ νŠΉμ • μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œμ™€ νƒ€μž… λ©”μ„œλ“œ κ΅¬ν˜„μ„ μš”κ΅¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

protocol RandomNumberGenerator {
    func random() -> Double
}

λ³€κ²½ λ©”μ„œλ“œ μš”κ΅¬μ‚¬ν•­ (Mutating Method Requirements)

κ΅¬μ‘°μ²΄λ‚˜ μ—΄κ±°ν˜•μ˜ λ©”μ„œλ“œκ°€ μΈμŠ€ν„΄μŠ€ 자체λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•˜λŠ” 경우, mutating ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€:

protocol Togglable {
    mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case off, on
    
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}

μ°Έκ³ : 클래슀 λ©”μ„œλ“œλŠ” 항상 μΈμŠ€ν„΄μŠ€λ₯Ό μˆ˜μ •ν•  수 μžˆμœΌλ―€λ‘œ 클래슀 κ΅¬ν˜„μ—μ„œλŠ” mutating ν‚€μ›Œλ“œλ₯Ό λͺ…μ‹œν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

πŸ“Œ μ΄ˆκΈ°ν™” ꡬ문 μš”κ΅¬μ‚¬ν•­ (Initializer Requirements)

ν”„λ‘œν† μ½œμ€ νŠΉμ • μ΄ˆκΈ°ν™” ꡬ문을 μš”κ΅¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

protocol SomeProtocol {
    init(someParameter: Int)
}

클래슀의 μ΄ˆκΈ°ν™” ꡬ문 κ΅¬ν˜„

ν΄λž˜μŠ€κ°€ ν”„λ‘œν† μ½œμ˜ μ΄ˆκΈ°ν™” ꡬ문 μš”κ΅¬μ‚¬ν•­μ„ κ΅¬ν˜„ν•  λ•ŒλŠ” required ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€:

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // μ΄ˆκΈ°ν™” κ΅¬ν˜„
    }
}

μ°Έκ³ : final ν΄λž˜μŠ€λŠ” 상속될 수 μ—†μœΌλ―€λ‘œ required ν‚€μ›Œλ“œκ°€ ν•„μš” μ—†μŠ΅λ‹ˆλ‹€.

μƒμœ„ 클래슀의 μ§€μ • μ΄ˆκΈ°ν™” ꡬ문을 μž¬μ •μ˜ν•˜κ³  λ™μ‹œμ— ν”„λ‘œν† μ½œ μš”κ΅¬μ‚¬ν•­μ„ μΆ©μ‘±ν•˜λŠ” 경우, required와 override ν‚€μ›Œλ“œλ₯Ό λͺ¨λ‘ μ‚¬μš©ν•©λ‹ˆλ‹€:

protocol SomeProtocol {
    init()
}

class SomeSuperClass {
    init() {
        // μ΄ˆκΈ°ν™” κ΅¬ν˜„
    }
}

class SomeSubClass: SomeSuperClass, SomeProtocol {
    // "required"λŠ” SomeProtocol μ€€μˆ˜λ₯Ό μœ„ν•΄, "override"λŠ” SomeSuperClassμ—μ„œ 상속
    required override init() {
        // μ΄ˆκΈ°ν™” κ΅¬ν˜„
    }
}

πŸ“Œ νƒ€μž…μœΌλ‘œμ„œμ˜ ν”„λ‘œν† μ½œ (Protocols as Types)

ν”„λ‘œν† μ½œμ€ λ‹€μŒκ³Ό 같은 μƒν™©μ—μ„œ νƒ€μž…μœΌλ‘œ μ‚¬μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€:

  • ν•¨μˆ˜, λ©”μ„œλ“œ, μ΄ˆκΈ°ν™” ꡬ문의 λ§€κ°œλ³€μˆ˜ νƒ€μž… λ˜λŠ” λ°˜ν™˜ νƒ€μž…
  • μƒμˆ˜, λ³€μˆ˜, ν”„λ‘œνΌν‹°μ˜ νƒ€μž…
  • λ°°μ—΄, λ”•μ…”λ„ˆλ¦¬ λ“± μ»¬λ ‰μ…˜μ˜ μš”μ†Œ νƒ€μž…
 
protocol TextRepresentable {
    var textualDescription: String { get }
}

// ν”„λ‘œν† μ½œ νƒ€μž…μ˜ λ°°μ—΄
let textRepresentableThings: [TextRepresentable] = [someTextRepresentableInstance, anotherTextRepresentableInstance]

// ν”„λ‘œν† μ½œ νƒ€μž…μ„ λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ” ν•¨μˆ˜
func printDescription(_ thing: TextRepresentable) {
    print(thing.textualDescription)
}

πŸ“Œ μœ„μž„ (Delegation)

μœ„μž„μ€ ν΄λž˜μŠ€λ‚˜ ꡬ쑰체가 μ±…μž„μ˜ 일뢀λ₯Ό λ‹€λ₯Έ νƒ€μž…(λŒ€λ¦¬μž)μ—κ²Œ λ„˜κΈ°λŠ” λ””μžμΈ νŒ¨ν„΄μœΌλ‘œ, ν”„λ‘œν† μ½œμ„ 톡해 μ‰½κ²Œ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

protocol DiceGameDelegate: AnyObject {
    func gameDidStart(_ game: DiceGame)
    func game(_ game: DiceGame, didEndRound round: Int, winner: Int?)
    func gameDidEnd(_ game: DiceGame)
}

class DiceGame {
    weak var delegate: DiceGameDelegate?
    
    func play() {
        delegate?.gameDidStart(self)
        // κ²Œμž„ 둜직...
        delegate?.gameDidEnd(self)
    }
}

// μœ„μž„ νŒ¨ν„΄ κ΅¬ν˜„
class DiceGameTracker: DiceGameDelegate {
    func gameDidStart(_ game: DiceGame) {
        print("κ²Œμž„μ΄ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€")
    }
    
    func game(_ game: DiceGame, didEndRound round: Int, winner: Int?) {
        // λΌμš΄λ“œ μ’…λ£Œ 처리
    }
    
    func gameDidEnd(_ game: DiceGame) {
        print("κ²Œμž„μ΄ μ’…λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€")
    }
}

μ°Έκ³ : λ©”λͺ¨λ¦¬ λˆ„μˆ˜(κ°•ν•œ μ°Έμ‘° μˆœν™˜)λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ λŒ€λ¦¬μž ν”„λ‘œνΌν‹°λŠ” 보톡 weak 참쑰둜 μ„ μ–Έν•©λ‹ˆλ‹€.

πŸ“Œ ν™•μž₯을 ν†΅ν•œ ν”„λ‘œν† μ½œ 채택 (Adding Protocol Conformance with Extensions)

이미 μ‘΄μž¬ν•˜λŠ” νƒ€μž…μ— ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜κΈ° μœ„ν•΄ ν™•μž₯(extension)을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

protocol TextRepresentable {
    var textualDescription: String { get }
}

// κΈ°μ‘΄ νƒ€μž…μ— ν”„λ‘œν† μ½œ 채택 μΆ”κ°€
extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

쑰건뢀 ν”„λ‘œν† μ½œ μ€€μˆ˜ (Conditional Conformance)

νŠΉμ • μ‘°κ±΄μ—μ„œλ§Œ ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λ„λ‘ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€:

extension Array: TextRepresentable where Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}

μœ„ μ˜ˆμ œμ—μ„œλŠ” λ°°μ—΄μ˜ μš”μ†Œκ°€ TextRepresentable을 μ€€μˆ˜ν•  λ•Œλ§Œ 배열도 TextRepresentable을 μ€€μˆ˜ν•©λ‹ˆλ‹€.

πŸ“Œ ν”„λ‘œν† μ½œ 상속 (Protocol Inheritance)

ν”„λ‘œν† μ½œμ€ ν•˜λ‚˜ μ΄μƒμ˜ λ‹€λ₯Έ ν”„λ‘œν† μ½œμ„ 상속할 수 μžˆμŠ΅λ‹ˆλ‹€:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // μΆ”κ°€ μš”κ΅¬μ‚¬ν•­
}

예λ₯Ό λ“€μ–΄:

protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}

PrettyTextRepresentable을 μ±„νƒν•˜λŠ” νƒ€μž…μ€ TextRepresentable의 μš”κ΅¬μ‚¬ν•­κ³Ό PrettyTextRepresentable의 μΆ”κ°€ μš”κ΅¬μ‚¬ν•­μ„ λͺ¨λ‘ μΆ©μ‘±ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ“Œ 클래슀 μ „μš© ν”„λ‘œν† μ½œ (Class-Only Protocols)

ν”„λ‘œν† μ½œμ΄ ν΄λž˜μŠ€μ—λ§Œ μ±„νƒλ˜λ„λ‘ μ œν•œν•˜λ €λ©΄ AnyObject ν”„λ‘œν† μ½œμ„ μƒμ†ν•©λ‹ˆλ‹€:

protocol SomeClassOnlyProtocol: AnyObject {
    // 클래슀 μ „μš© ν”„λ‘œν† μ½œ μ •μ˜
}

μ‚¬μš© μ‹œκΈ°: μ°Έμ‘° 의미둠(reference semantics)이 ν•„μš”ν•œ κ²½μš°λ‚˜ μ•½ν•œ μ°Έμ‘°(weak reference)κ°€ ν•„μš”ν•œ κ²½μš°μ— 클래슀 μ „μš© ν”„λ‘œν† μ½œμ„ μ‚¬μš©ν•©λ‹ˆλ‹€.

πŸ“Œ ν”„λ‘œν† μ½œ ν•©μ„± (Protocol Composition)

ν•˜λ‚˜μ˜ νƒ€μž…μ΄ μ—¬λŸ¬ ν”„λ‘œν† μ½œμ„ λ™μ‹œμ— μ€€μˆ˜ν•˜λ„λ‘ μš”κ΅¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

protocol Named {
    var name: String { get }
}

protocol Aged {
    var age: Int { get }
}

// Named와 Aged ν”„λ‘œν† μ½œμ„ λͺ¨λ‘ μ€€μˆ˜ν•˜λŠ” νƒ€μž…λ§Œ ν—ˆμš©
func wishHappyBirthday(to celebrator: Named & Aged) {
    print("생일 μΆ•ν•˜ν•©λ‹ˆλ‹€, \(celebrator.name)λ‹˜! 당신은 \(celebrator.age)살이 λ˜μ—ˆκ΅°μš”!")
}

클래슀 νƒ€μž…κ³Ό ν”„λ‘œν† μ½œλ„ ν•¨κ»˜ μ‘°ν•©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

func beginConcert(in location: Location & Named) {
    // Location의 ν•˜μœ„ ν΄λž˜μŠ€μ΄λ©΄μ„œ Named ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ” νƒ€μž…λ§Œ ν—ˆμš©
}

πŸ“Œ ν”„λ‘œν† μ½œ μ€€μˆ˜μ„± 검사 (Checking Protocol Conformance)

νƒ€μž…μ΄ νŠΉμ • ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ is 및 as μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

if someObject is HasArea {
    // someObjectλŠ” HasArea ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•¨
}

// λ‹€μš΄μΊμŠ€νŒ… (μ•ˆμ „ν•œ 방식)
if let objectWithArea = someObject as? HasArea {
    print("면적은 \(objectWithArea.area)μž…λ‹ˆλ‹€")
}

πŸ“Œ μ˜΅μ…”λ„ ν”„λ‘œν† μ½œ μš”κ΅¬μ‚¬ν•­ (Optional Protocol Requirements)

ν”„λ‘œν† μ½œμ˜ 일뢀 μš”κ΅¬μ‚¬ν•­μ„ μ„ νƒμ μœΌλ‘œ κ΅¬ν˜„ν•  수 있게 ν•˜λ €λ©΄ @objc 속성과 optional ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€:

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

μ˜΅μ…”λ„ μš”κ΅¬μ‚¬ν•­μ„ κ°€μ§„ ν”„λ‘œν† μ½œμ€ Objective-C ν΄λž˜μŠ€λ‚˜ @objc 속성이 ν‘œμ‹œλœ ν΄λž˜μŠ€μ—μ„œλ§Œ 채택할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜΅μ…”λ„ μš”κ΅¬μ‚¬ν•­μ„ μ‚¬μš©ν•  λ•ŒλŠ” μ˜΅μ…”λ„ 체이닝을 톡해 μ•ˆμ „ν•˜κ²Œ μ ‘κ·Όν•©λ‹ˆλ‹€:

if let amount = dataSource?.increment?(forCount: count) {
    count += amount
}

πŸ“Œ ν”„λ‘œν† μ½œ ν™•μž₯ (Protocol Extensions)

ν”„λ‘œν† μ½œμ— κΈ°λ³Έ κ΅¬ν˜„μ„ μ œκ³΅ν•˜κΈ° μœ„ν•΄ ν™•μž₯을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

이제 RandomNumberGenerator ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ”, λͺ¨λ“  νƒ€μž…μ€ μžλ™μœΌλ‘œ randomBool() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ œμ•½ 쑰건이 μžˆλŠ” ν”„λ‘œν† μ½œ ν™•μž₯

νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ” κ²½μš°μ—λ§Œ ν”„λ‘œν† μ½œ ν™•μž₯을 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

extension Collection where Element: Equatable {
    func allEqual() -> Bool {
        for element in self {
            if element != self.first {
                return false
            }
        }
        return true
    }
}

// μ‚¬μš© 예
let equalNumbers = [100, 100, 100, 100]
print(equalNumbers.allEqual())  // true

μœ„ μ˜ˆμ œμ—μ„œλŠ” Collection의 μš”μ†Œκ°€ Equatable을 μ€€μˆ˜ν•  λ•Œλ§Œ allEqual() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“Œ ν”„λ‘œν† μ½œκ³Ό μžλ™ ν•©μ„± (Synthesized Implementations)

SwiftλŠ” νŠΉμ • ν”„λ‘œν† μ½œμ— λŒ€ν•΄ μžλ™μœΌλ‘œ κ΅¬ν˜„μ„ μ œκ³΅ν•©λ‹ˆλ‹€:

  • Equatable: μ €μž₯ ν”„λ‘œνΌν‹°λ§Œ μžˆλŠ” κ΅¬μ‘°μ²΄λ‚˜ μ—΄κ±°ν˜•
  • Hashable: μ €μž₯ ν”„λ‘œνΌν‹°λ§Œ μžˆλŠ” κ΅¬μ‘°μ²΄λ‚˜ μ—΄κ±°ν˜•
  • Comparable: μ›μ‹œκ°’μ΄ μ—†λŠ” μ—΄κ±°ν˜•
 
struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
    // == μ—°μ‚°μžκ°€ μžλ™μœΌλ‘œ 합성됨
}

enum SkillLevel: Comparable {
    case beginner
    case intermediate
    case expert(stars: Int)
    // <, >, <=, >= μ—°μ‚°μžκ°€ μžλ™μœΌλ‘œ 합성됨
}

πŸ“Œ 정리

ν”„λ‘œν† μ½œμ€ Swift의 핡심 κΈ°λŠ₯ 쀑 ν•˜λ‚˜λ‘œ, λ‹€μ–‘ν•œ νƒ€μž…μ΄ κ³΅ν†΅λœ κΈ°λŠ₯을 κ΅¬ν˜„ν•  수 μžˆλ„λ‘ μœ μ—°ν•œ 청사진을 μ œκ³΅ν•©λ‹ˆλ‹€. ν”„λ‘œν† μ½œμ„ 톡해:

  1. μΈν„°νŽ˜μ΄μŠ€ 뢄리: κ΅¬ν˜„κ³Ό μΈν„°νŽ˜μ΄μŠ€λ₯Ό λΆ„λ¦¬ν•˜μ—¬ μ½”λ“œμ˜ λͺ¨λ“ˆμ„± ν–₯상
  2. λ‹€ν˜•μ„± 제곡: μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…μ΄ κ³΅ν†΅λœ λ™μž‘μ„ κ΅¬ν˜„
  3. μ½”λ“œ μž¬μ‚¬μš©: ν”„λ‘œν† μ½œ ν™•μž₯을 ν†΅ν•œ κΈ°λ³Έ κ΅¬ν˜„ 제곡
  4. ν…ŒμŠ€νŠΈ μš©μ΄μ„±: λͺ¨μ˜ 객체(mock)λ₯Ό μ‰½κ²Œ λ§Œλ“€μ–΄ ν…ŒμŠ€νŠΈ κ°€λŠ₯

ν”„λ‘œν† μ½œμ€ 객체 μ§€ν–₯ ν”„λ‘œκ·Έλž˜λ°κ³Ό ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ μž₯점을 κ²°ν•©ν•œ ν”„λ‘œν† μ½œ μ§€ν–₯ ν”„λ‘œκ·Έλž˜λ°(Protocol-Oriented Programming)의 기반이 λ©λ‹ˆλ‹€.