델리게이트란?

상태
완료
담당자
날짜
숫자
0
여러분. 델리게이트가 무엇인지 아시나요? 델리게이트는 C#에서 굉장히 중요한 기능으로 이벤트 처리와 메서드 호출을 유연하게 해주는데요.
저도 처음에는 델리게이트를 사용해야하는 이유를 잘 못 느꼈습니다. 아직은 여러분이 간단한 코드들을 작성하다보니 더 필요를 못 느낄수 있습니다. 하지만 점점 복잡한 코드들을 작성하며 다양한 로직들을 구현하다보면 필요해질 것입니다.
그럼 델리게이트의 기본 개념과 사용법, 그리고 코드를 통한 델리게이트의 활용 사례를 보며 학습해보겠습니다.

델리게이트(Delegate)란?

델리게이트는 C#에서 메서드의 참조를 저장하는 형식입니다.
이를 통해, 특정 메서드를 나중에 호출하거나, 여러 메서드를 하나로 묶어서 관리할 수 있습니다.
델리게이트는 메서드를 객체처럼 다루기 때문에, 메서드 호출을 직접 코드에 작성하는 대신, 델리게이트를 통해 호출할 수 있습니다. 이는 메서드 호출의 유연성을 높여주며, 특히 이벤트 처리와 콜백 메서드에서 유용합니다.
이렇게만 보시면 무슨 이야기이신지 이해가 잘 안가실테니 먼저 델리게이트의 기본 문법을 알아보고 델리게이트 사용 유무에 따른 차이점을 코드를 통해 설명하겠습니다.

델리게이트 기본 문법

1. 델리게이트 선언

// 델리게이트 선언 public delegate void MyDelegate();
C#
복사
public: 접근 제한자로, 다른 클래스에서도 이 델리게이트 사용 가능하게 함
delegate: 델리게이트를 정의할 때 사용하는 키워드
void: 델리게이트가 참조할 메서드의 반환 형식. 이 경우, 반환 값이 없는 메서드를 참조
MyDelegate: 델리게이트의 이름

2. 델리게이트에 메서드 추가

// 델리게이트 인스턴스 생성 및 메서드 추가 MyDelegate del = new MyDelegate(SomeMethod); del += AnotherMethod; // del -= AnotherMethod; = AnotherMethod 메서드 제외
C#
복사
MyDelegate: 델리게이트의 타입
del: 델리게이트의 인스턴스 이름입니다.
new MyDelegate(SomeMethod): SomeMethod라는 메서드를 델리게이트에 연결하여, del 델리게이트 객체가 SomeMethod를 참조하도록 함.
AnotherMethod: del 델리게이트에 추가로 참조할 메서드
델리게이트 del은 이제 SomeMethodAnotherMethod를 모두 참조하게 됨.

3. 델리게이트 호출

// 델리게이트 호출 del?.Invoke();
C#
복사
델리게이트 객체 del을 호출합니다.
Invoke()는 델리게이트가 참조하는 모든 메서드를 순차적으로 호출합니다.
만약 델리게이트가 null이라면 Invoke()를 호출할 수 없습니다. ?.Invoke() 패턴을 사용하면 델리게이트가 null인지 확인한 후 호출할 수 있습니다.
del 델리게이트가 참조하는 SomeMethodAnotherMethod가 순서대로 호출됩니다.
자. 여기까지 델리게이트 기본 문법에 대해 알아보았습니다. 이제 델리게이트 사용 유무에 따른 코드의 차이점을 보며, 델리게이트의 필요성과 델리게이트 사용 예시를 알아보겠습니다.

코드 예시

여러분이 게임 캐릭터를 만들어본다고 가정하겠습니다. 그 캐릭터에는 다양한 능력치가 있겠죠? 체력도 있을 수 있고, 마나, 힘, 방어력, 스피드 등 다양한 속성이 있을 수 있습니다. 게임 캐릭터가 레벨업을 할 때마다 속성이 증가한다고 할 때, 코드를 예시로 알아보겠습니다.
먼저, 델리게이트를 쓰지 않았을 때의 코드를 먼저 보겠습니다.
using System; namespace GameCharacter { public class Character { public int Level { get; private set; } public int Health { get; private set; } public int Strength { get; private set; } public Character() { Level = 1; Health = 100; Strength = 10; } // 레벨업 메서드 public void LevelUp() { Level++; IncreaseHealth(); IncreaseStrength(); } // 속성 증가 메서드 private void IncreaseHealth() => Health += 20; private void IncreaseStrength() => Strength += 5; } class Program { static void Main(string[] args) { Character character = new Character(); character.LevelUp(); } } }
C#
복사
델리게이트를 사용하지 않은 경우, 레벨업 메서드에서 직접 각 속성 증가 메서드를 호출합니다. 이렇게 하면 레벨업 과정에서 속성을 증가시키는 방법을 변경하거나 추가할 때, 코드를 수정해야 할 부분이 많아집니다. 이는 코드 유지보수에 어려움을 줄 수 있습니다.
그렇다면, 델리게이트를 사용했을 경우의 코드를 보겠습니다.
using System; namespace GameCharacter { // 캐릭터 클래스 public class Character { public int Level { get; private set; } public int Health { get; private set; } public int Strength { get; private set; } // 델리게이트 선언 public delegate void LevelUpDelegate(); public LevelUpDelegate OnLevelUp; public Character() { Level = 1; Health = 100; Strength = 10; // 델리게이트에 메서드 추가 OnLevelUp += IncreaseHealth; OnLevelUp += IncreaseStrength; } // 레벨업 메서드 public void LevelUp() { Level++; OnLevelUp?.Invoke(); // 델리게이트 호출 } // 속성 증가 메서드 private void IncreaseHealth() => Health += 20; private void IncreaseStrength() => Strength += 5; } class Program { static void Main(string[] args) { Character character = new Character(); character.LevelUp(); } } }
C#
복사
위 코드에서 캐릭터가 레벨업할 때마다 속성이 증가합니다. 델리게이트를 사용해 각 속성 증가 메서드를 OnLevelUp 델리게이트에 등록하고, 레벨업 시 OnLevelUp을 호출합니다.
그렇다면 델리게이트를 사용하지 않고 코드를 작성했을 때의 문제점에 대해 알아보고 델리게이트를 사용하면 어떻게 해결이 가능한지 코드를 보며 알아보겠습니다.

델리게이트를 사용하지 않았을 때의 문제점

1. 코드 중복 및 수정 어려움

예를 들어, 새로운 속성 ‘Speed’가 추가되었다고 가정해봅시다. 그러면 레벨업 메서드에 다음과 같이 새로운 메서드를 추가해야합니다.
// IncreaseSpeed(); : Speed 속성 증가 메서드 public void LevelUp() { Level++; IncreaseHealth(); IncreaseStrength(); IncreaseSpeed(); // 새로운 속성 증가 메서드 추가 } private void IncreaseSpeed() => Speed += 4;
C#
복사
이렇게 하면 코드의 가독성이 떨어지고, 유지보수가 어려워집니다.
그렇다면 델리게이트를 사용하여 코드를 작성하면 어떻게 될까요?
public Character() { Level = 1; Health = 100; Strength = 10; Agility = 5; OnLevelUp += IncreaseHealth; OnLevelUp += IncreaseStrength; OnLevelUp += IncreaseSpeed; } // 새로운 속성 증가 메서드 추가 OnLevelUp += IncreaseSpeed; private void IncreaseSpeed() => Speed += 4;
C#
복사
델리게이트에 메서드를 등록하고, 레벨업 시 델리게이트를 호출하기만 하면 되므로, 새로운 속성 증가 메서드를 추가하거나 기존 메서드를 수정할 때 레벨업 메서드를 변경할 필요가 없습니다.

2. 확장성 문제

또한, 만약 레벨업 시 증가할 속성의 규칙이 자주 변경되거나, 조건에 따라 다르게 적용되어야 하는 경우, 레벨업 메서드는 더욱 복잡해집니다.
예를 들어, 특정 레벨에서는 체력이 더 많이 증가하고, 다른 레벨에서는 힘이 더 많이 증가해야한다고 가정해봅시다.
public void LevelUp() { Level++; if (Level % 5 == 0) { IncreaseHealth(40); // 특정 레벨에서는 Health가 더 많이 증가 } else { IncreaseHealth(20); } if (Level % 3 == 0) { IncreaseStrength(10); // 특정 레벨에서는 Strength가 더 많이 증가 } else { IncreaseStrength(5); } IncreaseSpeed(); } private void IncreaseHealth(int amount) => Health += amount; private void IncreaseStrength(int amount) => Strength += amount;
C#
복사
이런 식으로 조건에 따라 메서드 호출을 다르게 해야 할 때마다 레벨업 메서드가 길어지고 복잡해집니다. 여러분이 이외에도 조건을 추가하면 할수록 레벨업 메서드는 더욱 길어지고 복잡해질 것입니다.
하지만 델리게이트를 사용하면 조건에 따라 델리게이트에 등록된 메서드를 동적으로 변경할 수 있어, 레벨업 메서드를 간결하게 유지할 수 있습니다.
using System; namespace GameCharacter { public class Character { public int Level { get; private set; } public int Health { get; private set; } public int Strength { get; private set; } public int Speed { get; private set; } public delegate void LevelUpDelegate(); public LevelUpDelegate OnLevelUp; public Character() { Level = 1; Health = 100; Strength = 10; Speed = 5; // 기본 레벨업 델리게이트 설정 OnLevelUp += IncreaseHealth; OnLevelUp += IncreaseStrength; OnLevelUp += IncreaseSpeed; } public void LevelUp() { Level++; if (Level % 5 == 0) { // 특별한 레벨업 델리게이트로 변경 OnLevelUp = SpecialLevelUp; } OnLevelUp?.Invoke(); } private void SpecialLevelUp() { IncreaseHealth(40); IncreaseStrength(10); IncreaseSpeed(); } private void IncreaseHealth() => Health += 20; private void IncreaseStrength() => Strength += 5; private void IncreaseSpeed() => Speed += 3; } class Program { static void Main(string[] args) { Character character = new Character(); character.LevelUp(); } } }
C#
복사
이 코드에서 OnLevelUp 델리게이트는 기본적으로 속성 증가 메서드를 포함합니다. 레벨 업 시 특정 조건이 충족되면 OnLevelUp 델리게이트를 SpecialLevelUp 메서드로 변경하여 해당 조건에 맞는 속성 증가를 적용합니다.

3. 테스트와 디버깅 어려움

델리게이트를 사용하지 않고 코드 작성 시
레벨업 메서드가 복잡해질수록, 각 속성 증가 메서드가 올바르게 호출되고 있는지 확인하는 것이 어려워집니다. 레벨업 메서드의 복잡한 로직을 통합적으로 검증해야 하므로, 테스트와 유지보수가 복잡해집니다.
속성 증가 로직을 변경 하게 되면, 레벨업 메서드를 수정해야하며, 관련된 모든 테스트 코드도 수정해야합니다.
델리게이트를 사용하고 코드 작성 시
각 속성 증가 메서드를 독립적으로 설정하고 호출할 수 있어, 레벨업 메서드와는 관계 없이 개별 메서드를 독립적으로 검증하기 쉽습니다. 통합적인 검증 대신, 메서드 별로 간편하게 테스트할 수 있습니다.
각 메서드를 델리게이트에 추가하거나 제거하는 것만으로, 전체적인 로직을 수정하지 않고도 기능을 조정할 수 있습니다.
지금까지 델리게이트 사용 여부에 따른 차이를 보여드렸습니다. 그렇다면 이제 델리게이트의 장점을 정리해보겠습니다.

델리게이트의 장점

1. 유연성

델리게이트를 사용하면 메서드를 동적으로 할당하고 호출할 수 있습니다. 이는 코드의 유연성을 높여주며, 특정 작업을 다양한 방식으로 처리할 수 있게 해줍니다. 예를 들어, 델리게이트에 여러 메서드를 추가하거나 제거하여 다양한 기능을 조절할 수 있습니다.

2. 모듈화

델리게이트를 사용하면 관련된 기능들을 모듈화할 수 있습니다. 특정 이벤트나 작업에 대해 메서드를 등록하고 호출함으로써, 코드의 구조를 더 깔끔하고 관리하기 쉽게 만들 수 있습니다.

3. 확장성

델리게이트를 사용하면 새로운 기능을 추가하거나 기존 기능을 변경할 때, 기존 코드를 수정할 필요가 줄어듭니다. 델리게이트에 메서드를 추가하거나 교체함으로써, 코드의 변경 없이 기능을 확장할 수 있습니다.

4. 테스트와 디버깅 용이

델리게이트에 등록된 각 메서드를 독립적으로 테스트할 수 있습니다. 델리게이트에 메서드를 추가하거나 제거하는 작업이 간단하므로, 테스트와 디버깅이 더 수월합니다.

결론

이제 델리게이트를 왜 사용해야하는지 이해하셨나요? 델리게이트를 사용하면 코드의 유연성을 높이고 유지보수를 쉽게 할 수 있습니다. 게임 개발에서는 다양한 이벤트와 콜백을 처리해야 하는데, 델리게이트를 잘 활용하면 이러한 작업을 더 효율적으로 관리할 수 있습니다.
앞으로 델리게이트를 잘 이해하고 이를 활용한다면 더 복잡한 시스템도 쉽게 구현할 수 있을 것입니다.