스프링

토비의 스프링 - 3.4 컨텍스트와 DI

초롱불 2021. 6. 18. 06:26

개요

1. JdbcContext의 분리

2. JdbcContext의 특별한 DI

본문

1. JdbcContext의 분리

전략 패턴의 구조로 보자면 UserDao의 메소드가 클라이언트이고, 익명 내부 클래스로 만들어지는 것이 개별적인 전략 jdbcContextwithStatementStrategy() 메소드는 컨텍스트이다. jdbcContextwithStatementStrategy()는 다른 DAO에서도 사용가능하기에 클래스 밖으로 분리 시키는 것으로 활용성을 높일 수 있다.

분리한 클래스를 JdbcContext라고 이름 붙이고 그 메소드를 workWithStatementStrategy로 이름 붙인다. JdbcContext는 DataSource에 의존하고 있으므로 DataSource타입 빈을 DI받을 수 있게 한다.

public class JdbcContext {
	
private DataSource dataSource;//DB커넥션 기능을 가진 제공되는 인터페이스 DataSource인터페이스로 변환
	
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}//수정자 메소드 이용해서 주입
	
	public void workWithStatementStrategy(StatementStrategy stmt) throws SQLException{
		Connection c = null;
		PreparedStatement ps = null;
		
		try {
			 c= dataSource.getConnection();
			 ps = stmt.makePreparedStatement(c);
			 ps.executeUpdate();
		} catch (SQLException e) {
			throw e;
		} finally {
			if ( ps != null) {try {ps.close();} catch (SQLException e) {}}
			if ( ps != null) {try {c.close();} catch (SQLException e) {}}
		}
		
	}
}

UserDao에서도 JdbcContext를 DI받을 수 있도록 수정한다.

public class UserDao {
	...
    
	private JdbcContext jdbcContext;
	
	public void setJdbcContext(JdbcContext jdbcContext) {
		this.jdbcContext = jdbcContext;
	}

	public void add(final User user) throws SQLException{//로컬 클래스에 final로 선언된 User 를 전달할 수 있다.
		
		this.jdbcContext.workWithStatementStrategy(
				new StatementStrategy() {...}
				);
		
	}

	public void deleteAll() throws SQLException {
			
		this.jdbcContext.workWithStatementStrategy(
					new StatementStrategy() {...}
			
					); 
	}
	
	

}

JdbcContext를 추가하고 의존관계를 정리하여 XML설정 파일도 수정한다.

<bean id="dataSource"
		class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
		<property name="driverClass"
			value="com.mysql.jdbc.Driver" />
		<property name="url"
			value="jdbc:mysql://localhost:3306/springbook?allowPublicKeyRetrieval=true&amp;useSSL=false&amp;serverTimezone=Asia/Seoul&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;" />
		<property name="username" value="root" />
		<property name="password" value="1234" />

	</bean>
	<bean id="userDao" class="springbook.user.template9.dao.UserDao">
		<property name="dataSource" ref="dataSource" />
		<property name="jdbcContext" ref="jdbcContext"/>
	</bean>
	<bean id="jdbcContext" class="springbook.user.tempalte9.dao.UserDao">
		<property name="dataSource" ref="dataSource"/>
	</bean>

아직까지 UserDao의 모든 메소드가 JdbcContext를 사용하는 것은 아니기에 dataSource를 주입받고 있다는 것에 주의할 필요가 있다.

2. JdbcContext의 특별한 DI

위와 같은 의존관계 설정은 기존 작업하면서 인터페이스를 이용하지 않고 클래스와 클래스를 곧장 연결하고 있기에 이전에 볼 수 있었던 연결보다 결합도가 높다. DI에 충실할 경우 런타임시 관계가 결정되는 인터페이스를 이용한 느슨한 연결이 옳지만 넓게 보자면 객체의 생성과 관계 설정에 대한 제어권한을 외부로 위임한다는 IOC의 개념을 지니고 있기에 DI의 기본을 따르고 있다고 볼 수 있다. 

인터페이스를 사용하지 않는 이유는 JdbcContext는 Datasource와 달리 다른 구현으로 대체해서 사용할 이유가 없다. 왜냐하면 UserDao와 강한 응집도로 연결되어 있고 만약 Jdbc방식 이외의 방식으로 대체될 경우 완전히 새로운 형식으로 통째로 교체되어야만 하기에 구현체를 통해서 관계를 설정할 필요가 없는 것이다. 그렇기에 클래스 대 클래스로 주입받아 Spring bean으로 등록되었을 때 누릴 수 있는 싱글톤, JdbcContext의 DI관계의 장점을 활용하는 것도 문제는 없다.

 

 

정리

여러 DAO에서 활용 가능한 JdbcConText를 다른 클래스로 분리해보았다. 분리 과정 자체는 1장에서부터 지속적이고 반복적으로 행해졌던 것이기에 어려울 부분이 없었지만 그 결과물이 클래스와 클래스가 직접적으로 의존관계를 형성하고 있기에 그동안 만들어왔던 인터페이스를 이용한 느슨한 관계와는 다른 부분이 있어서 이에 대해서 학습해보았다. 인터페이스를 활용하는 것이 좁은 의미의 DI에 부합하지만 넓은 의미에서 IoC를 따르고 있기에 문제는 없다. 런타임 시에 구현 관계를 설정할 필요가 있는지, 두 클래스 간의 응집도에 대한 판단 근거가 있을 때 클래스와 클래스를 직접 연결하는 DI도 문제가 없다는 것을 확인할 수 있었다.

 

소스 깃허브

 

https://github.com/cholongbul/Tobyspring