3.3 JDBC 전략 패턴의 최적화
- StatementStrategy 를 인터페이스로 하는 클래스를 전략마다 만들어야 한다는 점은 꽤나 번거롭다
- 또, 전략마다 클래스를 만드는 식의 방법으로는 부가적인 정보를 넘겨주어야 할 때 인스턴스 변수와 생성자 파라미터를 추가해야 한다는 번거로움이 존재함
클래스를 전략마다 만들지 않는 방법
- 클래스 안의 내부 클래스로 정의하는 방법
- 특정 메서드에서만 사용된다면 로컬 클래스로 만들수도 있음
- 클래스파일이 하나 줄어들고, 생성로직도 함께 볼 수 있어 좋음
- 또한, 로컬 클래스는 클래스가 내부 클래스이기에 생성자에 파라미터 추가할 필요 없이 메서드로 파라미터를 넘겨서 클래스 생성을 할 수 있다
- 또는 익명 내부 클래스로 만들수도 있다
3.4 컨텍스트와 DI
전략 패턴의 구조로 보자면
- UserDao의 메소드가 클라이언트
- 익명 내부 클래스로 만들어지는 것이 개별적인 전략
jdbcContextWithStatementStrategy()
메서드는 컨텍스트
jdbcContextWithStatementStrategy()
메서드는 JDBC의 일반적인 작업 흐름을 담고 있기에 다른 DAO에서 사용 가능하니 UserDao 클래스 밖으로 독립시키자JdbcContext의 특별한 DI

이 경우 JdbcContext는 그 자체로 독립적인 JDBC 컨텍스트를 제공해주는 서비스 오브젝트로서 의미가 있을 뿐 구현 방법이 바뀔 가능성은 없다.
따라서 인터페이스를 구현하도록 만들지 않았고, UserDao와 JdbcContext는 인터페이스를 사이에 두지 않고 DI를 적용하는 특별한 구조가 된다.
- 인터페이스를 사용해서 클래스를 자유롭게 변경할 수 있게 하지는 않았지만, JdbcContext를 UserDao와 DI 구조로 만들어야 할 이유
- JdbcContext는 그 자체로 변경되는 상태정보를 갖고 있지 않음. 내부에서 사용할 dataSource라는 인스턴스 변수가 있지만, dataSource는 읽기전용이므로 JdbcContext가 싱글톤이 되는 데 아무런 문제가 없음
JdbcContext가 스프링 컨테이너의 싱글톤 레지스트리에서 관리되는 싱글톤 빈이 되기 때문
JdbcContext가 DI를 통해 다른 빈에 의존하고 있기 때문(DataSource 오브젝트)
DI를 위해서는 주입되는 오브젝트와 주입받는 오브젝트 양쪽 모두 스프링 빈으로 등록되어 있어야 함
따라서 JdbcContext는 다른 빈을 DI 받기 위해서라도 스프링 빈으로 등록되어야 함
- 여기서 중요한 것은 인터페이스의 사용 여부. 인터페이스가 없다는 건 UserDao와 JdbcContext가 매우 긴밀한 관계를 가지고 강하게 결합되어 있다는 의미임.
- 비록 클래스는 구분되어 있지만 이 둘은 강한 응집도를 갖고 있음
- UserDao가 JDBC 방식 대신 JPA나 하이버네이트 같은 ORM을 사용해야 한다면 JdbcContext도 통째로 바뀌어야 함
- 단, 이런 클래스를 바로 사용하는 코드 구성을 DI에 적용하는 것은 가장 마지막 단계에서 고려해볼 사항임을 잊지 말기. 그저 인터페이스를 만들기 귀찮으니 그냥 클래스를 사용하자는 건 잘못된 생각이다.
변하는 것과 변하지 않는 것을 분리하고 변하지 않는 건 유연하게 재활용할 수 있게 만든다는 간단한 원리를 계속 적용했을 때, 이렇게 단순하면서도 안전하게 작성 가능한 Jdbc 활용 코드가 완성된다. 바로 이런 게 객체지향 언어와 설계를 사용하는 매력이 아닐까
스프링 빈으로 DI
- JdbcContext를 빈으로 만들어, UserDao에 빈으로 주입하는 방법
- 오브젝트 사이의 실제 의존관계가 설정파일에 명확하게 드러난다는 장점
- 하지만 DI의 근본적인 원칙에 부합하지 않는 구체적인 클래스와의 관계가 설정에 직접 노출되는 단점이 있음
- 이것이 중요한 이유는, 나중에 변경이 필요할 때 수정하지 않고 변경하기 위함. 결국 변경에 용이하기 위하여
코드를 이용하는 수동 DI
- JdbcContext를 빈으로 만들지 않고 UserDao의 내부에서 직접 DI를 적용하는 방식
- DAO 클래스마다 하나의 JdbcContext 오브젝트를 갖고 있게 하는 방식으로
- 이 방법의 장점은 굳이 인터페이스를 두지 않아도 될 만큼 긴밀한 관계를 갖는 DAO 클래스와 JdbcContext를 어색하게 따로 빈으로 분리하지 않고 내부에서 직접 만들어 사용하면서도 다른 오브젝트에 대한 DI를 적용할 수 있다는 점.
- 한 오브젝트의 수정자 메소드에서 다른 오브젝트를 초기화하고 코드를 이용해 DI 하는 것은 스프링에서도 종종 사용되는 기법임
- 관계를 외부에 드러내지 않는다는 장점이 있음
- 하지만 JdbcContext를 여러 오브젝트가 사용하더라도 싱글톤으로 만들 수 없고, DI 작업을 위한 부가적인 코드가 필요하다는 단점이 있음
템플릿 콜백 패턴
- 가장 전형적인 템플릿/콜백 패턴의 후보는 try/catch/finally 블록을 사용하는 코드다.
- 일정한 리소스를 만들거나 가져와 작업하면서 예외가 발생할 가능성이 있는 코드는 보통 try/catch/finally 구조로 코드가 만들어질 가능성이 높다.
스프링의 JdbcTemplate
스프링에는 JdbcTemplate 외에도 십여 가지의 템플릿/콜백 패턴을 적용한 API가 존재함.
클래스 이름이 Template으로 끝나거나 인터페이스 이름이 Callback으로 끝난다면 템플릿/콜백이 적용된 것이라고 보면 됨
테스트 보완
- 성공적인 테스트 결과를 보면 빨리 다음 기능으로 넘어가고 싶겠지만 너무 서두르는 것은 좋지 않다. 항상 꼼꼼하게 빠진 것은 없는지 더 개선할 부분은 없는지 한 번쯤 생각해보자
- 네거티브 테스트라 불리는, 예외상황에 대한 테스트는 항상 빼먹기 쉬움. get()이라면 ID가 없을 때는 어떻게 되는지, getAll() 이라면 결과가 하나도 없는 경우에는 어떻게 되는 지 등.
- 스프링의 개발자인 로드 존슨은 테스트를 작성할 때 항상 네거티브 테스트부터 만드는 습관이 있다고 함. 정상적인 조건의 테스트부터 만들면 테스트가 성공하는 것을보고 쉽게 만족해서 예외적인 상황은 빼먹고 넘어가기 쉽기 때문