1. 예시
만약 DB로부터 정보를 얻어오는 다음과같은 예제를 구현했다고 가정하면, 예제는 정상작동하지만, 메소드의 종류가 늘어나면 Connection 부분이 반복적으로 수행된다. 이는 DB의 정보가 바뀌면 메소드의 개수만큼 수정이 일어나야한다는 단점이 있고, Connection부분이 각 A, B회사가 다르게 구성하고 싶을때 UserDao부분의 수정이 일어나야하므로 적절한 방법으로의 수정이 필요하다.
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 insertUser(User user) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
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());
ps.executeUpdate();
ps.close();
c.close();
}
public User getUserList(String id) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/test?serverTimezone=UTC", "root", "1234");
PreparedStatement ps = c.prepareStatement("SELECT * FROM USERS WHERE id = ?");
ps.setString(1,id);
ResultSet rs = ps.executeQuery();
rs.next();
User user = new User();
user.setId(rs.getString("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
rs.close();
ps.close();
c.close();
return user;
}
}
따라서 제공하는 다음과 같은 구조를 이용하여 코드를 개선한다. ConnectionMaker라는 인터페이스를 통하여 다음과 같이 수정한다.
UserDao
public class UserDao {
private ConnectionMaker connectionMaker;
public UserDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
}
public void insert.....
public User get.....
}
ConnectionMaker interface
public interface ConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException;
}
AConnectionMaker(A회사의 구현 클래스)
public class AConnectionMaker implements ConnectionMaker {
...
public Connection makeConnection() throws ClassNotFoundException, SQLException {
// A회사의 Connection을 생성하는 코드
}
}
2. IOC란
IOC란 Inversion Of Control의 약자로, 제어의 역전이라는 뜻을 가지고 있다.
일반적으로 프로그램은 자신이 사용할 오브젝트를 직접 선택하고, 생성한다
new를 통해 어떤 오브젝트를 생성할건지 선택해 놓음.
private ConnectionMaker connectionMaker = new AConnectionMkaer();
앞선 예제에는 UserDao에서 new를 통해직접적으로 오브젝트를 생성하는대신, 생성자 파라미터를 통해 오브젝트를 선택하게된다. 즉 변화에 유연한 코드를 만들면서 DAO를 수동적인 상태로 변환시켰다.
또한 필요한 오브젝트를 직접 만들지않고, 이후에 나올 Factory가 만들어 준 오브젝트를 전달받게 된다.
즉 제어의 역전이란, 말 그대로 제어권을 역전 시키는 것으로써, 제 3자(Factory)에게 오브젝트에 대한 제어권을 넘겨주고, 자신은 제 3자가 선택하고 생성한 오브젝트를 받아서 사용하는 수동적인 상태가 되는 것을 말한다. 스프링은 IOC를 기반으로 개발자가 활용할 수 있게 해주는 프레임워크이다.
DaoFactory(A, B 회사중 선택할 수 있는 클래스)
public class DaoFactory {
public UserDao userDao() {
ConnectionMaker connectionMaker = new AConnectionMaker(); //A회사 선택
UserDao userDao = new UserDao(connectionMaker); //DI
return userDao;
}
}
다음과 같이 구성하게되면 더이상 UserDao는 수정될 필요없는 부분을 유지하고 수정될 여지가 있는 부분은 인터페이스를 implement한 클래스들이, 어떤 클래스를 UserDao가 사용할지는 Factory부분에서 정해주는것을 확인할 수 있다. 이렇게 interface를 이용한 부분이 IOC를 설명하고 있다.
3. DI / 의존성 주입
DI란 Dependency Injection의 약자로, 의존관계 주입 이라는 의미를 가지고 있다.
의존 관계란 하나의 오브젝트에서 다른 오브젝트를 사용할 때를 말한다. A라는 클래스에서 B라는 클래스를 사용할 경우, A클래스는 B클래스에 의존하고 있다 라고 표현한다. 즉 UserDao는 ConnectionMaker Interface에 의존한다. 따라서 A회사인지 B회사인지의 여부는 알지 못하는 상태이다.
이때 AConnectionMaker클래스를 생성해서 UserDao클래스에 넣어주는 과정, 이를 의존성 주입이라고 보면 된다.
즉 Factory에서 다음과 같은 부분이 DI가 일어나는 부분이다
UserDao userDao = new UserDao(connectionMaker); //DI
4. 정리
기존 IOC 개념 = 객체 new로 생성안하고 다른곳에서 생성해서 사용하는것 정도로 알고 있던것이 명확하게 알게되었고, 앞서 나온 Interface를 사용한 패턴이 전략패턴이라는것을 알게 되었다 전략패턴은 IOC에 기본이 되는 패턴이라 한다.
또한 스프링에서 Factory부분을 컨테이너(ApplicationContext)로 제공하기 때문에 Factory를 통해 의존성 주입을 하지 않아도 된다.
'프로그래밍 > 시큐어코딩' 카테고리의 다른 글
[스프링 이해와 원리] 서비스단에서 트랜잭션 추상화하기 (0) | 2020.07.08 |
---|---|
[스프링 이해와 원리] 전략패턴 이란? 순서2 (0) | 2020.07.08 |
[스프링 이해와 원리] #5. 예외 관리 (0) | 2020.07.07 |
[스프링 이해와 원리] #4. 스프링 의존성 주입 (0) | 2020.07.07 |
[스프링 이해와 원리] #3. 싱글톤 레지스트리와 오브젝트 스코프 순서3 (0) | 2020.07.07 |