공부하기 싫다.
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 |