[Java] Java 리플렉션

나른한 개발자·2025년 12월 24일

f-lab

목록 보기
2/44

리플렉션

자바에서 클래스나 멤버에 대한 정보를 런타임에 조사하고, 조작할 수 있는 기능이다. 예를 들어 클래스 이름, 메서드, 필드, 생성자 등에 대한 정보를 프로그램을 실행하면서 알아내고 이를 통해 객체를 생성하거나 메서드를 호출할 수 있다. 이 기능 때문에 개발자는 코드의 유연성과 확장성을 높일 수 있다.

Class<?> clazz = Class.forName("java.lang.String");
Method[] methods = clazz.getDeclaredMethods();

for (Method method : methods) {
    System.out.println(method.getName());
}

리플렉션의 중요성

자바의 리플렉션은 프레임워크와 라이브러리에서 중요하게 사용된다. 스프링 프레임워크에서는 리플렉션을 사용해서 많은 핵심 기능들을 구현한다.

예를 들어, 의존성 주입이 대표적이다. 스프링은 리플렉션을 통해서 클래스의 메타 데이터를 분석하고, @Autowired 어노테이션이 붙은 필드를 찾아서 자동으로 의존성을 주입해준다.

이 과정에서 스프링 컨테이너는 리플렉션을 사용해서 해당 필드의 타입에 맞는 빈을 찾고 그 빈을 해당 필드에 할당한다. 이를 통해 개발자는 객체 생성과 의존성 관리를 수동으로 하지 않아도 된다.

스프링의 @Autowired

스프링의 @Autowired는 리플렉션을 사용하여 생성자 주입을 한다.

1. 생성자 찾기
스프링은 클래스의 @Autowired 어노테이션이 붙은 생성자를 특별히 주목한다. 스프링은 내부적으로 리플렉션을 사용하여 이런 생성자들을 찾아낸다. 리플렉션을 사용하면 프로그램이 실행되는 동안 클래스의 구조를 들여다보고, @Autowired 어노테이션이 적용된 생성자를 식별할 수 있다.

2. 의존성 해석
스프링은 @Autowired가 붙은 생성자를 살펴보고 그 안에 있는 매개변수를 검토한다. 스프링이 객체를 만들때 주입해야하는 '의존성'들을 찾아낸다. 스프링은 매개변수의 타입을 보고 같은 타입의 객체(빈)를 스프링 컨테이너에서 찾아낸다.

3. 객체 생성 및 주입
생성가 요구하는 의존성을 찾으면 의존성 객체들을 스프링 컨테이너에서 꺼내어 리플렉션의 newInstance() 메서드로 주입하여 인스턴스를 생성한다.

리플렉션의 클래스 정보 접근 방법

자바에서 클래스 정보에 접근하기 위해서는 Class 타입의 객체가 필요하다. 자바의 모든 클래스와 인터페이스에 대응되는 메타데이터를 담고있다.

  • Object 클래스의 getClass() 메서드 사용
  • .class 문법 사용
  • Class.forName() 메서드 사용

리플렉션 사용 사례

  1. 스프링 프레임워크
    스프링에서는 리플렉션을 사용해서 의존성 주입(Dependency Injection)을 구현한다. 예를 들어, @Autowired 어노테이션을 사용하면 스프링 컨테이너가 리플렉션을 이용해 필요한 객체를 클래스에 자동으로 주입해 준다. (이 설명은 위에 적혀있다.)

  2. 하이버네이트
    하이버네이트는 리플렉션을 사용해 객체와 데이터베이스 테이블 간의 매핑을 동적으로 처리한다.

  3. JUnit 테스트 프레임워크
    JUnit에서는 리플렉션을 활용해서 테스트 케이스를 동적으로 로드하고 실행한다. @Test 어노테이션이 달린 메소드를 찾아내 테스트를 수행하는 방식이다.

장점

  • 코드의 유연성 향상: 런타임에 클래스, 메서드, 필드 정보를 동적으로 조사하고 조작할 수 있어 코드 유연성을 크게 향상 시킨다. 예를 들어 외부 설정 파일에 따라서 동적으로 객체를 생성하거나, 메서드를 호출하는 작업이 가능해진다.
  • 프레임워크 및 라이브러리 개발에 유용: 스프링과 같은 프레임워크는 리플렉션을 통해 더 유연하고 확장 가능한 구조를 제공한다.

단점

  • 보안 문제: 리플렉션을 사용하면 private 필드에도 접근 가능하다. 내부적으로 보호해야할 데이터에 접근하거나 변경할 수 있어서 보안을 위협할 수 있다.
  • 성능 저하: 런타임에 메서드나 필드를 찾는 과정이 추가되기 때문에 일반적인 코드 실행에 비해 더 많은 시간과 자원을 소모한다. 그래서 성능이 중요한 애플리케이션에서는 리플렉션을 신중히 사용해야한다.

리플렉션이란 런타임 시에 클래스, 메서드, 필드에 대한 정보를 조사하고 조작할 수 있는 자바 API 입니다. 리플렉션을 활용하면 유연성과 확장성 높일 수 있는데, 주로 프레임워크나 라이브러리 개발 시에 유용하게 활용됩니다.
대표적으로 스프링이 있습니다. 스프링에서는 의존성 주입에 리플렉션을 사용합니다. 프로그램 실행시에 리플렉션을 사용해서 @Autowired가 달린 생성자를 찾으면 생성자의 매개변수를 통해 의존성을 알아냅니다. 스프링 컨테이너에서 해당 의존성이 존재하면 가져와서 리플렉션을 통해 의존성을 주입하여 객체를 생성합니다. 비슷한 용례로 JUnit같은 도구에서 @Test 어노테이션이 붙은 메서드를 찾아 수행하는 것도 리플렉션을 사용한 것 입니다.

이렇게 리플렉션을 사용하면 유연한 코드를 작성할 수 있지만 private 필드에도 접근 가능하여 내부에서 보호되어야할 데이터까지 조작이 가능할수 있습니다. 또한 컴파일 시점에 타입 체크가 불가능하다는 단점도 있습니다. 따라서 필요한 경우에만 신중하게 사용해야합니다.

  • 리플렉션 성능이 왜 느린가요?
    : JIT 컴파일러가 메서드 호출을 최적화(인라이닝 등)할 수 없고, 클래스 로더를 통해 메타데이터를 매번 조회하거나 보안 검사를 수행하는 과정이 추가되기 때문입니다.

    +) 메서드 인라이닝이란?
    자주 호출되는 아주 짧은 메서드가 있을 때, 매번 그 메서드로 점프해서 실행하는 게 아니라 메서드의 내용을 호출한 곳에 직접 심어버리는 것입니다.
  • 최적화 전: A가 B()를 호출 → B 위치로 이동 → 실행 → 다시 A로 복귀 (이 이동 과정이 '오버헤드'가 됨)
  • 최적화 후: A 코드 안에 B의 내용이 아예 합쳐짐 (이동 없이 쭉 실행)

참고링크

profile
Start fast to fail fast

0개의 댓글