[자바/Java] Reflection의 개념 및 사용법 (1)

dongbrown·2025년 8월 13일

Java

목록 보기
5/6

📖 리플렉션(Reflection)이란?

리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API입니다.

동작 원리

JVM의 클래스 로더가 클래스 파일을 로딩하면, 해당 클래스의 정보를 담은 Class 타입의 객체를 생성하여 힙(Heap) 영역에 저장합니다. 리플렉션은 이 Class 객체를 통해 클래스의 구조를 분석하고 조작합니다.

⚠️ 주의: Class 객체는 new 키워드로 생성하는 일반 인스턴스와는 다릅니다.

주요 특징

장점 ✅

  • 동적으로 클래스를 사용할 수 있음
  • 접근 제어자에 관계없이 필드와 메서드 접근 가능
  • 프레임워크나 라이브러리 개발에 유용

단점 ⚠️

  • 컴파일 시점에 오류를 잡을 수 없음
  • 런타임에 동작하므로 성능 오버헤드 발생
  • 캡슐화를 깨뜨릴 수 있음

리플렉션으로 가져올 수 있는 정보

  • Class - 클래스 정보
  • Constructor - 생성자 정보
  • Method - 메서드 정보
  • Field - 필드 정보

🔍 Class 객체 얻는 3가지 방법

리플렉션을 사용하려면 먼저 Class 객체를 얻어야 합니다.

1️⃣ Object.getClass() 메서드

이미 생성된 인스턴스에서 Class 객체를 얻는 방법입니다.

public static void main(String[] args) {
    String str = new String("Class 클래스 테스트");
    
    *// getClass() 메서드로 얻기*
    Class<? extends String> cls = str.getClass();
    System.out.println(cls); *// class java.lang.String*
}

2️⃣ .class 리터럴

클래스 이름 뒤에 .class를 붙여 직접 얻는 방법입니다.

Class<?> clazz = String.class;

3️⃣ Class.forName() 메서드

클래스의 전체 경로(FQCN)를 문자열로 전달하는 방법입니다.

Class<?> clazz = Class.forName("java.lang.String");

💡 Tip: Class.forName()은 동적으로 클래스를 로드할 때 유용하지만, ClassNotFoundException 예외 처리가 필요합니다.


📊 클래스 정보 조회하기

Class 객체를 얻었다면, 클래스의 다양한 메타데이터를 조회할 수 있습니다.

Class<?> clazz = String.class;

// 클래스 이름 얻기
System.out.println("Class name: " + clazz.getName());

// 모든 메서드 얻기
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
	System.out.println("Method name: " + method.getName());
}

// 모든 필드 얻기
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
	System.out.println("Field name: " + field.getName());
}

🔧 필드(Field) 조작하기

리플렉션의 가장 강력한 기능 중 하나는 private 필드에도 접근할 수 있다는 것입니다.

필드 접근 메서드

  • getField() - public 필드에만 접근
  • getDeclaredField() - 모든 접근 제어자의 필드에 접근

예제 코드

class Example {
	public int publicField;
	private Stirng privateField;
}
// public 필드 접근
Field publicField = clazz.getField("publicField");

*// private 필드 접근*
Field privateField = clazz.getDeclaredField("privateField");

*// private 필드는 접근 권한을 변경해야 함*
privateField.setAccessible(true);

필드 값 읽기 및 수정

get()set() 메서드를 사용합니다.

Example example = new Example();

*// public 필드 읽기*
int publicFieldValue = (Integer) publicField.get(example);
System.out.println("Public Field Value: " + publicFieldValue);

*// private 필드 읽기*
String privateFieldValue = (String) privateField.get(example);
System.out.println("Private Field Value: " + privateFieldValue);

*// 필드 값 수정*
publicField.set(example, 10); *// public 필드 수정*
privateField.set(example, "Hello World"); *// private 필드 수정*

⚙️ 메서드(Method) 조작하기

리플렉션을 사용하면 런타임에 동적으로 메서드를 호출할 수 있습니다.

메서드 접근

class Example {
    public void publicMethod() {
        System.out.println("Public method");
    }

    private void privateMethod() {
        System.out.println("Private method");
    }
}
Class<?> clazz = Example.class;
*// public 메서드 접근*
Method publicMethod = clazz.getMethod("publicMethod");

*// private 메서드 접근*
Method privateMethod = clazz.getDeclaredMethod("privateMethod");

*// private 메서드는 접근 가능하도록 설정*
privateMethod.setAccessible(true);

메서드 실행

invoke() 메서드를 사용하여 메서드를 실행합니다.

Example example = new Example();

// public 메서드 실행
publicMethod.invoke(example);

// private 메서드 실행
privateMethod.invoke(example);

💡 invoke() 메서드란?
리플렉션 API의 Method 클래스가 제공하는 메서드로, 런타임에 특정 객체의 메서드를 동적으로 실행할 수 있게 해줍니다.


🏗️ 생성자(Constructor) 조작하기

리플렉션을 사용하면 private 생성자로도 객체를 생성할 수 있습니다.

생성자 접근

class Example {
	public Example() {
		System.out.println("Public constructor");
	}
	
	private Example(String arg) {
		System.out.println("Private constructor: " + arg);
	}
}
Class<?> clazz = Example.class;

*// public 생성자 접근*
Constructor<?> publicConstructor = clazz.getConstructor();

*// private 생성자 접근*
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);

*// private 생성자는 접근 가능하도록 설정*
privateConstructor.setAccessible(true);

객체 생성

// public 생성자를 사용한 객체 생성
Example example1 = (Example) publicConstructor.newInstance();

*// private 생성자를 사용한 객체 생성*
Example example2 = (Example) privateConstructor.newInstance("argument");

🎯 정리

이번 포스팅에서는 자바 리플렉션의 기본 개념과 사용 방법을 알아보았습니다.

핵심 내용 요약

  • 리플렉션은 런타임에 클래스 정보에 접근하고 조작하는 API
  • Class 객체를 얻는 3가지 방법: getClass(), .class, Class.forName()
  • 필드, 메서드, 생성자 모두 접근 제어자와 무관하게 접근 가능
  • setAccessible(true)로 private 멤버에 접근 가능
  • 강력하지만 성능 이슈와 안전성 문제가 있으므로 신중하게 사용해야 함

📌 다음 편 예고

다음 포스팅에서는 리플렉션의 실전 활용과 심화 내용을 다룰 예정입니다.

예정된 주제

  • 리플렉션 실전 활용 사례 (Spring, JPA 등)
  • 리플렉션 사용 시 주의사항 및 Best Practice
  • 성능 최적화 방법
  • 리플렉션 대안 기술

0개의 댓글