아래 글은 토비의 스프링을 읽고 공부하였습니다.
목차
1. IoC란
1) 팩토리
2) IOC(제어의 역전)
3) 프레임워크와 라이브러리
2. 스프링의 IoC
1) Bean과 Bean Factory
2) 애플리케이션 컨텍스트와 동작방식
3) 직접 만든 애플리케이션 컨텍스트와 object factory의 차이점
3. 싱글톤
1) 싱글톤으로 만드는 이유
2) 싱글톤 패턴의 한계
3) 싱글톤 레지스트리
4) 싱글톤 사용 시 주의할 점
1. 오브젝트 팩토리
1. 팩토리
앞에서 우리는 UserDaoTest에 DB 연결에 대한 책임(ConnectionMaker 구현 클래스)을 던져버렸다. 이번에는 이 책임을 DaoFactory로 분리해보자.
public class DaoFactory {
public UserDao userDao(){
ConnectionMaker connectionMaker = new NConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
return userDao
}
}
팩토리의 메소드는 UserDao 타입의 오브젝트를 어떻게 만들고, 어떻게 준비시킬지를 결정한다. 즉, UserDao의 생성 책임을 맡은 클래스이다.
오브젝트 팩토리(위에서 DaoFactory)는 중복 문제 해결에도 큰 장점을 갖는데, 아래의 코드를 살펴보자.
public class DaoFactory {
public UserDao userDao() {
return new UserDao(new DConnectionMaker());
}
public AccountDao userDao() {
return new UserDao(new DConnectionMaker());
}
public UserDao userDao() {
return new MessageDao(new DConnectionMaker());
}
}
------- 변화 코드 -----------
public class DaoFactory {
public UserDao userDao() {
return new UserDao(connectionMaker());
}
public ConnectionMaker connectionMaker() {
return new DConnectionMaker();
};
}
2. 제어의 역전
DtoFactory 방식으로 이 제어의 역전을 설명해볼 수 있다. 우리가 직관적으로 코드를 짤 때, A 메소드를 만들고, A에서 B를 필요로 한다면 B를 적고, 불러오고 .. 이런 '호출'의 방식을 많이 사용한다.
제어의 역전은 위의 제어 흐름을 거꾸로 뒤집는 것이다. 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 당연히 생성하도 않는다. 또 자기도 어디에서 만들어지고 사용되는지 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다. 우리가 알고 있는 서블릿, JSP 처럼 컨테이너 안에서 동작하는 구조 또한 제어의 역전 개념이 적용되어 있다.
그럼 위에서 본 DtoFactory를 다시 한 번 보면, 원래 DB ConnectionMaker의 구현 클래스를 결정하고 오브젝트를 만드는 제어권은 UserDao에게 있었으나 지금은 DaoFactory에게 있다. UserDao는 DaoFactory가 만들고 초기화해서 자신에게 사용하도록 공급해주는 ConnectionMaker 클래스 만을 사용할 수밖에 없다. 더욱이 위의 사진에서 볼 수 있듯 UserDao와 ConnectionMaker의 구현체를 생성하는 책임도 DaoFactory가 맡고 있다.
3. 프레임워크와 라이브러리
우리가 흔히 쓰는 프레임워크와 라이브러리에도 이 제어의 역전 개념을 찾아볼 수 있다.
"불러온다"라는 말과 함께 쓰이는 이 라이브러리 개념은 개발에 필요한 것들을 미리 구현해놓은 도구이다. C++ 의 STL과 같은 것을 예로 들 수 있다. 하지만 프레임워크, 즉 Java spring 같은 것들은 '능동적으로 우리가 직접 사용하고 끌어다 쓸 수 있는 라이브러리'와 달리, 프레임워크가 흐름을 주도하는 중에 개발자가 애플리케이션 코드를 사용한다.
2. 스프링의 IoC
1. Bean과 Bean Factory
그럼 이제 스프링에서는 IoC 개념이 어디에 들어있는지 알아보자! 스프링에서는 스프링이라는 이 프레임워크가 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(Bean)이라고 부른다. 또한 이 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리(Bean Factory), 또는 애플리케이션 컨텍스트(application context)라고 부른다.
2. 애플리케이션 컨텍스트와 동작방식
- @Configuration을 클래스에 붙이면, 해당 클래스가 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스라고 알릴 수 있다.
- @Bean을 객체 생성 메소드에 붙이면, 해당 메소드가 생성한 객체를 스프링 빈으로서 활용할 수 있다.
앞에서 보았던 DaoFactory는 Dao 객체를 생성하고, 해당 Dao와 DB ConnectionMaker 간의 관계를 맺어주는 제한적인 역할을 했다. 반면, 애플리케이션 컨텍스트는 애플리케이션에서 IoC를 적용해 관리할 모든 오브젝트에 대한 생성과 관계 설정을 담당한다.
단, 애플리케이션 컨텍스트는 직접적인 관계 작성 코드는 없고, 생성정보와 연관관계 정보는 별도의 설정정보를 통해서만 얻는다. 때로는 외부 오브젝트 팩토리에 그 작업을 위임하고 그 결과를 가져다가 사용하기도 한다,
3. 직접 만든 오브젝트 팩토리와 스프링의 애플리케이션 컨텍스트의 차이점
직접 만든 오브젝트 팩토리에서 userDao를 매번 출력하면 항상 새로운 오브젝트가 만들어지지만 스프링 컨텍스트에서 출력하면, 여러번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다. 엇 왜지??
3. 싱글톤
애플리케이션 컨텍스트는 우리가 만들었던 오브젝트 컨텍스트와 비슷한 방식으로 동작하는 IoC 컨테이너이다. 동시에 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이기도 하다. 스프링은 빈을 싱글톤으로 만든다.
1. 싱글톤으로 만드는 이유
매번 클라이언트에서 요청이 올 때마다 각 로직을 담당하는 오브젝트를 새로 만들어서 사용한다고 해보자. 요청 한 번에 5개의 오브젝트가 새로 만들어지고 초당 500개의 요청이 들어오면, 초당 2500개의 새로운 오브젝트가 생성된다. 아무리 자바의 오브젝트 생성과 컬렉션(GC)의 성능이 좋아졌다고 하더라도 이렇게 부하가 걸리면 서버가 감당하기 힘들다.
따라서 애플리케이션 안에 제한된 수, 대개 한 개의 오브젝트만 만들어서 사용하는 것이 싱글톤 패턴의 원리이다.
**주의** 객체 생성과 인스턴스 생성을 헷갈리지 말자!
public class Person {
...
}
Person sample1 = new Person();
Person sample2= new Person();
"객체"가 아니라 "인스턴스" Person 의 구조를 한 번만 생성한다는 것을 의미한다. sample1과 sample2가 다른 객체라는 것은 싱글톤 패턴과 연관이 없다.
객체란 클래스에 선언된 모양 그대로 생성된 실체를 말하며 '클래스의 인스턴스'라고 부른다.
즉, 클래스는 '인스턴스의 틀'이고, 인스턴스는 클래스를 통해서 구현해야 할 대상(객체)이 실제로 구현된 구체적인 실체를 의미한다 !! 인스턴스랑 객체 같은 개념 아님! ❌❌
2. 싱글톤 패턴의 한계
- private 생성자를 가지고 있어 상속할 수 없다.
- 싱글톤은 테스트하기가 힘들다.
- 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
- 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
3. 싱글톤 레지스트리
위에서 볼 수 있듯 서버환경에서 싱글톤이 만들어져 서비스 오브젝트 방식으로 사용되는 것은 좋다. 하지만 자바로 싱글톤 패턴을 구현하는 방식은 2번과 같이 여러 한계를 가지고 있다. 따라서 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능인 싱글톤 레지스트리를 제공한다.
싱글톤 레지스트리를 통해 우리는 여러 싱글톤 객체를 한 곳에서 관리하고 액세스할 수 있다. static, private를 사용해야 하는 비정상적 클래스가 아닌 평범한 자바 클래스도 싱글톤으로 활용하게 해준다. 따라서 테스트 환경에서도 자유롭게 오브젝트를 만들거나 목 오브텍트로 대체해서 만들 수도 있는 것이다.
4. 싱글톤 사용 시 주의할 점
객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다. 즉 stateless하게 설계해야 한다!
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다
- 가급적 읽기만 가능해야 한다.
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.'
아래 예시와 같이(출처) 인스턴스 틀 자체를 공유자원으로 사용하지 않고, 상태를 가진 필드를 최소화하거나 사용하지 않는 방식으로 설계하면 동기화 문제 등에 걸리지 않을 수 있다.
public class StatefulService {
private int price; // 공유필드
public void order(String name, int price){
System.out.println("name = " + name);
System.out.println("price = " + price);
this.price = price; // 값을 넣어주고
}
public int getPrice(){
return price; // 그 값을 부르게 하면?
}
}
-------------수정된 코드--------------------
public class StatefulService {
//private int price; // 공유필드는 사용하지말자
public int order(String name, int price){
System.out.println("name = " + name);
System.out.println("price = " + price);
//this.price = price; // 값을 넣는 행위 금지, 읽기만 하자
return price; // 호출된 메소드 안에서 결과를 넘기자.
}
/*
public int getPrice(){
//return price; // 사용하지말자
}*/
}
'BackEnd > JAVA\SPRING' 카테고리의 다른 글
[SPRING] DI, 템플릿과 콜백 (0) | 2023.09.22 |
---|---|
[SPRING] DI와 XML을 이용한 설정 (0) | 2023.09.05 |
[SPRING] DAO의 분리와 확장 (5) | 2023.09.03 |
[OPEN SOURCE] FOSSLIGHT 오픈소스 기여 (0) | 2023.08.21 |
[SPRING+JAVA] Whisper API와 ChatGPT API 연동 (2) | 2023.06.29 |