관리 메뉴

deVlog

[λ””μžμΈ νŒ¨ν„΄] 생성 νŒ¨ν„΄ - Singleton Pattern (싱글톀 νŒ¨ν„΄) λ³Έλ¬Έ

πŸ› οΈ Software Architecture

[λ””μžμΈ νŒ¨ν„΄] 생성 νŒ¨ν„΄ - Singleton Pattern (싱글톀 νŒ¨ν„΄)

은루밍 2025. 4. 3. 19:40

λͺ©μ°¨

     

     

    πŸ“‡ κ°œμš”

    싱글톀 νŒ¨ν„΄(Singleton Pattern)은 클래슀의 μΈμŠ€ν„΄μŠ€κ°€ 였직 ν•˜λ‚˜λ§Œ μƒμ„±λ˜κ³ , μ–΄λ””μ„œλ“  κ·Έ μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•˜λŠ” 생성 λ””μžμΈ νŒ¨ν„΄μ΄λ‹€. 즉, μ „μ—­ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ 객체λ₯Ό ν•˜λ‚˜λ§Œ μƒμ„±ν•˜μ—¬ μ–΄λ””μ„œλ“  μ ‘κ·Όν•  수 있게 ν•˜λŠ” νŒ¨ν„΄μ΄λ‹€.

     

    ⭐️ νŠΉμ§•

    • μΈμŠ€ν„΄μŠ€κ°€ 였직 ν•œ 개만 μ‘΄μž¬ν•˜λ„λ‘ 보μž₯ν•œλ‹€.
    • 전역적인 접근점을 μ œκ³΅ν•˜μ—¬ μ–΄λ””μ„œλ“  λ™μΌν•œ μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν•  수 μžˆλ‹€.
    • μ§€μ—° μ΄ˆκΈ°ν™”(lazy initialization)κ°€ κ°€λŠ₯ν•˜μ—¬ ν•„μš”ν•œ μ‹œμ μ— μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆλ‹€.

     

     

    πŸ‘©πŸ»‍πŸ’» μ½”λ“œλ‘œ μ•Œμ•„λ³΄μž

    싱글톀 νŒ¨ν„΄μ˜ λ‹€μ–‘ν•œ κ΅¬ν˜„ 방법을 μ‚΄νŽ΄λ³΄μž.

    1. κΈ°λ³Έ 싱글톀 νŒ¨ν„΄

    public class BasicSingleton {
        // 1. private static λ³€μˆ˜λ‘œ μœ μΌν•œ μΈμŠ€ν„΄μŠ€ μ„ μ–Έ
        private static BasicSingleton instance;
        
        // 2. private μƒμ„±μžλ‘œ μ™ΈλΆ€μ—μ„œ μΈμŠ€ν„΄μŠ€ 생성 λ°©μ§€
        private BasicSingleton() {
            // μ΄ˆκΈ°ν™” μ½”λ“œ
        }
        
        // 3. public static λ©”μ„œλ“œλ‘œ μΈμŠ€ν„΄μŠ€ 접근점 제곡
        public static BasicSingleton getInstance() {
            // μΈμŠ€ν„΄μŠ€κ°€ μ—†μœΌλ©΄ 생성 (μ§€μ—° μ΄ˆκΈ°ν™”)
            if (instance == null) {
                instance = new BasicSingleton();
            }
            return instance;
        }
        
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 λ©”μ„œλ“œ
        public void doSomething() {
            System.out.println("싱글톀 객체의 κΈ°λŠ₯ μˆ˜ν–‰");
        }
    }
    

    2. μŠ€λ ˆλ“œ μ•ˆμ „ν•œ 싱글톀 (Thread-Safe Singleton)

    public class ThreadSafeSingleton {
        private static ThreadSafeSingleton instance;
        
        private ThreadSafeSingleton() {
            // μ΄ˆκΈ°ν™” μ½”λ“œ
        }
        
        // synchronized ν‚€μ›Œλ“œλ‘œ μŠ€λ ˆλ“œ μ•ˆμ „μ„± 보μž₯
        public static synchronized ThreadSafeSingleton getInstance() {
            if (instance == null) {
                instance = new ThreadSafeSingleton();
            }
            return instance;
        }
    }
    

    3. DCL(Double-Checked Locking) 싱글톀

    public class DCLSingleton {
        // volatile ν‚€μ›Œλ“œλ‘œ λ³€μˆ˜μ˜ κ°€μ‹œμ„± 보μž₯
        private static volatile DCLSingleton instance;
        
        private DCLSingleton() {
            // μ΄ˆκΈ°ν™” μ½”λ“œ
        }
        
        public static DCLSingleton getInstance() {
            // 첫 번째 검사 (락 없이 λΉ λ₯Έ 체크)
            if (instance == null) {
                // μΈμŠ€ν„΄μŠ€ν™” λΆ€λΆ„μ—λ§Œ 동기화 적용
                synchronized (DCLSingleton.class) {
                    // 두 번째 검사 (락 νšλ“ ν›„ μž¬ν™•μΈ)
                    if (instance == null) {
                        instance = new DCLSingleton();
                    }
                }
            }
            return instance;
        }
    }
    

    4. μ΄ˆκΈ°ν™” 온 λ””λ§¨λ“œ 홀더(Initialization On Demand Holder) νŒ¨ν„΄

    public class InitOnDemandSingleton {
        private InitOnDemandSingleton() {
            // μ΄ˆκΈ°ν™” μ½”λ“œ
        }
        
        // λ‚΄λΆ€ static 클래슀λ₯Ό μ‚¬μš©ν•΄ μ§€μ—° μ΄ˆκΈ°ν™” κ΅¬ν˜„
        private static class LazyHolder {
            // JVM이 클래슀 λ‘œλ“œ μ‹œμ μ— μΈμŠ€ν„΄μŠ€ 생성 (thread-safe)
            private static final InitOnDemandSingleton INSTANCE = new InitOnDemandSingleton();
        }
        
        public static InitOnDemandSingleton getInstance() {
            return LazyHolder.INSTANCE;
        }
    }
    

    5. Enum 싱글톀

    public enum EnumSingleton {
        INSTANCE; // μœ μΌν•œ enum μƒμˆ˜κ°€ 싱글톀 μΈμŠ€ν„΄μŠ€ μ—­ν• 
        
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
        public void doSomething() {
            System.out.println("Enum 싱글톀 κΈ°λŠ₯ μˆ˜ν–‰");
        }
    }
    
    // μ‚¬μš© 예
    EnumSingleton singleton = EnumSingleton.INSTANCE;
    singleton.doSomething();
    

    6. μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ—μ„œμ˜ 싱글톀

    import org.springframework.stereotype.Component;
    
    @Component // μŠ€ν”„λ§ 빈으둜 등둝
    public class SpringSingleton {
        // μŠ€ν”„λ§μ΄ μ•Œμ•„μ„œ μ‹±κΈ€ν†€μœΌλ‘œ 관리
        public void doSomething() {
            System.out.println("μŠ€ν”„λ§ 싱글톀 빈의 κΈ°λŠ₯ μˆ˜ν–‰");
        }
    }
    
    // μ‚¬μš© 예
    @Autowired
    private SpringSingleton springSingleton;
    

     

    ☘️ μž₯/단점

    βœ… μž₯점

    1. λ©”λͺ¨λ¦¬ νš¨μœ¨μ„±
      • μΈμŠ€ν„΄μŠ€κ°€ 였직 ν•œ 번만 μƒμ„±λ˜λ―€λ‘œ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ 쀄일 수 μžˆλ‹€.
      • 특히 μΈμŠ€ν„΄μŠ€ 생성 λΉ„μš©μ΄ 큰 경우 νš¨μœ¨μ μ΄λ‹€.
    2. μ „μ—­ μ ‘κ·Όμ„±
      • μ–΄λ””μ„œλ“  λ™μΌν•œ μΈμŠ€ν„΄μŠ€μ— μ‰½κ²Œ μ ‘κ·Όν•  수 μžˆλ‹€.
      • μ—¬λŸ¬ μ»΄ν¬λ„ŒνŠΈκ°€ λ™μΌν•œ λ¦¬μ†ŒμŠ€λ‚˜ μ„œλΉ„μŠ€λ₯Ό κ³΅μœ ν•΄μ•Ό ν•  λ•Œ μœ μš©ν•˜λ‹€.
    3. μƒνƒœ 곡유
      • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ „μ²΄μ—μ„œ λ™μΌν•œ μƒνƒœλ₯Ό κ³΅μœ ν•  수 μžˆλ‹€.
      • μ„€μ • 관리, 캐싱, λ¦¬μ†ŒμŠ€ 풀링 등에 μ ν•©ν•˜λ‹€.
    4. μΈμŠ€ν„΄μŠ€ μ œμ–΄
      • μΈμŠ€ν„΄μŠ€ 생성을 μ—„κ²©ν•˜κ²Œ μ œμ–΄ν•  수 μžˆλ‹€.
      • 생성 μ‹œμ κ³Ό 방법을 μ •λ°€ν•˜κ²Œ 관리할 수 μžˆλ‹€.

    ❌ 단점

    1. 단일 μ±…μž„ 원칙(SRP) μœ„λ°˜
      • 싱글톀 ν΄λž˜μŠ€λŠ” μžμ‹ μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό κ΄€λ¦¬ν•˜λ©΄μ„œ λ™μ‹œμ— λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§λ„ μ²˜λ¦¬ν•˜λ―€λ‘œ μ±…μž„μ΄ μ—¬λŸ¬ κ°œκ°€ λœλ‹€.
      • μ΄λŠ” SOLID 원칙 쀑 SRPλ₯Ό μœ„λ°˜ν•œλ‹€.
    2. ν…ŒμŠ€νŠΈ 어렀움
      • μ „μ—­ μƒνƒœλ₯Ό κ°€μ§€λ―€λ‘œ λ‹¨μœ„ ν…ŒμŠ€νŠΈκ°€ μ–΄λ €μ›Œμ§ˆ 수 μžˆλ‹€.
      • ν…ŒμŠ€νŠΈ 간에 μƒνƒœκ°€ κ³΅μœ λ˜μ–΄ 격리된 ν…ŒμŠ€νŠΈκ°€ μ–΄λ ΅λ‹€.
    3. μ˜μ‘΄μ„± μˆ¨κΉ€
      • 클래슀 κ°„μ˜ μ˜μ‘΄μ„±μ΄ λͺ…μ‹œμ μœΌλ‘œ λ“œλŸ¬λ‚˜μ§€ μ•ŠλŠ”λ‹€.
      • 싱글톀을 μ‚¬μš©ν•˜λŠ” μ½”λ“œκ°€ μ–΄λ””μ„œλ“  μ ‘κ·Όν•  수 μžˆμ–΄ μ½”λ“œ 좔적이 μ–΄λ €μ›Œμ§„λ‹€.
    4. 닀쀑 μΈμŠ€ν„΄μŠ€ 문제
      • 클래슀 λ‘œλ”κ°€ μ—¬λŸ¬ 개인 경우 μ˜λ„μΉ˜ μ•Šκ²Œ μ—¬λŸ¬ μΈμŠ€ν„΄μŠ€κ°€ 생성될 수 μžˆλ‹€.
      • 직렬화/역직렬화 μ‹œμ—λ„ μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€κ°€ 생성될 수 μžˆλ‹€.
    5. κ³Όλ„ν•œ μ‚¬μš©
      • 싱글톀 νŒ¨ν„΄μ„ λ‚¨μš©ν•˜λ©΄ μ „μ—­ μƒνƒœμ— μ˜μ‘΄ν•˜λŠ” μ½”λ“œκ°€ λ§Žμ•„μ Έ μœ μ§€λ³΄μˆ˜κ°€ μ–΄λ €μ›Œμ§ˆ 수 μžˆλ‹€.
      • λͺ¨λ“ˆ κ°„ 결합도가 λ†’μ•„μ Έ μ½”λ“œ 변경이 μ–΄λ €μ›Œμ§„λ‹€.

     

    πŸ’‘ μžλ°”/μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ—μ„œμ˜ 싱글톀

    1. μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ™€ 싱글톀

    μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬λŠ” 기본적으둜 빈(Bean)을 μ‹±κΈ€ν†€μœΌλ‘œ κ΄€λ¦¬ν•œλ‹€. μ΄λŠ” μŠ€ν”„λ§μ˜ 핡심 νŠΉμ§• 쀑 ν•˜λ‚˜μ΄λ‹€.

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    
    @Configuration
    public class AppConfig {
        
        // 기본적으둜 싱글톀 μŠ€μ½”ν”„λ₯Ό 가짐
        @Bean
        public UserService userService() {
            return new UserServiceImpl();
        }
        
        // λͺ…μ‹œμ μœΌλ‘œ 싱글톀 μŠ€μ½”ν”„ μ§€μ •
        @Bean
        @Scope("singleton")
        public ProductService productService() {
            return new ProductServiceImpl();
        }
        
        // ν”„λ‘œν† νƒ€μž… μŠ€μ½”ν”„ (맀번 μƒˆ μΈμŠ€ν„΄μŠ€ 생성)
        @Bean
        @Scope("prototype")
        public OrderService orderService() {
            return new OrderServiceImpl();
        }
    }
    

    2. μ˜μ‘΄μ„± μ£Όμž…μ„ ν†΅ν•œ 싱글톀 μ‚¬μš©

    μŠ€ν”„λ§μ—μ„œλŠ” 싱글톀 λΉˆμ— λŒ€ν•œ μ˜μ‘΄μ„±μ„ μ£Όμž…λ°›μ•„ μ‚¬μš©ν•œλ‹€.

    @Service
    public class ProductServiceImpl implements ProductService {
        
        private final UserService userService;
        
        // μƒμ„±μž μ£Όμž…μ„ 톡해 싱글톀 빈 μ°Έμ‘°
        @Autowired
        public ProductServiceImpl(UserService userService) {
            this.userService = userService;
        }
        
        @Override
        public void processProduct() {
            // userServiceλŠ” 싱글톀 빈
            userService.getUserInfo();
        }
    }
    

    3. 싱글톀 빈 등둝 방법

    μŠ€ν”„λ§μ—μ„œ 싱글톀 λΉˆμ„ λ“±λ‘ν•˜λŠ” μ—¬λŸ¬ 방법은 μ•„λž˜μ™€ κ°™λ‹€.

    // 1. @Component μ• λ…Έν…Œμ΄μ…˜ μ‚¬μš©
    @Component
    public class LogManager {
        public void log(String message) {
            System.out.println("Log: " + message);
        }
    }
    
    // 2. @Service μ• λ…Έν…Œμ΄μ…˜ μ‚¬μš© (μ„œλΉ„μŠ€ λ ˆμ΄μ–΄)
    @Service
    public class UserService {
        // λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
    }
    
    // 3. @Repository μ• λ…Έν…Œμ΄μ…˜ μ‚¬μš© (데이터 μ ‘κ·Ό λ ˆμ΄μ–΄)
    @Repository
    public class UserRepository {
        // 데이터 μ ‘κ·Ό 둜직
    }
    
    // 4. @Controller μ• λ…Έν…Œμ΄μ…˜ μ‚¬μš© (μ›Ή λ ˆμ΄μ–΄)
    @Controller
    public class UserController {
        // μ›Ή μš”μ²­ 처리 둜직
    }
    
    // 5. @Configurationκ³Ό @Bean μ‚¬μš©
    @Configuration
    public class AppConfig {
        @Bean
        public EmailService emailService() {
            return new EmailServiceImpl();
        }
    }
    

     

    πŸ“š μ‹€μ œ μ‚¬μš© 사둀

    1. λ‘œκΉ… μ‹œμŠ€ν…œ
      • 둜그 μΈμŠ€ν„΄μŠ€λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ „μ²΄μ—μ„œ ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•΄μ•Ό 함
      • 예: Java의 java.util.logging.Logger
    2. λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° ν’€
      • DB μ—°κ²°κ³Ό 같은 λΉ„μš©μ΄ 큰 λ¦¬μ†ŒμŠ€λ₯Ό 효율적으둜 관리
      • 예: 컀λ„₯μ…˜ ν’€ 라이브러리(HikariCP, DBCP λ“±)
    3. μ„€μ • 관리
      • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 섀정을 μ€‘μ•™μ—μ„œ 관리
      • 예: ν”„λ‘œνΌν‹° κ΄€λ¦¬μž, ν™˜κ²½ μ„€μ • λ§€λ‹ˆμ €
    4. μΊμ‹œ κ΅¬ν˜„
      • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ „μ²΄μ—μ„œ λ™μΌν•œ μΊμ‹œ μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Ό
      • 예: λ©”λͺ¨λ¦¬ μΊμ‹œ, λΆ„μ‚° μΊμ‹œμ˜ ν΄λΌμ΄μ–ΈνŠΈ
    5. μŠ€λ ˆλ“œ ν’€
      • μ œν•œλœ 수의 μŠ€λ ˆλ“œλ₯Ό 효율적으둜 관리
      • 예: Java의 ExecutorService κ΅¬ν˜„μ²΄
    6. λ””λ°”μ΄μŠ€ κ΄€λ¦¬μž
      • ν”„λ¦°ν„°, μŠ€μΊλ„ˆ λ“± ν•˜λ“œμ›¨μ–΄ μž₯μΉ˜μ™€μ˜ 톡신 관리
      • 예: ν”„λ¦°ν„° μŠ€ν’€λŸ¬, λ””λ°”μ΄μŠ€ λ“œλΌμ΄λ²„ μΈν„°νŽ˜μ΄μŠ€
    7. UI κ΅¬μ„±μš”μ†Œ 관리
      • λŒ€ν™”μƒμž, μŠ€ν”Œλž˜μ‹œ ν™”λ©΄ λ“± UI μš”μ†Œ 관리
      • 예: JavaFX의 Stage 관리, Android의 ActivityManager
    8. μŠ€ν”„λ§ 빈
      • μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ—μ„œ 기본적으둜 λͺ¨λ“  λΉˆμ€ μ‹±κΈ€ν†€μœΌλ‘œ 관리됨
      • 예: Service, Repository, Controller λ“±μ˜ 빈
    9. 파일 μ‹œμŠ€ν…œ μ ‘κ·Ό
      • 파일 μ‹œμŠ€ν…œμ— λŒ€ν•œ 접근을 μ€‘μ•™μ—μ„œ 관리
      • 예: 파일 λ§€λ‹ˆμ €, I/O μœ ν‹Έλ¦¬ν‹°
    10. λ„€νŠΈμ›Œν¬ 관리
      • λ„€νŠΈμ›Œν¬ μ—°κ²° 및 μ†ŒμΌ“ 관리
      • 예: HTTP ν΄λΌμ΄μ–ΈνŠΈ, μ†ŒμΌ“ μ„œλ²„

     

    πŸ“Œ μ •리

    싱글톀 νŒ¨ν„΄μ€ μΈμŠ€ν„΄μŠ€λ₯Ό 였직 ν•˜λ‚˜λ§Œ μƒμ„±ν•˜κ³  μ „μ—­μ μœΌλ‘œ μ ‘κ·Όν•  수 있게 ν•˜λŠ” λ””μžμΈ νŒ¨ν„΄μ΄λ‹€.

    이 νŒ¨ν„΄μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ 곡유 λ¦¬μ†ŒμŠ€λ‚˜ μ„œλΉ„μŠ€μ— 효율적으둜 μ ‘κ·Όν•΄μ•Ό ν•  λ•Œ μœ μš©ν•˜μ§€λ§Œ, κ³Όλ„ν•œ μ‚¬μš©μ€ μ½”λ“œμ˜ 결합도λ₯Ό 높이고 ν…ŒμŠ€νŠΈλ₯Ό μ–΄λ ΅κ²Œ λ§Œλ“€ 수 μžˆλ‹€. λ”°λΌμ„œ ν•„μš”ν•œ κ²½μš°μ—λ§Œ μ‹ μ€‘ν•˜κ²Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

    βœ… μ–Έμ œ μ‚¬μš©ν•˜λ©΄ μ’‹μ„κΉŒ?

    • 곡유 λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ μ ‘κ·Ό 관리가 ν•„μš”ν•  λ•Œ(DB μ—°κ²° ν’€, μŠ€λ ˆλ“œ ν’€ λ“±)
    • μ „μ—­μ μœΌλ‘œ μœ μΌν•œ μ„€μ •μ΄λ‚˜ μƒνƒœλ₯Ό 관리해야 ν•  λ•Œ
    • λ¦¬μ†ŒμŠ€ 생성 λΉ„μš©μ΄ 큰 객체λ₯Ό μ—¬λŸ¬ 번 μƒμ„±ν•˜μ§€ μ•Šμ•„μ•Ό ν•  λ•Œ
    • μ—¬λŸ¬ μ»΄ν¬λ„ŒνŠΈκ°€ λ™μΌν•œ μΈμŠ€ν„΄μŠ€μ— μ ‘κ·Όν•΄μ•Ό ν•  λ•Œ

    ❌ μ–Έμ œ ν”Όν•΄μ•Ό ν• κΉŒ?

    • 객체 μ§€ν–₯ 섀계 원칙을 μ—„κ²©νžˆ μ€€μˆ˜ν•΄μ•Ό ν•  λ•Œ
    • λ‹¨μœ„ ν…ŒμŠ€νŠΈκ°€ μ€‘μš”ν•œ 경우
    • μƒνƒœλ₯Ό κ³΅μœ ν•˜μ§€ μ•ŠλŠ” 것이 더 μ ν•©ν•œ 경우
    • μΈμŠ€ν„΄μŠ€ 생성을 μ œμ–΄ν•  ν•„μš”κ°€ μ—†λŠ” 경우

     

     

     

    πŸ“‹ μ°Έκ³ 

    'πŸ› οΈ Software Architecture' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

    Fanout On Write (Push Model) vs Fanout On Read (Pull Model)  (3) 2024.08.30