[Swift] 구조체부터 스위치까지

Daehyeon Yun·2025년 3월 9일
post-thumbnail

1. 구조체

  • 키워드 : struct
  • 의미 : 하나의 동작을 수행하는 객체
  • 💡 struct 내부엔 funcvar 등이 포함된다.
  • 구조체는 값 타입 으로 데이터를 묶어서 저장하고, 복사 시 이 복사 된다.
    • 값을 복사하여 전달되기 때문에 인스턴스를 다른 변수에 할당하거나 함수에 전달할 때 원본이 아닌 복사본 이 전달된다.
  • 자동 멤버와이즈 이니셜라이저 제공
    • Swift의 구조체는 모든 프로퍼티를 초기화하는 기본 생성자(멤버와이즈 이니셜라이저, Memberwise Initializer) 제공 → ⭐ 클래스에선 제공되지 않는다!
    • Java의 “생성자”와 매우 유사하며 Swift에선 기본 생성자가 자동 제공되어 코드가 깔끔하고 직관적이다.
  • 구조체 메서드에서 자신의 프로퍼티를 변경하려면 mutating 키워드 필수
  • 상속이 불가능하다. (클래스만 상속 가능)
  • let으로 선언한 구조체 인스턴스는 내부 프로퍼티를 변경 할 수 없음.
  • 언제 사용하는가?
    • 데이터의 복사가 필요할 때(ex. 좌표, 색상, 설정값)
    • 상속이 필요 없는 경우
    • 값의 불변성 을 유지하는 데이터 모델을 만들 때
// 문법

// 구조체 정의
struct Person{
	// 프로퍼티(속성)
	var name: String
	var age: Int
	
	// 메서드
	func introduce(){
		print("안녕하세요 제 이름은 \(name)이고, \(age)살 입니다.")
	}
}

// 구조체 인스턴스 생성
var person1 = Person(name: "홍길동", age: "25")
person1.introduce() // 출력 : 안녕하세요 제 이름은 홍길동이고 25살입니다.

// 메소드에서 프로퍼티 변경 시 (mutating)
struct Counter{
	var count = 0
	
	mutating func increment(){ // func increment는 오류
		count += 1
	}
}

var counter = Counter()
counter.increment()
print(counter.count) // 출력 : 1

// let으로 선언한 구조체 인스턴스의 경우
let person2 = Person(name: "고길동", age: "30") // 구조체 인스턴스 생성
person2.age = 31 // 오류 발생 

2. 클래스

  • 키워드 : class
  • 클래스는 참조 타입(Reference Type) 이다. 클래스를 복사하면 원분과 복사본이 같은 객체를 참조한다.
  • 클래스를 상속 할 수 있다.
  • 클래스는 디이니셜라이저(Deinitializer) 를 제공한다.
    • 객체가 메모리에서 해제될 때 실행되는 deinit 메서드를 제공한다.
  • 구조체와 달리 멤버와이즈 이니셜라이저를 제공하지 않는다.
    • 직접 init 생성자를 정의해야한다.
  • 두 객체가 같은 인스턴스를 참조하는지 확인할 수 있다.
    • Identical 연산자 === 사용 가능
// 클래스 정의
class Person {
	var name: String
	var age: Int
	
	// 이니셜라이저 (생성자)
	init(name: String, age: Int){
		self.name = name
		self.age = age
	}
	
	// 메서드
	func introduce() {
		print("안녕하세요 제 이름은 \(name)이고, \(age)살 입니다.")
	}
}

// 클래스 인스턴스 생성
let person1 = Person(name: "지민", 25)
person1.introduce()

// 클래스를 복사하면 원문과 복사본이 같은 객체를 공유한다.
let person2 = person1
person2.name = "민지"
print(person1.name) // 출력 : 민지

// Identical 연산자 (===)
let person3 = person1
print(person1 === person3) // 출력 : true (같은 객체를 참조한다.
  • 클래스는 상속을 통해 다른 클래스를 확장할 수 있다. (Inferitance)
class Student: Person {
    var studentID: String

    init(name: String, age: Int, studentID: String) {
        self.studentID = studentID
        super.init(name: name, age: age)
    }

    override func introduce() {
        print("저는 학생 \(name)이고, \(age)살이며 학번은 \(studentID)입니다.")
    }
}

let student = Student(name: "유나", age: 20, studentID: "S1234")
student.introduce()
// 출력: 저는 학생 유나이고, 20살이며 학번은 S1234입니다.
  • 메모리에서 해제될 때 실행되는 deinit
class File {
    var filename: String

    init(filename: String) {
        self.filename = filename
        print("\(filename) 파일이 생성되었습니다.")
    }

    deinit {
        print("\(filename) 파일이 삭제되었습니다.")
    }
}

var file: File? = File(filename: "test.txt")
file = nil // 파일 삭제 시 deinit 호출
// 출력: test.txt 파일이 삭제되었습니다.

Tip.🤔 구조체와 클래스의 차이는?

  • 구조체(struct)는 값 타입 으로 인스턴스가 복사될 때 이 복사된다.
  • 클래스(class)는 참조 타입 으로 인스턴스가 복사될 때 참조 가 복사된다.

구조체에 대한 자세한 의미.

구조체는 값 타입이기 때문에 원본 구조체를 복사해도 다른 변수에 영향을 주지 않는다.

struct Person{
	var name: String
}

var person1 = Person(name: "Alice")
var person2 = person1 // person1을 2에 복사
person2.name = "Bob" // 원본 구조체에 영향가지 않음. 따라서 person2만 변경된다.

// 실제 결과
print(person1.name) // 출력 : "Alice"
print(person2.name) // 출력 : "Bob"

// 즉, person1과 person2은 "서로 다른 복사본"

클래스에 대한 자세한 의미

  • 클래스는 참조타입이다. 클래스를 변수에 할당하거나 함수에 전달할 때 참조가 복사된다. → 즉, 같은 인스턴스를 여러 변수에서 참조하게 되어, 한 곳에서 변경하면 다른 곳에도 영향을 끼친다.
class Person{
	var name: String
	init(name: String){
		self.name = name
	}
}

var person1 = Person(name: "Alice")
var person2 = person1 // person2에 person1 참조 전달
person2.name = "Bob" // person1과 person2 모두 데이터가 변경됨.

print(person1.name) // 출력 : "Bob"
print(person2.name) // 출력 : "Bob"

// 즉, person1과 person2는 같은 객체를 참조하고 있기에 하나를 변경하면 다른 하나도 변경된다.

2-1. 클래스와 SwiftUI 데이터 연동 요약

  • 클래스는 참조 타입(Reference Type)같은 인스턴스를 여러 변수에서 공유하고, 한 곳에서 변경하면 모든 곳에 반영됨.
  • SwiftUI에서 클래스 상태 관리:
    • @ObservedObject → 뷰가 클래스 인스턴스를 구독해 데이터 변화를 감지.
    • @Published → 클래스 속성에 붙여, 값이 바뀔 때 SwiftUI가 자동으로 화면 업데이트.
    • 클래스는 ObservableObject 프로토콜을 채택해야 @Published가 작동.

💡 한 줄 정리:

@ObservedObject@Published클래스 전용. 값 변경 → 화면 자동 업데이트!

import SwiftUI

struct Diff: View {
    
    // 구조체
    let myCar = Car(name: "리어카", owner: "라이오")
    // 클래스
    @ObservedObject var myKar = Kar(name: "리어카2", owner: "라이오2")
    
    var body: some View {
        VStack{
            Text("\(myKar.name)의 주인은 \(myKar.owner)입니다.")
            Button{
                let broCar = myKar
                broCar.name = "동생차"
                broCar.owner = "동생"
                
                myKar.sayHi()
            } label : {
                Text("출발")
            }
        }
    }
}

struct Car{
    let name: String
    let owner: String
    
    func sayHi(){
        print("hi \(owner)")
    }
}

class Kar: ObservableObject{
    @Published var name: String
    var owner: String
    
    init(name: String, owner: String){
        self.name = name
        self.owner = owner
    }
    
    func sayHi(){
        print("hi \(owner)2")
    }
}

#Preview {
    Diff()
}

3. 열거형

  • 열거형(Enumeration, enum) : 관련된 값들의 그룹을 한데 묶어 표현하는 데이터 타입
  • 단순 정수 형태 뿐만 아닌, 다양한 타입의 값이나 연관된 데이터를 가질 수 있다.
  • 열거형에 접근할 때는 열거형이름.값 형태로 접근하지만 같은 타입 내에서 사용할 때는 .값 으로 축약이 가능하다.
  • ⭐ 자바의 제네릭처럼 특정 변수에 들어가야 하는 값의 형식을 미리 정의할 수 있다. → 인수인계의 극대화!
// 기본 열거형 선언
enum CompassPoint {
	case north
	case south
	case east
	case west
}

// 방법 1
var direction = CompassPoint.north
direction = .east // 축약형 사용 가능
print(direction) // east

// 방법 2
var dirction: CompassPoint = .north
print(dicetion) // north

// 즉, direction에는 4가지의 변수만 들어갈 수 있도록 하고싶다.
  • 열거형에 기본 타입에 원시 값(Raw Value)를 연결할 수 있다.
enum Weekday: String{
	case monday = "Mon"
	case tuesday = "Tue"
	case wednesday = "Wed"
}

print(Weekday.monday.rawValue) // "Mon"

// 실제 Swift UI
import SwiftUI

struct Choice: View {
	var direction: Direction = .north
	var member: String = "대현"
	
	var body: some View {
		Text("방향은 \(direction.rawValue)쪽 입니다.")
		// 출력 : 방향은 북쪽 입니다.
	}
}

enum Direction: String {
	case north = "북"
	case west
	case east
	case south
}
  • 정수형 원시 값은 첫 값부터 자동으로 0부터 할당된다.
    • 문자열 또한 case 이름을 그대로 원시 값으로 사용할 수 있다.
enum Number: Int {
	case one = 1
	case two
	case three
}

print(Number.two.rawValue)
  • 각 열거형 케이스에 서로 다른 타입의 데이터를 저장할 수 있다. (연관 값, Associated Values)
enum MediaType {
	case audio(String)
	case video(String, Int)
}

let song = MediaType.audio("Pop")
let movie = MediaType.video("Action", 120)

switch movie {
case .audio(let genre):
	print("Audio Genre: \(genre)")
case .video(let genre, let duration):
	print("Video Genre: \(genre), Duration : \(duration) min")
}

// Video Genre: Action, Duration: 120 min
  • 열거형은 메서드와 프로퍼티를 가질 수 있다. → 열거형 자체가 하나의 타입으로 취급된다.
enum Beverage: String{
	case coffee, tea, jucie
	
	var description: String{
		switch self {
		case .coffee: return "뜨겁거나 차가운"
		case .tea: return "얼그레이나 복숭아"
		case .juice: return "스무디나 에이드"
		}
	}
}

let drink = Beverage.coffee
print(dring.description) // 뜨겁거나 차가운
  • 열거형은 옵셔널 타입 , 예외 처리 , 상태 관리 등 매우 자주 사용된다.
enum NetworkError: Error{
	case badURL
	case timeout
	case noInternet
}

4. 스위치

  • Switch 는 하나의 값에 따라 여러 경우(case) 중 실행할 코드를 결정하는 조건문
  • if-else 보다 가독성이 뛰어나고 복잡한 조건 처리에 적합하다.
  • switch 는 강력하고 안전한 패턴 매칭을 지원한다.
  • 스위치는 모든 경우를 반드시 처리해야한다. → 모든 가능성을 처리해야 하며, 특히 ⭐ enum 의 모든 케이스가 나오지 않으면 오류 발생 → swift의 switch는 각 case 실행 후 자동으로 종료되기 때문에 break 가 필요 없다. → 단순 값 비교 외에 range, where 절 등 다양한 조건 추가 가능 → default 를 반드시 사용해야 한다. (단, 모든 enum 케이스를 처리하면 필요 없다.)
// switch 문법 구조
switch{
case 조건1:
	code
case 조건2:
	code
default:
	위 조건에 모두 해당되지 않을 때
}

// 예시
import SwiftUI

struct Diff: View {
    
    @State var myDirection: Direction = .south
    
    var body: some View {
        switch myDirection{
        case .north:
            Text("북쪽은 추워요")
        case .west:
            Text("서쪽은 석양이 예뻐요")
        case .east:
            Text("동쪽은 해보기 좋아요")
        case .south:
            Text("남쪽은 놀기 좋아요")
        }
        
        Button{
            myDirection = .east
        }label:{
            Text("돌리기")
        }
    }
}

enum Direction: String{
    case north = "북"
    case west = "서"
    case east = "동"
    case south = "남"
}
profile
열심히 살아야지

0개의 댓글