Struct 구조체..... (C에서만 보던 녀석이 또 튀어나왔다)

BS_Lee·2025년 6월 24일

swift

목록 보기
8/21

Swift에서 struct는 굉장히 자주 쓰이는데, 클래스랑 비슷해 보이지만 사실 꽤 다르다.
딱 두 단어로 정리하면 값 타입이고, 상속 불가이다.

그래서 오늘은 Swift의 구조체에 대해 차근차근 정리해보려 한다.


구조체?

struct Person {
    let name: String
    let age: Int
}

let foo = Person(name: "Foo", age: 20)

Swift의 구조체는 내부에 저장된 속성들을 기준으로 자동으로 생성자(init)를 만들어준다.
이런 걸 멤버와이즈 이니셜라이저라고 부른다.

즉, 따로 init 안 만들어도 이렇게 Person(name:age:)처럼 바로 만들 수 있다는 얘기다.


그럼 언제 사용자 정의 init이 필요할까?

struct CommodoreComputer {
    let name: String
    let manufacturer: String

    init(name: String) {
        self.name = name
        self.manufacturer = "Commodore"
    }
}

여기서 궁금한 게 하나 생긴다.
"기본 생성자도 있는데 굳이 init을 왜 만들지?"

그 이유는 manufacturer처럼 내부에서 고정 값을 넣고 싶을 때!
외부에서 굳이 넣을 필요 없는 값을 내부에서 세팅해줄 수 있어서다.


기본값을 활용하면 더 간단해진다

struct CommodoreComputer2 {
    let name: String
    let manufacturer = "Commodore"
}

이렇게 속성에 기본값을 주면?
Swift는 알아서 생성자를 만들어준다.
그래서 CommodoreComputer2(name:)만 있어도 된다.


fullName을 계산해주는 두 가지 방식

struct Person2 {
    let firstName: String
    let lastName: String
    let fullName: String

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.fullName = "\(firstName) \(lastName)"
    }
}

이렇게 init에서 fullName을 지정해줄 수도 있지만,
사실 더 Swift스러운 방법은 따로 있다.

struct Person22 {
    let firstName: String
    let lastName: String

    var fullName: String {
        "\(firstName) \(lastName)"
    }
}

왜 이게 더 좋을까?

  • fullName은 실제로 저장된 값이 아니라 계산 가능한 값이다.
  • 이렇게 하면 속성이 바뀌더라도 항상 최신 상태를 유지할 수 있다.

구조체 내부 값을 바꾸고 싶다면? → mutating

struct Car {
    var currentSpeed: Int

    mutating func drive(speed: Int) {
        currentSpeed = speed
    }
}

여기서 궁금한 점.
"왜 굳이 mutating이라는 키워드가 필요한 걸까?"

그 이유는 구조체는 값 타입이라 내부에서 값을 바꾸려면 스스로를 다시 바꿔야 하기 때문.
그래서 명시적으로 mutating을 붙여줘야 한다.

// currentSpeed가 let이면 당연히 안바뀜.
var car = Car(currentSpeed: 100)
car.drive(speed: 300)

구조체는 복사된다 (값 타입 특징)

let copy = car
car.drive(speed: 500)
print(copy.currentSpeed)  // 300

이 예제를 보면 명확하다.
copycar를 복사한 값이라, 이후 car가 바뀌어도 copy는 그대로다.


구조체는 상속이 안 된다

struct LivingThing {
    init() {
        print("I'm living thing")
    }
}

// 에러 발생: 구조체는 상속이 안 된다
// struct Animal: LivingThing { }

이쯤에서 이런 의문이 생긴다.
"왜 struct는 상속이 안 되는 거지?"

Swift는 컴포지션(구성)을 더 중요하게 본다.
클래스처럼 복잡하게 상속보단, 작은 구조체들을 조립해서 쓰는 걸 더 선호한다.
그래서 swiftUI 잠깐 배웠을 때, 데이터는 struct. 데이터를 제어하는건 class를 사용한다고 들었던거같다.


copy 메서드 패턴으로 새 인스턴스 만들기

struct Bike {
    let manufacturer: String
    let currentSpeed: Int

    func copy(currentSpeed: Int) -> Bike {
        Bike(manufacturer: self.manufacturer, currentSpeed: currentSpeed)
    }
}

값 타입에서 가장 많이 쓰이는 패턴 중 하나가 바로 이거다.

기존 값을 유지하면서 일부 속성만 바꾼 새로운 인스턴스를 만들고 싶을 때 유용하다.

let bike1 = Bike(manufacturer: "HD", currentSpeed: 20)
let bike2 = bike1.copy(currentSpeed: 30)

다른 방법도 있다.

struct Bike {
	let manufacturer: String
    let currentSpeed: int
    
    mutating func moveToOrigin() {
    	// 이런식으로 작성하면 self 전체를 다른 값으로 통째로 바꾸는 것도 가능.
    	self = Point(manufacturer: "YD", currentSpeed: 30)
    }
}

정리하자면

  • 구조체는 값 타입이고 상속 불가
  • 생성자는 자동으로 만들어지지만, 필요에 따라 init 정의 가능
  • 계산 속성(computed property) 활용은 코드 간결성과 최신 상태 유지에 좋다
  • 내부 값 수정은 mutating 키워드로 명시
  • 복사(copy) 패턴은 상태 변경을 안전하게 다룰 수 있는 방법

한 줄 요약

Swift의 struct는 값 기반 프로그래밍을 위한 핵심 도구다.

불변성, 복사, 컴포지션을 잘 이해하고 쓰면, Swift가 훨씬 더 명확하고 안전한 언어로 다가온다.

0개의 댓글