템플릿과 콜백

정훈희·2022년 10월 21일
0

Spring

목록 보기
11/24
post-thumbnail

템플릿과 콜백

참조

  • 토비의 스프링 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를 사랑하는 백엔드 개발자입니다. 열심히 공부하고 열심히 기록합니다.

0개의 댓글