스터디/[토비의 스프링 3.1 Vol 1] (2025.03)

[토비의 스프링 3.1 Vol 1] 3장. 템플릿

ttoance 2025. 3. 23. 09:00
반응형

3장 템플릿

 

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

 

3.1 다시 보는 초난감 DAO 

 

JDBC 수정 기능의 예외처리 

  • 일반적으로 서버에는 제한된 개수의 DB 커넥션을 만들어서 재사용 가능한 폴로 고나리한다. 
  • DB폴은 매번 getConnection()으로 가져간 커넥션을 명시적으로 close()해서 돌려주어야만 다시 폴에 넣었다가 다음 커넥션 요청이 있을때 재사용 할 수 있다.  
  • 오류가 날 때마다 반환되지 못하는 Connection이 계속 쌓인다. 
    • try/catch/finally 문으로 예외처리를 한다. 

 

 

JDBC 조회 기능의 예외처리 

  • Connection, PreparedStatement 외에도 ResultSet 처리도 신경써야 한다. 

 

 

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

메소드 추출 

  • 변하는 부분을 메소드로 빼는 것을 고려할 수 있지만, 변하지 않는 부분이 변하는 부분을 감싸고 있어 변하지 않는 부분을 추출하기가 어려워 반대로 한다. 
    • 당장 봐서는 별 이득이 없다. 
    • 분리시킨 메소드를 다른 곳에서 재사용 할 수 있어야 하는데, 이 경우 분리시키고 남은 메소드가 재사용이 필요하고, 분리된 메소드는 DAO로직마다 새롭게 만들어서 확장해야 한다.

 

 

템플릿 메소드 패턴의 적용 

  • 템플릿 메소드 패턴은 상속을 통해 기능을 확장해서 사용하는 부분이다. 
    • 변하지 않는 부분은 슈퍼클래스에 두고 변하는 부분은 추상 메소드로 정의해둬서 서브클래스에서 오버라이드하여 새롭게 정의해서 쓰도록 하는 것이다. 
    • UserDao클래스 기능을 확장하고 싶을때마다 상속을 통해 자유롭게 확장하고, 확장 때문에 기존 상위DAO클래스에 불필요한 변화는 생기지 않다는 점에서 개방 폐쇄 원칙 OCP를 지키는 구조를 만들어 낼 수 있다. 
      • 하지만, 가장 큰 문제는 DAO로직마다 상속을 통해 새로운 클래스를 만들어야 하고 
      • 확장구조가 클래스를 설계하는 시점에서 고정되어 버려 유연성이 떨어진다. 

 

 

전략 패턴의 적용 

  • 템플릿 메소드 패턴보다 유연하고 확장성 뛰어난 것은 오브젝트를 아예 둘로 분리하고 클래스 레벨에서 인터페이스 통해서만 의존하도록 만드는 전략 패턴이다. 
    • 전략 패턴은 OCP 관점에 보면 확장에 해당하는 변하는 부분을 별도의 클래스로 만들어 추상화된 인터페이스 통해 위임하는 방식
    • 좌측에 있는 Context의 contextMethod()에서 일정한 구조를 가지고 동작하다가 특정 확장 기능은 Strategy 인터페이스 통해 외부 독립된 전략 클래스에 위임하는 것
      • delteAll() 메소드에서 변하지 않는 부분이라고 명시한 것이 바로 이 contextMethod()가 된다. 

 

 

  • deletAll()의 컨텍스트를 정리해보면 다음과 같다. 
    • DB 커넥션 가져오기
    • PreparedStatement 를 만들어줄 외부 기능 호출하기 (전략 패턴에서 말하는 전략)
    • 전달받은 PreparedStatement 실행하기
    • 예외가 발생하면 이를 다시 메소드 밖으로 던지기
    • 모든 경우에 만들어진 PreparedStatement 와 Connection 을 적절히 닫아주기 

 

  • 하지만 전략 패턴은 필요에 따라 컨텍스트는 그대로 유지하면서 전략을 바꿔 쓸 수 있는 전략인데,
    • 이렇게 컨텍스트 안에서 이미 구체적인 전략 클래스인 DeleteAllStatement을 사용하도록 고정되어 있다면 이상하다. 
    • 컨텍스트가 StatementStrategy 인터페이스 뿐만 아니라 특정 구현 클래스인 DeleteAllStatement을 알고있는건, OCP에도 잘 들어맞는다고 볼 수 없다. 

 

DI 적용을 위한 클라이언트/컨텍스트 분리 

  • 클라이언트가 컨텍스트가 사용할 전략을 정해서 전달한다는 면에서 DI 구조라고 할 수 있다. 

 

 

 

3.3 JDBC 전략 패턴의 최적화 

add() 메소드 적용

  • add() 메소드에서 변하는 부분인 PreparedStatement를 만드는 코드를 AddStatement 클래스로 옮긴다. 

 

현재까지의 문제점은 

  • 먼저 DAO메소드마다 새로운 StatementStrategy 구현 클래스를 만들어야 한다. 
    • 이렇게 되면 클래스 파일의 개수가 많이 늘어날 수 있다. 
  • 또 다른 문제점은 DAO 메소드에서 StatementStrategy에 전달할 부가적인 정보가 있는 경우, 이를 위해 오브젝트를 전달받는 생성자와 이를 저장해둘 인스턴스 변수를 만들어야 한다는 것. 

 

 

이 중 클래스 파일의 갯수가 늘어나는 것에 대한 해결책으로 로컬 클래스가 있다. 

로컬 클래스 

  • 매번 독립된 파일로 만들지 않고 UserDao 클래스 안에 내부 클래스로 정의한다.
    • 로컬 변수 선언하듯이 선언하고 로컬 클래스는 선언된 메소드 내에서만 사용할 수 있다. 

 

(+) 익명 내부 클래스 

  • 만들어진 익명 내부 클래스의 오브젝트는 딱 한 번만 사용할 테니 변수에 담아두지 않는다. 
new 인터페이스이름() { 클래스 본문 };

 

 

3.4 컨텍스트와 DI 

3.4.1 JdbcContext의 분리 

  • 전략패턴의 구조로 보면
    • UserDao의 메소드가 클라이언트이고,
    • 익명 내부 클래스로 만들어지는 것이 개별 전략이고, 
    • JdbcContextWithStatementStrategy() 메소드카 컨텍스트이다. 
      • 다른 DAO에서 사용가능하기 때문에 이 JdbcContextWithStatementStrategy()를 UserDao 클래스 밖으로 독립시키려고 한다. 

 

클래스 분리 

  • 분리해서 만들 클래스의 이름은 JdbcContext 라고 한다. 

 

 

  • UserDao와 JdbcContext를 DI로 받아서 설정한다. 

 

 

 

 

3.4.2 JdbcContext의 특별한 DI 

  • UserDao는 인터페이스를 거치지 않고 코드에서 바로 JdbcContext를 사용하고 있다. 
    • UserDao와 JdbcContext는 클래스 레벨에서 의존관계가 결정된다. 
  • 인터페이스를 사용하지 않으면 엄밀히 말해서 온전한 DI라고 볼 수 없다. 
    • 그러나 스프링의 DI는 넓게 보자면 객체의 생성과 관계설정에 대한 제어권한을 오브젝트에서 제거하고 외부로 위임했다는 IoC라는 개념 포괄하기 때문에 그런 의미에서 DI의 기본을 따르고 있다고 볼 수 있다. 
  • DI로 만들어야 하는 이유는 다음 2가지로 생각할 수 있다.  
    • JdbcContext가 스프링 컨테이너의 싱글톤 레지스토리에서 관리되는 싱글톤 빈이고 
    • JdbcContext가 DI를 통해 다른 빈에 의존(datasurce) 해야한다. 

 

코드를 이용하는 수동 DI

  • JdbcContext를 스프링의 빈으로 등록해서 UserDao에 DI하는 대신 사용할 수 있는 방법으로는
  • UserDao 내부에서 직접 DI를 적용하는 방법이다. 이 방법을 쓰면 싱글톤으로 만들려는 것은 포기해야 한다. 

  • 스프링의 설정파일에 userDao와 dataSource 두 개의 빈으로 정의한다. 
  • 그리고 userDao 빈에 DataSurce 타입 프로퍼티를 지정해서 dataSource 빈을 주입받도록 한다. 

 

  • 이 방법의 장점은 굳이 인터페이스를 두지 않아도 될 만큼 긴밀한 관계를 갖는 DAO 클래스와 JdbcContext 를 따로 분리하지 않고 내부에서 만들어 사용하면서 다른 오브젝트에 대한 DI를 적용할 수 있다는 점이다. 

 

결론

  • 인터페이스를 사용하지 않는 클래스와의 의존관계이지만 스프링의 DI를 이용하기 위해 빈으로 등록해서 사용하는 방법
    • 오브젝트 사이의 실제 의존관계가 설정파일에 명확하게 드러난다는 장점이 있다. 
    • 하지만 DI의 근본적인 원칙에 부합하지 않는 구체적인 클래스와의 관계가 설정에 직접 노출된다는 단점이 있다. 
  • DAO의 코드를 이용해 수동으로 DI하는 방법 
    • jdbcContext가 UserDao의 내부에서 만들어지고 사용되면서 그 관계를 외부에 드러내지 않는다. 
    • 하지만 JdbcContext가 여러 오브젝트가 사용하더라도 싱글톤으로 만들 수 없고, DI 작업을 위해 부가적인 코드가 필요하다는 단점도 있다. 

 

3.5 템플릿과 콜백 

  • 전략패턴의 기본 구조에 익명 내부 클래스를 활용하는 방식을 스프링에서 템플릿/콜백 패턴이라고 한다. 
    • 전략 패턴의 컨텍스트를 템플릿이라 부르고, 
    • 익병 내부 클래스로 만들어지는 오브젝트를 콜백이라고 한다. 

3.5.1 템플릿/콜백의 동작원리

템플릿/콜백의 특징 

  • 매번 메소드 단위로 사용할 오브젝트를 새롭게 전달받는다. 
  • 콜백 오브젝트가 내부 클래스로서 자신을 생성한 클라이언트 메소드 내의 정보를 직접 참조한다. 
  • 클라이언트와 콜백이 강하게 결합된다. 

 

  • 클라이언트의 역할은 템플릿 안에서 실행될 로직을 담은 콜백 오브젝트를 만들고, 콜백이 참조할 정보를 제공하는 것이다. 만들어진 콜백은 클라이언트가 템플리의 메소드를 호출할 때 파라미터로 전달된다.
  • 템플릿은 정해진 작업 흐름을 따라 작업을 진행하다가 내부에서 생성한 참조정보를 가지고 콜백 오브젝트의 메소드를 호출한다. 콜백은 클라이언트 메소드에 있는 정보와 템플릿이 제공한 참조정보를 이용해서 작업을 수행하고 그 결과를 다시 템플릿에 돌려준다.
  • 템플릿은 콜백이 돌려준 정보를 사용해서 작업을 마저 수행한다. 경우에 따라서 최종 결과를 클라이언트에 다시 돌려주기도 한다. 

 

JdbcContext에 적용된 템플릿/콜백 

 

 

3.5.2 편리한 콜백의 재활용 

  • 한 가지 아쉬운 점은 상대적으로 코드를 작성하고 읽기가 조금 불편하다는 점이다. 

 

이를 해결하기 위해 SQL 문장만 파라미터로 받아서 바꿀 수 있게 메소드 내용 전체를 분리해 별도의 메소드로 만든다.

 

 

한단계 더 나아가 UserDao 말고 다른 Dao에서도 사용할 수 있도록 메소드 접근자를 public으로 바꾼다. 

 

 

3.7 요약

  • JDBC와 같은 예외가 발생할 가능성이 있으며 공유 리소스의 반환이 필요한 코드는 반드시 try/catch/finally 블록으로 관리해야 한다. 
  • 일정한 작업 흐름이 반복되면서 그 중 일부 기능만 바뀌는 코드가 존재한다면 전략 패턴을 적용한다. 바뀌지 않는 부분은 컨텍스트로, 바뀌는 부분은 전략으로 만들고 인터페이스를 통해 유연하게 전략을 변경할 수 있도록 구성한다. 
  • 같은 애플리케이션 안에서 여러 가지 종류의 전략을 다이나믹하게 구성하고 사용해야 한다면 컨텍스트를 이용하는 클라이언트 메소드에서 직접 전략을 정의하고 제공하게 만든다. 
  • 클라이언트 메소드 안에 익명 내부 클래스를 사용해서 전략 오브젝트를 구현하면 코드도 간결해지고 메소드의 정보를 직접 사용할 수 있어서 편리하다. 
  • 컨텍스트가 하나 이상의 클라이언트 오브젝트에서 사용된다면 클래스를 분리해서 공유하도록 만든다. 
  • 컨텍스트는 별도의 빈으로 등록해서 DI 받거나 클라이언트 클래스에서 직접 생성해서 사용한다. 클래스 내부에서 컨텍스트를 사용할 때 컨텍스트가 의존하는 외부의 오브젝트가 있다면 코드를 이용해서 직접 DI 해줄 수 있다. 
  • 단일 전략 메소드를 갖는 전략 패턴이면서 익명 내부 클래스를 사용해서 매번 전략을 새로 만들어 사용하고, 컨텍스트 호출과 동시에 전략 DI를 수행하는 방식을 템플릿/콜백 패턴이라고 한다. 
  • 콜백의 코드에서 일정한 패턴이 반복된다면 콜백을 템플릿에 넣고 재활용하는 것이 편리하다. 
  • 템플릿과 콜백의 타입이 다양하게 바뀔 수 있다면 제네릭스를 이용한다. 
  • 스프링은 JDBC 코드 작성을 위해 JdbcTemplate을 기반으로 하는 다양한 템플릿과 콜백을 제공한다. 
  • 템플릿은 한 번에 하나 이상의 콜백을 사용할 수도 있고, 하나의 콜백을 여러 번 호출할 수도 있다. 
  • 템플릿/콜백을 설계할 때는 템플릿과 콜백 사이에 주고받는 정보에 관심을 둬야 한다. 

 

 

 

 


토비 스프링 소스코드 ▶ 
https://github.com/AcornPublishing/toby-spring3-1/tree/main

 

GitHub - AcornPublishing/toby-spring3-1: 토비의 스프링 3.1

토비의 스프링 3.1. Contribute to AcornPublishing/toby-spring3-1 development by creating an account on GitHub.

github.com

 

 

토비 스프링 ▶ https://product.kyobobook.co.kr/detail/S000000935358

 

토비의 스프링 3.1 Vol 1: 스프링의 이해와 원리 | 이일민 - 교보문고

토비의 스프링 3.1 Vol 1: 스프링의 이해와 원리 | 대한민국 전자정부 표준 프레임워크 스프링을 설명하는 No. 1 베스트셀러! 단순한 예제를 스프링 3.0과 스프링 3.1의 기술을 적용하며 발전시켜 나

product.kyobobook.co.kr

 

반응형