스프링/핵심원리-기본

웹 애플리케이션과 싱글톤

eunkyung 2022. 9. 21. 03:24

*싱글톤 - 객체인스턴스가 자바에 딱 하나 있는 패턴.

 

<웹 애플리케이션과 싱글톤>

스프링은 기업용 온라인 서비스를 지원하기 위해 탄생한 기술.

스프링 애플리케이션은 대부분 웹 애플리케이션(웹 아니어도 개발 가능은 O).

웹 애플리케이션 -> 보통 여러 고객이 동시 요청.

(EX)
클라이언트 A,B,C가 멤버서비스를 스프링에 요청
(기존 코드)-> AppConfig에서 DI 컨테이너가 new MemberServiceImpl(memberRepository())로 멤버 서비스 생성해 반환. A에게 생성한거 반환 / B, C에게 또 생성해서 반환 =>고객 3명이 요청 시 객체 3개가 생성됨. 
웹 애플리케이션은 고객이 계속 요청하는 형식이기에 요청마다 객체 만들어야 함. (비효율적)

 

-요청마다 객체 생성 되는지 테스트

#스프링 사용 안하는 순수한 DI컨테이너

package hello.core.singleton;
import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

public class SingletonTest {

     @Test
     @DisplayName("스프링 없는 순수한 DI 컨테이너")
     void pureContainer() {
         AppConfig appConfig = new AppConfig();

         //1. 조회: 호출할 때 마다 객체를 생성 - 첫번째 요청.
         MemberService memberService1 = appConfig.memberService();

         //2. 조회: 호출할 때 마다 객체를 생성 - 두번째 요청.
         MemberService memberService2 = appConfig.memberService();

         //참조값이 다른 것을 확인 -> 다른 객체인 것을 확인할 수 있음. 
         System.out.println("memberService1 = " + memberService1);
         System.out.println("memberService2 = " + memberService2);

         //memberService1 != memberService2 멤버 1 != 멤버2인 것을 검증.
         //눈으로 확인하는게 아닌 자동화 되게 테스트 코드 만들어야 함. 
         assertThat(memberService1).isNotSameAs(memberService2);
 	}
}
  • 스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청마다 객체 새로 생성.
  • 만약 고객 트래픽이 초당 100이면 ? 초당 100개의 객체가 생성되고 소멸되는 것이기에 메모리 낭비가 심함(실제로는 트래픽이 100보다 크기에 메모리 낭비 심함.)
  • 해결방안: 싱글톤 패턴 - 해당 객체가 1개만 생성되고, 공유하도록 설계 ~>효율적

<싱글톤 패턴>

-클래스 인스턴스가 딱 1개만 생성되게 보장하는 디자인 패턴 

 방법:객체 2개 이상 생성 제한.->private 생성자를 사용해 외부에서 임의로 new 키워드 사용 못하게 해야함. +)싱글톤 패턴 구현 방법은 정말 많음. 그중에 가장 단순하고 안전한 방법임.

package hello.core.singleton;

public class SingletonService {
     //1. static 영역에 객체를 딱 1개만 생성해둔다.
     //자바가 뜰 때 static영역에 new부분이 있으면 내부적으로 실행해서 객체를 생성하고 instance에 참조로 넣어놓음. 
     private static final SingletonService instance = new SingletonService();
     
     //2. public으로 열어서 객체 인스터스가 필요하면 이 static 메서드를 통해서만 조회하도록
    허용한다. 인스턴스의 참조를 꺼낼 수 있는 유일한 방법. 이 메소드 호출 시 항상 같은 인스턴스 반환.
    ->다른 곳에서 new로 객체 생성하지도, 조회하지도 못하게 됨.
     public static SingletonService getInstance() {
     	return instance;
     }
     
     //3. 생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 막는다. 딱 하나의 객체만 생성되게.
     //private은 가능 범위가 이 파일 내에서만 가능. 이 코드를 보고 싱글톤임을 알 수 있음.
     private SingletonService() {
     }
     
     public void logic() {
     	System.out.println("싱글톤 객체 로직 호출");
     }
}

->테스트해보면 두번 요청해도 같은 객체 반환. 하나 만들어두고 반환하는 형태.

+) isSameAs -객체 인스턴스 참조 비교(진짜 넣어서 비교). /  equal은 실제 자바 .equals 메소드로 비교(모든 것을 비교하는 메소드)

 

->appConfig를 싱글톤으로 수정 필요 ? 

  스프링 컨테이너 사용하면 굳이 직접 수정 안해도 됨. 알아서 객체를 싱글톤으로 만들어서 관리해줌. 

 고객 요청 100개 와도 만들어놓은 1개의 객체 재활용(공유)함. 

 

하지만 싱글톤 패턴은 수많은 문제가 있음. 

  • 싱글톤 패턴을 구현하는 코드 자체가 많음. (클래스 개수가 많아지기에)
  • 의존 관계 상 클라이언트가 구체 클래스에 의존. getInstance로 객체 가져오는 것이기에  -> DIP위반, OCP 위반 가능성 높음.
  • 테스트 하기가 어려움.인스터 미리 받아 설정이 먼저 끝나기에 유연하게 테스트하기 어려움.(유연성이 떨어짐)
  • 내부 속성을 변경하거나 초기화 어렵다
  • private 생성자->자식 생성하기 어려움.
  • 결론적으로 유연성이 떨어져 DI 같은 것 적용 힘듬. 

 =>안티 패턴으로 불리기도. 

=>>스프링이 싱글톤 패턴 문제, 단점 다 해결하고, 싱글톤이 적용된 싱글톤 컨테이너 제공.


그럼 어떻게 해결할까. 다음 게시물 확인해주세요!

'스프링 > 핵심원리-기본' 카테고리의 다른 글

복습 / 정리  (0) 2022.11.23
싱글톤 컨테이너  (0) 2022.10.02
스프링 객체지향 설계 적용 (2)  (0) 2022.09.18
스프링에 객체지향 원리 적용  (0) 2022.09.18
스프링 예제  (0) 2022.09.06