스프링/토비의 스프링

토비의 스프링 1장 - 오브젝트와 의존관계

Rudtjs 2023. 4. 7. 19:57

스프링이 가장 관심을 많이 두는 대상은 오브젝트다. 이 오브젝트를 이해해야 스프링을 이해할 수 있다.

이번 장에서는 오브젝트의 설계와 구현, 동작원리를 알아보자.

 

초난감 DAO

public class UserDao {

    public void add(User user) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");

        PreparedStatement ps = c.prepareStatement("insert into users(id, name, password) value(?,?,?)");
        ps.setString(1, user.getId());
        ps.setString(2, user.getName());
        ps.setString(3, user.getPassword());

        ps.executeUpdate();

        ps.close();
        c.close();
    }

    public User get(String id) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");

        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;
    }
}

DAO 클래스를 만들고

main 메서드에서 테스트를 해보자

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        UserDao dao = new UserDao();

        User user = new User();
        user.setId("1");
        user.setName("name_test");
        user.setPassword("pwd_test");

        dao.add(user);

        System.out.println(user1.getId());
    }

 

초난감 DAO 문제점

위의 코드는 실행이 잘되겠지만 많은 문제점을 가지고 있다.

  • 로직이 중복된 코드가 가득하다.
  • DB에 연결된 주소가 바뀐다면 찾아가서 일일이 바꿔줘야 한다.

 

관심사의 분리

 관심이 같은 것끼리 하나의 객체로 모으고, 관심이 다른 것은 서로 떨어뜨려 영향을 주지 않게 해야 한다.

 

중복 코드의 메소드 추출

UserDao에서 DB 커넥션이 중복되고 있는 걸 볼 수 있다.

이걸 하나의 메소드로 추출하여 사용해 보자.

public void add(User user) throws SQLException, ClassNotFoundException {
    // 중복 코드의 메소드 추출
    Connection c = getConnection();

    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 get(String id) throws SQLException, ClassNotFoundException {
    // 중복 코드의 메소드 추출
    Connection c = getConnection();

    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;
}

// DB 커넥션 메소드로 추출하기
public Connection getConnection() throws ClassNotFoundException, SQLException {
   Class.forName("com.mysql.jdbc.Driver");

    String user = "root";
    String password = "1111";

    Connection c = DriverManager.getConnection(
            "jdbc:mysql://localhost/toby_spring"
            , user
            , password
    );
}
  • 코드의 중복을 메서드 형태로 추출하여 관심사를 분리할 수 있었다.
  • 메소드로 중복된 코드를 뽑아내는 것을 리팩토링에서는 메소드 추출이라고 한다.

 

리팩토링 - 상속을 통한 확장

public abstract class UserDao {

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

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

    public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
}
  • 추상 메서드로 작성하여 코드 재작성 없이 변화가 일어나는 부분만 작성하여 코드를 확장할 수 있다.

 

클래스의 분리

public class UserDao {

    SimpleConnectionMaker simpleConnectionMaker;

    public UserDao() {
        this.simpleConnectionMaker = new SimpleConnectionMaker();
    }

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

    public User get(String id) throws SQLException, ClassNotFoundException {
        Connection c = simpleConnectionMaker.makeNewConnection();
		.....
    }
}

public class SimpleConnectionMaker {
    public Connection makeNewConnection() throws ClassNotFoundException, SQLException {
	.....
}
  • 오브젝트를 합성해서 문제를 해결할 수 도 있다.
  • 하지만 하나의 클래스에 의존하기 때문에 다른 문제점을 일으킬 수 있다.

인터페이스 도입

이런 문제를 해결하기 위해서는 중간에 추상적인 느슨한 연결고리를 만드는 것이다.

추상화란 공통적인 성격을 뽑아내 따로 분리해내는 작업이다.

public interface SimpleConnectionMaker {
    Connection makeNewConnection() throws ClassNotFoundException, SQLException;
}

public UserDao(SimpleConnectionMaker simpleConnectionMaker) {
        this.simpleConnectionMaker = simpleConnectionMaker;
}

 인터페이스 타입을 받기 때문에 어떤 클래스라도 이 인터페이스를 구현했다면 받을 수 있다.

 

 

스프링의 IoC

오브젝트 팩토리를 이용한 스프링 IoC

  • 스프링이 제어권을 가지고 관계를 부여하는 오브젝트를 빈(bean) 이라고 한다.
  • 스프링에서 빈의 제어를 담당하는 오브젝트를 빈 팩토리(bean factory) 라고 한다.
  • 빈 팩토리(bean factory)를 확장한 것이 application context 이다. 

 

DaoFactory를 사용하는 애플리케이션 컨텍스트

@Configuration // `애플리케이션 컨텍스트` 혹은 `빈 팩토리`가 사용할 설정 정보라는 표시
public class DaoFactory {

    @Bean // 오브젝트 생성을 담당하는 IoC용 메소드라는 표시
    public UserDao userDao() {
        return new UserDao(getConnectionMaker());
    }

    @Bean
    public DSimpleConnectionMaker getConnectionMaker() {
        return new DSimpleConnectionMaker();
    }
}
  • @Configuration: 빈 팩토리로 인식할 수 있게 해주는 어노테이션이다.
  • @Bean: 빈으로 등록할 수 있게 해주는 어노테이션이다.

 

ApplicationContext

  • ApplicaitonContext는 빈 팩토리를 확장한 IoC 컨테이너다.
  • ApplicaitonContext는 BeanFactory의 모든기능을 가지고 있다.
  • Factory처럼 오브젝트를 단순히 생성해주는 것이 아니라 종합 IoC(제어의 역전) 서비스를 제공해준다.

 

의존관계 주입

  • 제어의 역전(IoC)는 프레임워크 사이에서 매우 폭 넓게 사용되던 용어이다.
  • 스프링에 더욱 알맞은 용어가 새로 나왔고 그것이 의존관계 주입(Dependency Injection)이다.
  • 의존관계 주입은 의존 오브젝트를 사용할 오브젝트와 의존 오브젝트의 관계를 런타임 시 연결해주는 작업을 말한다.
    • 클래스 모델이나 코드에서는 런타임 시점의 의존관계가 드러나지 않아야 한다. 즉, 인터페이스에 의존해야 한다.
    • 런타임 시점의 의존관계는 컨테이너나 팩토리와 같은 제3의 존재가 결정한다.
    • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공해줌으로써 만들어진다.

 

 

'스프링 > 토비의 스프링' 카테고리의 다른 글

토비의 스프링 4장 - 예외  (0) 2023.05.27
토비의 스프링 3장 - 템플릿  (0) 2023.05.14
토비의 스프링 2장 - 테스트  (0) 2023.04.27