리플렉션이란?
- 리플렉션
→ 속을 비추다 (코드의 내부를 들여다보는 것)
보통 런타임에 리플렉션이 일어난다. (모든걸 탐색하게 함)
코드 실행전 컴파일시 분석하게 하기 위해서는 어노테이션을 활용하게 할 수 있음.
어노테이션을 활용하기 위해서는 행위를 정의해야한다. 다만, 사람이 나무를 수리한다고 했을 때 나무의 높이에 따라 
수리한다 를 제외한 행위는 다르게 정의될 수 있다.어노테이션이란? 
JVM 이 보는 힌트. 주석같은 역할을 한다.
1. 리플렉션 예제
리플렉션을 사용하지 않는 코드

1.1 컨트롤러 생성
먼저, 
UserController라는 클래스를 만들어 로그인과 회원가입 기능을 제공하는 메서드를 정의합니다.package ex01;
public class UserController {
    
    public void login(){
        System.out.println("login 호출됨");
    }
    
    public void join(){
        System.out.println("join 호출됨");
    }
}
1.2 App 클래스 생성
이제, 
App 클래스에서 사용자가 입력한 URL에 따라 해당 메서드를 호출하도록 코드를 작성해봅시다.package ex01;
public class App {
    public static void main(String[] args) {
        String path = "/login";
        UserController uc = new UserController();
        if(path.equals("/login")) {
            uc.login();
        }else if (path.equals("/join")) {
            uc.join();
        }
    }
}
1.3 실행

String path = "/login"; 를 join 으로 바꾸면
🎈 이 코드는 단순히 
path 변수를 통해 /login 또는 /join 경로에 맞춰 UserController의 메서드를 호출합니다. 그러나 이런 방식은 확장성이 떨어집니다. 새로운 메서드가 추가될 때마다 App 클래스도 수정해야 하기 때문입니다.
2. 리플렉션 활용
🎈 참고로 리플렉션은 순서 보장이 되지 않는다.
아래 코드는 
UserController 클래스의 메서드를 리플렉션을 사용해 탐색하고, 그 이름을 출력합니다.2.1 UserController 생성
public class UserController {
    
    public void login(){
        System.out.println("login 호출됨");
    }
    
    public void join(){
        System.out.println("join 호출됨");
    }
}2.2 App 클래스 생성
import java.lang.reflect.Method;
public class App {
    public static void main(String[] args) {
        UserController uc = new UserController();
        Method[] methods = uc.getClass().getDeclaredMethods();
        for (Method mt : methods) {
            System.out.println(mt.getName());
        }
    }
}  Method[] methods = uc.getClass().getDeclaredMethods();를 통해 UserController  안에 있는 메서드의 이름을 조회할 수 있다. (실행도 가능하다!)
- 실행하는 코드

mt.invoke(uc); 를 사용하기 위해서는 Exception 이 필요함.(매개변수로 넣은 uc 안에 메서드가 존재하지 않을 수 있기 때문에)
2.3 어노테이션 정의
어노테이션을 사용하면 메서드 이름 대신 어노테이션 값을 기준으로 동적으로 메서드를 호출할 수 있다.
먼저 
RequestMapping 이라는 어노테이션을 정의해 본다.import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) //메서드 위에만 붙일 수 있다
@Retention(RetentionPolicy.RUNTIME) // 해당 깃발은 런타임시 실행됨.
public @interface RequestMapping {
    String uri(); // String 타입의 uri 를 넣을 수 있음.
}
소스 : 컴파일시 , 런타임 : 런타임시 

이 엘레멘트가 붙을 수 있는 위치를 의미한다.
2.4 어노테이션 적용 및 최종 코드
public class UserController {
    @RequestMapping(uri = "/login")
    public void login(){
        System.out.println("login 호출됨");
    }
    @RequestMapping(uri = "/join")
    public void join(){
        System.out.println("join 호출됨");
    }
}import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class App {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        String path = "/login";
        UserController uc = new UserController();
        Method[] methods = uc.getClass().getDeclaredMethods();
        for (Method mt : methods) {
            Annotation anno = mt.getDeclaredAnnotation(RequestMapping.class);
            RequestMapping rm = (RequestMapping) anno;
						// 이렇게 통합할 수 있음
						// RequestMapping rm = mt.getAnnotation(RequestMapping.class);
            if (rm == null) break;
            if(rm.uri().equals(path)){
                mt.invoke(uc);
            }
        }
    }
}위 코드에서는 어노테이션을 사용해 각 메서드가 어떤 URL 경로와 매핑되는지를 정의한 후, 런타임에 이 매핑 정보를 바탕으로 메서드를 호출한다.
path 변수만 바꾸면 어떤 메서드든 호출할 수 있게 되며, 새로운 메서드가 추가되더라도 App 클래스는 수정할 필요가 없다.결론
이번 포스팅에서는 스프링 프로젝트에서 자주 사용되는 어노테이션이 어떻게 동작하는지 살펴보며, 리플렉션에 대해 알아보았다. 
리플렉션을 통해 어노테이션의 실제 사용 방식과 그 동작 원리를 더 깊이 이해할 수 있었으며, 이를 바탕으로 기존에 활용하던 코드를 분석하고 개선할 수 있는 방법도 배울 수 있었다. 
앞으로 어노테이션을 더 효과적으로 활용하기 위해 이번에 배운 내용을 프로젝트에 적용해 보면 좋을것이다.
Share article
