[C#] 델리게이트는 왜 사용해야 할까?

shin0112·2025년 10월 13일
0

CSHARP

목록 보기
1/5

✨ 들어가며

오늘은 C# 강의를 듣다가 “델리게이트를 왜 써야 하지?” 라는 의문이 생겼다.
결국 델리게이트는 함수를 이어붙이는 개념인데, “그냥 함수를 호출하면 되지 않을까?” 싶었다.

그러다 예전에 만들었던 TEXT_RPG 과제가 떠올랐다.
그때 멘토님이 이런 말씀을 하셨다.

“게임은 기획이 자주 바뀌니까, 수정하지 않고도 확장할 수 있는 구조로 짜야 합니다.”

그 말이 무슨 뜻인지는 이해했지만, 그 구조가 구체적으로 어떤 방식으로 구현되는지,
그리고 왜 델리게이트(delegate) 같은 개념이 그 해답이 되는지는 그 당시엔 잘 와닿지 않았다.

그래서 직접 여러 클래스를 만들어 함수를 직접 호출하는 방식델리게이트(이벤트) 기반 구조를 비교하고, 코드의 흐름을 따라가며 델리게이트를 왜 사용하는지 한 번 알아보려고 한다.

1️⃣ 현재 구조: 강한 결합 방식

TEXT_RPG를 만들 때, 대부분의 클래스가 GameManager를 통해 서로 연결되도록 만들었다.
이 구조는 처음엔 직관적이었고, 어디서든 접근할 수 있어서 편리했다.

하지만 점점 기능이 많아질수록 의존 관계가 꼬이고, 한 부분을 수정하면 다른 클래스까지 영향을 받기 시작했다.

예를 들어, 아래의 Player 클래스 안에 있는 SpendStamina()를 보자.

public bool SpendStamina(int stamina)
{
    if (Stamina >= stamina)
    {
        Stamina -= stamina;
        return true;
    }
    else
    {
        GameManager.Instance.HeaderText = "스테미나가 부족합니다.";
        return false;
    }
}

이 코드는 정상적으로 작동하지만, Player가 직접 GameManager를 알고 있다는 점이 문제다.

  • Player는 원래 게임 로직만 담당해야 하는데, 지금은 UI에 관련된 역할(HeaderText 수정)까지 하고 있다.

  • 즉, PlayerGameManager가 강하게 결합되어 있다. = “강한 결합(Strong Coupling)” 구조

이 상태에서 UI 구조를 바꾸거나 Manager를 분리하면 Player 내부 코드까지 손봐야 한다.
결국 “수정에 닫혀 있지 못한 구조”가 되어버린다.

2️⃣ 델리게이트를 활용한 느슨한 결합

여기서 바로 델리게이트(delegate) 를 사용한다면, 문제가 해결된다.
PlayerGameManager를 직접 호출하지 않고, 그저 “함수를 넣어주는 방식”으로 동작을 넘길 수 있기 때문이다.

public class Player
{
    public event Action<string>? OnNotify;

    public bool SpendStamina(int stamina)
    {
        if (Stamina >= stamina)
        {
            Stamina -= stamina;
            return true;
        }
        else
        {
            OnNotify?.Invoke("스테미나가 부족합니다."); // 단순히 신호만 보냄
            return false;
        }
    }
}

이제 Player는 누가 그걸 받을지는 모른채, 단지 “메시지를 보낼 수 있다”는 사실만 알고 있다.

외부에서는 이렇게 함수를 등록해주기만 하면 된다.

Player player = new Player();
player.OnNotify += (msg) => GameManager.Instance.HeaderText = msg;

이제 PlayerGameManager의 존재를 완전히 모른다.
UI 구조가 바뀌거나 Manager가 분리되어도 Player는 수정할 필요가 없다.
그저 이벤트를 발행하고, 외부에서 처리하면 된다.

3️⃣ 구조 비교

항목기존 구조델리게이트 구조
호출 방식PlayerGameManager를 직접 호출Player가 이벤트를 발행
결합도높음 (강한 연결)낮음 (느슨한 연결)
수정 시 영향Manager 수정 시 Player도 수정 필요Manager 수정 시 Player 그대로
확장성기능 추가 시 Player 수정 필요구독만 추가하면 확장 가능

4️⃣ 델리게이트 도입 전후 구조 비교

마지막으로 두 개의 클래스 다이어그램을 직접 그려보았다.
하나는 기존 구조(직접 호출 방식), 다른 하나는 델리게이트 기반 구조다.

첫 번째 다이어그램에서는 PlayerGameManager를 직접 참조하고 있다.
즉, Player는 자신의 로직뿐 아니라 UI와 Manager의 상태까지 알아야 하며, UI 시스템이 바뀌면 Player 내부 코드도 함께 수정해야 한다.
이건 단일 책임 원칙(SRP)OCP(개방-폐쇄 원칙) 모두 위반하는 구조다.

반면 두 번째 다이어그램에서는 PlayerGameManager를 직접 알지 않는다.
Player는 단지 “이벤트를 발생시킨다”는 역할만 수행하고, GameManager는 그 이벤트를 구독(subscribe) 하여 필요한 처리를 수행한다.

이 구조에서는

  • Player의 내부 로직을 수정하지 않아도 새로운 동작을 얼마든지 추가할 수 있고,
  • Manager나 UI를 변경하더라도 Player에는 전혀 영향이 없다.

즉, 의존 관계가 단방향으로 정리되며, 코드는 확장에 강하고 유지보수가 쉬운 형태로 진화했다.

결국 델리게이트는 단순히 “함수를 넘기는 문법”이 아니라, 객체 간 결합을 끊고 흐름을 유연하게 바꾸는 설계 도구라는 것이다.

✍️ 마치며

델리게이트는 단순히 “함수를 이어붙이는 문법”이 아니라, 객체 간의 결합을 끊고 구조를 유연하게 만드는 장치다.
Player는 이제 “무슨 일이 일어났다”는 사실만 알리고, 그걸 어떻게 처리할지는 외부가 담당한다.

이렇게 역할을 분리하면 코드는 자연스럽게 확장에 열리고, 수정에는 닫힌(OCP) 구조로 발전한다.

✍️ 한줄 요약
델리게이트는 객체 간의 직접 연결을 끊고,
“이벤트 기반의 유연한 구조”를 만드는 핵심 도구다.

0개의 댓글