스파르타 코딩 클럽 - iOS 스타터 6기/본 캠프

17. 스파르타 코딩 클럽 - 프로토콜 (Protocol), 확장 (Extension)

seongpil Heo 2025. 3. 17. 21:59

 📚 프로토콜 (Protocol)

  • 프로토콜 자체는 기능을 구현하지 않으며, 오직 설계만 제공합니다.
  • class, struct, enum에서 프로토콜을 채택할 수 있으며, 프로토콜에서 정의한 프로퍼티와 메소드를 모두 구현해야 합니다.
    • 프로토콜을 채택하는 방법은 타입의 이름 뒤에 : 콜론을 넣은 후 프로토콜 이름을 작성하면 됩니다.
    • 프로토콜은 여러 개를 채택 할 수 있으며, 프로토콜 이름을 , 로 구분합니다.
  • 프로토콜에서 정의된 프로퍼티는 항상 var로 선언되어야 합니다.
  • 프로토콜에서 정의하는 프로퍼티는 읽기 전용 { get } 또는 읽기-쓰기 가능 { get set }으로 설정할 수 있습니다.
    • { get }으로만 설정해도 프로퍼티의 값을 변경할 수 있지만, 명시적으로 작성하면 코드의 의도를 쉽게 파악할 수 있습니다.
  • 프로토콜에서 정의하는 메소드는 이름, 파라미터, 리턴타입만 선언하며, 구현부 {   }는 작성하지 않습니다.
  • Swift에서 프로토콜은 다른 언어에서 말하는 인터페이스 개념과 유사합니다.

 프로토콜 정의 방법

1️⃣ 기본 정의 방법

protocol 프로토콜이름 {
	// 프로퍼티 정의
	// 메소드 정의
}

protocol FullyNamed {
	var fullName: String { get } 
	
	func sayMyFullName() -> String // 구현부는 작성하지 않습니다.
}

 

2️⃣ 프로토콜 채택하여 구현하는 방법

// 1개의 프로토콜 채택

protocol FullyNamed {
	var fullName: String { get } 
	
	func sayMyFullName() -> String // 구현부는 작성하지 않습니다.
}

class Person: FullyNamed { // FullyName 프로토콜을 채택합니다.
    var fullName: String  // FullyName 프로토콜에 있는 fullName 프로퍼티를 구현해야 합니다.
    
    func sayMyFullName() -> String { // 프로토콜에 있는 메소드를 구현해야 합니다.
        return fullName
    }
    
    init(fullName: String) {
        self.fullName = fullName
    }
}

var person = Person(fullName: "Brody")

print(person.fullName) // "Brody" 출력
print(person.sayMyFullName()) // "Brody" 출력

 

3️⃣ 여러개의 프로토콜 채택 하는 방법

// 여러개의 프로토콜 채택

protocol FullyNamed {
    var fullName: String { get }
    
    func sayMyFullName() -> String
}

protocol ShortNamed {
    var shortName: String { get }
}

class Person: FullyNamed, ShortNamed { // 프로토콜 여러개를 채택하는 클래스입니다.
    var fullName: String
    
    func sayMyFullName() -> String {
        return fullName
    }
    
    var shortName: String {
        return "ShortName"
    }
    
    init(fullName: String) {
        self.fullName = fullName
    }
}


var person = Person(fullName: "Brody")

print(person.fullName) // "Brody" 출력
print(person.sayMyFullName()) // "Brody" 출력
print(person.shortName) // "ShortName" 출력

 

4️⃣ 클래스 전용 프로토콜 만들기

  • class 전용 프로토콜은 struct, enum에서 사용될 수 없습니다.
  • 프로토콜 정의 시, AnyObject를 채택하면 클래스 전용 프로토콜로 만들 수 있습니다.
protocol OnlyClassProtocol: AnyObject {

}

 실습하기

import UIKit

// 프로토콜 실습하기

protocol Walkable {
    var feetCount: Int {set get}
    func walk() -> String
}

protocol Introduceable {
    func introduce()
}

class Person: Walkable, Introduceable {
    var feetCount: Int = 2
    var name: String
    func walk() -> String {
        return "사람이 걷습니다"
    }
    
    init(name: String) {
        self.name = name
    }
    
    func introduce() {
        print("이름은 \(name) 입니다. 발 갯수는 \(feetCount)개 입니다.")
    }
}

struct Cat: Walkable, Introduceable {
    var feetCount: Int = 4
    var color: String
    func walk() -> String {
        return "고양이가 걷습니다."
    }
    
    init(color: String) {
        self.color = color
    }
    
    func introduce() {
        print("고양이 털 색은 \(color)이고, 발 갯수는 \(feetCount)개 입니다.")
    }
}


let person = Person(name: "Brody")
person.introduce()
print(person.walk())


let cat = Cat(color: "노란색")
cat.introduce()
print(cat.walk())

 📚 확장 (Extension)

  • extension 키워드를 사용하여 기존 타입을 확장할 수 있습니다.
  • 하나 이상의 프로토콜을 extension으로 추가해 적용할 수 있습니다. 이를 통해 기존 타입을 수정하지 않고 프로토콜 요구사항을 구현할 수 있어 코드 유지보수가 편리해집니다.
  • 하나의 타입에 extension 여러 번 가능합니다.
  • 확장할 수 있는 것들은 아래와 같습니다.
    • 연산 프로퍼티
      1. 확장된 곳에서 저장 프로퍼티는 사용할 수 없습니다.
    • 메소드
    • 새로운 초기화 init
    • 중첩된 타입(Nested Type)

 프로토콜 사용 방법

1️⃣ 기본 사용 방법

// 기본 사용 방법

struct Person {
	 let name: String
}

// extension 키워드 작성 후 확장시키고 싶은 타입 이름을 명시합니다.
extension Person {

}

// 특정 타입의 프로토콜을 확장시키고 싶을 때
extension Person: Equatable {

}

 

2️⃣ 확장할 수 있는 것들

// 확장할 수 있는 것들

struct Person {
    let lastName: String
    let firstName: String
    let age: Int
}

protocol FullyNamed {
    var fullName: String { get }
    
    func sayMyFullName() -> String
}

// extension에서 연산 프로퍼티를 구현할 수 있습니다.
extension Person {
    var nameAge: String {
        return "\(firstName)(\(age)세)"
    }
}

// extension에서 메소드를 구현할 수 있습니다.
extension Person {
    func sayHello() {
        print("\(firstName)님 안녕하세요?")
    }
}

// extension에서 protocol을 채택하여 구현할 수 있습니다.
extension Person: FullyNamed {
    var fullName: String {
        return "\(lastName)\(firstName)"
    }
    
    func sayMyFullName() -> String {
        return "제 이름은 \(fullName)입니다."
    }
}


let person = Person(lastName: "홍", firstName: "길동", age: 20)

print(person.nameAge) // extension에서 구현한 연산프로퍼티를 사용할 수 있습니다.
person.sayHello()     // extension에서 구현한 메소드를 호출할 수 있습니다.
print(person.fullName) // extension에서 구현한 프로토콜을 사용할 수 있습니다.
print(person.sayMyFullName()) // extension에서 구현한 프로토콜을 사용할 수 있습니다.

/* 출력 값
길동(20세)
길동님 안녕하세요?
홍길동
제 이름은 홍길동입니다.
*/

 실습하기

import UIKit

// 프로토콜 실습하기

protocol Walkable {
    var feetCount: Int {set get}
    func walk() -> String
}

protocol Introduceable {
    func introduce()
}

class Person: Introduceable {
    var feetCount: Int = 2
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func introduce() {
        print("이름은 \(name) 입니다. 발 갯수는 \(feetCount)개 입니다.")
    }
}

extension Person {
    var fullNameWithFeetCount: String {
        return "\(name)_\(feetCount)"
    }
    
    func introduceOnlyName() {
        print("안녕하세요. \(name)입니다.")
    }
}

extension Person: Walkable {
    func walk() -> String {
        return "사람이 걷습니다"
    }
}


let person = Person(name: "Brody")
person.introduce()
print(person.walk())

 ✓ TIL

Swift에서 프로토콜은 다른 언어에서 말하는 인터페이스 개념과 유사하다.

프로토콜 내부에서는 선언만 하며, 구현은 채택 후 클래스나 구조체, 열거형 등에서 프로퍼티와 메소드를 모두 구현한다.

 

프로퍼티는 읽기 전용 또는 읽기-쓰기 가능으로 설정할 수 있다.

  • 읽기 전용 : { get }
  • 읽기 - 쓰기 : { get set }

Swift에서 확장은 extension 키워드를 사용하여 기존 타입을 확장할 수 있다.

코드의 유지보수가 편리해진다.