Swift : Struct vs Class

Struct 와 Class 에 대한 개인적인 생각들~

Jay Kim
9 min readJan 8, 2022
Photo by Ridham Nagralawala on Unsplash

많은 iOS 개발자들이 struct (with protocol)은 좋은 것이고, class 는 사용을 지양해야 할 나쁜 것이라는 편견을 가지고 있는 것 같아서, 이에 대한 내용을 한 번 정리해보고자 한다.

iOS 개발을 언제부터 시작했는지, 조금 더 풀이하면

  • Objective-C
  • Swift

중에서 어떤 language 로 개발을 시작했는지에 따라서 의견이 어느정도 갈릴 것 같기는 하지만, 그것과는 별개로 왜 이런 오해(?)가 시작되었는지에 대해서 이야기해보고 싶다.

다만 오늘 이야기하는 내용들은 개발자들끼리도 서로 의견이 많이 상충되는 것들이고, 필자 본인의 생각이 정답이라고 말하고 싶은 생각은 없다.
하지만 무엇이 옳다 그르다를 떠나서 이 내용에 대한 본인의 의견을 주장하기 전에 적어도 아래 내용들에 대해서 고민을 한 번씩 해보면 어떨까 하는 생각이다.

1. 모든 것의 시작은 Apple 공식 문서에서

`swift 로 개발할 때 class 는 쓰지 않는 것이 좋다`라고 이야기하는 분들이 주로 언급하는 내용이 Choosing Between Structures and Classes 라는 애플 공식 문서이다.

위 문서에서 발췌한

  • Use structures by default.

라는 내용이 아마 많은 분들을 오해하게 만든 문장이 아닐까 생각된다. (실제로도 저 문장을 언급하면서 이 이야기가 시작되는 경우가 많다 ^^;;;)

그럼 저 문장 주변 내용을 한 번 자세히 짚어보자.

위에 빨간색 박스처리한 내용들을 보면, 애플에서 struct 를 기본적으로 사용하라고 이야기한 내용은 data model 에 관한 이야기이다.
한국 iOS 개발자들이 주로 사용하는 디자인 패턴인 MVVM 의 관점에서 이야기하면

  • View 도 아니고
  • ViewModel 도 아니고
  • Model 에 대한 이야기

인 것이다.

현재 회사에 입사 후 본인이 처음 코드를 분석할 때 가장 이해가 안되었던 부분이 ViewModel 을 struct 로 만들어 사용하고 있다는 것이었다.

물론 MVVM 이라는 패턴이 아주 rough 한 형태의 제안만 던지는 것이고 그 구현체는 회사마다 다를 수 밖에 없다는 것을 인정한다.
그래서 실제로 ViewModel 을 struct 로 정의하여 사용하는 회사들도 많을 것이라 생각한다.

잡설이 길어지므로, 위 문서의 하단에 있는 다른 내용들을 한 번 살펴보자.

위의 내용을 보면 Identity 즉 해당 object instance를 사용하는 사용처들이 instance 의 동일성을 보장해야 할 경우에는 struct 가 아니라 class 를 사용하라고 이야기하고 있다.

struct 는 기본적으로 copy 방식으로 동작을 하기 때문에, 이미 할당된 struct 를 다른 변수에 assign 하여 사용하여 복사본이 여러 벌 생성되고 이 복사본들이 코드 내에서 각각 사용될 때는 instance 의 동일성이 보장되지 않기 때문에 identity 가 중요할 경우에는 class 를 사용하라는 이야기이다.

instance 의 동일성, 이라고만 하면 대부분의 iOS 개발자들이 이해할 수 있을 것이라고 생각하지만 혹시라도 내용이 잘 와닿지 않는 분들은 아래의 playground 코드와 실행결과를 보면 쉽게 이해가 될 것이다.

위 코드를 실행하면 결과는 아래처럼 출력된다.

ModelAsStruct 는 instance 의 동일성이 보장되지 않고, ModealAsClass 는 instance 의 동일성이 보장되고 있다.

2. Struct 내부에서 property 로 Class 사용

사실 가장 문제가 되는 내용이기도 하고 오늘의 글을 쓰고 싶었던 이유이기도 한 내용이 ‘Struct 내부에서 사용하는 Class’ 에 대한 것이다.

  • struct 내부에 class 형태의 property 가 존재하고
  • 해당 struct 로 만든 instance 가 복사되어 여러 곳으로 전파된 경우에 (assign 하는 방식으로)
  • 위의 class property 는 언제 deinit() 되는 것일까?

한 번 위 내용에 대해서 생각해보자, 바로 답이 떠오르는 분도 있을 것이고 아리송한 분들도 있을 것이다.

그렇다면 코드로 한 번 확인을 해보자.
아래 형태로 샘플 코드를 만들어보고자 한다.

  • Model : class 형태의 property 를 가지고 있는 struct
  • ViewController : 이 녀석이 Initial View Controller 이다.
  • SubViewController1 : ViewController 가 push 할 ViewController
  • SubViewController2 : SubViewController1 이 push 할 ViewController
  • SubViewController1 은 init 시에 Model 을 하나 생성해서 property 로 할당
  • SubViewController2 는 init 시에 SubViewController1 의 Model instance 복사본을 전달받음
  • VC → SubVC1 → SubVC2 로 navigation-push 를 진행했다가
  • VC 까지 navigation-pop 두 번으로 돌아옴

Model 의 구성과 코드는 아래와 같다.

  • Model 자신은 struct
  • name 이라는 value-type(String) property 를 가지고 있음
  • classProperty 라는 reference-type(Class) property 를 가지고 있음
  • ClassInStruct 는 deinit 이 호출될 때 print 를 실행한다 (byebye~)
Model.swift

ViewController 의 구성과 코드는 아래와 같다.

  • ViewController 는 최초 앱이 시작되면 호출되는 VC
  • pushSubVC1() 이라는 함수를 호출하면
  • SubViewController1 이라는 VC 를 생성하여 navigation-stack 에 push 한다.
ViewController.swift

SubViewController1 는 구성과 코드는 아래와 같다.

  • init 시점에 model 이라는 이름으로 Model instance를 생성하여 property 에 저장
  • pushSubVC2() 이라는 함수를 호출하면 model 의 복사본인 passedModel 을 생성하고 model, passedModel 내용을 console 에 출력
  • SubViewController2 라는 VC 를 생성한 후 passedModel 전달한 후, navigation-stack 에 push 한다.
SubViewController1.swift

SubViewController2 는 구성과 코드는 아래와 같다.

  • init 시점에 전달받은 Model instance를 자신의 model property 에 저장
SubViewController2.swift

자 이제 이 섹션의 제일 처음에 이야기했던

  • VC → SubVC1 → SubVC2 로 navigation-push 를 진행했다가
  • VC 까지 navigation-pop 을 두 번 진행하여 돌아옴

하는 과정을 진행할 것인데, ClassInStruct 는 과연 몇 번 그리고 어느 시점에 deinit 될까~~~~~~~~~~~~~~~~요?

Struct 내부의 Class property 가 memory 해제되는 시점

위와 같이 model, passedModel 이 각각 별개의 instance 임에도 불구하고, model, passedModel 이 모두 메모리에서 해제된 이후에 classProperty 는 한 번만 deinit 되는 것을 확인할 수 있다!!!

많은 iOS 개발자들이 struct 를 쓰는 것이 class 를 사용하는 것보다 무조건 좋다고 생각하고, struct 는 assign 한 이후 여기저기에 전달해서 사용해도 메모리 내에서 아무런 문제도 없고 신경쓸 사항도 없다…라고 생각하는 경우가 많은데, 그건…

  • struct 내부에 value-type 만 있을 경에에만 해당

되는 이야기이다.

struct 내부에 class property 가 존재하는 경우

  • 이 class property 가 memory-capture 되지는 않는지
  • class property 가 예상하는 기간보다 더 오래 memory 내에서 deinit 되지 않은 채로 남아있지는 않을지

여러가지 고려할 사항들이 생기고, struct 에는 class 와 달리 deinit() 함수가 존재하지 않기 때문에 struct 내부의 class property 로 인한 memory leak 이 발생했을 때 원인을 추적하기가 정말 어렵다.
또한 위에서 이야기한 것처럼 struct 자체에 대한 identity 가 중요한 경우에는 struct 를 class 로 변경하여 사용하는 방법을 고려해야 한다.

class 는 나쁜 것이 아니라 단순히 data-model 로서 사용하기에는 closure 내에서의 memory-capture 등 신경써주어야 할 것들이 많기에, 여러가지 면에서 struct 의 사용을 우선적으로 권장한다는 것이 Apple 공식문서에서 이야기하고자 했던 것이 아닐까 한다.

struct 와 class 는 다 자기나름 대로의 효용이 존재하고, 뭐가 좋고 뭐가 나쁘다는 것은 개발자들의 잘못된 편견이라고 생각한다.
더 재밌는 것은 우리가 항상 아무렇지 않게 사용하는 UIViewController 와 같은 Apple Framework 에서 제공하는 정말 많은 기능들이 class 로 만들어져 있는데, 그것들은 과연 나쁜 것일까? 잘못 만들어져 있는 것인가?

오늘은 Struct vs Class 라는 주제를 가지고 본인이 생각하던 것들을 이야기했는데, 뭐… 사실… 정답은 없다고 생각하고 , 개발자들 모두 각자 이에 대한 자신만의 생각이 있을 것이다.

다만 무언가를 좋다, 나쁘다 라고 확정된 생각을 가지기 전에, 사람들이 왜 무언가는 좋고 무언가를 나쁘다고 생각하게 되었는지에 대해 한 번 쯤은 돌아볼 수 있기를 바란다.

여기까지 읽어주셔서 감사하고,
모든 iOS 개발자에게 행복 있기를~

--

--

Jay Kim

iOS를 사랑하는 Software Engineer, 반복 설명하는 것이 점점 귀찮아져서 글을 씁니다 ^^;;;;