본래 이번 글은 코딩테스트 문제를 풀고 나의 풀이방법을 기술하려고 했다.
하지만, 예상보다 문제 하나를 푸는데 시간이 많이 걸리고 있는 상황이다.
프로그래머스 Lv.3 수준의 문제인데, 먼저 A4용지에 pseudo 코드와 도식도를 그린 다음,
최종적으로 해결방법을 도출하였다.
뿌듯한 마음과 함께 문제를 풀기 시작하였으나, 이 문제를 푼 사람들은 코드가 약 30줄인 것에 비해
점점 길어지는 내 코드를 보고 부담감을 느끼기 시작했다.
암튼!!
곧 다가오는 면접에서 기술면접도 포함되어 있다고 하니,
내가 잘 모르거나 개념이 모호한 것을 하나 적어보고자 한다.
바로.. DI(Dependency Injection), 한국말로 의존성 주입이다.
그래서, 의존성 주입이라는 게 뭐야?
의존이라는 단어는 우리에게 생소한 단어가 아니다.
"A가 B에 의존한다."와 같은 문장을 통해 A가 독립적이지 못하고 B에 의지, 혹은 종속되었다는 느낌을 가질 수 있다.
다만 의존성 주입이라는.. 다소 우리가 실생활에서 듣지 못하는 두 단어의 조합이,
CS세계에서만의 다른 버전의 뜻을 가지고 있다는 것을 유추하기에는 충분하다.
Class라는 개념에 대한 이해가 필요하다!
혹시, Class나 인스턴스에 대한 개념이 좀 생소하시다면, 그 부분에 대한 이해가 필요하다고 말씀드리고 싶다.
본 글에서 Class 개념을 설명하기에는 내용이 장대해져, 이부분은 알고계신다는 전제하에 설명을 해보고자 한다.
클라스라는 개념을 접한 지 얼마 안된 사람이라면,
클라스는 인스턴스를 생성하기 위한 일종의 틀 정도로 이해하는 경향이 강하다.
나 또한 그랬으니까..

아마도 이 익숙한 붕어빵 굽기 도식도를 많이들 보았을 것이다.
클라스는 위 그림에서는 붕어빵 틀, 딱 한가지이지만,
실제로 개발자는 다양한 종류의 클라스를 여러 개 생성할 수 있을 것이다.
예를 들자면, 계란빵 틀, 델리만튜 틀.. 등등
이러한 틀(클라스)는 개별로 독립적이고 서로 영향을 주지 않는 것처럼 보인다.
물론, 기존에 만들어 놓은 클라스에 별도의 작업을 하지 않으면 개별로 독립적이다.
하지만, 클라스 간에 서로 섞고 싶다면?
클라스 A에 있는 기능a를 클라스 B에서 사용하고 싶다면?
이는 실제로 가능하다.
그리고 이를 가능하게 하는 것을 의존성 생성이라 한다.
참고로, 의존성 생성과 의존성 주입은 다르다!
이 둘의 차이를 설명하고, 의존성 주입이 어떠한 장점이 있는지 설명을 하고자 한다.
붕어빵 틀로는 이를 설명하기에는 부족하니, 다른 예시를 추가로 들자면,
먼저 자동차라는 class가 존재하고, 디젤 엔진이라는 class 두 개가 존재한다고 가정해보자.
class DieselEngine:
def start(self):
print("달달달달...")
class Car:
def __init__(self):
self.engine = Engine()
def start(self):
self.engine.start()
car = Car()
car.start()
위의 코드에서는 Car라는 class에 __init__(self) 함수를 통해 DieselEngine이라는 class에 의존성을 직접 생성했다.
이를 통해서 Car 클라스를 통해 생성된 car라는 인스턴스에서는 DieselEngine이라는 클라스의 구동소리 함수를 불러올 수 있게 된다.
그러므로 car.start() 에서는 "달달달달..."이라는 문자가 출력되게 된다.
그러나, 문제가 있다.
만약 가솔린 엔진 class와 같이 추가 클라스를 만들면 어떻게 적용하지?
라는 문제다.
지금과 같은 위의 코드로는 DieselEngine만 출력이 가능한 상태이다.
다른 엔진, 즉, 다른 class에 대한 의존성을 넣으려면 Car 클라스의 스크립트를 일일이 고쳐야 한다.
위와 같은 코드를 의존성 생성이라고 하며, 제약이 많고 일일이 수정해야 한다는 단점이 있다.
그럼, 의존성 주입에 대해 알아보자.
먼저 스크립트를 의존성 주입 방식으로 변경을 하자면...
class DieselEngine:
def start(self):
print("달달달달...")
class GasolineEngine:
def start(self):
print("부릉부릉!")
class Car:
def __init__(self, engine: Engine):
self.engine = engine
def start(self):
self.engine.start()
gasolineEngine = GasolinEngine()
dieselEngine = DieselEngine()
gasolineCar = Car(gasolineEngine)
gasolineCar.start() // 부릉부릉!
dieselCar = Car(dieselEngine)
dieselCar.start() // 달달달달...
위와 같이 설명이 가능하다.
__init__(self, engine: Engine) 으로 하게 된다면,
인스턴스를 생성하기 위해 부른 Car라는 클라스 내에,
gasolineEngine인스턴스나 dieselEngine인스턴스 어느 것을 넣냐에 따라 그 성질이 바뀌게 된다.
한마디로, 의존성이 있는 class간에 일일이 수정을 할 필요도 없으며,
코드의 재활용도도 올라가게 된다!
따라서 dieselCar라는 인스턴스에서 start()함수를 부르게 되면,
DieselEngine의 함수인 달달달달... 이 불러오게 되며,
gasolineCar라는 인스턴스에서 start()함수를 불러오게 되면,
GasolineCar의 함수인 부릉부릉! 이 불러오게 된다.
제가 이해를 하는 데 큰 도움이 된 영상들입니다.
출처 1: https://www.youtube.com/watch?v=Jau0a0IvveY&t=492s
출처 2: https://youtu.be/GkBom4oqJag?si=CncMc5jhRRSwxoLf
출처 3: chat GPT
'TIL' 카테고리의 다른 글
| 세션(Session)과 토큰(Token)의 차이를 알아보자 (0) | 2024.08.01 |
|---|---|
| 프로세스와 스레드(thread)의 차이, 그리고 멀티 스레드란? (5) | 2024.07.24 |
| 기술 면접 예상 질문 대비: 동기와 비동기 통신 (0) | 2024.07.13 |
| DP(Dynamic Programing: 동적 계획법)에 대해 이해해보자 (0) | 2024.07.09 |
| "Index가 뭐에요?" 라는 질문에 깨진 기념으로 (0) | 2024.07.06 |