Swift 클래스 상속 제대로 알기: 프로토콜과 final, override
프로그래밍을 배우다 보면, "상속"이라는 개념이 꼭 나온다. 그런데 이게 처음에는 되게 추상적이고 어려운 말처럼 느껴질 수 있다.
"부모 클래스?", "override?", "final?" 이런 단어들이 막 나오는데, 말도 어렵고 개념도 어려워서 머리가 아파올 수 있다 ㅎㅎ. 그래서 오늘은 정말 쉽게, 동물을 예시로 들어서 하나씩 풀어보려고 한다. Swift 코드도 같이 보면서, 진짜 왜 상속이 필요한지 이해해보자.
🧠 상속이란?
먼저, 상속(inheritance)이란?
"어떤 클래스가 다른 클래스의 기능을 물려받는 것"이다.
예를 들어, ‘동물(Animal)’이라는 큰 개념이 있고, 그 아래에 ‘개구리’, ‘오리’ 같은 동물들이 있다.
이때, 공통된 특징(예: 움직인다)을 부모 클래스인 Animal에 적어두고,
각 동물마다 다른 특징(예: 날기, 수영, 뛰기)은 자식 클래스에서 따로 구현하는 방식이다.
📦 그림으로 보면 이렇게 된다:
Animal (부모 클래스)
│
┌──────┴────────┐
Frog (개구리) Duck (오리)
🐸 개구리와 오리 예시로 쉽게 보기
우리는 다음처럼 동물을 나눌 수 있다.
- 개구리(Frog)
- 뛸 수 있다 (jump)
- 수영할 수 있다 (swim)
- 오리(Duck)
- 날 수 있다 (fly)
- 수영할 수 있다 (swim)
여기서 공통점이 보일 것이다.
개구리와 오리 모두 수영을 할 수 있다.
하지만 개구리는 날 수 없고, 오리는 뛸 수 없다.
그럼 이걸 클래스 상속 구조로 짜보자.
📄 Swift 코드로 표현해보기
// 부모 클래스 Animal
class Animal {
func move() {
print("동물이 움직입니다.")
}
}
// 개구리 클래스
class Frog: Animal {
func jump() {
print("개구리가 뜁니다.")
}
func swim() {
print("개구리가 헤엄칩니다.")
}
}
// 오리 클래스
class Duck: Animal {
func fly() {
print("오리가 납니다.")
}
func swim() {
print("오리가 헤엄칩니다.")
}
}
💡 여기서 중요한 건:
Frog와 Duck은 둘 다 Animal을 상속받아서 move() 메서드를 자동으로 사용할 수 있다.
각 동물 고유의 행동은 개별적으로 정의해주는 것이다.
🤔 그런데 문제점은 없을까?
좋은 질문이다 ㅎㅎ. 지금 구조에는 문제가 하나 있다.
바로, 공통 행동인 swim()이 중복된다는 점이다.
- Frog도 swim()을 가지고
- Duck도 swim()을 가진다
그럼 "이걸 부모 클래스인 Animal에 넣으면 되지 않나요?" 라고 생각할 수도 있다.
하지만 그럼 문제가 생긴다.
예를 들어, Dog 클래스를 추가했는데 개는 수영 못하면?
강아지도 swim()을 자동으로 가지게 되니까 잘못된 정보가 된다.
🧩 그럼 어떻게 해야 할까? (중요!)
Swift는 이런 문제를 해결할 수 있도록 "프로토콜(protocol)"이라는 기능을 제공한다. 💡 프로토콜은 "이 클래스는 이런 행동이 가능합니다!" 라는 걸 표시해주는 약속 같은 것이다.
protocol Swimmable {
func swim()
}
protocol Flyable {
func fly()
}
protocol Jumpable {
func jump()
}
이제 각각 필요한 기능만 골라서 붙이면 된다.
class Frog: Animal, Swimmable, Jumpable {
func swim() {
print("개구리가 헤엄칩니다.")
}
func jump() {
print("개구리가 뜁니다.")
}
}
class Duck: Animal, Swimmable, Flyable {
func swim() {
print("오리가 헤엄칩니다.")
}
func fly() {
print("오리가 납니다.")
}
}
이렇게 하면 swim()이라는 기능은 공통된 곳에 안 넣고,
필요한 동물에게만 붙일 수 있다.
즉, 상속은 부모에게서 물려받고,
프로토콜은 개성 있는 행동을 골라 붙이는 방식이라고 생각하면 된다!
🚫 추가 개념들: final, override, static, class
이제 조금만 더 가보자. Swift에서 상속을 쓸 때 자주 나오는 단어들이 있다. 하나씩 쉽게 보자.
키워드 | 설명 |
override | 부모 클래스의 메서드를 다시 정의할 때 사용한다 |
final | 이 메서드나 클래스는 절대 재정의하지 못하게 막는다 |
static | 타입 메서드, 상속과 상관없이 고정된 메서드 |
class | 타입 메서드지만 재정의가 가능한 메서드 |
예제:
class Animal {
final func hello() {
print("안녕!")
}
static func typeMethod() {
print("이건 static이야")
}
class func classMethod() {
print("이건 class야")
}
}
class Dog: Animal {
// override func hello() ❌ → 에러 (final이라 안됨)
// override static func typeMethod() ❌ → 에러 (static은 override 안됨)
override class func classMethod() {
print("강아지용 class method")
}
}
✅ 정리
- 상속은 코드 재사용을 위한 도구이다.
- 부모 클래스에는 공통적인 기능을, 자식 클래스에는 고유 기능을 적는다.
- 여러 동물들이 비슷한 기능을 갖는다면 프로토콜로 빼는 것이 좋다.
- final, override, static, class는 상속에서 중요한 제어 도구들이다.
'IT' 카테고리의 다른 글
Swift 옵셔널 체이닝과 nil 병합 연산자 (0) | 2025.05.11 |
---|---|
Swift 클래스, 인스턴스, 그리고 메모리 관리: init과 deinit의 역할 (0) | 2025.05.10 |
Swift 프로퍼티 감시자: willSet과 didSet 완벽 이해하기 (1) | 2025.05.05 |
Swift 프로퍼티 정리: 처음부터 끝까지 이해하는 계산 속성 (0) | 2025.05.05 |
Swift 클로저 고급편: 단축 인자, 암시적 반환, 후행 클로저, map, filter (0) | 2025.05.04 |
댓글
이 글 공유하기
다른 글
-
Swift 옵셔널 체이닝과 nil 병합 연산자
Swift 옵셔널 체이닝과 nil 병합 연산자
2025.05.11 -
Swift 클래스, 인스턴스, 그리고 메모리 관리: init과 deinit의 역할
Swift 클래스, 인스턴스, 그리고 메모리 관리: init과 deinit의 역할
2025.05.10 -
Swift 프로퍼티 감시자: willSet과 didSet 완벽 이해하기
Swift 프로퍼티 감시자: willSet과 didSet 완벽 이해하기
2025.05.05 -
Swift 프로퍼티 정리: 처음부터 끝까지 이해하는 계산 속성
Swift 프로퍼티 정리: 처음부터 끝까지 이해하는 계산 속성
2025.05.05