오늘은 의존성 주입(DI; Dependency Injection)의 개념과
이를 활용한 코드 예제를 통해 결합도를 낮추고, 유지보수성을 높이는 방법을 알아봅시다.
1. 의존성
우리가 어떤 클래스를 만들고 나면 실제로 사용할땐 아래의 예시코드처럼 인스턴스를 생성해서 씁니다.
class InkjetPrinter {
func printDocument(content: String) {
print("Inkjet Printer: \(content)")
}
}
class LaserPrinter {
func printDocument(content: String) {
print("Laser Printer: \(content)")
}
}
class Document {
private var printer: InkjetPrinter? // 결합도가 높음
init() {
self.printer = InkjetPrinter() // 직접 인스턴스 생성
}
func print(content: String) {
printer?.printDocument(content: content)
}
}
// 사용 예시
let document = Document()
document.print(content: "Hello, World!")
위처럼 사용하게 되면 InkjetPrinter에 변동이 생길 경우 Document 클래스에선 매번 수정해줘야 하겠죠?
이런 경우를 결합도가 높다
라고 합니다.
위 예시에 따른 문제점은 다음과 같습니다.
Document 클래스 내부에서 직접 InkjetPrinter를 생성해주고 있죠? 이렇게 되면 InkjetPrinter에 직접적으로 결합이 생깁니다. 만약에 LaserPrinter로 바꾸고싶으면 Document 클래스를 직접 수정해줘야 합니다.
- 이 때문에 유지보수가 어려워지고,
- 테스트를 위해 Mock을 넣으려고 해도 직접 넣어줘야 합니다.
하지만 이를 해결할 수 있는 방법이 있겠죠?
2. 의존성 주입(Dependency Injection)
일단은 의존성 주입에 대해서 설명부터 해드리자면
의존성 주입이란 의존성을 외부에서 주입해
(특정 클래스에 의존하지 않고 공통의 인터페이스 또는 프로토콜에 의존합니다.)
객체 스스로가 의존성을 만들지 않게 되어 결합도를 낮추는 방법입니다.
어렵게 느껴질 수 있어요. 일단 의존성 주입 예시코드부터 보시죠.
protocol Printer {
func printDocument(content: String)
}
class InkjetPrinter: Printer {
func printDocument(content: String) {
print("Inkjet Printer: \(content)")
}
}
class LaserPrinter: Printer {
func printDocument(content: String) {
print("Laser Printer: \(content)")
}
}
class Document {
private let printer: Printer
// 의존성 주입: 생성자에서 Printer를 주입받음
init(printer: Printer) {
self.printer = printer
}
func print(content: String) {
printer.printDocument(content: content)
}
}
// 사용 예시
let inkjetPrinter = InkjetPrinter()
let laserPrinter = LaserPrinter()
let documentUsingInkjet = Document(printer: inkjetPrinter)
documentUsingInkjet.print(content: "Hello, World with Inkjet!")
let documentUsingLaser = Document(printer: laserPrinter)
documentUsingLaser.print(content: "Hello, World with Laser!")
이걸 보시고 느낌이 빡 오실수도 있어요.
맞습니다. 의존성 주입은 다형성을 활용한 방법입니다.
만일 다형성을 모르신다면 이 글부터 보고 오시면 될 것 같습니다.
예제를 설명해드리면 처음 예시와 달라진 부분은
Document 클래스 내부에서 Printer 프로토콜을 채택한 객체라면
어떤 객체던 받을 수 있도록 만들어져있습니다.
때문에 Document 객체를 생성할 때
inkjetPrinter와 laserPrinter 객체를 Document 클래스 외부에서 생성해서 주입해줍니다.
맨처음에 말했듯이
Document 클래스는 Inkjet과 Laser 프린터 클래스에 의존 X
Printer 프로토콜(다른 언어에서는 인터페이스가 되겠죠?)에 의존.
-> 기능을 확장시 Printer 프로토콜을 채택한 클래스에서만 메서드를 구현해주면
Document 클래스에서는 Printer 프로토콜에 맞춰 기능을 확장할 수 있어 유연성이 늘어남.
-> 나중에 테스트를 위한 코드를 작성할 때도 이미 프로토콜이 구현되어있기 때문에 테스트 코드도 작성하기 편하고, 해당 프로토콜을 채택한 Mock 객체도 주입해줄 수 있어 쉽게 테스트할 수 있습니다.
3. 의존성 주입의 장점 정리
- 코드를 유연하게 한다.
- 클래스간 결합도가 낮아진다.
- 코드의 재사용성이 높아져 유지보수가 용이해진다.
- 테스트하기 더 쉬워진다.
다음에는 DIP에 대해서 설명해드리도록 하겠습니다.
적다보니 생각보다 알려드려야 할 내용이 많네요ㅎㅎ...
'공부 > CS' 카테고리의 다른 글
[CS]Swift로 다형성 이해하기 (0) | 2025.02.21 |
---|---|
[CS] 비즈니스 로직과 서비스 로직 (0) | 2025.01.30 |
[CS] OOP(객체 지향 프로그래밍)(feat. Swift) (0) | 2024.11.25 |
[CS] final, static, class(feat.Swift) (1) | 2024.10.19 |
[CS] 인스턴스화란 무엇일까?(클래스, 객체, 인스턴스) (1) | 2024.01.02 |