이번 챕터에서는 구조와 관심사에 관해 이야기 하는 것 같다.
public class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
var connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/tobi",
"root",
"seungkyu");
PreparedStatement preparedStatement = connection.prepareStatement(
"insert into users(id, name, password) values(?,?,?)"
);
preparedStatement.setString(1, user.getId());
preparedStatement.setString(2, user.getName());
preparedStatement.setString(3, user.getPassword());
preparedStatement.executeUpdate();
preparedStatement.close();
connection.close();
}
public User get(String id) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
var connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/tobi",
"root",
"seungkyu");
PreparedStatement preparedStatement = connection.prepareStatement(
"select * from users where id = ?"
);
preparedStatement.setString(1, id);
var resultSet = preparedStatement.executeQuery();
resultSet.next();
var user = new User();
user.setId(resultSet.getString("id"));
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
resultSet.close();
preparedStatement.close();
connection.close();
return user;
}
}
이렇게 만든 코드에서 만약 데이터베이스의 정보를 바꾸려고 한다면, 모든 코드에서 하나하나 코드를 바꿔야 한다는 문제가 생긴다는 것이다.
그렇기 때문에 관심사에 따라 중복되는 코드를 추출해 별도로 분리해야 한다.
private Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/tobi",
"root",
"seungkyu");
}
이렇게 연결에 관한 부분을 분리했다.
만약 데이터베이스의 연결에 관한 정보가 변경된다면, 이 곳에서 한번에 연결에 대한 정보를 변경 할 수 있다.
이렇게 기존의 동작에는 영향을 주지 않으면서 내부 구조를 재구성하는 작업을 리펙토링이라고 한다.
방금 이러한 작업이 리펙토링의 일종이며, 유지보수를 위해 꼭 필요한 작업 중 하나이다.
조금 더 리펙토링 해보도록 하자.
만약, 현재 UserDao에서 add와 get의 코드를 공개하지 않고 데이터베이스의 연결을 수정하고 싶다면 어떻게 해야할까?
생성자를 통해 데이터베이스의 연결 정보를 받아올 수는 있지만, 일부의 메서드만 수정할 수 있도록 하기 위해서는 객체지향의 특징은 추상화를 이용하면 된다.
public abstract class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException {
Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(
"insert into users(id, name, password) values(?,?,?)"
);
preparedStatement.setString(1, user.getId());
preparedStatement.setString(2, user.getName());
preparedStatement.setString(3, user.getPassword());
preparedStatement.executeUpdate();
preparedStatement.close();
connection.close();
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(
"select * from users where id = ?"
);
preparedStatement.setString(1, id);
var resultSet = preparedStatement.executeQuery();
resultSet.next();
var user = new User();
user.setId(resultSet.getString("id"));
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
resultSet.close();
preparedStatement.close();
connection.close();
return user;
}
public abstract Connection getConnection() throws ClassNotFoundException ,SQLException;
}
이렇게 UserDao를 추상 클래스로, getConnection()을 추상 메서드로 변경한 후
public class SeungkyuUserDao extends UserDao{
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/tobi",
"root",
"seungkyu");
}
}
상속받는 객체에서 해당 추상 메서드를 구현하고
UserDao userDao = new SeungkyuUserDao();
SeungkyuUserDao를 부모 객체인 UserDao로 할당하여 그대로 사용한다.
이런 식으로 객체지향의 특징을 잘 활용하면 리펙토링을 적절하게 할 수 있다.
이런 방식으로 부모 객체에서 추상화해둔 메서드들을 자식 메서드에서 구현하여 사용하는 디자인 패턴을 템플릿 메서드 패턴이라고 한다.
'백엔드 > 토비의 스프링' 카테고리의 다른 글
[토비의 스프링] 1.6 싱글톤 레지스트리와 오브젝트 스코프 (1) | 2025.05.14 |
---|---|
[토비의 스프링] 1.5 스프링의 IoC (0) | 2025.05.13 |
[토비의 스프링] 1.4 제어의 역전(IoC) (0) | 2025.05.07 |
[토비의 스프링] 1.3 Dao의 확장 (0) | 2025.05.07 |
[토비의 스프링] 1.1 초난감 DAO (1) | 2025.05.06 |