* 이 포스팅은 부산대학교 2023 백엔드 미니 부트캠프 2주차 학습 내용을 정리한 글입니다.

리플렉션(Reflection)이란?

객체의 메타데이터 가져오기

대부분 언어에서 객체는 자기 자신에 대한 정보를 가지고 있다. 자바에서도 마찬가지로 객체가 자기 자신에 대한 정보 즉, 메타데이터를 가지고 있다.

클래스 자체

Class<?> clazz = Class.forName("reflection.example.SampleClass");
System.out.println("Class name: " + clazz.getName());

생성자

// 기본 생성자
Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();
// 파라미터가 있는 생성자
Constructor<?> paramConstructor = clazz.getDeclaredConstructor(String.class);

필드(클래스 내부 변수)

Field privateField = clazz.getDeclaredField("privateField");
privateField.setAccessible(true);

Field publicField = clazz.getField("publicField");

메소드(클래스 내부 함수)

Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);

Method publicMethod = clazz.getMethod("publicMethod");
publicMethod.invoke(instance);

인터페이스, 어노테이션 등

Class<?>[] interfaces = clazz.getInterfaces();
Annotation[] annotations = clazz.getAnnotations();

스프링부트에서 사용되는 리플렉션

스프링부트는 이 리플렉션으로 해당 클래스의 여러 작업을 수행한다.

  • 컴포넌트 스캐닝: 패키지 내부 모든 클래스 파일에 대해
  • 의존성 주입 (Dependency Injection): 의존 관계에 있는 두 객체에 대해
  • 프록시 생성: 프록시 객체를 생성할 때
  • 데이터 바인딩: DTO로 DB 데이터를 가져올 때

리플렉션과 .class 파일의 관계

결론부터 말하면, 다른 과정이다.

처음 리플렉션 개념을 공부할 때, javac로 컴파일된 .class 파일에서 리플렉션이 일어나는 줄 알았다. 우테코 유튜브 세미나 자료를 보고 제대로 이해할 수 있었다.

링크된 영상을 요약하자면, JVM 동작 과정에서 .class 파일에서 해당 클래스의 인스턴스는 call stack, operand stack에 담기고 내부 프레임에서 인덱스가 부여된다고 한다. 정확한 정보는 영상에서 자세하게 설명했으니 참고바란다.

결국 모든 필드와 메소드 선언 또한 JVM이 .class 파일로 변환하고, execution engine을 통해서 실행한다. 리플렉션도 결국 자바 소스 코드로 구현된 클래스이니, 위 과정을 거친다. 단지 클래스 메타데이터를 가진다는 점에서 특별한 것이다.

리플렉션의 장단점 feat.chatGPT

장점

1. 유연성

리플렉션을 사용해 동적으로 클래스/패키지를 로딩할 수 있다. 이런 유연성은 파이썬과 C++ 같은 객체 지향 언어에서도 다른 모듈이나 디렉토리를 어느 정도 컨트롤할 수 있다.

2. 프레임워크 개발

스프링이나 하이버네이트와 같은 플러그인에서 자주 사용되는 기능이다. 클래스와 더불어 어떤 일련된 동작을 캡슐화하기 좋다.

3. 코드 분석

JVM의 코드 구조를 분석하거나 디버깅 도구를 개발하는 데 유용하다고 한다. 하지만, 나는 이렇게 리플렉션을 사용해보지 않아 잘 모르겠다.

4. 어노테이션 기반의 메타데이터 처리

리플렉션은 종종 어노테이션과 같이 사용된다. 어노테이션에서 해당 필드/메소드를 가져와 비교적 자유롭게 사용할 수 있다.

단점

1. 성능 오버헤드

리플렉션 연산은 일반적인 자바 연산에 비해 상대적으로 느리다. 따라서, 반복적이고 빈번한 리플렉션 호출은 성능에 부정적인 영향을 줄 수 있다.

2. 보안

privateMethod.setAccessible(true);를 선언하면 private 메소드에 접근할 수 있다. 외부에서 객체를 불러와서 이런 연산을 한다면, 숨겨진 객체에 접근할 가능성이 있다.

3. 복잡성

리플렉션은 언제까지나 메타데이터를 컨트롤하기 위해 사용되야 한다. 물론 인스턴스를 만들 때, getDeclaredConstructor() 메소드로 굳이 만들 수 있지만, 앞서 말했던 상대적으로 느린 연산과 더불어 가독성이 떨어진다.

4. 타입 안정성

예를 들어 필드와 메소드를 리플렉션을 통해 불러와서 이를 String이나 Integer로 다운 캐스팅할 때, 에러가 많이 발생한다.

어노테이션(Annotation)이란?

데코레이터 vs 어노테이션

파이썬에는 데코레이터(decorator)라는 객체가 있다. 어노테이션을 보는데 파이썬 데코레이터가 필요한지 의문이 들 수 있지만, 두 개념을 비교하며 공부하면 재밌을 것 같아 가져와봤다.

공통점

1. 메타 데이터 제공

둘 다 코드에 메타데이터를 부여한다. 동작 제어, 특정 도구/라이브러리와 상호작용할 때 정보를 제공한다.

Flask

@app.route('/home')
def home():
    return "Hello, Home!"

Spring

@RestController
public class HomeController {
    @GetMapping("/home")
    public String home() {
        return "Hello, Home!";
    }
}

2. 코드 수정 X

코드 구조, 로직의 변경 없이 기능을 변경할 수 있다. 코드 중복을 줄이고 모듈성이 증가한다.

python 실행 시간 출력 함수

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time:.2f} seconds.")
        return result
    return wrapper

@timer
def example_func():
    time.sleep(2)

java 의존성 주입

@Service
public class ExampleService { }

@RestController
public class ExampleController {
    @Autowired
    private ExampleService exampleService;
}

3. 선언적 접근

행위가 아닌 상태에 집중한 코드를 작성하게 도와준다. 코드 의도를 명확하게 드러내고, 복잡한 구현 디테일을 숨길 수 있다.

python 로깅

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}...")
        return func(*args, **kwargs)
    return wrapper

@logger
def say_hello(name):
    return f"Hello, {name}!"

java JPA에서 엔티티와 필드 매핑

@Entity
public class User {
    @Id
    private Long id;
    
    @Column(name = "username")
    private String name;
}

차이점

1. 동작 메커니즘

파이썬의 데코레이터는 런타임에 동작한다. 함수/클래스가 정의될 때 데코레이터가 바로 실행되어 원래 함수/클래스를 수정 혹은 대체할 수 있다. 반면, 자바의 어노테이션은 주로 컴파일 타임에 동작한다. 런타임에서도 동작하긴 하지만, 어노테이션이 코드를 수정 혹은 대체하지 않는다. 대신 메타데이터를 제공해 외부에서 이를 활용한다.

python 로거

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logger
def say_hello():
    print("Hello!")

java @Override

public class Example {
    @Override
    public String toString() {
        return "This is an example.";
    }
}

2. 적용 대상

파이썬 데코레이터는 함수와 클래스에 적용되어 수정된 함수나 클래스를 반환하는 반면, 자바 어노테이션은 메소드, 클래스, 패키지, 파라미터, 변수 등 여러 대상에 적용된다.

python class

def decorator(cls):
    cls.new_attribute = "New attribute added!"
    return cls

@decorator
class Example:
    pass

java class, method

@Entity
public class User {
    @Id
    private Long id;
}

3. 생성 및 정의

파이썬 데코레이터는 일반 함수/클래스로 정의된다.

def simple_decorator(func):
    return func

반면, 자바 어노테이션은 인터페이스의 특별한 형태로 정의된다. 자체 로직을 가지지 못하고 메타데이터로 동작한다.

public @interface CustomAnnotation {
    String value() default "";
}
profile
Someday, the dream will come true

0개의 댓글

Powered by GraphCDN, the GraphQL CDN