[2026] Swift 프로토콜과 확장 | Protocol, Extension
이 글의 핵심
protocol Drawable { func draw() var color: String { get set } }.
들어가며
프로토콜은 “이 타입이 구현해야 할 행동”을 설계도처럼 적어 두는 역할입니다. 클래스 상속 대신 프로토콜 확장으로 공통 구현을 나누는 패턴이 흔합니다.
1. 프로토콜 정의
기본 프로토콜
다음은 swift를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
protocol Drawable {
func draw()
var color: String { get set }
}
struct Circle: Drawable {
var color: String
func draw() {
print("\(color) 원 그리기")
}
}
let circle = Circle(color: "빨강")
circle.draw()
프로퍼티 요구사항
아래 코드는 swift를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
protocol Named {
var name: String { get } // 읽기 전용
var fullName: String { get set } // 읽기/쓰기
}
struct Person: Named {
var name: String
var fullName: String
}
메서드 요구사항
아래 코드는 swift를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
protocol Resettable {
mutating func reset()
}
struct Counter: Resettable {
var count = 0
mutating func reset() {
count = 0
}
}
다중 프로토콜 채택
다음은 swift를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
protocol Drawable {
func draw()
}
protocol Movable {
func move(to x: Int, y: Int)
}
struct Shape: Drawable, Movable {
func draw() {
print("도형 그리기")
}
func move(to x: Int, y: Int) {
print("(\(x), \(y))로 이동")
}
}
2. Extension (확장)
기본 확장
아래 코드는 swift를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
extension Int {
func squared() -> Int {
return self * self
}
var isEven: Bool {
return self % 2 == 0
}
}
print(5.squared()) // 25
print(4.isEven) // true
프로토콜 구현 분리
아래 코드는 swift를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
protocol Describable {
func describe() -> String
}
struct User {
let name: String
let age: Int
}
extension User: Describable {
func describe() -> String {
return "\(name), \(age)세"
}
}
조건부 확장
아래 코드는 swift를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
extension Array where Element: Numeric {
func sum() -> Element {
return reduce(0, +)
}
}
let numbers = [1, 2, 3, 4, 5]
print(numbers.sum()) // 15
3. 프로토콜 기본 구현
다음은 swift를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
protocol Greetable {
var name: String { get }
func greet()
}
extension Greetable {
func greet() {
print("안녕하세요, \(name)님!")
}
}
struct Person: Greetable {
let name: String
}
let person = Person(name: "홍길동")
person.greet() // 안녕하세요, 홍길동님!
4. 실전 예제
예제: 도형 시스템
다음은 swift를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
protocol Shape {
func area() -> Double
func perimeter() -> Double
}
extension Shape {
func describe() {
print("넓이: \(area()), 둘레: \(perimeter())")
}
}
struct Circle: Shape {
let radius: Double
func area() -> Double {
return Double.pi * radius * radius
}
func perimeter() -> Double {
return 2 * Double.pi * radius
}
}
struct Rectangle: Shape {
let width: Double
let height: Double
func area() -> Double {
return width * height
}
func perimeter() -> Double {
return 2 * (width + height)
}
}
let shapes: [Shape] = [
Circle(radius: 5),
Rectangle(width: 10, height: 20)
]
for shape in shapes {
shape.describe()
}
실전 심화 보강
실전 예제: 네트워크 레이어 추상화 (URLSession 대역 테스트용)
프로토콜로 요청을 감싸면, 프로덕션은 실제 세션, 테스트는 목을 주입할 수 있습니다. 1단계: 요청을 나타내는 타입과 응답 프로토콜을 정의합니다. 다음은 swift를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import Foundation
protocol HTTPClientProtocol {
func data(for request: URLRequest) async throws -> (Data, URLResponse)
}
extension URLSession: HTTPClientProtocol {}
struct UserDTO: Codable {
let id: Int
let name: String
}
protocol UserProviding {
func fetchUser(id: Int) async throws -> UserDTO
}
struct RemoteUserService: UserProviding {
let baseURL: URL
let client: HTTPClientProtocol
func fetchUser(id: Int) async throws -> UserDTO {
let url = baseURL.appendingPathComponent("users/\(id)")
let req = URLRequest(url: url)
let (data, _) = try await client.data(for: req)
return try JSONDecoder().decode(UserDTO.self, from: data)
}
}
struct MockHTTPClient: HTTPClientProtocol {
let data: Data
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
(data, HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!)
}
}
2단계: 테스트에서 MockHTTPClient에 JSON 문자열을 넣어 디코딩 경로만 검증합니다.
자주 하는 실수
- 프로토콜에 연관 타입이 필요한데 일반 제네릭처럼 써서 컴파일 에러가 나는 경우.
extension으로 기본 구현을 줬는데 구체 타입에서 시그니처를 살짝 바꿔 의도치 않은 오버로드가 생기는 경우.Shape같은 프로토콜을 배열에 넣을 때any Shape(existential)와 제네릭의 차이를 모르고 성능·동적 디스패치를 혼동하는 경우.
주의사항
- Objective-C와 브리징 시 @objc protocol은
struct준수가 불가능합니다. - 프로토콜 확장의 기본 구현은
mutating요구사항과 조합할 때 제약이 까다롭습니다. 필요하면Self제약을 명시하세요.
실무에서는 이렇게
- 작은 프로토콜 여러 개(읽기 전용, 쓰기 전용)로 쪼개면 조합이 쉽고 테스트도 가벼워집니다.
- UI 레이어에는
ObservableObject+ 프로토콜로 ViewModel을 추상화해 프리뷰에 목 객체를 주입합니다. - 문서화를 위해 프로토콜 이름은
-able,-ing,Providing등 팀 컨벤션을 통일합니다.
비교 및 대안
| 방식 | 장점 | 단점 |
|---|---|---|
| 프로토콜 + 구조체 | 값 타입, 조합 용이 | 프로토콜 존재형 저장 시 주의 |
| 상속 기반 베이스 클래스 | 공통 코드 한곳 | UIKit 스타일 결합도↑ |
| 제네릭만 사용 | 정적 디스패치 | 타입 폭발 가능 |
추가 리소스
정리
핵심 요약
- Protocol: 인터페이스, 요구사항 정의
- Extension: 기존 타입에 기능 추가
- 기본 구현: 프로토콜 확장으로 제공
- POP: 상속 대신 프로토콜 조합
- 조건부 확장: where 절로 제약