Computer Science/Java

Reflection(리플렉션) 에 대한 개념 및 단점, 그럼에도 쓰는 이유

is낫널 2023. 10. 10. 20:01
728x90
서론 

안녕하세요, is낫널입니다. 

최근에 블로그에 글 작성이 좀 드물었습니다. 

정보처리기사 실기를 준비하느라 CS 공부를 잠시 쉬고 있었습니다 ㅎㅎ 

그런 덕에, 가채점을 보니 안정적으로 합격권에 든 것 같아서 너무 기분이 좋습니다 :-) 

정보처리기사 실기 합격 후기로도 조만간 찾아뵙겠습니다!  

이제 취직에 더 가까워질 수 있게 CS 지식 쌓기에 더 노력해야겠습니다. 

 

본론 

 

Reflection(리플렉션) 이란?

생성자, 메서드, 필드 등 클래스에 대한 정보를 읽고 수정하는 행위를 말합니다. 

먼저, Java는 클래스와 인터페이스의 메타정보를 Class 객체로 관리합니다. 

이러한 메타정보를 프로그램에서 읽고 수정하는 행위Reflection(리플렉션) 이라고 하는 것입니다. 

 

* 메타정보

더보기

패키지 정보, 타입 정보, 멤버(생성자, 필드, 메서드) 정보 등을 말합니다. 

 

 

Reflection = 반사 

Reflection 을 번역하면, 반사 라는 의미를 가집니다.

왜 Reflection 이 이러한 의미를 가진 이름을 갖게 되었는지 아시나요? 

 

JVM 은 클래스 정보를 클래스 로더를 통해 읽어와서 해당 정보를 JVM 메모리에 저장합니다. 

그렇게 저장된 클래스에 대한 정보가 마치 거울에 투영된 모습과 닮아있어,

Reflection 이라는 이름을 가지게 되었다고 합니다. 

 

* JVM(Java Virtual Machine)

더보기
- Java 를 실행하기 위한 가상 기계(컴퓨터)입니다.- Java 는 OS에 종속적이지 않기 때문에, OS에 종속받지 않고 실행하기 위해서
   OS 위에서 Java 를 실행시키기 위해 JVM 이 필요합니다. 
=> 즉, OS 에 종속받지 않고 CPU 가 Java를 인식, 실행할 수 있게 하는 가상 컴퓨터가 JVM입니다. 

그렇다면 리플렉션은 어디에서 사용될까요? 

 

 

 

리플렉션이 사용된 대표적인 예

대표적으로 여러 라이브러리, 프레임워크에서 사용되는 어노테이션이 리플렉션을 사용한 예시입니다. 

리플렉션을 사용하게 되면 클래스와 메소드에 어떤 어노테이션이 붙어있는지 확인할 수 있습니다. 

어노테이션은 그 자체로는 아무 역할도 하지 않기 때문에,

리플렉션을 사용하여 스프링에서 @Bean, @Component 와 같은 어노테이션을 프레임워크의 기능을 사용할 수 있는 것입니다.

이클립스와 같은 IDE에서 Getter, Setter을 자동으로 생성해 주는 기능도 리플렉션을 사용하여 필드 정보를 가져와 구현한다고 합니다. 

 

 

이제 간단하게 리플렉션이 어떤 것인지에 대해 알아봤으니, 이를 사용하는 방법을 간단하게 확인해 보겠습니다. 

 

 

 

Class 객체를 얻는 방법 3가지

앞서 말했듯이 Java는 메타정보를 Class 객체로 다루기 때문에 

리플렉션을 통해 메타정보를 확인하기 위해서 클래스 객체를 얻는 방식을 알아야 합니다. 

//1. 클래스로 얻는 방법
Class a = 클래스이름.class; 
//2. 클래스로 얻는 방법2
Class a = Class.forName("패키지...클래스이름"); 
//3. 객체로부터 얻는 방법 
Class a = 객체참조변수.getClass();

이렇게 간단한 3가지 방식이 있는데, 이를 통한 예시를 확인한다면 아래와 같습니다. 

#1.
Class a = String.class; 

#2. 
Class a = Class.forName("java.lang.String");

#3. 
String str = "리플렉션"
Class a = str.getClass();

생각보다 간단하게 클래스 객체를 얻어오는 것을 확인할 수 있습니다. 

이제 클래스 객체를 얻어왔으면, 클래스 객체가 가지고 있는 메서드를 통해 리플렉션을 할 수 있게 됩니다.

그에 따른 메서드를 확인해보겠습니다. 

 

 

 

Class 객체의 메서드

  • 패키지와 타입 정보를 얻는 메서드
    • Package getPackage()
      • 패키지 정보 읽기
      • 예시(여기서 a는 위에서 생성한 클래스객체의 변수명으로 예시를 들었습니다.) 
        • a.getPackage().getName(): 패키지의 정보를 읽어와 패키지 이름반환
    • String getStringName()
      • 패키지를 제외한 타입 이름
      • 예시
        • a.getStringName() : 해당 클래스명을 반환
    • String getName()
      • 패키지를 포함한 전체 타입 이름
      • 예시
        • a.getName() : 패키지를 포함한 전체 클래스 이름을 반환
  • 멤버 정보(생성자,필드,메서드)를 얻는 메서드
    • Constructor[] getDeclaredConstructor()
      • 생성자 정보 얻기
    • Field[] getDeclaredField()
      • 필드 정보 얻기
    • Method[] getDeclaredMethod()
      • 메서드 정보 얻기
    멤버 정보를 얻는 메서드의 리턴 타입이 배열인 이유는
    한 클래스의 생성자, 필드, 메서드는 overloading(오버로딩) 이 될 수 있기 때문에,
    여러 개의 생성자, 필드, 메서드를 가질 수 있기 때문에 배열타입이 되는 것입니다. 

 

그런데, 우리가 아는 클래스 객체에는 get으로 시작하는 메서드가 또 존재합니다.

바로 getXXX 인데요, 이 메서드와 리플렉션을 위한 메서드와의 차이점에 대해 확인해 보겠습니다. 

 

 

getXXX() vs getDeclaredXXX()

getXXX() 는 상속받은 클래스와 인터페이스를 포함하여 모든 public 요소를 가져옵니다.

반면, getDeclaredXXX() 는 상속받은 클래스와 인터페이스를 제외하고 해당 클래스에 직접 정의된 내용만 가져옵니다.

또한 getXXX() 와는 다르게, 직접 정의된 private, protected, public 메서드를 전부 가져온다는 것입니다. 

이로써 알 수 있는 것은 리플렉션을 사용하면 private, protected, public과 같은 접근 제어자의 영향을 받지 않고

클래스의 필드나 메소드도 가져와서 호출할 수 있다는 것입니다. 

하지만 이것은 장점이 될 수도 있지만 치명적인 단점이 될 수도 있습니다. 

 

 

 

리플렉션의 단점

성능 상의 가장 큰 문제점으로 컴파일러 최적화를 전혀 받지 못한다는 것입니다. 

일반적인 메소드를 호출한다면, 컴파일 시점에 분석된 클래스를 사용하지만 리플렉션은 런타임에 클래스를 분석하므로 속도가 느립니다.

이로 인해 타입 체크가 컴파일타임에 불가능하다는 것을 의미합니다.

즉, JVM 컴파일러에서 타입이 정해지지 않았기 때문에 JIT 컴파일러에서 해당 클래스에 대한 타입을 알고 있지 못합니다.

그래서 JIT 컴파일러에서 최적화되지 않고 매번 미리 명시된 클래스 타입이 맞는지, 생성자가 존재하는지, 생성자에 대한 검증 과정이 들어가야 합니다.

그렇기 때문에 속도가 느려지는 이유와 함께 성능에 대한 문제가 생기는 것입니다. 

 

이뿐만 아니라, 생성 및 호출을 하기 위해서 static 영역에서 클래스 정보에 대한 발견과정이 필요하고

모든 인자들이 박싱 또는 언박싱 과정을 거쳐야 하는 등의 성능이슈 문제도 존재합니다. 

또한, 성능 외에도 런타임에서의 접근 허용에 의한 보안상의 문제와

앞서 얘기한 private 접근제어자에 접근이 가능하게 되면서 노출되는 문제 등 여러 단점들이 존재합니다. 

* 최적화

더보기
  • 같은 결과를 내기만 한다면 더 적은 비용과 시간을 소모할 수 있게 하는 것을 말합니다.
  • 웹 개발에서의 최적화 : 화면을 최대한 빠른 속도로 표시하게 하는 것이 될 수 있습니다.

* JIT(Just-In-Time) 

더보기

런타임 시 기본 시스템 코드로 바이트 코드를 컴파일하여 Java 애플리케이션의 성능을 향상시키는 런타임환경의 컴포넌트입니다.

 

 

그렇다면 이러한 리플렉션은 사용하는 걸까?

어떤 클래스가 들어올지 모르는 상황에서 어떤 클래스가 들어오든 대처를 해야하는 상황에서 리플렉션이 사용됩니다.

하지만 이는, 프레임워크 혹은 라이브러리 등을 만드는 데에 주로 리플렉션이 사용되기 때문에

대부분의 개발자들은 리플렉션이 꼭 필요한 이유가 있는 것이 아니라면, 리플렉션을 한정적으로 사용하는 것을 권장합니다. 

 

 

 

 

마무리

 

CS 스터디를 시작하면서

Java 를 어느 정도 알고 있다고 생각했으나, 그건 제 착각이고 오만이었다는 생각이 들 때가 있습니다.

특히 개발에 있어서는 무언가를 알고 있다는 것에 대해 조금 더 겸손해질 줄 알아야 하는 것 같다는 생각이 들었습니다.

그리고 그 겸손함을 발돋움삼아 계속해서 저 자신을 돌아봐야겠다는 생각도 드네요. 

오늘도 끝까지 읽어주셔서 감사합니다. 

 

 

 

 

참고한 블로그

해당 글은 아래 블로그를 참고하여 글을 작성하였습니다. 


- hudi.blog 

 

자바 리플렉션 (Reflection) 기초

리플렉션 (Reflection) JVM은 클래스 정보를 클래스 로더를 통해 읽어와서 해당 정보를 JVM 메모리에 저장한다. 그렇게 저장된 클래스에 대한 정보가 마치 거울에 투영된 모습과 닮아있어, 리플렉션

hudi.blog

- 기억용 블로그 

 

자바에서 리플렉션을 통해 구현된 기능들은 왜 느릴까??

프레임워크의 내부 구현체를 공부하다 보면 심심치 않게 나오는 단어가 Reflection이었고 많은 사람들이 리플렉션이 느리다고 하지만 딱히 속시원하게 설명해주는 글이 없어 따로 공부해보았다.

middleearth.tistory.com

 

728x90