책&강의 정리

[Java / Spring] 토비의 스프링 - 1. 오브젝트와 의존관계 - DAO 분리

마볼링 2023. 5. 13. 10:36
토비의 스프링 공부 후 정리한 글입니다.

1.2  DAO 분리

개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항은 미래의 변화를 어떻게 대비할 것인가이다

여기서 변경 혹은 발전은 한 번에 한 가지 관심사항에 집중해서 일어난다

즉, 관심이 같은 것끼리는 모으고, 관심이 다른 것은 따로 떨어져 있게하는 관심사의 분리가 중요하다

 

📍 커넥션 만들기의 추출

UserDao - add() 메소드 관심사항

  1. DB와 연결을 위한 커넥션을 어떻게 가져올까
  2. 사용자 등록을 위해 DB에 보낼 SQL 문장을 담을 Statement를 어떻게 만들고 실행할까
  3. 작업이 끝나면 사용한 리소스인 Statement와 Connection 오브젝트 닫아주기

첫번째 "DB와 연결을 위한 커넥션을 어떻게 가져올까"

=> 커넥션을 가져오는 중복된 코드를 분리 => getConnection() 메소드

 

getConnection() 메소드를 추출해서 중복을 제거한 UserDao

package chapter1;

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 add(User user) throws SQLException, ClassNotFoundException {
      Connection c = getConnection();
      // 생략
   }

   public User get(String id) throws SQLException, ClassNotFoundException {
      Connection c = getConnection();
      // 생략
      return user;
   }

   public Connection getConnection() throws ClassNotFoundException, SQLException {
      Class.forName("org.mariadb.jdbc.Driver");

      Connection c = DriverManager.getConnection(
         "jdbc:mariadb://localhost:3306/tobySpring"
         , "root"
         , "1234"
      );
      return c;
   }
}

 

📍 변경사항에 대한 검증: 리팩토링과 테스트

main() 메소드를 이용하여 테스트하면 같은 결과가 출력된다(기존 데이터 삭제 후)

 

방금 한 작업은 UserDao의 기능에는 아무런 변화를 주지 않고 코드의 구조만 변경한다

기능이 추가되거나 바뀐 것은 없지만 코드가 이전보다 훨씬 깔끔해졌고 미래의 변화에 좀 더 손쉽게 대응할 수 있다

이런 작업을 리팩토링(Refactoring) 이라고 한다.

 

또한, 위에서 사용한 getConnection()이라고 하는 공통의 기능을 담당하는 메소드로

중복된 코드를 뽑아내는 것을 메소드 추출(extract method) 기법이라고 부른다.

 

📍 DB 커넥션 만들기의 독립

추후에 소스 코드를 공개하지 않고 고객에서 컴파일된 클래스 바이너리 파일만 제공했을 때

고객이 스스로 원하는 DB 커넥션 생성 방식을 적용하게 하고싶다면?

=> UserDao 코드 한 단계 더 분리

 

UserDao에서 메소드의 구현 코드를 제거하고 getConnection()을 추상 메소드로 만들어 놓는다.

=> 고객은 UserDao 클래스를 상속해서 각각 자신의 서브클래스를 만든다.

 

상속을 통한 확장 방법이 제공되는 UserDao

package chapter1;

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

public abstract class UserDao2 {

   public void add(User user) throws SQLException, ClassNotFoundException {
      Connection c = getConnection();
      // 생략
   }

   public User get(String id) throws SQLException, ClassNotFoundException {
      Connection c = getConnection();
      // 생략
      return user;
   }

   // 구현 코드는 제거되고 추상 메소드로 바뀌었다
   // 메소드의 구현은 서브클래스가 담당한다.
   public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
}
package chapter1;

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

public class NUserDao extends UserDao2{

   @Override
   public Connection getConnection() throws ClassNotFoundException, SQLException {
      Class.forName("org.mariadb.jdbc.Driver");

      Connection c = DriverManager.getConnection(
         "jdbc:mariadb://localhost:3306/tobySpring"
         , "root"
         , "1234"
      );
      return c;
   }
}

DAO의 핵심 기능인 어떻게 데이터를 등록하고 가져올 것인가 => UserDao

DB 연결 방법은 어떻게 할 것인가 => NUserDao

 

클래스 계층구조를 통해 두 개의 관심이 독립적으로 분리되면서 변경 작업 용이

=> UserDao의 코드는 수정 필요없이 DB 연결 기능을 새롭게 정의한 클래스 생성 가능

 

이렇게 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나

오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게

구현해서 사용하도록 하는 방법을 템플릿 메소드 패턴(Template Method Pattern)이라고 한다.

 

또한, 위 방식처럼 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것을 

팩토리 메소드 패턴(Factory Method Pattern)이라고 부르기도 한다.

 

(계속...)