- 템플릿 : 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며, 일정한 패턴을 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜 효과적으로 활용할 수 있도록 하는 방법
- 기존의 DAO 코드는 dao를 통해 커넥션 풀을 생성하고, 디비에 접근하여 쿼리를 실행하고, 커넥션 풀을 반환해주는 플로우. try-catch-finally 문을 통해 혹시 모를 예외상황에 대비하여 커넥션 풀을 반환하는 코드를 짤 경우, 여러개의 dao을 만들때마다 try-catch-finally를 제대로 사용하였는지 코드를 검수해야함.
- 템플릿 메소드 패턴: 상속을 통해 기능을 확장해서 사용하는 패턴으로, 변하지 않는 부분은 슈퍼 클래스에 두고 변하는 부분은 추상메서드로 정의하여 서브 클래스에서 오버라이드하여 새롭게 정의하여 쓰도록 하는 것.
abstract protected PreparedStatement makeStatement(Connection c) throws SQLException;
위와 같이 변하는 부분을 추상메소드로 정의한다.
그리고 이를 상속하여 다음과 같이 메서드를 구현한다.
public class UserDaoDeleteAll extends UserDao {
protected PreparedStatement makeStatement(Connection c) throws SQLException {
PreparedStatement ps = c.prepareStatement("delete from users");
return ps;
}
}
=> 다만 이 방법은 dao를 새로 생성해야할때마다 상속을 통해 새로운 서브 클래스를 만들어야한다는 것.
- 전략패턴 : 오브젝트를 아예 둘로 분리하고, 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 것.
-Context :
1. 디비 커넥션 맺어주기
2. PreparedStatment를 만들어줄 외부 기능 호출하기
3. 전달받은 PreparedStatement 실행하기
4. 예외가 발생하면 이를 다시 메소드 밖으로 던지기
5. 모든 경우에 만들어진 PreparedStatement와 Connection을 적절히 닫아주기와 같은 기능을 함
위의 경우 포함
- Strategy : PreparedStatement의 반환형을 반환하는 외부기능의 인터페이스
- Strategy 전략 클래스 : Strategy의 기능을 구현하는 구현체
=> 하지만 이 전략을 수정하지 않고 사용하게 되면, Context상에서 구현클래스를 직접 호출하기 때문에 OCP의 원칙과 들어 맞는다고 할 수 없음.
- DI 적용을 위한 클라이언트 컨텍스트 분리:
- 클라이언트 측에서 전략을 선택하여 오브젝트를 생성하고, 컨텍스트를 호출하여 전략 오브젝트를 전달한다.
- 제공받은 전략 오브젝트는 PreparedStatement생성이 필요한 시점에 호출하여 사용한다.
- 클라이언트가 컨텍스트를 사용할 전략을 정해서 전달한다는 면에서 DI구조라고 이해할 수 있다.
컨텍스트와 DI
package springbook.user.dao;
public class JdbcContext {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void workWithStatementStrategy(StatementStrategy stmt) throws SQLException{
Connection c = null;
PreparedStatement ps = null;
try{
c = this.dataSource.getConnection();
ps = stmt.makePreparedStatement(c);
ps.executeUpdate();
}catch(SQLException e) {
throw e;
} finally {
if(ps != null) { try {ps.close(); } catch(SQLException e) {} }
if(c != null) { try{c.close(); } catch(SQLException e) {} }
}
}
}
- DB커넥션을 필요로 하는 코드를 바깥으로 클래스를 분리시켜서, UserDao가 JdbcContext를 DI받아서 사용할 수 있게끔 한다.
public class UserDao {
..
private JdbcContext jdbcContext;
public void setJdbcContext(JdbcContext jdbcContext) {
this.jdbcContext = jdbcContext;
}
public void add(final User user) throws SQLException {
this.jdbcContext.workWithStatementStrategy(
new StatementStrategy() { ... }
);
}
public void deleteAll() throws SQLException {
this.jdbcContext.workWithStatementStrategy(
new StatementStrategy() { ... }
);
}
}
- 인터페이스를 사용해서 클래스를 자유롭게 변경할 수 있게 하지는 않았지만 JdbcContext를 UserDao와 DI구조로 만든 이유는,
1. JdbcContext가 스프링 컨테이너의 싱글톤 레지스트리에 관리되는 싱글톤 빈이 되기 때문이다. -> JdbcContext는 JdbcContext메서드를 제공해주는 일종의 서비스 오브젝트로 의미가 있고, 싱글톤으로 등록되어 여러 오브젝트에서 공유해서 사용하는 것이 이상적이기 때문
2. JdbcContext가 DI를 통해 다른 빈(dataSource)에 의존하고 있기 때문임. DI를 위해서는 주입되는 오브젝트와 주입받는 오브젝트 양쪽 모두 스프링 빈으로 등록되어야 함.
템플릿과 콜백
- Template : 어떤 목적을 위해 미리 만들어둔 모양이 있는 틀. 학생들이 도형을 그릴 때 사용하는 도형자 또는 모양자가 바로 템플릿임. 고정된 작업 흐름을 가진 코드를 재사용한다는 의미에서 붙인 이름.
- Callback : 콜백은 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 말함. functional Object.
- 템플릿/콜백 패턴의 작업 흐름 :
1. Client: Callback 생성
2. Client : Callback 전달 / Template 호출
3. Template : workflow시작
4. Template : 참조 정보 생성
5. Template: Callback 호출 / 참조정보 전달
6. Callback : Client final변수 참조
7. Callback : 작업 수행
8. Callback : Callback 작업 결과 리턴
9. Template : workflow 진행
10. Template : workflow 마무리
11. Template : Template 작업 결과
- 클라이언트의 역할은 템플릿 안에서 실행될 로직을 담을 콜백 오브젝트를 만들고, 콜백이 참조할 정보를 제공하는 것.
- 템플릿은 정해진 작업을 따라 작업을 진행하다가, 내부에서 생성한 참조정보를 가지고 콜백 오브젝트의 메서드 호출함.
- 콜백이 클라이언트 메소드에 있는 정보와 템플릿이 제공한 참조 정보를 이용하여 작업을 수행하고, 그 결과를 다시 템플릿에 돌려줌
- 템플릿은 콜백이 돌려준 정보를 사용하여 작업을 마저 수행함.
앞서 만들었던 UserDao, JdbcContext, StatementStrategy의 코드에 템플릿 / 콜백 패턴이 적용되어 있음
JdbcContext의 workWithStatementStrategy()가 템플릿에해당하고, UserDao가 클라이언트, 콜백이 StatementyStrategy이다.
public class JdbcContext {
public void deleteAll() throws SQLException {
this.jdbcContext.excecuteSql("delete from users");
}
public void executeSql(final String query) throws SQLExcpetion {
workWithStatementStrategy(
new StatementStrategy() {
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
return c.prepareStatement(query);
}
}
);
}
}
이 클래스는 복잡한 익명 내부 클래스의 사용을 최소화하기 위해 만든 클래스. 중복을 제거하고 기능의 구현에 집중하였다.
위와 같이 하나의 목적을 위해 서로 긴밀하게 연결된 코드들은 하나의 클래스에 모아두는게 편할 수 도 있다.
스프링의 JdbcTemplate
- 스프링은 JDBC를 이용하는 dao에서 사용할 수 있도록 다양한 템플릿과 콜 백을 제공함
- 스프링이 제공하는 JDBC 코드용 기본 템플릿은 JdbcTemplate.
public class UserDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.dataSource = dataSource;
}
}
- JDBCTemplate은 생성자의 파라미터로 DataSource를 주입하면 된다.
update
this.jdbcTeplate.update("delete from users");
-파라미터로 sql문장을 전달하며,
아래와 같이 사용할 수도 있다.
this.jdbcTemplate.update("insert into users(id, name, password) values(?, ?, ? )", user.getId(), user.getName(), user.getPassword());
queryForInt()
'Spring' 카테고리의 다른 글
RequestContextHolder (0) | 2023.11.04 |
---|---|
Reactive Programming & Spring Webflux (0) | 2023.10.30 |
토비 스프링 2장. 테스트 (0) | 2022.09.18 |
토비의 스프링 1장. 오브젝트와 의존관계 (0) | 2022.09.11 |
업무에 쓰인 Bean Validation 정리 (0) | 2022.02.17 |