블로그로 돌아가기
C#

c# reflection 사용법: 개념과 실무 적용 가이드

c# reflection 사용법을 통해 런타임에 타입을 조사하고 동적으로 멤버를 호출하는 방법을 실무 예제와 체크리스트로 정리합니다. 성능·보안 주의점과 언제 사용/회피할지 명확히 설명합니다.

Reflection리플렉션런타임 메타프로그래밍실무 예제
c# reflection 사용법을 설명하는 다이어그램과 코드 예시 이미지

c# reflection 사용법이 필요한 상황

c# reflection 사용법은 런타임에 타입 정보를 확인하거나 동적으로 인스턴스를 생성·메서드를 호출해야 할 때 필요합니다. 이 글에서는 개념 이해, 실무 적용 예제, 성능·보안 주의점, 바로 적용 가능한 체크리스트까지 제공합니다. 실무 웹 개발자가 실제 코드에 바로 적용할 수 있도록 구체적인 코드와 판단 기준도 포함합니다.

c# reflection 사용법 핵심 개념

Reflection은 System.Reflection 네임스페이스를 통해 타입(Type), 생성자(ConstructorInfo), 메서드(MethodInfo), 프로퍼티(PropertyInfo), 필드(FieldInfo) 등을 런타임에 조회하고 조작하는 기능입니다. 주요 용도는 다음과 같습니다:

  • 타입 메타데이터 조회(Type.GetType, typeof)
  • 멤버 정보 획득(Type.GetMethods, GetProperties 등)
  • 동적 인스턴스 생성(Activator.CreateInstance)
  • 비공개 멤버 접근(비추천, 제한적 사용)

Reflection은 강력하지만 직접 호출보다 느리고 보안·유지보수 측면에서 위험 요소가 있으므로 필요한 경우에만 제한적으로 사용해야 합니다.

c# reflection 사용법 실무 예제

아래 예제는 런타임에 타입을 찾아 인스턴스를 만들고, 프로퍼티를 읽고, 메서드를 호출하는 기본 흐름입니다.

using System; using System.Reflection; public class User { public string Name { get; set; } public int GetId() => 42; } class Program { static void Main() { // 타입 얻기 Type t = Type.GetType("User"); // 네임스페이스 포함 필요할 수 있음 if (t == null) t = typeof(User); // 인스턴스 생성 object instance = Activator.CreateInstance(t); // PropertyInfo 사용 PropertyInfo pi = t.GetProperty("Name"); pi.SetValue(instance, "Alice"); Console.WriteLine(pi.GetValue(instance)); // MethodInfo 사용 MethodInfo mi = t.GetMethod("GetId"); var id = mi.Invoke(instance, null); Console.WriteLine(id); } }

실무에서는 다음과 같은 패턴으로 사용합니다:

  • 설정 파일로 타입 이름을 받아 플러그인 로드
  • DTO/자동 매핑 테스트 시 동적 인스턴스 생성
  • 단위 테스트에서 내부 로직 검증(단, 접근 제한 고려)

비교: Reflection vs 직접 호출 vs Expression

아래 표는 판단을 돕기 위한 기준표입니다.

구분항목1항목2항목3
내용가독성성능유지보수
Reflection낮음(복잡)낮음(오버헤드 있음)낮음(런타임 의존)
직접 호출높음(명시적)높음(최고)높음(컴파일 타임 안정)
Expression/Delegate보통(조금 복잡)보통~높음(캐시 가능)보통(코드 복잡도 증가)

언제 쓰면 좋고 언제 피해야 할까

언제 쓰면 좋은가

  • 플러그인 아키텍처에서 런타임 타입 로딩이 필요할 때
  • 다양한 타입을 한 코드에서 일반화하여 처리해야 할 때(예: 직렬화 라이브러리 확장)
  • 테스트나 디버깅 목적으로 내부 상태를 점검해야 할 때(제약 하에)

언제 피해야 하는가

  • 빈번한 호출이 요구되는 핫스팟(성능이 중요한 루프)에서는 사용하지 마세요
  • 보안상 민감한 환경에서 외부 입력을 통해 타입이나 멤버를 선택해야 하는 경우
  • 단순한 문제를 해결하는데 과도한 복잡도를 추가하는 경우

실무 체크리스트

  • Reflection 사용 전 대체 가능한지 검토(인터페이스, 제네릭, DI 등)
  • 성능이 중요한 경로에선 사용 금지 또는 캐시 적용
  • 외부 입력으로 타입을 결정할 때 화이트리스트 적용
  • 비공개 멤버 접근은 로그·주석으로 사유 명시
  • 예외 처리: TypeLoadException, TargetInvocationException 등을 적절히 처리

성능·보안·운영 주의점

성능

  • Reflection은 메타데이터 탐색과 박싱/언박싱 비용 때문에 직접 호출보다 느립니다. 빈번한 호출이 필요한 경우 MethodInfo.Invoke 대신 delegate를 생성해 캐시하세요 (예: Expression 또는 DynamicMethod).

보안

  • 외부에서 입력받은 타입 이름으로 바로 Type.GetType 등 호출 시 임의 코드 실행 위험이 있습니다. 허용된 어셈블리/타입 목록(화이트리스트)을 검증하세요.
  • 비공개 멤버 접근은 .NET 런타임 정책이나 향후 프레임워크 변경에 취약합니다.

운영

  • 리플렉션 관련 예외가 발생하면 런타임 시점에만 드러나므로 광범위한 예외 로깅과 모니터링을 준비하세요.
  • AOT(Ahead-of-Time) 컴파일 환경(예: trimmed publish)에서는 필요한 멤버가 제거될 수 있으니 사용 전에 테스트하세요.

고급 팁: 성능 최적화 예시

  • MethodInfo.Invoke 대신 Delegate.CreateDelegate로 델리게이트를 만들어 재사용하면 호출 비용을 크게 줄일 수 있습니다.
  • PropertyInfo.GetValue/SetValue도 Reflection.Emit 또는 Expression 기반 접근으로 대체 가능.
// Delegate 생성 예시 var mi = typeof(User).GetMethod("GetId"); var del = (Func<User, int>)Delegate.CreateDelegate(typeof(Func<User, int>), mi); int id = del((User)instance);

자주 묻는 질문

Q1: Reflection이 모든 경우에 느린가요?

A1: 기본적으로 메타데이터 탐색과 Invoke는 비용이 있습니다. 하지만 한 번만 수행해 캐시하면 반복 호출 비용을 줄일 수 있습니다. 델리게이트 캐시를 권장합니다.

Q2: 비공개(private) 필드나 메서드를 Reflection으로 접근해도 되나요?

A2: 기술적으로 가능하지만 권장하지 않습니다. 프레임워크 업데이트로 깨질 수 있고 보안 정책 상 문제를 일으킬 수 있으니 정당한 사유와 검증, 로그가 필요합니다.

Q3: Assembly.Load와 Type.GetType 차이는 무엇인가요?

A3: Assembly.Load는 명시적 어셈블리를 로드하고 GetType은 로드된 어셈블리에서 타입을 찾습니다. 외부 어셈블리에서 타입을 가져오려면 먼저 Assembly.Load 또는 Assembly.LoadFrom로 로드해야 할 때가 있습니다.

Q4: Reflection 사용 시 AOT 환경에서 주의할 점은?

A4: 트리밍(trim)으로 인해 사용하지 않는 멤버가 제거될 수 있어 리플렉션으로 접근하려는 멤버를 보존하도록 설정하거나 테스트해야 합니다.

정리

  • Reflection은 런타임 타입 조사와 동적 호출에 유용하지만 성능·보안 이슈 존재
  • 대체 가능하면 인터페이스, DI, 제네릭 등 컴파일 타임 방법을 우선 사용
  • 성능 문제는 델리게이트 캐시나 Expression으로 개선 가능
  • 외부 입력 기반 타입 결정은 화이트리스트로 검증
  • 실무 체크리스트를 통해 사용 전/후 점검을 습관화
c# reflection 사용법 가이드: 개념·실무·주의점 정리 | RYUSLOG DEV