[로봇활용_12주차] C# 리플렉션(Reflection)

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

코드가 자신을 돌아본다고요?

"만약 프로그램이 실행되는 도중에 자신의 구조를 뜯어보고,
심지어 그걸로 무언가를 할 수 있다면 어떨까요?" 마치 우리가
거울을 보면서 자신의 모습을 확인하고 머리를 정돈하는 것처럼 말이죠.
사실 C#에는 이런 마법 같은 기능을 하는 리플렉션(Reflection)이 존재합니다.
이번 글에서는 리플렉션이 무엇인지 그 '개념'에 대해 알아보겠습니다!

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

리플렉션(Reflection)은 프로그램이 실행 중(런타임)에
자기 자신의 정보(메타데이터)를 들여다보고, 이를 활용하여
객체를 생성하거나 메소드를 호출하는 등의 작업을 할 수 있게 해주는 기능입니다.

비유: 리플렉션은 거울에 비친 코드의 모습과 같아요.

  • 거울 보기: 프로그램이 실행 중에 자신의 코드를 살펴봅니다.
  • 거울에 비친 내 모습: 코드의 구조 정보, 즉 어떤 클래스가 있는지, 그 클래스에는
    어떤 속성(Property)과 메소드(Method)가 있는지 등의 정보(메타데이터)를 확인합니다.
  • 내 모습을 보고 행동: 확인한 정보를 바탕으로, 특정 클래스의 인스턴스를 만들거나, 이름만 알고 있는 메소드를 호출할 수도 있습니다.

컴파일 시점에는 모든 것이 정해져 있지만, 리플렉션을 사용하면 런타임에
동적으로 코드의 구조를 파악하고 제어할 수 있는 유연함을 얻게 되는 것이죠.

2)리플렉션은 언제 사용할까요?

리플렉션은 기능이 강력한 만큼, 꼭 필요한 곳에서 사용해야 하는데요,
주로 다음과 같은 시나리오에서 그 진가를 발휘합니다.

1. 플러그인(Plug-in) 아키텍처

비주얼 스튜디오, 웹 브라우저에 확장 프로그램을 설치하는 것을 생각해 보세요.
메인 프로그램은 이 플러그인들이 어떤 클래스와 메소드를 가졌는지 알지 못합니다.
프로그램이 시작될 때 특정 폴더에 있는 DLL 파일들을 읽어오고, 리플렉션을 사용해
"DLL에서 IFilter인터페이스를 구현한 클래스가 있네! 이걸 메뉴에 추가해야겠다!"와 같이
동적으로 메인 프로그램의 기능을 확장할 수 있습니다.

2. 객체-관계 매핑(ORM) 및 직렬화(Serialization)

Entity Framework 같은 ORM이나 Json.NET 같은 직렬화
리플렉션을 통해 컴파일 시점에 알 수 없는 타입을 런타임에 처리할 수 있도록 하고,
그 이름과 타입을 알아내어 데이터베이스 테이블이나 JSON 데이터에 매핑합니다.
이처럼 리플렉션은 범용적이고 유연한 프레임워크를 구축하는 데 필수적인 기술입니다.

3. 유닛 테스트(Unit Test) 프레임워크

NUnit이나 xUnit 같은 테스트 프레임워크는 사용자가 작성한 어셈블리(DLL)를 로드한 후,
리플렉션을 사용하여 클래스와 메소드를 검사합니다. [Test][Fact]같은
특정 애트리뷰트를 가진 테스트 코드를 찾아내고 동적으로 호출합니다.

3)간단한 코드 예제로 맛보기

리플렉션이 실제로 어떻게 동작하는지 간단한 코드로 살펴볼까요?
Person이라는 클래스의 정보를 런타임에 어떻게 가져오는지 보세요.

[코드]

using System;
using System.Reflection; // 리플렉션 기능을 사용하려면 필요

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

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void SayHello()
    {
        Console.WriteLine($"안녕하세요, 제 이름은 {Name}입니다.");
    }
}

class Program
{
    static void Main()
    {
        Person person = new Person("홍길동", 25);
        Type type = person.GetType(); // 1. person 객체의 Type 정보 가져오기

        Console.WriteLine($"[클래스 이름]: {type.Name}");
        Console.WriteLine("--- 속성(Properties) 목록 ---");

        // 2. 클래스의 모든 public 속성 정보 가져오기
        PropertyInfo[] properties = type.GetProperties();
        foreach (PropertyInfo property in properties)
        {
            Console.WriteLine($"# {property.PropertyType.Name} {property.Name}");
        }

        Console.WriteLine("\n--- 메소드(Methods) 목록 ---");

        // 3. 클래스의 모든 public 메소드 정보 가져오기
        MethodInfo[] methods =
            type.GetMethods(
                BindingFlags.Public | // public으로 선언된 메서드
                BindingFlags.Instance | // 인스턴스에 속하는 메서드
                BindingFlags.DeclaredOnly); // 해당 클래스에 선언된 메서드
        foreach (MethodInfo method in methods)
        {
            Console.WriteLine($"# {method.ReturnType.Name} {method.Name}()");
        }
    }
}

[실행 결과]

[클래스 이름]: Person
--- 속성(Properties) 목록 ---
# String Name
# Int32 Age

--- 메소드(Methods) 목록 ---
# String get_Name()
# Void set_Name()
# Int32 get_Age()
# Void SayHello()

person객체만 가지고 GetType()메소드를 통해 Type객체를 얻어오면,
이 클래스가 어떤 속성과 메소드를 가졌는지 런타임에 모두 파악할 수 있습니다.

4)리플렉션 사용 시 주의할 점

  • 성능 저하: 컴파일 타임에 모든 정보가 결정되는 일반적인 코드 호출에 비해,
    런타임에 메타데이터를 조회하고 동적으로 호출하는 과정은 상대적으로 느립니다.
    성능이 중요한 로직에서는 사용을 피하는 것이 좋습니다.
  • 가독성 및 유지보수: 코드가 문자열 기반으로 동작하는 경우가 많아
    컴파일러의 타입 체크 도움을 받을 수 없습니다. 예를 들어 메소드 이름을
    "SayHello" 대신 "SayHelo"라고 오타를 내도 컴파일 오류가 나지 않고,
    실행 시점에야 오류가 발생합니다. 이는 디버깅을 어렵게 만들 수 있습니다.
  • 캡슐화 위반 가능성: 리플렉션을 사용하면 private멤버를 수정할 수 있습니다.
    이는 객체지향의 중요한 원칙인 캡슐화를 깨뜨릴 수 있어 신중하게 사용해야 합니다.

5)리플렉션 요약

리플렉션은 매우 유연하고 동적인 프로그래밍을 가능하게 하는 멋진 도구입니다.
하지만 강력한 만큼 성능과 유지보수 측면에서 고려해야 할 점도 분명히 존재하죠.
따라서 리플렉션을 꼭 필요한 순간에만 적절히 활용하는 것이 중요합니다.

profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글