Java

Java의 Reflection 사용법과 주의점

누구세연 2024. 11. 24. 22:09

Java에서 Reflection은 클래스나 메서드, 필드 같은 객체 정보를 "런타임"에 읽고 수정할 수 있는 도구입니다. 간단히 말해, 코드가 실행되는 도중에 "클래스의 내부를 엿보거나 조작"할 수 있게 해주는 강력한 기능입니다.

 

이 글에서는 Reflection이 무엇인지, 어떻게 사용하는지, 그리고 주의해야 할 점을 하나씩 살펴보겠습니다! 🚀

 

Reflection이란?

Reflection은 말 그대로 반사처럼 프로그램이 자신을 들여다보고 조작할 수 있게 하는 기능입니다.

어디에 쓰일까요?

  • 라이브러리/프레임워크: Spring 같은 프레임워크에서 빈(bean)을 생성하거나 주입할 때
  • 동적 동작: 컴파일 시점에 알 수 없는 클래스의 메서드나 필드를 실행해야 할 때
  • 테스트: 테스트 코드에서 private 메서드나 필드를 확인할 때

 

Reflection 사용법

Reflection은 `java.lang.reflect`패키지에서 제공됩니다.

1. 클래스 정보를 조회하기

Class<?> clazz = Class.forName("java.util.ArrayList"); // 클래스 이름으로 로드
System.out.println("클래스 이름: " + clazz.getName());
System.out.println("패키지 이름: " + clazz.getPackageName());

결과는 아래와 같습니다.

결과

2.  필드 정보 가져오기

클래스 내부 필드의 정보를 확인하고 값을 변경해 볼 수 있습니다.

Class<?> clazz = Class.forName("com.example.User");
Field field = clazz.getDeclaredField("name"); // "name" 필드 조회
field.setAccessible(true); // private 필드에 접근 가능하게 설정

User user = new User();
field.set(user, "Alice"); // 필드 값 설정
System.out.println(field.get(user)); // 필드 값 읽기

3. 메서드 실행하기

Reflection을 사용하면 메서드도 동적으로 호출할 수 있습니다.

Method method = clazz.getDeclaredMethod("sayHello", String.class); // 메서드 가져오기
method.setAccessible(true); // private 메서드 접근 허용

String result = (String) method.invoke(user, "World"); // 메서드 호출
System.out.println(result); // 결과 출력

4. 객체 생성하기

Reflection을 사용해서 객체를 동적으로 만들 수 있습니다.

Constructor<?> constructor = clazz.getConstructor(); // 기본 생성자 가져오기
User user = (User) constructor.newInstance(); // 객체 생성
System.out.println("생성된 객체: " + user);

 

 

Reflection의 장점

  1. 동적 동작 가능
    런타임에 어떤 클래스인지 몰라도 해당 클래스를 조작할 수 있습니다.
    예: Spring에서 의존성 주입 시 Reflection을 활용합니다.
  2. 유연성
    메서드 이름이나 필드 이름만 알아도 조건에 따라 다른 동작을 수행할 수 있습니다.
    예: JSON 변환 라이브러리에서 객체의 필드를 읽고 JSON으로 변환할 때
  3. 주석(annotation) 처리
    런타임에 주석을 읽어 동적으로 처리할 수 있습니다.
    예: Hibernate에서 엔티티 클래스의 `@Table`이나 `@Column`정보 읽기

 

Reflection의 단점

1) 성능 문제

Reflection은 일반적인 메서드 호출보다 느립니다. JVM 최적화가 어렵기 때문입니다.

👉 해결: 반복적으로 사용하는 Reflection은 캐싱

private static final Method cachedMethod = User.class.getDeclaredMethod("sayHello", String.class);

2) 캡슐화 위반

`setAccessible(true)`로 private 필드나 메서드에 접근하면 객체 지향 원칙을 깨뜨릴 수 있습니다.

👉 해결: 꼭 필요한 경우에만 사용

3) 보안 위험

잘못된 권한 관리로 민감한 데이터가 노출될 수 있습니다.

👉 해결: Reflection 사용 시 적절한 권한 검사를 추가

4) 디버깅 어려움

Reflection은 컴파일 시점에 오류를 잡을 수 없고, 런타임에 에러가 발생할 가능성이 높습니다.

 

 

실무에서 Reflection 활용 예시

1) Spring 프레임워크

Spring에서 빈(bean)을 생성하거나 의존성을 주입할 때, Reflection이 내부적으로 사용됩니다.

2) JSON 변환기

Jackson이나 Gson 같은 라이브러리는 Reflection을 이용해 객체 필드 정보를 읽고 JSON으로 변환합니다.

3) 테스트 코드 작성

테스트할 때 private 필드나 메서드를 Reflection으로 검증할 수 있어요.

 

Reflection을 안전하고 효율적으로 사용하는 팁

  1. 최소한으로 사용하세요.
    • Reflection은 필요한 경우에만 신중히 사용하세요. 일반적인 로직에는 사용하지 않는 것이 좋습니다.
  2. 캐싱 활용
    • 자주 사용하는 Reflection 결과(메서드, 필드)는 캐싱하여 성능 저하를 줄이는 것이 좋습니다.
  3. 보안 매커니즘 적용
    • setAccessible(true)를 사용한 접근은 꼭 신중히 결정해야 합니다.
  4. 대안 검토
    • Reflection 없이 인터페이스나 추상 클래스를 활용할 수 있는지 먼저 고려해야 합니다.

 

 

💡 Reflection은 런타임에 동적으로 코드를 조작할 수 있는 강력한 기능을 제공합니다.
하지만, 성능 저하와 보안 문제를 초래할 수 있으므로 꼭 필요한 경우에만 사용하세요.