728x90

권장되지는 않지만 서버 운영 중에 SQL을 변경해야 할 수도 있다고 한다.

스프링답게 이 방법을 만들어보도록 하자.

 

DI와 기능의 확장

DI는 기술이 아니다.

DI 자체를 공부한다는 느낌보다는 최대한 이용하며 설계하는 방법을 배우고 있는 중이다.

단순히 Controller, Service, Repository를 사용하는 것이 아니라 DI를 통해 유연하게 의존관계를 지정하도록 설계해야 제대로 DI를 사용하는 것이다.

제대로 DI를 사용할 수 있도록 의식하면서 프로그래밍을 하는 습관이 필요하다.

하나의 커다란 오브젝트를 적절한 책임에 따라 분리해주는 습관을 들여야 한다.

물론 당장 사용하지는 않겠지만, DI를 미래에 대한 보험이라고 생각하면 될 것이다.

 

DI를 사용할 때에는 최대한 인터페이스를 사용해야 한다.

물론 클래스를 직접 주입해서 사용하는 것도 가능은 하다.

하지만 그러면 그 클래스에 직접적으로 의존하게 된다.

만약 구현코드가 변경된다면, 해당 클래스를 의존하고 있는 모든 오브젝트도 변경해야 한다.

 

이렇게 인터페이스를 사용하는 첫번째 이유는 다형성을 얻기 위해서다.

하나의 인터페이스로 여러개의 구현체를 바꿔가며 사용하도록 하는 것이 DI의 첫번째 목표이다.

그리고 또 인터페이스를 사용해야 하는 이유가 있다면 인터페이스 분리 원칙을 통해 클라이언트와 의존 오브젝트 사이의 관계를 명확하게 해줄 수 있기 때문이다.

인터페이스는 하나의 오브젝트가 여러 개를 구현할 수 있으므로, 하나의 오브젝트를 바라보는 관점이 여러가지일 수도 있다.

하나의 인터페이스를 하나의 관심사라고 한다면, 하나의 오브젝트가 다양한 관심사를 구현할 수도 있다는 것이다.

여기서 본인의 관심사에만 의존한다면 의존받는 오브젝트의 다른 부분이 변경되더라도, 의존하는 오브젝트는 관심이 없기 때문에 변경될 필요도 없다.

이런 부분을 잘 이용해서 관심사에 따라 오브젝트를 구현하도록 만드는 것도 DI를 잘 이용하는 방법이다.

 

오브젝트가 그 자체로 응집도가 높게 설계되었더라도, 목적과 관심이 각기 다른 클라이언트가 있다면 이를 적절하게 분리해주는 것이 좋다.

이를 객체지향의 5대 원칙 중 하나인 인터페이스 분리 원칙(Interface Segregation Principle)이라고 한다.

 

인터페이스 상속

인터페이스 분리 원칙이 주는 장점은 모든 클라이언트가 자신의 관심에 따른 접근 방식을 간섭없이 유지할 수 있다는 점이다.

어차피 클라이언트는 본인이 의존하고 있는 인터페이스의 메서드만 신경쓰기에, 다른 부분의 변경은 영향을 주지 않기 때문이다.

 

다음과 같은 구조에서

BaseSqlService는 어떤 클래스가 SqlReader를 구현했던, 신경쓰지 않고 그저 인터페이스만 사용해서 변경없이 코드를 유지할 수 있다.

 

그리고 또 하나의 장점은, 2개의 인터페이스를 구현해서 각각 다른 클라이언트가 사용할 수 있다는 점이다.

 

현재 SqlRegistry는

public interface SqlRegistry{
	void registerSql(String key, String sql);
    
    String findSql(String key) throws SqlNotFoundException;   
}

 

이렇게 2가지를 가지고 있다. 그리고 이 인터페이스를 MySqlRegistry가 다른 인터페이스를 추가로 구현하고 싶다고 한다.

당연히 현재 BaseSqlService는 이런 등록하는 과정이 필요가 없기 때문에 SqlRegistry 인터페이스에 그런 메서드를 추가하면 안된다.

 

이런 부분은 분리를 해야한다.

public interface UpdatableSqlRegistry extends SqlRegistry{
    
    public void updateSql(String key, String sql);
    
    public void updateSql(Map<String, String> sqlmap);
}

 

 

이렇게 SqlRegistry에 업데이트를 하는 기능을 추가한다.

 

이러면 현재 BaseSqlService는 저 위의 메서드들만 사용하고 있기에, 아래의 메서드들에는 영향을 전혀 받지 않는다.

 

이런 Sql의 Update를 담당하는 클래스를 SqlManager라고 하자.

이런 식으로 BaseSqlService는 결국 SimpleUpdatableSqlRegistry를 주입받지만, 그 중 SqlRegistry의 메서드만 사용하게 된다.

그리고 이런 업데이트들은 다시 UpdatableSqlRegistry의 메서드들을 통해서 일어나게 되며, 해당 인터페이스의 구현체 또한 SimpleUpdatableSqlRegistry 이기 때문에 이런 업데이트가 결국 BaseSqlService에도 반영이 되는 것이다.

 

이렇게 인터페이스를 추가하거나 상속을 통해 확장하는 방식을 잘 이용하면 기존의 인터페이스를 사용하는 클라이언트가 있더라도 유연한 확장이 가능해진다.

 

잘 적용된 DI는 결국 잘 설계된 오브젝트 의존관계에 달려있다.

인터페이스를 적절하게 분리하고 확장하는 방법을 항상 고민하고, 의존관계를 명확하게 해주는 방법을 항상 고민하며 개발하자.

+ Recent posts