#토비의_스프링
1.1 초난감 DAO
1.2 DAO의 분리
슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법을 템플릿 메서드 패턴이라고 한다.
UserDao의 서브클래스의 getConnection() 메소드는 어떤 커넥션 클래스의 오브젝틀르 어떻게 생성할 것인지를 결정하는 방법이라고도 볼 수 있다. 이렇게 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것을 팩토리 메서드 패턴이라고 한다.
아무튼 결론적으로 상속구조를 통해 성격이 다른 관심사항을 분리한 코드를 만들어내고, 서로 덜 영향을 주도록 할 수 있는지!가 중요하다.
- 하지만 상속을 사용했다는 단점이 존재한다. 왜 단점일까?
- 상속을 사용한다면 다중 상속이 불가하므로 userDao가 이미 다른 목적으로 상속하는 경우 이용할 수 없다.
- 하위 클래스가 상위 클래스의 기능을 직접 사용할 수 있으므로 캡슐화가 깨진다.
- 상위 클래스 내부의 변경이 있을 때 모든 서브 클래스의 코드를 함께 수정하여야한다. 이렇게 하고 싶지 않다면, 상위 클래스의 변경을 강제적으로 최소화해야한다.
어떤 방식으로 Connection 오브젝트를 만들어내는지도 하위 클래스의 관심사항이다. 서버의 DB 커넥션 풀에서도 가져올 수 있고, 드라이버를 직접 이용해 새로운 디비 커넥션을 만들수도 있다.
- 커넥션 풀이란?
- 미리 여러 개의 데이터베이스 Connection을 생성해서 보관한다.
- Connection이 필요할 때 마다 Connection pool로부터 하나씩 꺼내서 사용하고, 사용이 끝나면 다시 커넥션을 보관한다.
- Connection Pool이 데이터베이스의 연결과 해제를 직접 관리한다.
커넥션을 미리 생성해두고, 커넥션을 가져와서 사용하고 다시 반납하게 되면 매번 새로운 커넥션을 생성하지 않아도 된다.
1.3 DAO의 확장
모든 오브젝트는 변한다. 특히 관심의 변화에 따라 변하기 마련이고, 그에 따라 구조 또한 다르게 변한다.
추상 클래스를 만들고 이를 상속한 서브 클래스에서 변화가 필요한 부분을 바꿔서 쓸 수 있게 만든 이유는 바로 이렇게 변화의 성격이 다른 것을 분리해서, 서로 영향을 주지 않은 채로 각각 필요한 시점에 독립적으로 변경할 수 있게 하기 위해서다. 하지만 상속을 이용했다는 점이 불-편
불-편을 해소하기 위해 인터페이스를 도입하자.
UserDao에서 커넥션을 생성하는 부분을 connectionMaker라는 인터페이스에게 위임하자. userDao는 이제 자신이 사용할 클래스가 어떤 것인지 몰라도 되고, 단지 인터페이스를 통해 원하는 기능을 사용하기만 하면 된다.
- 인터페이스를 사용하면 어떤 점이 좋을까?
인터페이스에는 어떻게 하겠다는 구체적인 구현 방법은 없이, 어떤 일을 하겠다는 기능만 정의해놓았다. 그 기능은 인터페이스를 구현한 클래스들이 알아서 결정한다. 따라서 인터페이스를 사용한 클래스는 인터페이스에 명시된 기능에만 관심을 두면 되지, 그 기능을 어떻게 구현했는지에는 관심을 둘 필요가 없다.
이 또한 캡슐화라고 볼 수 있을까?
하지만 인터페이스로 구현한다해도, userDao의 생성자 안에서 new WoowaConnection(); 과 같이 구체적인 연결 관계를 명시해주어야한다. 이렇게 되면, 또 여전히 의존관계가 풀리지 않고 원점으로 복귀된 것을 알 수 있다. 어떻게 해야 userDao가 가장 상위 인터페이스인 ConnectionMaker만을 알고, 그 아래 구현체들은 몰라도 될 수 있을까?
우리는 이제, 사용하는 클라이언트 오브젝트에서 어떤 커넥션을 사용할 것인지를 알려주기로 하자. 그렇다면, userDao를 생성할 때 생성자의 인자로서 커넥션을 명시해 주입해줄 수 있다.
Userdao userDao = new UserDao(new WoowaConnection());
public class WoowaConnection implements ConnectionMaker {
..
}
이렇게 되면, userDao에는 전혀 손대지 않고 여러 회사에서 만족스럽게 DB 연결 기능을 확장해서 사용할 수 있게 되었다.
userDao는 자신의 관심사이자 책임인 사용자 데이터 엑세스 작업을 위해 SQL을 생성하고, 실행하는 데에만 집중할 수 있고, 더이상 DB 생성 방법이나 전략에 대해서는 고민할 필요가 사라진 것이다.
원칙과 패턴
개방 폐쇄 원칙 (Open-Closed Principle)
클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
인터페이스를 통해 제공되는 확장 포인트는 확장을 위해 열려있다. 반면 인터페이스를 이용하는 클래스는 자신의 변화가 불필요하게 일어나지 않도록 폐쇄되어있다.
OCP와 연관된 말로 높은 응집도와 낮은 결합도
가 있다.
여기서 응집도가 높다는 것은 변화가 일어날 때 해당 모듈에서 변화하는 부분이 크다는 것으로 이야기할 수 있다. 변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다면 응집도가 크다고 할 수 있다.
이 말은 조금 헷갈리는데, 반대로 생각했을 때 만약 변화가 일어날 때 모듈의 일부만 변경이 일어난다면, 그 부분을 찾기 어렵고, 어느 부분에서 변경이 일어나는지 파악해야하는 단점이 존재한다. 따라서 많은 부분이 함께 변경된다는 것은 공통된 부분이 잘 묶여있다?고 생각하면 좋을 것 같다. 😅
결합도는 하나의 오브젝트가 변경이 일어날 때에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도라고 설명할 수 있다. 따라서, 느슨하게 결합된 상태를 유지하는 것이 중요하다. 하나의 변경이 발생할 때 마치 파문이 이는 것처럼 여타 모듈과 객체로 변경에 대한 요구가 전파되지 않도록 해야한다.
전략 패턴
자신의 기능 맥락에서 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴
UserDaoTest - UserDao - ConnectionMaker
구조는 디자인 패턴의 시각으로 보면 전략 패턴에 해당한다고 볼 수 있다.
UserDao는 전략 패턴의 컨텍스트에 해당하고, DB 연결 방식이라는 구체적인 알고리즘을 ConnectionMaker라는 인터페이스로 분리시키고, 이를 구현한 구체적 알고리즘인 WoowaConnection을 이용해 필요에 따라 바꿔 사용할 수 있도록 하는 것이다!
1.4 제어의 역전 (IoC)
성격이 다른 책임이나 관심사는 분리하자. 분리시킬 기능을 담당할 클래스를 하나 만든다. 이 클래스의 역할은 객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 것인데, 이런 일을 하는 오브젝트를 팩토리라고 한다.
팩토리는 오브젝트들을 구성하고 그 관계를 정의하는 책임을 갖는다.
더 나아가기 위해서는 팩토리에서 Dao를 생성할 때 넣는 ConnectionMaker의 구현 클래스를 결정하는 부분을 분리하는 것이다.
// before
public UserDao userDao() {
return new UserDao(new WoowaConnection());
}
---
// after
public UserDao userDao() {
return new UserDao(connectionMaker());
}
private Connection connectionMaker() {
return new WoowaConnection();
}
제어권의 이전을 통한 제어 관계 역전
제어의 역전이란 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 설명할 수 있다.
관심을 분리하고 책임을 나누고 유연하게 확장 가능한 구조로 만들기 위해 팩토리를 도입하면서, 우리는 자연스럽게 IoC를 행한다.
프레임워크에는 분명한 제어의 역전 개념이 적용되어있어야한다.
프레임워크 vs 라이브러리
- 프레임워크는 애플리케이션 코드가 프레임워크에 의해 사용된다. 프레임워크 위에 개발한 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만든다.
- 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다.
애플리케이션 컨텍스트와 설정정보
빈 : 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트
스프링에서는 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리라고 부른다. 빈 팩토리보다는 이를 좀 더 확장한 application context를 사용한다. 애플리케이션 컨텍스트는 IoC 방식을 따라 만들어진 일종의 빈 팩토리라고 생각하면 된다.
- 빈 팩토리 - 빈을 생성하고 관계를 설정하는 IoC의 기본 기능에 초점을 맞춤.
- 애플리케이션 컨텍스트 - 애플리케이션 전반에 걸쳐 모든 구성 요소의 제어 작업을 담당하는 IoC 엔진이라는 의미가 더 부각