이번에는 Swift 클래스(Class) 심화 과정을 다뤄보겠습니다. 기본적인 클래스 문법과 고급 기능을 넘어서, 더 효율적이고 안정적인 객체지향 프로그래밍(OOP) 을 위한 실전 중심의 클래스 활용법을 소개합니다. 이 과정에서는 클래스의 본질적인 설계 원칙과 디자인 패턴, 메모리 관리 심화(ARC), 프로토콜과 클래스의 결합, 그리고 Swift만의 클래스 활용 전략까지 다루어 실제 프로젝트에 적용할 수 있도록 구성했습니다.
🏛️ Swift 클래스(Class) 심화 과정
– 객체지향 설계부터 메모리 최적화, 디자인 패턴까지!
Swift의 클래스는 단순히 상속과 메서드만을 위한 도구가 아닙니다. 유지보수성과 확장성, 그리고 안정적인 메모리 관리를 고려한 전략적인 설계가 중요합니다. 이번 글에서는 클래스의 심화 개념과 실전 패턴을 알아보겠습니다.
1️⃣ 클래스 설계 원칙: SOLID 원칙 적용하기
객체지향 설계의 기본인 SOLID 원칙을 이해하면
클래스를 더 깔끔하고 확장성 있게 설계할 수 있어요.
원칙 |
설명 |
---|---|
S: 단일 책임 원칙(SRP) |
클래스는 하나의 책임만 가져야 함 |
O: 개방-폐쇄 원칙(OCP) |
확장에는 열려 있고, 수정에는 닫혀 있어야 함 |
L: 리스코프 치환 원칙(LSP) |
자식 클래스는 부모 클래스를 대체할 수 있어야 함 |
I: 인터페이스 분리 원칙(ISP) |
불필요한 의존을 피하기 |
D: 의존 역전 원칙(DIP) |
구체화보다 추상화에 의존 |
Swift에서는 프로토콜(Protocol) 을 적극 활용해 OCP, DIP를 실천할 수 있습니다.
2️⃣ 클래스와 프로토콜의 결합 – 유연한 설계
클래스는 상속 외에도 프로토콜을 통해 기능을 확장하고,
더 유연한 설계를 할 수 있어요.
예제: 프로토콜 기반 설계
protocol Drivable {
func drive()
}
class Car: Drivable {
func drive() {
print("자동차가 달립니다!")
}
}
class Bike: Drivable {
func drive() {
print("자전거가 달립니다!")
}
}
let vehicles: [Drivable] = [Car(), Bike()]
vehicles.forEach { $0.drive() }
-
상속보다 더 가볍고 유연한 설계 가능!
-
실무에서 프로토콜 + 클래스 조합은 필수입니다.
예제: 프로토콜 기반 설계 |
3️⃣ 클래스 메모리 관리 심화 – ARC, weak, unowned
기본적인 ARC 이해를 넘어서,
강한 참조(Strong Reference) 와
약한 참조(Weak Reference),
그리고 unowned 의 차이점을 명확히 알아야 합니다.
키워드 |
설명 |
---|---|
strong |
기본 참조 방식 (카운트 증가) |
weak |
참조 카운트 증가 ❌ (Optional 타입) |
unowned |
참조 카운트 증가 ❌ (Non-Optional, 해제 시 크래시 발생 가능) |
예제: Delegate 패턴에서 weak 사용
protocol DownloadDelegate: AnyObject {
func downloadFinished()
}
class Downloader {
weak var delegate: DownloadDelegate?
func start() {
// 다운로드 완료 시
delegate?.downloadFinished()
}
}
-
delegate 패턴에서는 항상 weak 사용!
4️⃣ 클래스 기반 디자인 패턴
Swift 개발에서 자주 쓰이는 클래스 기반 디자인 패턴을 이해하면
더 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있어요.
✅ 대표 패턴들
패턴명 |
설명 |
---|---|
Singleton |
앱 전체에서 하나의 인스턴스만 사용 |
Delegate |
객체 간 의사소통 (이벤트 전달) |
MVC |
Model-View-Controller 구조 |
Factory |
객체 생성을 캡슐화 |
Observer |
상태 변화 감지 및 알림 |
예제: 싱글톤 패턴
class Settings {
static let shared = Settings()
private init() {}
var theme: String = "Light"
}
Settings.shared.theme = "Dark"
-
전역 상태 관리에 유용하지만, 남용은 주의!
5️⃣ 클래스 상속 최적화 – 추상화와 final 클래스
모든 클래스를 상속 가능하게 열어두면,
예측하기 어려운 코드가 될 수 있어요.
-
추상화(Abstract Class): Swift엔 직접 지원은 없지만,
프로토콜 + 기본 클래스 조합으로 구현 가능
-
final 클래스: 상속 금지로 안정성 확보
final class NetworkManager {
func fetchData() { ... }
}
6️⃣ 클래스 vs 구조체 선택 기준 심화
상황 |
클래스 선택 |
구조체 선택 |
---|---|---|
데이터 복사 vs 참조 |
참조가 필요할 때 |
독립적 데이터 |
상속 필요 여부 |
상속이 필요할 때 |
상속 불필요 |
공유 상태 관리 |
클래스 (싱글톤 등) |
구조체 지양 |
멀티스레드 안전성 |
위험할 수 있음 |
안전 (값 타입) |
SwiftUI 스타일의 선언적 코드 |
지양 |
Struct 사용 |
Swift에서는 가능한 한 구조체 + 프로토콜 중심으로 설계하고,
필요한 경우에만 클래스를 사용하는 것이 트렌드입니다.
✏️ 클래스 심화 실습 과제
Delegate 패턴을 활용한 다운로드 매니저 구현
Factory 패턴으로 다양한 캐릭터 생성 시스템 설계
Observer 패턴으로 상태 변화 감지 시스템 만들기
싱글톤 패턴으로 앱 설정 관리 클래스 구현
Protocol + Class 조합으로 유연한 알림(Notification) 시스템 만들기
✅ 1. Delegate 패턴 – 다운로드 매니저 구현
protocol DownloadDelegate: AnyObject {
func downloadDidFinish(fileName: String)
}
class Downloader {
weak var delegate: DownloadDelegate?
func startDownload(fileName: String) {
print("📥 \(fileName) 다운로드 중...")
// 다운로드 완료 시점 (가정)
delegate?.downloadDidFinish(fileName: fileName)
}
}
class ViewController: DownloadDelegate {
func downloadDidFinish(fileName: String) {
print("✅ 다운로드 완료: \(fileName)")
}
}
// 사용
let downloader = Downloader()
let viewController = ViewController()
downloader.delegate = viewController
downloader.startDownload(fileName: "sample.pdf")
Delegate 패턴 – 다운로드 매니저 구현 |
✅ 2. Factory 패턴 – 캐릭터 생성 시스템
protocol GameCharacter {
var name: String { get }
func attack()
}
class Warrior: GameCharacter {
var name = "전사"
func attack() {
print("🗡️ \(name)이 검으로 공격합니다!")
}
}
class Mage: GameCharacter {
var name = "마법사"
func attack() {
print("✨ \(name)가 마법을 시전합니다!")
}
}
class CharacterFactory {
static func createCharacter(type: String) -> GameCharacter? {
switch type {
case "warrior": return Warrior()
case "mage": return Mage()
default: return nil
}
}
}
// 사용
if let myCharacter = CharacterFactory.createCharacter(type: "mage") {
myCharacter.attack()
}
Factory 패턴 – 캐릭터 생성 시스템 |
✅ 3. Observer 패턴 – 상태 변화 감지 시스템
class GameState {
var health: Int = 100 {
didSet {
print("❤️ 체력 상태가 변경됨: \(health)")
if health <= 0 {
observer?.didDie()
}
}
}
weak var observer: GameObserver?
}
protocol GameObserver: AnyObject {
func didDie()
}
class GameManager: GameObserver {
func didDie() {
print("💀 캐릭터 사망! 게임 오버 처리")
}
}
// 사용
let state = GameState()
let manager = GameManager()
state.observer = manager
state.health = 50
state.health = 0
Observer 패턴 – 상태 변화 감지 시스템 |
✅ 4. 싱글톤 패턴 – 앱 설정 관리
class AppSettings {
static let shared = AppSettings()
private init() {}
var theme: String = "Light"
var language: String = "Korean"
func displaySettings() {
print("🌐 테마: \(theme), 언어: \(language)")
}
}
// 사용
AppSettings.shared.theme = "Dark"
AppSettings.shared.displaySettings()
✅ 5. Protocol + Class – 유연한 알림 시스템
protocol Notifiable {
func sendMessage()
}
class PushNotification: Notifiable {
func sendMessage() {
print("🔔 푸시 알림 전송")
}
}
class EmailNotification: Notifiable {
func sendMessage() {
print("📧 이메일 전송")
}
}
class NotificationManager {
var notification: Notifiable
init(notification: Notifiable) {
self.notification = notification
}
func notifyUser() {
notification.sendMessage()
}
}
// 사용
let push = NotificationManager(notification: PushNotification())
push.notifyUser() // 🔔 푸시 알림 전송
let email = NotificationManager(notification: EmailNotification())
email.notifyUser() // 📧 이메일 전송
Protocol + Class – 유연한 알림 시스템 |
🎯 과제 실습 정리
과제 |
설계 패턴 |
핵심 포인트 |
---|---|---|
1번 |
Delegate |
객체 간 메시지 전달 |
2번 |
Factory |
타입에 따라 객체 생성 |
3번 |
Observer |
상태 변경 → 반응 처리 |
4번 |
Singleton |
전역 단일 인스턴스 공유 |
5번 |
Protocol + Class |
느슨한 결합과 다형성 |
🎯 마무리하며
Swift 클래스 심화 과정 핵심 요약!
-
SOLID 원칙을 반영한 객체지향 설계
-
프로토콜과 결합해 상속보다 유연한 구조 설계
-
ARC와 메모리 최적화, 순환 참조 방지
-
다양한 디자인 패턴으로 코드 재사용성과 유지보수성 향상
-
클래스와 구조체의 적절한 선택 기준 확립
댓글 쓰기