C#

C# 기초부터 고급까지 Chapter 3.2. Reflection & Dynamic Type – 런타임 메타프로그래밍

Juan_ 2025. 5. 5. 18:25
728x90

📘 Chapter 3.2: Reflection & Dynamic Type – 런타임 메타프로그래밍


✅ 이 챕터에서 배울 것

  • 리플렉션(Reflection)이란?
  • 어떤 상황에서 쓰는가 (실무 사례)
  • Type, PropertyInfo, MethodInfo 사용법
  • dynamic 키워드와 ExpandoObject
  • 실전 시나리오 예제 (DTO ↔ Entity, 자동 매핑 등)
  • 성능 이슈와 개선 전략

1️⃣ 리플렉션(Reflection)이란?

실행 중인 객체, 타입, 메서드, 속성 정보를 코드에서 직접 들여다보고 조작하는 기술

C#에서는 System.Reflection 네임스페이스를 통해 사용한다.

📦 간단 예제

Type type = typeof(string);
Console.WriteLine(type.FullName); // System.String

MethodInfo method = type.GetMethod("Contains");
Console.WriteLine(method.Name); // Contains

🧠 실무에서 어디 쓰이노?

  • DI Container – 어떤 클래스를 만들고 어떤 인터페이스인지
  • Moq, AutoFixture – 타입 자동 생성
  • JsonSerializer – 속성들 순회
  • EntityFramework – DTO ↔ Entity 매핑
  • Swagger – 컨트롤러/파라미터 정보 추출

2️⃣ 실전 – 객체 속성 모두 출력하기

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

var person = new Person { Name = "장형", Age = 35 };

Type type = typeof(Person);
foreach (var prop in type.GetProperties())
{
    Console.WriteLine($"{prop.Name}: {prop.GetValue(person)}");
}

✅ 결과:

Name: 장형
Age: 35

→ 객체의 모든 속성 정보를 동적으로 출력 가능


3️⃣ 동적으로 메서드 호출하기

public class Calculator
{
    public int Add(int a, int b) => a + b;
}

var calc = new Calculator();

Type type = typeof(Calculator);
MethodInfo method = type.GetMethod("Add");

object result = method.Invoke(calc, new object[] { 3, 4 });
Console.WriteLine(result); // 7

✅ 컴파일 시점에 메서드 이름이 없어도
런타임에 찾아서 호출 가능! (플러그인 구조, 모듈 확장에 핵심)


4️⃣ dynamic – 타입 없이도 호출되는 마법

dynamic obj = "Hello";
Console.WriteLine(obj.Length); // 컴파일 시엔 체크 안 됨

✅ 특징:

  • 타입을 런타임에 결정
  • 컴파일러는 무시, 대신 런타임에 호출 성공 여부 판단
  • 내부적으로 IDynamicMetaObjectProvider 기반 작동

🔧 ExpandoObject

완전 런타임 조작 가능한 객체 (속성 추가도 가능)

dynamic expando = new ExpandoObject();
expando.Name = "장형";
expando.SayHi = (Action)(() => Console.WriteLine("안녕~"));

Console.WriteLine(expando.Name);
expando.SayHi();

✅ 실무에서는 동적 바인딩, 스크립트 엔진, 데이터 표현 등에 자주 사용


5️⃣ 실무 예제 – DTO 자동 매핑기 만들기

public class UserDto
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}
public static TTarget Map<TSource, TTarget>(TSource src)
    where TTarget : new()
{
    var target = new TTarget();

    var sourceProps = typeof(TSource).GetProperties();
    var targetProps = typeof(TTarget).GetProperties();

    foreach (var sProp in sourceProps)
    {
        var tProp = targetProps.FirstOrDefault(p => p.Name == sProp.Name);
        if (tProp != null && tProp.CanWrite)
        {
            tProp.SetValue(target, sProp.GetValue(src));
        }
    }

    return target;
}
var dto = new UserDto { Name = "장형", Age = 35 };
var entity = Map<UserDto, User>(dto);

Console.WriteLine(entity.Name); // 장형

➡️ 실무에서 AutoMapper가 하는 일의 핵심 로직


6️⃣ 성능 이슈 & 대안

✅ 문제:

  • 리플렉션은 런타임 탐색 → 속도 느림
  • 코드가 복잡 → 예외 처리 어려움
  • JIT 최적화 못 받음

✅ 대안:

방법설명
Expression Tree런타임에 코드 생성 후 컴파일
Source Generator컴파일 타임에 코드 생성
delegate 캐싱GetMethod 후 Delegate로 변환해서 재사용
var method = typeof(Calculator).GetMethod("Add");
var del = (Func<Calculator, int, int, int>)
    Delegate.CreateDelegate(typeof(Func<Calculator, int, int, int>), method);

var result = del(new Calculator(), 3, 4); // 빠름!

✅ 정리 요약

항목설명
Reflection런타임에 타입/속성/메서드 조작
dynamic컴파일 타임 타입 무시, 런타임 결정
ExpandoObject속성도 런타임에 추가 가능한 객체
실무 활용ORM, DI, 시리얼라이저, Mock, Swagger
성능 대안Expression, Source Generator, Delegate 캐싱

📢 다음 챕터 예고 🎓

다음 챕터주제
Chapter 3.3Expression Tree로 컴파일 타임 코드 생성
728x90