TIL: 클래스(class)와 구조체(struct)의 이해

Royce·2025년 3월 19일

Swift 문법

목록 보기
22/63

클래스(class)

  • 클래스는 객체를 만들기 위한 설계도(붕어빵 틀) 역할을 한다
  • 클래스를 사용하면 같은 속성과 기능을 가진 여러 개의 객체를 생성할 수 있다

클래스 내부 구성 요소

1. 속성(Property)

  • 객체(인스턴스)의 데이터(정보) 를 저장하는 변수 또는 상수
  • var 또는 let 키워드를 사용하여 선언
  • 예: 자동차의 브랜드(brand), 현재 속도(speed)

2. 메서드(Method)

  • 객체(인스턴스)가 수행하는 동작(기능)
  • func 키워드를 사용하여 선언
  • 예: 자동차가 가속(accelerate), 감속(decelerate)

3. 인스턴스(Instance)

  • 클래스를 기반으로 생성된 실체(객체)
  • 인스턴스를 통해 속성과 메서드를 사용할 수 있다
  • Swift에서는 클래스의 인스턴스만 객체(Object)라고 부른다
class Dog {
    // 속성(Property) - 강아지의 특징
    var name = "강아지"
    var weight = 0

    // 메서드(Method) - 강아지의 행동
    func sit() {
        print("\(name)이(가) 앉았습니다.")
    }

    func lieDown() {
        print("\(name)이(가) 누웠습니다.")
    }
}

// 여러 개의 인스턴스(Instance) 생성
var dog1 = Dog()
var dog2 = Dog()
var dog3 = Dog()

// 각 인스턴스의 속성 변경
dog1.name = "망고"
dog1.weight = 12

dog2.name = "루나"
dog2.weight = 18

dog3.name = "바닐라"
dog3.weight = 9

// 각 인스턴스의 속성 확인
print(dog1.name, dog1.weight)  // 망고 12
print(dog2.name, dog2.weight)  // 루나 18
print(dog3.name, dog3.weight)  // 바닐라 9

// 각 인스턴스의 메서드 호출
dog1.sit()
dog2.lieDown()
dog3.sit()

구조체(struct)

  • 구조체는 클래스와 유사한 역할을 하지만, 갑 타입(Value Type) 이라는 점에서 차이가 있다
  • 즉, 구조체는 인스턴스를 다른 변수에 할당하면 새로운 복사본이 생성된다

구조체 내부 구성 요소

1. 속성(Property)

  • 구조체가 가지는 데이터(정보) 를 저장하는 변수 또는 상수
  • var 또는 let 키워드를 사용하여 선언
  • 예: 스마트폰의 모델(model), 배터리 잔량(batteryLevel)

2. 메서드(Method)

  • 구조체가 수행하는 동작(기능)
  • func 키워드를 사용하여 선언
  • 예: 스마트폰 충전(charge), 배터리 사용(useBattery)

3. 인스턴스(Instance)

  • 구조체를 기반으로 생성된 실체
  • 할당될 때마다 새로운 복사본이 생성됨
struct Bird {
    // 속성(Property) - 새의 특징
    var name = "새"
    var weight = 0.0

    // 메서드(Method) - 새의 행동
    func fly() {
        print("\(name)이(가) 날아갑니다.")
    }
}

// 여러 개의 인스턴스 생성
var bird1 = Bird()
var bird2 = Bird()
var bird3 = Bird()

// 각 인스턴스의 속성 변경
bird1.name = "참새"
bird1.weight = 0.2

bird2.name = "비둘기"
bird2.weight = 0.4

bird3.name = "독수리"
bird3.weight = 3.0

// 각 인스턴스의 속성 확인
print(bird1.name, bird1.weight)  // 참새 0.2
print(bird2.name, bird2.weight)  // 비둘기 0.4
print(bird3.name, bird3.weight)  // 독수리 3.0

// 각 인스턴스의 메서드 호출
bird1.fly()
bird2.fly()
bird3.fly()

클래스(class)와 구조체(struct)의 차이점

비교 항목클래스(class)구조체(struct)
메모리 저장 방식힙(Heap) 영역스택(Stack) 영역
복사 방식참조(Reference) 전달값(Value) 복사
사용 방식객체지향 프로그래밍(OOP)데이터 중심의 경량 타입
상속 가능 여부✅ 가능❌ 불가능
변수(속성) 변경var로 선언하면 가능var로 선언해도 let으로 할당하면 변경 불가능

클래스와 구조체의 메모리 저장 방식

클래스는 참조 타입(Reference Type)

  • 클래스는 인스턴스를 변수에 할당하면 같은 객체를 공유한다
class Person {
    var name = "사람"
}

var person1 = Person()
var person2 = person1  // 같은 객체를 참조 (얕은 복사)

person2.name = "영희"

print(person1.name)  // "영희"
print(person2.name)  // "영희"  (같은 객체를 참조)

구조체는 값 타입(Value Type)

  • 구조체는 인스턴스를 변수에 할당하면 새로운 복사본이 생성된다
struct Animal {
    var name = "동물"
}

var animal1 = Animal()
var animal2 = animal1  // 새로운 복사본이 생성됨 (깊은 복사)

animal2.name = "강아지"

print(animal1.name)  // "동물"
print(animal2.name)  // "강아지"  (서로 다른 객체)

클래스(class)와 구조체(struct)의 letvar 키워드

  • Swift에서 클래스는 참조 타입(Reference Type), 구조체는 값 타입(Value Type) 이므로, varlet을 사용할 때 동작 방식이 다르다

클래스의 인스턴스 생성 시 varlet 차이점

  • 클래스는 참조 타입(Reference Type) 이므로, let으로 선언해도 내부 속성의 변경이 가능하다
  • 즉, 참조하고 있는 객체(메모리 주소)는 고정되지만, 그 안의 속성 값은 변경할 수 있다
class PersonClass {
    var name = "사람"
    var age = 0
}

var pclass1 = PersonClass()  // var로 선언한 클래스 인스턴스
let pclass2 = PersonClass()  // let으로 선언한 클래스 인스턴스

pclass1.name = "철수"  // ✅ 가능 (속성 변경 가능)
pclass2.name = "영희"  // ✅ 가능 (속성 변경 가능)

pclass1 = PersonClass()  // ✅ 가능 (새로운 인스턴스 할당 가능)
// pclass2 = PersonClass()  // ❌ 오류 발생 (let으로 선언된 변수는 새로운 참조로 변경 불가)

이런 차이가 발생하는 이유

  • 클래스는 힙(Heap) 메모리에 인스턴스를 저장하고, 변수(var 또는 let)는 스택(Stack) 메모리에 객체의 참조(주소 값)를 저장한다
  • let pclass2 = PersonClass()라고 선언하면, pclass2가 가리키는 메모리 주소(참조)는 변경할 수 없지만, 그 메모리 안에 있는 속성(name, age)은 변경할 수 있다

정리

  • var로 선언한 클래스 인스턴스: 속성 변경 가능 + 새로운 인스턴스 할당 가능
  • let으로 선언한 클래스 인스턴스: 속성 변경 가능 + 새로운 인스턴스 할당 불가능

구조체의 인스턴스 생성 시 varlet 차이점

  • 구조체는 값 타입(Value Type) 이므로, let으로 선언하면 인스턴스 자체가 불변(immutable) 상태가 되어 속성을 변경할 수 없다
  • 즉, 변수 자체가 하나의 값을 저장하고 있으며, 값 자체가 변경되지 않는다면 속성도 변경할 수 없다
struct AnimalStruct {
    var name = "동물"
    var age = 0
}

var astruct1 = AnimalStruct()  // var로 선언한 구조체 인스턴스
let astruct2 = AnimalStruct()  // let으로 선언한 구조체 인스턴스

astruct1.name = "강아지"  // ✅ 가능 (속성 변경 가능)
// astruct2.name = "고양이"  // ❌ 오류 발생 (let으로 선언된 구조체 인스턴스는 속성 변경 불가능)

astruct1 = AnimalStruct()  // ✅ 가능 (새로운 구조체 할당 가능)
// astruct2 = AnimalStruct()  // ❌ 오류 발생 (let으로 선언된 구조체 인스턴스는 새로운 할당 불가능)

이런 차이가 발생하는 이유

  • 구조체는 스택(Stack) 메모리에 저장된다
  • let으로 선언하면 그 값이 고정되며 변경할 수 없게 된다
  • 클래스와 달리 값 자체가 변하지 않으면 내부 속성도 변경할 수 없다

정리

  • var로 선언한 구조체 인스턴스: 속성 변경 가능 + 새로운 구조체 할당 가능
  • let으로 선언한 구조체 인스턴스: 속성 변경 불가능 + 새로운 구조체 할당 불가능

클래스(class)와 구조체(struct)의 멤버 접근법

  • Swift에서 클래스와 구조체의 멤버(속성 및 메서드)에 접근하는 방법은 점문법(dot synax, .연산자)을 사용하여 이루어진다
  • 이 방식을 명시적 멤버 표현식(Explicit Member Expression) 이라고도 한다

명시적 멤버 표현식(Explicit Member Expression)

  • 명시적 멤버 표현식이란, 클래스(class)나 구조체(struct)의 인스턴스를 통해 멤버(속성, 메서드 등)에 접근하는 방식을 말한다

기본 형식

인스턴스.멤버
  • 인스턴스: 클래스 또는 구조체의 객체(Instance)
  • 멤버: 인스턴스 내부의 속성(Property) 또는 메서드(Method)

예제

struct Bird {
    var name: String
    func fly() {
        print("\(name)이 날아갑니다!")
    }
}

// 인스턴스 생성
let bBird = Bird(name: "독수리")

// 점문법을 이용해 속성 및 메서드 접근
print(bBird.name)  // "독수리"
bBird.fly()        // "독수리이 날아갑니다!"

위의 코드에서 bBird.fly() 부분이 명시적 멤버 표현식이다

  • bBird ➡️ 구조체 Bird의 인스턴스
  • fly() ➡️ Bird 내부의 메서드
  • bBird.fly()를 호출하면 Birdfly() 메서드가 실행됩니다.

명시적 멤버 표현식의 활용

1. 구조체, 클래스의 속성과 메서드 접근

class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
    func bark() {
        print("\(name): 멍멍!")
    }
}

let myDog = Dog(name: "바둑이")
print(myDog.name)  // "바둑이"
myDog.bark()       // "바둑이: 멍멍!"
  • myDog.name: Dog 클래스의 name 속성에 접근
  • myDog.bark(): Dog 클래스의 bark() 메서드를 호출

2. 타입 멤버(static 멤버) 접근

struct Math {
    static func square(_ num: Int) -> Int {
        return num * num
    }
}

let result = Math.square(5)  // 25
  • Math.square(5): Math 구조체의 static 메서드를 호출하는 방식

3. Swift 표준 라이브러리 활용 예

let randomNumber = Int.random(in: 1...10)
print(randomNumber) // 1~10 사이의 랜덤 값 출력
  • Int.random(in: 1...10): Int 타입의 random(in:) 메서드를 호출하여 난수 생성

정리

  • 점문법(. 연산자) 을 사용하여 클래스 및 구조체의 속성과 메서드에 접근할 수 있다
  • 인스턴스 멤버 접근: 인스턴스.속성 또는 인스턴스.메서드()
  • 타입 멤버 접근: 타입이름.속성 또는 타입이름.메서드()
  • Swift 표준 라이브러리에서도 random() 같은 함수 호출 시 명시적 멤버 표현식을 사용한다

클래스(class)와 구조체(struct) 선언 시 관습적인 작성 순서

클래스와 구조체의 작성 순서

  • 클래스(class)와 구조체(struct)를 선언할 때는 일반적으로 다음 순서로 작성한다
  1. 속성(Property) 선언
  2. 메서드(Method) 선언
struct Person {
    // 1️⃣ 속성 (Properties)
    var name: String
    var age: Int

    // 2️⃣ 메서드 (Methods)
    func introduce() {
        print("안녕하세요, 저는 \(name)이고, 나이는 \(age)살입니다.")
    }
}
  • 위와 같은 순서를 유지하면 일관성이 있고 읽기 쉬운 코드가 된다

클래스 선언 예제

class Car {
    // 1️⃣ 속성 (Properties)
    var brand: String
    var speed: Int

    // 2️⃣ 생성자 (Initializer)
    init(brand: String, speed: Int) {
        self.brand = brand
        self.speed = speed
    }

    // 3️⃣ 메서드 (Methods)
    func drive() {
        print("\(brand) 차량이 \(speed)km/h 속도로 달립니다.")
    }
}

중요한 점: 클래스 내부에는 직접 실행문이 올 수 없다

class Animal {
    var name: String = "강아지"

    print(name)  // ❌ 오류! 클래스 내부에서는 직접 실행문이 올 수 없음

    func speak() {
        print("\(name)가 소리를 냅니다.")
    }
}

오류 발생 원인

  • 클래스 내부에서는 속성과 메서드만 선언 가능
  • print(name) 처럼 클래스 선언부에서 직접 실행문을 작성하면 오류 발생
  • 메서드 내부에서 실행문을 작성해야 한다

올바른 코드

class Animal {
    var name: String = "강아지"

    func speak() {
        print("\(name)가 소리를 냅니다.") // ✅ 올바른 실행문 위치
    }
}

클래스와 구조체의 내부 실행문 차이점

❌ 잘못된 코드 (오류 발생)

class Example {
    var num = 10
    print(num)  // ❌ 클래스 내부에서 직접 실행 불가
}

✅ 올바른 코드

class Example {
    var num = 10
    
    func showNumber() {
        print(num)  // ✅ 메서드 내부에서 실행해야 함
    }
}

구조체에서도 동일한 규칙이 적용된다

struct ExampleStruct {
    var num = 10
    
    func showNumber() {
        print(num)  // ✅ 올바른 코드
    }
}
profile
iOS 개발자 지망생

0개의 댓글