TIL: 클래스와 구조체의 차이 / 클래스와 구조체의 사용 기준 / 객체지향의 4대 특징

Royce·2025년 3월 20일

Swift 문법

목록 보기
24/63

class vs struct 차이점

  • Swift에서 class(클래스)와 struct(구조체) 모두 데이터를 정의하는 기본적인 방법이다
  • 하지만 메모리 관리 방식, 상속 여부, 참조 방식, 속성 변경 가능 여부에서 큰 차이가 있다
구분struct(구조체)class(클래스)
메모리 저장 방식값 형식(Value Type)참조 형식(Reference Type)
인스턴스 복사 방식값 복사(새로운 복사본 생성)참조 복사(같은 인스턴스를 참조)
상속(Inheritance)❌ 지원하지 않음✅ 지원됨
초기화 방식멤버와이즈 이니셜라이저 제공생성자를 직접 정의해야 한다
속성 변경 여부mutating 키워드를 사용해야 변경 가능메서드 내에서 기본적으로 변경 가능
let 선언 시 동작모든 속성이 let처럼 동작(수정 불가)인스턴스는 고정되지만, 내부 속성은 변경 가능
소멸자(deinit)❌ 없음✅ 있음(메모리에서 해제 시 호출)
ARC(자동 메모리 관리)❌ 적용되지 않음(값 타입이라 필요 없음)✅ 적용됨(클래스의 참조 카운트 관리)

값 형식(Value Type)과 참조 형식(Reference Type)의 차이점

  • Swift의 구조체와 클래스의 가장 큰 차이점은 값 형식(구조체)과 참조 형식(클래스) 라는 점이다

값 형식(Value Type)

  • 구조체(struct), (enum)은 값 형식이다
  • 인스턴스를 변수나 상수에 할당할 때 값 자체가 복사된다(완전히 독립적인 복사본이 만들어짐)
  • 하나의 인스턴스를 여러 변수나 상수에 저장하더라도 각각 별개의 인스턴스로 취급된다
struct Car {
    var brand: String
}

var car1 = Car(brand: "Tesla")
var car2 = car1  // car1의 복사본을 car2로 생성 (독립적인 인스턴스)

car2.brand = "BMW"  // car2의 brand만 변경

print(car1.brand)  // "Tesla" (원본은 영향 없음)
print(car2.brand)  // "BMW"

참조 형식(Reference Type)

  • 클래스(class)는 참조 형식이다
  • 인스턴스를 변수나 상수에 할당할 때 참조(메모리 주소)만 복사된다
  • 여러 변수가 동일한 인스턴스를가리키게 된다
  • 하나의 변수를 수정하면, 같은 인스턴스를 참조하는 다른 변수에도 영향을 미친다
class Car {
    var brand: String
    
    init(brand: String) {
        self.brand = brand
    }
}

var car1 = Car(brand: "Tesla")
var car2 = car1  // car1의 참조를 car2로 복사 (같은 인스턴스를 가리킴)

car2.brand = "BMW"  // car2를 변경하면 car1도 변경됨

print(car1.brand)  // "BMW"
print(car2.brand)  // "BMW"

구조체와 클래스의 특징 및 주요 차이점

구조체(struct)의 특징

  1. 값 타입(Value Type)
    • 인스턴스를 복사하면, 값이 복사됨.
    • 서로 독립적인 인스턴스로 취급됨.
  2. 상속 불가능(Inheritance)
    • 다른 구조체로부터 상속받을 수 없음.
  3. mutating 키워드 필요
    • 메서드 내부에서 속성을 변경하려면 mutating 키워드를 사용해야 함.
  4. 멤버와이즈 이니셜라이저 자동 제공
    • 모든 속성을 초기화하는 이니셜라이저가 자동으로 제공됨.
  5. let 선언 시 제한
    • let으로 선언된 구조체는 내부 속성도 변경 불가

클래스(class)의 특징

  1. 참조 타입(Reference Type)
    • 인스턴스를 복사하면, 같은 인스턴스를 가리키게 됨.
    • 하나의 변수를 수정하면 다른 변수도 영향을 받음.
  2. 상속 가능(Inheritance)
    • 다른 클래스로부터 상속받거나 상속할 수 있음.
  3. mutating 키워드 필요 없음
    • 메서드 내에서 속성 변경이 기본적으로 허용됨.
  4. 생성자(이니셜라이저)를 직접 정의해야 함
    • 자동으로 제공되지 않음.
  5. 소멸자 (deinit) 사용 가능
    • 인스턴스가 메모리에서 해제될 때 호출되어 정리 작업을 수행할 수 있음.

구조체의 mutating 키워드 사용 방법

  • 구조체의 인스턴스 메서드는 기본적으로 내부 속성을 변경할 수 없다
  • 하지만 mutating 키워드를 사용하면 속성을 변경할 수 있다
struct Car {
    var brand: String
    
    mutating func changeBrand(to newBrand: String) {
        self.brand = newBrand
    }
}

var myCar = Car(brand: "Tesla")
myCar.changeBrand(to: "BMW")
print(myCar.brand)  // "BMW"

클래스의 소멸자

  • 클래스는 참조 타입이기 때문에, 인스턴스가 메모리에서 해제될 때 deinit을 통해 정리 작업을 수행할 수 있다
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) 객체가 메모리에서 해제됩니다.")
    }
}

var person: Person? = Person(name: "철수")
person = nil  // "철수 객체가 메모리에서 해제됩니다." 출력

객체지향 프로그래밍(Object-Oriented Programming, OOP)

  • 객체지향 프로그래밍(Object-Oriented Programming, OOP)은 현실 세계의 사물과 개념을 프로그래밍으로 모델링하여 코드의 복잡성을 줄이고, 유지보수 및 재사용성을 높이기 위한 방법론이다
  • Swift에서 객체지향 프로그래밍은 클래스와 구조체를 이용해 데이터를 묶고, 해당 데이터와 관련된 기능들을 하나로 캡슐화하는 방식으로 구현된다
  • 결국, 클래스와 구조체는 데이터를 관련 있는 기능과 함께 하나의 묶음으로 만들려는 것이다
class Player {
    var name: String
    var health: Int
    var level: Int
    
    init(name: String, health: Int, level: Int) {
        self.name = name
        self.health = health
        self.level = level
    }
    
    func attack() {
        print("\(name) is attacking!")
    }
}

let player1 = Player(name: "Warrior", health: 100, level: 1)
player1.attack()  // 출력: Warrior is attacking!

클래스와 구조체의 사용 기준

  • Swift에서는 클래스와 구조체 모두 객체를 정의하는 데 사용된다
  • 하지만 두 가지는 중요한 차이가 있기 때문에 상황에 따라 다르게 사용해야 한다

클래스를 사용해야 하는 경우

  • 클래스는 참조 타입(Reference Type) 이기 때문에 인스턴스를 복사하면 같은 메모리 주소를 참조하게 된다
  • 즉, 하나의 인스턴스를 여러 곳에서 수정할 수 있다

클래스를 사용해야 하는 경우

  1. 상속이 필요할 때
    • 클래스는 상속을 지원하여 코드의 재사용성을 높일 수 있다
  2. 참조를 통한 공유가 필요할 때
    • 동일한 객체를 여러 곳에서 공유하여 수정할 필요가 있을 때 사용한다
  3. 의도적으로 인스턴스가 변경 가능한 상태를 유지해야 할 때
    • 데이터의 변경을 허용하는 상황일 때 유용하다
class Dog {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

let dog1 = Dog(name: "Buddy")
let dog2 = dog1  // 같은 메모리 주소를 공유

dog2.name = "Max"
print(dog1.name)  // 출력: Max  (dog1과 dog2는 같은 객체를 참조함)

구조체를 사용해야 하는 경우

  • 구조체는 값 타입(Value Type) 이기 때문에 인스턴스를 복사하면 독립된 복사본이 만들어진다
  • 각 인스턴스는 별도로 존재하며, 수정해도 원본 데이터에 영향을 주지 않는다

구조체를 사용해야 하는 경우

  1. 상속이 필요 없을 때
    • 구조체는 상속을 지원하지 않기 때문에, 클래스 계층 구조가 필요하지 않은 경우 사용한다
  2. 값 불변성을 유지하려고 할 때
    • 구조체는 데이터를 복사하여 전달하므로 원본 데이터의 안전성을 보장한다
  3. 데이터의 복사와 독립성을 보장할 때
    • 예를 들어 좌표, 크기, 거리 등 값으로 표현되는 데이터 모델에 적합하다
  4. 메모리 관리 효율성을 높이고자 할 때
    • 값 타입으로서 메모리 할당 및 해제가 더 효율적이다
struct Point {
    var x: Int
    var y: Int
}

var pointA = Point(x: 0, y: 0)
var pointB = pointA  // 값이 복사됨

pointB.x = 10
print(pointA.x)  // 출력: 0 (pointA와 pointB는 별개)

객체지향의 4대 특징

1. 캡슐화(Encapsulation)

  • 데이터를 보호하고, 외부에서 접근할 수 없도록 숨기는 것
  • 접근 제어자(private, internal, public)를 사용하여 구현한다
class BankAccount {
    private var balance: Double = 0.0
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func getBalance() -> Double {
        return balance
    }
}

2. 상속(Inheritance)

  • 기존 클래스를 확장하여 새로운 클래스를 정의하는 방법
  • 코드의 재사용성을 증가시킨다
class Vehicle {
    var speed: Int = 0
    func drive() {
        print("Driving at \(speed) km/h")
    }
}

class Car: Vehicle {
    var brand: String
    
    init(brand: String) {
        self.brand = brand
    }
}

3. 다형성(Polymorphism)

  • 동일한 이름의 메서드나 프로퍼티가 클래스별로 다르게 동작할 수 있도록 하는 것이다
  • Swift에서는 프로토콜클래스의 메서드 오버라이딩으로 구현한다
protocol Shape {
    func area() -> Double
}

class Circle: Shape {
    var radius: Double
    
    init(radius: Double) {
        self.radius = radius
    }
    
    func area() -> Double {
        return 3.14 * radius * radius
    }
}

4. 추상화(Abstraction)

  • 불필요한 구현을 숨기고, 필요한 정보만 노출하는 방법이다
  • Swift에서 프로토콜을 이용하여 인터페이스를 정의한다
protocol Drawable {
    func draw()
}

class Triangle: Drawable {
    func draw() {
        print("Drawing a triangle.")
    }
}

정리

  • 객체지향 프로그래밍은 현실 세계의 개념을 코드로 모델링하여 유지보수와 재사용성을 높이는 방법이다
  • 클래스는 참조 타입으로 상속과 공유가 필요할 때 사용하며, 구조체는 값 타입 으로 데이터의 독립성을 보장할 때 사용한다
  • Swift의 객체지향 프로그래밍은 클래스와 구조체의 적절한 사용4대 특징(캡슐화, 상속, 다형성, 추상화) 을 활용하는 것이 중요하다
profile
iOS 개발자 지망생

0개의 댓글