Replies: 2 comments
-
|
Reflection은 실행 중인 자바 프로그램이 클래스, 필드, 메서드, 생성자, 애노테이션 같은 자기 자신의 구조 정보를 조회하고 조작할 수 있게 해주는 기능이라고 이해했습니다. 일반적인 자바 코드는 컴파일 시점에 어떤 타입의 어떤 메서드를 호출할지 정해지지만, 이런 기능은 주로 Spring, Jackson, JPA 같은 프레임워크에서 많이 사용됩니다. 즉, Reflection은 프레임워크가 사용자의 코드를 일반화해서 다루기 위한 도구라고 볼 수 있습니다. 다만 강력한 만큼 비용도 있습니다. 그래서 애플리케이션 코드에서 일상적으로 직접 사용하기보다는, Reflection은 어떤 개념인가?Reflection은 런타임에 클래스의 구조 정보를 조회하고 조작할 수 있는 자바 기능입니다. 일반적인 코드는 컴파일 시점에 어떤 타입을 사용할지 알고 있습니다. Member member = new Member("moca");
member.getName();이 코드는 반면 Reflection을 사용하면 실행 중에 클래스 정보를 가져올 수 있습니다. Class<?> clazz = Class.forName("roomescape.Member");
Method method = clazz.getDeclaredMethod("getName");즉, Reflection은 컴파일 시점에 직접 알지 못해도, 런타임에 클래스의 필드, 메서드, 생성자, 애노테이션 정보를 들여다볼 수 있게 해주는 기능입니다. 왜 사용하게 될까?Reflection은 주로 프레임워크나 라이브러리가 사용자의 코드를 일반화해서 다루기 위해 사용됩니다. 예를 들어 Spring은 우리가 만든 클래스를 미리 알 수 없습니다. 하지만 실행 중에 클래스들을 살펴보고, 또 생성자를 찾아 객체를 만들고, 필요한 의존성을 주입할 수도 있습니다. Jackson도 비슷합니다. JSON을 DTO 객체로 바꿀 때, DTO에 어떤 필드가 있는지 런타임에 확인하고 값을 매핑합니다. JPA도 DB에서 조회한 row를 엔티티 객체로 복원할 때 기본 생성자를 호출하고 필드에 값을 넣는 과정에서 Reflection을 사용할 수 있습니다. 즉, Reflection은 아래와 같은 자동화를 가능하게 합니다.
프레임워크는 사용자가 어떤 클래스를 만들지 모르기 때문에, Reflection을 통해 런타임에 구조를 분석하고 처리합니다. Reflection은 어떻게 동작하나요?JVM은 클래스가 로딩될 때 해당 클래스의 메타데이터를 가지고 있습니다. 여기에는 클래스 이름, 필드, 메서드, 생성자, 애노테이션 정보 등이 포함됩니다. Reflection API는 이 메타데이터에 접근할 수 있게 해줍니다. 먼저 Class<Member> clazz = Member.class;또는 클래스 이름 문자열로 가져올 수도 있습니다. Class<?> clazz = Class.forName("roomescape.Member");이후 필드, 메서드, 생성자 정보를 조회할 수 있습니다. Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
Constructor<?>[] constructors = clazz.getDeclaredConstructors();객체를 생성할 수도 있습니다. Constructor<Member> constructor = clazz.getDeclaredConstructor(String.class);
Member member = constructor.newInstance("moca");메서드를 실행할 수도 있습니다. Method method = clazz.getDeclaredMethod("getName");
Object result = method.invoke(member);필드 값을 읽거나 바꿀 수도 있습니다. Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(member, "newName");여기서 그래서 Reflection은 강력하지만, 객체가 의도한 접근 제한을 우회할 수 있습니다. Reflection의 장점Reflection의 가장 큰 장점은 유연성입니다. 프레임워크는 사용자가 어떤 클래스를 만들지 미리 알 수 없습니다. 그런데도 Reflection을 사용하면 런타임에 클래스 구조를 읽고 필요한 작업을 할 수 있습니다. 예를 들어 Spring은 우리가 작성한 클래스를 스캔해서 빈으로 등록하고, 생성자를 호출하고, 의존성을 주입합니다. Jackson은 DTO의 필드를 읽어서 JSON과 객체를 서로 변환합니다. JPA는 DB row를 엔티티 객체로 복원합니다. 즉, Reflection 덕분에 프레임워크는 다양한 사용자 코드를 공통된 방식으로 다룰 수 있습니다. Reflection의 단점과 주의점Reflection은 강력하지만 비용도 있습니다. 첫째, 컴파일 시점 타입 검사의 도움을 덜 받습니다. clazz.getDeclaredMethod("getNmae");위처럼 메서드 이름에 오타가 있어도 컴파일러가 잡아주지 못합니다. 실행 중에 예외가 발생합니다. 둘째, 성능 비용이 있을 수 있습니다. 일반 메서드 호출보다 Reflection 호출은 메타데이터 조회, 접근 권한 확인, 동적 호출 과정이 들어가기 때문에 더 무거울 수 있습니다. 셋째, 캡슐화를 깨뜨릴 수 있습니다.
이렇게 되면 객체가 생성자나 메서드를 통해 보장하려던 규칙을 우회할 수 있습니다. 예를 들어 도메인 객체가 생성자에서 검증을 수행해 항상 유효한 상태로 만들어지도록 설계했더라도, Reflection으로 필드에 직접 값을 넣으면 그 검증을 우회할 수 있습니다. 이 때문에 불변식이 깨질 위험이 있습니다. JPA와 Reflection을 연결해서 보면JPA는 엔티티를 DB에서 조회한 뒤 객체로 복원해야 합니다. 이때 JPA는 기본 생성자를 호출하고, 필드에 값을 주입하는 방식으로 객체를 만들 수 있습니다. 그래서 JPA 엔티티에는 보통 기본 생성자가 필요합니다. protected Member() {
}또한 JPA는 필드에 직접 값을 넣을 수 있기 때문에, 우리가 만든 생성자 검증 경로를 반드시 통과하지 않을 수 있습니다. 이 점에서 JPA 엔티티는 순수한 도메인 객체와 조금 다른 제약을 가집니다. 예를 들어 즉, Reflection은 JPA가 객체 복원을 자동화할 수 있게 해주지만, 그 대가로 도메인 객체의 캡슐화나 불변성 설계에는 제약이 생길 수 있습니다. 정리Reflection은 런타임에 클래스의 구조 정보를 조회하고 조작할 수 있게 해주는 자바 기능입니다. 프레임워크는 Reflection을 통해 사용자가 만든 클래스를 미리 알지 못해도 객체 생성, 의존성 주입, JSON 매핑, DB row 매핑 등을 자동화할 수 있습니다. 다만 Reflection은 문자열 기반 접근, 성능 비용, 캡슐화 우회라는 단점이 있습니다. 그래서 직접 자주 사용하는 기능이라기보다는, Spring, Jackson, JPA 같은 프레임워크가 유연한 자동화를 제공하기 위해 내부적으로 사용하는 기술에 가깝다고 생각합니다. |
Beta Was this translation helpful? Give feedback.
-
1. 어떤 개념인가요?보통의 자바 코드가 컴파일 시점에 어떤 클래스를 쓸지 정해져 있는 반면, 2. 왜 사용하게 될까요?프레임워크는 우리가 어떤 클래스를 만들지 미리 알 수 없기 때문입니다. 예를 들어 Spring은 어떤 클래스를
와 같은 같은 작업을 합니다. 3. 어떻게 동작하나요?JVM은 클래스를 로딩할 때 그 클래스의 정보를 담은
예시 코드아래 코드는 리플렉션을 이용해서 User 클래스의 메서드를 실행하는 과정입니다 이런 식으로 런타임에 동적으로 객체를 다룰 수 있습니다. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Beta Was this translation helpful? Give feedback.
All reactions