토비의 스프링을 읽고 개인적으로 기록을 하고 있어요 :)
템플릿: 변화가 일어나지 않는 부분과, 변화가 일어나는 부분을 독립시켜서 효과적으로 활용할 수 있도록 하는 방법
예외 처리 없는 deleteAll() 함수 로직의 대해서 예외가 발생했을 때의 로직을 추가해 보자.
// 수정 전
public void deleteAll() throws SQLException {
Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement("delete from users");
ps.executeUpdate();
ps.close();
c.close();
}
// 수정 후
public void deleteAll() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
try {
c = dataSource.getConnection();
ps = c.prepareStatement("delete from users");
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){}
}
}
}
1개의 메서드에서는 이렇게 예외처리를 할 수 있다. 이런 처리가 수십 수백 개가 되었을 때 상상을 해보아라. 수백 개의 메서드를 빠짐없이 정상적으로 close() 할 수 있겠는가? 커넥션풀이 다 차는 문제가 발생한다면 하루종일 수백 개의 메서드를 체크하며 close() 처리가 안되었는지 확인해야 된다.
개선을 해보자 :)
분석!!! -> 변하는 부분과, 변하지 않는 부분을 구분한다. (아주 중요!!)
먼저 해볼 수 있는 건 메서드 추출법. 변하는 부분을 메서드로 추출하고 변하지 않는 부분에서 호출 하도록 수정
이 방법은 거꾸로 됐다. deleteAll(), getCount() 함수에서 변하지 않는 예외처리는 놔두고, 변하는 비즈니스 로직만을 분리해서 별도로 호출하게 된다. 거꾸로 된 모양새다.
그래서 시도해 볼 방법은 템플릿 메서드 패턴이다.
템플릿 메서드 패턴: 변하지 않는 부분은 슈퍼클래스에 두고, 변하는 부분은 추상 메서드로 정의하여 서브클래스에서 정의해서 사용 하는 패턴.
템플릿 메서드 패턴만을 사용하면, 필요 기능의 따라서 모든 서브클래스를 만들어야 하며, 서브클래스를 공통적으로 사용할 수 있는 장치(e.x, 인터페이스)가 없음
그래서 전략패턴을 사용한다.
전략 패턴: 오브젝트를 둘로 분리하고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 정의
-> prepareStatement 부분이 전략이다. prepareStatement에 기능마다 다른 sql을 작성해주고 변하는 부분. 인터페이스를 상속받아서 기능들을 개발. 자주 변하는 부분만을 분리하여 깔끔하게 분리 [0]
템플릿/콜백 패턴: 전략 패턴의 컨텍스트를 템플릿이라 부르고, 익명 내부 클래스로 만들어지는 오브젝트를 콜백이라부른다.
템플릿 메서드에서는 기능들을 별도의 클래스로 만들었지만, 템플릿/콜백에서는 익명 내부 클래스 사용을 선호.
익명 내부 클래스를 선언해서 기능을 만들다 보니, 람다 사용을 하면 엄청나게 깔끔해 보인다. [1]
템플릿/콜백 패턴 연습을 해보자. 계산기 프로그램에 최초 덧셈 기능만을 추가하였다.
public class Calculator {
public Integer calcSum(String filepath) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(filepath));
Integer sum = 0;
String line = null;
while ((line = br.readLine()) !=null) {
sum += Integer.valueOf(line);
}
return sum;
} catch (IOException e) {
System.out.println(e.getMessage());
throw e;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
}
try/catch문이 무시무시하다[2] 이후에 곱셈 기능을 추가한다면 반복되는 try/catch문을 또 반복하며 곰셈 함수를 작성할거야? 이제는 그러면 안된다. 템플릿/콜백 패턴을 공부했으니 패턴을 적용해보자 :)
공통된 부분을 템플릿으로 만들고, 기능은 callback으로 만든다. 이후에 공통된 부분이 더 보인다면 공통된 부분을 템플릿 내에 추가를 시켜주자. 한 번에 공통된 부분이 안 보일 수도 있어. 괜찮다. 깔끔한 코드는 한번에 만들어지는게 아니니까. 리팩토링을 진행하면 되겠다.
예시를 보자면 파일을 읽고 덧셈, 곱셈 프로그램을 만들었다. 파일을 읽는 부분이 공통된 부분이라 파일을 읽는 부분만 템플릿을 만들었다. 기능이 잘 돌아가는데 코드를 가만 보니, 라인을 읽는 부분도 똑같다. 그렇다면 기존의 템플릿에 파일 읽기 및 라인을 읽는 부분까지 템플릿에 포함시키면 되겠다.
스프링에서의 DB 작업은 정말 간단하다. 원래 대로라면 DB 작업을 하기 위한 코드를 커넥션 부터 예외처리까지 작성을 해줘야 되지만, 템플릿/콜백 패턴을 활용한 JdbcTemplate가 이미 만들어져 있다. 그런 이유로 우리는 JdbcTemplate를 활용하여 필요한 기능만을 구현해주면 되겠다. [3]
[0] 템플릿 메소드 / 전략 패턴 사용
https://github.com/junngo/spring-playground/commit/1feadb87c5b4eb2dffe4bfce4f7e97f6adf81260
[1] 템플릿/콜백 패턴 사용
https://github.com/junngo/spring-playground/commit/2a41456054fc8fc597a0886b8b22e87179d5c52f
[2] 템플릿/콜백 패턴 연습 중(덧셈 기능 추가 -> 템플릿/콜백 패턴 적용)
https://github.com/junngo/spring-playground/commit/1201343e85fa6033677a3cd271774fb5193b5701
https://github.com/junngo/spring-playground/commit/dda53f95e386cbf2b5f16f5f88cdf31123689d00
https://github.com/junngo/spring-playground/commit/dc3c8467dc00d83a084af6dc6bae721eb0c58d52
[3]
https://github.com/junngo/spring-playground/commit/c7d27476a0cdae18898e322776b3aa1934430f8d
'개발 기록 > 스프링' 카테고리의 다른 글
[스프링] Unsupported Media Type 415 에러 (0) | 2023.05.04 |
---|---|
[토비의 스프링 정리] #2 테스트의 대해서 리뷰 및 기록 (0) | 2023.02.07 |
[스프링] H2 데이터베이스 콘솔 로그인 (0) | 2023.02.04 |
[토비의 스프링 정리] #1 오브젝트와 의존관계의 대해서 리뷰 및 기록 (0) | 2023.02.02 |
[JPA 실습] java.lang.IllegalArgumentException: Unknown entity 에러 (0) | 2023.01.27 |