[DI] 의존성 관리하기 (1)

728x90
반응형

의존성 관리하기

의존성으로 인해 발생할 수 있는 문제점들은 실제로 문제점들이 드러난 후에는 되돌릴기가 어려울 수도 있다. 
시작단계에서 의존성을 관리하고,  지속적으로 관심을 두어 문제가 발생하지 않도록 해야 한다.

 

 

구현과 인터페이스의 비교

인터페이스를 기반으로 하는 프로그래밍에 익숙하지 않은 개발자는 인터페이스의 이면을 이해하는데 어려움을 격는다.
컴파일 시에는 인터페이스의 클라이언트는 해당 인터페이스에 대한 어떤 구현체가 사용되고 있는지에 대해 전혀 알 필요가 없다. 

 

 

new 키워드의 코드 스멜

인터페이스는 어떤 일을 수행할 수 있는지를 서술하며,  클래스는 어떻게 특정 작업을 수행할 것인지를 서술 한다.
실제 구현에 대한 상세 내용은 오직 클래스만이 알고 있다.
즉, 인터페이스는 해당 작업이 어떻게 수행되는지에 대해서는 철저히 무관심 해야 한다.
A는 B이다 라는 관점에서 볼때 new 키워드를 사용한다는 것은 코드스멜(code smell)로 취급할 수 있다.

 

코드스멜 : 어떤 코드가 잠재적으로 (potentially) 문제가 있을 수 있음을 표현하는 단어이다.

 

new 키워드를 이용해 객체의 인스턴스를 직접 생성하는 코드 스멜 예

public class AccountController
{
    private readonly SecurityService securityService;
    public AccountController()
    {
        this.securityService = new SecurityService();
    }

    [HttpPost]
    public void ChangePassword(string userID, string newPassword)
    {
        var userRepository = new UserRepository();
        var user = userRepository.GetByID(userID);
        this.securityService.ChangeUserPassword(user, newPassword);
    }
}

이 코드에는 여러 문제가 내포되어 있으며, 두번의 new 키워드 사용은 다음 문제점들의 원인이 된다.

 

  • AccountController 클래스는 SecurityService 클래스와 UserRepository 클래스의 구현에 영원히 의존적이 된다.
  • SecurityService 클래스와 UserRepository 클래스가 가지고 있는 의존성은 AccountController 클래스의 잠재적 의존성이 된다.
  • AccountController는 단위 테스트를 하기가 어려워졌다. 평범한 방법으로는 의존하고 있는 두 클래스의 모의 객체(mocking object)를 만들 수 없기 때문이다.
  • SecurityService.ChangeUserPassword 메서드는 클라이언트가 User 객체를 로드할 수 밖에 없도록 만든다.

 

SecurityService 클래스의 구현을 변경할 수 있는 방법은 두가지있다.
1. AccountController가 새롭게 구현한 클래스를 사용 할 수 있도록 수정하는 것.
2. 기존의 SecurityService 클래스에 새로운 기능을 추가하는 것.

 

 

테스트 가능성의 부재

테스트 가능성은 매우 중요한 항목이며, 이를 위해서는 코드 자체가 특정한 방식을 통해 디자인되어야 한다.
AccountController 클래스와 SecurityService 클래스는 쉽게 테스트하기가 어렵다.
그 이유는 두 클래스가 가지고 있는 의존성을 실제로는 아무런 기능도 수행하지 않는 모의객체로 교체할 수가 없기 때문이다.

 


보다 부적절한 결합

AccountController.ChangePassword 메서드는 UserRepository클래스의 인스턴스를 생성하여 User 객체의 인스턴스를 조회한다. 그렇게 해야 하는 이유는 SecurityService.ChangeUserPassword 매서드가 User 객체를 필요로 하기 때문이다.
즉, User 객체의 인스턴스를 얻지 못하면 메서드를 호출 할 수 없다. 이런 형태는 잘못된 메서드 인터페이스 디자인으로 꼽힌다. 

 

 

[HttpPost]
public void ChangePassword(string userID, string newPassword)
{
    this.securityService.ChangeUserPassword(user, newPassword);
}

...
public void ChangeUserPassword(string userID, string newPassword)
{
    var userRepository = new UserRepository();
    var user = userRepository.GetByID(userID);
    user.ChangePassword(newPassword);
}

 

이렇게 하면 AccountController 클래스는 개선되지만, 
ChangeUserPassword 메서드는 UserRepository 객체의 인스턴스를 직접 생성하는 문제점을 여전히 가지고 있다.

 

 

 

해당 글은 C#으로 배우는 적응형 코드 서적을 토대로 개념을 정리중인 글입니다.
728x90

'Programming' 카테고리의 다른 글

[gRPC] gRPC 알아보기  (0) 2023.01.02
[DI] 의존성 관리하기 (2)  (0) 2019.12.25
[DI] 의존성과 계층화  (0) 2019.12.16
[C#] interface 와 abstract class 의 차이  (0) 2019.12.12
[C#] 인터페이스 (interface)에 대한 정리  (0) 2019.12.06