[Swift] 왜 Class는 Initializer를 자동으로 만들어주지 않을까?

2026. 4. 21. 22:28·Apple/Swift
728x90
반응형

안녕하세요! 피피아노입니다 🎵

 

Swift로 개발을 하다보면 한 가지 의문이 생기게 됩니다.

 

구조체인 Struct는 프로퍼티만 선언하면 init이 자동으로 생기는데, Class는 왜 직접 써줘야 할까?

struct Point {
    var x: Int
    var y: Int
    // init(x: Int, y: Int) 자동 생성 
}

class PointClass {
    var x: Int
    var y: Int
    //  자동 생성 안 됨 -> 직접 써야 함
}

 

Struct는 왜 자동 생성이 가능할까?

Struct는 상속이 없습니다.

(상속이 안 되는 건 아닌데 일단 그냥 구조체와 클래스 두가지로만 보면)

컴파일러의 입장에서 보면, 지금 선언된 프로퍼티가 이 타입의 전부입니다. 그래서 모든 프로퍼티를 매개변수로 받는 memberwise initializer를 기계적으로 만들어줄 수 있습니다.

struct User {
    var name: String
    var age: Int
    // 컴파일러: "name이랑 age만 있네"
    // -> init(name: String, age: Int) 자동 생성
}

여기에서는 고민할 게 없습니다. 그냥 프로퍼티 목록을 보고 그대로 init을 만들면 끝입니다.

 

Class가 안 되는 이유

Class는 기본적으로 상속을 지원합니다. 이 한 가지 차이 때문에 초기화가 복잡해집니다.

 

쇼핑앱의 화면을 만들어본다고 가정을 해보겠습니다.

모든 화면에 공통으로 들어가는 기능(네비게이션 바, 로딩 인디케이터 등)을 가진 기본 화면이 있고, 이 화면을 상속해서 각 화면을 만듭니다.

class BaseViewController {
    var screenTitle: String
    var isLoading: Bool

    init(screenTitle: String) {
        self.screenTitle = screenTitle
        self.isLoading = false
    }
}

class ProductDetailViewController: BaseViewController {
    var product: Product
}

ProductDetailViewController는 자기 프로퍼티인 product도 있고, 부모에게 물려받은 screenTitle과 isLoading도 있습니다. 둘 다 값이 들어가야 하는데, 여기서 핵심 원칙이 있습니다.

 

각자 자기 프로퍼티는 자기가 초기화한다는 것입니다.

 

screenTitle은 BaseViewController의 것이니까 BaseViewController의 init이 세팅하고, product는 ProductDetailViewController의 것이니까 ProductDetailViewController의 init이 세팅합니다. 자식은 super.init()을 호출해서 부모에게 맡길 뿐, 직접 부모 프로퍼티를 건드리지 않습니다.

class ProductDetailViewController: BaseViewController {
    var product: Product

    init(product: Product) {
        self.product = product                    // 내 거는 내가
        super.init(screenTitle: product.name)     // 부모 거는 부모한테
    }
}

문제는 컴파일러가 이 super.init() 호출을 자동으로 결정할 수 없다는 것입니다.

 

컴파일러가 결정할 수 없는 것

쇼핑앱이 커지면서 BaseViewController에 다양한 생성 방식이 생겼다고 해보겠습니다.

class BaseViewController {
    var screenTitle: String
    var isLoading: Bool
    var analyticsID: String

    // 방법 1: 제목만 받기
    init(screenTitle: String) {
        self.screenTitle = screenTitle
        self.isLoading = false
        self.analyticsID = UUID().uuidString
    }

    // 방법 2: 제목 + 분석용 ID 직접 지정
    init(screenTitle: String, analyticsID: String) {
        self.screenTitle = screenTitle
        self.isLoading = false
        self.analyticsID = analyticsID
    }
}

이제 상품 상세 화면을 만들 때, 컴파일러가 자동으로 init을 생성한다고 하면 컴파일러 입장에서는 부모의 init이 2개인데 어떤 걸 호출하지? analyticsID는 자동 생성할까 아님 직접 지정할까? 등등 여러가지 혼란이 생깁니다.

그래서 이러한 것은 컴파일러가 임의로 판단할 수 없기 때문에 개발자가 직접 명시하도록 한 것입니다.

// 상품 상세: 분석 ID 자동 생성으로 충분
class ProductDetailViewController: BaseViewController {
    var product: Product

    init(product: Product) {
        self.product = product
        super.init(screenTitle: product.name)  // 방법 1 선택
    }
}

// 결제 화면: 분석 ID를 직접 지정해야 함
class CheckoutViewController: BaseViewController {
    var cartItems: [Product]

    init(cartItems: [Product], analyticsID: String) {
        self.cartItems = cartItems
        super.init(screenTitle: "결제", analyticsID: analyticsID)  // 방법 2 선택
    }
}

 

초기화 순서도 지켜야 하는 이유

Class의 초기화가 복잡한 이유 중 또 한가지 이유는 순서가 중요하기 때문입니다.

Swift는 2-Phase Initalization이라는 규칙을 강제합니다.

 

Phase 1: 모든 저장 프로퍼티에 값 넣기 (아래→위)

자식 클래스의 프로퍼티를 먼저 초기화하고, super.init()을 호출해서 부모 프로퍼티까지 모두 값이 채워진 상태를 만듭니다.

 

Phase 2: 커스터마이징 (위→아래)

모든 프로퍼티가 세팅된 후에야 self에 접근하거나 메서드를 호출할 수 있습니다.

 

class ProductDetailViewController: BaseViewController {
    var product: Product

    init(product: Product) {
        // Phase 1
        self.product = product                    // 1 내 프로퍼티 먼저
        super.init(screenTitle: product.name)     // 2 부모 프로퍼티 초기화

        // Phase 2
        self.loadProductImage()                   // 3 이제 self 사용 가능
    }
}

만약 이 순서를 무시하고 super.init() 전에 self 메서드를 호출하면, 아직 초기화되지 않은 부모 프로퍼티에 접근할 위험이 생깁니다.

 

결국 상속이라는 차이로 인해서 어떤 부모 init을 호출할지, 어떤 인자를 넘길지는 개발자만 알 수 있는 비즈니스 로직이기 때문에 이러한 일이 생기게 되는 것입니다.


감사합니다.

 

잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!

궁금하신 부분은 댓글로 질문 부탁드립니다!

728x90
반응형

'Apple > Swift' 카테고리의 다른 글

[Swift] ARC(Automatic Reference Counting)란 무엇인가  (1) 2026.05.16
[Swift] SwiftData 상속과 스키마 마이그레이션 알아보기  (3) 2026.03.18
[Swift] Swift 6로 마이그레이션 하기  (3) 2026.03.04
[Swift] 싱글톤 패턴(Singleton Pattern) 알아보기  (4) 2026.02.23
[Swift] Swift 동시성 사용하기  (0) 2026.02.09
'Apple/Swift' 카테고리의 다른 글
  • [Swift] ARC(Automatic Reference Counting)란 무엇인가
  • [Swift] SwiftData 상속과 스키마 마이그레이션 알아보기
  • [Swift] Swift 6로 마이그레이션 하기
  • [Swift] 싱글톤 패턴(Singleton Pattern) 알아보기
P_Piano
P_Piano
Apple 생태계 개발자가 되기 위해 학습과 경험을 기록하고 있습니다.
    반응형
    250x250
  • P_Piano
    피피아노의 개발 일지
    P_Piano
  • 전체
    오늘
    어제
    • 분류 전체보기 (231) N
      • Apple (149) N
        • iOS (25)
        • visionOS (5)
        • Swift (78) N
        • UIKit (2)
        • SwiftUI (27)
        • RxSwift (2)
        • Xcode (6)
        • Metal (2)
      • C언어 (5)
      • C++ (8)
      • Dart (1)
      • Python (3)
      • JavaScript (17)
      • Git (1)
      • CS (1)
        • 디자인 패턴 (6)
        • 네트워크 (20)
        • 운영체제 (8)
        • Database (5)
        • 자료구조 (0)
      • IT 지식 (2)
      • IT 뉴스 (4)
      • 경험 (1)
      • 출처 표기 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    데이터베이스
    ios
    코딩테스트
    Apple
    Xcode
    클래스
    swiftUI
    프로세스
    오블완
    스위프트
    배열
    상속
    운영체제
    비동기
    티스토리챌린지
    자바스크립트
    visionOS
    Vision Pro
    변수
    함수
    SWIFT
    네트워크
    wwdc25
    UIKit
    디자인패턴
    제어문
    연산자
    Initializers
    프로퍼티 래퍼
    이니셜라이저
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
P_Piano
[Swift] 왜 Class는 Initializer를 자동으로 만들어주지 않을까?
상단으로

티스토리툴바