public class SingletonTest {
// JUnit 테스트 클래스 선언
@Test
// 테스트 메서드에 대한 설명을 제공
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContainer() {
AppConfig appConfig = new AppConfig();
// AppConfig 클래스의 인스턴스를 생성
MemberService memberService1 = appConfig.memberService();
// memberService() 메서드를 호출하여 MemberService 인스턴스를 생성
MemberService memberService2 = appConfig.memberService();
// 동일한 memberService() 메서드를 다시 호출하여 또 다른 MemberService 인스턴스를 생성
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
// 두 객체의 참조 값을 콘솔에 출력하여 서로 다름을 확인
assertThat(memberService1).isNotSameAs(memberService2);
// memberService1과 memberService2가 서로 다른 객체임을 검증
}
}
싱글톤 패턴(Singleton Pattern)
싱글톤 패턴은 어플리케이션에서 해당 클래스의 인스턴스가 단 하나만 생성되어야 할 때 사용하는 디자인 패턴입니다. 이 패턴을 사용하면 동일한 리소스에 대한 여러 요청에서도 메모리 낭비를 방지하고, 데이터의 일관성을 유지할 수 있습니다.
위 코드에서는 AppConfig의 memberService() 메서드를 호출할 때마다 새로운 MemberService 인스턴스가 생성됩니다. 이는 각 요청마다 새로운 객체를 생성하기 때문에 메모리 사용이 늘어나고, 객체 간의 데이터 공유가 어려워집니다.
싱글톤 패턴을 적용하면, memberService() 메서드는 처음 호출될 때만 MemberService 객체를 생성하고, 이후의 호출에서는 처음 생성된 객체를 재사용합니다. 이 방법은 객체 생성 비용을 절감하고, 애플리케이션 전체에서 일관된 서비스의 상태를 유지할 수 있도록 도와줍니다.
public class SingletonService { // SingletonService 클래스 선언
private static final SingletonService instance = new SingletonService(); // private 접근 제어자를 이용해 외부에서 접근을 제한하고, static과 final을 사용하여 유일한 인스턴스를 생성
public static SingletonService getInstance() { // 인스턴스에 접근할 수 있는 public static 메서드 제공
return instance; // 유일한 인스턴스 반환
}
private SingletonService() { // 생성자를 private으로 선언하여 외부에서 인스턴스 생성을 제한
}
public void logic() { // 실제 서비스 로직을 수행하는 메서드
System.out.println("싱글톤 객체 로직 호출"); // 로직 호출 출력
}
}
자바에서의 static
자바에서 static은 클래스 레벨의 변수나 메서드를 정의하기 위해 사용된다. 이 키워드가 붙은 멤버는 클래스에 속하며, 인스턴스를 생성하지 않고 클래스 이름을 통해 직접 접근할 수 있다. static 멤버는 모든 인스턴스에서 공유되며, 클래스가 메모리에 로드될 때 한 번만 생성된다.
@Test // JUnit 테스트 메서드임을 나타내는 어노테이션
@DisplayName("싱글톤 패턴을 적용한 객체 사용") // 테스트 메서드의 이름 또는 설명을 지정
public void singletonServiceTest() { // 테스트 메서드 선언
//private으로 생성자를 막아두었다. 컴파일 오류가 발생한다.
//new SingletonService(); // 이 코드는 주석 처리된 상태로, 싱글톤 객체를 직접 생성하려 시도할 경우 컴파일 오류를 유발함을 설명
//1. 조회: 호출할 때 마다 같은 객체를 반환
SingletonService singletonService1 = SingletonService.getInstance(); // 싱글톤 인스턴스를 가져오는 메서드 호출, 첫 번째 참조 저장
SingletonService singletonService2 = SingletonService.getInstance(); // 싱글톤 인스턴스를 다시 가져오는 메서드 호출, 두 번째 참조 저장
//참조값이 같은 것을 확인
System.out.println("singletonService1 = " + singletonService1); // 첫 번째 싱글톤 인스턴스의 참조값 출력
System.out.println("singletonService2 = " + singletonService2); // 두 번째 싱글톤 인스턴스의 참조값 출력
// singletonService1 == singletonService2
assertThat(singletonService1).isSameAs(singletonService2); // 두 객체 참조가 동일한지 확인 (실제 같은 객체인지 테스트)
singletonService1.logic(); // 싱글톤 객체의 로직 메서드 호출
}
isSameAs
isSameAs 메서드는 두 객체의 참조가 같은지, 즉 두 객체가 메모리에서 동일한 위치를 가리키는지를 검사한다. 이는 == 연산자를 사용하는 것과 동일하다. 이 메서드는 주로 싱글톤과 같이 객체의 인스턴스가 단 하나만 존재해야 하는 경우에 유용하게 사용된다. 예를 들어, 싱글톤 패턴의 객체가 올바르게 하나만 생성되었는지 확인할 때 isSameAs를 사용할 수 있다.
isEqualTo
isEqualTo 메서드는 두 객체의 값이 같은지를 검사한다. 이는 equals() 메서드를 이용하는 것과 동일하다. isEqualTo는 객체의 내용이 같은지 비교할 때 사용되며, equals() 메서드가 오버라이드된 경우에는 해당 구현에 따라 값의 동등성을 판단한다. 이 메서드는 컬렉션, 문자열, 숫자 등 값의 동등성을 검사할 때 주로 사용된다.