Post

토비의 스프링 3장

3. 템플릿

템플릿이란 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜서 효과적으로 활용할 수 있는 방법

3.1 다시 보는 초난감 DAO

  • 3.1.1 예외처리 기능을 갖춘 DAO
    • JDBC 수정 기능의 예외처리 코드
      • Connection과 PreparedStatement를 가져와서 사용하지만 예외가 발생했을 때 리소스가 제대로 반환되지 않는 문제가 있다.
      • try catch finally를 이용하여 해결
      • 하지만 close에서도 SQLException이 발생할 수 있기 때문에 try-catch가 또 필요하다.
    • JDBC 조회 기능의 예외처리
      • ResultSet도 반환이 필요

3.2 변하는 것과 변하지 않는 것

  • 3.2.1 JDBC try/catch/finally 코드의 문제점

    • try/catch/finally가 너무 많아 실수할 가능성이 높음
  • 3.2.2 분리와 재사용을 위한 디자인 패턴 적용

    • 메소드 추출
      • 자주 바뀌는 부분을 메소드로 독립시켰는데 확장돼야 하는 부분이 분리되어 이득이 없다.
    • 템플릿 메소드 패턴의 적용
      • 변하지 않는 부분은 슈퍼클래스에 두고 변하는 부분은 추상 메소드로 정의해둬서 서브클래스에서 오버라이드하여 새롭게 정의하여 쓴다.
      • 하지만 템플릿 메소드 패턴으로의 접근은 DAO 로직마다 상속을 통해 새로운 클래스를 만들어야 한다는 문제가 있다.
    • 전략 패턴의 적용
      • 오브젝트를 아예 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 전략 패턴 사용
      • 전략 패턴은 필요에 따라 컨텍스트는 그대로 유지되면서 전략을 바꿔 쓸 수 있다는 것인데, 컨텍스트 안에서 이미 구체적인 전략 클래스를 사용하도록 고정되기 때문에 문제가 있다.
        1
        2
        3
        
          public interface StatementStrategy {
          PreparedStatement makePreparedStatement(Conncetion c) throws SQLException;
          }
        
        1
        2
        3
        4
        5
        6
        
          public class DeleteAllStatement implements StatementStrategy {
          public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
          PreparedStatement ps = c.prepareStatement("delete from users");
          return ps;
          }
          }
        
    • DI 적용을 위한 클라이언트/컨텍스트 분리
      • 전략 패턴에 따르면 Context가 어떤 전략을 사용하게 할 것인가는 Context를 사용하는 앞단의 Client가 결정하는 게 일반적이다.
      • 클라이언트가 컨텍스트가 사용할 전략을 정해서 전달하기 때문에 DI구조

3.3 JDBC 전략 패턴의 최적화

  • 3.3.1 전략 클래스의 추가 정보
    • add()에도 전략 패턴을 적용, User 정보가 필요하기 때문에 StatementStrategy를 상속받은 AddStatement에서 User에 관한 정보를 생성자로부터 제공받는다.
  • 3.3.2 전략과 클라이언트의 동거
    • 현재 DAO 메소드마다 새로운 StatementStrategy 구현 클래스를 만들어야 한다는 문제가 있다.
    • 그리고 전달할 부가적인 정보가 있는 경우, 이를 위해 오브젝트를 전달받는 생성자와 오브젝트 변수가 필요하다는 문제가 있다.
    • 로컬 클래스
      • add() 메소드 내의 로컬 클래스로 AddStatement를 이전한다.
      • 이렇게 하면 클래스 파일을 줄일 수 있고, 정보에 직접 접근할 수 있다.
    • 익명 내부 클래스
      • 익명 내부 클래스: 클래스 선언과 오브젝트 생성이 결합된 형태로 만들어지며, 상속할 클래스나 구현할 인터페이스를 생성자 대신 사용한다.
        • new 인터페이스이름() { 클래스 본문 };

3.4 컨텍스트와 DI

  • 3.4.1 JdbcContext의 분리
    • 클래스 분리
    • 빈 의존관계 변경
  • 3.4.2 JdbcContext의 특별한 DI
    • 스프링 빈으로 DI
      • JdbcContext를 UserDao와 DI 구조로 만들어야 할 이유
        1. JdbcContext가 스프링 컨테이너의 싱글톤 레지스트리에서 관리되는 싱글톤 빈이기 때문에
        2. JdbcContext가 DI를 통해 다른 빈에 의존하고 있기 때문에
      • 왜 인터페이스를 사용하지 않았을까?
        • UserDao와 JdbcContext가 매우 긴밀한 관계를 가지고 강하게 결합되어 있기 때문에
    • 코드를 이용하는 수동 DI
      • UserDao에서 dataSource를 주입받아 jdbcContext에 수동으로 주입한다.
      • 이 방법을 사용하면 굳이 인터페이스를 두지 않아도 될 만큼 긴밀한 관계를 갖는 DAO클래스와 JdbcContext를 어색하게 따로 빈으로 분리하지 않고 내부에서 직접 만들어 사용하면서도 다른 오브젝트에 대한 DI를 적용할 수 있다는 점이다.

3.5 템플릿과 콜백

템플릿/콜백 패턴: 전략패턴의 기본 구조에 익명 내부 클래스를 활용한 방식

  • 템플릿: 고정된 틀 안에 바꿀 수 있는 부분을 넣어서 사용하는 경우
  • 콜백: 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트

  • 3.5.1 템플릿/콜백의 작동원리
    • 템플릿/콜백의 특징
      • 클라이언트의 역할은 템플릿 안에서 실행될 로직을 담은 콜백 오브젝트를 만들고, 콜백이 참조할 정보를 제공하는 것이다. 만들어진 콜백은 클라이언트가 템플릿의 메소드를 호출할 때 파라미터로 전달된다.
      • 템플릿은 정해진 작업 흐름을 따라 작업을 진행하다가 내부에서 생성한 참조정보를 가지고 콜백 오브젝트의 메소드를 호출한다. 콜백은 클라이언트 메소드에 있는 정보와 템플릿이 제공한 참조정보를 이용해서 작업을 수행하고 그 결과를 다시 템플릿에 돌려준다.
      • 템플릿은 콜백이 돌려준 정보를 사용해서 작업을 마저 수행한다. 경우에 따라 최종 결과를 클라이언트에 다시 돌려주기도 한다.
  • 3.5.2 편리한 콜백의 재활용 DAO 메소드에서 매번 익명 내부 클래스를 사용하기 때문에 상대적으로 코드를 작성하고 읽기가 조금 불편하다는 단점이 있다.
    • 콜백의 분리와 재활용 SQL 부분만 바뀌기 때문에 SQL문장만 파라미터로 받도록 변경
    • 콜백과 템플릿의 결합 일반적으로 성격이 다른 코드들은 가능한 한 분리하는 편이 낫지만, 이 경우에는 하나의 목적을 위해 서로 긴밀하게 연관되어 동작하는 응집력이 강한 코드들이기 때문에 한 군데 모여 있는 게 유리하다.
  • 3.5.3 템플릿/콜백의 응용
    • 제네릭스 응용: 타입이 다양하게 바뀔 수 있다면 사용

3.6 스프링의 JdbcTemplate

  • 3.6.1 update()
  • 3.6.2 queryForInt()
  • 3.6.3 queryForObject()
  • 3.6.4 query()
    • 테스트 보완
      • getAll()에 대한 네거티브 테스트 필요
  • 3.6.5 재사용 가능한 콜백의 분리
    • DI를 위한 코드정리
    • 중복 제거
    • 템플릿/콜백 패턴과 UserDao
      • UserDao의 개선점
        1. userMapper가 인스턴스 변수로 설정되어 있고, 한 번 만들어지면 변경되지 않는 프로퍼티와 같은 성격을 띠고 있으니 아ㅖ UserDao 빈의 DI용 프로퍼티로 만든다.
        2. DAO 메소드에서 사용하는 SQL문장을 UserDao 코드가 아니라 외부 리소스에 담고 이를 읽어와 사용
This post is licensed under CC BY 4.0 by the author.