리플렉션
리플렉션(Reflection)은 자바 프로그램이 런타임에 자신의 구조를 검사하고, 심지어 동적으로 변경할 수 있도록 하는 기능으로, java.lang.reflect 패키지에 포함된 클래스들을 사용하여 구현한다.
-
본질: 코드가 실행되는 도중 자신의 클래스, 메소드, 필드, 생성자 등의 메타 데이터에 접근하고 이들을 객체처럼 다룰 수 있게 한다.
-
접근 가능 범위: 접근 지정자와 관계 없이 모든 클래스 멤버에 접근하고 조작할 수 있다.
리플렉션의 주요 기능 및 구성 요소
주로 class 객체를 얻는 것에서 시작하며, 이를 통해 클래스의 모든 세부 정보를 추출하고 조작한다.
-
class 객체: 런타임에 클래스 정보를 얻는 세 가지 주요 방법이 있다.
-
.class 사용: 컴파일 시점에 클래스 이름을 알고 있을 때 얻을 수 있다.
-
Object.getClass() 메소드 사용: 이미 생성된 객체로부터 클래스 정보를 얻을 때 얻을 수 있다.
-
Class.forName() 메소드 사용: 클래스 이름을 문자열로 알고 있을 때 얻을 수 있다.
-
핵심 java.lang.reflect 클래스 표는 다음과 같다.
| 클래스 | 역할 | 주요 메소드 |
|---|---|---|
| Field | 클래스의 필드 정보를 나타낸다. | getName(), getType(), get(Object obj), set(Object obj, Object value) |
| Method | 클래스의 메소드 정보를 나타낸다. | getName(), getReturnType(), invoke(Object obj, Object args) |
| Constructor | 클래스의 생성자 정보를 나타낸다. | getName(), newInstance(Object args) |
리플렉션의 주요 활용 분야
직접적인 비즈니스 로직보다는 프레임워크, 라이브러리, 테스트 코드 등 간접적인 영역에서 사용된다.
-
DI/IoC 컨테이너: 스프링 프레임워크(Spring Framework)의 핵심 원리로 클래스 이름에 붙은 어노테이션 정보를 런타임에 읽으며, 이를 바탕으로 객체를 동적으로 생성하거나 메소드를 호출하여 의존성을 주입한다.
-
동적 프록시/AOP: 실행 시점에 기존 클래스를 상속받거나 구현하는 새로운 프록시 클래스를 생성하여, 메소드 호출 전/후로 로깅, 트랜잭션 등 공통 로직(AOP)을 삽입할 때 사용된다.
-
ORM: JPA/Hibernate 도구와 같은 ORM 도구는
@Entity어노테이션이 붙은 클래스의 필드 정보를 리플렉션으로 추출하여, 해당 필드 이름과 타입에 맞게 데이터베이스 테이블의 컬럼을 매핑하고 데이터를 읽고 쓴다. -
Unit Test 프레임워크: JUnit 같은 테스트 프레임워크는
@Test어노테이션이 붙은 메소드를 찾아내어 자동으로 실행한다.
리플렉션 사용 단점
리플렉션을 남용하면 성능 저하와 보안/유지 보수의 문제를 야기할 수 있다.
-
성능 저하: 일반적인 직접 호출 방식보다 느리다. 리플렉션을 사용할 때는 JVM 최적화가 어렵고, 런타임에 클래스 정보를 검색하고 메소드를 호출하는 과정에서 오버헤드가 발생한다.
-
추상화 파괴 및 보안 문제: 접근 지정자를 무시하고 멤버에 접근할 수 있어, 객체지향의 캡슐화를 파괴하고 보안 취약점을 만들 위험이 있다.
-
컴파일 타임 오류 검사 불가: 메소드나 필드 이름을 문자열로 사용하기 때문에, 이름이 잘못되어도 컴파일 타임에 오류를 잡아낼 수 없고, 오직 런타임에만 NoSuchMethodException 예외가 발생한다.