[로봇활용_11주차] C# 이벤트(Event)

최윤호·2025년 10월 26일
post-thumbnail

이벤트(Event): 객체 간의 소통

객체 지향 프로그래밍을 하다 보면, 한 객체에서 어떤 일이 발생했을 때,
그 사실을 다른 여러 객체에 알려줘야 하는 상황이 정말 많습니다.

"플레이어의 게임 캐릭터가 레벨업을 했습니다!
이 사실을 UI 관리자에게 알려서 화면에 '레벨업!' 이펙트를 띄우고,
사운드 관리자에게 알려서 축하 효과음을 재생해야 해!"

이때 Player클래스가 UIManagerSoundManager를 직접 알고 있다면 어떻게 될까요?
코드가 서로 단단히 얽혀서(Tight Coupling), 나중에 파티클 효과를 추가하려면
Player클래스 코드를 또 수정해야 합니다. 이런 문제를 해결하기 위해
C#은 이벤트(Event)라는 발행-구독(Publish-Subscribe) 패턴을 제공합니다.
이번 글에서는 C# 이벤트의 핵심 원리를 파헤치고, 이것이 어떻게 객체들을
느슨하게 연결하여 유연하고 확장할 수 있는 코드를 만드는지 알아보겠습니다!

1)이벤트의 개념: 발행-구독

이벤트를 이해하는 가장 좋은 방법은 '유튜브 채널'을 떠올리는 것입니다.

  • 게시자 (Publisher / 유튜버): 이벤트를 발생시키는 주체.
    "새 영상이 올라왔습니다!"라고 알림을 보냅니다. (예: Player클래스)
  • 구독자 (Subscriber / 구독자): 이벤트에 관심이 있어 미리 '구독'을 신청한 객체들.
    알림을 받으면 각자 정해진 행동을 합니다. (예: UIManager, SoundManager)
  • 이벤트 (Event / 알림): 게시자가 발행하는 '사건' 그 자체. (예: OnLevelUp)

가장 중요한 점은, 유튜버는 자신의 구독자가 누구인지, 전혀 알 필요가 없다는 것입니다.
이 덕분에 새로운 구독자가 생기거나 기존 구독자가 떠나도, 유튜버는 영향받지 않습니다.
이것이 바로 느슨한 결합(Loose Coupling)의 핵심입니다.

2)event 키워드: 안전장치

"멀티캐스트 대리자 기능이랑 event키워드는 똑같은 거 아닌가요?"

C#에서 이벤트의 내부 메커니즘은 멀티캐스트 대리자를 기반으로 합니다.
하지만 그냥 public대리자를 사용하는 것과 event키워드를 붙이는 것에는
안정성에서 매우 크게 차이 납니다.

1. public 대리자의 위험성

만약 Player클래스에 public대리자를 그냥 선언하면 어떤 문제가 생길까요?

public class Player
{
    // 그냥 public 대리자 (나쁜 예!)
    public Action OnLevelUp;

    public void LevelUp()
    {
        // ... 레벨업 로직 ...
        OnLevelUp?.Invoke(); // 구독자들에게 알림
    }
}
  1. 외부에서 강제 실행 가능: Player클래스 외부의 아무 코드나
    player.OnLevelUp()을 호출해서 가짜 레벨업 알림을 보낼 수 있습니다.
    (구독자가 유튜버 계정을 해킹해서 마음대로 알림을 보내는 격!)
  2. 구독자 목록 초기화: 외부에서 player.OnLevelUp = null;코드를
    실행하면, 힘들게 등록했던 모든 구독자가 한 번에 사라져 버립니다.
    (=할당으로 덮어쓰기 가능)

2. event 키워드의 안정성

event키워드는 대리자에게 두 가지 강력한 안전장치를 걸어줍니다.

public class Player
{
    // event 키워드로 안전하게 보호!
    public event Action OnLevelUp;
    // ...
}
  1. 게시자만 이벤트를 발생: OnLevelUp이벤트는 Player클래스 내부에서만
    호출(Invoke)할 수 있습니다. 외부에서는 절대 불가능합니다.
  2. 구독/구독 취소만 허용:
    외부에서는 +=(구독)와 -=(구독 취소) 연산자만 사용할 수 있습니다.
    =연산자로 구독자 목록을 통째로 덮어쓰는 행위가 원천적으로 차단됩니다.

3)이벤트 직접 만들기

이제 Player, UIManager, SoundManager를 이용해 레벨업 알림 시스템을 구축해 봅시다.

[코드]

// 1. 게시자(Publisher) 클래스 구현
public class Player
{
    // 이벤트 선언
    public event Action OnLevelUp;

    private int _level = 1;

    public void GainExperience(int amount)
    {
        Console.WriteLine("플레이어가 경험치를 얻었습니다.");
        // 경험치 획득 후 레벨업 조건을 만족했다고 가정

        _level++;
        Console.WriteLine($"레벨 업! 현재 레벨: {_level}");

        // 이벤트 발생! (게시)
        // OnLevelUp이 null이 아닐 때만 호출 (구독자가 한 명이라도 있을 때)
        OnLevelUp?.Invoke();
    }
}

// 2. 구독자(Subscriber) 클래스 구현
public class UIManager
{
    // 이벤트 핸들러(EventHandler) 메서드
    // 이벤트가 발생했을 때 실행될 실제 로직
    public void OnPlayerLevelUp_ShowEffect()
    {
        Console.WriteLine("[UI] 레벨업 이펙트가 화면에 표시됩니다!");
    }
}

public class SoundManager
{
    public void OnPlayerLevelUp_PlaySound()
    {
        Console.WriteLine("[Sound] 빰빠밤~ 레벨업 축하 사운드가 재생됩니다!");
    }
}

// 3. 이벤트 연결 및 실행
class Program
{
    static void Main()
    {
        Player player = new Player();
        UIManager ui = new UIManager();
        SoundManager sound = new SoundManager();

        // 이벤트 구독!
        // Player의 OnLevelUp 이벤트에 각 관리자의 메서드를 연결(+=)
        player.OnLevelUp += ui.OnPlayerLevelUp_ShowEffect;
        player.OnLevelUp += sound.OnPlayerLevelUp_PlaySound;

        // 이벤트 발생의 트리거가 되는 행동 실행
        player.GainExperience(100);
    }
}

[실행 결과]

플레이어가 경험치를 얻었습니다.
레벨 업! 현재 레벨: 2
[UI] 레벨업 이펙트가 화면에 표시됩니다!
[Sound] 빰빠밤~ 레벨업 축하 사운드가 재생됩니다!

PlayerUIManagerSoundManager의 존재를 전혀 모르지만,
레벨업을 하자 구독을 신청했던 두 객체의 메서드가 모두 실행되었습니다!

4)정리

이벤트는 C#에서 객체 간의 상호작용을 설계하는 가장 중요하고 기본적인 패턴입니다.

개념역할설명
게시자이벤트 소유 및 발생"사건 발생!"을 알리는 객체
구독자이벤트 처리알림을 받고 특정 동작을 수행하는 객체
이벤트 핸들러이벤트 발생 시 실행될 메서드구독자가 제공하는 실제 처리 로직
event 키워드대리자 보호외부에서의 임의 호출 및 덮어쓰기를 방지하는 안전장치
profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글