📱 모바일 프로그래밍/iOS 프로그래밍 실무
iOS 프로그래밍 실무 7주차
SA성아
2025. 4. 16. 16:08
열거형(enum), 구조체(struct),
클래스 vs. 구조체 vs. 열거형
구조체와 클래스 차이 중요
열거형
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/
Documentation
docs.swift.org
✅ 열거형(enum)을 기본적으로 지원하는 언어
언어 | 특징 |
Java | enum을 객체처럼 사용할 수 있으며 메서드도 정의 가능 |
C | 전통적인 열거형 지원 (값은 기본적으로 정수) |
C++ | C와 유사하지만 enum class로 더 강력한 타입 지원 |
Swift | 매우 강력한 enum 기능 (연관 값, 메서드 등 지원) |
Kotlin | Java보다 유연하며 클래스처럼 사용 가능 |
C# | 강력한 타입 시스템과 함께 enum 지원 |
TypeScript | JavaScript의 상위 집합으로 enum 문법 제공 |
Rust | enum이 매우 강력하며 패턴 매칭과 함께 자주 사용됨 |
Go | 직접적인 enum 키워드는 없지만 const + iota로 구현 |
Python | 3.4 이상에서 enum 모듈로 지원 (Enum, IntEnum 등) |
Dart | Flutter 등에서 자주 사용되며 enum 문법 명확함 |
Scala | sealed trait와 case object 조합으로 enum 유사 구현 |
Ruby | 기본적으로 없지만 gem이나 Module로 구현 가능 |
PHP | 8.1부터 enum 공식 지원 시작됨 |
Haskell | Algebraic Data Type(ADT)으로 enum과 유사한 기능 지원 |
❗ 참고 사항
- JavaScript는 enum을 기본적으로 지원하지 않지만, TypeScript에서 제공됩니다.
- Go는 enum 키워드 없이 iota로 열거형 상수를 정의합니다.
- Python의 enum은 클래스 기반입니다.
- 관련있는 데이터들이 멤버로 구성되어 있는 자료형 객체
ex)
enum Compass {
case North
case South
case East
case West
}
print(Compass.North) // North
var x = Compass.North // Compass형, 위의 4개 중 하나만 들어갈 수 있다
print(type(of:x)) // Compass
// 이미 위에서 x가 Compass 형인 것을 알기 때문에 열거형 이름 생략 가능(처음에만 Compass를 써주면 된다)
x = .East
print(x) // East
// x = .aa // err, aa라는 멤버 없음
- 열거형은 타입이고, 열거형 안의 애들은 멤버라고 함
- ,(콤마)로 나열 가능
enum Compass {
case North, South, East, West
}
열거형 멤버별 기능 정의
enum Compass {
case North, South, East, West
}
var direction : Compass
direction = .North
switch direction { // switch의 비교값이 열거형 Compass
case .North: // direction이 .North면 "북" 출력
print("북")
case .South:
print("남")
case .East:
print("동")
case .West:
print("서") // 모든 열거형 case를 포함하면 default 없어도 됨
}
열거형 멤버에는 메서드도 가능
enum Week {
case Mon, Tue, Wed, Thur, Fri, Sat, Sun
func printWeek() {
switch self {
case .Mon, .Tue, .Wed, .Thur, .Fri:
print("주중")
case .Sat, .Sun:
print("주말")
}
}
}
Week.Sun.printWeek() // 주말
열거형의 rawValue
enum Color : Int { // 원시값(rawValue) 지정
case red
case green = 2
case blue
}
print(Color.red) // red
print(Color.blue) // blue
print(Color.red.rawValue) // 0
print(Color.blue.rawValue) // 3
enum Color : Int { // 원시값(rawValue) 지정
case red
case green = 3
case blue
}
print(Color.red) // red
print(Color.blue) // blue
print(Color.red.rawValue) // 0
print(Color.blue.rawValue) // 4
String형 값을 갖는 열거형의 rawValue
enum Week: String {
case Monday = "월"
case Tuesday = "화"
case Wednesday = "수"
case Thursday = "목"
case Friday = "금"
case Saturday // 값이 지정되지 않으면 case 이름 할당
case Sunday // = "Sunday"
}
print(Week.Monday) // Monday
print(Week.Monday.rawValue) // 월
print(Week.Sunday) // Sunday
print(Week.Sunday.rawValue) // Sunday
연관값을 갖는 enum
enum Date {
case intDate(Int, Int, Int) // (Int, Int, Int)형 연관값을 갖는 intDate
case stringDate(String) // String형 연관값을 갖는 stringDate
}
var todayDate = Date.intDate(2025, 4, 30)
todayDate = .stringDate("2025년 4월 16일")
switch todayDate {
case .intDate(let year, let month, let day):
print("\(year)년 \(month)월 \(day)일")
case .stringDate(let date):
print(date)
}
- 실행결과: 2025년 4월 16일
- todayDate = .stringDate ~ <- 주석처리하면
- 실행결과: 2025년 4월 30일
옵셔널은 연관 값을 갖는 enum
스위프트에서 옵셔널은 none과 some을 연관 값으로 갖는 enum으로 만들어져있다
let age: Int? = 30
switch age {
case .none: // nil인 경우
print("나이 정보가 없습니다")
case .some(let a) where a < 20:
print("\(a)살 미성년자 입니다")
case .some(let a) where a < 71:
print("\(a)살 성인 입니다")
default:
print("경로우대 입니다")
} // 30살 성인 입니다
var x : Int? = 20
var y : Int? = Optional.some(10)
var z : Int? = Optional.none
var x1 : Optional<Int> = 30
print(x, y, z, x1) // Optional(20) Optional nil Optional(30)
구조체
- 가장 큰 장점: Memberwise Initializer가 자동으로 만들어짐
- Int, Double, String 등 기본 자료형은 구조체
- 구조체 \enum의 인스턴스 값(value) 타입, 클래스의 인스턴스는 참조(reference) 타입
- 구조체는 상속 불가(클래스는 가능)
구조체 : Memberwise Initializer 자동 생성
struct Resolution { // 구조체 정의
var width = 1024 // 프로퍼티
var height = 768
}
let myComputer = Resolution() // 인스턴스 생성
print(myComputer.width) // 프로퍼티 접근, 1024
- struct를 class로 변경(오류)
class Resolution {
var width: Int
var height: Int
}
let myComputer = Resolution(width: 1920, height: 1080)
print(myComputer.width)
- 정상
class Resolution {
var width: Int
var height: Int
init(width: Int, height: Int) {
self.width = width
self.height = height
}
}
let myComputer = Resolution(width: 1920, height: 1080)
print(myComputer.width) // 1920
- struct는 init() 없어도 됨(있어도 당연히 됨)
- init (멤버별로 초기화하는 생성자) <- 자동으로 만들어짐 => Memberwise Initializer
struct Resolution { // 구조체 정의
var width: Int // 프로퍼티 초기값이 없음
var height: Int
} // init() 메서드 없음
let myComputer = Resolution(width: 1920, height: 1080) // 인스턴스 생성
print(myComputer.width) // 프로퍼티 접근, 1920
ex) 중간: Memberwise Initializer란?
클래스 내에 구조체
struct Resolution {
var width = 1024
var height = 768
}
class VideoMode {
var resolution = Resolution()
var frameRate = 0.0
}
let myVideo = VideoMode()
print(myVideo.resolution.width) // 1024
- 접근하기 위해서는 .이 필요함
Swift 기본 데이터 타입은 모두 구조체
- public Struct Int
- public Struct Double
- public Struct String
- public Struct Array<Element>
클래스 vs. 구조체 vs. 열거형
✅ 클래스 vs 구조체 간단 비교
항목 | 클래스 (class) | 구조체 (struct) |
참조/값 타입 | 참조 타입 (Reference) | 값 타입 (Value) |
상속 | O (상속 가능) | ❌ (상속 불가능) |
타입 캐스팅 | O | ❌ |
deinit (소멸자) | O | ❌ |
ARC (메모리 관리) | O (자동 참조 카운트) | ❌ |
복사 시 행동 | 같은 인스턴스를 참조 | 값을 복사 |
멀티스레드 안정성 | 위험할 수 있음 | 안전한 편 (복사되니까) |
💡 언제 struct를 써야 할까?
Swift에서는 기본적으로 struct 사용을 권장합니다. 다음과 같은 경우에요:
- 데이터를 캡슐화하고 전달만 하면 되는 경우
- 복사된 값이 독립적으로 존재해야 할 때
- UI 요소에서 ViewModel, Model 등 단순한 데이터 구조일 때
- SwiftUI에서는 거의 다 struct로 View를 만듦
🟢 예시:
struct User {
var name: String
var age: Int
}
💡 언제 class를 써야 할까?
class는 다음 같은 상황에서 사용해요:
- 인스턴스가 공유되어야 할 때 (참조가 필요한 경우)
- 상속이 필요한 구조 (예: ViewController, Delegate 등)
- 리소스를 많이 다루는 객체 (네트워크, DB 연결 등)
- 객체 수명이 중요할 때 (deinit 필요할 때)
🟢 예시:
class NetworkManager {
var isConnected: Bool = false
func connect() {
isConnected = true
}
}
🧠 기억하기 쉬운 정리
"값을 복사해도 괜찮고, 비교적 단순한 데이터면 struct.
참조가 필요하고, 상속 또는 객체 수명이 중요하면 class."
중간: 공통점은 시험에 안나옴(차이점이 나옴)
class가 struct보다 더 갖는 특징
- 상속 가능
- 구조체는 값 타입(value type) 클래스는 참조 타입(reference type)
- 타입 캐스팅(부모 - 자식간에는 형변환 가능) 가능
클래스/구조체 정의하기
- 클래스 정의하기
- class 이름 {...}
- 구조체 정의하기
- struct 이름 {...}
- 타입(클래스, 구조체, 열거형) 이름에는 Upper Camel Case 사용
- 대문자로 시작
- 클래스/구조체 안의 프로퍼티나 메서드는 lower Camel Case 사용
구조체는 값 타입(value type) 클래스는 참조 타입(reference type)
struct
var x = 1 // var x : Int = 1 <- 구조체로 만들어짐
var y = x
print(x,y) // 1 1
x = 2
print(x,y) // 2 1
y = 3
print(x,y) // 24
struct Human {
var age : Int = 1
}
var kim = Human() // : Human 생략
var lee = kim //값 타입, lee는 Human 형
print(kim.age, lee.age) // 1 1
lee.age = 20
print(kim.age, lee.age) // 1 20
kim.age = 30
print(kim.age, lee.age) // 30 20
- 값 타입은 복사할 때 새로운 데이터가 하나 더 생김
class
class Human {
var age : Int = 1
}
var kim = Human() // : Human 생략
var lee = kim // 참조 타입, lee는 Human 형
print(kim.age, lee.age) // 1 1
lee.age = 20
print(kim.age, lee.age) // 20 20
kim.age = 30
print(kim.age, lee.age) // 30 30
- 참조 타입은 복사할 때 주소를 복사해서 한 데이터의 reference가 2개 생김
- 주소를 복사하기 때문에 하나가 바뀌면 동시에 두 개가 바뀜
값
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var frameRate = 0
var name: String?
}
var hd = Resolution(width: 1920, height: 1080)
//자동 Memberwise Initializer
var highDef = hd
//구조체는 값타입(value type)
print(hd.width, highDef.width) // 1920 1920
hd.width = 1024
print(hd.width, highDef.width) // 1024 1920
참조
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var frameRate = 0
var name: String?
}
var hd = Resolution(width: 1920, height: 1080)
//자동 Memberwise Initializer
var xMonitor = VideoMode()
xMonitor.resolution = hd
xMonitor.name = "LG"
xMonitor.frameRate = 30
print(xMonitor.frameRate) // 30
var yMonitor = xMonitor
//클래스는 참조타입(reference type)
yMonitor.frameRate = 25
print(yMonitor.frameRate) // 25
print(xMonitor.frameRate) // 25
언제 클래스를 쓰고 언제 구조체를 쓰나?
클래스는 참조 타입, 구조체는 값 타입
- 구조체
- 구조체는 간단한 데이터 값들을 한데 묶어서 사용하는 경우
- 전체 덩어리 크기가 작은 경우, 복사를 통해 전달해도 좋은 경우
- 멀티 쓰레드 환경
- 상속할 필요가 없는 경우
- 클래스
- 상속이 필요한, 기능이 다양하고 복잡한 것을 만들 때
🔁 보너스: 클래스(class)는 언제 쓸까?
- 인스턴스를 여러 곳에서 공유해야 할 때
예: 네트워크 매니저, 데이터 캐시 등 - 상속과 다형성을 사용해야 할 때
예: UIView, UITableViewCell, 커스텀 컨트롤 등 - 객체의 수명(라이프사이클)을 관리할 필요가 있을 때
예: 메모리 해제를 감지하고 정리하는 경우 (deinit 사용) - 대용량 데이터를 한 번만 생성해 공유하고 싶을 때
extension
클래스, 구조체, 열거형, protocol에 새로운 기능을 추가
extension 기존타입이름 {
// 새로운 기능
}
사용 경우
- 기존 클래스에 메서드, 생성자, 계산 프로퍼티 등의 기능을 추가
- Swift built-in 클래스와 iOS 프레임워크에 내장된 클래스에 기능 추가
let myValue: Double = 3.5
print(myValue.isZero) // instance property, false
// isZero : 0이면 true, 아니면 false
let myValue: Double = 3.0
print(myValue.squared) // 9.0
print(3.5.squared) // 12.25
print(myValue.isZero) // instance property
- squared 가 없으니 에러가 남
ex)
extension Double {
var squared: Double {
return self * self
}
}
let myValue: Double = 3.0
print(myValue.squared) // 9.0
print(3.5.squared) // 12.25
print(myValue.isZero) // instance property
- Double 을 건들이지 않고 squared라는 기능을 넣음
class { }
struct { }
enum { }
protocol { }
extension { }
✅ 한 줄 요약표
키워드 | 용도 한 줄 요약 |
class | 참조 타입 객체를 정의할 때 사용 |
struct | 값 타입 데이터 모델을 만들 때 사용 |
enum | 제한된 선택지를 정의하거나 상태를 표현할 때 사용 |
protocol | 공통 기능의 인터페이스(규약)를 정의할 때 사용 |
extension | 기존 타입에 기능을 추가할 때 사용 (소스 수정 없이) |
🔍 각각 자세히 설명
1. class { }
- 참조 타입 (Reference Type)
- 상속 가능, deinit(소멸자) 사용 가능
- 메모리는 Heap에 저장되고 ARC로 관리됨
- 공유되는 객체나 상속 구조가 필요한 경우 사용
🟢 예시:
class Animal {
var name: String
init(name: String) {
self.name = name
}
}
2. struct { }
- 값 타입 (Value Type)
- 복사 시 새로운 인스턴스가 만들어짐
- 상속 불가
- Swift의 기본 타입(Int, String 등)도 전부 struct
- Swift에서는 가능한 한 struct 사용을 권장함
🟢 예시:
struct Point {
var x: Int
var y: Int
}
3. enum { }
- 제한된 상태나 선택지를 표현할 때 사용
- 연관 값(Associated Value) 가능 → 매우 유연함
- 패턴 매칭, switch문과 함께 자주 사용됨
🟢 예시:
enum Direction {
case north, south, east, west
}
🟢 연관 값 예시:
enum Result {
case success(String)
case failure(Int)
}
4. protocol { }
- **공통된 기능(인터페이스)**를 정의
- 클래스, 구조체, 열거형 어디든 채택 가능
- "이 타입은 이런 기능을 가질 것이다"라고 약속하는 것
- Swift는 프로토콜 중심 설계 지향 (Protocol-Oriented Programming)
🟢 예시:
protocol Runnable {
func run()
}
struct Person: Runnable {
func run() {
print("뛰는 중")
}
}
5. extension { }
- 기존 타입에 기능을 추가할 때 사용
- 소스를 직접 수정하지 않고도 메서드, 계산 속성, 프로토콜 채택 등 추가 가능
- View, String, Array 등 확장해서 유틸리티 자주 구현함
🟢 예시:
extension String {
var isNotEmpty: Bool {
return !self.isEmpty
}
}
🔚 정리 슬로건
클래스는 공유, 구조체는 복사, 열거형은 선택, 프로토콜은 약속, 익스텐션은 확장!
출처: iOS 프로그래밍 실무 강의자료