C# 리플렉션과 애트리뷰트

김민구·2025년 5월 27일
0

C#

목록 보기
26/31

리플렉션(Reflection)이란 무엇일까요?

리플렉션은 실행 시점에 어셈블리, 모듈, 형식(클래스, 인터페이스 등)의 정보를 검사하고 조작하는 기능입니다. 즉, 리플렉션을 통해 코드의 메타데이터(Metadata), 즉 코드에 대한 데이터에 접근할 수 있습니다.

리플렉션을 사용하면 다음과 같은 작업이 가능해집니다:

  • 특정 형식의 필드(Field), 속성(Property), 메서드(Method), 인터페이스(Interface) 등의 정보를 얻어올 수 있습니다.
  • 형식의 인스턴스를 동적으로 생성할 수 있습니다.
  • 동적으로 생성된 인스턴스의 속성 값을 설정/가져오거나 메서드를 호출할 수 있습니다.

1. 형식 정보 얻기: Object.GetType(), typeof, Type.GetType()

어떤 형식의 정보를 얻으려면 우선 해당 형식을 나타내는 Type 객체를 가져와야 합니다. Type 객체를 얻는 방법은 여러 가지가 있습니다.

  • Object.GetType() 메서드: 객체의 인스턴스가 있을 때 사용합니다. 해당 인스턴스의 런타임 형식을 반환합니다.
    int a = 0;
    Type type = a.GetType(); // a 인스턴스의 Type 객체 가져오기
  • typeof 연산자: 컴파일 시점에 형식이 정해져 있을 때 사용합니다. 특정 형식의 Type 객체를 반환합니다. Object.GetType()과 달리 인스턴스가 필요 없습니다.
    Type type = typeof(int); // int 형식의 Type 객체 가져오기
  • Type.GetType() 메서드: 형식의 이름을 문자열로 알고 있을 때 사용합니다. 동적으로 형식 이름을 결정하여 Type 객체를 가져올 때 유용합니다.
    Type type = Type.GetType("System.Int32"); // "System.Int32" 형식의 Type 객체 가져오기

Type 객체를 얻은 후에는 다양한 메서드를 사용하여 해당 형식의 세부 정보를 얻을 수 있습니다. 예를 들어 GetFields(), GetProperties(), GetMethods(), GetInterfaces() 등을 사용할 수 있습니다. GetFields()GetProperties() 같은 메서드는 특정 BindingFlags를 사용하여 인스턴스/정적 필드, public/non-public 필드 등 원하는 정보만 필터링하여 가져올 수 있습니다.

2. 인스턴스 동적 생성 및 조작: Activator.CreateInstance(), Invoke(), SetValue(), GetValue()

Type 객체를 사용하여 해당 형식의 인스턴스를 동적으로 생성할 수 있습니다. 주로 Activator.CreateInstance() 메서드를 사용합니다. 매개변수 없는 생성자를 호출하여 인스턴스를 만들 수도 있고, 생성자 매개변수를 넘겨서 인스턴스를 만들 수도 있습니다.

// 매개변수 없는 생성자 호출
object obj = Activator.CreateInstance(typeof(MyClass));

// 매개변수 있는 생성자 호출
object profile = Activator.CreateInstance(type, "박상원", "512-1234");

동적으로 생성된 객체의 속성 값을 설정하거나 가져오려면 Type.GetProperty() 메서드로 PropertyInfo 객체를 얻은 후, SetValue() 또는 GetValue() 메서드를 사용합니다. 메서드를 호출하려면 Type.GetMethod() 메서드로 MethodInfo 객체를 얻은 후, Invoke() 메서드를 사용합니다.

// 속성 값 설정/가져오기 예시
PropertyInfo nameProperty = type.GetProperty("Name");
nameProperty.SetValue(profile, "박찬호", null); // 값 설정
object nameValue = nameProperty.GetValue(profile, null); // 값 가져오기

// 메서드 호출 예시
MethodInfo printMethod = type.GetMethod("Print");
printMethod.Invoke(profile, null); // Print() 메서드 호출 (매개변수 없으므로 null)

3. 동적 코드 생성 (Emit)

리플렉션은 단순히 기존 코드를 조사하고 조작하는 것을 넘어, 실행 중에 새로운 어셈블리, 형식, 메서드를 동적으로 생성하고 IL(Intermediate Language) 코드를 직접 작성하여 메서드의 구현부를 정의하는 것도 가능합니다. 이는 AssemblyBuilder, ModuleBuilder, TypeBuilder, MethodBuilder, ILGenerator 등의 클래스를 사용합니다. 동적으로 생성된 코드는 런타임에 로드되어 실행될 수 있습니다.

애트리뷰트(Attribute)란 무엇일까요?

애트리뷰트는 코드 요소(어셈블리, 형식, 멤버 등)에 메타데이터를 연결하는 선언적인 방법입니다. 애트리뷰트 자체는 코드의 동작에 직접적인 영향을 미치지 않지만, 리플렉션을 통해 런타임에 이 메타데이터를 읽어와서 특정 로직을 수행하는 데 활용될 수 있습니다.

1. 애트리뷰트 사용하기

애트리뷰트는 대괄호 [] 안에 애트리뷰트 이름을 쓰고 적용하려는 코드 요소 앞에 붙여 사용합니다. 생성자를 통해 매개변수를 전달하거나 속성 값을 설정하여 애트리뷰트에 추가 정보를 저장할 수 있습니다.

[애트리뷰트_이름(매개변수, 속성=)] // 기본 형식
public class MyClass { ... }

[Obsolete("OldMethod는 폐기되었습니다. NewMethod()를 이용하세요.")] // 내장 애트리뷰트 예시
public void OldMethod() { ... }

소스에서는 [Obsolete] 애트리뷰트의 사용 예시를 보여줍니다. 이 애트리뷰트가 적용된 메서드를 사용하면 컴파일러 경고나 오류가 발생하여 해당 메서드가 더 이상 사용되지 않음을 알릴 수 있습니다.

[CallerFilePath], [CallerLineNumber], [CallerMemberName]과 같은 호출자 정보 애트리뷰트는 로깅이나 디버깅 시 유용하게 사용됩니다. 이 애트리뷰트가 적용된 매개변수는 메서드 호출 시 자동으로 호출자의 소스 파일 경로, 줄 번호, 멤버 이름 정보로 채워집니다.

2. 나만의 애트리뷰트 만들기

나만의 커스텀 애트리뷰트를 만들려면 System.Attribute 클래스를 상속하는 클래스를 정의하면 됩니다. 애트리뷰트 클래스의 이름은 보통 "xxxAttribute" 형태로 짓고, 사용할 때는 "xxx"만 써도 됩니다.

애트리뷰트 클래스에는 정보를 저장할 속성이나 필드를 정의할 수 있으며, 생성자를 정의하여 애트리뷰트를 적용할 때 필수적으로 입력해야 할 정보를 지정할 수 있습니다.

// History 애트리뷰트 정의 예시
class History : System.Attribute
{
    private string programmer;
    public double Version { get; set; }
    public string Changes { get; set; }

    public History(string programmer) // 생성자 정의
    {
        this.programmer = programmer;
        Version = 1.0;
        Changes = "First release";
    }

    public string GetProgrammer() // 정보 가져오는 메서드
    { return programmer; }
}

// 클래스에 History 애트리뷰트 적용 예시
[History("Sean", Version = 0.1, Changes = "2017-11-01 Created class stub")]
class MyClass { ... }

[System.AttributeUsage] 애트리뷰트를 사용하여 내가 만든 애트리뷰트를 어디에 적용할 수 있는지(클래스, 메서드 등) 지정하고, 하나의 코드 요소에 여러 번 적용할 수 있는지(AllowMultiple) 등을 설정할 수 있습니다.

3. 애트리뷰트 정보 읽어오기

적용된 애트리뷰트 정보는 리플렉션을 통해서 런타임에 접근할 수 있습니다. Type 객체나 MethodInfo, PropertyInfo 등의 객체에서 GetCustomAttributes() 메서드를 사용하여 해당 요소에 적용된 애트리뷰트들을 가져올 수 있습니다.

Type type = typeof(MyClass);
Attribute[] attributes = Attribute.GetCustomAttributes(type); // MyClass에 적용된 모든 애트리뷰트 가져오기

foreach (Attribute a in attributes)
{
    if (a is History h) // 가져온 애트리뷰트가 History 타입인지 확인하고 캐스팅
    {
        Console.WriteLine($"Ver:{h.Version}, Programmer:{h.GetProgrammer()}, Changes:{h.Changes}"); // 정보 출력
    }
}

이를 통해 런타임에 코드의 메타데이터를 읽어와서 필요에 따라 다른 동작을 수행하게 할 수 있습니다.

profile
C#, Unity

0개의 댓글