//////
Search
🗒️

스프링 기초

날짜
2022/10/18
작성자
황민우
카테고리
회고

JDBC

DAO

관심사 분리

객체지향 기술은 자체가 지니는, 변화에 효과적으로 대처할 수 있다는 기술적인 특징
객체지향 기술이 만들어내는 가상의 추상세계 자체를 효과적으로 구성할 수 있고, 이를 자유롭고 편리하게 변경, 발전, 확장시킬 수 있다.
가장 좋은 대책은 변화의 폭을 최소한으로 줄여주는 것
분리과정
1.
메소드 추출
2.
공통 메소드 추상화
3.
의존 주입
관심사
1.
DB 연결
a.
DB 종류
b.
드라이버
c.
로그인 정보
d.
커넥션 생성 방법
2.
SQL문 전송
a.
Statement 생성
b.
파라미터 바인딩
c.
실행
3.
결과 반환
메소드 추출 기법
public class UserDao { public void add(User user) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Map<String, String> env = System.getenv(); String DB_HOST = env.get("DB_HOST"); String DB_USER = env.get("DB_USER"); String DB_PASSWORD = env.get("DB_PASSWORD"); Connection c = DriverManager.getConnection(DB_HOST, DB_USER, DB_PASSWORD); 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 get(String id) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Map<String, String> env = System.getenv(); String DB_HOST = env.get("DB_HOST"); String DB_USER = env.get("DB_USER"); String DB_PASSWORD = env.get("DB_PASSWORD"); Connection c = DriverManager.getConnection(DB_HOST, DB_USER, DB_PASSWORD); 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; } }
Java
복사
add(), get() 처럼 DB와 연결하고 sql문을 날리는 메소드가 1000개가 있다고 가정해보자.
그 코드에서 DB연결 방법이 수정된다면 1000개의 메소드를 찾아가 일일이 수작업으로 바꿔줘야한다.
매우 비효율적이고 사람의 실수로 인해 에러가 발생할 확률이 매우 커진다.
기존의 코드에서 DB연결 과정을 하나의 메소드로 분리해주고 필요한 곳에서 그 메소드를 호출해 사용하도록 설계하면 DB연결 방법이 달라지더라도 한 메소드만 수정해주면 되는 코드를 만들 수 있다.
public class UserDao { public void add(User user) throws ClassNotFoundException, SQLException { Connection c = getConnection(); // ... } public User get(String id) throws ClassNotFoundException, SQLException { Connection c = getConnection(); // ... } private Connection getConnection() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Map<String, String> env = System.getenv(); String DB_HOST = env.get("DB_HOST"); String DB_USER = env.get("DB_USER"); String DB_PASSWORD = env.get("DB_PASSWORD"); Connection c = DriverManager.getConnection(DB_HOST, DB_USER, DB_PASSWORD); return c; } }
Java
복사
이렇게 중복되는 코드를 메소드로 분리하는 기법을 메소드 추출 기법이라고 한다.
템플릿 메소드 패턴
변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만드는 패턴.
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; }
Java
복사
예를 들어, User를 add하는 기능과 get하는 기능은 그대로 사용하고 DB연결 과정을 사용자마다 바꾸고 싶다면?
이때, 템플릿 메소드 패턴을 사용하여 add와 get은 슈퍼클래스에 구현해 두고 getConnection 메소드는 서브 클래스에서 구현하도록 할 수 있다.
팩토리 메소드 패턴
서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것
public class NUserDao extends UserDao{ @Override public Connection getConnection() throws ClassNotFoundException, SQLException { Map<String, String> env = System.getenv(); String dbHost = env.get("DB_HOST"); String dbUser = env.get("DB_USER"); String dbPW = env.get("DB_PASSWORD"); Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection(dbHost, dbUser, dbPW); return conn; } }
Java
복사
최최최종 인터페이스로 분리
public interface ConnectionMaker { Connection makeConnection() throws SQLException, ClassNotFoundException; }
Java
복사
public class NConnectionMaker implements ConnectionMaker { @Override public Connection makeConnection() throws SQLException, ClassNotFoundException { Class.forName("com.mysql.cj.jdbc.Driver"); Map<String, String> env = System.getenv(); String DB_HOST = env.get("DB_HOST"); String DB_USER = env.get("DB_USER"); String DB_PASSWORD = env.get("DB_PASSWORD"); Connection c = DriverManager.getConnection(DB_HOST, DB_USER, DB_PASSWORD); return c; } }
Java
복사
public class UserDao { private ConnectionMaker connectionMaker; public UserDao(ConnectionMaker connectionMaker) { this.connectionMaker = connectionMaker; } // ... }
Java
복사
인터페이스로 분리하고 UserDao에서 인터페이스로 느슨한 연결을 해줌으로써 UserDao는 Connection 객체를 어떻게 만들어서 본인한테 주어지는 지는 몰라도 동작하게 할 수 있다.
ConnectionMaker 또한 자기가 만든 Connection 객체가 어떻게 쓰이는지 알 필요가 없다.
이렇게 서로의 관심사가 분리되고 또한 DB연결방법을 직접 다루고 싶은 사용자는 ConnectionMaker 인터페스를 구현해 UserDao에 전달해주기만 하면 된다.