토비의 스프링 [1.6장 - 1장 마무리] 발표 스터디 자료

토비의 스프링 [1.6장 - 1장 마무리] 발표 스터디 자료

#toby-spring

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

1.6.1. 싱글톤 레지스트리로서의 애플리케이션 컨텍스트

서버 애플리케이션과 싱글톤

싱글톤
- 어떤 클래스를 애플리케이션 내에서 제한된 인스턴스 개수, 주로 하나만 존재하도록 강제하는 패턴
- 하나만 만들어지는 클래스의 오브젝트는 애플리케이션 내에서 전역적으로 접근 가능
- 단일 오브젝트만 존재, 이를 애플리케이션 여러 곳에서 공유하는 경우 주로 사용

싱글톤의 한계

  • private 생성자를 갖고 있기 때문에 상속할 수 없다.
  • 테스트하기 힘들다.
  • 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
  • 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
public class UserDao {
	private static UserDao INSTANCE;
	...
	private UserDao(ConnectionMaker connectionMaker) {
		this.connectionMaker = connectionMaker;
	}

	public static synchrozied UserDao getInstance() {
		if (INSTANCE == null) INSTANCE = new UserDao(???);
		return INSTANCE;
	}
}
싱글톤 레지스트리
- 기본적인 자바의 싱글톤 패턴은 여러 단점이 있기 때문에, 스프링이 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능
- 스프링 컨테이너에 의해 생성, 관리, 공급
- 평범한 자바 클래스를 싱글톤으로 활용하게 해준다.
- 중요!! 싱글톤 패턴과 달리 스프링이 지지하는 객체지향적인 설계 방식과 원칙, 디자인 패턴등을 적용하는 데 아무런 제약이 없다.

1.6.2. 싱글톤과 오브젝트의 상태

- 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우, 상태정보를 내부에 갖고 있지 않은 무상태(stateless)방식으로 만들어져야 함.
- 읽기전용의 값이라면 초기화 시점에서 인스턴스 변수에 저장해두고 공유하는 것은 아무 문제 없음.
- 상태가 없는 방식으로 클래스를 만드는 경우
	- 각 요청에 대한 정보, DB, 서버의 리소스로부터 생성한 정보는 파라미터와 로컬변수, 리턴 값 등을 이용하여 다룬다.
	- 메소드 파라미터나, 메소드 안에서 생성되는 로컬 변수는 매번 새로운 값을 저장할 독립적인 공간이 만들어지기 때문에 여러 스레드가 변수의 값을 덮어쓸 일이 없다.

1.6.3. 스프링 빈의 스코프

- 스프링 빈이 생성되고, 존재하고, 적용되는 범위
- 스프링에서 만들어지는 대부분의 빈은 싱글톤의 스코프를 갖는다.
- 싱글톤 외의 스코프
	-  프로토타입 스코프
		- 싱글톤과 달리 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만들어준다.
	- 요청 스코프
		- 웹을 통해 새로운 HTTP 요청이 생길 때마다 생성된다.
	- 세션 스코프
		- 웹의 세션과 유사하다.

1.7. 의존관계 주입(DI Dependency Injection)

1.7.1. 제어의 역전(IoC)과 의존관계 주입

IoC
- 객체 생명 관리, 흐름 제어를 제 3자에게 위임하는 프로그래밍 모델
- 동작원리는 의존관계 주입이라 불린다.

DI
- DI는 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심이다.

1.7.2. 런타임 의존관계 설정

의존관계

- 두 개의 클래스 또는 모듈이 의존관계에 있다. -> 방향성을 부여해줘야 한다.
- 아래 그림은 A가 B에 의존하고 있는 관계를 UML로 나타낸 것이다.
- 의존하고 있다: B가 변하면 A에 영향을 미친다.

UserDao의 의존관계

- UserDao가 인터페이스 ConnectionMaker에 의존하고 있는 형태
- ConnectionMaker 인터페이스가 변한다면 그 영향을 UserDao가 직접적으로 받게 된다.
- ConnectionMaker를 구현한 클래스, 즉 DconnectionMaker 등이 다른 것으로 바뀌거나 그 내부에서 사용하는 메소드에 변화가 생겨도 UserDao에 영향 없음.
- 인터페이스에 대해서만 의존관계를 만들어두면 인터페이스 구현 클래스와의 관계는 느슨해지면서 변화에 영향을 덜 받는 상태가 된다.
- 결합도가 낮다고 볼 수 있다.
- UML에서 말하는 의존관계란 이렇게 설계 모델의 관점에서 이야기하는 것

Runtime에서 만들어지는 의존관계

- 설계 시점의 의존관계가 실체화된 것
- 프로그램이 시작되고 UserDao 오브젝트가 만들어지고 나서 런타임 시에 의존관계를 맺는 대상
- 실제 사용대상인 오브젝트를 의존 오브젝트(Dependent Object)라 한다.

의존관계 주입

- 구체적인 의존 오브젝트와 그것을 사용할 주체(클라이언트) 오브젝트를 런타임 시에 연결해주는 작업을 말한다.
- UserDao는 ConnectionMaker 인터페이스 사용하는 조건만 만족하면 어떤 클래스로부터 만들어졌든 상관없이 오브젝트를 받아들이고 사용한다.

의존관계 주입 핵심

- 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다.
인터페이스에만 의존하고 있어야 한다.
- 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
- 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.
- 설계 시점에는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제 3의 존재가 있다는 것.
- DI에서 말하는 제 3의 존재는 관계설정 책임을 가진 코드를 분리해서 만들어진 오브젝트라고 볼 수 있다.
- e.g. 전략패턴에 등장하는 클라이언트, DaoFactory, 스프링의 애플리케이션 컨텍스트, 빈 팩토리, IoC 컨테이너 등이 모두 외부에서 오브젝트 사이의 런타임 관계를 맺어주는 책임을 지닌 제 3의 존재라 볼 수 있다.

UserDao의 의존관계 주입


- 이미 설계 시점에서 구체적인 클래스의 존재를 알고 있는 상황.
- 즉, ConnectionMaker 인터페이스의 관계뿐 아니라 런타임 의존관계, 즉 ConnectionMaker 오브젝트를 사용하겠다는 것까지 UserDao가 결정하고 관리하고 있는 셈이다.
- 문제점 -> 이미 런타임 시의 의존관계가 코드 속에 다 미리 결정되어 있음
- 해결 -> DaoFactory가 제 3의 역할을 해줌으로써 UserDao와 DconnectionMaker가 런타임 의존관계를 정당하게 맺게 해준다.
- 즉, DaoFactory는 두 오브젝트 사이의 런타임 의존관계를 설정해주는 DI 컨테이너로 정의할 수 있다.

DI 컨테이너

- UserDao를 만드는 시점에서 생성자의 파라미터로 DConnectionMaker의 오브젝트의 레퍼런스를 전달한다.
- 자바에서 오브젝트에 무엇인가를 넣어준다는 개념은 메소드를 실행하면서 파라미터로 오브젝트의 레퍼런스를 전달해주는 방법뿐.
- DI는 자신이 사용할 오브젝트에 대한 선택과 생성 제어권을 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다는 점에서 IoC의 개념에 잘 들어맞는다.
- 스프링 컨테이너의 IoC는 주로 의존관계 주입 또는 DI라는 데 초점이 맞춰져 있다.
public class UserDao {
	private ConnectionMaker connectionMaker;

	public UserDao(ConnectionMaker connectionMaker) {
		this.connectionMaker = connectionMaker;
	}
}

1.7.3. 의존관계 검색과 주입

- 외부로부터의 주입이 아니라 스스로 검색을 이용
- 자신이 필요로 하는 의존 오브젝트를 능동적으로 찾는다.
- 어떤 클래스의 오브젝트를 이용할지 결정하지는 않는다.
public UserDao() {
	DaoFactory daoFactory = new DaoFactory();
	this.connectionMaker = daoFactory.connectionMaker();
}
- 이렇게 해도 UserDao는 여전히 자신이 어떤 ConnectionMaker 오브젝트를 사용할지 미리 알지 못한다.
- 런타임 시에 DaoFactory가 만들어서 돌려주는 오브젝트와 다이나믹하게 런타임 의존관계를 맺는다.
- ApplicationContext.getBean() 메소드가 대표적인 사용 사례이다.
public UserDao() {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
	this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class);
}
- 의존관계 검색 방식에서는 검색하는 오브젝트는 자신이 스프링 빈일 필요가 없다.

DI를 원하는 오브젝트는 먼저 자기 자신이 컨테이너가 관리하는 빈이 돼야 한다.

DI 받는다
DI의 동작방식은 외부로부터의 주입
외부에서 파라미터로 오브젝트를 넘겨줬다고 해서, 다 DI가 아니다.
주입받은 메소드 파라미터가 이미 특정 클래스 타입으로 고정되어 있다면 DI가 일어날 수 없다.
DI에서 말하는 주입은 다이나믹하게 구현 클래스를 결정해서 제공받을 수 있도록 인터페이스 타입의 파라미터를 통해 이뤄져야 한다.

1.7.4. 의존관계 주입의 응용

사례

- 개발 환경과 운영 환경의 동적인 환경 세팅에서 사용할 수 있다.
- 부가기능 추가
	- DAO가 DB를 얼마나 많이 연결해서 사용하는지 파악하고 싶다 -> DAO와 DB 커넥션을 만드는 오브젝트 사이에 연결횟수를 카운팅하는 오브젝트 하나 더 추가

1.7.5.메소드를 이용한 의존관계 주입

- 수정자 메소드를 이용한 주입
public class UserDao {
	private ConnectionMaker connectionMaker;

	public void setConnectionMaker(ConnectionMaker connectionMaker) {
		this.connectionMaker = connectionMaker;
	}
}
- 일반 메소드를 이용한 주입 (참고정도로만 인지)

좋은 웹페이지 즐겨찾기