본문 바로가기
Learning-log/Spring & JPA

(스프링 핵심 원리 - 기본편) 5-(1)웹 어플리케이션과 싱글톤 ,(2) 싱글톤 패턴, (3) 싱글톤 컨테이너 (4) 싱글톤 방식의 주의점

by why제곱 2023. 4. 3.

- 웹 어플리케이션과 싱글톤

  • 대부분의 스프링 애플리케이션은 웹 어플리케이션. ( 웹 아닌 것도 개발 가능)
  • 웹 어플리케이션은 여러 고객이 동시에 요청
  • 우리가 만든 memberService를 모든 고객마다 new로 객체 생성
  • 계속 요청이 올 때마다 객체를 매번 만들어야 하는 것. 메모리 낭비 심함. => 해결책 : 싱글톤패턴

 

 

- 싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되도록 하는 디자인 패턴
  • 객체 인스턴스를 2개 이상 생성하지 못하도록 해야 함.
    • private생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 함.
  • 싱글턴패턴 설정 방법
    1. static 영역에 instance 미리 하나 생성해서 올려두기
    2. 이 객체가 필요하면 오직 getInstance() 메서드를 통해서만 조회 가능. 이 메서드를 호출하면 static에 올려둔 항상 같은 객체(인스턴스) 반환
    3. 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막기

  • 정말 같은 객체를 반환하는지 확인하기

  • 이제 appConfig에 있던 것들을 싱글톤으로 바꾸면 되겠지 ? 하지만 스프링 컨테이너를 쓰면 스프링 컨테이너가 알아서 모든 객체를 싱글톤으로 만들어서 관리해주므로 그럴 필요가 없다 !! 
  • 싱글톤패턴을 구현하는 방법은 여러가지가 있음!! 
  • 싱글톤 패턴을 적용하면 고객의 요청이 들어올 때마다 객체 생성하는게 아니라 이미 만들어진 객체를 공유해서 효율적으로 사용 가능
  • 싱글톤 패턴의 문제점
    • 코드가 많이 필요함
    • 클라이언트가 구체 클래스에 의존 => DIP 위반
    • 클라이언트가 구체 클래스에 의존해서 OCP 원칙 위반할 가능성이 높음
    • 유연한 테스트가 어려움
    • 내부 속성을 변경, 초기화하기 어려움
    • private생성자로 자식 클래스 만들기가 어려움
    • 유연성 떨어짐
    • 안티패턴이라고도 불림
  • 스프링 프레임워크는 이런 싱글톤 패턴의 단점을 제거하면서 객체를 싱글톤으로 관리해줌 ! 

 

 

-  싱글톤 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리해줌
  • 스프링 컨테이너는 싱글톤 컨테이너의 역할 => 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 함
  • 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 됨
  • DIP, OCP, 테스트, private생성자로부터 자유롭게 싱글톤 패턴 사용 가능
  • 아래는 싱글톤패턴 코드를 작성한 적이 없지만 테스트 코드가 성공적으로 수행됨(같은 객체가 생성됐다는 뜻)

  • 참고! 스프링의 기본 빈 등록 방식 : 싱글톤 but 싱글톤 방식만 지원하는 건 아님. 요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 있음.

 

- 싱글톤 방식의 주의점

  • 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 여러 클라이언트가 같은 객체 인스턴스를 공유해서 쓰기 때문에 싱글톤 객체는 상태를 유지하도록 설계하면 안됨
  • 무상태로 설계해야 함
    • 특정 클라이언트에 의존적인 필드 존재하면 안 됨.
    • 가급적 읽기만 가능해야 하며, 특정 클라이언트가 값을 변경할 수 있는 필드가 존재하면 안 됨.
    • 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 함.
  • 스프링 빈의 필드에 공유값을 설정하면 큰 장애 발생 가능!!!
    • StatefulService의 price필드는 공유되는 필드인데 특정 클라이언트가 값을 변경할 수 있도록 코드가 짜여 있다.
    • 이럴 때, ThreadA가 10000원으로 설정해둔 price값을 B가 20000원으로 바꿀 수 있게 되며, 실무라면 10000원짜리를 구매한 A고객의 카드가 20000원이 긁히는 상황이 발생할 수 있다 => 정말 큰 문제,,
    • 실무에서 이런 문제를 몇년에 한번씩은 꼭 만나고 이로 인해 정말 해결하기 어려운 큰 문제들이 터진다고 하니, 공유필드는 항상 조심하고 스프링 빈은 항상 무상태로 설계해야 한다는걸  명심하자 !!

위 코드를 아래처럼 고쳐야한다 !