ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 1장) 1.3 DAO의 확장 ~ 1.5 스프링의 IoC
    Java & Spring/토비의 스프링 3.1 2021. 5. 17. 15:03
    반응형

    1장 오브젝트와 의존관계

    1.3 DAO의 확장

    • 모든 오브젝트는 관심사가 바뀔 때마다 변경이 일어난다.
    • 앞서 팩토리 메소드 패턴을 통해 변화의 성격이 다른 것을 분리해서, 서로 영향을 주지 않고 독립적으로 변경하도록 리팩토링을 했다.
    • 이번 장에서는 상속관계가 아닌 완전한 독립 클래스로 만들어보자.
      • public class UserDao {
            private SimpleConnectionMaker;
        
            public UserDao() {
                simpleConnectionMaker = new SimpleConnectionMaker();
            }
        
            public void add(User user) throws ClassNotFoundException, SQLEXception {
                Connection c = simpleConnectionMaker.makeNewConnection();
                // ...
            }
        }
      • public class SimpleConnectionMaker {
            public Connection makeNewConnection() throws ClassNotFoundException, SQLException {
                Class.forName("com.mysql.jdbc.Driver");
                Connection c = DriverManager.getConnection("", "", "");
                return c;
            }
        }
      • 코드를 확실하게 분리했지만, 기존 N사와 D사에 UserDao 클래스만 공급하고 DB 커넥션은 상속으로 확장해서 사용하던게 불가능해졌다.
    • 위의 코드를 자유롭게 확장하려면 두 가지 문제를 해결해야 한다.
      • 첫째는 SimpleConnectionMaker의 메소드
        • Connection을 얻는 메소드의 네이밍을 통일해야한다. (다른 클래스에서 널리 이용하기 위함)
      • 둘째는 DB 커넥션을 제공하는 클래스가 어떤 것인지를 UserDao가 구체적으로 알고있어야 한다는 점(불필요한 관심)
        • UserDao가 바뀔 수 있는 정보(DB 커넥션을 가져오는 클래스)에 대해 많이 알고 있기 때문에 UserDao를 수정해야하는 경우가 발생한다.
    • 인터페이스를 도입해서 두 클래스 간 추상적인 느슨한 연결고리를 만들어 주자.
      • 인터페이스는 어떤 일을 하겠다는 기능만 정의해두고 구현 방법은 나타나 있지 않다.
      • public interface ConnectionMaker {
            public Connection makeConnection() throws ClassNotFoundException, SQLException;
        }
      • public class DConectionMaker implements ConnectionMaker {
            public Connection makeConnection() throws ClassNotFoundException, SQLException {
                // D사의 Connection 로직
            }
        }
      • public class UserDao() {
            private ConnectionMaker connectionMaker;
        
            public UserDao() {
                connectionMaker = new DConnectionMaker();
            }
            public void add(User user) throws ClassNotFoundException, SQLException {
                Connection c = connectionMaker.makeConnection();
            }
        }
      • 위의 코드로 인해 DB 접속 클래스를 다시 만든다고 해도 UserDao코드를 고칠일이 없어졌다.
      • 하지만, 여전히 DConnection 클래스의 생성자를 호출해서 오브젝트를 생성하는 코드가 남아있어서 확장에 자유롭지 못하다.
    • 관계설정 책임의 분리
      • 위의 코드를 독립적인 확장에 자유롭게 하기 위해서는, UserDao와 UserDao가 사용할 ConnectionMaker의 특정 구현 클래스 사이의 관계를 설정해주는 것에 관한 관심을 분리해야한다.
      • UserDao의 코든 코드는 ConnectionMaker 인터페이스 외에는 어떤 클래스와도 관계를 가져서는 안되게 해야, UserDao의 수정 없이 DB 커넥션 구현 클래스를 변경할 수 있다.
      • 외부에서 만든 오브젝트를 전달받기 위해 메소드 파라미터나 생성자 파라미터를 이용하자.
      • public UserDao(ConnectionMaker connectionMaker) {
            this.connectionMaker = connectionMaker;
        }
      • 생성자를 통해 connectionMaker 오브젝트를 받아 ConnectionMaker의 구현에 관심이 사라졌다.
    • 원칙과 패턴
      • SOLID 5 원칙 (객체지향 설계 5원칙)
        • 단일 책임 원칙(The Single Responsibility Principle => SRP)
          • 모든 클래스는 하나의 책임만 가져야하고, 그 책임을 완전히 캡슐화 해야한다.
        • 개방 폐쇄 원칙(The Open Closed Priciple => OCP)
          • 확장에는 열려있고 수정에는 닫혀야한다.
        • 리스코프 치환 원칙(The Liskov Subsitution Principle => LSP)
          • 자식 클래스는 언제나 부모 클래스를 대체할 수 있어야한다.
        • 인터페이스 분리 원칙(The Interface Segregation Principle => ISP)
          • 하나의 일반적인 인터페이스 보다는 여러 개의 구체적인 인터페이스로 구성해야한다.
        • 의존관계 역전 원칙(The Dependency Inversion Principle => DIP)
          • 의존 관계를 맺을 때는 구체적인 클래스보다 인터페이스나 추상 클래스와 관계를 맺어야 한다.(변화가 없는 것에 의존)
      • 높은 응집도와 낮은 결합도 (개방 폐쇄 원칙과 연관)
        • 응집도가 높다
          • 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중
          • 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다 (모듈의 많은 부분이 함께 바뀐다.)
        • 결합도가 낮다.
          • 책임과 관심사가 다른 오브젝트 or 모듈과는 느슨하게 연결된 형태를 유지하는 것이 바람직하다.
          • 관계 유지에 최소한만 간접적인 형태로 제공하고, 나머지는 서로 독립적이고 알 필요도 없게 하라
          • 결합도 = 하나의 오브젝트가 변경될 때, 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도
      • 전략 패턴
        • 디자인 패턴의 꽃
        • 자신의 기능 맥락(Context)에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스로 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔 사용할 수 있게하는 디자인 패턴
        • 위의 예시로 보면, UserDao가 전략 패턴의 Context에 해당

    1.4 제어의 역전(IoC)

    • UserDao와 ConnectionMaker 구현 클래스의 오브젝트를 만드는 것과, 이 둘을 연결해서 사용하도록 관계를 맺어주는 관심을 예시에 추가해보자.
    • 팩토리
      • 객체의 생성 방법을 결정하고 만들어진 오브젝트를 돌려주는 오브젝트
      • public class DaoFactory() {
            public UserDao userDao() {
                return new UserDao(connectionMaker());
            }
        
            public accountDao() {
                return new AccountDao(connectionMaker());
            }
        
            private ConnectionMaker connectionMaker() {
                return new DConnectionMaker();
            }
        }
      • UserDao와 ConnectionMaker는 데이터 로직과 기술 로직(실질적 로직)을 담당하고, DaoFactory는 Application의 오브젝트를 구성하고 관계를 정의하는 책임(설계도 역할)을 하게 된다.
    • 제어권 이전을 통한 제어 관계 역전
      • 제어의 역전 => 프로그램의 제어 흐름 구조가 뒤바뀌는 것
        • 일반적으로 main() 메소드와 같이 프로그램 시작 지점에서 다음에 사용할 오브젝트 결정 ~> 생성 ~> 메소드 호출 ~> 메소드 내부에서 다음에 사용할 것을 결정 ~> 호출 의 반복이다.
      • 제어의 역전
        • 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지도 생성하지도 않는다.
        • 자신도 어떻게 만들어지고 어디서 사용되는지 알 수 없다.
        • 엔트리 포인트를 제외한 모든 오브젝트는 위임받은 제어 권한을 갖는 특별한 오브젝트에 의해 결정되고 만들어진다.
      • 프레임워크가 제어의 역전 개념이 적용된 대표 기술이다.

    1.5 스프링의 IoC

    • 스프링의 핵심은 Bean Factory, Application Context
    • 애플리케이션 컨텍스트와 설정정보
      • bean
        • 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트
        • 오브젝트 단위의 애플리케이션 컴포넌트
        • 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트
      • bean factory
        • 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트
        • 빈을 생성하고 관계를 설정하는 IoC의 기본 기능에 초점을 둘 때, bean factory 라는 명칭을 주로 활용(생성과 제어의 관점)
      • Application Context
        • 빈 팩토리를 확장한 IoC 컨테이너
        • 애플리케이션 전반에 걸쳐 모든 구성요소의 제어 작업을 당당하는 IoC 엔진의 의미가 좀 더 부각됨(스프링이 지원하는 애플리케이션의 지원 기능을 포함한 의미)
        • 보통, bean factory와 Application Context를 같게보고 어디에 초점을 두고 말하는지에 따라 다르게 부를 때도 있음
    • 앞서 작성했던 DaoFactory를 스프링 빈 팩토리가 사용할 수 있는 설정정보로 만들어보자.
      • @Configuration // application context or bean factory가 사용할 설정정보라는 표시
        public class DaoFactory {
            @Bean // 오브젝트 생성을 담당하는 IoC용 메소드라는 표시
            public UserDao userDao() {
                return new UserDao(connectionMaker());
            }
        
            @Bean
            public ConnectionMaker connectionMaker() {
                return new DConnectionMaker();
            }
        }
      • Test 코드에서 이를 활용할 때는, AnnotationConfigApplicationContext를 이용해서 Application Context 객체를 생성하고 getBean 메소드를 통해 UserDao를 불러오면 된다.
    • Application Context의 동작 방식
      • ApplicationContext 인터페이스를 구현하는데, 빈 팩토리가 구현하는 BeanFactory 인터페이스를 상속했으므로 일종의 Bean Factory이다.
      • DaoFactory는 DAO 오브젝트 생성과 DB 오브젝트와 관계를 맺어조는 제한적인 역할을 하는데, Application Context는 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계 설정을 담당한다.
      • 즉, 클라이언트의 요청이 들어오면 application context에서 bean factory 조회/호출/등록 등을 통해 관리하고, 오브젝트를 넘겨준다.
      • Application Context의 장점
        • 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
          • 애플리케이션 발전에 따라 오브젝트가 추가되어도 클라이언트가 직접 알 필요가 없다.
        • 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
          • 부가적인 기능과 빈이 사용할 수 있는 기반기술 서비스, 외부 시스템과의 연동 등을 컨테이너 차원에서 제공
        • 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.

    용어 정리

    • 설정정보 / 설정 메타정보
      • IoC를 적용하기 위해 사용하는 메타정보, IoC 컨테이너에 의해 관리되는 애플리케이션 오브젝트를 생성하고 구성할 때 사용
      • 애플리케이션의 형상 정보 / 청사진
    • 컨테이너 or IoC 컨테이너
      • IoC방식으로 빈을 관리한다는 의미에서 Application Context나 빈 팩토리를 컨테이너 or IoC 컨테이너라고 한다.
      • IoC 컨테이너는 주로 빈 팩토리 관점에서 이야기하는 것, 그냥 컨테이너 or 스프링 컨테이너라고 할 때는 애플리케이션 컨텍스트를 가리키는 것
    • 스프링 프레임워크
      • IoC 컨테이너, 애플리케이션 컨텍스트를 포함해서 스프링이 제공하는 모든 기능을 통틀어 말할 때 주로 사용
    반응형

    댓글

Designed by Tistory.