리플렉션에 대해 설명해 주세요.
런타임 시점에 클래스의 정보(필드, 메서드, 생성자 등)를 동적으로 조회하고 조작할 수 있는 기능입니다.
핵심 개념
일반적인 코드 실행
컴파일 타임에 클래스 정보 확정 → 런타임 실행
리플렉션
런타임에 클래스 정보 조회 → 동적으로 메서드 호출, 필드 접근
기본 사용법
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
private void secret() {
System.out.println("비밀 메서드");
}
public void hello() {
System.out.println("안녕하세요, " + name);
}
}
// 1. Class 객체 획득
Class<?> clazz = Class.forName("com.example.User");
// 2. 생성자로 인스턴스 생성
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user = constructor.newInstance("kim", 25);
// 3. 메서드 호출
Method helloMethod = clazz.getMethod("hello");
helloMethod.invoke(user); // "안녕하세요, kim"
// 4. private 접근 (AccessibleObject)
Method secretMethod = clazz.getDeclaredMethod("secret");
secretMethod.setAccessible(true); // private 접근 허용
secretMethod.invoke(user); // "비밀 메서드"
// 5. private 필드 값 변경
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(user, "lee"); // name 변경
리플렉션이 사용되는 곳
Spring DI (의존성 주입)
@Component
public class UserService {
@Autowired
private UserRepository repo; // 런타임에 리플렉션으로 주입
}
// Spring 내부 동작
Field field = UserService.class.getDeclaredField("repo");
field.setAccessible(true);
field.set(userServiceInstance, repoInstance); // 리플렉션으로 주입
JUnit 테스트
// @Test 붙은 메서드를 리플렉션으로 탐색 후 자동 실행
for (Method method : testClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)) {
method.invoke(testInstance); // 리플렉션으로 실행
}
}
Jackson (JSON 직렬화)
// 리플렉션으로 필드명/값을 읽어 JSON 변환
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
// → {"name":"kim","age":25}
장단점
| 내용 | |
|---|---|
| 장점 | 런타임 동적 처리, 프레임워크/라이브러리 개발에 필수 |
| 단점 1 | 컴파일 타임 타입 체크 불가 → 런타임 에러 위험 |
| 단점 2 | 일반 호출보다 성능 느림 (캐싱으로 완화 가능) |
| 단점 3 | private 접근 가능 → 캡슐화 위반 |
성능 문제 완화 - 캐싱
// X 매번 리플렉션 탐색 → 느림
public void invoke(Object obj) {
Method method = obj.getClass().getMethod("hello");
method.invoke(obj);
}
// O Method 객체 캐싱 → 성능 개선
private static final Map<Class<?>, Method> cache = new HashMap<>();
public void invoke(Object obj) {
Method method = cache.computeIfAbsent(
obj.getClass(),
clazz -> clazz.getMethod("hello")
);
method.invoke(obj);
}
리플렉션은 프레임워크 내부에서 쓰는 강력한 도구입니다. 일반 애플리케이션 코드에서 직접 사용하면 캡슐화 위반 + 성능 저하 + 타입 안정성 감소로 이어지므로, 꼭 필요한 경우에만 신중하게 사용해야 합니다.