[Swift] 정말 기본적으로 구조체를 사용해야 할까?

iosSwiftClassStruct
avatar
2025.04.23
·
6 min read

Swift를 처음 배울 때 들었던 말이 있다.

기본적으로 구조체를 사용해라."

Apple 공식 문서를 보면 아래 이미지에서 붉은색으로 표시한 것처럼 기본적으로 구조체를 사용하라고 명시되어 있다.

5476

정말 그래야할까?

이번에는 위 이미지에서 파란색으로 밑줄 친 부분을 살펴보자. 문서에서 구조체를 기본으로 권장하면서 전제하고 있는 부분이 있다.

  • store data and model behavior

즉, 문서는 데이터를 저장하고 해당 데이터에 관련된 동작을 모델링하는 타입에 대해 얘기하고 있다. 다시 말해, Apple에서 기본적으로 구조체를 사용하라고 권장하는 것은 주로 데이터를 표현하거나, 그 데이터에 직접 연결된 간단한 동작을 수반하는 타입들에 해당한다.

예를 들면, 아래처럼 사용자 정보를 담고, 간단한 유효성 검사를 수행하는 User 타입은 구조체로 구현하기에 적절하다. 또한 ViewModel의 State 타입처럼 앱의 UI 상태를 표현할 때도 구조체가 어울린다.

struct User {
    let name: String
    let age: Int
    let email: String

    func isValidEmail() -> Bool {
        email.contains("@")
    }
}

extension UserViewModel {
    struct State {
        var allUsers: [User]
        var filteredUsers: [User]
    }
}

그렇다면 클래스는 언제 사용해야 할까?

구조체와 클래스의 선택에 있어 내가 생각하는 가장 기본적인 기준은 내부 상태의 공유 여부이다. 해당 타입이 내부 상태를 공유해야한다면 클래스를, 그렇지 않다면 구조체를 사용하면 된다.

하지만 이 기준만으로는 부족할 수 있다. 예를 들어, 상태를 가지지 않더라도 다음과 같은 경우에는 클래스가 더 적절할 수 있다.

  • 의존성을 주입해야 하는 경우

    • 클래스는 여러 객체가 동일한 인스턴스를 공유할 수 있으므로 하나의 인스턴스를 여러 곳에 주입해서 사용할 수 있다.

    • 반면 구조체는 인스턴스를 주입하면 복사되기 때문에 공유가 되지 않아 의존성의 일관성 유지가 어렵다.

  • 객체의 참조를 교체하거나 추적해야 하는 경우

    • Observer pattern, Delegate Pattern 등을 통해 인스턴스 간의 연결이 필요한 경우

    • 특정 객체를 외부에서 교체하거나, 뷰의 상태를 외부에서 갱신해야할 경우

  • 생명주기를 명확히 관리해야 하는 경우

    • deinit 같이 객체가 메모리에서 해제될 때 특정 행위를 해야할 경우

즉, 구조체와 클래스는 단순히 값 타입, 참조 타입의 문제가 아니라, 이 타입이 어떤 책임을 가지며, 어떻게 사용되어야 하는가에 따라 결정되는 것이다.

결과적으로 나는 구조체와 클래스를 선택하기 위한 기준을 아래처럼 만들었다.

  1. 이 타입이 상태를 공유해야 하는지

  2. 참조로 다루어져야 의미가 있는지

  3. 값이 복사되는 게 아닌, 변경 사항이 퍼져야 하는지

  4. 생명주기를 명확히 컨트롤해야 하는지

이 질문에 해당하는 게 많다면 클래스를, 그렇지 않다면 구조체를 사용한다.


Apple이 "Use structures by default."라고 말한 것은, 모든 타입에 구조체를 우선시하라는 의미가 아니다. 실제로 "모든 곳에 구조체를 우선시하라"는 말이라 하더라도, 그 의미는 구조체로 표현할 수 있는 단순하고 독립적인 값은 구조체로 표현하되, 불필요하게 클래스를 남발하지 말라는 철학에 가까운 것 같다.

기본적으로 구조체를 사용하는 건 합리적일 수 있다. 하지만 소프트웨어 설계에서 중요한 건 기본이 아니라 의도이다. 책임, 역할, 동작 방식 등에 따라 구조체와 클래스를 적절히 구분할 수 있어야 더 유연한 설계가 가능하다.







- 컬렉션 아티클