1. 오브젝트와 의존관계 - DAO의 분리


1.2 DAO의 분리

1.2.1 관심사의 분리

객체지향 세계에서는 비즈니스 요구사항에 따라서 오브젝트에 대한 설계가 변할 수 있다.
즉, 오브젝트를 구현한 코드가 변할 수 있다는 것 이다.

또한, 애플리케이션이 기반을 두고 있는 운영 환경이나 기술 또한 바뀌거나 폐기처분 될 때가 있다.

객체를 설계할 때, 가장 중요시 생각해야 하는 것은 미래의 변화에 어떻게 대응할 것인가 이다.

객체지향 설계와 프로그래밍이 이전의 절차적 프로그래밍에 비해 좀 더 번거러운 작업을 요구하는 이유도 여기에 있다.

  • 객체지향 설계의 변화에 효과적으로 대처할 수 있다는 특징 때문이다.

변화에 대응할 수 있는 가장 좋은 대책 : 변화의 폭을 최소한으로 줄여주는 것이다.

  • 즉, 변경이 일어날때 필요한 작업을 최소화 하고,
  • 해당 변경이 다른 곳에 문제를 일으키지 않게 하여야 한다.
  • 이렇게 하기 위해서는 분리와 확장을 고려한 설계가 필요하다.

분리
변경과 발전은 한 번에 한 가지 관심사항에 집중해서 일어난다.

여기서, 개발자는 한 가지 관심이 한 군데에 집중되게 하는 것이다.
즉 관심이 같은 것끼리는 모으고, 관심이 다른 것은 따로 떨어져 있게 하는 것이다. (관심사의 분리)

1.2.2 커넥션 만들기의 추출

UserDao의 관심사항

이전 장의 UserDao의 add() 메소드 하나에서 3 가지 관심사항을 발견할 수 있다.

  1. DB와 연결을 위한 커넥션을 가져오는 방법
    더 세분화 하여, DB 종류, 드라이버 종류, 로그인 정보, 커넥션 생성 방법 등을 정의 해 줘야한다.

  2. Statement를 만들고 실행하는 것
    파라미터로 넘어온 User객체의 정보를 Statement에 바인딩 시키고, 해당 Statement를 DB를 통해 실행시키는 방법이 하나의 관심사항 이다.

  3. 작업 종료 시, 실행 중 사용되었던 리소스를 종료시켜 주는 것

또한, 현재 UserDao에는 예외처리가 되어있지 않다.

중복 코드의 메소드 추출

DB Connection을 가져오는 부분이 UserDao의 add() 와 get() 메소드에 동일한 코드가 중복되어 존재한다.

해당 부분의 코드를 getConnection() 이라는 독립적인 메소드로 만들어 줄 수 이따.

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

public void get(String id) throws ClassNotFoundException, SQLException {
	Connection c = getConnection();
    ...
}

public Connection getConnection() throws ClassNotFoundException, SQLException {
    Class.forName("com.mysql.cj.jdbc.Driver");

    // Connection 을 가져온다.
    Connection c = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/toby?useSSL=false", "root", "password"
    );

    return c;
}

나중에 드라이버 클래스 또는 DataBase URL 정보가 바뀌었을 때, getConnection() 이라는 한 메소드의 코드만 수정하면 된다.

관심이 다른 코드가 있는 메소드에는 영향을 주지도 않을뿐더러, 관심 내용이 독립적으로 존재하므로 수정도 간단해졌다.

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

메소드 추출 기법
특정 기능을 담당하는 여러 곳에서 중복되는 부분을 메소드로 뽑아내는 것

Refactoring : 기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술

getConnection() 메소드의 생성과 같은 Refactoring 후에는 반드시 테스트를 통해 기능에 문제가 없다는 것을 보장 해 주어야 한다.

이전 장의 main() 메소드를 이용한 테스트를 실행해 보면 된다.

  • 현재 main 메소드 테스트는 여러 번 실행 시, 두 번째부터는 무조건 예외가 발생한다.
  • users 테이블의 기본키인 id 값이 중복되기 때문이다.
  • 해결책 : users 테이블의 데이터를 삭제 후 main() 테스트를 수행하면 된다.

1.2.3 DB 커넥션 만들기의 독립

만약 서로 다른 두 클라이언트 N사와 D사가 존재한다고 가정해보자.
이 두 클라이언트는 서로 다른 종류의 DB를 사용한다.

이때, UserDao 소스코드를 N사와 D사에 제공해주지 않고도 각 클라이언트의 DB 종류에 맞춰서 DB 커넥션 생성 방식을 적용해가면서 UserDao를 사용하게 할 수 있을까?

상속을 통한 확장

UserDao 코드를 한 단계 더 분리하면 된다.

  • UserDao 코드의 getConnection()을 추상 메소드로 만들어놓는다.
  • 이렇게 되면, N사와 D사는 UserDao 클래스를 상속받아서, 각각 NUserDao와 DUserDao라는 서브클래스를 만든다.

public abstract class UserDao {
    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = getConnection();
    ...
    }

    public void get(String id) throws ClassNotFoundException, SQLException {
        Connection c = getConnection();
    ...
    }

    public abstract Connection getConnection() throws ClassNotFoundException, SQLException ;
}
public class NUserDao extends UserDao{
    @Override
    public Connection getConnection() throws ClassNotFoundException, SQLException {
    ...
	}
}

public class DUserDao extends UserDao{
    @Override
    public Connection getConnection() throws ClassNotFoundException, SQLException {
    ...
	}
}

위 코드를 통해 관심사를 분리할 수 있다.

  • DAO의 핵심 기능인 어떻게 데이터를 등록하고 가져올 것인가라는 관심사
  • DB 연결 방법에 대한 관심사

이제 UserDao는 단순히 변경이 쉽다는 점을 넘어, 확장 또한 쉽게 가능하다라고 말할 수도 있게 됐다.

  • 새로운 DB연결 방법을 적용 할 때는 UserDao를 상속을 통해 확장해주기만 하면 된다.

템플릿 메서드 패턴
슈퍼클래스에 기본적인 로직의 프름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤, 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법

UserDao의 서브클래스(NUserDao, DUserDao)의 getConnection() 메소드는 어떤 Connection 클래스의 오브젝트를 어떻게 생성할 것인지를 결정하는 방법이라고 볼 수 있다.

  • 이렇게 서브 클래스에서 구체적인 오브젝트 생성 방법을 결정하레 하는 것을 팩토리 메소드 패턴 이라고 부른다.

"UserDao에 팩토리 메소드 패턴을 적용해서 getConnection()을 분리합시다" 라는 의사소통에 익숙해지자.

상속을 통한 관심사 분리의 문제점

  1. 자바는 클래스의 다중 상속을 허용하지 않는다.
    만약 나중에 서브 클래스(NUserDao)가 UserDao 뿐만 아니라, 다른 슈퍼 클래스를 상속해야 한다면 문제가 발생한다.

  2. 상속을 통한 상•하위 클래스의 관계는 생각보다 밀접하다.
    서브클래스는 슈퍼클래스의 기능을 직접 사용할 수 없다.
    그래서 슈퍼클래스 내부의 변경이 있을 때, 모든 서브클래스를 함께 수정해주어야 하는 문제가 발생한다.

  3. 확장된 기능인 DB 커넥션을 생성한느 코드를 다른 DAO 클래스에 적용할 수 없다는 것도 큰 단점이다.

소스코드 : github

좋은 웹페이지 즐겨찾기