[Swift] 프로토콜

Daehyeon Yun·2025년 3월 9일
post-thumbnail

7. 프로토콜(Protocol)

  • Swift의 프로토콜은 인터페이스(Interface) 역할을 수행하며 특정 기능에 대한 청사진(Blueprint)을 정의한다.
  • 프로토콜을 채택(Adopt) 한 타입(구조체, 클래스, 열거형)은 해당 프로토콜에서 요구하는 속성과 메서드를 반드시 구현해야 한다.
  • 프로토콜의 핵심 개념 3가지
    • 규약 정의 : 프로토콜은 특정 메서드, 속성, 연산자 등을 정의한다.
    • 구현 강제 : 프로토콜을 채택한 타입은 반드시 규악을 준수해야 한다.
    • 다형성 지원 : 하나의 프로토콜로 여러 타입을 동일하게 다룰 수 있다.
  • 프로토콜의 특징
    특징설명
    메서드 요구특정 메서드를 요구할 수 있음
    속성 요구특정 속성(프로퍼티)을 요구할 수 있음
    다중 채택 가능하나의 타입이 여러 프로토콜을 동시에 채택 가능
    확장(Extension)프로토콜에 기본 구현 제공 가능
  • 프로토콜 정의 및 채택(사용법)
protocol Driveable {
	func speedDown(with speed: Int) -> Int
}
// Driveable -> 프로토콜 이름
//speedDown(with:) -> 프로토콜이 요구하는 메소드 시그니처

// 원하는 타입(구조체, 클래스, 열거형)에서 프로토콜 채택
// 구조체
struct KIA: Driveable {
	func speedDown(with speed: Int) -> Int {
		print("속도가 줄어듭니다.")
		return speed - 5
	}
}

// KIA 구조체가 Driveable 프로토콜을 채택(Adopt)
// speedDown(with:) 메서드를 필수로 구현.

7-1. 프로토콜을 활용한 다형성(Polymorphism)

  • 프로토콜을 사용하면 서로 다른 타입을 하나의 프로토콜 타입으로 묶어 처리할 수 있다.
  • 아래의 예시는 cars 배열에 Driveable 프로토콜을 채택한 모든 타입을 담을 수 있다는 것을 보여준다. → 다형성을 통해 코드의 유연성과 확장성 증가!
    let cars: [Driveable] = [KIA(), Hyundai()]
    
    // cars 배열에 Driveable 프로토콜을 채택한 서로 다른 구조체 저장
    cars.first?.speedDown(with: 100)
    cars.randomElement()?.speedDown(with: 100)

7.2 프로토콜 확장(Protocol Extension)

  • 객체지향의 오버라이드(Overried)와 같은 개념이다.
  • 정의된 프로토콜에 추가적인 메소드 구현을 진행할 수 있다.
protocol Driveable {
    func speedDown(with speed: Int) -> Int
}

extension Driveable {
    func stop() {
        print("차량이 멈춥니다.")
    }
}

let cars: [Driveable] = [KIA(), Hyundai()]
cars.first?.stop() // 차량이 멈춥니다.

Tip. SwiftUI의 Identifiable 프로토콜

  • SwiftUI의 ForEach 는 각 항목을 고유하게 구분하기 위하여 Identifiable 프로토콜을 요구한다.
    • 컬렉션의 각 항목을 식별하여 뷰가 효율적으로 업데이트 되도록 하는 역할을 수행한다.
    • 💡 React 에서 반복되는 컴포넌트의 Key 가 고유한 식별자로 사용되는 것과 같은 기능이다.
  • SwiftUI에서 Identifiable 프로토콜의 역할은 아래와 같다.
    1. 각 항목에 고유한 식별자(id)를 제공한다.
    2. SwiftUI의 ForEach 가 항목을 추적하고 변경 사항을 감지할 수 있도록 돕는다.
    3. 리렌더링을 최적화하여 항목치 추가,삭제,변경될 때 정확히 어떤 항목이 수정되었는지 빠르게 파악한다.
  • Identifiable 프로토콜의 정의
protocol Identifiable {
    associatedtype ID: Hashable // Hashable을 준수해야한다. (UUID, Int, String 등)
    var id: ID { get } // 필수 구현 요소. 해당 값은 고유해야 한다.
}
  • 커스텀 프로토콜과 Identifiable 프로토콜을 채택한 코드의 예시
import SwiftUI

// Driveable 프로토콜을 채택하고 Identifiable도 채택
protocol Driveable: Identifiable {
    func speedDown(with speed: Int) -> Int
}

struct KIA: Driveable {
    let id = UUID() // 고유 식별자
    func speedDown(with speed: Int) -> Int {
        print("속도가 줄어듭니다.")
        return speed - 5
    }
}

struct Hyundai: Driveable {
    let id = UUID() // 고유 식별자
    func speedDown(with speed: Int) -> Int {
        print("속도가 더욱 줄어듭니다.")
        return speed - 10
    }
}

struct CarListView: View {
    let cars: [Driveable] = [KIA(), Hyundai()]

    var body: some View {
        VStack {
            ForEach(cars) { car in
                Text("차량 감속 테스트 중")
            }
        }
    }
}

#Preview {
    CarListView()
}

/*
	1. KIA, Hyundai 구조체는 Driveable과 Identifiable 프로토콜을 동시에 채택한다.
	2. 각 구조체 인스턴스에 id를 UUID()로 할당하여 인스턴스가 고유한 식별자를 가지도록 유도
	3. ForEach는 각 인스턴스를 고유하게 식별하게 된다.
*/

profile
열심히 살아야지

0개의 댓글