스프링

토비의 스프링 - 1.2 DAO의 분리

초롱불 2021. 5. 19. 12:00

개요

1. 관심사의 분리의 필요성

2. 분리의 방법1 - 메소드 추출

3. 분리의 방법2 - , 상속을 통한 확장

본문

1. 관심사의 분리의 필요성

모든 것은 융통성있게 확장, 변화할 수 있게 하기 위해서이다. 한 클래스에 필요하다 싶은 대로 떠오르는 대로 코딩하면 작동이야 하겠지만 이후에 수정하거나 다른 기능을 추가하려면 어디가 어떻게 작동하는지 이해하는 것도 어렵고 코드를 수정 추가할 때 분리했을 때보다 더 많은 양의 코드를 수정해야하고, 생각대로 수정했는데 잘못 이해해서 코드가 작동하지 않는 일이 일어날 수도 있다. 이를 피하고 좀 더 효율적인 코딩을 위해서 개발자들은 여러가지 방법론을 만들었고 그 중하나가 분리이다. 변화는 한 가지 관심을 중심으로 일어나고 그 관심을 중심으로 적절히 분리할 수 있다면 변화가 필요한 부분과 필요없는 부분을 따로 바라볼 수 있다. 이것은 전체 코드를 이해하고 변화가 필요 없는 영역에는 영향을 끼치지 않는 효율적인 코딩을 가능하게 한다.

 

2. 분리의 방법1 - 메소드 추출

분리가 필요한 것을 이해했다면 그 방법을 고민해볼차례다. 앞서 만들었던 '초난감DAO'를 중심으로 고민해보자면 세 가지로 그 관심사항을 분리할 수가 있다.

 

첫째, DB와 연결을 위한 커넥션을 어떻게 가져올 것인가.

둘째, DB에 보낼 SQL문장을 담고 실행시키는 것.

셋째, 사용한 statement와 Connection을 닫아서 공유 리소스를 시스템에 돌려주는 것.

 

 

관심사항이 어떻게 나누어지는 지 알았으니 이제 어떻게 실천할 수 있는지 방법을 배워가야 한다. 가장 기초적인 방법으로 메소드의 추출이 있다.

 

앞서 만들었던 초난감DAO에서는 add, get각 메소드에 커넥션을 어떻게 가져올 것인가를 반복해서 쓰고 있다. 이 부분을 메소드 단계에서 분리해보도록 하자.

	public void add(User user) throws ClassNotFoundException, SQLException{
		
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection(
				"jdbc:mysql://localhost:3306/springbook?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=UTF8&", "root", "1234");
		
		...
		
	}
    
    	public User get(String id) throws ClassNotFoundException, SQLException {
		
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection(
				"jdbc:mysql://localhost:3306/springbook?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=UTF8&", "root", "1234"
				);
		
		...
				
				
		
	}
    }

분리 이전 코드

	public void add(User user) throws ClassNotFoundException, SQLException{
		
//		Class.forName("com.mysql.jdbc.Driver");
//		Connection c = DriverManager.getConnection(
//				"jdbc:mysql://localhost:3306/springbook?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=UTF8&", "root", "1234");
//		관심사가 다른 중복 코드
        
		Connection c =getConnection();
		
		...
		
	}
    
    	public User get(String id) throws ClassNotFoundException, SQLException {
		
//		Class.forName("com.mysql.jdbc.Driver");
//		Connection c = DriverManager.getConnection(
//				"jdbc:mysql://localhost:3306/springbook?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=UTF8&", "root", "1234");
//		관심사가 다른 중복 코드
		
		Connection c =getConnection();
		
		...
		
						
		
	}
    
    	//메소드 추출해서 중복 제거
	private Connection getConnection() throws ClassNotFoundException, SQLException {
		
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection(
				"jdbc:mysql://localhost:3306/springbook?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=UTF8&", "root", "1234");
		
		return c;
	}

분리 이후 코드

이렇게 분리할 경우 다른 DB에 연결해야 할 경우 getConnection메소드만 고치면 add와 get메소드의 커넥션이 모두 수정된다. add, get이외에 수천개의 db연결이 필요한 메소드가 있다면 일일이 수정할 필요가 없으니 수정 이전보다 엄청난 효율을 보일 수 있다. 다음으로 상속을 통해 서브클래스로 분리하는 방법과 그 기능을 알아보도록 하겠다.

 

2. 분리의 방법2 - 상속을 통한 확장

토비의 스프링에서는 커넥션 할 DB가 다른 N사와 D사에게 프로그램을 판매하는 상황을 예시로 설명을 하고 있다. getConnection의 코드가 N사와 D사의 경우의 수에 따라서 분리되어 있는 게 아니기에 N사와 D사에게 일일이 수정하게끔 하는 방법을 고려해볼 수 있는데, 이 경우에 UserDao의 코드를 전부 보여야 되기 때문에 난감하다. 이를 막기 위해서 상속을 통해 UserDao의 코드를 한 단계 더 분리하는 방법을 쓴다.

 

package springbook.user.ex3.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import springbook.user.domain.User;
//상속의 통한 확장
public abstract class UserDao {
	
	
	public void add(User user) throws ClassNotFoundException, SQLException{
		
		Connection c =getConnection();
		
	...
		
	}
	
	public User get(String id) throws ClassNotFoundException, SQLException {
		
		Connection c =getConnection();
		
	...
				
				
		
	}
	
	//추상 메소드
	public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
	

	

}

public class DUserDao extends UserDao {

	@Override
	public Connection getConnection() throws ClassNotFoundException, SQLException {
		//D사 DB connection 생성 코드
		return null;
	}


}

public class NUserDao extends UserDao {

	@Override
	public Connection getConnection() throws ClassNotFoundException, SQLException {
		//N사 DB connection 생성 코드
		return null;
	}


}

DB연결 방법을 어떻게 할 것인가를 NuserDao, DUserDao 클래스에서 맡으면서 관심사가 클래스 레벨로 구분되고 있다. 이렇게 슈퍼클래스에서 기본적인 로직의 흐름을 만들고 그 기증의 일부를 추상 메소드나 오버라이딩이 가능한 protected메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법을 템플릿 메소드 패턴이라고 한다. 또한 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것을 팩토리 메소드 패턴이라고 한다. 

정리

기본적인 관심사의 분리를 적용해보았다. 관심사의 분리는 프로그램의 효율적인 변화와 확장을 위해서 필요하다. 메소드에서 중복되는 코드를 추출하여 getConnection 메소드로 분리해보았고 커넥션에 변화가 필요할 경우 getConnection만 수정하면 다른 커넥션 기능을 모두 변화시킬 수 있다는 점을 통해 그 효율성을 확인했다. 또한 다양한 DB연결 요구에 대응하기 위해서 상속을 통한 분리와 그 기능 확장을 확인해보았다. 이를 디자인 패턴에서 템플릿 메소드 패턴, 생성에 관심을 둔다는 점에서 팩토리 메소드 패턴이라는 걸 알 수 있었다.

하지만 이러한 상속을 사용하는 방법에는 단점과 한계점이 있다. 자바의 클래스는 다중상속을 허용하지 않는다. 하나의 상속 구조가 만들어지면 다른 목적으로 상속을 적용하기 힘들다. 또한 상속을 통해 연결되어 있다면 그 서브 클래스 와의 관계가 밀접하다는 문제가 있다. 서브클래스에서는 슈퍼 클래스의 기능을 직접 사용할 수 있다. 슈퍼 클래스가 수정되면 서브클래스의 구조에 문제가 생길 수 있다. 이를 극복하는 방법 알아가보자.

 

소스 깃허브

 

https://github.com/cholongbul/Tobyspring

 

cholongbul/Tobyspring

토비의 스프링 연습. Contribute to cholongbul/Tobyspring development by creating an account on GitHub.

github.com

 

'스프링' 카테고리의 다른 글

토비의 스프링 - 1.5 스프링IoC  (0) 2021.05.20
토비의 스프링 - 1.4 제어의 역전  (0) 2021.05.20
토비의 스프링 - 1.3 DAO의 확장  (0) 2021.05.19
토비의 스프링 - 1.1 초난감 DAO  (0) 2021.05.19
테스트 입문  (0) 2021.04.08