iOS 개발을 하다 보면 델리게이트 패턴은 정말 지겹도록 만나게 됩니다.
UITableView, UITextField 만질 때마다 delegate를 붙이면서 이런 생각 한 번쯤 해보셨을 거예요. “이게 왜 델리게이트지? 그냥 클로저나 노티피케이션 쓰면 안 되나?”
특히 옵저버 패턴까지 배우고 나면 오히려 더 헷갈리기 쉽습니다. 둘 다 “무슨 일이 생기면 알려주는” 구조인데, 대체 뭐가 다른 건지 말이죠.
결론부터 말씀드릴게요.
델리게이트는 한 객체가 딱 한 명에게 “이건 너한테 맡길게”라고 위임하는 1:1 통신이고,
옵저버는 하나의 사건을 여러 명이 동시에 구독하는 1:N 방송입니다.
이 한 줄만 잡고 가셔도 절반은 이해하신 거예요. 오늘은 이 차이를 코드와 함께 확실하게 정리해 드리겠습니다.
델리게이트 패턴이 뭔가요?
델리게이트(delegate)는 우리말로 “위임”이에요.
내가 직접 처리하기 애매한 일을 믿을 만한 다른 객체 하나에게 “이건 네가 대신 결정해줘”라고 넘기는 겁니다.
실생활로 비유하면 이래요.
제가 식당을 예약하려는데 직접 전화하기 귀찮아서 비서 한 명에게 부탁합니다. “예약 좀 대신 해줘.”
여기서 비서가 바로 델리게이트예요. 부탁하는 사람은 한 명, 부탁받는 사람도 한 명. 딱 1:1이죠.
Swift에서는 이걸 프로토콜로 약속합니다.
// 위임할 일을 프로토콜로 정의
protocol OrderDelegate: AnyObject {
func didFinishOrder(_ menu: String)
}
class Restaurant {
weak var delegate: OrderDelegate? // 딱 한 명만 지정
func order(_ menu: String) {
delegate?.didFinishOrder(menu) // 그 한 명에게만 통보
}
}
여기서 눈여겨보실 부분은 delegate가 배열이 아니라 단 하나의 프로퍼티라는 점이에요.
한 명에게만 맡기니까 당연히 하나만 담깁니다. 이게 델리게이트가 1:1인 이유예요.
그리고 weak가 붙어 있죠. 서로 강하게 참조하면 순환 참조(retain cycle)로 메모리가 새기 때문에, 델리게이트는 관례적으로 약한 참조로 잡습니다.
옵저버 패턴과 뭐가 다를까?
옵저버(observer)는 “관찰자”예요.
어떤 사건이 터지면, 그걸 지켜보던 여러 명에게 한꺼번에 알림이 갑니다.
아까 비서 비유를 이어가 볼게요.
이번엔 제가 식당 오픈 소식을 동네에 방송으로 뿌립니다. “오늘 저녁 오픈합니다!”
이 방송을 듣는 사람은 열 명일 수도, 백 명일 수도 있어요. 저는 누가 듣는지 신경 쓰지 않고 그냥 뿌리기만 합니다.
이게 1:N 방송, 옵저버 패턴이에요.
iOS에서는 NotificationCenter가 대표적이죠.
// 사건 발생 → 구독한 모두에게 방송
NotificationCenter.default.post(name: .storeOpen, object: nil)
// 관심 있는 객체들이 각자 구독
NotificationCenter.default.addObserver(
self, selector: #selector(handleOpen),
name: .storeOpen, object: nil)
보내는 쪽은 받는 대상이 누군지 몰라요. 그냥 이름표(name)를 붙여 던질 뿐입니다.
받는 쪽도 여럿이 각자 알아서 구독하고요.
서로를 몰라도 되니 결합도가 낮은 대신, 지금 누가 이 알림을 듣고 있는지 추적하기가 어렵다는 단점이 있어요.
한눈에 보는 델리게이트 vs 옵저버
말로만 풀면 헷갈리니 표로 묶어봤어요.
| 구분 | 델리게이트 | 옵저버 |
|---|---|---|
| 통신 방식 | 1:1 (한 명에게 위임) | 1:N (여러 명에게 방송) |
| 상대를 아는가 | 서로 명확히 앎 | 서로 몰라도 됨 |
| 대표 예시 | UITableViewDelegate | NotificationCenter |
| 응답(리턴) | 받기 쉬움 | 받기 어려움 |
| 결합도 | 상대적으로 높음 | 낮음 |
| 추적/디버깅 | 쉬움 | 어려운 편 |
핵심은 “응답을 돌려받아야 하느냐”예요.
델리게이트는 “이 셀 높이를 얼마로 할까?” 물으면 상대가 값을 돌려줍니다. 대화가 오가는 구조죠.
옵저버는 그냥 던지고 끝이에요. 누가 어떻게 반응하는지 보낸 쪽은 관심이 없습니다.
그래서 언제 뭘 써야 하나요?
제가 직접 프로젝트에 써보며 세운 기준은 이렇습니다.
델리게이트를 쓸 때
- 딱 한 객체와만 소통하면 될 때
- 상대에게 값을 돌려받아야 할 때 (예: 셀 개수, 높이)
- 화면 전환처럼 순서와 흐름이 명확할 때
옵저버를 쓸 때
- 하나의 사건을 여러 화면이 동시에 반응해야 할 때
- 로그인 상태 변경, 다크모드 전환처럼 앱 전역 이벤트일 때
- 보내는 쪽과 받는 쪽이 서로 몰라도 될 때
예를 들어 로그인에 성공했을 때 홈 화면, 마이페이지, 상단 배너가 동시에 바뀌어야 한다면 델리게이트로는 벅찹니다.
한 명에게만 위임하는 구조라 세 곳에 다 연결하려면 코드가 지저분해지거든요.
이럴 땐 옵저버로 “로그인 됐어!” 한 번 방송하는 게 훨씬 깔끔합니다.
반대로 “입력창에서 확인 버튼을 눌렀다” 같은 1:1 상황은 델리게이트나 클로저가 정답이에요.
마무리하며
둘이 비슷해 보여도 “몇 명에게 알릴 것인가”만 떠올리면 선택이 쉬워집니다.
한 명이면 델리게이트, 여러 명이면 옵저버. 이 기준 하나만 챙겨 가셔도 충분합니다.
오늘 배운 걸 다음에 코드 짜실 때 한 번 적용해 보세요. 머리로만 알던 게 손에 붙는 순간, 패턴이 확 쉬워질 거예요. 응원하겠습니다!
