Web
스프링 설명회 - Dependency
2024-11-05

의존 Dependency
어떠한 대상에 기대고 지지하게 되는 증상, 어떠한 일을 자신의 힘으로 하지 못하고 다른 것의 도움을 받아 의지함
마마보이는 스스로 일을 처리하거나 의사결정을 하지 못하고 어머님께 의존하는 남성을 지칭합니다. 하루 종일 스마트폰을 붙잡고 사는 사람들은 스마트폰에 과의존한다고 합니다. 남자친구에게 과하게 의지하고 잠시도 떨어지지 못하는 의존적인 여자친구가 있을 수도 있습니다. 우리는 이렇게 일상생활에서 의존이란 단어를 많이 사용하곤 합니다.
의존의 특성
의존에는 몇가지 일반적인 특성이 있는 것 같습니다.
1. 알지 못한다면, 의존성이 있을 수 없다.
부정문이 두 개가 들어있어 혼란스러운 문장입니다. 어떠한 대상을 모르는 상태에서는 의존을 할 수도 없다는, 너무나도 당연한 특성입니다. 스마트폰을 살면서 한 번도 본적 없고 뭔지도 모르는 사람은 스마트폰에 의존을 할 수가 없을 것입니다.
2. 어떤 대상에 의존한다면, 대상으로부터 영향을 받는다.
스마트폰에 과의존적인 사람은 스마트폰 배터리가 다 떨어진다면 큰 불편함을 느낄 것입니다. 이는 스마트폰이 고장나거나, 큰 업데이트가 생겨 기능이 많이 바뀌어도 마찬가지입니다. 어떤 대상에 의존한다면 그 대상의 변경에 대한 영향을 받게됩니다.
3. 의존에는 방향성이 있다.
남자친구에게 의존하는 여자친구가 있습니다. 이 때 남자친구 역시 여자친구에게 의존하리라는 법은 없습니다. 이 경우 여자친구는 남자친구의 변심에 영향을 받겠지만, 남자친구는 여자친구가 떠나가도 아무렇지 않을지도 모릅니다. 이렇게 의존성에는 방향이 존재합니다.
4. 대상과의 상호작용이 많을수록, 의존도는 커진다.
10년 넘게 Java만을 사용한 개발자가 있습니다. 이 개발자는 이미 이 언어와 너무 많은 시간을 보내어 Java 전문가가 되었습니다. 이렇게 대상과 많은 시간을 상호작용을 한 개발자는 이제 Java가 아닌 언어로 개발할 일이 생기면 기존의 퍼포먼스도 잘 나오지 않고, 본인도 흥미를 이어나가기 어려울지도 모릅니다. 이렇게 대상에 대한 많은 상호작용은 의존도를 높이고, 다른 대상으로의 변경을 어렵게 만듧니다.
Dependency in software
소프트웨어 세계에서도 의존은 존재합니다. 소프트웨어 세계에서의 코드들은 홀로있는 섬처럼 존재하지 않습니다. 소프트웨어의 개체들은 각자의 역할과 책임을 가지며 서로 협업함으로써 프로그램은 동작합니다.
의존성의 실체
소프트웨어 의존성은 소프트웨어 개체 간 관계에 의해서 형성이 됩니다. 여기서 소프트웨어 개체란 단순한 코드의 묶음인 파일부터 함수, 클래스, 모듈, 서브시스템, 시스템 등이 될 수 있습니다. 이러한 소프트웨어 개체들이 동작을 위해 다른 개체를 사용하고자 할 때 두 개체 간 관계가 형성되며 의존성이 발생합니다. 여기서 사용이란 단순히 다른 개체를 호출하는 것을 넘어 개체를 가지고 있거나, 생성하거나, 상속하는 것도 포함됩니다.
이렇게 만들어진 의존관계는 현실세계의 특성을 그대로 따릅니다. A 개체가 B 개체를 사용함으로써 의존하는 순간, A는 B를 알게되고, B의 변경에 A는 영향을 받게 됩니다. B의 스펙이나 로직의 변경이 A의 코드에도 영향을 미친다는 것입니다. B의 public API가 변경된다면 A는 컴파일 에러가 발생할 수 있으며, B의 로직이 변경된다면 자연스럽게 A의 동작도 변경될 수 있습니다.
의존성과 UML Diagram
UML이란 소프트웨어 시스템을 다이어그램으로 표현하는 표준 방법입니다. UML에서는 소프트웨어 개체 간 관계를 표현하는 6가지 방법을 제공합니다. 이 방법들을 통해 의존성이 발생하는 개체 간 관계를 더 구체적으로 표현할 수 있습니다.

직선(solid line)과 삼각형 화살표로 표현되는 Inheritance는 상속관계를 나타내며, 화살표 방향이 부모 클래스를 가리킵니다. 부모 클래스를 상속한 자식 클래스는 부모의 상태와 행동을 모두 물려받습니다. 이를 통해 부모가 변경되면 자식도 함께 변경되는 의존관계가 형성됩니다.
점선(dashed-line)과 삼각형 화살표로 표현되는 Realization은 인터페이스와 클래스의 관계를 나타내며, 화살표는 인터페이스를 가리킵니다.
직선과 직선 화살표로 표현되는 Association은 주로 하나의 클래스에서 다른 클래스를 필드 레벨로 가지고 있으면서 협동하는 연관관계를 표현합니다. A 클래스의 필드에 B가 존재한다면 A가 B에 의존하는 상황으로 화살표는 B 측을 가리킵니다.
점선과 직선 화살표로 표현되는 Dependency는 그 연관관계가 파라미터, 로컬 변수, 반환 타입 등 일시적으로 발생하는 관계성을 표현하는 방법입니다. 여기서는 위에서 계속 언급한 의존성 이론에서의 Dependency가 아닌 하나의 개체간 관계를 표현하는 방법으로써의 용어로써 인식해주시면 좋을 것 같습니다.
Aggregation과 Composition은 둘 다 전체와 부분 관계를 갖는 개체 관계를 표현하는 방법입니다. Aggregation은 부분이 전체에 종속되지 않고 독립적으로 사용될 수 있을 때에 사용하고, Composition은 전체가 소멸되면 부분도 함께 소멸해야하는 더 강한 결합관계에서 사용합니다. 두 경우 모두 마름모는 전체 측을 가리켜야합니다.

위 다이어그램은 클래스와 그들간의 의존 관계를 보여줍니다. Person은 Address를 가지고 있으며 이와 협업하여 동작합니다. Person은 Association이라는 관계 형성을 통해 Address에 의존합니다. Person은 추상 클래스이기에, Student와 Processor와 같은 클래스는 Person을 상속하고 있습니다. Student와 Professor는 상속이라는 관계를 통해 Person에 의존합니다.
//Address.java
class Address {
...
}//Person.java
abstract class Person {
private Address addess;
...
}//Student.java
class Student extends Person { ... }이 때 유의미하게 볼 점은 A가 B에게 의존할 때에 A의 코드에서는 의존 대상이 나타나지만, B에게서는 나타나지 않는다는 점입니다. Student.java에는 Student의 클래스 선언 부분에서 Person을 상속한다는 코드가 존재하지만, Person 측에서는 Student와 관련된 어떠한 내용도 찾을 수 없습니다. 마찬가지로 Person의 코드에는 Address가 나타나지만, Address의 코드에는 Person이 나타나지 않습니다.
의존성의 전이와 변경의 전파
결국 어떠한 개체가 다른 개체를 '알고 있고', 그로 인해 의존 관계가 형성되었다면, 개체는 의존하는 상대방에 의해 영향을 받습니다. Person의 이름이 바뀌면, Student의 선언에 있는 Person이라는 이름도 바꾸어야 합니다. 또한 Person의 기능이 바뀌면, Student는 자연스럽게 변경된 기능을 따르게 됩니다.
abstract class AbstractPerson { ... } //rename to AbstractPerson
class Student extends Person { ... } //compile error!!이러한 변경의 전파는 인접하게 관계를 맺은 개체에만 적용되는 내용은 아닙니다. 의존성은 전이됩니다. 예를 들어 Address의 변경은 Person의 변경을 낳게 되고, 이는 다시 Student와 Professor에게 영향을 줄 수 있습니다.
의존성이 높은 프로그램의 특징
하나의 코드를 변경했더니 다른 코드들이 연이어 수정되어야하고, 이 코드들을 변경했더니 또 다른 부분을 수정해야하는 상황이 반복되는 경험을 다들 해보셨을 것 같습니다. 이렇게 유지보수를 어렵게 만드는 상황은 의존성에 의해 발생합니다. 위 예시에서 Address를 변경했더니 그 변경의 전파가 Person을 넘어 Student와 Professor까지 발생하는 것을 예시로 들 수 있겠습니다.
결국 의존성이 높다는 것, 즉 서로 다른 개체들의 의존관계가 얽히고 섥혀있는 상태는 코드를 변경하기 어렵게 만들고, 한 군데의 변경이 다른 곳에 영향을 미쳐서 쉽게 깨지게 만들고, 모듈이 추출되기 어렵게 만들고, 계층 관계를 흐릿하게 만듧니다. 따라서 의존관계를 적절히 제어하는 것은 개발자들이 수행해야할 중요한 임무 중 하나입니다.
마무리
개체 간 의존성의 정도를 결합도 Coupling 라고 표현하기도 합니다. 개체가 서로 많이 알고 있으면서 서로 끈끈하게 엮여있는 경우 두 개체는 결합도가 높습니다. 결국 우리의 프로그램 구성요소들이 낮은 결합도를 갖도록 설계한다면 시스템의 유지보수성을 높이고 행복한 프로그래밍을 할 수 있습니다. 다음 글에서는 느슨한 결합을 만드는 객체지향 설계 원칙인 의존성 역전 원칙에 대해 알아보겠습니다.