앱 화면 테마를 라이트에서 다크로 통째로 바꿔본 적 있으신가요?
버튼 하나, 라벨 하나 색을 일일이 갈아끼우다 보면 어딘가 하나는 꼭 놓칩니다. 버튼은 검은 배경인데 라벨만 흰 배경으로 남는 식의 문제가 생기기 쉬워요.
이럴 때 딱 맞는 게 Swift 추상 팩토리 패턴입니다. 서로 관련된 객체들을 하나의 세트로 묶어두고, 세트째 갈아끼우는 방법이에요.
이 글에서는 추상 팩토리 패턴이 정확히 뭘 해결하는지, Swift 코드로 어떻게 관련 객체를 통째로 바꾸는지, 그리고 언제 쓰고 언제 안 쓰는 게 좋은지까지 정리해드릴게요.
추상 팩토리 패턴이 뭔가요?
한 문장으로 답하면 이렇습니다.
서로 어울려야 하는 객체들을 한 묶음으로 만들어주는 공장을, 상황에 따라 통째로 바꿔 끼우는 패턴입니다.
핵심은 “함께 쓰여야 하는 객체들”이에요. 다크 테마의 버튼과 라이트 테마의 배경이 섞이면 곤란하잖아요.
추상 팩토리는 이런 조합 사고를 막아줍니다.
공장(팩토리)을 하나 고르면, 그 공장이 만들어내는 부품들은 서로 자동으로 어울립니다. 라이트 공장을 고르면 라이트 버튼, 라이트 라벨, 라이트 배경이 나오는 식이죠.
부품을 개별로 고르는 게 아니라 공장을 통째로 고르는 것.
이게 추상 팩토리의 전부입니다.
Swift에서 관련 객체를 통째로 갈아끼우는 법
실제 코드로 보면 훨씬 명확합니다. UI 테마를 예로 들어볼게요.
먼저 만들어질 부품들의 규격(프로토콜)과, 그 부품들을 찍어내는 공장의 규격을 정합니다.
protocol Button { func render() -> String }
protocol Label { func render() -> String }
// 공장은 어울리는 부품 세트를 통째로 책임진다
protocol ThemeFactory {
func makeButton() -> Button
func makeLabel() -> Label
}
이제 라이트 테마 공장과 다크 테마 공장을 각각 만듭니다. 공장마다 자기 테마에 맞는 부품만 찍어내요.
struct LightFactory: ThemeFactory {
func makeButton() -> Button { LightButton() }
func makeLabel() -> Label { LightLabel() }
}
struct DarkFactory: ThemeFactory {
func makeButton() -> Button { DarkButton() }
func makeLabel() -> Label { DarkLabel() }
}
화면을 그리는 쪽은 공장 하나만 넘겨받으면 끝입니다. 어떤 공장인지는 신경 쓰지 않아요.
func buildScreen(with factory: ThemeFactory) {
let button = factory.makeButton()
let label = factory.makeLabel()
print(button.render(), label.render())
}
// 테마 교체는 공장 한 줄만 바꾸면 된다
buildScreen(with: DarkFactory())
LightFactory()를 DarkFactory()로 바꾸는 순간, 버튼과 라벨이 한꺼번에 다크로 넘어갑니다.
버튼 색 따로, 라벨 색 따로 고칠 일이 사라지는 거죠.
팩토리 메서드랑 뭐가 다른가요?
이름이 비슷해서 헷갈리기 쉬운 부분이라 짚고 갈게요.
| 구분 | 팩토리 메서드 | 추상 팩토리 |
|---|---|---|
| 만드는 대상 | 객체 한 종류 | 관련 객체 여러 종류(세트) |
| 초점 | “무엇을 만들까” | “어울리는 것끼리 묶어서” |
| 대표 예 | 버튼 하나 생성 | 버튼+라벨+배경 한 벌 |
간단히 말하면 팩토리 메서드는 부품 하나를 만드는 방법이고, 추상 팩토리는 그 부품들을 한 벌로 묶어주는 방법입니다.
추상 팩토리 안에서 팩토리 메서드가 여러 개 쓰인다고 보셔도 무방해요.
언제 쓰고 언제 피하는 게 좋을까
직접 써보니 잘 맞는 상황과 오히려 과한 상황이 갈리더라고요.
이럴 때 좋아요
- 테마·플랫폼·국가처럼 “세트 단위”로 바뀌는 게 명확할 때
- iOS와 macOS처럼 UI 부품군을 통째로 교체해야 할 때
- 결제 수단, 데이터베이스 드라이버처럼 한 벌씩 갈아끼우는 구조일 때
이럴 땐 과해요
- 만들 객체가 한두 종류뿐일 때
- 조합 규칙이 자주 바뀌어서 세트 자체가 불안정할 때
객체가 하나뿐인데 추상 팩토리를 세우면, 오히려 코드만 늘고 읽기 어려워집니다.
세트로 묶을 이유가 뚜렷할 때 꺼내 쓰는 게 좋아요.
자주 묻는 질문 두 가지
Q. 부품 종류를 나중에 추가하려면요?
프로토콜에 메서드를 추가하고 모든 공장에 그 메서드를 구현해줘야 합니다. 공장이 많으면 손이 많이 가는 부분이라 부품 종류가 자주 늘어나는 프로젝트라면 미리 고민해두는 게 좋아요.
Q. SwiftUI에서도 쓰나요?
물론 쓸 수 있지만 SwiftUI는 Environment나 @Observable 같은 자체 주입 방식이 잘 갖춰져 있어서 테마 정도는 그쪽이 더 간결할 때가 많습니다. UIKit 기반이거나 순수 로직 계층에서 세트 교체가 필요할 때 추상 팩토리가 더 빛을 발해요.
추상 팩토리는 결국 “따로 놀면 안 되는 것들을 한 손잡이로 묶는” 패턴입니다.
테마 교체 한 줄로 화면 전체가 착 맞아떨어지는 경험을 한 번 해보시면, 왜 이 패턴을 쓰는지 바로 와닿으실 거예요. 작은 예제부터 직접 손으로 옮겨보시길 추천드립니다.
