공부하기 싫다. 





3.5 템플릿과 콜벡


 우리는 지금까지 전략패턴을 적용하고 익명 내부 클래스를 활용한 방식으로 코드를 작성해왔다. 이런 방식을 스프링에서는 템플릿/콜백 패턴이라고 부른다. 

이 때, 템플릿은 미리 만들어둔 모양이 있는 툴을 가리킨다. 

콜백은 실행되는 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 말한다. 이 때, 이 오브젝트는 파라미터로 전달되지만 값을 참조하기 위함이아니라 특정 로직을 담은 메소드를 실행시키기 위해 사용한다. 


템플릿/콜백의 특징

1) 일반적으로 단일 메소드 인터페이스를 사용한다. 다만, 여러가지 종류의 전략을 사용해야한다면 하나이상의 코백 오브젝트를 사용할 수 있다.

2) 콜백 인터페이스의 메소드에는 보통 파라미터가 있는데, 이 파라미터는 템플릿의 작업 흐름 중에 만들어지는 컨텍스트 정보를 전달 받을 때 사용한다.


콜백의 분리와 재활용

우리가 구현해온 탬플릿/콜백 패턴은 익명 내부클래스를 통해서 작성된 코드이다보니 적지 않게 중첩된 괄호가 만들어지게된다. 이 부분을 분리해 보도록하자.


1
2
3
4
5
6
7
8
9
10
11
public void deleteAll() throws SQLException{
    executeSql("delete from users");
}
public void executeSql(final String query) throws SQLException{
    this.jdbcContext.workWithStatementStrategy(new StatementStrategy() {
        @Override
        public PreparedStatement makePreParedStatement(Connection c) throws SQLException {
            return c.prepareStatement(query);
        }
    });
}
cs


애초에 필요한 것은 sql문장 뿐이었으니 위처럼 익명 클래스를 만드는 작업을 분리를 해서 재활용해보도록 하자. 또한, 이 executeSql은 UserDao뿐만 아니라 다른 Dao에서도 생성 될 수 있다. 따라서 우리가 이전에 만들어둔 JdbcContext클래스로 옮기도록하자


deleteAll 메소드가 더 간결하고 보기 편해졌다. 


 * * * 예제 * * *

 파일에서 라인별로 숫자를 입력받아 더한 후 출력하는 것을 템플릿 콜백으로 작성하라
 파일에서 라인별로 숫자를 입력받아 곱한 후 출력하는 것을 템플릿 콜백으로 작성하라





이제 JdbContext를 버리고 스프링이 제공하는 JDBC 코드용 기본 템플릿을 사용해보자. 클래스명은 JdbcTemplate이다


1
2
3
4
5
public void setDataSource(DataSource dataSource){
    this.dataSource = dataSource;
    
    jdbcTemplate = new JdbcTemplate(dataSource);
}
cs


다음과 같이 설정한 후 deleteAll에 적용해보도록 하자.

1
2
3
4
5
6
7
8
public void deleteAll() throws SQLException{
    jdbcTemplate.update(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
            return connection.prepareStatement("delete from users");
        }
    });
}
cs


더 편하게 다음과 같이 사용할 수도 있다.

1
2
3
public void deleteAll() throws SQLException{
    jdbcTemplate.update("delete from users");
}
cs


add 메소드는 다음과 같이 편하게 구현할 수 있다.



1
2
3
4
public void add(final User user) throws ClassNotFoundException, SQLException{
    jdbcTemplate.update("insert into users(id, name, password) values(?,?,?)",
            user.getId(), user.getName(), user.getPassword());
}
cs



getCount는 다음과 같이 구현할 수 있다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int getCount() throws SQLException{
    return this.jdbcTemplate.query(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
 
            return connection.prepareStatement("select count(*) from users");
        }
    }, new ResultSetExtractor<Integer>() {
        @Override
        public Integer extractData(ResultSet resultSet) throws SQLException, DataAccessException {
            resultSet.next();
            return resultSet.getInt(1);
        }
    });
}
cs


JdbcTemplate은 이보다 더 간단한 방식으로 처리하는 메소드로 queryForInt()라는 친구를 제공한다고 써저 있으나 spring의 3.2.2버전에서 deprecated됐다. 

 


1
2
3
public int getCount() throws SQLException{
    return this.jdbcTemplate.queryForObject("select count(*) from users",Integer.class);
}
cs


대신 위와 같이 구현이 가능하다.


get 메소드는 이보다 좀더 복잡한데, qureyForObject를 통해서 구현이 가능하다



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public User get(String id) throws ClassNotFoundException, SQLException{
    return this.jdbcTemplate.queryForObject(
            "select * from users where id = ?",
            new Object[]{id},
            new RowMapper<User>() {
                @Override
                public User mapRow(ResultSet resultSet, int i) throws SQLException {
                    User user= new User();
                    user.setId(resultSet.getString("id"));
                    user.setName(resultSet.getString("name"));
                    user.setPassword(resultSet.getString("password"));
                    return user;
                }
            });
}
cs



원래 get 메소드는 EmptyResultDataAccessException을 던지게 만들어져 있었는데, 위의 queryForObject가 조회결과가없을 경우에는 해당 Exception을 발생시켜준다.


여기까지 테스트하고 확인!



 * * * 예제 * * *

 getAll을 구현해보자!

 TDD할 것 (즉 테스트 먼저 생성 후,  


이 이후에 네거티브 테스트에 관하여 나온다. 요약해보자면, 테스트를 만들 때, 부정적인 테스트를 먼저 만들라는 것이다. 예를 들자면, 나이에 음수값을 넣어보거나 생일에 문자를 넣어보는 등에 관련 된 테스트에 관하여 중요하게 설명하고 있다. 



소스코드 : https://github.com/choiking10/spring_test/tree/vol1_3.5_3.7/lifeignite  



'web > spring' 카테고리의 다른 글

스프링 공부 일시 중지  (0) 2017.08.04
[토비 vol1] 4.1~4.3 예외  (0) 2017.07.26
[토비 vol1] 3.1~3.4 템플릿(1)  (0) 2017.07.26
[토비 vol1] 2.4~2.6 테스트(2)  (0) 2017.07.25
[토비 vol1] 2.1~2.3 테스트(1)  (0) 2017.07.25

+ Recent posts