iOS 개발자가 많이 하는 실수 - struct vs class 차이를 오해하여 발생하는 문제 (Value Type / Reference Type 혼동)
Dev Study/iOS 2025. 12. 4. 19:51iOS 개발자가 많이 하는 실수 - struct vs class 차이를 오해하여 발생하는 문제 (Value Type / Reference Type 혼동)
1. 핵심 차이 요약
| 구분 | struct | class |
| 타입 | 값 타입(Value Type) | 참조 타입(Reference Type) |
| 동작 | 복사(Copy) | 참조(Reference) |
| 메모리 | 스택 중심(COW 포함) | 힙 할당 |
| ARC | 없음 | 있음 |
| Thread-safe | 상대적으로 안전 | 공유 시 위험 |
| SwiftUI/TCA | struct 기반 설계 추천 | 최소화 필요 |
| UIKit View | struct 불가 | class 필수 |
핵심 문장:
struct는 복사되고, class는 공유된다.
이 한 문장이 대부분의 문제를 설명한다.
2. 초보자가 흔히 하는 실수
실수 1) struct를 class처럼 생각함
struct User {
var name: String
}
var a = User(name: "Jun")
var b = a // 복사
b.name = "Lee"
print(a.name) // "Jun"
print(b.name) // "Lee"
초보자는 a와 b가 같은 객체라고 생각하지만
struct는 값 타입이므로 완전히 다른 인스턴스이다.
→ 값이 공유된다고 착각하여 버그 발생
실수 2) class를 struct처럼 생각함
class User {
var name: String
init(name: String) { self.name = name }
}
var a = User(name: "Jun")
var b = a // 참조 복사 (실제로는 같은 객체)
b.name = "Lee"
print(a.name) // "Lee"
print(b.name) // "Lee"
여기서는 a와 b가 같은 객체이다.
즉, b를 수정하면 a도 수정됨.
→ “왜 값이 바뀌지?” 라는 상황 발생
실수 3) SwiftUI에서 class 기반 ViewModel을 잘못 다룸
class MyViewModel: ObservableObject {
@Published var count = 0
}
View 쪽에서:
@ObservedObject var vm = MyViewModel() // ❌ 매 렌더링 때마다 새로 만들어짐
→ 상태가 계속 초기화됨
→ SwiftUI가 struct 기반 렌더링이라는 점을 이해하지 못한 실수
정해진 정답:
@StateObject var vm = MyViewModel() // ✔️ 한번만 유지됨
실수 4) TCA에서 State를 class로 선언
struct Feature {
class State { } // ❌ 절대 안 됨 (불변성 깨짐)
}
TCA(State, Action, Reducer)에서는
State는 반드시 struct 여야 함.
→ class를 State로 쓰면 불변성(Inmutability) 붕괴 + 예측 불가 상태 변경 발생
→ DebuggingTools(Logging, Time Travel Debugger)가 오작동함
실수 5) struct 내부에 mutable class를 넣어놓는 실수
struct UserState {
var profile: Profile // Profile이 class라면?
}
외형적으로는 값 타입처럼 보여도
내부에 참조 타입(class)이 있으면 부분적으로 reference 타입처럼 동작해 버림.
이로 인해:
- struct 복사해도 내부 class는 공유됨
- 예상치 못한 상태 공유 발생
- SwiftUI 렌더링이 꼬임
→ “겉만 struct”인 잘못된 설계
3. Value Type(값 타입)의 장점
Swift가 struct를 적극 추천하는 이유는 다음과 같다.
1) COW(Copy-on-Write)로 설계되어 효율적
값이 실제로 변경되기 전까지는 복사하지 않아 성능에 유리.
2) 상태(State)가 예측 가능
참조 공유가 없어서 디버깅이 쉬움.
3) SwiftUI/TCA 등 최신 프레임워크와 궁합 최상
- SwiftUI → 모든 View가 struct
- TCA → State struct 기반
4) Race Condition 감소
참조 타입처럼 여러 스레드에서 같은 객체를 공유하지 않기 때문.
4. Reference Type(class)의 장점과 반드시 필요한 경우
class는 “나쁘다”가 아니라 필요한 곳이 다르다.
✔ 반드시 class를 써야 하는 경우
- UIKit View (UIButton, UIView 등)
- NSObject 기반 API 사용할 때
- 싱글턴(Service, Manager 등)
- Actor/GCD 관리 객체
- 참조 공유가 필요할 때
즉,
- UI 요소
- OS 시스템 객체
- 서비스/매니저 계층
에서는 class가 필수다.
5. struct vs class 선택 기준 (실무용 체크리스트)
struct가 맞는 경우
- 모델(Model)
- DTO, Domain State
- 값의 복제가 자연스러운 경우
- 상태를 명확하게 추적하고 싶을 때
- SwiftUI/TCA state
- 테스트 용이성 높이기
class가 맞는 경우
- UIKit View / ViewController
- 공유 리소스
- 싱글턴
- FileManager / URLSession / CLManager / AVFoundation 등 시스템 객체
- Objective‑C 기반 API
6. struct vs class를 잘못 선택했을 때 발생하는 문제
- 예상치 못한 값 수정
- UI 업데이트 이상 동작
- SwiftUI View 재렌더링 꼬임
- 레이스 컨디션 / 상태 충돌
- TCA State 시간 여행 디버깅 불가능
- 메모리 관리 어려움(ARC over-retain)
대부분은 참조 타입의 공유로 인해 일어난다.
7. 실무 예시: struct → class 오해로 생긴 버그
struct Counter {
var count: Int
}
class Manager {
var counter = Counter(count: 0)
}
let m = Manager()
let c = m.counter
c.count += 1
print(m.counter.count) // 0 ❌ 예상과 다름
초보자는 c.count += 1 하면 Manager 안의 값도 증가한다고 착각하지만
실제로는 Counter가 값 타입이라 복사본만 증가한다.
8. 정리
- struct = Value Type → 복사
- class = Reference Type → 공유
- Swift에서 struct 중심 설계를 선호하는 이유:
- 안정성, 성능, 예측 가능한 상태
- 적절한 타입 선택이 코드의 안정성과 예측 가능성을 결정한다.
- SwiftUI, TCA 등 현대 iOS 아키텍처에서는 struct가 기본이다.
'Dev Study > iOS' 카테고리의 다른 글
| iOS 개발자가 많이 하는 실수 - 클로저에서 self를 강하게(strong) 캡처해 메모리 누수가 발생 (0) | 2025.12.04 |
|---|---|
| iOS 개발자가 많이 하는 실수 - Codable decode 실패 시 에러 원인 확인 없이 try? 사용 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - switch-case에서 default를 사용하고 enum 케이스 추가 시 실수 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - Dictionary에서 key 존재 여부 확인 없이 강제 언래핑(!) (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - Array append 반복 사용 vs reserveCapacity / Array(repeating:count:) 성능 문제 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - 문자열 비교 시 lowercased() 반복 사용 문제 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - guard를 잘못 사용(Early Exit Misuse) (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - Optional Binding 중첩(if let 지옥) (0) | 2025.12.04 |

