iOS 프로그래밍 실무 7주차

2025. 4. 16. 16:08📱 모바일 프로그래밍/iOS 프로그래밍 실무

열거형(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
    }
}

🔚 정리 슬로건

클래스는 공유, 구조체는 복사, 열거형은 선택, 프로토콜은 약속, 익스텐션은 확장!

 


중간고사 정리

2주차 - gurad let 중요

7주차 - 개념만(코드같은거 다 쓸 필요X), 공통점 X