Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240920 리플렉션(Reflection)의 활용 본문

개발Article

[TIL]20240920 리플렉션(Reflection)의 활용

최밤빵 2024. 9. 20. 21:52

나는 결제 기능을 구현하는 과정에서 리플렉션(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