자바/자바

(자바) 리플렉션의 개념 * 예제

불광동 물주먹 2025. 6. 23. 23:41

1. 리플렉션의 개념

리플렉션(Reflection)은 자바에서 런타임 중 클래스, 메서드, 필드 등의 정보를 조회하고 조작할 수 있는 기능이다.
java.lang.reflect 패키지를 통해 제공되며,
Class 객체를 통해 메서드를 호출하거나 필드에 접근하는 것이 가능하다.

Class<?> clazz = Class.forName("com.example.MyService");
Object instance = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("run");
method.invoke(instance);

 

이처럼 클래스 이름이 문자열로 주어졌을 때도 객체 생성과 메서드 호출이 가능하다는 점이 리플렉션의 핵심이다.
컴파일 타임이 아닌 런타임에 구조를 해석하고 실행하므로 동적 처리가 가능하다.


2. 정적과 동적의 차이

자바는 정적 언어다. 즉, 대부분의 구조는 컴파일 타임에 결정된다.
new MyService()처럼 클래스가 명확하게 지정되어 있으면, 컴파일러가 타입과 메서드를 모두 인식한다.

반면, 리플렉션은 클래스 이름, 메서드 이름이 런타임에 문자열로 주어지기 때문에
컴파일러가 구조를 인식하지 못한다. 런타임에 오류가 발생할 수 있다는 단점이 있지만,
코드 변경 없이 다양한 클래스를 유연하게 처리할 수 있다는 장점이 있다.


3. 리플렉션이 사용되는 대표적인 상황

1) 의존성 주입(DI)

Spring Framework의 @Autowired, @Inject는 리플렉션을 기반으로 동작한다.
어노테이션이 붙은 필드나 생성자를 찾아서, 컨테이너에 등록된 객체를 주입한다.

@Autowired
private UserService userService;

 

이 때 Spring 내부에서는 다음과 같은 흐름이 있다.

  • 클래스 로딩 후, 모든 필드를 탐색
  • @Autowired가 붙은 필드를 찾고, 해당 타입의 빈을 주입
  • field.setAccessible(true)로 private 필드에 접근하여 값 대입

직접 new로 생성한 객체에는 이 과정이 일어나지 않는다.


2) 어노테이션 기반 자동 처리

Spring은 클래스에 붙은 어노테이션을 분석하여 특정 동작을 실행한다.
예를 들어 @Controller, @Service, @Repository 같은 클래스 레벨 어노테이션이나,
@RequestMapping, @Transactional 같은 메서드/필드 어노테이션을 분석하는 과정에 리플렉션이 사용된다.

if (clazz.isAnnotationPresent(Controller.class)) {
   // 컨트롤러로 등록
}
 

이 구조 덕분에, 개발자는 단지 어노테이션을 선언하는 것만으로
핵심 비즈니스 외의 다양한 기능(AOP, 트랜잭션 등)을 자동으로 적용받을 수 있다.


3) JSON 직렬화/역직렬화

Jackson, Gson 같은 라이브러리는 리플렉션을 사용해
JSON 문자열을 객체로 변환하거나, 객체를 JSON으로 직렬화한다.

for (Field field : obj.getClass().getDeclaredFields()) {
    field.setAccessible(true);
    Object value = field.get(obj);
}

 

이때 필드 이름이나 타입을 분석하여 JSON과 매핑하는데,
getter가 없어도 private 필드를 직접 접근해서 처리할 수 있다.


4) 테스트 프레임워크

JUnit은 @Test 어노테이션이 붙은 메서드를 찾아 실행한다.
이 때도 메서드를 반복 탐색하고 리플렉션으로 실행하는 방식이다.

for (Method method : clazz.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Test.class)) {
        method.invoke(testInstance);
    }
}

4. new와 리플렉션의 차이


 

구분 new 리플렉션
타입 결정 시점 컴파일 타임 런타임
타입 안전성 컴파일러 확인 가능 런타임 에러 발생 가능
유연성 낮음 매우 높음
코드 변경 시 영향 적음 (설정 변경으로 대체 가능)
 

new는 빠르고 안전하지만 유연성이 떨어진다.
리플렉션은 위험하지만 설정 기반 시스템, 플러그인 구조, 프레임워크 설계에 필수적인 기술이다.


5. 실제 적용 예시

(1) 다양한 클래스를 순회하며 공통 메서드 실행

String[] classNames = {
    "com.app.ImageProcessor",
    "com.app.VideoProcessor",
    "com.app.AudioProcessor"
};

for (String name : classNames) {
    Class<?> clazz = Class.forName(name);
    Object obj = clazz.getDeclaredConstructor().newInstance();
    Method method = clazz.getMethod("run");
    method.invoke(obj);
}

이렇게 하면 새 클래스를 추가해도, 코드를 수정할 필요가 없다.


(2) 어노테이션이 붙은 클래스 자동 실행

for (Class<?> clazz : scannedClasses) {
    if (clazz.isAnnotationPresent(Job.class)) {
        Method method = clazz.getMethod("execute");
        Object obj = clazz.getDeclaredConstructor().newInstance();
        method.invoke(obj);
    }
}

 

새로운 Job 클래스가 추가돼도, 어노테이션만 붙이면 자동 실행 대상이 된다.


6. 리플렉션의 단점과 보완

  • 성능이 일반 호출보다 느리다 → 캐싱이나 MethodHandle 사용으로 보완 가능
  • 타입 안전성이 없다 → 프레임워크는 어노테이션 기반으로 구조 제약을 줌
  • private 접근이 가능해 보안과 캡슐화 위반 가능성 있음 → 제한적으로 사용

7. 마무리 정리

Spring을 비롯한 대부분의 자바 프레임워크는
리플렉션을 바탕으로 DI, 어노테이션 기반 처리, AOP, 프록시, JSON 매핑 등을 구현한다.

프로그래머는 직접 리플렉션을 자주 쓰진 않지만,
리플렉션의 구조를 이해하는 것이 Spring의 동작 원리와 설계 구조를 이해하는 데 핵심이다.

new로는 고정된 객체만 다룰 수 있지만,
리플렉션은 런타임에 동적으로 유연한 처리를 가능하게 하며,
이로 인해 확장성과 설정 기반의 시스템 구현이 가능해진다.

 

 

 

 

https://moon-97.tistory.com/159