HwangHub

Singleton 디자인 패턴 본문

DEV-STUDY/Design Pattern

Singleton 디자인 패턴

HwangJerry 2024. 1. 18. 10:39

스프링에서는 singleton 디자인 패턴을 기본적으로 사용하고 있다. 이게 도대체 뭐길래 스프링 프레임워크에서 기본 룰로 채택하고 있고, 어떤 장점이 있을까?

객체의 생성 제어

싱글톤 디자인 패턴은 불필요한 객체 생성을 제어하기 위해 등장하였다. 어떤 객체가 불필요할까?

객체는 세상에 있는 사물 뿐만 아니라 특정 행위나 논리 등 세상의 모든 것이 객체화될 수 있다고 하였다. 이를 이해하는 게 중요하다. 자, 그럼 생각을 해 보자. "일기 쓰기"라는 객체가 있다고 해 보자. 일기 쓰기 객체는 아래 코드와 같이 간단히 표현해보겠다.

public class DiaryWriting {

    // 일기를 작성하는 메서드
    public void writeDiary(String title, String body) {
        System.out.println(title);
        System.out.println(body);
    }
}

최대한 단순하게 표현한 거라 약간 존재 의미가 의심스러운 객체로 보이지만, 하고 싶은 말은 다음과 같다.

수정할 필드값이 있는 것도 아니고, 기능만 있는데, 저 메서드를 사용하기 위해서 매 실행할 떄 마다 객체를 생성하면 어떻게 될까?

가정을 해 보는 거다. 만약 내가 "일기를 작성할 수 있는 웹 서비스"를 개발하고 있는 상황이고, 일기를 쓰는 기능을 수행하는 객체를 설계해 둔 게 저 위의 DiaryWriting 클래스인 것이다. 근데, 저 클래스의 객체는 유저가 요청을 할 때마다 일기를 쓰는 메서드 사용을 위해 객체를 생성해야 할 것이다. 근데, 객체를 생성하는 이유가 단지 저 메서드를 사용하기 위한 것에 불과하다면, 서로 다르게 생성된 DiaryWriting 객체는 사실 서로 다르지 않은 내용을 가지고 있는 거다. 그런데 객체를 매번 생성하려면 힙에 계속 쌓이다가 가비지 컬렉션이 돌아야 하고, GC가 돌면 stop-the-world가 발생하게 되는 거다. 이해가 되는 사람은 이게 얼마나 불합리적인 구조인지 느껴질 것이다.

따라서 매번 똑같은 내용의 객체를 여러개 생성할 게 아니라, 하나의 객체를 생성해두고 그걸 재활용 하겠다는 아이디어가 나온 것이다. 바로 이게 "싱글톤 디자인 패턴" 이다.

싱글톤 디자인 패턴

싱글톤 디자인 패턴을 사용하는 클래스 중 하나를 간소하게 아래와 같이 가져와봤다.

public class Logger {
    // 유일한 인스턴스를 저장하기 위한 정적 변수
    private static Logger logger;

    // 외부에서 인스턴스를 생성하지 못하도록 private 생성자를 선언
    private Logger() {}

    // 유일한 인스턴스를 반환하는 정적 메서드
    public static synchronized Logger getInstance() {
        if (logger == null) {
            logger = new Logger();
        }
        return logger;
    }

    // 로그 메시지를 출력하는 메서드
    public void log(String message) {
        System.out.println("Log message: " + message);
    }
}

public class Main {
    public static void main(String[] args) {
        // Logger 인스턴스를 얻고 메서드를 호출
        Logger logger = Logger.getInstance();
        logger.log("This is a log message.");
    }
}

위 클래스는 시스템에 로깅을 하는 객체를 생성하기 위한 Logger 클래스이다. 이는 그저 콘솔에 parameter로 받는 메세지를 출력해주는 역할을 할 뿐이다. 이러한 Logger 객체를 여러 개 생성해봤자 어차피 객체 간에 구성 데이터의 차이가 없어 사용을 할 때 완전히 동일한 효과를 누리고 있게 되는 것이므로, Logger 객체를 여러 개 생성할 필요가 없다. 따라서 하나의 객체를 생성해두고 이를 재활용하면 어떨까 하는 생각이 드는 것이고, 이를 "singleton 디자인 패턴"이라고 부른다.

이를 구현하기 위해서는 생성자를 private으로 닫아두고 객체를 멤버 변수로 private static으로 선언해 둔 뒤, 객체를 얻는 메서드를 public으로 열어둠으로써 getInstance()로만 객체를 얻을 수 있게 하면 된다. 이렇게 하면 외부에서는 new 키워드로 객체를 생성할 수 없고, 오로지 getInstance로만 객체를 받게 되는데, 이렇게 받게 되는 객체가 정적(static) 객체이므로 클래스 로딩 시점에 딱 하나의 객체가 할당되어 메모리에 올라가게 되고, 이는 나중에 로직 상에서 getInstance()를 여러번 호출하여도 프로그램이 계속 돌고 있는 동안은런타임 동안 하나의 객체를 계속 재사용하게 되는 것이다.

정리하자면

  • 수정 가능한 멤버 변수가 없고, 기능만 있는 경우에는 객체를 구별할 필요가 없다. 이런 객체를 stateless한 객체라고 한다.
  • 따라서 힙 메모리의 효율적인 사용과 GC 최소화를 위해 stateless 객체들은 재사용이 유리하다.
  • 이를 구현하기 위해서는 생성자의 접근 제한자를 private으로 설정한다.
  • 내부적으로는 객체를 멤버 변수로 private static으로 선언해두고, 외부에서 객체를 얻는 getInstance() 메서드를 public static 으로 열어서 객체 없이 외부에서 접근하도록 하여 단 하나의 객체만을 얻을 수 있게 한다.
  • 외부에서는 이 getter를 통해서 객체를 참조할 수 밖에 없으므로, 런타임 동안 하나의 static한 객체를 계속 재사용하게 된다.

자바는 기본적으로 동적으로 수행하는 일이 많은데, 일부 항목에 대하여는 의도적으로 정적으로 제한하는 것이 좀 더 효율적으로 사용할 수 있게끔 만들어준다. 이것의 가장 대표적인 예시가 싱글톤 디자인 패턴이다. 스프링 프레임워크에서도 service, repository와 같은 객체들은 각 역할을 수행할 뿐, 각 객체가 수정되는 멤버 변수가 없고 기능만 있는 형태이므로 싱글톤 디자인 패턴을 기본적으로 적용하고 있다. 물론, DB에 저장되는 Entity나 data를 레이어 간 전달하는 dto와 같은 객체들은 고유 데이터들을 갖고 있으므로 싱글톤과 무관하다. 참... 이런거 보다 보면 컴퓨터 문화를 만들어가는 사람들은 똑똑한 것 같다.

'DEV-STUDY > Design Pattern' 카테고리의 다른 글

CQS  (0) 2023.07.25
CQRS  (0) 2023.07.25
Comments