- 웹 어플리케이션과 싱글톤
- 대부분의 스프링 애플리케이션은 웹 어플리케이션. ( 웹 아닌 것도 개발 가능)
- 웹 어플리케이션은 여러 고객이 동시에 요청
- 우리가 만든 memberService를 모든 고객마다 new로 객체 생성
- 계속 요청이 올 때마다 객체를 매번 만들어야 하는 것. 메모리 낭비 심함. => 해결책 : 싱글톤패턴
- 싱글톤 패턴
- 클래스의 인스턴스가 딱 1개만 생성되도록 하는 디자인 패턴
- 객체 인스턴스를 2개 이상 생성하지 못하도록 해야 함.
- private생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 함.
- 싱글턴패턴 설정 방법
- static 영역에 instance 미리 하나 생성해서 올려두기
- 이 객체가 필요하면 오직 getInstance() 메서드를 통해서만 조회 가능. 이 메서드를 호출하면 static에 올려둔 항상 같은 객체(인스턴스) 반환
- 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막기
- 정말 같은 객체를 반환하는지 확인하기
- 이제 appConfig에 있던 것들을 싱글톤으로 바꾸면 되겠지 ? 하지만 스프링 컨테이너를 쓰면 스프링 컨테이너가 알아서 모든 객체를 싱글톤으로 만들어서 관리해주므로 그럴 필요가 없다 !!
- 싱글톤패턴을 구현하는 방법은 여러가지가 있음!!
- 싱글톤 패턴을 적용하면 고객의 요청이 들어올 때마다 객체 생성하는게 아니라 이미 만들어진 객체를 공유해서 효율적으로 사용 가능
- 싱글톤 패턴의 문제점
- 코드가 많이 필요함
- 클라이언트가 구체 클래스에 의존 => DIP 위반
- 클라이언트가 구체 클래스에 의존해서 OCP 원칙 위반할 가능성이 높음
- 유연한 테스트가 어려움
- 내부 속성을 변경, 초기화하기 어려움
- private생성자로 자식 클래스 만들기가 어려움
- 유연성 떨어짐
- 안티패턴이라고도 불림
- 스프링 프레임워크는 이런 싱글톤 패턴의 단점을 제거하면서 객체를 싱글톤으로 관리해줌 !
- 싱글톤 컨테이너
- 스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리해줌
- 스프링 컨테이너는 싱글톤 컨테이너의 역할 => 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 함
- 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 됨
- DIP, OCP, 테스트, private생성자로부터 자유롭게 싱글톤 패턴 사용 가능
- 아래는 싱글톤패턴 코드를 작성한 적이 없지만 테스트 코드가 성공적으로 수행됨(같은 객체가 생성됐다는 뜻)
- 참고! 스프링의 기본 빈 등록 방식 : 싱글톤 but 싱글톤 방식만 지원하는 건 아님. 요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 있음.
- 싱글톤 방식의 주의점
- 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 여러 클라이언트가 같은 객체 인스턴스를 공유해서 쓰기 때문에 싱글톤 객체는 상태를 유지하도록 설계하면 안됨
- 무상태로 설계해야 함
- 특정 클라이언트에 의존적인 필드 존재하면 안 됨.
- 가급적 읽기만 가능해야 하며, 특정 클라이언트가 값을 변경할 수 있는 필드가 존재하면 안 됨.
- 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 함.
- 스프링 빈의 필드에 공유값을 설정하면 큰 장애 발생 가능!!!
- StatefulService의 price필드는 공유되는 필드인데 특정 클라이언트가 값을 변경할 수 있도록 코드가 짜여 있다.
- 이럴 때, ThreadA가 10000원으로 설정해둔 price값을 B가 20000원으로 바꿀 수 있게 되며, 실무라면 10000원짜리를 구매한 A고객의 카드가 20000원이 긁히는 상황이 발생할 수 있다 => 정말 큰 문제,,
- 실무에서 이런 문제를 몇년에 한번씩은 꼭 만나고 이로 인해 정말 해결하기 어려운 큰 문제들이 터진다고 하니, 공유필드는 항상 조심하고 스프링 빈은 항상 무상태로 설계해야 한다는걸 명심하자 !!
위 코드를 아래처럼 고쳐야한다 !