iOS & Swift

생성자 주입 vs 프로퍼티 주입 vs 메서드 주입, 뭘 써야 할까 (Swift DI 총정리)

Swift로 iOS 앱을 만들다 보면 꼭 마주치는 고민이 있어요.

이석우iOS Developer4분 읽기
생성자 주입 vs 프로퍼티 주입 vs 메서드 주입, 뭘 써야 할까 (Swift DI 총정리) 대표 이미지

Swift로 iOS 앱을 만들다 보면 꼭 마주치는 고민이 있어요.

“의존성 주입, 대체 어떤 방식으로 해야 맞는 거지?”

결론부터 말씀드릴게요.

특별한 이유가 없다면 생성자(init) 주입을 쓰는 게 정답에 가장 가깝습니다.

왜 그런지, 그리고 나머지 두 방식은 언제 쓰는지를 실제 경험 위주로 풀어볼게요.

생성자·프로퍼티·메서드 주입, 첫 화면부터 차이가 보이죠
생성자·프로퍼티·메서드 주입, 첫 화면부터 차이가 보이죠

세 가지 주입 방식, 핵심만 먼저 정리

헷갈리기 전에 뼈대부터 잡고 가겠습니다. Swift에서 의존성을 넣는 방법은 크게 세 가지예요.

  1. 생성자(init) 주입init 파라미터로 의존성을 받음
  2. 프로퍼티 주입var 프로퍼티에 나중에 할당
  3. 메서드 주입 — 별도 메서드로 주입

먼저 가장 권장되는 생성자 주입 코드입니다.

final class OrderService {
    private let repo: MemberRepository // let 으로 불변 보장

    init(repo: MemberRepository) { // 생성자(init) 주입
        self.repo = repo
    }
}

별도 라이브러리 없이 순수 Swift 문법만으로 되니까 코드가 깔끔합니다.

반면 프로퍼티 주입은 이렇게 짧죠.

final class OrderService {
    var repo: MemberRepository! // 한 줄이라 편해 보이지만…
}

딱 봐도 프로퍼티 주입이 간편해 보여요. 그런데 이 편함이 나중에 발목을 잡습니다.


프로퍼티 주입은 왜 쓰지 말라고 할까?

제가 실제로 겪은 일부터 얘기해 볼게요.

프로퍼티 주입은 테스트할 때 정말 불편합니다. 객체를 만든 뒤 주입을 깜빡하면 옵셔널 강제 해제 크래시가 나고 타입만 봐서는 뭘 넣어야 완성되는지 알 길이 없거든요.

생성자 주입이라면 OrderService(repo: mockRepo)처럼 순수 Swift 코드로 목 객체를 바로 넣을 수 있어요.

두 번째 문제는 let 키워드를 못 쓴다는 점입니다.

생성자 주입은 프로퍼티를 let으로 선언할 수 있어서, 한 번 주입되면 바뀌지 않는 불변 객체가 됩니다.

반대로 프로퍼티 주입은 var라 값을 언제든 바꿀 수 있어서 실수의 여지가 생겨요.

세 번째는 순환 참조 문제입니다.

A가 B를, B가 A를 서로 참조하면 프로퍼티 주입은 앱이 실행될 때까지 이걸 몰라요. 해당 화면에 진입해서야 터집니다.

생성자 주입은 객체를 만드는 시점에 바로 문제가 드러나서(설계가 꼬였다면 아예 컴파일이 안 돼서) 문제를 훨씬 빨리 잡을 수 있습니다.

순환 참조, 터지는 타이밍부터가 다릅니다
순환 참조, 터지는 타이밍부터가 다릅니다
필드 주입만 쓰던 시절엔 테스트 짤 때마다 애먹었어요
필드 주입만 쓰던 시절엔 테스트 짤 때마다 애먹었어요

한눈에 보는 비교표

말로 풀면 헷갈리니 표로 정리했습니다. (2026년 기준 Swift 커뮤니티 권장 방향)

구분 생성자(init) 주입 프로퍼티 주입 메서드 주입
불변성(let) 가능 ⭕ 불가 ❌ 불가 ❌
테스트 편의성 높음 낮음 보통
순환 참조 감지 생성 시점 즉시 늦게 발견 늦게 발견
필수/선택 의존성 필수에 적합 구분 애매 선택에 적합
코드 간결함 보통 매우 간결 보통

표만 봐도 흐름이 보이시죠?

생성자 주입이 대부분의 항목에서 앞섭니다. 그래서 Swinject나 Factory 같은 DI 라이브러리 문서들도 생성자 주입을 기본으로 권장하고 있어요.


그럼 나머지 두 방식은 언제 쓰나요?

무조건 생성자 주입만 쓰라는 얘기는 아닙니다. 각자 어울리는 자리가 있어요.

메서드 주입은 선택적 의존성에 잘 맞습니다.

주입할 대상이 없어도 되는 경우, 즉 있으면 쓰고 없어도 괜찮은 의존성에 configure(with:) 같은 메서드로 유연하게 넣을 수 있어요.

프로퍼티 주입은 그래도 쓸 자리가 있다면, 스토리보드로 만든 뷰컨트롤러처럼 초기화 시점을 우리가 직접 제어할 수 없는 곳 정도입니다.

일반 애플리케이션 코드에서는 되도록 피하는 게 좋아요.

정리하면 이렇습니다.

  • 필수 의존성 → 생성자 주입
  • 선택 의존성 → 메서드 주입
  • 일반 코드에서 프로퍼티 주입 → 지양

자주 묻는 질문

Q. Factory 같은 DI 라이브러리를 쓰면 생성자 주입이 더 편해지나요?

네. Swinject나 Factory에 의존성 만드는 법을 등록해 두면, 생성자에 넘길 객체를 컨테이너가 대신 만들어 줍니다. 코드가 짧아지면서 let 불변성 같은 장점은 그대로 챙길 수 있어요.

Q. 생성자에 파라미터가 너무 많아지면요?

그건 주입 방식의 문제가 아니라 그 클래스가 너무 많은 일을 하고 있다는 신호예요. 책임을 나눠 클래스를 분리하는 걸 먼저 고민해 보세요.

Q. 작은 프로젝트에도 DI 라이브러리가 꼭 필요한가요?

아니요. 화면 몇 개 규모라면 init으로 직접 넘기는 순수 생성자 주입만으로 충분합니다.

고민될 땐 이 순서대로 골라보시면 편합니다
고민될 땐 이 순서대로 골라보시면 편합니다

처음엔 프로퍼티 주입의 간편함에 끌리기 쉽지만, 테스트를 짜고 협업을 하다 보면 생성자 주입의 장점이 왜 그렇게 강조되는지 몸으로 체감하게 됩니다.

고민되신다면 일단 생성자 주입으로 시작해 보세요. 나중에 코드가 커질수록 고마워질 선택입니다.