반응형

딥시크(V3)가 유용한 이유

MS Copilot, Google Gemini로는 불가능에 가까운 프로그래밍 코딩을 딥시크는 훨씬 빠르고 정확하게 알려줍니다.

Copilot은 자사 프로그램과 언어인 Visual Studio, C#을 정확히 다룰 줄 모르며 연계 질문이 많아지면 버벅거리고, 오답이 늘어납니다. 제미나이는 연속 채팅이 불가능해서 사용하기 불편합니다.

딥시크 채팅 서버의 현재시점 업데이트 날짜는 2024년 7월입니다. 2025년 1월에는 업데이트 날짜가 2023년 10월이었는데 수개월분이 추가로 학습되었습니다.🤓

질문 모음

딥시크는 연속 채팅으로 연계 질문이 가능합니다. 한국어로 물어보면 한국어로 답합니다.

  • C# 플러그인 방식으로 프로그래밍할 거야.
  • Hook와 같은 메서드 실행 전/후 처리를 할 수 있게 해 줘.
  • 인터페이스로 처리해 줘.
  • 컨트롤에 현재시각을 표시하는 예제를 알려줘

최종 결과

인터페이스 프로젝트

dll 파일로 컴파일해서 프로그램과 플러그인이 공유할 수 있도록 합니다.

IPlugin

    public interface IPlugin
    {
        string Name { get; }
        void Initialize(PluginManager pluginManager);
    }

ILogger

    public interface ILogger
    {
        void Log(string message);
    }

IStatusStripProvider

public interface IStatusStripProvider
{
    StatusStrip GetStatusStrip();
}

EventNames

    public enum EventNames
    {
        OnProgramStarted,
        OnProgramStopped,
        BeforeFormLoad,
        AfterFormLoad,
        RequestControl,
    }

Controls

    public enum Controls
    {
        StatusStrip,
        RichTextBox_Log,
    }

ControlRequestContext

    public class ControlRequestContext
    {
        public Controls ControlName { get; set; }
        public Control Control { get; set; }
    }

PluginManager

using System.Reflection;

namespace XmlEditor.Plugins
{
    public class PluginManager
    {
        private readonly Dictionary<EventNames, Action<object>> _events = new Dictionary<EventNames, Action<object>>();

        public ILogger Logger { get; }
        private Form _mainForm;

        public PluginManager(ILogger logger)
        {
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
        public PluginManager(ILogger logger, Form mainForm)
        {
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _mainForm = mainForm ?? throw new ArgumentNullException(nameof(mainForm));
        }

        // 이벤트 구독
        public void Subscribe(EventNames eventName, Action<object> handler)
        {
            if (_events.ContainsKey(eventName))
            {
                _events[eventName] += handler;
            }
            else
            {
                _events[eventName] = handler;
            }
        }

        // 이벤트 구독 해제
        public void Unsubscribe(EventNames eventName, Action<object> handler)
        {
            if (_events.ContainsKey(eventName))
            {
                _events[eventName] -= handler;
            }
        }

        // 이벤트 발생
        public void RaiseEvent(EventNames eventName, object context)
        {
            if (_events.ContainsKey(eventName))
            {
                _events[eventName]?.Invoke(context);
            }
        }

        // 플러그인 로드 및 초기화
        public void LoadPlugins(string pluginDirectory)
        {
            // 플러그인 루트 디렉토리가 존재하는지 확인
            if (!Directory.Exists(pluginDirectory))
            {
                Logger.Log($"Plugin directory not found: {pluginDirectory}");
                Directory.CreateDirectory(pluginDirectory);
                return;
            }

            // 플러그인 디렉토리의 모든 하위 폴더 탐색
            foreach (var pluginFolder in Directory.GetDirectories(pluginDirectory))
            {
                string pluginName = Path.GetFileName(pluginFolder); // 폴더 이름을 플러그인 이름으로 사용
                Logger.Log($"Loading plugins from folder: {pluginName}");

                // 플러그인 루트 디렉토리 내의 모든 폴더 탐색
                foreach (var dllFile in Directory.GetFiles(pluginFolder, "*.dll"))
                {
                    try
                    {
                        Assembly assembly = Assembly.LoadFrom(dllFile);
                        foreach (var type in assembly.GetTypes())
                        {
                            if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface)
                            {
                                IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
                                Logger.Log($"Loaded plugin: {plugin.Name}");
                                plugin?.Initialize(this); // 플러그인 초기화
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Log($"Failed to load plugin from {dllFile}: {ex.Message}");
                    }
                }
            }
        }
    }
}

Windows 프로젝트

exe 파일로 컴파일합니다.

MainForm

    public partial class MainForm : Form
    {
        private PluginManager _pluginManager;

        public MainForm()
        {
            InitializeComponent();

            _pluginManager = new(new ControlLogger(richTextBox_Log), this);
            // RequestControl 이벤트 핸들러 등록
            _pluginManager.Subscribe(EventNames.RequestControl, OnRequestControl);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 플러그인 로드
            string pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
            _pluginManager.LoadPlugins(pluginDirectory);

            // 전처리 이벤트 발생
            _pluginManager.RaiseEvent(EventNames.BeforeFormLoad, sender);
        }
    }

플러그인 프로젝트

사용자 정의 플러그인입니다. dll로 컴파일합니다.

ClockPlugin

namespace XmlEditor.Plugins

{
    public class ClockPlugin : IPlugin
    {
        private System.Windows.Forms.Timer _timer;
        private ToolStripStatusLabel _clockLabel;
        private ILogger _logger;
        private PluginManager _pluginManager;

        public string Name => "ClockPlugin";

        public void Initialize(PluginManager pluginManager)
        {
            _pluginManager = pluginManager; // 프로그램 참조 저장
            _logger = pluginManager.Logger; // ILogger를 멤버 변수로 저장

            // 필요한 이벤트 구독
            _pluginManager.Subscribe(EventNames.BeforeFormLoad, OnBeforeFormLoad);
            _pluginManager.Subscribe(EventNames.AfterFormLoad, OnAfterDoSomething);

            pluginManager.Logger.Log($"[{Name}] Initialized.");
        }

        private void OnBeforeFormLoad(object context)
        {
            if (context == null)
            {
                _logger.Log($"[{Name}] Context is null.");
                return;
            }

            // StatusStrip 요청
            var controlRequest = new ControlRequestContext { ControlName = Controls.StatusStrip };
            _pluginManager.RaiseEvent(EventNames.RequestControl, controlRequest);

            if (controlRequest.Control is StatusStrip statusStrip)
            {
                // ToolStripStatusLabel 추가
                _clockLabel = new ToolStripStatusLabel();
                statusStrip.Items.Add(_clockLabel);

                // 타이머 설정 (1초마다 현재 시각 업데이트)
                _timer = new System.Windows.Forms.Timer();
                _timer.Interval = 1000; // 1초
                _timer.Tick += (sender, e) => UpdateClock();
                _timer.Start();

                _logger.Log($"[{Name}] Clock added to StatusStrip.");
            }
            else
            {
                _logger.Log($"[{Name}] StatusStrip not found.");
            }
        }

        private void OnAfterDoSomething(object context)
        {
            if (context == null)
            {
                _logger.Log($"[{Name}] Context is null.");
                return;
            }
        }


        private void UpdateClock()
        {
            if (_clockLabel != null)
            {
                _clockLabel.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            }
        }
    }
}

실행 결과

모두 빌드 후, 플러그인의 dll 파일을 실행 프로그램 경로의 Plugings\플러그인명\폴더에 넣어두면 실행이 됩니다.

로깅 문자열과 함께 현재 시각이 표시됩니다.

딥시크 덕분에 삽질하거나 책을 찾아보거나 Stack Overflow에서 필요한 내용을 뒤지는 시간이 줄어들었습니다.

문제점

private 이름에 대해 _로 시작하는 구식 이름 표기법을 사용함🤣➡️ 첫 글자는 소문자로 시작해야 함.

클래스 인스턴스화에 대해 구식 방식인 var 이름 = new 형식();을 사용함🤣➡️ 동적 형식이 아니면 형식 이름 = new();을 사용해야 함.

반응형

관련글