728x90

전략 패턴의 구조로 본다면 UserDao가 클라이언트고, 익명의 내부 클래스로 만들어지는 것이 개별적인 전략이고, jdbcContextWithStatementStrategy() 메서드는 컨텍스트다.

jdbcContextWithStatementStrategy()와 같은 컨텍스트 메서드는 다른 Dao에서도 공유가 가능하다.

그렇기에 UserDao만 사용할 것이 아니라 모든 Dao가 사용하도록 만들 수 있다.

 

JdbcContext라는 이름으로 분리해보자.

public class JdbcContext {

    private final DataSource dataSource;

    public JdbcContext(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void workWithStatementStrategy(StatementStrategy statementStrategy) throws SQLException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try{
            connection = this.dataSource.getConnection();
            preparedStatement = statementStrategy.makePreparedStatement(connection);
            
            preparedStatement.executeUpdate();
        }finally {
            if(preparedStatement != null) {try{preparedStatement.close();}catch(SQLException ignored){}}
            if(connection != null) {try{connection.close();}catch(SQLException ignored){}}
        }
    }
}

이렇게 StatementStrategy를 받아서 컨텍스트를 처리해주는 클래스로 분리가 가능하다.

 

이제 UserDao에서도 자체적으로 컨텍스트를 가지고 있는게 아니라 JdbcContext를 가져와서 사용할 수 있다.

public class UserDao {

    private final DataSource dataSource;
    private final JdbcContext jdbcContext;

    public UserDao(
            DataSource dataSource,
            JdbcContext jdbcContext) {
        this.dataSource = dataSource;
        this.jdbcContext = jdbcContext;
    }

    public void add(User user) throws SQLException {
        this.jdbcContext.workWithStatementStrategy(c -> {
            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());

            return ps;
        });
    }
}

하지만 당연히 알고 있을 것이다.

이렇게 하고 실행하면 jdbcContext를 가져올 수 없기에 빈으로 설정을 해주어야 한다.

그렇기에 applicationContext.xml에서 등록을 해주었다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="dataSource"
          class = "org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="1204"/>
    </bean>

    <bean id="jdbcContext"
          class = "seungkyu.JdbcContext">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="userDao" class="seungkyu.UserDao">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="jdbcContext" />
    </bean>


</beans>

 

intellij의 다이어그램을 보니 다음과 같이 나오는 것을 볼 수 있었다.

물론 이제 userDao의 모든 메서드에서 dataSource를 통한 connection을 제거하고, userDao가 dataSource에 의존하지 않도록 만들어야 하지만, add와 deleteAll만 수정해두었다.

 

그럼 이 JdbcContext를 주목해보자.

현재 인터페이스를 사용하지 않고 클래스로 바로 구현하고 있다.

기존의 DI에 맞지 않는 느낌이다.

클래스 레벨에서의 의존관계가 결정되기 때문이다. 의존관계 주입의 개념에 따르면, 인터페이스를 통해 클래스 레벨에서는 의존관계가 생기지 않도록 하고, 런타임 시에 의존할 오브젝트와의 관계를 동적으로 주입해주는 것이 맞다.

그렇기에 인터페이스를 통하지 않았다면 DI라고 볼 수 없을 것이다.

 

이렇게 인터페이스를 통해 클래스를 자유롭게 변경할 수 있도록 하지 않았지만, 그럼에도 이렇게 만든 이유에 대해 생각해보자.

  • JdbcContext를 싱글톤으로 만들기 위해서다.
  • JdbcContext가 DI를 통해 다른 빈에 의존하고 있기 때문이다. (Datasource에 의존)

이러한 장점들 때문에 JdbcContext를 스프링 빈으로 생성한 것이다.

그리고 현재 UserDao와 JdbcContext가 길밀하게 연관되어 있다.

어차피 UserDao는 한상 JdbcContext와 함께 사용되어야 하며, UserDao에 따라 JdbcContext를 수정할 가능성도 높다.

그렇기에 굳이 인터페이스를 두지 않고 이렇게 강력한 결합을 만들어도 되는 것이다.

이런 경우에는 DI의 필요성을 통해 빈으로 등록해도 나쁘지는 않은 생각인 것이다.

 

스프링 빈으로 등록하지 않고 UserDao에 DI 하는 방법 대신 다른 방법도 존재한다.

UserDao 내부에서 직접 DI를 적용하는 방법이다.

public class UserDao {

    private final DataSource dataSource;
    private final JdbcContext jdbcContext;

    public UserDao(
            DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcContext = new JdbcContext(dataSource);
    }
}

이렇게 UserDao의 생성자에서 JdbcContext를 생성하고 가져가는 방법으로 JdbcContext를 이용할 수 있다.

하지만 이 방법도 UserDao와 JdbcContext간에 긴밀한 연결이 생긴다.

그리고 JdbcContext가 싱글톤으로 생성되지 않고 Dao마다 하나씩 생성된다는 문제가 있다.

 

대신 JdbcContext가 UserDao를 내부에서 만들어서 사용하기에 연결관계가 외부로 드러나지 않는다는 장점이 있다.

 

빈으로 등록하기 VS 생성자에서 만들어서 사용하기

 

상황에 맞게 사용하도록 하자(나는 그냥 빈으로 등록해서 사용할 거 같다)

+ Recent posts