디자인 패턴(Design Pattern)이란 무엇이며, 싱글톤 패턴에 대해 설명해주세요.

김상욱·2024년 11월 20일

디자인 패턴(Design Pattern)이란 무엇이며, 싱글톤 패턴에 대해 설명해주세요.

디자인 패턴은 소프트웨어에서 자주 발생하는 문제를 해결하기 위한 재사용 가능한 설계 구조를 의미. 개발자들이 복잡한 문제를 효과적으로 해결할 수 있도록 도와주는 검증된 방법론. 코드의 가독성, 유지보수성, 확장성을 높이는 데 기여.

디자인 패턴은 일반적으로 생성(Creational), 구조(Structural), 행위(Behavioral) 세 가지

싱글톤 패턴은 클래스의 인스턴스가 오직 하나만 생성되도록 보장하며, 그 인스턴스에 접근할 수 있는 전역적인 접근점을 제공하는 디자인 패턴

  • 애플리케이션 전체에서 특정 클래스의 인스턴스가 단 하나만 존재하도록 제약
  • 인스턴스가 어디서든 접근 가능하도록 보장
  • 인스턴스를 공유하기 때문에 리소스 사용을 최소화.

Eager Initialization(즉시 초기화)

public class Singleton {
    private static final Singleton INSTANCE = new Singleton(); // 인스턴스를 미리 생성

    private Singleton() {
        // private 생성자로 외부에서 인스턴스 생성 차단
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
  • 클래스 로딩 시점에 인스턴스를 생성하여 초기화 비용이 낮음
  • 사용하지 않아도 인스턴스가 메모리를 차지할 수 있음

Lazy Initialization(지연 초기화)

public class Singleton {
    private static Singleton instance; // 초기에는 null

    private Singleton() {
        // private 생성자로 외부에서 인스턴스 생성 차단
    }

    public static Singleton getInstance() {
        if (instance == null) { // 필요할 때 생성
            instance = new Singleton();
        }
        return instance;
    }
}
  • 필요한 시점에 인스턴스를 생성하여 메모리 절약.
  • 멀티스레드 환경에서 동시 접근 시 문제 발생 가능.

Thread-Safe Singleton(스레드 안전)

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() { // 동기화 처리
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 멀티스레드 환경에서도 안전.
  • synchronized로 인해 성능 저하 가능

Double-Checked Locking(이중 검증 잠금)

public class Singleton {
    private static volatile Singleton instance; // volatile 키워드 사용

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 스레드 안전성과 성능 모두 만족
  • 구현 복잡성이 약간 증가

사용 사례

  • 로그 관리 : 애플리케이션 전역에서 동일한 로그 객체를 사용
  • 설정 클래스 : 환경 설정이나 구성 정보의 전역 객체 관리.
  • 데이터베이스 연결 풀 : 다수의 연결을 관리하는 객체를 단일 인스턴스로 관리
  • 캐싱 : 전역적인 캐싱 시스템에서 데이터 공유

실습 아이디어

1. 싱글톤 패턴 구현

  • 위에서 설명한 Eager Initialization, Lazy Initialization, Thread-Safe Singleton, Double-Checked Locking 중 하나를 선택해 직접 구현해보고, 이를 사용하는 간단한 예제를 만들어보세요.
    • 예: 로그 관리 시스템
      • 싱글톤 패턴으로 Logger 클래스를 만들고, 프로그램 여러 부분에서 로그를 기록하도록 구현.
      • 로그가 항상 하나의 인스턴스를 통해 기록되는지 확인.

2. 멀티스레드 환경에서 싱글톤 테스트

  • ExecutorService를 사용하여 멀티스레드 환경에서 싱글톤 객체에 접근하는 테스트를 작성해 보세요.
    • 동기화를 하지 않은 경우와 동기화를 한 경우의 차이를 비교.
    • volatile 키워드가 없으면 어떻게 동작하는지도 실험.

3. Spring에서의 싱글톤 스코프 이해

Spring에서 기본 빈 스코프는 Singleton입니다. 아래 과정을 통해 이를 실습해볼 수 있습니다.

  1. 싱글톤 확인

    • Spring에서 두 개의 컨트롤러나 서비스 클래스에서 같은 빈을 주입받아 동일한 인스턴스인지 확인.

    • 예:

      @Service
      public class MyService {
          public void printHashCode() {
              System.out.println(this.hashCode());
          }
      }
      
      @RestController
      public class MyController {
          private final MyService myService;
      
          public MyController(MyService myService) {
              this.myService = myService;
          }
      
          @GetMapping("/test")
          public void test() {
              myService.printHashCode();
          }
      }
    • 위 코드를 실행해 /test를 여러 번 호출하여 동일한 hashCode() 값이 출력되는지 확인.

  2. Prototype 스코프 비교

    • @Scope("prototype")을 사용해 동일한 빈을 매번 다른 인스턴스로 생성하도록 설정.
    • Prototype 스코프에서의 차이점 확인.

4. 빈 초기화 및 소멸 라이프사이클 실습

  • 싱글톤 빈의 생성 및 소멸 과정을 @PostConstruct@PreDestroy로 확인.
    • 빈이 생성될 때와 소멸될 때 메시지를 출력하도록 구현.

5. 싱글톤 레지스트리 직접 구현

  • Spring을 사용하지 않고, 간단한 싱글톤 레지스트리를 만들어 관리해 보세요.

    • 예: 아래 코드와 같이 특정 타입의 객체를 싱글톤으로 관리하는 SingletonRegistry를 구현.

      public class SingletonRegistry {
          private static final Map<String, Object> registry = new HashMap<>();
      
          public static Object getInstance(String key, Supplier<Object> creator) {
              return registry.computeIfAbsent(key, k -> creator.get());
          }
      }
      
      public class Main {
          public static void main(String[] args) {
              Object instance1 = SingletonRegistry.getInstance("Logger", Logger::new);
              Object instance2 = SingletonRegistry.getInstance("Logger", Logger::new);
      
              System.out.println(instance1 == instance2); // true 출력
          }
      }

6. 싱글톤 패턴의 단점 실습

  • 싱글톤 패턴의 단점인 테스트 어려움을 직접 경험:
    • 싱글톤으로 만든 객체를 단위 테스트에서 모킹하려 할 때, 테스트의 유연성이 낮아지는 문제를 관찰.

7. Spring Bean의 라이프사이클과 싱글톤 패턴

  • ApplicationContext를 사용해 빈을 직접 가져오고, 싱글톤 스코프의 동작 원리를 실험.
    • 예: ConfigurableApplicationContext를 사용해 컨텍스트를 닫으면 싱글톤 빈이 어떻게 소멸되는지 확인.

8. 실제 프로젝트에 적용

  • 간단한 프로젝트를 하나 만들어 싱글톤 빈을 설계하고 활용.
    • 예: 사용자 인증 시스템
      • 사용자 세션 정보를 관리하는 SessionManager 클래스를 싱글톤으로 구현.
      • 여러 컨트롤러에서 SessionManager를 공유하며 세션 정보에 접근.

실습을 통해 얻을 수 있는 점

  • 싱글톤 패턴의 개념과 구현 원리를 명확히 이해.
  • Spring의 기본 설계 철학(싱글톤 스코프 등)을 체득.
  • 멀티스레드 환경에서의 안전한 싱글톤 사용법 습득.
  • 코드의 유지보수성과 테스트 가능성을 고려한 설계 능력 향상.

이런 실습을 통해 싱글톤 패턴을 단순한 개념이 아니라 실무에 활용할 수 있는 도구로 만들 수 있습니다!

0개의 댓글