Objective-C나 Java를 먼저 배우고 Swift로 넘어오면 꼭 한 번 걸리는 지점이 있어요.
바로 “객체를 복제하려면 어떻게 하지?“라는 질문이에요.
clone() 같은 메서드를 찾아 헤매기 쉬운데, Swift에서는 그럴 필요가 거의 없어요.
결론부터 말씀드릴게요.
Swift는 구조체·배열 같은 값 타입과 Copy-on-Write(복사 시점 지연 복사) 최적화 덕분에, 디자인 패턴에서 말하는 ’프로토타입 패턴’을 언어 차원에서 대체합니다.
객체를 복제하려고 별도 패턴을 설계할 필요 없이, 그냥 값을 대입하기만 하면 안전한 복사본이 생겨요.
이 글에서는 프로토타입 패턴이 원래 뭘 해결하려던 건지, Swift의 값 타입과 Copy-on-Write가 그걸 어떻게 자연스럽게 흡수하는지 예제와 함께 정리해 드릴게요.
프로토타입 패턴이 원래 풀려던 문제
프로토타입 패턴은 GoF 디자인 패턴 중 생성 패턴에 속해요.
핵심은 간단해요. 새 객체를 처음부터 만드는 대신, 이미 있는 객체를 복제해서 새 인스턴스를 얻는 방식이에요.
왜 이런 게 필요했을까요?
Objective-C나 Java 같은 언어에서 객체는 대부분 참조 타입이에요.
변수에 객체를 대입하면 값이 복사되는 게 아니라, 같은 객체를 가리키는 주소만 복사돼요.
그래서 A를 바꾸면 B도 같이 바뀌는 사고가 나요.
이걸 피하려고 개발자가 직접 clone()이나 copy() 메서드를 만들어 “진짜 복사본”을 반환하게 했던 거예요.
프로토타입 패턴은 이런 상황을 위한 도구예요.
- 객체 생성 비용이 큰데, 비슷한 객체가 여러 개 필요할 때
- 원본을 건드리지 않고 독립된 사본이 필요할 때
- 복제 로직을 객체 스스로 책임지게 하고 싶을 때
Swift 값 타입, 대입만 하면 복사가 끝나요
Swift의 구조체(struct)와 열거형(enum), 그리고 Array·Dictionary·String 같은 표준 타입은 전부 값 타입이에요.
값 타입은 대입하거나 함수에 넘기는 순간 복사돼요.
다시 말해 언어가 알아서 복사본을 만들어 줘요.
예제로 보면 훨씬 명확해요.
struct Point { var x: Int; var y: Int }
var a = Point(x: 1, y: 2)
var b = a // 이 순간 값이 복사됨
b.x = 99
// a.x는 여전히 1, b.x만 99
b = a 한 줄이 프로토타입 패턴의 clone()이 하던 일을 대신해 줘요.
별도의 복제 메서드도, 복사 프로토콜 채택도 필요 없어요.
원본 a는 안전하게 보호되고, b는 완전히 독립된 사본이 돼요.
객체가 서로 얽혀서 생기는 그 골치 아픈 버그가, 애초에 발생할 수 없는 구조인 거예요.
Copy-on-Write가 성능 문제를 푸는 방법
여기서 자연스럽게 걱정이 생겨요.
“대입할 때마다 통째로 복사하면, 큰 배열은 너무 느려지지 않나요?”
맞는 지적이에요. 그래서 Swift가 쓰는 최적화가 바로 Copy-on-Write, 줄여서 CoW예요.
CoW의 원리는 이래요.
값을 대입하면 일단은 내부 저장 공간을 공유해요. 실제 데이터 복사는 미루죠.
그러다 둘 중 하나가 값을 수정하려는 순간, 그때 비로소 진짜 복사가 일어나요.
| 시점 | 내부 동작 | 비용 |
|---|---|---|
| 대입할 때 | 저장 공간 공유 (참조만) | 매우 저렴 |
| 읽기만 할 때 | 계속 공유 유지 | 복사 없음 |
| 값을 수정할 때 | 이때 실제로 복사 | 이 시점에만 발생 |
덕분에 개발자는 값 타입의 안전함을 누리면서도, 불필요한 복사 비용은 내지 않아요.
읽기만 하고 안 고치면 복사는 아예 안 일어나거든요.
Array, Dictionary, Set, String 같은 표준 라이브러리 타입에는 이 CoW가 이미 들어가 있어요.
우리가 따로 구현하지 않아도 언어와 표준 라이브러리가 알아서 처리해 줘요.
그래도 프로토타입 패턴이 필요한 경우가 있을까요?
“그럼 Swift에서 프로토타입 패턴은 완전히 죽은 개념인가요?”
꼭 그렇지는 않아요.
클래스(class)를 써야 하는 상황이 있거든요. 참조 의미가 꼭 필요하거나, Objective-C와 상호 운용해야 하거나, 상속이 필요한 경우죠.
이럴 때 클래스 인스턴스의 독립 사본이 필요하면, 여전히 복제 로직을 직접 만들어야 해요.
Swift에는 NSCopying 프로토콜과 copy()가 있고, 이게 사실상 프로토타입 패턴의 전통적인 모습이에요.
이렇게 나눠 생각하시면 편해요.
- 구조체·값 타입을 쓴다 → 대입만으로 복사 완료, 프로토타입 패턴 불필요
- 클래스를 꼭 써야 한다 → 필요 시 직접 복제 로직 구현, 여기서 패턴이 살아남음
그래서 Swift 커뮤니티에서 “값 타입을 우선 고려하라”는 조언을 자주 하는 거예요.
값 타입을 기본으로 두면 복제 문제 자체가 사라지니까요.
마무리하며
낯선 언어의 디자인 패턴을 억지로 옮겨오려다 보면 오히려 코드가 복잡해질 때가 많아요.
Swift에서는 값 타입과 Copy-on-Write가 프로토타입 패턴의 자리를 자연스럽게 채워 준다는 것, 이것만 기억하셔도 복제 관련 고민이 훨씬 가벼워질 거예요.
다음에 객체를 복사할 일이 생기면, clone()을 찾기 전에 “이거 구조체로 바꾸면 되지 않을까?“부터 떠올려 보세요. 분명 도움이 될 거예요.
