자바 리플렉션 API는 실행 중인 자바 애플리케이션이 자신의 내부 구조를 검사하고, 클래스의 멤버(생성자, 필드, 메소드)를 동적으로 조작할 수 있게 해주는 기능입니다.
자바 명세상 리플렉션은 컴파일 시점에 알 수 없었던 클래스나 멤버에 접근하여 호출하거나 값을 변경하는 것을 허용합니다. 이는 정적 언어인 자바에 동적(Dynamic) 특성을 부여하며, 주로 프레임워크(Spring, JUnit 등)나 라이브러리 제작에 필수적으로 사용됩니다.
멤버를 취득할 때는 'Public만 가져올 것인가' 아니면 '선언된 모든 멤버를 가져올 것인가'에 따라 메소드가 나뉩니다.

・setAccessible(boolean flag): private 멤버에 접근하기 위해 반드시 true로 설정해야 하는 핵심 메소드입니다.
리플렉션을 통한 멤버 조작은 일반적인 객체 접근보다 복잡한 단계를 거칩니다.
1.객체 확인: 힙(Heap) 메모리에 있는 대상 인스턴스의 Class 정보를 참조합니다.
2.메타데이터 검색: 메소드 영역(Method Area)에 저장된 클래스 구조 정보에서 요청한 이름과 일치하는 Field나 Method 객체를 생성하여 반환합니다.
3.권한 체크: 해당 멤버의 접근 제어자(private 등)를 확인합니다. 리플렉션 사용 시 이 단계를 우회하기 위해 setAccessible(true)를 호출하면 JVM의 보안 검사를 일시적으로 통과합니다.
4.실행/수정: * 필드: 힙 메모리에 있는 실제 인스턴스의 특정 메모리 주소 값을 직접 수정합니다.
・메소드: 바이트코드를 실행하기 위해 해당 메소드의 시작 주소로 점프하여 로직을 수행합니다.
회사 연수 중에 가끔 "private 변수인데 강제로 값을 바꿔야 하거나, 메소드 이름을 문자열로 받아서 실행해야 하는 상황"을 가정해 보겠습니다.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class User {
private String name = "Default";
private void sayHello() { System.out.println("Hello, " + name); }
}
public class ReflectionMemberTest {
public static void main(String[] args) throws Exception {
User user = new User();
Class<?> clazz = user.getClass();
// 1. private 필드 취득 및 수정
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // private 벽 허물기
nameField.set(user, "HyunWook"); // 값 변경
// 2. private 메소드 취득 및 실행
Method helloMethod = clazz.getDeclaredMethod("sayHello");
helloMethod.setAccessible(true);
helloMethod.invoke(user); // "Hello, HyunWook" 출력
}
}
1.성능 저하 (Overhead): 리플렉션은 JVM의 최적화(JIT 컴파일러)를 방해합니다. 일반적인 메소드 호출보다 수십 배 느릴 수 있으므로, 성능이 중요한 반복문 안에서는 사용을 피해야 합니다.
2.보안 및 캡슐화 파괴: private 멤버를 강제로 조작하는 것은 객체 지향의 원칙을 깨뜨리는 행위입니다. 클래스 내부 구조가 바뀌면 리플렉션 코드는 즉시 깨지므로 유지보수가 어렵습니다.
3.컴파일 시점 타입 체크 불가: 존재하지 않는 필드 이름을 문자열로 적어도 컴파일 에러가 나지 않습니다. 대신 실행 시점에 NoSuchFieldException이나 NoSuchMethodException이 발생하므로 반드시 예외 처리가 필요합니다.
4.디버깅 팁: 리플렉션 코드에서 에러가 발생하면 스택 트레이스가 매우 길어집니다. InvocationTargetException이 발생했다면, 이는 리플렉션 자체의 문제가 아니라 호출된 실제 메소드 내부에서 에러가 난 것이므로 getCause()를 통해 실제 원인을 확인해야 합니다.