싱글톤 진짜 이대로 괜찮을까 싶었던 분들께
iOS 개발하다 보면 싱글톤(Singleton) 안 써본 사람이 없을 거예요. 저도 그랬어요. SomeManager.shared 이런 코드, 프로젝트마다 몇 개씩은 꼭 있었거든요.
그런데 어느 순간부터 이 .shared가 슬슬 불편해지더라고요. 테스트할 때 상태가 엉키고 팀원들이 여기저기서 마음대로 접근하고요.
그러다 알게 된 게 바로 모노스테이트(Monostate) 패턴이에요. 오늘은 Swift에서 모노스테이트가 뭔지, 그리고 정말 싱글톤의 대안이 될 수 있는지 제가 직접 써본 경험을 나눠볼게요.
먼저 결론부터 말씀드리면, 모노스테이트는 “상태는 하나로 공유하되 인스턴스는 여러 개 만들 수 있게” 하는 패턴이에요. 싱글톤과 목적은 같지만 접근 방식이 정반대죠. 다만 만능은 아니라 상황에 따라 갈려요. 아래에서 하나씩 풀어볼게요.
모노스테이트 패턴이 대체 뭔가요?
싱글톤은 “인스턴스를 딱 하나만 만든다”가 핵심이에요. 생성자를 막아두고 shared 하나만 쓰게 하죠.
모노스테이트는 반대예요. 인스턴스는 얼마든지 만들 수 있어요. 대신 그 안의 상태(데이터)를 전부 static으로 공유해버려요.
즉 객체 A를 만들든 B를 만들든, 둘이 바라보는 데이터는 같은 곳이에요. 겉으로는 평범한 객체처럼 보이는데 속은 하나로 연결돼 있는 거죠.
Swift로 간단히 보면 이런 모양이에요.
struct Settings {
private static var _volume = 50 // 상태를 static으로 공유
var volume: Int { // 인스턴스처럼 쓰지만
get { Settings._volume }
set { Settings._volume = newValue }
}
}
// 서로 다른 인스턴스지만 상태는 하나
var a = Settings(); a.volume = 80
print(Settings().volume) // 80
Settings()를 새로 만들어도 volume은 80으로 나와요. 인스턴스는 여러 개인데 상태는 하나, 이게 모노스테이트의 전부예요.
싱글톤이랑 뭐가 다른가요?
가장 크게 다른 건 사용하는 쪽 코드예요.
싱글톤은 쓰는 사람이 항상 .shared를 의식해야 해요. “아 이건 싱글톤이지” 하면서요. 모노스테이트는 그냥 평범한 객체처럼 Settings() 만들어 쓰면 돼서, 쓰는 쪽이 내부 구현을 몰라도 돼요.
표로 정리하면 이래요.
| 구분 | 싱글톤 | 모노스테이트 |
|---|---|---|
| 인스턴스 개수 | 딱 1개 | 여러 개 가능 |
| 공유되는 것 | 인스턴스 자체 | 상태(static 데이터) |
| 사용하는 쪽 | .shared 명시 |
일반 객체처럼 |
| 상속 | 까다로움 | 상대적으로 자유로움 |
특히 상속이 재밌어요. 싱글톤은 상속하기가 참 애매한데, 모노스테이트는 일반 타입이라 확장이 좀 더 유연한 편이에요.
그래서 싱글톤의 대안이 될 수 있나요?
솔직하게 말하면, 될 때도 있고 아닐 때도 있어요.
장점부터요. 쓰는 쪽 코드가 깔끔해지고 .shared라는 전역 접근점이 코드 곳곳에 박히지 않아요. 인터페이스가 일반 객체랑 똑같으니 나중에 구조를 바꿀 때도 부담이 적고요.
모노스테이트는 “전역 상태를 감추는” 패턴이에요. 편해 보이지만, 감춰진 전역 상태는 그 자체로 양날의 검입니다.
문제는 여기예요. 겉보기엔 평범한 객체라서 팀원이 “이거 그냥 지역 객체겠지” 하고 만들었는데 알고 보니 상태가 전역으로 공유되고 있으면 오히려 더 헷갈려요.
그리고 static 공유라 멀티스레드 환경에서 동시성 문제도 똑같이 신경 써야 해요. 이 부분은 싱글톤이랑 다를 게 없어요.
제 경험으로는, 요즘 Swift라면 모노스테이트보다 의존성 주입(Dependency Injection)을 먼저 고려하는 걸 추천해요. 테스트도 쉽고 의존 관계도 명확하거든요.
그럼 언제 쓰면 좋을까요?
정리하면 이런 경우예요.
- 기존 싱글톤 코드가 너무 많아서 점진적으로 걷어내고 싶을 때
- 사용하는 쪽 인터페이스를 일반 객체처럼 깔끔하게 유지하고 싶을 때
- 상속이나 확장이 필요한 전역 상태를 다뤄야 할 때
반대로 새 프로젝트를 처음부터 짜는 거라면, 저는 모노스테이트보다 의존성 주입이나 명시적인 상태 관리를 먼저 보라고 말하고 싶어요.
결국 모노스테이트는 “싱글톤의 완벽한 상위호환”이 아니라 “성격이 다른 또 하나의 선택지”예요. 도구함에 넣어두고, 상황 맞을 때 꺼내 쓰면 되는 거죠.
오늘 내용이 .shared 남발에 지쳤던 분들께 작은 힌트가 됐으면 좋겠어요. 무작정 갈아타기보다, 우리 프로젝트에 뭐가 맞을지 한 번쯤 고민해보시길 응원할게요!
