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
'스터디 > [토비의 스프링 3.1 Vol 1] (2025.03)' 카테고리의 다른 글
[토비의 스프링 3.1 Vol 1] 5장. 서비스 추상화 (~5.2.2) (0) | 2025.04.06 |
---|---|
[토비의 스프링 3.1 Vol 1] 4장. 예외 처리 (0) | 2025.03.29 |
[토비의 스프링 3.1 Vol 1] 2장. 테스트 (0) | 2025.03.16 |
[토비의 스프링 3.1 Vol 1] 1장. 오브젝트와 의존관계 (1) | 2025.03.09 |