프로젝트마다 그 파일이 하나씩 있습니다. 여는 순간 스크롤바가 실처럼 가늘어지는 파일.
이전 글 코드 냄새 #3에서 이어지는 내용입니다.
이름은 AppManager나 MainViewController, DataStore 같은 것이죠. 팀에서 가장 자주 바뀌면서 가장 자주 충돌하는 파일이기도 합니다.
갓 오브젝트(God Object)입니다.
1998년 나온 『AntiPatterns』에서는 이걸 블롭(The Blob)이라고 부릅니다(출판 정보). 주변의 책임과 데이터를 계속 흡수하며 커지는 덩어리를 가리키는 말이죠.
무엇이 갓 오브젝트인가
줄 수만으로 정하는 건 아닙니다. 2,000줄짜리 타입이라도 한 가지 일만 한다면 그건 그냥 큰 타입입니다(God Class 판별 연구).
갓 오브젝트를 가르는 건 아는 것의 범위입니다. 세 가지가 겹치면 거의 확실합니다.
- 너무 많은 것을 압니다. 네트워크, 데이터베이스, 화면 상태, 로그인 정보, 결제까지 한 타입 안에 있습니다.
import목록만 봐도 드러납니다. - 너무 많은 것이 그것을 압니다. 프로젝트 곳곳에서 이 타입을 참조합니다. 싱글톤이면 참조 경로가 코드에 안 드러나 더 심합니다.
- 상태가 흩어져 있습니다. 프로퍼티가 서른 개인데 어떤 조합이 유효한지는 아무도 모릅니다.
isLoading이 true인데 error도 nil이 아닌 상태가 가능한지, 같은 질문에 답할 수 없게 됩니다.
iOS에서 이 자리는 대체로 정해져 있습니다. 뷰 컨트롤러입니다.
화면 하나의 레이아웃, 네트워크 호출, 테이블 데이터 소스, 화면 전환, 상태 관리가 전부 한 파일에 모입니다.
이런 구조를 매시브 뷰 컨트롤러(Massive View Controller)라고 부릅니다. AppDelegate와 이름에 Manager가 붙은 타입도 단골입니다.
왜 계속 커지나
아무도 그렇게 만들려고 하지 않았습니다. 그게 핵심입니다.
갓 오브젝트는 설계 결정이 아니라 중력의 결과입니다.
새 기능을 넣어야 한다고 해 봅시다. 필요한 데이터가 이미 이 클래스 안에 다 있습니다.
| 선택 | 비용 |
|---|---|
| 기존 클래스에 메서드 하나 추가 | 20분 |
| 새 타입 생성 | 의존성 전달, 초기화 지점, 생명주기까지 반나절 |
합리적인 선택을 매번 하는데 그 선택이 매번 같은 방향입니다. 그렇게 200줄이던 파일이 3년 뒤 4,000줄이 됩니다.
이 성장에는 단계마다 정당한 이유가 하나씩 붙어 있습니다. 그래서 되돌리기가 어렵습니다.
여기에 깨진 유리창 효과가 얹힙니다. 이미 3,000줄인 파일에 50줄 더 넣는다고 아무도 반대하지 않습니다.
200줄짜리 파일에 50줄을 넣을 때와 심리적 저항이 다릅니다.
어디가 아픈가
피해는 네 가지로 나타납니다.
- 테스트가 불가능해집니다. 이 타입 하나를 만들려면 네트워크, 데이터베이스, 사용자 세션이 전부 필요합니다. 순수한 계산 로직 하나를 검증하려는데 서버가 떠 있어야 하는 상황이 됩니다.
- 병렬 작업이 막힙니다. 팀원 셋이 각자 다른 기능을 만드는데 전부 같은 파일을 고칩니다. 충돌이 상시로 나고 리뷰할 때 변경 사항이 뒤섞여 보입니다.
- 변경 영향 범위를 알 수 없습니다. 프로퍼티 하나를 고쳤을 때 무엇이 깨질지 예측하려면 4,000줄을 다 알아야 합니다. 실제로는 아무도 모르니 고치지 않고 새 프로퍼티를 추가합니다. 그래서 또 커집니다.
- 재사용이 안 됩니다. 그 안의 날짜 포맷 로직이 다른 화면에도 필요한데 꺼내오려면 클래스 전체가 딸려옵니다. 그래서 복사해서 붙입니다.
특히 첫 번째가 치명적입니다. 테스트가 없으니 쪼갤 수 없고 못 쪼개니 계속 커집니다.
이 순환이 갓 오브젝트를 오래 살아남게 하는 진짜 이유죠.
커지는 걸 어떻게 알아채나
느낌 말고 신호를 씁니다.
신호 1 · Git 변경 빈도
가장 실용적인 지표입니다. 최근 1년간 가장 많이 수정된 파일 목록을 뽑으면 갓 오브젝트가 상위에 있습니다.
git log --format=format: --name-only --since=1.year.ago \
| grep '\.swift$' | sort | uniq -c | sort -rn | head -20
변경이 잦다는 건 그 파일이 서로 다른 이유로 바뀐다는 신호입니다. 단일 책임 원칙 위반의 실측값이죠.
신호 2 · 응집도
메서드들이 서로 다른 프로퍼티 집합만 건드린다면 그 타입은 사실 두 개일 가능성이 큽니다.
프로퍼티 A·B만 쓰는 메서드 다섯 개와 C·D만 쓰는 메서드 여섯 개가 있다면 경계선 후보가 이미 보입니다.
신호 3 · 타입 길이 규칙
SwiftLint를 쓴다면 type_body_length와 file_length가 기본으로 켜져 있습니다.
타입 본문 250줄, 파일 400줄이 기본 경고선이고 프로젝트에 맞게 조정하면 됩니다. 숫자 자체는 임의지만 선을 넘는 순간 대화가 시작된다는 게 값어치입니다.
해체 순서
한 번에 다시 쓰는 건 거의 실패합니다. 순서가 있습니다.
| # | 단계 | 무엇을 하나 |
|---|---|---|
| 1 | 그물 치기 | 테스트가 없다면 특성화 테스트부터 만듭니다 |
| 2 | 무상태 로직 분리 | 날짜 포맷·문자열 검증·금액 계산부터 뗍니다 |
| 3 | 상태 뭉치 분리 | 함께 바뀌는 프로퍼티를 한 타입으로 묶습니다 |
| 4 | 역할별 분리 | 데이터 소스·코디네이터·뷰 모델·자식 뷰 컨트롤러로 나눕니다 |
| 5 | 흡수 경로 차단 | 새 코드의 자리를 정하고 문턱을 둡니다 |
특성화 테스트는 코드가 옳은지가 아니라 지금 어떻게 동작하는지를 그대로 기록하는 테스트입니다. 마이클 페더스가 『레거시 코드 활용 전략』에서 정리한 기법이죠.
동작을 보존하며 옮기는 게 목표이니 현재 동작이 기준선이 됩니다.
2단계는 인스턴스 상태를 안 쓰는 로직이라 가장 쉽습니다. 이 단계에서 파일이 눈에 띄게 줄어드는 경우가 많습니다.
3단계에서 Swift는 enum이 유용합니다. 화면 상태 세 개가 늘 같이 바뀐다면 그건 상태 객체 하나입니다.
// 전: 유효하지 않은 조합이 표현 가능합니다
var isLoading = false
var items: [Item] = []
var error: Error?
// 후: 셋 중 하나만 존재합니다
enum ViewState {
case loading
case loaded([Item])
case failed(Error)
}
enum으로 묶으면 불가능한 조합 자체를 없앨 수 있습니다.
4단계의 iOS 경로는 정해져 있습니다. 테이블 데이터 소스는 별도 타입으로, 화면 전환은 코디네이터로, 화면 상태와 표시 규칙은 뷰 모델로, 큰 화면은 자식 뷰 컨트롤러로 나눕니다.
5단계가 빠지면 6개월 뒤에 원상 복구됩니다. 다음 기능이 다시 그 파일로 가지 않도록 새 코드의 자리를 명확히 하고 파일 길이 규칙이나 리뷰 합의로 문턱을 만들어 둡니다.
쪼개다 실패하는 두 가지 방식
| 실패 방식 | 무슨 일이 생기나 |
|---|---|
| 이름만 바꾸기 | AppManager를 UserManager·DataManager·NetworkManager 셋으로 나눴는데 셋이 서로를 전부 참조합니다. 갓 오브젝트가 갓 클러스터가 됐을 뿐입니다 |
| 너무 잘게 나누기 | 4,000줄 클래스를 40개 타입으로 쪼개면 이번엔 흐름이 사라집니다. 앞 편에서 본 라비올리 코드입니다 |
나눌 때는 의존 방향이 한쪽으로만 흐르는지 함께 봐야 합니다.
해체의 목표는 조각을 만드는 게 아니라 각 조각이 자기 이유로만 바뀌게 하는 겁니다. 조각 수는 그 결과일 뿐입니다.
정리하면
- 갓 오브젝트는 크기가 아니라 아는 범위로 판별합니다. 많이 알고 많이 알려져 있고 상태가 흩어져 있으면 해당합니다.
- 나쁜 설계라서가 아니라 매번 가장 싼 선택을 한 결과로 자랍니다.
- 가장 큰 피해는 테스트 불가입니다. 테스트가 없으니 못 쪼개고 못 쪼개니 계속 큽니다.
- Git 변경 빈도 상위 목록이 실측 지표로 쓸 만합니다.
- 해체는 특성화 테스트 → 무상태 로직 → 상태 뭉치 → 역할 분리 순서로 하고 마지막에 다시 커지는 경로를 막습니다.
다음 편은 갓 오브젝트를 쪼갠 다음에 만나는 문제입니다. 기능 하나 바꾸는데 파일 열두 개를 고쳐야 하는 상태, 산탄총 수술을 봅니다.
출처 및 확인 기준
- AntiPatterns — Wiley · 공식 데이터 · 확인 2026-08-17 · 근거: 1998년 AntiPatterns 출간 정보와 The Blob 맥락
- Human Perception on God Class Detection — Springer Nature · 논문 원문 · 확인 2026-08-17 · 근거: God Class 판별 기준의 사람·도구별 차이에 대한 통제 실험

![[코드 냄새 #4] 갓 오브젝트(God Object) 완벽 정리 대표 이미지](/assets/images/posts/c44a316d-1d8c-4de3-8558-2b5452bdbcfe/god-object-dependency-overload.jpg)