오늘은 Swift 프로그래밍의 가장 근간이 되는 두 가지 사용자 정의 데이터 타입, 바로 구조체(Struct)와 클래스(Class)에 대해 깊이 파고들 것입니다.
구조체와 클래스는 데이터를 캡슐화하고, 관련된 기능들을 묶어서 관리하는 객체 지향 프로그래밍의 핵심 요소입니다.
이 둘은 겉보기에는 비슷해 보이지만, 내부적으로 데이터를 다루는 방식에 큰 차이가 있어 Swift 개발에서 매우 중요한 선택의 기로에 서게 합니다.
언제 무엇을 사용해야 할지 명확히 이해하는 것이 견고하고 효율적인 앱을 만드는 데 결정적인 역할을 할 것입니다.
1. 구조체(Struct): 값 타입(Value Type)의 대표 주자
구조체는 관련된 데이터를 하나로 묶어 표현하는 경량화된 타입입니다. Swift의 대부분의 기본 데이터 타입(Int, String, Array, Dictionary 등)이 사실은 구조체로 구현되어 있습니다. 구조체의 가장 큰 특징은 **값 타입(Value Type)**이라는 점입니다.
💡 값 타입이란?
값 타입은 데이터를 복사할 때, 실제 값을 통째로 복사합니다. 마치 문서를 복사해서 원본과 완전히 다른 사본을 만드는 것과 같습니다. 사본을 아무리 수정해도 원본에는 영향을 주지 않습니다.
//Swift 값 타입 예제
struct Point {
var x: Int
var y: Int
}
var p1 = Point(x: 10, y: 20)
var p2 = p1 // p1의 값이 p2로 복사됨 (별개의 복사본 생성)
print("p1: (\(p1.x), \(p1.y))") // 출력: p1: (10, 20)
print("p2: (\(p2.x), \(p2.y))") // 출력: p2: (10, 20)
p2.x = 30 // p2의 값만 변경
p2.y = 40
print("p1 (변경 후): (\(p1.x), \(p1.y))") // 출력: p1 (변경 후): (10, 20) - p1은 변하지 않음
print("p2 (변경 후): (\(p2.x), \(p2.y))") // 출력: p2 (변경 후): (30, 40)
위 예시에서 p2 = p1 을 했을 때 p1 의 모든 값이 p2 로 복사되었고, 이후 p2를 변경해도 p1은 영향을 받지 않는 것을 볼 수 있습니다.
2. 클래스(Class): 참조 타입(Reference Type)의 대표 주자
클래스는 객체 지향 프로그래밍의 전통적인 구성 요소로, 구조체보다 더 많은 기능을 제공합니다. 클래스는 참조 타입(Reference Type)이라는 특징을 가집니다.
💡 참조 타입이란?
참조 타입은 데이터를 복사할 때, 데이터가 저장된 '메모리 주소'를 복사합니다.
마치 원본 문서가 있는 폴더의 '바로가기'를 만드는 것과 같습니다.
여러 개의 바로가기가 있더라도 모두 같은 원본 문서를 가리키므로, 어떤 바로가기를 통해 원본 문서를 수정하면 다른 모든 바로가기를 통해서도 수정된 내용이 보입니다.
//Swift 참조타입 예제
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
var person1 = Person(name: "김민수", age: 30)
var person2 = person1 // person1이 참조하는 메모리 주소가 person2에 복사됨
print("person1 이름: \(person1.name), 나이: \(person1.age)") // 출력: 김민수, 30
print("person2 이름: \(person2.name), 나이: \(person2.age)") // 출력: 김민수, 30
person2.name = "박영희" // person2가 참조하는 객체의 속성 변경
person2.age = 25
print("person1 이름 (변경 후): \(person1.name), 나이: \(person1.age)") // 출력: 박영희, 25 - person1도 같이 변경됨!
print("person2 이름 (변경 후): \(person2.name), 나이: \(person2.age)") // 출력: 박영희, 25
위 예시에서 person2 = person1을 했을 때, person1과 person2는 같은 Person 객체를 참조하게 됩니다. 따라서 person2를 통해 객체의 속성을 변경하면, person1을 통해서도 변경된 값이 반영되는 것을 볼 수 있습니다.
3. 구조체 vs 클래스: 주요 차이점 비교
4. 언제 무엇을 사용해야 할까요? Swift의 선택 가이드
Swift에서는 대부분의 경우 구조체를 사용하는 것을 기본 원칙(Value Semantics)으로 삼는 것이 좋습니다. Apple도 이 방식을 권장하며, SwiftUI와 같은 최신 프레임워크는 구조체를 중심으로 설계되어 있습니다.
💡 구조체를 권장하는 경우:
- 값의 복사가 필요할 때: 데이터를 전달할 때 원본의 복사본이 필요하여, 원본 변경으로부터 독립성을 유지하고 싶을 때.
- 간단한 데이터를 캡슐화할 때: 몇 개의 속성만으로 구성된 작은 데이터 묶음을 표현할 때 (예: Point, Size, Color, Date).
- 상속이 필요 없을 때: 다른 타입으로부터 기능을 물려받거나, 다른 타입에게 기능을 물려줄 필요가 없을 때.
- 멀티스레드 환경에서 안전성: 값 타입은 복사되므로, 여러 스레드에서 동시에 접근해도 데이터 충돌 위험이 적어 스레드 안전성(Thread Safety)을 확보하기 용이합니다.
💡 클래스를 권장하는 경우:
- 참조 공유가 필요할 때: 하나의 인스턴스를 여러 곳에서 공유하고, 한 곳에서 변경하면 다른 모든 곳에 그 변경이 반영되어야 할 때 (예: Singleton 객체, UIViewController).
- 상속이 필요할 때: 기존 클래스의 기능을 확장하거나 재정의해야 할 때. (Cocoa/Cocoa Touch 프레임워크의 많은 부분이 클래스 상속을 기반으로 함
- 클래스 인스턴스의 타입 확인 및 캐스팅이 필요할 때: 런타임에 인스턴스의 특정 클래스 타입을 확인하거나 다른 클래스 타입으로 캐스팅해야 할 때.
deinit
(디이니셜라이저)를 사용하여 리소스 해제가 필요할 때: 클래스 인스턴스가 메모리에서 해제될 때 특정 정리 작업을 수행해야 할 때.
정리하며
오늘은 Swift 프로그래밍의 두 핵심 타입인 구조체와 클래스에 대해 깊이 있게 알아보았습니다.
- 구조체(Struct): 값 타입이며, 데이터 복사 시 실제 값을 복사하여 독립성을 보장합니다. 스택 메모리에 주로 할당되어 일반적으로 성능이 더 좋습니다.
- 클래스(Class): 참조 타입이며, 데이터 복사 시 메모리 주소를 복사하여 하나의 인스턴스를 여러 곳에서 공유할 수 있습니다. 상속, 타입 캐스팅, 디이니셜라이저 등의 기능을 제공합니다.
Swift에서는 특별한 이유가 없다면 구조체를 기본적으로 사용하고, 참조 공유나 상속과 같은 클래스만의 고유한 기능이 필요할 때만 클래스를 사용하는 것이 좋은 개발 습관입니다. 이 선택은 앱의 성능, 안정성, 유지보수성에 큰 영향을 미치므로 충분히 고민하고 결정해야 합니다.
다음 시간에는 관련된 값들을 하나의 타입으로 묶는 강력한 도구인 열거형(Enum)에 대해 알아보겠습니다.
궁금한 점이 있다면 언제든지 댓글로 남겨주세요.
댓글 없음:
댓글 쓰기