February-13-2021
Comments
1장 오브젝트와 의존관계
- 초난감 DAO : 세 가지 관심 사항이 한 곳에 있음
- DB와 연결을 위한 커넥션을 가지고 오는 코드
- 쿼리문 코드
- 리소스 회수 코드
- DAO의 분리
- 관심사의 분리(Separation of Concerns)
- 객체지향 설계와 프로그래밍이 이전의 절차적 프로그래밍 패러다임에 비해 초기에 좀 더 많은 번거로운 작업을 요구하는 이유는 객체지향 기술 자체가 지니는, 변화에 효과적으로 대처할 수 있다는 기술적인 특징 때문이다. 객체 지향 기술은 흔히 실세계를 최대한 가깝게 모델링해낼 수 있기 때문에 의미가 있다고 여겨진다. 하지만 그보다는 객체지향 기술이 만들어내는 가상의 추상세계 자체를 효과적으로 구성할 수 있고, 이를 자유롭고 편리하게 변경, 발전, 확장시킬 수 있다는 데 더 의미가 있다.
- 관심이 같은 것끼리는 하나의 객체 안으로 또는 친한 객체로 모이게 하고, 관심이 다른 것은 가능한 한 따로 떨어져서 서로 영향을 주지 않도록 분리하는 것
- 커넥션 관련 코드를 메소드 추출(extract method) 리팩토링 방법 사용
- 커넥션 관련 코드가 사이트마다 다양할 경우, 추상 클래스를 만들어서 커넥션 관련 코드를 추상 메소드로 만듬 : 템플릿 메소드 패턴(template method pattern)
- getConnection() 이라는 메소드는 Connection 클래스의 오브젝트를 어떻게 생성할 것인지를 결정하는 방법이라고도 볼 수 있음. 하위 클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 방식 : 팩토리 메소드 패턴(factory method pattern)
- 상속 단점 : 상속은 다중상속을 허용하지 않으므로, 후에 다른 목적으로 상속을 적용하기 힘들다. 또 다른 문제는 상속을 통한 상하위 클래스의 관계를 생각보다 밀접하다는 점이다. 상속을 통해 관심이 다른 기능을 분리하고, 필요에 따라 다양한 변신이 가능하도록 확장성도 줬지만 여전히 상속관계는 두 가지 다른 관심사에 대해 긴밀한 결합을 허용한다. 서브클래스는 슈퍼클래스의 기능을 직접 사용할 수 있으므로 슈퍼클래스 내부의 변경이 있을 때 서브클래스를 함께 수정하거나 다시 개발해야 할 수도 있다.
- DAO의 확장
- 클래스의 분리
- 상속관계가 아닌 완전히 독립적인 클래스로 나눔
- UserDao에서 SimpleConnectionMaker를 인스턴스 변수로 관리
- 하지만, SimpleConnectionMaker을 다른 클래스로 대체하려고 하면 UserDao에서 SimpleConnectionMaker 이 부분을 변경해야 함.
- 다시 정리하여 두 가지 문제점
- 첫번째 : 우리는 makeNewConnection() 을 사용해 DB 커넥션을 가져오게 했는
데, 만약 D 사에서 만든 DB 커넥션 제공 클래스는 openConnection() 이리는 메소드 이름을 사용했다면 UserDao 내에 있는 add (). get () 메소드의 커넥션을 가져오는 코드를 다음과 같이 일일이 변경해야 한다. 지금은 메소드가 단 두 개이니 상관없지만, 수십, 수백 개가 되면 그때는 작업의 양이 너무 커진다.
- 두번째 : DB 커넥션을 제공하는 클래스가 어떤 것인지를 UserDao가 구체적으
로 알고 있어야 한다는 점이다. UserDao에 SimpleConnectionMaker라는 클래스 타입의 인스턴스 변수까지 정의해놓고 있으니, N 사에서 다른 클래스를 구현하면 어쩔 수 없이 UserDao 자체를 다시 수정해야 한다.
- 따라서 인터페이스를 사용한다.
- 인터페이스의 도입
- 인터페이스를 사용하여 위 문제를 해결할 수 있음
- 하지만, UserDao에서 어떤 구현체를 사용할지에 대한 소스가 그대로 남아있음
- connectionMaker = new DConnectionMaker();
- 관계설정 책임의 분리
- UserDao가 어떤 구현체를 사용할지도 하나의 관심사이므로, 이를 분리해야 함.
- UserDao가 어떤 구현체를 사용할지는 UserDao를 사용하는 쪽에서 설정을 해주자.
- 원칙과 패턴
- 개방 폐쇄 원칙(Open-closed Principle) : 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
- 높은 응집도와 낮은 결합도(high coherence and low coupling)
- 응집도가 높으면, 변화가 일어날 때 변화는 부분이 크기때문에 한번에 확인할 수 있으므로, 어디를 고쳐야할지도 알기쉬우며 테스트도 그 해당부분만 하면 됨
- 오브젝트의 변경이 일어날 때에 관계를 맺고 있는 관심사가 다른 오브젝트에게 변화를 요구하는 정도(결합도)가 낮아야 함.
- UserDaoTest-UserDao-ConnectionMaker 구조는 전략 패턴(Strategy Pattern)d이다.
- 전략 패턴은 자신의 기능 맥락에서 필요해 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴이다.
- 제어의 역전(IoC)
- 오브젝트 팩토리
- UserDaoTest는 테스트하려고 만든 것인데, UserDao가 어떤 구현체를 사용할지 결정하는 기능도 맡게 되버림. 그러므로, 어떤 구현체를 사용할지 결정하도록 클래스를 만들어보자.
- 객체의 생성 방법을 결정하고, 그렇게 만들어진 오브젝트를 돌려주는것을 팩토리(factory)라고 부른다. DaoFactory를 만들어, UserDao 오브젝트를 돌려주는 메소드를 만든다.
- DaoFactory는 컴포넌트의 구조와 관계를 정의한 설계도와 같은 역할을 함.
- 오브젝트 팩토리의 활용
- 여러 Dao를 생성할 때, 중복되는 코드는 메소드로 뽑아내자.
- 제어권의 이전을 통한 제어관계 역전
- 일반적으로는 모든 오브젝트가 능동적으로 자신이 시용할 클래스를 결정하고 언제 어떻게 그 오브젝트를 만들지를 스스로 관장한다. 모든종류의 작업을 사용하는쪽에서 제어하는구조다.
- 제어의 역전이란 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다.
- 제어의 역전은 여러곳에서 사용되어 왔다.
- 서블릿, JSP, EJB처럼 컨테이너 안에서 동작하는 구조
- 템플릿 메소드 패턴도, 서브클래스에서 구현한 메소드가 언제 호출될지 자신은 모르고, 슈퍼 클래스가 필요할 때 호출되어 사용되도록 한다는 방식도 제어의 역전
- 프레임워크는 라이브러리와 다르게 애플리케이션 코드가 프레임워크에 의해 사용되므로 제어의 역전 개념이 적용되어 있음.
- 스프링 없이도 IoC 개념을 적용해 보았다. IoC는 기본적으로 프레임워크만의 기술도 아니고 프레임워크가 꼭 필요한 개념도 아니다.
- 제어의 역전에서는 프레임워크 또는 컨테이너와 같이 애플리케이션 컴포넌트의 생성과 관계설정, 사용, 생명주기 관리 등을 관장하는 존재가 필요하다. 스프링은 IoC를 모든 기능의 기초가 되는 기반기술로 삼고 있으며, IoC를 극한까지 적용하고 있는 프레임워크다.
- 스프링의 IoC
- 오브젝트 팩토리를 이용한 스프링 IoC
- @Configuration과 @Bean을 사용하여 DaoFactory를 스프링 설정정보로 사용할 수 있음
- 애플리케이션 컨텍스트의 동작방식
- 애플리케이션 컨텍스트는 DaoFactory 클래스를 설정정보로 등록해두고 @Bean이 붙은 메소드의 이름을 가져와 빈 목록을 만들어둔다. 클라이언트가 요청하면, 해당 이름이 있는지 찾고, 있다면 빈을 생성하는 메소드를 호출해서 생성 후 돌려준다.
- 오브젝트 팩토리와의 차이점
- 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다. 애플리케이션 컨텍스트는 팩토리가 많아져도, 애플리케이션 컨텍스트가 관리하기 때문에
- 애플리케이션 컨텍스트는 다양한 IoC 서비스를 제공해준다.
- 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다. getBean()뿐만 아니라, 타입 또는 애노테이션만으로도 찾을 수 있다.
- 스프링 IoC 용어 정리
- 빈(bean) : 스프링이 IoC 방식으로 관리하는 오브젝트
- 빈 팩토리(bean factory) : 스프링의 IoC를 담당하는 핵심 컨테이너. 빈 팩토리를 바로 사용하지 않고 이를 확장한 애플리케이션 컨텍스트를 이용한다.
- 애플리케이션 컨텍스트(application context) : 빈 팩토리를 확장한 IoC 컨테이너다.
- 설정정보/설정 메타정보(configuration metadata) : IoC를 적용하기 위해 사용하는 메타정보
- 컨테이너(container) 또는 IoC 컨테이너 : IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고도 한다.
- 싱글톤 레지스트리와 오브젝트 스코프
- 싱글톤 레지스트리로서의 애플리케이션 컨텍스트
- 싱글톤은, private 생성자를 갖고 있기 때문에 상속할 수 없고, 테스트하기 힘들며, JVM이 여러 개로 분산될 경우, 하나만 만들어지는 것을 보장하지 못하며 전역상태이기 때문에 바람직하지 못함
- 그렇기 때문에 스프링에서는 직접 싱글톤으로 관리하는 싱글톤 레지스트리를 제공
- 싱글톤과 오브젝트의 상태
- 싱글톤이라도 자원을 공유하기 때문에 공유자원에 대해 조심히 잘 사용해야 함.
- 스프링 빈의 스코프
- 스프링이 관리하는 빈이 생성되고, 존재하고, 적용되는 범위를 스코프(scope)라고 함
- 싱글톤 스코프 : 한 개의 오브젝트로만
- 프로토타입 스코프 : 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트
- 요청 스코프 : 웹을 통해 새로운 HTTP 요청이 생길때마다 생성되는
- 세션 스코프 : 웹의 세션과 유사한 세션
- 의존관계 주입(DI)
- 제어의 역전(IoC)과 의존관계 주입(DI)
- IoC는 매우느슨하게 정의돼서 폭넓게 사용되는 용어이기 때문에 좀 더 스프링의 의도가 명확히 드러나는 DI 라는 명칭을 스프링에서 사용
- 런타임 의존관계 설정
- A가 B에 의존한다. B가 변하면 그것이 A에 영향을 미친다.
- B에 영향을 덜 받기 위해 느슨하게 인터페이스를 두게 함.
- DI는 자신이 사용할 오브젝트에 대한 선택과 생성 제어권을 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다는 점에서 IoC의 개념에 잘 들어맞는다.
- 의존관계 검색과 주입
- 의존관계 주입 뿐만 아니라, 의존관계를 맺는 방법을 스스로 검색을 이용하는 방법인 의존관계 검색이 있다.
- getBean()등이 검색에 사용됨
- 의존관계 주입의 응용
- 기능 구현의 교환 : 다른 오브젝트를 주입
- 부가기능 추가 : 좀더 발전된 오브젝트를 주입
- 메소드를 이용한 의존관계 주입
- 생성자가 아닌 일반 메소드를 이용해 주입도 가능
- 수정자 메소드를 이용한 주입 : setter를 이용, 파라미터 1개
- 일반 메소드를 이용한 주입 : 여러개의 파라미터
- XML을 이용한 설정
- XML 설정
- XML을 이용하는 애플리케이션 컨텍스트
- GenericXmlApplicationcontext를 사용함 : 클래스패스 뿐만 아니라 다양한 소스로부터 설정파일을 읽어 옴. root부터 지정
- new GenericXmlApplicationContext(“springbook/user/dao/daoContext.xml”);
- ClassPathXmlApplicationContext는 XML 파일을 클래스패스에서 가져올 때만 사용하며 좀더 다양한 기능을 제공. 같은 패키지에 있는 클래스를 함께 넣어주면, 상대적으로 지정 가능
- new ClassPathXmlApplicationContext(“daoContext.xml “, UserDao.class );
- DataSource 인터페이스로 변환
- DB 커넥션을 가져오는 오브젝트의 기능을 추상화하여 사용할 수 있는 DataSource 인터페이스
- 자바 코드 설정 방식
- XML 설정 방식은 다음장 참조
- 프로터피 값의 주입
- XML 설정 방식에서 레퍼런스 값뿐만 아니라 일반 값도 주입할 수 있다.
- value 값은 스트링이지만, 자동으로 알맞은 형태로 변환해준다.