리플렉션에 대해 설명해 주세요.

런타임 시점에 클래스의 정보(필드, 메서드, 생성자 등)를 동적으로 조회하고 조작할 수 있는 기능입니다.

핵심 개념

일반적인 코드 실행
컴파일 타임에 클래스 정보 확정 → 런타임 실행

리플렉션
런타임에 클래스 정보 조회 → 동적으로 메서드 호출, 필드 접근

기본 사용법

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일반 호출보다 성능 느림 (캐싱으로 완화 가능)
단점 3private 접근 가능 → 캡슐화 위반

성능 문제 완화 - 캐싱

// 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);
}

리플렉션은 프레임워크 내부에서 쓰는 강력한 도구입니다. 일반 애플리케이션 코드에서 직접 사용하면 캡슐화 위반 + 성능 저하 + 타입 안정성 감소로 이어지므로, 꼭 필요한 경우에만 신중하게 사용해야 합니다.

CSLOG에서 보기