템플릿과 콜백

정훈희·2022년 10월 21일
0

Spring

목록 보기
11/24
post-thumbnail
post-custom-banner

템플릿과 콜백

참조

  • 토비의 스프링 3.1 vol.1 240p~278p

템플릿과 콜백

위에서 우리는 UserDao와 JdbcContext, StatementStrategy를 이용해 코드를 짰다.

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

콜백은 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 말한다.

템플릿/콜백의 특징

템플릿/콜백 패턴의 콜백은 일반적으로 하나의 메소드를 가진 인터페이스를 구현한 익명 내부 클래스로 만들어진다.

image

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

템플릿/콜백 방식은 전략 패턴과 DI의 장점을 익명 내부 클래스 사용 전략과 결합한 하나의 디자인 패턴이라고 봐도 된다.

image

이전에 만들었던 UserDao의 작업 흐름은 위와 같다.

하지만 템플릿/콜백 방식에서 아쉬운 점은 DAO 메소드에서 매번 익명 내부 클래스를 사용하기 때문에 코드의 가독성이 떨어진다.

만약 익명 내부클래스에서 반복되어서 분리를 통해 재사용이 가능한 코드를 찾아낼 수 있다면 익명 내부 클래스를 사용한 코드를 간결하게 만들 수도 있다.

public void deleteAll() throws SQLException {
	this.jdbcContext.workWithStatementStrategy(
		new StatementStrategy() (
			public PreparedStatement makePreparedStatement(Connection c)
					throws SQLException {
				return c.prepareStatement("delete from users");
			}
		)
	);
}

위의 코드를 살펴보면 SQL 쿼리를 하나 날리는 메소드를 구현하는 것이 전부이다.

많은 콜백 오브젝트가 위와 같이 SQL 쿼리를 하나 날리는 내용일 것이다.

→ SQL문장만 파라미터로 받아서 바꿀 수 있게 하고 메소드 내용 전체를 분리해 별도의 메소드로 만들어보자.

public void deleteAll() throws SQLException {
	executeSql(‘delete from users");
}

private void executeSql(final String query) throws SQLException {
	this.jdbcContext.workWithStatementStrategy(
		new StatementStrategy() {
			public PreparedStatement makePreparedStatement(Connection c)
					throws SQLException {
				return c.prepareStatement(Query);
			}
		}
	);
}

이렇게 deleteAll에서 executeSql을 분리시켜서 재사용 할 수 있게 만들었다. 하지만 executeSql() 메소드는 UserDao만 사용하기는 아깝다.

이렇게 재사용 가능한 콜백을 담고 있는 메소드라면 DAO가 공유할 수 있는 템플릿 클래스 안으로 옮겨도 된다.

executeSql()를 JdbcContext로 옮기면 아래와 같이 모든 DAO 메소드에서 executeSql() 메소드를 사용할 수 있게 된다.

public void deleteAll() throws SQLException {
	this.jdbcContext.executeSql("delete from users");
}

제네릭스를 이용한 콜백 인터페이스

자바 언어에 타입 파라미터라는 개념을 도입한 제네릭스를 활용하면 콜백 결과의 타입을 다양하게 설정 할 수 있다.

파일의 각 라인에 있는 문자를 모두 연결해서 문자열로 돌려주는 기능과 파일의 각 라인에 있는 숫자들의 곱을 정수로 돌려주는 기능을 개발한다고 하면 반환형이 각각 문자열과 정수여야 한다.

그런 경우, 제네릭스를 이용한 콜백 인터페이스를 사용하면 된다.

public interface LineCallback<T> {
	T doSomethingWithLine (String line , T value);
}

위와 같이 타입을 <>에 받을 수 있는 LineCallback 인터페이스를 정의하고

public <T> T lineReadTemplate(String filepath , LineCallback<T> callback, T initVal)
		throws IOException {
	BufferedReader br =null;
	try {
		br =new BufferedReader(new FileReader(filepath));
		T res =initVal;
		String line =null;
		while((line = br.readLine()) != null) {
			res = callback.doSomethingWithLine(line, res);
		}
		return res;
	}
	catch(IOException e) {...}
	finally {...}
}

T타입을 반환하는 lineReadTemplate 메소드를 정의하고 파라미터로 아까 선언한 제네릭 인터페이스 LineCallback 타입을 받는다.

그리고, 메소드 안에서 callback 오브젝트의 메소드를 호출한다.

public String concatenate(String filepath) throws IOException {
	LineCallback<String> concatenateCallback =
		new LineCallback<String>() {
			public String doSomethingWithLine(String line, String value) {
				return value + line;
			}
		};
	return lineReadTemplate(filepath, concatènateCallback, " “ );
}

그리고 위와 같이 문자열 연결 기능 콜백을 이용해 메소드를 만들었다.

또한 스프링은 JDBC 코드 작성을 위해 다양한 템플릿과 콜백을 제공한다(JdbcTemplate).

profile
DB를 사랑하는 백엔드 개발자입니다. 열심히 공부하고 열심히 기록합니다.
post-custom-banner

0개의 댓글