[자바] 자바 리플렉션

자바 리플렉션과 어노테이션에 대한 개념과 실습을 진행한 것을 정리하였습니다.
도경원's avatar
Aug 01, 2025
[자바] 자바 리플렉션

1. 리플렉션(Reflection)

실행 중(Class가 메모리에 올라간 후)에 클래스, 메서드, 필드, 생성자 등의 구조를 조회하고 조작할 수 있는 기능
코드 실행 중에 클래스 정보를 읽어내고 조작 가능하다(런타임 분석), A 개발자의 App.java를 건드리지 않고 B 개발자가 UserController.java를 자유롭게 수정 가능하다(유연성 확보),
Spring, JUnit, Jackson 등은 모두 리플렉션을 사용한다.
리플렉션의 문제점
모든 메서드/필드를 다 뒤지는 것은 비효율적이다. 그렇게 하면 실행 속도가 느려지고 복잡도가 상승한다.
어노테이션(Annotation) + 리플렉션
“깃발 꽂기 → 깃발만 보기”
즉, 의미 있는 곳에만 어노테이션을 붙이고, 리플렉션은 어노테이션이 붙은 대상만 처리하면 된다.

2. 어노테이션(Annotation)

“이 코드에 의미 있는 정보를 붙이기”
자바 컴파일러, 개발자, 또는 프레임워크(Spring, JUnit 등)에게 이 부분은 특별한 처리를 해줘라고 표시하는 깃발이자 약속이다.

어디에 쓸까 ?

  • 컴파일러용 지시어
    • @Override, @SuppressWarnings
  • 런타임 처리용 깃발
    • @Controller, @Autowired, @RequestMapping
  • 소스 코드 문서화
    • @Deprecated, @FunctionalInterface

3. 자바 리플렉션 실습

런타임 처리용 깃발

notion image

실습

App.java 는 A 개발자가 만들고, UserController.java 는 B 개발자가 만든다고 가정한다. B개발자가 A개발자의 코드를 건드리지 않고 본인 코드를 수정하기
커스텀 어노테이션 정의
package java_reflection; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 커스텀 어노테이션 정의 @Retention(RetentionPolicy.RUNTIME) // 런타임에도 어노테이션 정보를 유지해서 리플렉션으로 접근 가능하게 함. @Target(ElementType.METHOD) // 이 어노테이션은 메서드에만 붙일 수 있음. public @interface RequestMapping { String value(); // 어노테이션에 @RequestMapping("/login") 이런 식으로 사용할 수 있도록 함. }
A 개발자가 개발함, 리플렉션 기반 실행 -> App 클래스에서 실행 시 사용자 입력을 받아서 해당 메서드를 동적으로 호출
package java_reflection; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Scanner; // A 개발자가 개발함, 리플렉션 기반 실행 -> App 클래스에서 실행 시 사용자 입력을 받아서 해당 메서드를 동적으로 호출 public class App { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String path = sc.nextLine(); /* UserController.class → UserController 클래스의 Class 객체를 가져오는 리플렉션 방식이야. 클래스는 실행 중에 메타정보를 담고 있는 Class 객체로 표현됨. .getDeclaredMethods() → 이 클래스에 선언된 모든 메서드를 가져와서 배열로 리턴해줌. private, public, protected 전부 다 포함 상속받은 건 제외됨 (자기 클래스 안에 선언된 것만) */ Method[] methods = UserController.class.getDeclaredMethods(); // .invoke()로 메서드를 호출하기 위해서 UserController 객체를 직접 생성 UserController us = new UserController(); for (Method method : methods) { // 현재 method에 붙은 @RequestMapping 어노테이션이 있는지 확인하고 가져옴, 없으면 null, 있으면 어노테이션 객체가 리턴됨. Annotation anno = method.getAnnotation(RequestMapping.class); // anno는 타입이 Annotation이라서 다운캐스팅을 해줌. RequestMapping rm = (RequestMapping) anno; // value() 값이 일치하면 그 메서드를 실행함. if (rm.value().equals(path)) { try { // method.invoke(obj, args...)는 리플렉션을 통해 런타임에 메서드를 직접 실행시키는 방법이다. // 마치 obj.메서드명()을 코드가 아니라 데이터처럼 다루는 느낌! // 핵심임. 리플렉션을 통해 메서드를 동적으로 호출 method.invoke(us); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } } } }
B 개발자가 개발함, 메서드마다 어노테이션을 붙임
package java_reflection; // B 개발자가 개발함, 메서드마다 어노테이션을 붙임 public class UserController { @RequestMapping("/login") // 경로를 매핑하는 어노테이션을 붙임 public void login() { System.out.println("login 호출됨"); } @RequestMapping("/logout") public void logout() { System.out.println("logout 호출됨"); } @RequestMapping("/join") public void join() { System.out.println("join 호출됨"); } @RequestMapping("/delete") public void delete() { System.out.println("delete 호출됨"); } }
Java는 컴파일 시 default 파라미터 이름 정보를 버린다. 따라서 .getParameters()로 이름을 보기 위해선 컴파일 옵션이 필요하다.
💡
IntelliJ나 CLI에서 컴파일할 때 parameters 옵션을 주면 된다. 그래야 .getParameters()[0].getName()이 진짜 username, password 같이 나온다.
javac -parameters App.java
 
Share article

Gyeongwon's blog