[카테고리:] 미분류

  • 소프트웨어 디자인 패턴(K-AI)

    소프트웨어 개발에서 반복적으로 나타나는 문제들을 해결하기 위한 검증된 설계 템플릿들을 체계적으로 정리해보겠습니다. 각 패턴은 특정 상황에서 코드의 유지보수성, 확장성, 재사용성을 크게 향상시킵니다.

    🔧 Strategy Pattern (전략 패턴)

    목적: 알고리즘군을 정의하고 각각을 캡슐화하여 상호 교환 가능하게 만드는 패턴

    핵심 개념: 런타임에 객체의 행동을 변경할 수 있도록 알고리즘을 분리하여 독립적으로 변경 가능하게 합니다.

    구현 예시 – 버튼 타입별 실행 방식:

    pythonfrom abc import ABC, abstractmethod
    
    class ButtonStrategy(ABC):
        @abstractmethod
        def execute(self, config):
            pass
    
    class WebButtonStrategy(ButtonStrategy):
        def execute(self, config):
            return f"웹 브라우저로 {config['url']} 열기"
    
    class ProgramButtonStrategy(ButtonStrategy):
        def execute(self, config):
            return f"프로그램 {config['path']} 실행"
    
    class FileDialogButtonStrategy(ButtonStrategy):
        def execute(self, config):
            return f"파일 다이얼로그 {config['filter']}로 열기"
    
    class Button:
        def __init__(self, strategy: ButtonStrategy):
            self.strategy = strategy
        
        def click(self, config):
            return self.strategy.execute(config)
    

    장점: 조건문 없이 깔끔하게 다양한 실행 방식을 처리할 수 있으며, 새로운 버튼 타입 추가가 용이합니다.

    🏭 Factory Pattern (팩토리 패턴)

    목적: 객체 생성 로직을 별도 클래스로 분리하여 객체 생성을 캡슐화하는 패턴

    핵심 개념: 클라이언트는 구체적인 클래스를 몰라도 인터페이스를 통해 객체를 생성할 수 있습니다.

    구현 예시 – JSON 기반 동적 UI 생성:

    pythonimport json
    from typing import Dict, Any
    
    class UIElement:
        def __init__(self, config: Dict[str, Any]):
            self.config = config
    
    class Button(UIElement):
        def render(self):
            return f"Button: {self.config['text']}"
    
    class Label(UIElement):
        def render(self):
            return f"Label: {self.config['text']}"
    
    class UIFactory:
        _creators = {
            'button': Button,
            'label': Label
        }
        
        @classmethod
        def create_element(cls, element_type: str, config: Dict[str, Any]):
            creator = cls._creators.get(element_type)
            if not creator:
                raise ValueError(f"Unknown element type: {element_type}")
            return creator(config)
        
        @classmethod
        def create_from_json(cls, json_config: str):
            config = json.loads(json_config)
            elements = []
            for item in config['elements']:
                element = cls.create_element(item['type'], item)
                elements.append(element)
            return elements
    

    장점: JSON 설정만 변경하면 UI 구성을 완전히 바꿀 수 있어 유연성이 극대화됩니다.

    📋 Template Method Pattern (템플릿 메서드 패턴)

    목적: 알고리즘의 구조를 정의하고 하위 클래스에서 특정 단계를 재정의할 수 있게 하는 패턴

    핵심 개념: 전체적인 흐름은 상위 클래스에서 제어하고, 세부 구현만 하위 클래스에서 담당합니다.

    구현 예시 – 앱 초기화 프로세스:

    pythonfrom abc import ABC, abstractmethod
    
    class ApplicationInitializer(ABC):
        def initialize(self):
            """템플릿 메서드: 초기화 순서를 정의"""
            self.load_configuration()
            self.apply_theme()
            self.create_ui()
            self.setup_event_handlers()
        
        @abstractmethod
        def load_configuration(self):
            pass
        
        @abstractmethod
        def apply_theme(self):
            pass
        
        @abstractmethod
        def create_ui(self):
            pass
        
        def setup_event_handlers(self):
            """공통 이벤트 핸들러 설정"""
            print("기본 이벤트 핸들러 설정 완료")
    
    class LauncherInitializer(ApplicationInitializer):
        def load_configuration(self):
            print("JSON 설정 파일 로드")
        
        def apply_theme(self):
            print("다크/라이트 테마 적용")
        
        def create_ui(self):
            print("동적 버튼 UI 생성")
    

    장점: 일관된 초기화 순서를 보장하면서도 각 단계의 구체적인 구현은 유연하게 변경할 수 있습니다.

    👀 Observer Pattern (옵저버 패턴)

    목적: 객체 간의 일대다 의존성을 정의하여 한 객체의 상태 변화를 여러 객체에 자동으로 통지하는 패턴

    핵심 개념: Subject(주제)의 상태가 변경되면 등록된 모든 Observer(관찰자)에게 자동으로 알림이 전송됩니다.

    구현 예시 – WebView2 상태 모니터링:

    pythonfrom typing import List, Protocol
    
    class Observer(Protocol):
        def update(self, event_type: str, data: dict) -> None:
            pass
    
    class WebViewSubject:
        def __init__(self):
            self._observers: List[Observer] = []
            self._navigation_state = "idle"
        
        def attach(self, observer: Observer):
            self._observers.append(observer)
        
        def detach(self, observer: Observer):
            self._observers.remove(observer)
        
        def notify(self, event_type: str, data: dict):
            for observer in self._observers:
                observer.update(event_type, data)
        
        def navigate_to(self, url: str):
            self._navigation_state = "navigating"
            self.notify("navigation_started", {"url": url})
            
            # 네비게이션 완료 시뮬레이션
            self._navigation_state = "completed"
            self.notify("navigation_completed", {"url": url})
    
    class StatusBarObserver:
        def update(self, event_type: str, data: dict):
            if event_type == "navigation_started":
                print(f"상태바: {data['url']} 로딩 중...")
            elif event_type == "navigation_completed":
                print(f"상태바: {data['url']} 로딩 완료")
    
    class ProgressBarObserver:
        def update(self, event_type: str, data: dict):
            if event_type == "navigation_started":
                print("프로그레스바: 로딩 애니메이션 시작")
            elif event_type == "navigation_completed":
                print("프로그레스바: 로딩 완료, 숨김 처리")
    

    장점: UI 컴포넌트들이 WebView 상태에 자동으로 반응하여 사용자 경험이 향상됩니다.

    📦 Command Pattern (커맨드 패턴)

    목적: 요청을 객체로 캡슐화하여 요청을 매개변수화하고 큐에 저장하거나 로깅, 되돌리기 등을 지원하는 패턴

    핵심 개념: 행동(Action)을 객체로 만들어 실행, 취소, 로깅, 큐잉 등이 가능해집니다.

    구현 예시 – 버튼 액션 캡슐화:

    pythonfrom abc import ABC, abstractmethod
    import subprocess
    import webbrowser
    
    class ItemAction(ABC):
        @abstractmethod
        def execute(self) -> bool:
            pass
        
        @abstractmethod
        def can_undo(self) -> bool:
            pass
        
        @abstractmethod
        def undo(self) -> bool:
            pass
    
    class WebAction(ItemAction):
        def __init__(self, url: str):
            self.url = url
        
        def execute(self) -> bool:
            try:
                webbrowser.open(self.url)
                return True
            except Exception as e:
                print(f"웹 열기 실패: {e}")
                return False
        
        def can_undo(self) -> bool:
            return False  # 웹 페이지는 되돌릴 수 없음
        
        def undo(self) -> bool:
            return False
    
    class ProgramAction(ItemAction):
        def __init__(self, path: str, args: list = None):
            self.path = path
            self.args = args or []
            self.process = None
        
        def execute(self) -> bool:
            try:
                self.process = subprocess.Popen([self.path] + self.args)
                return True
            except Exception as e:
                print(f"프로그램 실행 실패: {e}")
                return False
        
        def can_undo(self) -> bool:
            return self.process and self.process.poll() is None
        
        def undo(self) -> bool:
            if self.can_undo():
                self.process.terminate()
                return True
            return False
    
    class ActionInvoker:
        def __init__(self):
            self.history = []
        
        def execute_action(self, action: ItemAction):
            if action.execute():
                self.history.append(action)
        
        def undo_last_action(self):
            if self.history:
                action = self.history.pop()
                if action.can_undo():
                    action.undo()
    

    장점: 각 버튼의 기능을 독립적인 객체로 관리하여 실행 이력 추적, 되돌리기 등의 고급 기능을 쉽게 구현할 수 있습니다.

    💉 Dependency Injection (의존성 주입)

    목적: 객체의 의존성을 외부에서 주입하여 결합도를 낮추고 테스트 용이성을 높이는 패턴

    핵심 개념: 객체가 직접 의존성을 생성하지 않고 外부에서 주입받아 사용합니다.

    구현 예시 – 런처 의존성 관리:

    pythonfrom typing import Protocol
    
    class ConfigLoader(Protocol):
        def load_config(self) -> dict:
            pass
    
    class ThemeManager(Protocol):
        def apply_theme(self, theme_name: str) -> None:
            pass
    
    class UIRenderer(Protocol):
        def render(self, config: dict) -> None:
            pass
    
    class JSONConfigLoader:
        def __init__(self, file_path: str):
            self.file_path = file_path
        
        def load_config(self) -> dict:
            import json
            with open(self.file_path, 'r', encoding='utf-8') as f:
                return json.load(f)
    
    class DarkThemeManager:
        def apply_theme(self, theme_name: str) -> None:
            print(f"다크 테마 '{theme_name}' 적용")
    
    class ModernUIRenderer:
        def render(self, config: dict) -> None:
            print(f"모던 UI 렌더링: {len(config.get('buttons', []))}개 버튼")
    
    class Launcher:
        def __init__(self, 
                     config_loader: ConfigLoader,
                     theme_manager: ThemeManager,
                     ui_renderer: UIRenderer):
            self.config_loader = config_loader
            self.theme_manager = theme_manager
            self.ui_renderer = ui_renderer
        
        def start(self):
            config = self.config_loader.load_config()
            self.theme_manager.apply_theme(config.get('theme', 'default'))
            self.ui_renderer.render(config)
    
    # 의존성 주입을 통한 런처 생성
    def create_launcher():
        config_loader = JSONConfigLoader('config.json')
        theme_manager = DarkThemeManager()
        ui_renderer = ModernUIRenderer()
        
        return Launcher(config_loader, theme_manager, ui_renderer)
    

    장점: 각 컴포넌트를 독립적으로 테스트할 수 있고, 런타임에 다른 구현체로 쉽게 교체할 수 있습니다.

    🎨 Builder Pattern (빌더 패턴)

    목적: 복잡한 객체의 생성 과정을 단계별로 나누어 동일한 생성 절차에서 서로 다른 표현 결과를 만드는 패턴

    핵심 개념: 객체 생성의 복잡함을 숨기고 직관적인 인터페이스로 단계별 구성을 가능하게 합니다.

    구현 예시 – 복잡한 설정 객체 구성:

    pythonfrom dataclasses import dataclass
    from typing import List, Optional
    
    @dataclass
    class ButtonConfig:
        text: str
        action_type: str
        action_data: dict
        style: dict = None
    
    @dataclass 
    class ThemeConfig:
        name: str
        colors: dict
        fonts: dict
    
    @dataclass
    class LauncherConfig:
        title: str
        buttons: List[ButtonConfig]
        theme: ThemeConfig
        window_size: tuple
        auto_start: bool = False
    
    class LauncherConfigBuilder:
        def __init__(self):
            self.reset()
        
        def reset(self):
            self._title = "기본 런처"
            self._buttons = []
            self._theme = None
            self._window_size = (800, 600)
            self._auto_start = False
            return self
        
        def set_title(self, title: str):
            self._title = title
            return self
        
        def add_button(self, text: str, action_type: str, action_data: dict, style: dict = None):
            button = ButtonConfig(text, action_type, action_data, style)
            self._buttons.append(button)
            return self
        
        def set_theme(self, name: str, colors: dict, fonts: dict):
            self._theme = ThemeConfig(name, colors, fonts)
            return self
        
        def set_window_size(self, width: int, height: int):
            self._window_size = (width, height)
            return self
        
        def set_auto_start(self, auto_start: bool):
            self._auto_start = auto_start
            return self
        
        def build(self) -> LauncherConfig:
            if not self._theme:
                self._theme = ThemeConfig("default", {"bg": "#ffffff"}, {"main": "Arial"})
            
            return LauncherConfig(
                title=self._title,
                buttons=self._buttons,
                theme=self._theme,
                window_size=self._window_size,
                auto_start=self._auto_start
            )
    
    # 사용 예시
    config = (LauncherConfigBuilder()
              .set_title("개발자 도구 런처")
              .add_button("VS Code", "program", {"path": "code.exe"})
              .add_button("GitHub", "web", {"url": "https://github.com"})
              .set_theme("dark", {"bg": "#2d2d2d", "fg": "#ffffff"}, {"main": "Consolas"})
              .set_window_size(1200, 800)
              .set_auto_start(True)
              .build())
    

    장점: 복잡한 설정 객체를 직관적이고 읽기 쉬운 방식으로 생성할 수 있으며, 필수/선택 매개변수를 명확히 구분할 수 있습니다.

    🔄 State Pattern (상태 패턴)

    목적: 객체의 내부 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있게 하는 패턴

    핵심 개념: 상태별 행동을 별도 클래스로 분리하여 상태 전환을 명확하게 관리합니다.

    구현 예시 – 테마별 상태 관리:

    pythonfrom abc import ABC, abstractmethod
    
    class ThemeState(ABC):
        @abstractmethod
        def get_colors(self) -> dict:
            pass
        
        @abstractmethod
        def get_layout_style(self) -> dict:
            pass
        
        @abstractmethod
        def apply_to_component(self, component):
            pass
    
    class LightThemeState(ThemeState):
        def get_colors(self) -> dict:
            return {
                "background": "#ffffff",
                "foreground": "#000000",
                "accent": "#0078d4",
                "border": "#e1e1e1"
            }
        
        def get_layout_style(self) -> dict:
            return {
                "padding": 10,
                "margin": 5,
                "border_width": 1,
                "shadow": True
            }
        
        def apply_to_component(self, component):
            colors = self.get_colors()
            component.set_background(colors["background"])
            component.set_foreground(colors["foreground"])
    
    class DarkThemeState(ThemeState):
        def get_colors(self) -> dict:
            return {
                "background": "#2d2d2d",
                "foreground": "#ffffff",
                "accent": "#00b4ff",
                "border": "#404040"
            }
        
        def get_layout_style(self) -> dict:
            return {
                "padding": 12,
                "margin": 6,
                "border_width": 0,
                "shadow": False
            }
        
        def apply_to_component(self, component):
            colors = self.get_colors()
            component.set_background(colors["background"])
            component.set_foreground(colors["foreground"])
    
    class HighContrastThemeState(ThemeState):
        def get_colors(self) -> dict:
            return {
                "background": "#000000",
                "foreground": "#ffffff",
                "accent": "#ffff00",
                "border": "#ffffff"
            }
        
        def get_layout_style(self) -> dict:
            return {
                "padding": 15,
                "margin": 8,
                "border_width": 2,
                "shadow": False
            }
        
        def apply_to_component(self, component):
            colors = self.get_colors()
            component.set_background(colors["background"])
            component.set_foreground(colors["foreground"])
    
    class ThemeManager:
        def __init__(self):
            self._current_state = LightThemeState()
            self._states = {
                "light": LightThemeState(),
                "dark": DarkThemeState(),
                "high_contrast": HighContrastThemeState()
            }
        
        def set_theme(self, theme_name: str):
            if theme_name in self._states:
                self._current_state = self._states[theme_name]
                self._apply_current_theme()
        
        def _apply_current_theme(self):
            # 모든 UI 컴포넌트에 현재 테마 적용
            colors = self._current_state.get_colors()
            layout = self._current_state.get_layout_style()
            print(f"테마 적용: {colors}, 레이아웃: {layout}")
        
        def get_current_colors(self) -> dict:
            return self._current_state.get_colors()
        
        def get_current_layout(self) -> dict:
            return self._current_state.get_layout_style()
    

    장점: 새로운 테마 추가가 쉽고, 각 테마의 복잡한 설정을 독립적으로 관리할 수 있습니다.

    🎭 Adapter Pattern (어댑터 패턴)

    목적: 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 기존 클래스의 인터페이스를 다른 인터페이스로 변환하는 패턴

    핵심 개념: 기존 코드를 수정하지 않고도 새로운 인터페이스에 맞게 동작하도록 중간 변환 계층을 제공합니다.

    구현 예시 – 하드코딩 버튼을 JSON 설정으로 변환:

    pythonfrom typing import List, Dict, Any
    
    # 기존 하드코딩된 버튼 클래스들
    class LegacyWebButton:
        def __init__(self, url: str, title: str):
            self.url = url
            self.title = title
        
        def open_url(self):
            print(f"웹 브라우저로 {self.url} 열기")
    
    class LegacyProgramButton:
        def __init__(self, exe_path: str, name: str):
            self.exe_path = exe_path  
            self.name = name
        
        def launch_program(self):
            print(f"프로그램 {self.exe_path} 실행")
    
    # 새로운 JSON 기반 인터페이스
    class JsonButton:
        def __init__(self, config: Dict[str, Any]):
            self.config = config
        
        def execute(self):
            action_type = self.config.get('action_type')
            if action_type == 'web':
                print(f"웹: {self.config['url']} 열기")
            elif action_type == 'program':
                print(f"프로그램: {self.config['path']} 실행")
    
    # 어댑터 클래스들
    class LegacyWebButtonAdapter(JsonButton):
        def __init__(self, legacy_button: LegacyWebButton):
            self.legacy_button = legacy_button
            # 레거시 버튼을 JSON 형태로 변환
            config = {
                'text': legacy_button.title,
                'action_type': 'web',
                'url': legacy_button.url
            }
            super().__init__(config)
        
        def execute(self):
            # 레거시 메서드 호출
            self.legacy_button.open_url()
    
    class LegacyProgramButtonAdapter(JsonButton):
        def __init__(self, legacy_button: LegacyProgramButton):
            self.legacy_button = legacy_button
            config = {
                'text': legacy_button.name,
                'action_type': 'program', 
                'path': legacy_button.exe_path
            }
            super().__init__(config)
        
        def execute(self):
            self.legacy_button.launch_program()
    
    # 마이그레이션 도우미
    class ButtonMigrationAdapter:
        def __init__(self):
            self.buttons: List[JsonButton] = []
        
        def add_legacy_web_button(self, url: str, title: str):
            legacy_button = LegacyWebButton(url, title)
            adapter = LegacyWebButtonAdapter(legacy_button)
            self.buttons.append(adapter)
        
        def add_legacy_program_button(self, exe_path: str, name: str):
            legacy_button = LegacyProgramButton(exe_path, name)
            adapter = LegacyProgramButtonAdapter(legacy_button)
            self.buttons.append(adapter)
        
        def add_json_button(self, config: Dict[str, Any]):
            json_button = JsonButton(config)
            self.buttons.append(json_button)
        
        def execute_all(self):
            for button in self.buttons:
                button.execute()
    
    # 사용 예시
    migration = ButtonMigrationAdapter()
    
    # 기존 하드코딩 버튼들을 어댑터로 감싸서 추가
    migration.add_legacy_web_button("https://google.com", "구글")
    migration.add_legacy_program_button("notepad.exe", "메모장")
    
    # 새로운 JSON 기반 버튼도 함께 사용
    migration.add_json_button({
        'text': 'Visual Studio Code',
        'action_type': 'program',
        'path': 'code.exe'
    })
    
    migration.execute_all()  # 모든 버튼이 통일된 방식으로 실행됨
    

    장점: 기존 코드를 전면 수정하지 않고도 점진적으로 새로운 아키텍처로 마이그레이션할 수 있습니다.

    📝 Configuration Pattern (설정 패턴)

    목적: 애플리케이션의 동작을 런타임에 외부 설정으로 완전히 변경할 수 있게 하는 패턴

    핵심 개념: 하드코딩된 값들을 외부 설정 파일로 분리하여 코드 수정 없이 애플리케이션 동작을 변경할 수 있습니다.

    구현 예시 – 완전한 런타임 설정 변경:

    pythonimport json
    import os
    from typing import Dict, Any, Optional
    from dataclasses import dataclass, asdict
    from pathlib import Path
    
    @dataclass
    class WindowConfig:
        width: int = 800
        height: int = 600
        resizable: bool = True
        always_on_top: bool = False
    
    @dataclass  
    class ThemeConfig:
        name: str = "default"
        background_color: str = "#ffffff"
        text_color: str = "#000000"
        accent_color: str = "#0078d4"
    
    @dataclass
    class ButtonConfig:
        text: str
        action_type: str  # web, program, file_dialog
        action_data: Dict[str, Any]
        position: tuple = (0, 0)
        size: tuple = (100, 30)
    
    @dataclass
    class ApplicationConfig:
        app_title: str = "동적 런처"
        window: WindowConfig = None
        theme: ThemeConfig = None
        buttons: list = None
        auto_save_config: bool = True
        config_file_path: str = "config.json"
        
        def __post_init__(self):
            if self.window is None:
                self.window = WindowConfig()
            if self.theme is None:
                self.theme = ThemeConfig()
            if self.buttons is None:
                self.buttons = []
    
    class ConfigurationManager:
        def __init__(self, config_path: str = "config.json"):
            self.config_path = Path(config_path)
            self.config: Optional[ApplicationConfig] = None
            self._watchers = []  # 설정 변경 감시자들
        
        def load_config(self) -> ApplicationConfig:
            """JSON 파일에서 설정 로드"""
            if self.config_path.exists():
                try:
                    with open(self.config_path, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                        self.config = self._dict_to_config(data)
                except Exception as e:
                    print(f"설정 로드 실패: {e}")
                    self.config = ApplicationConfig()
            else:
                self.config = ApplicationConfig()
                self.save_config()  # 기본 설정 파일 생성
            
            return self.config
        
        def save_config(self):
            """현재 설정을 JSON 파일로 저장"""
            if self.config:
                try:
                    with open(self.config_path, 'w', encoding='utf-8') as f:
                        data = self._config_to_dict(self.config)
                        json.dump(data, f, indent=2, ensure_ascii=False)
                except Exception as e:
                    print(f"설정 저장 실패: {e}")
        
        def _dict_to_config(self, data: Dict[str, Any]) -> ApplicationConfig:
            """딕셔너리를 ApplicationConfig 객체로 변환"""
            window_data = data.get('window', {})
            theme_data = data.get('theme', {})
            
            window = WindowConfig(**window_data)
            theme = ThemeConfig(**theme_data)
            
            buttons = []
            for btn_data in data.get('buttons', []):
                button = ButtonConfig(**btn_data)
                buttons.append(button)
            
            config = ApplicationConfig(
                app_title=data.get('app_title', '동적 런처'),
                window=window,
                theme=theme,
                buttons=buttons,
                auto_save_config=data.get('auto_save_config', True),
                config_file_path=data.get('config_file_path', 'config.json')
            )
            
            return config
        
        def _config_to_dict(self, config: ApplicationConfig) -> Dict[str, Any]:
            """ApplicationConfig 객체를 딕셔너리로 변환"""
            return {
                'app_title': config.app_title,
                'window': asdict(config.window),
                'theme': asdict(config.theme),
                'buttons': [asdict(btn) for btn in config.buttons],
                'auto_save_config': config.auto_save_config,
                'config_file_path': config.config_file_path
            }
        
        def update_config(self, **kwargs):
            """설정 업데이트"""
            if self.config:
                for key, value in kwargs.items():
                    if hasattr(self.config, key):
                        setattr(self.config, key, value)
                
                if self.config.auto_save_config:
                    self.save_config()
                
                self._notify_watchers()
        
        def add_button(self, text: str, action_type: str, action_data: Dict[str, Any], 
                       position: tuple = (0, 0), size: tuple = (100, 30)):
            """새 버튼 추가"""
            if self.config:
                button = ButtonConfig(text, action_type, action_data, position, size)
                self.config.buttons.append(button)
                
                if self.config.auto_save_config:
                    self.save_config()
                
                self._notify_watchers()
        
        def remove_button(self, index: int):
            """버튼 제거"""
            if self.config and 0 <= index < len(self.config.buttons):
                del self.config.buttons[index]
                
                if self.config.auto_save_config:
                    self.save_config()
                
                self._notify_watchers()
        
        def watch_changes(self, callback):
            """설정 변경 감시자 등록"""
            self._watchers.append(callback)
        
        def _notify_watchers(self):
            """모든 감시자에게 변경 알림"""
            for watcher in self._watchers:
                watcher(self.config)
    
    # 사용 예시
    def on_config_changed(config: ApplicationConfig):
        print(f"설정 변경됨: {config.app_title}")
        print(f"버튼 개수: {len(config.buttons)}")
    
    # 설정 관리자 초기화
    config_manager = ConfigurationManager("launcher_config.json")
    config_manager.watch_changes(on_config_changed)
    
    # 설정 로드
    app_config = config_manager.load_config()
    
    # 런타임에 버튼 추가
    config_manager.add_button(
        "GitHub Desktop", 
        "program", 
        {"path": "C:/Users/User/AppData/Local/GitHubDesktop/GitHubDesktop.exe"},
        (10, 10),
        (150, 40)
    )
    
    # 테마 변경
    config_manager.update_config(theme=ThemeConfig(
        name="dark",
        background_color="#2d2d2d", 
        text_color="#ffffff",
        accent_color="#00b4ff"
    ))
    

    장점: JSON 파일만 수정하면 애플리케이션의 모든 동작을 변경할 수 있어 개발자가 아닌 사용자도 커스터마이징이 가능합니다.

    패턴들의 상호작용과 조합

    이러한 패턴들은 독립적으로 사용될 수도 있지만, 실제 프로젝트에서는 여러 패턴을 조합하여 더욱 강력하고 유연한 아키텍처를 만들 수 있습니다:

    일반적인 조합 예시:

    • Factory + Strategy: 설정에 따라 다른 전략 객체를 생성
    • Observer + Command: 명령 실행 상태를 여러 UI 컴포넌트에 알림
    • Builder + Dependency Injection: 복잡한 의존성 그래프를 단계별로 구성
    • Configuration + Template Method: 설정에 따라 초기화 과정의 각 단계를 다르게 구현

    이러한 디자인 패턴들을 적절히 활용하면 코드의 가독성, 유지보수성, 확장성을 크게 향상시킬 수 있으며, 특히 사용자 요구사항이 자주 변경되는 프로젝트에서 그 진가를 발휘합니다.