밤빵's 개발일지
[TIL]20240920 리플렉션(Reflection)의 활용 본문
나는 결제 기능을 구현하는 과정에서 리플렉션(Reflection)이라는 기능을 처음 접하게 되었다. 리플렉션은 단순히 필드나 메서드에 강제로 접근하는 것 외에도 여러 가지 용도로 사용되고, 주로 동적 프로그래밍을 지원하기 위한 도구로 클래스의 구조를 실행 시간에 동적으로 조사하거나 조작할 수 있다. 이번 개발일지에서는 리플렉션을 통해 어노테이션, 클래스 정보, 생성자, 메서드, 필드 등을 다루는 다양한 사용 예와 그 의미를 정리해 보았다.
▶ 어노테이션(Annotations) 처리
리플렉션은 어노테이션을 동적으로 확인하고 처리하는 데에도 자주 사용된다. 어노테이션은 자바에서 메타데이터를 제공하는 방식으로, 리플렉션을 통해 클래스, 메서드, 필드에 붙어 있는 어노테이션을 실행 중에 읽어와 그 정보를 기반으로 동작을 수행할 수 있다.
▽ 어노테이션을 리플렉션으로 접근하는 예시
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
// 어노테이션 정의
@Retention(RetentionPolicy.RUNTIME) // 런타임에 어노테이션을 사용할 수 있게 설정
@interface MyAnnotation {
String value();
}
// 어노테이션이 붙은 메서드
class MyClass {
@MyAnnotation(value = "Hello")
public void myMethod() {
}
}
// 리플렉션으로 어노테이션 정보 추출
public class AnnotationExample {
public static void main(String[] args) throws Exception {
Method method = MyClass.class.getMethod("myMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("Annotation value: " + annotation.value());
}
}
}
→ 위 코드는 MyClass의 myMethod()에 붙어 있는 MyAnnotation의 값을 리플렉션을 통해 읽어오는 예시로, 어노테이션을 기반으로 특정 로직을 구현하는 프레임워크들이 많이 있다. 예를 들어, Spring의 DI(Dependency Injection)나 JUnit의 테스트 기능이 이 방식으로 동작한다.
▶클래스 정보 및 메타데이터 접근
리플렉션을 사용하면 클래스의 이름, 패키지 정보, 상위 클래스 등 클래스 메타데이터를 동적으로 가져올 수 있다. 이를 통해 클래스 구조를 동적으로 분석할 수 있다.
▽ 클래스 정보에 접근하는 예시
public class ReflectionExample {
public static void main(String[] args) {
Class<?> clazz = String.class;
// 클래스 이름 출력
System.out.println("Class Name: " + clazz.getName());
// 상위 클래스 정보 출력
System.out.println("Superclass: " + clazz.getSuperclass());
// 인터페이스 정보 출력
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
System.out.println("Implemented Interface: " + iface.getName());
}
}
}
→ 위 코드는 String 클래스의 이름, 상위 클래스, 그리고 구현한 인터페이스에 대한 정보를 출력하는 예시로, 리플렉션을 사용하여 클래스의 구조를 동적으로 확인하고 분석할 수 있다.
▶ 생성자(Constructor) 호출
리플렉션을 사용하면 생성자에 대한 정보에 접근하고, 이를 통해 객체를 동적으로 생성할 수 있다. 이 기능을 통해 동적으로 클래스의 객체를 생성할 수 있다는 점을 알게 되었다.
▽ 생성자 호출 예시
import java.lang.reflect.Constructor;
public class ConstructorExample {
public static void main(String[] args) throws Exception {
Class<?> clazz = String.class;
// String 클래스의 생성자 중에서 문자열을 받는 생성자를 가져옴
Constructor<?> constructor = clazz.getConstructor(String.class);
// 동적으로 객체 생성
String str = (String) constructor.newInstance("Hello Reflection");
System.out.println(str); // 출력: Hello Reflection
}
}
→ 위 코드는 String 클래스의 생성자를 가져와, 동적으로 객체를 생성하는 예시로, 특정 클래스의 이름을 알지 못한 상태에서도 클래스의 생성자를 통해 객체를 생성할 수 있다.
▶메서드 호출 및 필드 조작
리플렉션을 통해 클래스의 메서드를 실행하거나, 필드 값을 설정 및 읽어올 수 있는 방법도 배웠다. 이렇게 하면 컴파일 시점에 메서드나 필드가 확정되지 않아도, 런타임에 동적으로 해당 메서드나 필드를 사용할 수 있다.
▽ 메서드 호출 예시:
import java.lang.reflect.Method;
public class MethodExample {
public static void main(String[] args) throws Exception {
Class<?> clazz = String.class;
// 메서드 가져오기
Method method = clazz.getMethod("substring", int.class, int.class);
// 메서드 호출
String str = "Hello Reflection";
String result = (String) method.invoke(str, 0, 5);
System.out.println(result); // 출력: Hello
}
}
→ 위 코드는 String 클래스의 substring() 메서드를 리플렉션으로 호출하여 문자열의 일부를 잘라내는 예시로, 리플렉션을 통해 메서드를 동적으로 호출할 수 있음을 알 수 있다.
▶동적 프록시(Dynamic Proxy)
리플렉션을 기반으로 한 동적 프록시는 특정 인터페이스를 구현한 객체를 런타임에 동적으로 생성하는 기능이다. 이는 주로 AOP(Aspect-Oriented Programming)나 프레임워크에서 많이 사용된다.
▽동적 프록시 예시:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Hello {
void sayHello();
}
class HelloImpl implements Hello {
public void sayHello() {
System.out.println("Hello!");
}
}
public class ProxyExample {
public static void main(String[] args) {
Hello hello = new HelloImpl();
// 동적 프록시 생성
Hello proxyInstance = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[] { Hello.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method");
Object result = method.invoke(hello, args);
System.out.println("After method");
return result;
}
}
);
proxyInstance.sayHello();
}
}
→ 이 코드는 Hello 인터페이스의 구현체인 HelloImpl의 메서드 호출 전후에 다른 로직을 추가하기 위해 동적 프록시를 생성하는 예시이다. 이처럼 동적 프록시는 메서드 호출 전후에 원하는 동작을 삽입할 수 있어 매우 유용하다.
▶결론
이번 개발일지를 통해 리플렉션이 단순히 필드나 메서드에 강제로 접근하는 것 이상의 다양한 활용 방식을 제공한다는 것을 알 수 있었다. 리플렉션은 어노테이션 처리, 클래스 메타데이터 접근, 생성자 호출, 메서드 실행, 동적 프록시 생성 등 여러 가지 유연한 기능을 제공한다. 하지만 리플렉션은 성능 저하와 코드의 복잡성을 증가시킬 수 있어 신중하게 사용해야 한다는 점도 함께 알게 되었다.... 그래도 강제로 데이터를 가져오는 리플렉션은 많은 생각이 들게한다..😟
'개발Article' 카테고리의 다른 글
[WIL]20240922 API Rate Limiting (3) | 2024.09.23 |
---|---|
[TIL]20240921 SQL&NoSQL 데이터베이스 (0) | 2024.09.21 |
[TIL]20240919 REST 와 SOAP (0) | 2024.09.19 |
[TIL]20240918 TCP? UDP? (0) | 2024.09.18 |
[TIL]20240917 Singleton! (0) | 2024.09.18 |