오봉이와 함께하는 개발 블로그

Java - Reflection API 본문

자바

Java - Reflection API

오봉봉이 2022. 11. 26. 18:01
728x90

Reflection

Spring을 사용하면 Reflection을 자주 접할 수 있다. 하지만 초보 개발자들에겐 생소한 내용이라 생각한다.(작성자도 잘 몰라서 정리하고 있습니다..)

이 글은 간단하게 Reflection에 대해 알아보는 글이다.
자세한 내용은 다음 링크를 참조하자

Reflection API?

Reflection API란 구체적인 타입을 알지 못해도 그 클래스의 정보(메서드, 타입, 변수 등)에 접근할 수 있게 해주는 API다.
구체적인 타입을 알지 못 하는데 어떻게 클래스에 접근할 수 있는 건지 대충 들으면 이해하기 어렵다 생각하고 나 또한 그러하다.

이제부터 알아보자.

아래와 같은 Car클래스가 있다.

public class Car {
    private final String name;
    private int position;

    public Car(String name, int position) {
        this.name = name;
        this.position = position;
    }

    public void move() {
        this.position++;
    }

    public int getPosition() {
        return position;
    }
}

그럼 아래와 같이 객체 생성이 가능할 것이다.

public static void main(String[] args) {
    Object obj = new Car("foo", 0);
}

생성된 객체 objCar클래스의 move() 메서드 사용이 가능할까? 정답은 불가능하다.

자바는 컴파일러를 사용하기 때문에 컴파일 시점에 타입이 결정된다. obj라는 객체는 컴파일 시점에 Object라는 타입으로 결정됐기 때문에 Object 클래스의 인스턴스 변수와 메서드만 사용할 수 있다.

public static void main(String[] args) {
    Object obj = new Car("foo", 0);
    obj.move();    // 컴파일 에러 발생 java: cannot find symbol
}

위 코드는 컴파일 에러가 발생한다.

생성된 객체는 Object라는 타입만 알고 있지 Car 클래스라는 구체적 타입은 알지 못 하기 때문이다.
이 말은 자바에서 구체적인 클래스를 알지 못 하고 있으면 해당 클래스의 정보에 접근할 수 없다는 뜻이다.

이런 상황에서 Car 클래스에 접글할 수 있도록 도와주는 것이 Reflection API다.

예시

똑같은 상황에서 Car 클래스에 접근할 수 있도록 하는 코드는 아래 코드를 참조하자

public static void main(String[] args) throws Exception {
    Object obj = new Car("foo", 0);
    Class carClass = Car.class;
    Method move = carClass.getMethod("move");

    // move 메서드 실행, invoke(메서드를 실행시킬 객체, 해당 메서드에 넘길 인자)
    move.invoke(obj, null);

    Method getPosition = carClass.getMethod("getPosition");
    int position = (int)getPosition.invoke(obj, null);
    System.out.println(position);
    // 출력 결과: 1
}

move()메서드를 통해 생성당시 0이던 포지션 값을 1로 바꿔주고 getPosition()을 호출해서 값을 확인할 수 있다.

Class carClass2 = Class.forName("Car");

위 코드를 보면 클래스의 이름만 알고 있어도 해당 클래스의 정보를 가져올 수 있다.
Reflection API는 클래스의 이름만 가지고도 생성자, 필드, 메서드 등 해당 클래스에 대한 거의 모든 정보를 가져올 수 있는 API다.

어떻게?

자바에서는 JVM이 실행되면 사용자가 작성한 자바 코드가 컴파일러를 거쳐 바이트 코드로 변환되어 static 영역에 저장된다.
Reflection API는 이 정보를 활용한다. 그래서 클래스 이름만 알고 있다면 언제든 static 영역을 뒤져서 정보를 가져올 수 있다.

활용 방법

Reflection이 무엇인지 알았으니 어떻게 활용하는지도 알아야 할 것 이다.
보통의 상황 그니까 우리가 개발할 때는 사용할 일이 거의 없을 것이다. 왜냐하면 본인이(혹은 팀원이) 작성한 코드이기 때문에 구체적인 클래스를 모를 일이 없기 때문이다.

또한 치명적인 단점이 있기 때문에 사용하지 않을 수 있다면 사용하지 않는 것이 좋다.
대표적으로 성능 오버헤드가 있는데, 컴파일 타임이 아닌 런타임에 동적으로 타입을 분석하고 정보를 가져오기 때문에 JVM을 최적화 할 수 없다.
또 직접 접근이 불가능한 private 인스턴스 변수, 메서드에 접근하기 때문에 내부를 노출하여 추상화가 깨지고 이로 인해 예상치 못한 반작용이 발생할 수 있다.

그래서 Reflection은 주로 프레임워크나 라이브러리에서 사용한다. 프레임워크나 라이브러리에서 사용자가 어떤 클래스를 만들지 예측이 불가능하기 때문에 동적으로 해결하기 위해 Reflection을 사용한다.

Spring에서도 Reflection을 사용하는데 대표적으로 Spring Container의 BeanFactory가 있다.
Bean은 애플리케이션이 실행한 후 런타임에 객체가 호출될 때 동적으로 객체의 인스턴스를 생성하는데 이때 Spring Container의 BeanFactory에서 Reflection을 사용한다.

Spring Data JPA 에서 Entity에 기본 생성자가 필요한 이유도 동적으로 객체 생성 시 Reflection을 활용하기 때문이다.
Reflection으로 가져올 수 없는 정보 중 하나가 생성자의 인자 정보이다. 그래서 기본 생성자가 반드시 있어야 객체를 생성할 수 있는 것이다. 기본 생성자로 객체를 생성만 하면 필드 값 등은 Reflection으로 넣어줄 수 있다.

728x90
Comments