πŸ” μžλ°” λ¦¬ν”Œλ ‰μ…˜(Reflection) μ™„μ „ 정리

도두맨·2025λ…„ 5μ›” 30일

곡뢀

λͺ©λ‘ 보기
12/23

νŒŒλž‘, μ•„ν‚€μ˜ λ¦¬ν”Œλ ‰μ…˜ λ°œν‘œλ₯Ό 보고 μ •λ¦¬ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.
λ¦¬ν”Œλ ‰μ…˜μ˜ κ°œλ…λΆ€ν„° DI κ΅¬ν˜„κΉŒμ§€ 싀무 ν™œμš©μ— ν•„μš”ν•œ 핡심 λ‚΄μš© μœ„μ£Όλ‘œ λ‹€λ£Ήλ‹ˆλ‹€.


πŸͺž λ¦¬ν”Œλ ‰μ…˜μ΄λž€?

  • μŠ€ν”„λ§μ—μ„œ μ‹€ν–‰ μ‹œμ μ— 객체λ₯Ό μƒμ„±ν•˜κ³  μ£Όμž…ν•˜λŠ” 것,
  • JPAμ—μ„œ κΈ°λ³Έ μƒμ„±μžκ°€ κΌ­ ν•„μš”ν•œ 이유,
    β†’ λͺ¨λ‘ λ¦¬ν”Œλ ‰μ…˜ λ•Œλ¬Έμž…λ‹ˆλ‹€!

λ¦¬ν”Œλ ‰μ…˜(Reflection):
"κ±°μšΈμ— λΉ„μΉœ λͺ¨μŠ΅μ²˜λŸΌ" 객체λ₯Ό 직접 λ‹€λ£¨λŠ” 것이 μ•„λ‹ˆλΌ JVM λ©”λͺ¨λ¦¬μ—μ„œ λ°˜μ‚¬λœ 클래슀 정보λ₯Ό 톡해 객체, ν•„λ“œ, λ©”μ„œλ“œλ₯Ό μ‘°μž‘ν•˜λŠ” κΈ°μˆ μž…λ‹ˆλ‹€.

πŸ”§ μžλ°”μ˜ λ¦¬ν”Œλ ‰μ…˜ ꡬ쑰

  • classλŠ” JVM에 μ˜ν•΄ μžλ™ 생성
  • μ‹€ν–‰ 쀑 클래슀 정보(μƒμ„±μž, ν•„λ“œ, λ©”μ„œλ“œ λ“±)λ₯Ό λ™μ μœΌλ‘œ μ ‘κ·Ό κ°€λŠ₯

πŸ”Ž λ¦¬ν”Œλ ‰μ…˜μ΄ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯λ“€

κΈ°λŠ₯μ„€λͺ…
μ–΄λ…Έν…Œμ΄μ…˜ 쑰회getAnnotation(s), isAnnotationPresent()
μƒμ„±μž 쑰회 및 호좜getDeclaredConstructor() + newInstance()
ν•„λ“œ 쑰회 및 μ‘°μž‘getDeclaredFields() + setAccessible()
λ©”μ„œλ“œ 쑰회 및 μ‹€ν–‰getDeclaredMethods() + invoke()
클래슀 정보 μ‘°νšŒμƒμ† 정보, μΈν„°νŽ˜μ΄μŠ€ 정보 λ“±

βœ… 클래슀 κ°€μ Έμ˜€κΈ° 3κ°€μ§€

  1. {ν΄λž˜μŠ€νƒ€μž…}.class
Class<?> clazz = Dog.class;
  1. {μΈμŠ€ν„΄μŠ€}.getClass()
Dog hodo = new Dog("hodo");
Class<?> clazz = hodo.getClass();
  1. Class.forName("{전체 도메인 λ„€μž„}")
Class<?> clazz = Class.forName("org.example.Dog");

πŸ’¦ Class의 λ©”μ„œλ“œ μ‚¬μš© μ‹œ 주의점

getMethodsgetDeclaredMethods
μƒμœ„ ν΄λž˜μŠ€μ™€ μƒμœ„ μΈν„°νŽ˜μ΄μŠ€μ—μ„œ μƒμ†ν•œ λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•˜μ—¬ public인 λ©”μ„œλ“œλ“€μ„ λͺ¨λ‘ κ°€μ Έμ˜¨λ‹€.μ ‘κ·Ό μ œμ–΄μžμ™€ 관계 없이 μƒμ†ν•œ λ©”μ„œλ“œλ₯Ό μ œμ™Έν•˜κ³  직접 ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έν•œ λ©”μ„œλ“œλ“€μ„ λͺ¨λ‘ κ°€μ Έμ˜¨λ‹€.

getxxx와 getDeclaredxxxλ₯Ό 잘 κ΅¬λΆ„ν•΄μ„œ μ‚¬μš©ν•΄μ•Ό 함


πŸ§ͺ μƒμ„±μž μ ‘κ·Ό 및 객체 생성

public class Dog {

	private static final String CATEGORY = "동물";
    
    private String name;
    public int age;
    
    private Dog() {
    	this.name = "λˆ„λ μ΄";
        this.age = 0;
	}
    
    public Dog(final String name) {
    	this.name = name
        this.age = 0;
	}
    
    public Dog(final String name, final int age) {
    	this.name = name;
        this.age = age;
	}
}
  • μƒμ„±μžμ˜ νŒŒλΌλ―Έν„°λ‘œ κ΅¬λΆ„ν•˜μ—¬, ν΄λž˜μŠ€μ— μ„ μ–Έλœ μƒμ„±μžλ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.
java
Class<?> class = Class.forName("org.example.Dog");

Constructor<?> constructor1 = clazz.getDeclaredConstructor();
Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
Constructor<?> constructor3 = clazz.getDeclaredConstructor(String.class, int.class);
java
Object dog1 = constructor1.newInstance();
  • μ΄λ ‡κ²Œ newInstance() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  경우 μƒμ„±μžμ˜ μ ‘κ·Όμ œμ–΄μžκ°€ private이라 μ ‘κ·Ό λΆˆκ°€ν•˜λ‹€λŠ” μ˜ˆμ™Έκ°€ λ°œμƒ ν•˜λŠ”λ° μ ‘κ·Όμ œμ–΄μžκ°€ public이 μ•„λ‹Œ 경우 setAccessible λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜λ©΄ μ ‘κ·Όν•  수 μžˆλ‹€.
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Object dog = constructor.newInstance("ν˜Έλ‘", 5);

🐾 ν•„λ“œ μ ‘κ·Ό 및 μˆ˜μ •

Object dog = constructor.newInstance("ν˜Έλ‘", 5);

Field[] fields = clazz.getDeclaredFields();

for (Field field : fields) {
	field.setAccessible(true);
    System.out.println(field);
    System.out.println("value: " + field.get(dog));
    System.out.println("------------------");
}

  • private ν•„λ“œμ˜ 값도 λ³€κ²½ν•  수 μžˆλ‹€.
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(dog, "땅콩");


πŸ” λ©”μ„œλ“œ μ‹€ν–‰

  • 3κ°€μ§€μ˜ λ©”μ„œλ“œ μΆ”κ°€
public class Dog {

	...
    
    private void speak(final String sound, final int count) {
    	System.out.println(sound.repeat(count));
        
	public void eats() {
    	System.out.println("μ‚¬λ£Œλ₯Ό λ¨ΉμŠ΅λ‹ˆλ‹€.");
	}
    
    public int getAge() {
    	return age;
	}
}
  • λ©”μ„œλ“œμ˜ μ ‘κ·Όμ œμ–΄μž, 리턴 νƒ€μž…, λ„€μž„, νŒŒλΌλ―Έν„° νƒ€μž… λ“±μ˜ 정보λ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.
Method[] methods = clazz.getDeclaredMethods();

for (Method method : methods) {
	method.setAccessible(true);
    System.out.println(methods);
    System.out.println("\n------------------");
}

  • private λ©”μ„œλ“œλ„ 호좜 κ°€λŠ₯
Method method = clazz.getDeclaredMethod("speak", String.class, int.class);
method.setAccessible(true);
method.invoke(dog, "멍멍!", 5);


🧰 λ¦¬ν”Œλ ‰μ…˜μ΄ μ‚¬μš©λ˜λŠ” κ³³

μ˜ˆμ‹œμ„€λͺ…
JPAκΈ°λ³Έ μƒμ„±μž μ‚¬μš© 이유
Spring Frameworkμ˜μ‘΄μ„± μ£Όμž…, AOP λ“±
Jackson, GsonJSON <-> Object λ§€ν•‘
Mockito, JUnitν…ŒμŠ€νŠΈ 객체 μ‘°μž‘
IDE μžλ™μ™„μ„±λ™μ  정보 μ ‘κ·Ό

πŸ“Œ ν”„λ ˆμž„μ›Œν¬λŠ” 객체의 νƒ€μž…μ„ 컴파일 μ‹œμ μ— μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ— β†’ λ¦¬ν”Œλ ‰μ…˜μœΌλ‘œ 동적 처리


πŸ‘“ λ¦¬ν”Œλ ‰μ…˜μ€ κΈ°λ³Έ μƒμ„±μžκ°€ ν•„μˆ˜!

  • λ§Žμ€ ν”„λ ˆμž„μ›Œν¬λ‚˜ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλŠ” 객체에 κΈ°λ³Έ μƒμ„±μžκ°€ μ™œ ν•„μš”ν• κΉŒ?

  • entity, requestDto, responseDtoμ—μ„œλŠ” κΈ°λ³Έ μƒμ„±μžκ°€ κΌ­ ν•„μš”ν•˜λ‹€.

  • κΈ°λ³Έ μƒμ„±μžλ‘œ 객체λ₯Ό μƒμ„±ν•˜κ³  ν•„λ“œλ₯Ό 톡해 값을 λ„£μ–΄μ£ΌλŠ” 것이 κ°€μž₯ κ°„λ‹¨ν•œ 방법이기 λ•Œλ¬Έμž„

κΈ°λ³Έ μƒμ„±μžκ°€ ν•„μš”ν•œ 이유

  • μ–΄λ–€ μƒμ„±μžλ₯Ό μ‚¬μš©ν• μ§€ κ³ λ₯΄κΈ° 어렀움
  • μƒμ„±μžμ— 둜직이 μžˆλŠ” 경우 μ›ν•˜λŠ” 값을 λ°”λ‘œ 넣어쀄 수 μ—†λ‹€.
  • νŒŒλΌλ―Έν„°λ“€μ˜ νƒ€μž…μ΄ 같을 λ•Œ 이름이 ν•„λ“œμ™€ λ‹€λ₯΄λ©΄ 값을 μ•Œλ§žκ²Œ λ„£μ–΄μ£ΌκΈ° νž˜λ“€λ‹€

κΈ°λ³Έ μƒμ„±μžλ₯Ό μ‚¬μš©ν•  경우 이 λͺ¨λ“  경우의 μˆ˜λ“€μ„ κ³ λ €ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.


πŸ’Ώ μ–΄λ…Έν…Œμ΄μ…˜ μž‘λ™ 원리

  1. λ¦¬ν”Œλ ‰μ…˜μ„ 톡해 ν΄λž˜μŠ€λ‚˜ λ©”μ„œλ“œ, νŒŒλΌλ―Έν„° 정보λ₯Ό κ°€μ Έμ˜¨λ‹€.
  2. λ¦¬ν”Œλ ‰μ…˜μ˜ getAnnotation(s), getDeclaredAnnotation(s) λ“±μ˜ λ©”μ„œλ“œλ₯Ό 톡해 μ›ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜μ΄ λΆ™μ–΄ μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
  3. μ–΄λ…Έν…Œμ΄μ…˜μ΄ λΆ™μ–΄ μžˆλ‹€λ©΄ μ›ν•˜λŠ” λ‘œμ§μ„ μˆ˜ν–‰ν•œλ‹€.

λ¦¬ν”Œλ ‰μ…˜μ€ 생각보닀 우리 κ°€κΉŒμ΄μ— μžˆλ‹€!!


πŸ› οΈ κ°„λ‹¨ν•œ DI ν”„λ ˆμž„μ›Œν¬ λ§Œλ“€κΈ°

πŸ”§ @Autowired μ»€μŠ€ν…€ μ–΄λ…Έν…Œμ΄μ…˜

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {}

🧠 ApplicationContext 둜직

  • DI(Dependency Injection): μ˜μ‘΄κ΄€κ³„ μ£Όμž…
    D

  • μŠ€ν”„λ§μ—μ„œ μ‚¬μš©ν•˜λŠ” κ·ΈλŒ€λ‘œ @Autowired μ–΄λ…Έν…Œμ΄μ…˜μ„ 뢙이면 μ˜μ‘΄μ„±μ„ μ£Όμž…ν•΄ 쀄 수 μžˆλ„λ‘ λ§Œλ“€ μ˜ˆμ •

  • λŸ°νƒ€μž„μ—μ„œ ν˜ΈμΆœν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜μ΄κΈ° λ•Œλ¬Έμ— @Retention을 runtime으둜 μ„€μ •

  • ApplicationContextλŠ” 의쑴 관계 μ£Όμž…μ„ λ‹΄λ‹Ήν•˜λŠ” 핡심적인 λΆ€λΆ„

  • getInstance() λ©”μ„œλ“œλŠ” νŠΉμ • νƒ€μž…μ˜ 클래슀λ₯Ό 인자둜 λ°›μœΌλ©΄ κ·Έ νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” λ©”μ„œλ“œ

  • createInstance()λ₯Ό ν†΅ν•΄μ„œ 클래슀 νƒ€μž…μ— λ§žλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό 생성

  • createInstance() λ©”μ„œλ“œ 내에 λ¦¬ν”Œλ ‰μ…˜ APIκ°€ μ‚¬μš©λμŒμ„ μ•Œ 수 μžˆλ‹€

  • 그리고 κ·Έ 클래슀의 ν•„λ“œλ“€μ„ μˆœνšŒν•˜λ©΄μ„œ AutowiredλΌλŠ” μ–΄λ…Έν…Œμ΄μ…˜μ΄ λΆ™μ–΄μžˆλŠ”μ§€ 검사

  • Autowired μ–΄λ…Έν…Œμ΄μ…˜μ΄ λΆ™μ–΄ μžˆλ‹€λ©΄ κ·Έ ν•„λ“œμ˜ νƒ€μž…μ— λ§žλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό 생성

  • setAccessible을 ture둜 μ„€μ •
    λ§ˆμ§€λ§‰μœΌλ‘œ ν•„λ“œ μΈμŠ€ν„΄μŠ€λ₯Ό ν•΄λ‹Ή ν•„λ“œμ— μ£Όμž…

  • ApplicationContext의 getInstance()λ©”μ„œλ“œλ₯Ό ν†΅ν•΄μ„œ μΈμŠ€ν„΄μŠ€λ₯Ό 얻을 수 있음

  • ν…ŒμŠ€νŠΈ μ½”λ“œλ‘œ 확인결과 μ •μƒμ μœΌλ‘œ μ£Όμž…λ˜λŠ” 것을 λ³Ό 수 있음

⚠️ λ¦¬ν”Œλ ‰μ…˜μ˜ 단점

λ¬Έμ œμ μ„€λͺ…
μ„±λŠ₯ μ €ν•˜JVM μ΅œμ ν™” λΆˆκ°€, λŸ°νƒ€μž„μ— λ™μž‘
좔상화 파괴private, final μ ‘κ·Ό ν—ˆμš©λ¨
μ½”λ“œ λ³΅μž‘λ„ μ¦κ°€μ§€μ €λΆ„ν•˜κ³  μ˜ˆμ™Έμ²˜λ¦¬ 어렀움
νƒ€μž… μ•ˆμ „μ„±β†“μ»΄νŒŒμΌ νƒ€μž„ 확인 어렀움

λ”°λΌμ„œ λ¦¬ν”Œλ ‰μ…˜μ€ ν•„μš”ν•œ 곳에 ν•œμ •ν•΄ μ‹ μ€‘ν•˜κ²Œ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.


πŸ“Œ 정리

λ¦¬ν”Œλ ‰μ…˜μ€...

  • 객체 생성, ν•„λ“œ μˆ˜μ •, λ©”μ„œλ“œ ν˜ΈμΆœκΉŒμ§€ μ‹€ν–‰ μ‹œμ μ— κ°€λŠ₯
  • μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬, JPA, ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬μ˜ 핡심 μš”μ†Œ
  • μ„±λŠ₯κ³Ό μ•ˆμ „μ„± 이슈둜 λ‚¨μš© κΈˆμ§€

πŸŽ₯ 참고자료

πŸ“Ί νŒŒλž‘, μ•„ν‚€μ˜ λ¦¬ν”Œλ ‰μ…˜ λ°œν‘œ

0개의 λŒ“κΈ€