[스프링 이해와 원리] #1. 난감한 Dao, DaoI? :: 잡다한 프로그래밍
반응형

1. 난감한 DAO 만들기

다음과 같이 사용자 정보를 저장할 User 클래스를 만든다 

public class User {
	String id;
	String name;
	String password;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

 

이후 Mysql을 사용하여 id, Name, Password를 가진 테이블을 생성한다.

이후 다음과 같은 UserDao를 만든다. 사용자 생성, 조회 메소드를 가진 Dao 이다.

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

public class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
		PreparedStatement ps = c.prepareStatement("INSERT INTO USERS(ID, NAME, PASSWORD) values(?,?,?)");
		ps.setString(1,user.getId());
		ps.setString(2,user.getName());
		ps.setString(3,user.getPassword());

		ps.executeUpdate();
		
		ps.close();
		c.close();
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
		PreparedStatement ps = c.prepareStatement("SELECT * FROM USERS WHERE id = ?");
		ps.setString(1,id);

		ResultSet rs = ps.executeQuery();
		rs.next();
		User user = new User();
		user.setId(rs.getString("id"));
		user.setName(rs.getString("name"));
		user.setPassword(rs.getString("password"));
		
		rs.close();
		ps.close();
		c.close();
		
		return user;
	}
}

메인 코드를 다음과 같이 작성하면 정상적으로 동작하는것을 확인할 수 있다. 하지만. 이는 함수마다 connection을 반복적으로 진행하는 좋지 못한 방법이다. 만약 1000개의 함수가 존재하는데 비밀번호가 바뀐다면 1000번 수정해야한다. 따라서 DAO의 분리를 통해 다음과 같이 수정해보자.

 

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

public class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Connection c = getConnection();
        ...
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Connection c = get Connection();
        ...
	}
    private Connection getConnection() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
        return c;
    }
}

이렇게 된다면 getConnection함수를 호출하여 사용하므로 이전과 같은 문제를 해결할 수 있다.

 

이러한 Dao클래스를 외부에서 가져다 사용한다고 하자, A회사 B회사가 각기 다른 DB를 사용하고 Connection을 독자적으로 적용하고싶다고 하자 어떻게 제공할 수 있을까?

 

상속을 통한 확장

다음과 같이 상속을 통해 확장할 수 있다. A,B회사는 각각 UserDao를 상속받고 getConnection을 오버라이딩하여 Dao클래스를 만들 수 있다.

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

public abstract class UserDao {

	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Connection c = getConnection();
        ...
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Connection c = get Connection();
        ...
	}
    public abstract Connection getConnection() throws ClassNotFoundException, SQLException {
		//추상메소드로 구현하여 구현을 서브클래스에서 담당한다
    }
}

 

이렇게 해결한 Dao에도 단점이 있다 상속을 받았다 보니 다른 상속을 받을 수 없다는 단점과, 슈퍼클래스와 하위클래스의 긴밀한 결합이다. 예를들어 슈퍼클래스를 수정하면 모든 서브 클래스를 수정하거나 다시 개발해야할 수 있다.

 

따라서 이를 별도의 클래스로 분리하여 사용하는 방법을 이용한다. 다음과 같이 Connection부분을 별도의 클래스로 분리한다.

 

이후 다음과 같이 수정한다. 이렇게 수정하게되면 connection, dao부분이 분리가 되긴 하였으나, UserDao가 SimpleConnectionMaker에 종속 되어 있다 보니, A,B회사에서 Connection부분을 확장하기에 적합하지 않다. 

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

public class UserDao {
	private SimpleConectionMaker simpleConnectionMaker;
    
    public UserDao(){
    	simpleConnectionMaker = new SimpleConnectionMaker();
        //생성자를 통해 다음과 같이 만들어놓고 함수에서 사용하도록 하게 한다.
    }
    
	public void insertUser(User user) throws ClassNotFoundException, SQLException {
		Connection c = simpleConnectionMaker.makeNewConnection();
        ...
	}
	
	public User getUserList(String id) throws ClassNotFoundException, SQLException {
		Connection c = simpleConnectionMaker.makeNewConnection();
        ...
	}
}

 

따라서 인터페이스를 활용하면 다음과 같이 해결할 수 있다.

ConnectionMaker interface

public interface ConnectionMaker {
	
    public Connection makeConnection() throws ClassNotFoundException, SQLException;
}

 

ConnectionMaker구현 클래스

public class AConnectionMaker implements ConnectionMaker {
	...
    
    public Connection makeConnection() throws ClassNotFoundException, SQLException {
    	// A회사의 Connection을 생성하는 코드
    }
}

 

UserDao

public class UserDao {
	private ConnectionMaker connectionMaker;
    
    public UserDao(){
    	connectionMaker = new AConnectionMaker();
    }
    
    public void insert.....
    
    public User get.....
    
}

다음과 같이 구현하게 된다면 B사도 Interface에 맞는 클래스만 구현주면 되므로 UserDao부분을 고칠 이유가 사라진다 하지만 AConnection 클래스의 생성자를 호출해서(connectionMaker = new AConnectionMaker();) 오브젝트를 생성하는 부분이남아 있으므로 완벽하게 분리되지 않았다.

 

따라서 우리는 new AConnectionMaker로 객체를 생성하는 방법이 아닌 파라미터로 객체를 넘겨 받아 이를 구현하도록 하겠다.

UserDao 생성자부분

public UserDao(ConnectionMaker connectionMaker) {
	this.connectionMaker = connectionMaker;
}

 

main클래스

import java.sql.SQLException;

public class UserDaoTest {

	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		// TODO Auto-generated method stub
		ConnectionMaker connectionMaker = new DConnectionMaker(); //UserDao부분이 아닌 main에서 객체 생성
        
        UserDao dao = new UserDao(connectionMaker); //객체 주입
    }

}

 

이렇게 이용하게 된다면 main 클래스만 수정하고 UserDao는 완벽하게 분리되게 된다. 공부를 하면서 Dao를 분리시키는 작업이 의존성 주입이랑 비슷하다는것을 알게 되었다.

반응형

+ Recent posts