스파르타 코딩 클럽 - iOS 스타터 6기/본 캠프

6. 스파르타 코딩 클럽 - Struct와 Class / 프로토콜

seongpil Heo 2025. 3. 7. 16:45

1️⃣ 개념 바로 알기

1. Struct와 Class

1. Struct(구조체):

  • 값 타입(Value Type)으로, 메모리에서 값을 복사해 사용하므로 독립된 인스턴스 생성
  • 상속 불가능
  • 주로 데이터를 저장하거나 간단한 로직을 수행할 때 사용

2. Class(클래스):

  • 참조 타입(Reference Type)으로, 메모리에서 동일한 인스턴스를 여러 곳에서 참조
  • 상속 가능
  • 객체지향 프로그래밍의 주요 개념으로 사용

🛠️ 주요 차이점

특징 Struct Class
메모리 구조 Stack Heap
타입 값 타입 (Value Type) 참조 타입 (Reference Type)
상속 불가능 가능
성능 메모리 복사가 빨라 성능 우수 메모리 참조로 성능 낮음

2. 프로토콜

  • 프로토콜(Protocol)은 특정 기능을 정의한 청사진입니다
  • Struct, Class, Enum에서 프로토콜을 채택(Adopt)하고 구현(Implement)할 수 있습니다
  • 용도
    • 코드를 재사용 가능하고 유연하게 설계
    • 프로토콜 기반의 설계 방식 (Protocol-Oriented Programming)을 가능하게 합니다

🛠️ 프로토콜 문법 예제

protocol Greetable {
	func greet() -> String
}

struct Person: Greetable {
	var name: String
    func greet() -> String {
    	return "Hello, \(name)!"
    }
}

let person  = Person(name: "Alice")
print(person.greet()) // "Hello, Alice"

 

[ 실행 결과 ]


2️⃣ 직접 구현해보기

1. Struct 구현하기

  • 이름과 나이를 저장하는 User Struct를 작성하고, 두 명의 독립적인 사용자 인스턴스를 생성해주세요
  • 두 인스턴스의 값을 변경한 후, 각 인스턴스가 서로 독립적임을 확인하세요

[ 실습 코드 ]

import Foundation

struct User {
    var name: String
    var age: Int
    
}

var user1 = User(name: "Mario", age: 30)
var user2 = User(name: "Seongpil", age: 26)

print("User1 : \(user1.name),\(user1.age)")
print("User2 : \(user2.name),\(user2.age)")

// 값 변경
user1.name = "Luigi"
user1.age = 29

user2.name = "Dave"
user2.age = 22

print("------------------------------------")

print("User1 : \(user1.name),\(user1.age)")
print("User2 : \(user2.name),\(user2.age)")

 

[ 실행 결과 ]

 

- 독립적인 인스턴스 확인, 중간에 값을 변경하면 각각 변경됨

2. Class 사용

예제 :

  • 이름과 나이를 저장하는 User Class를 작성하고, 두 개의 사용자 참조를 생성해주세요
  • 한 참조의 값을 변경한 후, 두 참조가 동일한 값을 공유하는지 확인하세요

[ 실습 코드 ]

import Foundation

// User 클래스 정의 (참조 타입)
class User {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

// 두 개의 사용자 참조 생성
var user1 = User(name: "Alice", age: 25)
var user2 = user1 // user1을 user2에 할당 (같은 참조)

print("Before modification:")
print("User1: \(user1.name), \(user1.age)") // Alice, 25
print("User2: \(user2.name), \(user2.age)") // Alice, 25

// user2를 통해 값 변경
user2.name = "Bob"
user2.age = 30

print("\nAfter modification:")
print("User1: \(user1.name), \(user1.age)") // Bob, 30
print("User2: \(user2.name), \(user2.age)") // Bob, 30

 

[ 실행 결과 ]

 

- 참조 형식은 각각의 인스턴스를 생성하여도 참조된 인스턴스 하나의 값을 변경하면 따라서 값이 변경된다

3. Greetable 프로토콜 :

  • greet() 메서드를 포함한 Greetable 프로토콜을 정의합니다
  • Person Structdhk Robot Class에서 이 프로토콜을 채택하고 구현하세요
  • Person은 이름을 활용하여 인사를 출력, Robot은 고유 ID를 활용해 인사를 출력합니다

[ 실습 코드 ]

import Foundation

// Greetable 프로토콜 정의
protocol Greetable {
    func greet() -> String
}

// Person 구조체 (값 타입) - 이름을 활용한 인사
struct Person: Greetable {
    var name: String

    func greet() -> String {
        return "Hello, my name is \(name)!"
    }
}

// Robot 클래스 (참조 타입) - 고유 ID를 활용한 인사
class Robot: Greetable {
    var id: String

    init(id: String) {
        self.id = id
    }

    func greet() -> String {
        return "Beep boop! I am robot ID: \(id)."
    }
}

// 인스턴스 생성 및 테스트
let person = Person(name: "Louis")
let robot = Robot(id: "KKSR-M340")

print(person.greet()) // Hello, my name is Louis!
print(robot.greet())  // Beep boop! I am robot ID: KKSR-M340.

 

[ 실행 결과 ]

4. 프로토콜 확장 (Extension) :

  • Greetable 프로토콜에 기본 구현을 추가하여 greet() 메서드가 기본적으로 "Hello"!"를 반환하도록 하세요
  • 기본 구현을 사용하지 않고 고유 인사를 반환하는 Alien Struct를 추가로 구현하세요

[ 실습 코드 ]

import Foundation

// Greetable 프로토콜 정의
protocol Greetable {
    func greet() -> String
}

// 프로토콜 확장을 통한 기본 구현 제공
extension Greetable {
    func greet() -> String {
        return "Hello!"
    }
}

// Person 구조체 (기본 구현 사용)
struct Person: Greetable {
    var name: String
}

// Robot 클래스 (기본 구현 사용)
class Robot: Greetable {
    var id: String

    init(id: String) {
        self.id = id
    }
}

// Alien 구조체 (기본 구현을 오버라이드하여 별도 인사 구현)
struct Alien: Greetable {
    var planet: String

    func greet() -> String {
        return "Greetings, Earthlings! I come from \(planet)."
    }
}

// 인스턴스 생성 및 테스트
let person = Person(name: "Alice")
let robot = Robot(id: "XJ-9000")
let alien = Alien(planet: "Mars")

print(person.greet()) // Hello! (기본 구현 사용)
print(robot.greet())  // Hello! (기본 구현 사용)
print(alien.greet())  // Greetings, Earthlings! I come from Mars. (고유 구현)

 

[ 실행 결과 ]


[ TIL ]

프로토콜 확장을 활용하면 코드의 중복을 줄이면서 유연한 기능 확장이 가능하다.

 

Struct는 각각의 인스턴스가 독립적이고, Class는 하나의 클래스 인스턴스를 여러 변수에 할당하면 동일한 참조를 공유하게 되기 때문에 인스턴스가 독립적이지 않다

 

Class 는 참조 타입, Struct는 값 타입이다

 

클래스는 복잡한 객체 관리 또는 상속이 필요한 경우에 사용하고,

구조체는 독립적인 데이터 또는 간단한 값 저장의 경우에 사용한다