Post

토비의 스프링 1장

1. 오브젝트와 의존관계

1.1 초난감 DAO

  • 자바빈
    • 디폴트 생성자: 자바빈은 파라미터가 없는 디폴트 생성자를 가지고 있어야 한다. 툴이나 프레임워크에서 리플렉션을 이용해 오브젝트를 생성하기 때문에 필요하다.
    • 프로퍼티: 자바빈이 노출하는 이름을 가진 속성, getter/setter로 수정 또는 조회 가능

1.2 DAO의 분리

  • 1.2.1 관심사의 분리
    • 오브젝트에 대한 설계와 이를 구현한 코드가 변하기 때문에 개발자가 객체를 설계할 때 미래의 변화를 어떻게 대비할 지 염두해 두어야 한다.
    • 분리와 확장을 고려하여 미래의 변화에 대비한다.
    • 모든 변경과 발전은 한 번에 한 가지 관심사항에 집중해서 일어난다.
    • 문제는, 변화는 대체로 집중된 한 가지 관심에 대해 일어나지만 그에 따른 작업은 한 곳에 집중되지 않는 경우가 많다. ex) DB접속용 암호를 변경하기 위해 DAO클래스 수백개 수정
    • 그래서 관심이 같은 것끼리는 모으고, 관심이 다른 것은 떨어져 있게 해야 한다. (관심사의 분리)
  • 1.2.2 커넥션 만들기의 추출
    • UserDao의 관심사항
      1. DB와 연결을 위한 커넥션 가져오기
      2. 사용자 등록을 위해 DB에 보낼 SQL문장 실행
      3. 사용한 리소스 반환
    • 중복 코드의 메소드 추출
      • 관심사항 1인 DB와 연결부분 코드를 추출
    • 변경사항에 대한 검증: 리팩토링과 테스트
      • 리팩토링: 기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술
      • 리팩토링을 한 뒤 테스트를 하여 변경사항을 검증
  • 1.2.3 DB 커넥션 만들기의 독립
    • UserDao를 사용하지만 공개하지 않고 다른 DB를 사용하고 싶을 때 문제가 생김(N사와 D사가 구매를 한다면??)
    • 상속을 통한 확장
      • UserDao에서 구현 코드를 제거하고 getConnection()을 추상 메소드로 전환
      • DAO의 핵심 기능인 어떻게 데이터를 등록하고 가져올 것인가(SQL, 파라미터 바인딩, 쿼리 실행, 검색정보 전달)라는 관심을 담당하고 있는 UserDao와, DB연결 관심을 담고 있는 NUserDao, DUserDao가 클래스 레벨로 분리
    • 템플릿 메소드 패턴: 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하도록 하는 방법
    • 팩토리 메소드 패턴: 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것
      • 상속을 사용했기 때문에 다중상속을 허용하지 않는 단점이 있고, 상하위 클래스 간의 관계가 밀접하다는 단점이 있다.

1.3 DAO의 확장

  • 1.3.1 클래스의 분리
    • 상속이 아닌 다른 클래스로 분리한 뒤, UserDao에서 이용
    • 하지만, UserDao의 코드가 SimpleConnectionMaker 클래스에 종속되어 있기 때문에 N사와 D사에서 기능을 확장해서 사용하는 것이 불가능
  • 1.3.2 인터페이스의 도입
    • 두 개의 클래스가 서로 긴밀하게 연결되어 있지 않도록 중간에 추상적인 느슨한 연결고리를 만들어주는 것 => 인터페이스 도입
    • 하지만 생성자에서 클래스의 이름이 드러나기 때문에 여전히 문제임
  • 1.3.3 관계설정 책임의 분리
    • UserDao가 어떤 ConnectionMaker 구현 클래스의 오브젝트를 이용하게 할지를 결정하는 것에 관심이 있다.
    • 생성자 파라미터를 사용하여 해결
      1
      2
      3
      
        public UserDao(ConnectionMaker connectionmaker) {
        this.connectionMaker = connectionMaker;
        }
      
    • 런타임 오브젝트에서 의존 관계를 해결해 줌
  • 1.3.4 원칙과 패턴
    • 개방 폐쇄 원칙: 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
    • 높은 응집도와 낮은 결합도
      • 높은 응집도 : 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다. 만약 모듈 일부분에만 변경이 일어나도 된다면, 어떤 부분이 바뀌는지 알아야 하기 때문에 힘들고, 다른 영향을 미치는지 파악해야하기 때문에 힘들다.
      • 낮은 결합도: 책임과 관심사가 다른 오브젝트 또는 모듈과는 느슨하게 연결된 형태를 유지하는 것이 바람직하다(하나의 오브젝트가 변경이 일어날 때에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도)
    • 전략 패턴: 자신의 기능 맥락(context)에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴

1.4 제어의 역전(IOC)

  • 1.4.1 오브젝트 팩토리
    • UserDaoTest는 UserDao의 기능이 잘 동작하는지를 테스트하려고 만든 것인데 다른 책임을 맡고있으므로 분리가 필요하다.
    • 팩토리: 객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 오브젝트
  • 1.4.2 오브젝트 팩토리로의 활용
    • DaoFactory에서 UserDao가 아닌 다른 DAO의 생성 기능을 넣으면 오브젝트를 생성하는 부분의 코드가 반복되기 때문에 분리가 필요하다.
    • 그래서 오브젝트 생성 코드를 분리한다.
  • 1.4.3 제어권의 이전을 통한 제어관계 역전
    • 일반적인 프로그램의 흐름은 main() 메소드와 같이 프로그램이 시작되는 지저메서 다음에 사용할 오브젝트를 결정하고, 결정한 오브젝트를 생성하고, 만들어진 오브젝트에 있는 메소드를 호출하고, 그 오브젝트 메소드 안에서 다음에 사용할 것을 결정하고 호출하는 식의 작업이 반복된다.
    • 제어의 역전이란 이런 제어 흐름의 개념을 거꾸로 뒤집는 것이다.
    • 제어의 역전에서는 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다.
    • 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어하고, 필요한 기능이 있을 때 능동적으로 라이브러리를 사용한다. 반면에 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다.

1.5 스프링의 IOC

  • 1.5.1 오브젝트 팩토리를 이용한 스프링 IOC
    • 애플리케이션 컨텍스트와 설정정보
      • 빈: 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트
      • 빈 팩토리(어플리케이션 컨텍스트): 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트
    • DaoFactory를 사용하는 애플리케이션 컨텍스트
      • @Configuration: 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스
      • @Bean: 오브젝트를 만들어주는 메소드
      • getBean(): ApplicationContext가 관리하는 오브젝트를 요청하는 메소드
    • 1.5.2 애플리케이션 컨텍스트의 동작방식
      • @Configuration이 붙은 DaoFactory는 이 애플리케이션 컨텍스트가 활용하는 IoC 설정정보
      • 내부적으로는 애플리케이션 컨텍스트가 DaoFactory의 userDao() 메소드를 호출해서 오브젝트를 가져온 것을 클라이언트가 getBean()으로 요청할 때 전달
      • DaoFactory를 오브젝트 팩토리로 직접 사용했을 때와 비교해서 애플리케이션 컨텍스트를 사용했을 때 얻을 수 있는 장점
        1. 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
        2. 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
        3. 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.
    • 1.5.3 스프링 IoC 용어 정리
      • 빈: 스프링이 IoC방식으로 관리하는 오브젝트
      • 빈 팩토리: 스프링의 IoC를 담당하는 핵심 컨테이너(빈 등록, 생성, 조회 …)
      • 애플리케이션 컨텍스트: 빈 팩토리를 확장한 IoC컨테이너(빈 팩토리 + 스프링의 부가적인 기능)
      • 설정정보/설정 메타정보: IoC를 적용하기 위해 사용하는 메타정보
      • 컨테이너: 애플리케이션 컨텍스트나 빈 팩토리를 가리킴
      • 스프링 프레임워크: 스프링이 제공하는 모든 기능을 통틀어 말할 때 사용

1.6 싱글톤 레지스트리와 오브젝트 스코프

  • 오브젝트의 동일성과 동등성
    • 동일성(==): 두 개의 오브젝트가 동일하다면 사실은 하나의 오브젝트만 존재하는 것이고 두 개의 레퍼런스 변수를 가지고 있는 것이다.
    • 동등성(equals): equals() 메소드가 같아야 동등하다고 판단
  • 스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다. 매번 new에 의해 새로운 UserDao가 만들어지지 않는다.
  • 1.6.1 싱글톤 레지스트리로서의 애플리케이션 컨텍스트
    • 스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
    • 서버 애플리케이션과 싱글톤
      • 애플리케이션 안에 제한된 수, 대개 한 개의 오브젝트만 만들어서 사용
    • 싱글톤 패턴의 한계
      • private 생성자를 갖고 있기 때문에 상속할 수 없다.
      • 싱글톤은 테스트하기가 힘들다.
      • 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
      • 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
    • 싱글톤 레지스트리
      • 자바의 기본적인 싱글톤 패턴의 구현 방식은 여러가지 단점이 있기 때문에, 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다.
      • 평범한 자바 클래스로 IoC방식의 컨테이너를 사용해서 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리 가능
    • 1.6.2 싱글톤과 오브젝트의 상태
      • 기본적으로 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 한다.
    • 1.6.3 스프링 빈의 스코프
      • 기본 스코프는 싱글톤
      • 프로토타입 스코프: 빈을 요청할 때마다 새로운 오브젝트 생성
      • 요청 스코프, 세션 스코프…

1.7 의존관계 주입(DI)

  • 1.7.1 제어의 역전과 의존관게 주입

    • DI는 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심이다.
  • 1.7.2 런타임 의존관계 설정
    • 의존한다는 것은 의존대상이 변하면 의존하는 것에 영향을 미친다는 뜻이다.
      • ex) A가 B를 사용하는 경우
    • 의존 관계에는 방향성이 있다.
    • UserDao의 의존관계
      • UserDao가 ConnectionMaker에 의존하고 있는 형태
      • 프로그램이 시작되고 UserDao 오브젝트가 만들어지고 나서 런타임 시에 의존관계를 맺는 대상, 즉 실제 사용대상인 오브젝트를 의존 오브젝트라고 한다.
      • 의존관계 주입은 구체적인 의존 오브젝트와 그것을 사용할 주체, 보통 클라이언트라고 부르는 오브젝트를 런타임 시에 연결해주는 작업을 의미
      • 의존관계 주입의 3가지 조건
        • 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스에만 의존하고 있어야 한다.
        • 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
        • 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.
      • UserDao의 의존관계 주입
        • DI 컨테이너에 의해 런타임 시에 의존 오브젝트를 사용할 수 있도록 그 레퍼런스를 전달받는 과정이 마치 메소드(생성자)를 통해 DI 컨테이너가 UserDao에게 주입해주는 것과 같다고 해서 의존관계 주입이라고 부른다.
  • 1.7.3 의존관계 검색과 주입
    • 의존관계 검색: 외부로부터의 주입이 아니라 스스로 검색을 이용
      • 의존관계 검색은 런타임 시 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트의 생성 작업은 외부 컨테이너에게 IoC로 맡기지만, 이를 가져올 때는 메소드나 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용
        1
        2
        3
        4
        
          public UserDao() {
          DaoFactory daoFactory = new DaoFactory();
          this.connectionMaker = daoFactory.connectionMaker();
          }
        
      • 의존관계 검색 방식에서는 검색하는 오브젝트는 자신이 스프링의 빈일 필요가 없다, 하지만, 의존관계 주입에서는 검색하는 오브젝트도 스프링의 빈 오브젝트여야 한다.
  • 1.7.4 의존관계 주입의 응용
    • 기능 구현의 교환
      • 개발환경과 운영환경에서 DI의 설정정보에 해당하는 부부만 다르게 만들어두면 나머지 코드에 전혀 손대지 않고 해결할 수 있다.
    • 부가기능 추가
  • 1.7.5 메소드를 이용한 의존관계 주입
    • 수정자(setter) 메소드를 이용한 주입
    • 일반 메소드를 이용한 주입

1.8 XML을 이용한 설정

  • 1.8.1 XML 설정
    • <beans>를 루트 엘리먼트로 사용 (@Configuration)
    • <bean>(@Bean)
    • <property>를 사용해 의존 오브젝트와의 관계 정의
  • 1.8.3 DataSource 인터페이스로 변환
    • DataSource 인터페이스 적용
      • 자바에서는 DB커넥션을 가져오는 오브젝트의 기능을 추상화해서 비슷한 용도로 사용할 수 있게 만드렁진 DataSource라는 인터페이스가 이미 존재
This post is licensed under CC BY 4.0 by the author.