1.1 초난감 DAO
•
DAO : DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트
•
자바빈 : 두 가지 관례를 따라 만들어진 오브젝트
◦
디폴트 생성자 : 자바빈은 파라미터가 없는 디폴트 생성자를 가지고 있어야 함. 툴이나 프레임워크에서 리플렉션을 이용해 오브젝트를 생성하기 때문
▪
리플렉션 : 구체적인 클래스 타입을 알지 못해도, 그 클래스의 메소드, 타입, 변수들을 접근할 수 있도록 해주는 Java API
◦
프로퍼티 : 자바빈이 노출하는 이름을 가진 속성. 프로퍼티는 set으로 시작하는 수정자 메소드(setter)와 get으로 시작하는 접근자 메소드(getter)를 이용해 수정 또는 조회 가능
1.2 DAO의 분리
1.2.1 관심사의 분리
•
개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항, 미래의 변화를 어떻게 대비할 것인가
⇒ 객체지향 프로그래밍의 발전 (실세계를 최대한 가깝게 모델링)
◦
코드 변화의 폭을 최소한으로 줄여주기 (변경이 일어날 때 필요 작업 최소화, 문제를 일으키지 않도록 하기)
•
모든 변경과 발전은 한 번에 한 가지 관심사항에 집중되어 일어남 (but, 그에 따른 작업은 한 곳에 집중되지 않는다.)
◦
관심사의 분리
▪
관심이 같은 것끼리 하나의 객체 안으로, 관심이 다른 것은 서로 영향을 주지 않도록 분리하여 같은 관심에 효과적으로 집중하도록 만드는 것
1.2.2 커넥션 만들기의 추출
UserDao의 관심사항 세 가지
1.
DB와 연결을 위한 커넥션을 어떻게 가져올까
2.
사용자 등록을 위해 DB에 보낼 SQL 문장을 담을 Statement를 만들고 실행하는 것
3.
작업 후 Statement와 Connection 오브젝트 닫기
변경사항에 대한 검증: 리팩토링과 테스트
•
리팩토링
◦
기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 작업 또는 기술
◦
생산성 up, 코드 품질 up, 유지보수 용이, 견고하고 유연한 제품 개발 가능 !!
◦
메소드 추출 기법 : 리팩토링에서 공통의 기능을 담당하는 메소드로 중복된 코드를 뽑아내는 것
1.2.3 DB 커넥션 만들기의 독립
문제 가정)
•
사용자가 다른 종류의 DB를 사용하고,
DB커넥션을 가져오는 방법이 종종 변경될 가능성이 있으며,
소스코드를 사용자에게 직접 공개하고 싶지는 않을 때 어떻게 해야될까?!
상속을 통한 확장
•
관심사항을 분리해 상하위 클래스에 나눠 담기
•
데이터를 어떻게 등록하고 가져올 것인가
어떤 기능을 사용하는 지에 관심 ⇒ UserDao(슈퍼클래스)
•
DB 연결 방법은 어떻게 할 것인가
어떤 식으로 기능을 제공하는지에 관심 ⇒ NUserDao, DUserDao(서브클래스)
•
템플릿 메소드 패턴 : 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 메소드로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하는 방법
◦
훅 메소드 : 슈퍼클래스에서 디폴트 기능을 정의해두거나 비워뒀다가 서브클래스에서 선택적으로 오버라이드 할 수 있도록 만들어둔 메소드
•
팩토리 메소드 패턴 : 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것 / 오브젝트 생성 방법을 슈퍼클래스의 기본 코드에서 독립시키는 방법
◦
주로 인터페이스 타입으로 오브젝트를 리턴
◦
서브클래스는 다양한 방법으로 오브젝트를 생성하는 메소드를 재정의 가능
◦
팩토리 메소드 : 서브클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드
디자인 패턴
•
소프트웨어 설계 시 특정 상황에서 자주 만나는 문제를 해결하기 위해 사용할 수 있는 재사용 가능한 솔루션
•
객체지향적인 설계로 문제 해결에 적용할 수 있는 확장성 추구 방법은 클래스 상속, 오브젝트 합성
•
패턴을 적용할 상황, 해결해야 할 문제, 솔루션의 구조와 각 요소의 역할과 핵심 의도가 무엇인지 기억해 두기
상속의 한계점
•
이미 다른 목적을 위해 상속하고 있을 시 다중상속 불가
•
상속을 통한 상하위 클래스의 관계는 too much 밀접함
•
상속 코드가 사용된 클래스들이 계속 만들어진다면 상속을 통해 만들어진 코드가 매 클래스마다 중복돼서 나타나는 문제가 발생함
1.3 DAO의 확장
1.3.1 클래스의 분리
•
완전히 독립적인 클래스로 분리
•
UserDao의 코드가 SimpleConnectionMaker라는 특정 클래스에 종속되어 있기 때문에 상속을 사용했을 때 처럼 UserDao 코드의 수정 없이 DB 커넥션 생성 기능을 변경할 방법이 없다.
•
문제점
1.
SimpleConnectionMaker의 메소드
•
다른 클래스의 메소드명이 다르다면 커넥션을 가져오는 코드를 일일이 변경해야 하는 문제
2.
DB 커넥션을 제공하는 클래스가 어떤 것인지 UserDao가 구체적으로 알고 있어야 한다는 점
•
UserDao에 SimpleConnectionMaker라는 클래스 타입의 인스턴스 변수까지 정의해놓아서, 다른 클래스를 구현하면 UserDao 자체를 수정해야 함
•
⇒ UserDao가 바뀔 수 있는 정보, 즉 DB 커넥션을 가져오는 클래스에 대해 너무 많이 알고 있기 때문 (그 클래스에서 커넥션을 가져오는 메소드는 이름이 뭔지까지 일일이 알고 있어야 한다.)
•
=⇒ 종속적!!!
1.3.2 인터페이스의 도입
•
추상화 : 공통적인 성격을 뽑아내어 따로 분리해내는 작업 ⇒ 인터페이스
•
자신을 구현한 클래스에 대한 구체적인 정보는 모두 감추기 때문에 접근하는 쪽에서 오브젝트를 만들 때 사용할 클래스를 몰라도 됨
•
기능만 정의, 구현방법은 나타나 있지 않음
1.3.3 관계설정 책임의 분리*
•
UserDao와 / UserDao가 사용할 ConnectionMaker의 특정 구현 클래스 사이의 관계를 설정해주는 것에 관한 관심 (=DConnectionMaker, NConnectionMaker)
•
사용되는 오브젝트 ⇒ 서비스 / 사용하는 오브젝트 ⇒ 클라이언트
•
클라이언트 오브젝트에 기능 분리
◦
오브젝트와 오브젝트 사이의 관계를 설정해줘야 함 - ??????????
▪
직접 생성자를 호출해서 직접 오브젝트를 만드는 방법
▪
외부에서 만들어준 것을 가져오는 방법
package main.springbook.user.dao;
import java.sql.SQLException;
/**
* 1.3.3 관계설정 책임의 분리
* - 관계설정 책임이 추가된 메소드
* - 클라이언트 코드
* - UserDao의 변경 없이 다른 사용자가 자유로운 DB 접속 클래스를 만들어 사용 가능
*/
public class UserDaoTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException{
// UserDao가 사용할 ConnectionMaker 구현 클래스를 결정하고 오브젝트 만들기
ConnectionMaker connectionMaker = new DConnectionMaker();
// 1. UserDao 생성
// 2. 사용할 ConnectionMaker 타입의 오브젝트 제공, 결국 두 오브젝트 사이의 의존관계 설정 효과
// *UserDao 클래스에서 생성자 수정함 public UserDao(ConnectionMaker connectionMaker){ - }
UserDao dao = new UserDao(connectionMaker);
}
}
Java
복사
•
상속을 사용해 분리했을 때보다 훨씬 유연함
◦
ConnectionMaker라는 인터페이스를 사용하기만 하면 다른 DAO클래스에도 ConnectionMaker의 구현 클래스들을 적용 가능
◦
DAO가 많아져도 DB 접속 방법에 대한 관심은 오직 한 군데에 집중됨 !!!
◦
서로 영향을 주지 않고 필요에 따라 자유롭게 확장 가능
1.3.4 원칙과 패턴
•
초난감 DAO 코드의 개선 결과를 객체지향 기술의 이론을 통해 설명
개방 폐쇄 원칙
•
'클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다'
•
[그림 1-7] 인터페이스를 통해 제공되는 확장 포인트는 개방, 인터페이스를 이용하는 클래스는 불필요한 변화가 일어나지 않도록 폐쇄되어있음
디자인 패턴 ⇒ 특별한 상황에서 발생하는 문제에 대한 좀 더 구체적인 솔루션
객체지향 설계 원칙 ⇒ 좀 더 일반적인 상황에서 적용 가능한 설계 기준
SOLID 5가지 객체지향 설계 원칙
- SRP(The Single Responsibility Principle) : 단일 책임 원칙
- OCP(The Open Closed Principle) : 개방 폐쇄 원칙
- LSP(The Liskov Substitution Principle) : 리스코프 치환 원칙
- ISP(The Interface Segregation Principle) : 인터페이스 분리 원칙
- DIP(The Dependency Inversion Principle) : 의존관계 역전 원칙
높은 응집도와 낮은 결합도
•
높은 응집도 : 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있음
•
낮은 결합도 : 책임과 관심사가 다른 오브젝트 또는 모듈과 느슨하게 연결된 형태를 유지하는 것 / 하나의 오브젝트가 변경이 일어날 때에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도
◦
서로 독립적이며 알 필요도 없음
◦
변화에 대응하는 속도가 높아짐
◦
구성이 깔끔해짐
◦
확장이 편리함
전략패턴
•
자신의 기능 맥락에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고,
이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴
*알고리즘 : 독립적인 책임으로 분리가 가능한 기능
1.4 제어의 역전(IoC Inversuin of Control)
1.4.1 오브젝트 팩토리
•
클라이언트 UserDaoTest
◦
성격이 다른 책임이나 관심사는 분리 !!
팩토리
1.
분리시킬 기능을 담당할 클래스를 하나 만든다.
2.
이 클래스의 역할은 객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 것인데, 이런 일을 하는 오브젝트를 팩토리라고 부른다.
설계도로서의 팩토리
•
UserDao, ConnectionMaker
◦
각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직 담당
◦
실질적인 로직을 담당하는 컴포넌트
•
DaoFactory
◦
이러한 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있음
◦
애플리케이션을 구성하는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할(어떤 오브젝트 → 어떤 오브젝트를 사용?! 하는지를 정의해놓은 코드)
1.4.2 오브젝트 팩토리의 활용
•
DaoFactory에 UserDao가 아닌 다른 DAO의 생성 기능을 넣으면 어떻게 될까?
⇒ ConnectionMaker 구현 클래스의 오브젝트를 생성하는 코드가 메소드마다 반복되는 문제
⇒ 분리
public class DaoFactory{
public UserDao userDao(){
return new UserDao(new DConnectionMaker());
}
public AccountDao accountDao(){
return new AccountDao(new DConnectionMaker());
}
public MessageDao messageDao(){
return new MessageDao(new DConnectionMaker());
}
// new DConnectionMaker() => ConnectionMaker 구현 클래스를 선정하고 생성하는 코드의 중복 !!
}
Java
복사
분리 전
public class DaoFactory{
public UserDao userDao(){
return new UserDao(connectionMaker());
}
public AccountDao accountDao(){
return new AccountDao(connectionMaker());
}
public MessageDao messageDao(){
return new MessageDao(connectionMaker());
}
public ConnectionMaker connectionMaker(){
return new DConnectionMaker();
// 분리해서 중복을 제거한 ConnectionMaker 타입 오브젝트 생성 코드
// 구현 클래스를 변경하고 싶을 때도 여기만 수정하면 됨
}
}
Java
복사
분리 후
1.4.3 제어권의 이전을 통한 제어관계 역전
•
제어의 역전이란?
◦
프로그램의 제어 흐름 구조가 뒤바뀌는 것
▪
모든 제어 권한(결정, 생성)을 다른 대상에게 위임함
▪
예시
1.
서블릿 : 서블릿에 대한 제어 권한을 가진 컨테이너가 적절한 시점에 클래스 오브젝트를 만들고 그 안의 메소드를 호출
2.
템플릿 메소드 패턴 : 제어권을 상위 템플릿 메소드에 넘기고 자신은 필요할 때 호출되어 사용되도록 함
3.
프레임워크 : 애플리케이션 코드는 프레임워크가 짜놓은 틀에서 수동적으로 동작
4.
책에서 DaoFactory를 도입했던 과정 (ConnectionMaker의 구현 클래스를 결정하고 오브젝트를 만드는 제어권을 넘김)
1.5 스프링의 IoC
스프링의 핵심
•
⇒ 빈 팩토리 / 애플리케이션 컨텍스트
1.5.1 오브젝트 팩토리를 이용한 스프링 IoC
애플리케이션 컨텍스트와 설정정보
•
빈 bean
◦
스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트
•
빈 팩토리 bean factory
◦
빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트
•
애플리케이션 컨텍스트 application context
◦
빈 팩토리와 동일하지만 좀 더 확장된 개념
•
빈 팩토리와 용어가 함께 쓰일 때는
◦
빈 팩토리는 → IoC의 기본기능에 초점
◦
애플리케이션 컨텍스트는 → 애플리케이션 전반에 걸쳐 모든 구성요소의 제어 작업을 담당하는 IoC 엔진이라는 의미가 좀 더 부각
•
설계도
◦
⇒ 애플리케이션 컨텍스트와 그 설정 정보
◦
IoC방식을 이용해 애플리케이션 컴포넌트를 생성하고, 사용할 관계를 맺어주는 등의 책임을 담당
1.5.2 애플리케이션 컨텍스트의 동작방식
•
오브젝트 팩토리 (DaoFactory)
◦
DAO 오브젝트를 생성하고 DB 생성 오브젝트와 관계를 맺어주는 제한적인 역할
•
애플리케이션 컨텍스트
◦
스프링의 가장 대표적인 오브젝트
◦
애플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당
◦
생성, 연관관계 정보를 별도의 설정정보를 통해 얻음
1.
DaoFactory 클래스를 설정정보로 등록
2.
@Bean이 붙은 메소드의 이름을 가져와 빈 목록을 만듦
3.
getBean() 메소드 호출 → 자신의 빈 목록에서 요청한 이름이 찾음.
4.
있다면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에 돌려줌
•
오브젝트 팩토리로 직접 사용 했을 때와 비교하여 애플리케이션 컨텍스트를 사용했을 때 장점
◦
클라이언트는 구체적인 팩토리 클래스를 알 필요 없다
◦
애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다
◦
애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다
▪
getBean() 메소드는 빈의 이름을 이용해 빈을 찾아준다.
타입만으로 빈을 검색하거나 특별한 애노테이션 설정이 되어 있는 빈을 찾을 수도 있다.
1.5.3 스프링 IoC의 용어 정리
•
빈 bean
◦
스프링이 IoC 방식으로 관리하는, 관리되는 오브젝트
◦
애플리케이션에서 만들어지는 모든 오브젝트 중에 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 부름
•
빈 팩토리 bean factory
◦
스프링의 IoC를 담당하는 핵심 컨테이너
◦
빈을 등록, 생성, 조회, 반납, 그 외 부가적인 빈을 관리하는 기능을 담당
•
애플리케이션 컨텍스트 application context
◦
빈 팩토리를 확장한 IoC 컨테이너
◦
빈을 등록, 관리하고 + 스프링이 제공하는 각종 부가 서비스를 추가로 제공
◦
ApplicationContext → 애플리케이션 컨텍스트가 구현해야 하는 기본 인터페이스를 가리킴
◦
ApplicationContext는 BeanFactory를 상속
•
설정정보/ 설정 메타정보
◦
애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보
◦
'configuration' 구성정보, 형상정보
•
컨테이너 또는 IoC 컨테이너
◦
IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고 한다.
◦
컨테이너라는 말 자체가 IoC의 개념을 담고 있기 때문에 스프링 컨테이너라고 부른다.
◦
그냥 '스프링'이라고 부르기도 한다.
•
스프링 프레임워크
◦
IoC 컨테이너, 애플리케이션 컨텍스트를 포함해서 스프링이 제공하는 모든 기능을 통틀어 말할 때 주로 사용
1.6 싱글톤 레지스트리와 오브젝트 스코프
•
오브젝트의 동일성과 동등성
◦
동일한 오브젝트
▪
동일성 비교(identity)
▪
== 연산자
▪
두 개의 오브젝트가 동일하다면,
사실은 하나의 오브젝트만 존재하고 두 개의 오브젝트 레퍼런스 변수를 가짐
◦
동일한 정보를 담고 있는 오브젝트
▪
동등성 비교(equality)
▪
equals() 메소드
▪
메모리상에 각기 다른 오브젝트가 존재
•
DaoFactory의 userDao()를 여러 번 호출했을 때
⇒ 직접 만든 오브젝트 팩토리와 스프링의 애플리케이션 컨텍스트의 동작방식 차이 !!
DaoFactory factory = new DaoFactory();
UserDao dao1 = factory.userDao();
UserDao dao2 = factory.userDao();
System.out.println(dao1);
System.out.println(dao2);
// => 각기 다른 값을 가진 오브젝트임을 알 수 있다.
// => userDao를 호출할 때 마다 새로운 오브젝트 생성
Java
복사
직접 생성한 DaoFactory 오브젝트 출력 코드
ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao3 = context.getBean("userDao", UserDao.class);
UserDao dao4 = context.getBean("userDao", UserDao.class);
System.out.println(dao3);
System.out.println(dao4);
System.out.println(dao3 == dao4); // true
// 두 오브젝트의 출력 값이 같다.
// => 하나의 오브젝트!
Java
복사
스프링 컨텍스트로부터 가져온 오브젝트 출력 코드
1.6.1 싱글톤 레지스트리로서의 애플리케이션 컨텍스트
•
애플리케이션 컨텍스트는
◦
우리가 만들었던 오브젝트 팩토리DaoFactory와 비슷한 방식으로 동작하는 IoC 컨테이너
◦
동시에 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이기도 함
▪
스프링은 기본적으로 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
▪
디자인 패턴의 싱글톤 패턴과 비슷한 개념이지만 구현 방법은 다르다.
▪
*싱글톤이란?
서버 애플리케이션과 싱글톤
•
왜 스프링은 싱글톤으로 빈을 만들까?
◦
스프링이 주로 적용되는 대상이 자바 엔터프라이즈 기술을 사용하는 서버환경이기 때문
◦
서버 과부하 문제 때문에 일찍이 서비스 오브젝트라는 개념을 사용
◦
⇒ 서블릿 : 클래스당 하나의 오브젝트만 만들어두고, 사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 공유해 동시에 사용
•
싱글톤 패턴
◦
애플리케이션 안에 제한된 수, 대개 한 개의 오브젝트만 만들어서 사용하는 것
◦
서버환경에서 사용 권장
◦
but, 문제점이 많은 패턴임 ! ⇒ 안티패턴 이라고 불리기도 함
싱글톤 패턴의 한계
자바에서 싱글톤을 구현하는 방법
•
private 생성자를 갖고 있기 때문에 상속할 수 없다
◦
오직 싱글톤 클래스 자신만이 자기 오브젝트를 만들도록 제한하는 것
◦
객체지향의 장점인 상속과 이를 이용한 다형성을 적용할 수 없음
•
싱글톤은 테스트하기 힘들다
•
서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못함
•
싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못함
◦
싱글톤의 스태틱 메소드를 이용해 언제든지 싱글톤에 쉽게 접근할 수 있기 때문에 애플리케이션 어디서든지 사용될 수 있고,
그러다 보면 자연스럽게 아무 객체나 자유롭게 접근하고 수정하고 공유할 수 있는 전역 상태로 사용되기 쉽다
싱글톤 레지스트리
•
싱글톤 패턴의 구현 방식은 여러 가지 단점이 있기 때문에, 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다.
⇒ 싱글톤 레지스트리
•
스태틱 메소드와 private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글톤으로 활용하게 해준다는 장점
•
평범한 자바 클래스도 IoC 컨테이너를 사용해서 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에 넘기면 손쉽게 싱글톤 방식으로 만들어짐
•
public 생성자를 가질 수 있음
•
테스트도 가능함
•
생성자 파라미터를 이용해서 사용할 오브젝트를 넣을 수도 있음
1.6.2 싱글톤과 오브젝트의 상태
•
싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근하여 사용할 수 있기 때문에 상태 관리에 주의해야 함
◦
무상태 방식 으로 만들어져야 한다.
▪
상태정보를 내부에 갖고 있지 않은 상태
▪
다중 사용자의 요청을 한꺼번에 처리하는 스레드들이 동시에 싱글톤 오브젝트의 인스턴스를 수정하는 것은 매우 위험하다.
저장할 공간이 하나뿐이라서 서로 값을 덮어쓰고 자신이 저장하지 않은 값을 읽어올 수 있기 때문이다. (데이터가 엉망이 될 수 있다)
▪
•
개별적으로 바뀌는 정보는 인스턴스 변수가 아닌 로컬 변수로 정의하거나, 파라미터로 주고받으면서 사용하게 해야 한다.
•
읽기 전용의 정보는 인스턴스 변수를 사용해도 상관없다.
1.6.3 스프링 빈의 스코프
•
스코프 : 스프링이 관리하는 오브젝트, 즉 빈이 생성되고, 존재하고, 적용되는 범위
◦
싱글톤 스코프
◦
프로토타입 스코프
▪
싱글톤과 달리 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만들어줌
◦
요청 스코프
◦
세션 스코프
1.7 의존관계 주입(DI)
1.7.1 제어의 역전(IoC)과 의존관계 주입
•
의존관계 주입 Dependency Injection
◦
스프링 IoC 기능의 대표적인 동작원리
1.7.2 런타임 의존관계 설정
의존관계
•
두 개의 클래스 또는 모듈이 의존관계에 있다고 말할 때는 항상 방향성을 부여해줘야 한다. (누가 누구에게 의존하는 관계에 있다는 식)
A가 B에 의존하고 있음
•
'의존하고 있다' 의 의미
◦
의존대상(B)가 변하면 그것이 A에 영향을 미친다는 뜻
◦
B의 기능이 추가, 변경 혹은 형식이 바뀌면 A에게 영향을 미침
◦
'사용에 대한 의존관계'
▪
A가 B를 사용하는 경우, 예를 들어 A에서 B에 정의된 메소드를 호출해서 사용하는 경우
•
의존관계의 방향성
◦
A가 B에 의존하고 있지만 B는 A에 의존하지 않는다.
◦
즉, B는 A의 변화에 영향을 받지 않는다.
UserDao의 의존관계
•
UserDao가 ConnectionMaker에 의존하고 있는 형태
•
즉, ConnectionMaker 인터페이스 변한다면 UserDao가 영향을 받게 된다.
•
하지만 DConnectionMaker 등이 다른 것으로 바뀌거나 그 내부에서 사용하는 메소드에 변화가 생겨도 UserDao에 영향을 주지 않는다.
•
⇒ 인터페이스에 대해서만 의존관계를 만들어둠으로서, 결합도가 낮아진다.
1.7.3 의존관계 검색과 주입
1.7.5 메소드를 이용한 의존관계 주입
일반 메소드를 이용해 의존 오브젝트와의 관계 주입하기
•
수정자(setter) 메소드를 이용한 주입
◦
외부에서 오브젝트 내부의 애트리뷰트 값을 변경하려는 용도
◦
메소드는 항상 set으로 시작
◦
파라미터로 전달된 값을 보통 내부의 인스턴스 변수에 저장하는 것
•
일반 메소드를 이용한 주입
◦
수정자 메소드와는 달리 한 번에 여러 개의 파라미터를 받을 수 있음
•
자바 코드 보다 XML을 사용하는 경우에 자바빈 규약을 따르는 수정자 메소드가 사용하기 편리함
1.8 XML을 이용한 설정
•
자바 클래스 외의 방법으로 DI 의존관계 설정정보를 만들 수 있는 대표적인 방법 → XML
◦
XML은 단순 텍스트 파일 → 다루기 쉽다.
◦
컴파일 같은 별도의 빌드 작업이 없다.
◦
스키마나 DTD를 이용해서 정해진 포맷을 따라 작성됐는지 쉽게 확인 가능
1.9 정리
•
책임이 다른 코드를 분리해서 두 개의 클래스로 만들었다. (관심사의 분리, 리팩토링)
•
그 중 바뀔 수 있는 쪽의 클래스는 인터페이스를 구현하도록 하고, 다른 클래스에서 인터페이스를 통해서만 접근하도록 만들었다.
⇒ 인터페이스를 정의한 쪽의 구현 방법이 달라져 클래스가 바뀌더라도, 그 기능을 사용하는 클래스의 코드는 같이 수정할 필요가 없다. (전략 패턴)
•
이를 통해 불필요한 변화가 발생하지 않도록 막아주고, 자신이 사용하는 외부 오부젝트의 기능은 자유롭게 확장하거나 변경할 수 있게 만들었다. (개방 폐쇄 원칙)
•
==⇒ 낮은 결합도와 높은 응집도를 가진 깔끔한 코드를 만들었다.
•
오브젝트가 생성되고 여타 오브젝트와 관계를 맺는 작업의 제어권을 별도의 오브젝트 팩토리를 만들어 넘김 /
오브젝트 팩토리의 기능을 일반화한 IoC 컨테이너로 넘겨서 오브젝트가 자신이 사용할 대상의 생성이나 선택에 관한 책임으로부터 자유롭게 만들어줬다. (제어의 역전/IoC)
•
전통적인 싱글톤 패턴 구현 방식의 단점을 살펴보고,
서버에서 사용되는 서비스 오브젝트로서의 장점을 살릴 수 있는 싱글톤을 사용하면서도 싱글톤 패턴의 단점을 극복하도록 설계된 컨테이너를 활용하는 방법에 대해 알아봤다. (싱글톤 레지스토리)
•
설계 시점과 코드에는 클래스와 인터페이스 사이의 느슨한 의존관계만 만들어 놓고,
런타임 시에 실제 사용할 구체적인 의존 오브젝트를 제 3자(DI 컨테이너)의 도움으로 주입받아서 다이내믹한 의존관계를 갖게 해주는 IoC의 특별한 케이스를 알아봤다. (의존관계 주입/DI)
•
의존 오브젝트를 주입할 때 생성자를 이용하는 방법과 수정자 메소드를 이용하는 방법을 알아봤다. (생성자 주입과 수정자 주입)
•
XML을 이용해 DI 설정정보를 만들고 의존 오브젝트가 아닌 일반 값을 외부에서 설정해서 런타임 시에 주입하는 방법을 알아봤다. (XML 설정)













